[STM32F4] STM32F407的SPI通信应用及代码实现

[复制链接]
3105|10
 楼主| 童雨竹 发表于 2024-10-12 11:22 | 显示全部楼层 |阅读模式

STM32F407作为ST公司推出的高性能MCU,广泛应用于嵌入式系统的开发。它集成了丰富的外设接口,其中SPI(Serial Peripheral Interface)作为一种常见的高速串行通信协议,广泛应用于与传感器、存储设备等外设的通信。本文将详细介绍STM32F407的SPI通信功能及其代码实现。

实现功能本文将以SPI通信为例,实现STM32F407与外部SPI设备的通信,通过SPI接口向设备发送数据并接收返回的数据。本文的硬件连接以STM32F407与SPI Flash存储芯片W25Q64为例,演示如何与存储器进行数据读写。

硬件连接
  • STM32F407 MCU
  • W25Q64 SPI Flash存储芯片
  • SPI接口线:MISO、MOSI、SCK、CS
SPI的四根信号线分别与STM32的SPI引脚连接,CS作为片选信号,由GPIO控制。

软件开发环境
  • 开发工具:Keil uVision 5
  • 编程语言:C语言
  • 固件库:STM32 HAL库
代码实现
  1. #include "stm32f4xx_hal.h"
  2. #include "spi.h"
  3. #include "gpio.h"

  4. // W25Q64 指令定义
  5. #define WRITE_ENABLE  0x06
  6. #define READ_DATA     0x03
  7. #define WRITE_DATA    0x02
  8. #define SECTOR_ERASE  0x20
  9. #define W25Q64_CS_LOW()   HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
  10. #define W25Q64_CS_HIGH()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)

  11. // SPI发送一字节数据
  12. void SPI_SendByte(uint8_t data)
  13. {
  14.     HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY);
  15. }

  16. // SPI接收一字节数据
  17. uint8_t SPI_ReceiveByte(void)
  18. {
  19.     uint8_t data;
  20.     HAL_SPI_Receive(&hspi1, &data, 1, HAL_MAX_DELAY);
  21.     return data;
  22. }

  23. // 写使能
  24. void W25Q64_WriteEnable(void)
  25. {
  26.     W25Q64_CS_LOW();
  27.     SPI_SendByte(WRITE_ENABLE);
  28.     W25Q64_CS_HIGH();
  29. }

  30. // 读取数据
  31. void W25Q64_ReadData(uint32_t addr, uint8_t* buffer, uint16_t len)
  32. {
  33.     W25Q64_CS_LOW();
  34.     SPI_SendByte(READ_DATA);
  35.     SPI_SendByte((addr >> 16) & 0xFF);
  36.     SPI_SendByte((addr >> 8) & 0xFF);
  37.     SPI_SendByte(addr & 0xFF);
  38.     HAL_SPI_Receive(&hspi1, buffer, len, HAL_MAX_DELAY);
  39.     W25Q64_CS_HIGH();
  40. }

  41. // 写数据
  42. void W25Q64_WriteData(uint32_t addr, uint8_t* buffer, uint16_t len)
  43. {
  44.     W25Q64_WriteEnable();
  45.     W25Q64_CS_LOW();
  46.     SPI_SendByte(WRITE_DATA);
  47.     SPI_SendByte((addr >> 16) & 0xFF);
  48.     SPI_SendByte((addr >> 8) & 0xFF);
  49.     SPI_SendByte(addr & 0xFF);
  50.     HAL_SPI_Transmit(&hspi1, buffer, len, HAL_MAX_DELAY);
  51.     W25Q64_CS_HIGH();
  52. }

  53. void MX_SPI1_Init(void)
  54. {
  55.     hspi1.Instance = SPI1;
  56.     hspi1.Init.Mode = SPI_MODE_MASTER;
  57.     hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  58.     hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  59.     hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  60.     hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  61.     hspi1.Init.NSS = SPI_NSS_SOFT;
  62.     hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  63.     hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  64.     hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  65.     hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  66.     hspi1.Init.CRCPolynomial = 10;
  67.     if (HAL_SPI_Init(&hspi1) != HAL_OK)
  68.     {
  69.         Error_Handler();
  70.     }
  71. }

  72. void MX_GPIO_Init(void)
  73. {
  74.     GPIO_InitTypeDef GPIO_InitStruct = {0};

  75.     // 使能GPIO时钟
  76.     __HAL_RCC_GPIOA_CLK_ENABLE();
  77.    
  78.     // 配置CS引脚
  79.     GPIO_InitStruct.Pin = GPIO_PIN_4;
  80.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  81.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  82.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  83.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  84.     // 片选置高
  85.     HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
  86. }

  87. int main(void)
  88. {
  89.     HAL_Init();
  90.     SystemClock_Config();
  91.     MX_GPIO_Init();
  92.     MX_SPI1_Init();
  93.    
  94.     uint8_t data_to_write[] = {0x12, 0x34, 0x56, 0x78};
  95.     uint8_t data_to_read[4];
  96.    
  97.     // 写入数据到Flash存储器
  98.     W25Q64_WriteData(0x000000, data_to_write, sizeof(data_to_write));
  99.    
  100.     // 读取数据
  101.     W25Q64_ReadData(0x000000, data_to_read, sizeof(data_to_read));
  102.    
  103.     while (1)
  104.     {
  105.         // 可以在此处检查读取的数据
  106.     }
  107. }

代码说明
  • SPI通信配置:代码通过HAL_SPI_Init配置SPI通信的模式、时钟频率等参数。在通信时,通过HAL_SPI_Transmit和HAL_SPI_Receive分别实现数据的发送和接收。
  • Flash读写操作:通过W25Q64的读写指令,与存储器进行数据交互。写入数据前需调用W25Q64_WriteEnable函数启用写操作,数据读写以字节为单位。
  • GPIO控制:CS(片选)引脚通过GPIO控制,用于指示当前的SPI操作。在每次操作前,将CS拉低,操作结束后将其拉高。

功能扩展
  • 数据校验:在写入数据后,可以实现CRC校验,确保数据的完整性。
  • 多设备通信:SPI总线上可以挂载多个设备,只需为每个设备分配独立的CS引脚。
  • 高速传输:可以调整SPI的波特率设置,实现更高速的数据传输,适用于大数据量的存储场景。
  • 深度睡眠模式:STM32支持低功耗模式,可以在空闲时进入睡眠模式,进一步降低功耗。

总结STM32F407凭借其高性能和丰富的外设接口,特别是SPI接口,能够轻松与各种外部设备进行通信。本次例程通过SPI与W25Q64 Flash存储芯片的通信实现了数据的读写操作,展示了STM32的强大功能。开发者可以在此基础上,进一步扩展应用至多设备通信、数据校验等高级功能。

公羊子丹 发表于 2024-10-12 11:22 | 显示全部楼层
这个SPI的例程正好解决了我项目中和存储器通信的问题,感谢分享!
周半梅 发表于 2024-10-12 11:23 | 显示全部楼层
看了你的代码,我明白了CS引脚怎么用,之前一直搞不清楚。
周半梅 发表于 2024-10-12 11:23 | 显示全部楼层
请问SPI的时钟频率如果设置太高,会不会影响通信稳定性?
万图 发表于 2024-10-12 11:24 | 显示全部楼层
STM32F407的硬件资源真的丰富,用来做存储器管理很适合。
Wordsworth 发表于 2024-10-12 11:24 | 显示全部楼层
我现在正尝试用STM32控制多个SPI设备,这个例子给了我很多启发。
Bblythe 发表于 2024-10-12 11:25 | 显示全部楼层
想问下,如果在读写数据时中断了通信,应该如何处理这种异常情况?
Pulitzer 发表于 2024-10-12 11:26 | 显示全部楼层
HAL库的SPI接口确实方便,用起来比裸机写寄存器要省心很多。
Uriah 发表于 2024-10-12 11:27 | 显示全部楼层
能不能分享下如何给SPI传输加个CRC校验,确保数据的完整性?
Clyde011 发表于 2024-10-12 11:29 | 显示全部楼层
W25Q64的容量挺大的,这个例子写的代码可以用在其他更大的Flash上吗?
Stahan 发表于 2024-10-13 19:46 | 显示全部楼层
这个读写速度快吗
您需要登录后才可以回帖 登录 | 注册

本版积分规则

183

主题

6349

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部