1.新建工程
本章程序在串口printf工程的基础上修改,复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置。SPI1选择全双工主模式,不开启NSS。配置PA7为SPI_MOSI,PA6为SPI_MISO,PA5为SPI_SCK,PA4配置为GPIO输出模式,作为片选信号。
0
|
|
|
|
SPI配置中设置数据长度为8bit,MSB先输出分频为64分频,则波特率为1.6875 MBits/s。其他为默认设置。
Motorla格式,CPOL设置为Low,CPHA设置为第一个边沿。不开启CRC检验,NSS为软件控制。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/9402a82e2c454944b17edc6aa911caf2/clipboard.png
|
|
|
|
|
在GPIO管脚配置中设置PA4的用户标签为SPI1_CS。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/2b98807ff9d94dbaade6928a5322a9f7/clipboard.png
|
|
|
|
|
生成报告以及代码,编译程序。在spi.c文件中可以看到ADC初始化函数。在stm32f7xx_hal_spi.h头文件中可以看到spi的操作函数。分别对应轮询,中断和DMA三种控制方式。
下面为W25QXX的驱动文件。下载加压并添加进工程中。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/721ee0e243f6463381130e717658ce23/attachment.png?1462260698515file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/fbbb08374cb547acbf9f902bf890987c/attachment.png?1462260698516
w25Qxx.zip
|
|
|
|
|
在工程目录下新建文件夹BSP,把刚才下载的文件复制进去。
|
|
|
|
|
在工程框中,选择工程名按鼠标右键添加组,修改组名称为Drivers/BSP,选择刚才BSP文件夹的路径,添加W25QXX.c文件。
|
|
|
|
|
点击file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/c261ee31e38647a189bd0a02b5cf0f68/clipboard.png图标打开工程选项设置,选择C/C++栏,在Include Paths中添加头文件路径..BSP。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/42b28775fcdd4315826eeab0aad74ff7/clipboard.png
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/6f893807b25a45fb903fa28aa6f42dbc/clipboard.png
|
|
|
|
|
2.W25Qxx驱动函数介绍
在W25QXX.c文件中有很多操作函数,这个只接收几个简单的函数。
02 | * @brief Read Manufacture/Device ID. |
03 | * @param return value address |
06 | void BSP_W25Qx_Read_ID(uint8_t *ID) |
08 | uint8_t cmd[4] = {READ_ID_CMD,0x00,0x00,0x00}; |
11 | /* Send the read ID command */ |
12 | HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); |
13 | /* Reception of the data */ |
14 | HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE); |
|
|
|
|
|
以上为读W25Qxx读ID函数,函数开始先定义一个数组cmd保存读ID命令,其中READ_ID_CMD为读ID命令90H,在W25QXX.h头文件中通过宏定义。数组后三个值为地址000000H。
W25Qx_Enable(),W25Qx_Disable()分别为使能和失能SPI设备,即拉低和拉高/CS电平。在W25QXX.h头文件中可以找到如下宏定义。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/e9a9c4a5006940bf8b1c079a32b36db0/clipboard.png
|
|
|
|
|
在GPIO管脚配置中设置PA4的用户标签为SPI1_CS,所以mxconstants.h头文件中有如下宏定义。
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/5d88ef1c80a845a9ae5ee0611fe749d8/clipboard.png
HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE) 为通过SPI将cmd中四个字节的命令发送出去。 然后通过HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE);函数介绍两个字节的数据保存在对应的地址ID中。W25Qx_TIMEOUT_VALUE为超时。其值为1000.
file:///C:/Users/Administrator/AppData/Local/YNote/data/qq05E2D92FFCC85611A0CBEB4448FFA27E/6b81812f838f4999b799535e6a0b2072/clipboard.png
|
|
|
|
|
如下为W25QXX读函数。函数开始先将要发送的数据(命令和地址)存储在cmd数组中,然后后通过HAL_SPI_Transmit()函数发送出去,接着通过HAL_SPI_Receive()接收读取的数据。
02 | * @brief Reads an amount of data from the QSPI memory. |
03 | * @param pData: Pointer to data to be read |
04 | * @param ReadAddr: Read start address |
05 | * @param Size: Size of data to read |
06 | * @retval QSPI memory status |
08 | uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size) |
12 | /* Configure the command */ |
14 | cmd[1] = (uint8_t)(ReadAddr >> 16); |
15 | cmd[2] = (uint8_t)(ReadAddr >> 8); |
16 | cmd[3] = (uint8_t)(ReadAddr); |
19 | /* Send the read ID command */ |
20 | HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); |
21 | /* Reception of the data */ |
22 | if (HAL_SPI_Receive(&hspi1, pData,Size,W25Qx_TIMEOUT_VALUE) != HAL_OK) |
|
|
|
|
|
如下为W25Qxx写操作函数,采用页编程指令(02H),每次最多可以写入256字节。所以写函数中将要写入的数据分多次写入W25Qxx中,每次只写256个字节,不断循环直到数据完全写完。写数据前先使能写操作。
02 | * @brief Writes an amount of data to the QSPI memory. |
03 | * @param pData: Pointer to data to be written |
04 | * @param WriteAddr: Write start address |
05 | * @param Size: Size of data to write,No more than 256byte. |
06 | * @retval QSPI memory status |
08 | uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size) |
11 | uint32_t end_addr, current_size, current_addr; |
12 | uint32_t tickstart = HAL_GetTick(); |
14 | /* Calculation of the size between the write address and the end of the page */ |
17 | while (current_addr <= WriteAddr) |
19 | current_addr += W25Q128FV_PAGE_SIZE; |
21 | current_size = current_addr - WriteAddr; |
23 | /* Check if the size of the data is less than the remaining place in the page */ |
24 | if (current_size > Size) |
29 | /* Initialize the adress variables */ |
30 | current_addr = WriteAddr; |
31 | end_addr = WriteAddr + Size; |
33 | /* Perform the write page by page */ |
36 | /* Configure the command */ |
37 | cmd[0] = PAGE_PROG_CMD; |
38 | cmd[1] = (uint8_t)(current_addr >> 16); |
39 | cmd[2] = (uint8_t)(current_addr >> 8); |
40 | cmd[3] = (uint8_t)(current_addr); |
42 | /* Enable write operations */ |
43 | BSP_W25Qx_WriteEnable(); |
46 | /* Send the command */ |
47 | if (HAL_SPI_Transmit(&hspi1,cmd, 4, W25Qx_TIMEOUT_VALUE) != HAL_OK) |
52 | /* Transmission of the data */ |
53 | if (HAL_SPI_Transmit(&hspi1, pData,current_size, W25Qx_TIMEOUT_VALUE) != HAL_OK) |
58 | /* Wait the end of Flash writing */ |
59 | while(BSP_W25Qx_GetStatus() == W25Qx_BUSY); |
61 | /* Check for the Timeout */ |
62 | if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE) |
68 | /* Update the address and size variables for next page programming */ |
69 | current_addr += current_size; |
70 | pData += current_size; |
71 | current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128FV_PAGE_SIZE; |
72 | } while (current_addr < end_addr); |
|
|
|
|
|
扇区擦除函数,和写函数一样,擦除扇区前必先使能写操作。发送扇区擦除指令后不断读取W25Qxx的状态寄存器,判断flash是否为忙状态,如果不为忙则擦除操作完成。
02 | * @brief Erases the specified block of the QSPI memory. |
03 | * @param BlockAddress: Block address to erase |
04 | * @retval QSPI memory status |
06 | uint8_t BSP_W25Qx_Erase_Block(uint32_t Address) |
09 | uint32_t tickstart = HAL_GetTick(); |
10 | cmd[0] = SECTOR_ERASE_CMD; |
11 | cmd[1] = (uint8_t)(Address >> 16); |
12 | cmd[2] = (uint8_t)(Address >> 8); |
13 | cmd[3] = (uint8_t)(Address); |
15 | /* Enable write operations */ |
16 | BSP_W25Qx_WriteEnable(); |
18 | /*Select the FLASH: Chip Select low */ |
20 | /* Send the read ID command */ |
21 | HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); |
22 | /*Deselect the FLASH: Chip Select high */ |
25 | /* Wait the end of Flash writing */ |
26 | while(BSP_W25Qx_GetStatus() == W25Qx_BUSY); |
28 | /* Check for the Timeout */ |
29 | if((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME) |
|
|
|
|
|
3.添加应用程序
在main.c文件中声明变量,rData,wData分别存储读写的数据,ID存储读取的ID值。
1 | /* USER CODE BEGIN PV */ |
2 | /* Private variables ---------------------------------------------------------*/ |
在main函数中添加如下测试程序。
01 | /* USER CODE BEGIN 2 */ |
02 | printf("rn SPI-W25Qxxx Example rnrn"); |
04 | /*##-1- Read the device ID ########################*/ |
06 | BSP_W25Qx_Read_ID(ID); |
07 | printf(" W25Qxxx ID is : 0x%02X 0x%02X rnrn",ID[0],ID[1]); |
09 | /*##-2- Erase Block ##################################*/ |
10 | if(BSP_W25Qx_Erase_Block(0) == W25Qx_OK) |
11 | printf(" SPI Erase Block okrn"); |
15 | /*##-3- Written to the flash ########################*/ |
17 | for(i =0;i<0x100;i ++) |
23 | if(BSP_W25Qx_Write(wData,0x00,0x100)== W25Qx_OK) |
24 | printf(" SPI Write okrn"); |
28 | /*##-4- Read the flash ########################*/ |
29 | if(BSP_W25Qx_Read(rData,0x00,0x100)== W25Qx_OK) |
30 | printf(" SPI Read okrnrn"); |
34 | printf("SPI Read Data : rn"); |
36 | printf("0x%02X ",rData); |
39 | /*##-5- check date ########################*/ |
40 | if(memcmp(wData,rData,0x100) == 0 ) |
41 | printf(" W25Q128FV SPI Test OKrn"); |
43 | printf(" W25Q128FV SPI Test Falsern"); |
|
|
|
|
|