匿名
未登录
登录
丢石头百科
搜索
查看“STM32CubeMX系列教程11:串行外设接口SPI(二)”的源代码
来自丢石头百科
名字空间
页面
讨论
English
更多
更多
页面选项
查看
查看源代码
历史
←
STM32CubeMX系列教程11:串行外设接口SPI(二)
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
<b style="font-size: 14px; line-height: 16.8px; widows: auto; background-color: inherit;">1.新建工程</b> <b style="background-color: inherit;"> </b> 本章程序在串口printf工程的基础上修改,复制串口printf的工程,修改文件夹名。击STM32F746I.ioc打开STM32cubeMX的工程文件重新配置。SPI1选择全双工主模式,不开启NSS。配置PA7为SPI_MOSI,PA6为SPI_MISO,PA5为SPI_SCK,PA4配置为GPIO输出模式,作为片选信号。 [[File:153854br73w6r7hz4fy4k7.png]] [[File:153854algr1aoi2e22aor1.png]] SPI配置中设置<b style="background-color: inherit;">数据长度为8bit,MSB先输出</b>分频为64分频,则波特率为1.6875 MBits/s。其他为默认设置。 Motorla格式,CPOL设置为Low,CPHA设置为第一个边沿。不开启CRC检验,NSS为软件控制。 [[File:153855dam413303zg3g0g3.png]] 在GPIO管脚配置中设置PA4的用户标签为SPI1_CS。 [[File:153855qidfiu6lgfxdyzvp.png]] 生成报告以及代码,编译程序。在spi.c文件中可以看到ADC初始化函数。在stm32f7xx_hal_spi.h头文件中可以看到spi的操作函数。分别对应轮询,中断和DMA三种控制方式。 [[File:153855tkkiwk0rtsk9i3tz.png]] 下面为W25QXX的驱动文件。下载加压并添加进工程中。 <a class="attach" href="portal.php?mod=attachment&id=485" target="_blank">w25Qxx.zip 在工程目录下新建文件夹BSP,把刚才下载的文件复制进去。 [[File:153856jz73xupt47r7f5r7.png]] 在工程框中,选择工程名按鼠标右键添加组,修改组名称为Drivers/BSP,选择刚才BSP文件夹的路径,添加W25QXX.c文件。 [[File:153856l1b46hhi1cez414c.png]] 点击图标 [[File:153856hpo8t8ns7ro0ss4z.png]] 打开工程选项设置,选择C/C++栏,在Include Paths中添加头文件路径..\BSP。 [[File:153856e1hwlfxfl84y291h.png]] 重新编译工程,看是否有错误。 <b style="background-color: inherit;">2.W25Qxx驱动函数介绍</b> <b style="background-color: inherit;"> </b> 在W25QXX.c文件中有很多操作函数,这个只接收几个简单的函数。 <syntaxhighlight lang="python"> /** * @brief Read Manufacture/Device ID. * @param return value address * @retval None */ void BSP_W25Qx_Read_ID(uint8_t *ID) { uint8_t cmd[4] = {READ_ID_CMD,0x00,0x00,0x00}; W25Qx_Enable(); /* Send the read ID command */ HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); /* Reception of the data */ HAL_SPI_Receive(&hspi1,ID, 2, W25Qx_TIMEOUT_VALUE); W25Qx_Disable(); }</syntaxhighlight> [[File:153857m4n4uobe5f4cbocc.png]] 以上为读W25Qxx读ID函数,函数开始先定义一个数组cmd保存读ID命令,其中READ_ID_CMD为读ID命令90H,在W25QXX.h头文件中通过宏定义。数组后三个值为地址000000H。 [[File:153859inlh31l4cnhqy3d1.png]] W25Qx_Enable(),W25Qx_Disable()分别为使能和失能SPI设备,即拉低和拉高/CS电平。在W25QXX.h头文件中可以找到如下宏定义。 [[File:153859aogdbdvefyzgjeqo.png]] 在GPIO管脚配置中设置PA4的用户标签为SPI1_CS,所以mxconstants.h头文件中有如下宏定义。 [[File:153859c31b58w93jubrj3m.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:153900fqfn22e22tzyeel6.png]] 如下为W25QXX读函数。函数开始先将要发送的数据(命令和地址)存储在cmd数组中,然后后通过HAL_SPI_Transmit()函数发送出去,接着通过HAL_SPI_Receive()接收读取的数据。 <syntaxhighlight lang="python"> /** * @brief Reads an amount of data from the QSPI memory. * @param pData: Pointer to data to be read * @param ReadAddr: Read start address * @param Size: Size of data to read * @retval QSPI memory status */ uint8_t BSP_W25Qx_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size) { uint8_t cmd[4]; /* Configure the command */ cmd[0] = READ_CMD; cmd[1] = (uint8_t)(ReadAddr >> 16); cmd[2] = (uint8_t)(ReadAddr >> 8); cmd[3] = (uint8_t)(ReadAddr); W25Qx_Enable(); /* Send the read ID command */ HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); /* Reception of the data */ if (HAL_SPI_Receive(&hspi1, pData,Size,W25Qx_TIMEOUT_VALUE) != HAL_OK) { return W25Qx_ERROR; } W25Qx_Disable(); return W25Qx_OK; }</syntaxhighlight> [[File:153858g3ytu93raz5busy8.png]] 如下为W25Qxx写操作函数,采用页编程指令(02H),每次最多可以写入256字节。所以写函数中将要写入的数据分多次写入W25Qxx中,每次只写256个字节,不断循环直到数据完全写完。写数据前先使能写操作。 <syntaxhighlight lang="python"> /** * @brief Writes an amount of data to the QSPI memory. * @param pData: Pointer to data to be written * @param WriteAddr: Write start address * @param Size: Size of data to write,No more than 256byte. * @retval QSPI memory status */ uint8_t BSP_W25Qx_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size) { uint8_t cmd[4]; uint32_t end_addr, current_size, current_addr; uint32_t tickstart = HAL_GetTick(); /* Calculation of the size between the write address and the end of the page */ current_addr = 0; while (current_addr <= WriteAddr) { current_addr += W25Q128FV_PAGE_SIZE; } current_size = current_addr - WriteAddr; /* Check if the size of the data is less than the remaining place in the page */ if (current_size > Size) { current_size = Size; } /* Initialize the adress variables */ current_addr = WriteAddr; end_addr = WriteAddr + Size; /* Perform the write page by page */ do { /* Configure the command */ cmd[0] = PAGE_PROG_CMD; cmd[1] = (uint8_t)(current_addr >> 16); cmd[2] = (uint8_t)(current_addr >> 8); cmd[3] = (uint8_t)(current_addr); /* Enable write operations */ BSP_W25Qx_WriteEnable(); W25Qx_Enable(); /* Send the command */ if (HAL_SPI_Transmit(&hspi1,cmd, 4, W25Qx_TIMEOUT_VALUE) != HAL_OK) { return W25Qx_ERROR; } /* Transmission of the data */ if (HAL_SPI_Transmit(&hspi1, pData,current_size, W25Qx_TIMEOUT_VALUE) != HAL_OK) { return W25Qx_ERROR; } W25Qx_Disable(); /* Wait the end of Flash writing */ while(BSP_W25Qx_GetStatus() == W25Qx_BUSY); { /* Check for the Timeout */ if((HAL_GetTick() - tickstart) > W25Qx_TIMEOUT_VALUE) { return W25Qx_TIMEOUT; } } /* Update the address and size variables for next page programming */ current_addr += current_size; pData += current_size; current_size = ((current_addr + W25Q128FV_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : W25Q128FV_PAGE_SIZE; } while (current_addr < end_addr); return W25Qx_OK; }</syntaxhighlight> [[File:153858lxf03x9y2r0xzmty.png]] 扇区擦除函数,和写函数一样,擦除扇区前必先使能写操作。发送扇区擦除指令后不断读取W25Qxx的状态寄存器,判断flash是否为忙状态,如果不为忙则擦除操作完成。 <syntaxhighlight lang="python"> /** * @brief Erases the specified block of the QSPI memory. * @param BlockAddress: Block address to erase * @retval QSPI memory status */ uint8_t BSP_W25Qx_Erase_Block(uint32_t Address) { uint8_t cmd[4]; uint32_t tickstart = HAL_GetTick(); cmd[0] = SECTOR_ERASE_CMD; cmd[1] = (uint8_t)(Address >> 16); cmd[2] = (uint8_t)(Address >> 8); cmd[3] = (uint8_t)(Address); /* Enable write operations */ BSP_W25Qx_WriteEnable(); /*Select the FLASH: Chip Select low */ W25Qx_Enable(); /* Send the read ID command */ HAL_SPI_Transmit(&hspi1, cmd, 4, W25Qx_TIMEOUT_VALUE); /*Deselect the FLASH: Chip Select high */ W25Qx_Disable(); /* Wait the end of Flash writing */ while(BSP_W25Qx_GetStatus() == W25Qx_BUSY); { /* Check for the Timeout */ if((HAL_GetTick() - tickstart) > W25Q128FV_SECTOR_ERASE_MAX_TIME) { return W25Qx_TIMEOUT; } } return W25Qx_OK; }</syntaxhighlight> [[File:153858ngak3awlnlwklawc.png]] <b style="background-color: inherit;">3.添加应用程序</b> <b style="background-color: inherit;"> </b> 在main.c文件中声明变量,rData,wData分别存储读写的数据,ID存储读取的ID值。 <syntaxhighlight lang="python"> /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ uint8_t wData[0x100]; uint8_t rData[0x100]; uint32_t i; uint8_t ID[2]; /* USER CODE END PV */</syntaxhighlight> <syntaxhighlight lang="python"> </syntaxhighlight> 在main函数中添加如下测试程序。 <syntaxhighlight lang="python"> /* USER CODE BEGIN 2 */ printf("\r\n SPI-W25Qxxx Example \r\n\r\n"); /*##-1- Read the device ID ########################*/ BSP_W25Qx_Init(); BSP_W25Qx_Read_ID(ID); printf(" W25Qxxx ID is : 0x%02X 0x%02X \r\n\r\n",ID[0],ID[1]); /*##-2- Erase Block ##################################*/ if(BSP_W25Qx_Erase_Block(0) == W25Qx_OK) printf(" SPI Erase Block ok\r\n"); else Error_Handler(); /*##-3- Written to the flash ########################*/ /* fill buffer */ for(i =0;i<0x100;i ++) { wData[i] = i; rData[i] = 0; } if(BSP_W25Qx_Write(wData,0x00,0x100)== W25Qx_OK) printf(" SPI Write ok\r\n"); else Error_Handler(); /*##-4- Read the flash ########################*/ if(BSP_W25Qx_Read(rData,0x00,0x100)== W25Qx_OK) printf(" SPI Read ok\r\n\r\n"); else Error_Handler(); printf("SPI Read Data : \r\n"); for(i =0;i<0x100;i++) printf("0x%02X ",rData[i]); printf("\r\n\r\n"); /*##-5- check date ########################*/ if(memcmp(wData,rData,0x100) == 0 ) printf(" W25Q128FV SPI Test OK\r\n"); else printf(" W25Q128FV SPI Test False\r\n"); /* USER CODE END 2 */</syntaxhighlight> <syntaxhighlight lang="python"> </syntaxhighlight> <syntaxhighlight lang="python"> </syntaxhighlight> 程序中先是初始化W25QXX,即发送使能重启(66H)和使能重启设备(99H)命令初始化flash,然后读取设备ID并输出。第2步中擦除flash,写入数据前必先要擦除内存。然后填充写入数据缓存并写入flahs中。最后读取刚才写入的数据,并检测数据是否正确。 把程序中用的出错处理函数添加在main.c文件后面。 <syntaxhighlight lang="python"> /* USER CODE BEGIN 4 */ /** * @brief This function is executed in case of error occurrence. * @param None * @retval None */ static void Error_Handler(void) { printf("something wrong ....\r\n"); /* User may add here some code to deal with this error */ while(1) { } } /* USER CODE END 4 */</syntaxhighlight> <syntaxhighlight lang="python"> </syntaxhighlight> 在main.c文件开头添加应用的头文件。 <syntaxhighlight lang="python"> /* USER CODE BEGIN Includes */ #include <string.h> #include "W25QXX.h" /* USER CODE END Includes */</string.h></syntaxhighlight> <syntaxhighlight lang="python"> 将W25QXX DataFlash Board模块插入到Open746I开发板SPI1中,编译程序并下载到开发板。打开串口调试助手。设置波特率为115200。串口助手上会显示如下信息。 </syntaxhighlight> [[File:153859zwvh23z700ygqjqi.png]]
返回至
STM32CubeMX系列教程11:串行外设接口SPI(二)
。
导航
导航
首页
最近更改
随机页面
MediaWiki帮助
首页
首页
树莓派
主机
配件包
外壳
键鼠
电源
扩展板
显示屏
墨水屏
摄像模块
通信模块
继电器
电机驱动板
游戏机
产品分类
树莓派
Arduino
micro:bit
STM32
Espressif
WiFi模块
蓝牙模块
无线模块
LoRa模块
4G模块
GSM
GPRS
以太网
导航模块
北斗卫星
GPS
LCD
墨水屏
OLED
摄像头
USB模块
串口模块
RS232
RS485
CAN
传感器
温度模块
湿度模块
气压模块
继电器
电机模块
指纹模块
电平转换
音频模块
编程器
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志