[CubeMX] SPI
在 SPI 总线协议设计上,从机不会主动向主机发送数据。SPI 是一种 严格的主机驱动(Master-driven)通信协议,所有通信都必须由主机发起。常用的 STM32CubeMX SPI 主机 + 中断方案。流程分为三部分:
1️⃣ CubeMX 配置
2️⃣ 代码实现(HAL 中断模式)
3️⃣ 简单测试程序
以下以 SPI1 主机 + 全双工中断通信 为例。
一、CubeMX 配置
1 选择 SPI
在 Pinout & Configuration
选择:
SPI1 → Mode → Full-Duplex Master
CubeMX 会自动分配
| 引脚 | 功能 |
|---|---|
| SCK | SPI 时钟 |
| MISO | 主机接收 |
| MOSI | 主机发送 |
| NSS | 软件管理 |
建议:
NSS → Software
因为主机通常自己控制 CS。
2 SPI 参数配置
进入
SPI1 → Parameter Settings
推荐配置:
| 参数 | 设置 |
|---|---|
| Mode | Full Duplex Master |
| Data Size | 8 Bits |
| First Bit | MSB |
| Clock Polarity | Low |
| Clock Phase | 1 Edge |
| NSS | Software |
| Baudrate Prescaler | 16 或 32 |
| CRC | Disable |
如果不确定 SPI 模式:
Mode0 (CPOL=0 CPHA=0)
最常见。
3 使能 SPI 中断
进入:
NVIC Settings
勾选
SPI1 global interrupt
4 生成代码
Project Manager → Generate Code
二、SPI 中断代码结构
CubeMX 会生成
spi.c
stm32xx_it.c
main.c
HAL SPI 中断 API:
HAL_SPI_Transmit_IT()
HAL_SPI_Receive_IT()
HAL_SPI_TransmitReceive_IT()
通常使用
HAL_SPI_TransmitReceive_IT()
因为 SPI 是同步收发。
三、示例代码
1 全局变量
在 main.c
uint8_t tx_buf[8] = {1,2,3,4,5,6,7,8};
uint8_t rx_buf[8];
volatile uint8_t spi_done = 0;
2 发送函数
void spi_send_test()
{
spi_done = 0;
HAL_SPI_TransmitReceive_IT(
&hspi1,
tx_buf,
rx_buf,
sizeof(tx_buf)
);
}
3 完成回调
HAL 会自动调用
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1)
{
spi_done = 1;
}
}
4 错误回调(建议加)
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
if(hspi->Instance == SPI1)
{
printf("SPI Error\n");
}
}
5 主循环测试
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
spi_send_test();
while (1)
{
if(spi_done)
{
spi_done = 0;
printf("SPI RX:");
for(int i=0;i<8;i++)
printf("%02X ", rx_buf[i]);
printf("\n");
HAL_Delay(1000);
spi_send_test();
}
}
}
四、中断函数(CubeMX 已生成)
在
stm32xx_it.c
会有:
void SPI1_IRQHandler(void)
{
HAL_SPI_IRQHandler(&hspi1);
}
不用修改。
五、CS 控制(重要)
SPI 主机一般自己控制 CS。
CubeMX:
不要用 NSS
自己用 GPIO。
例如:
#define SPI_CS_LOW() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET)
#define SPI_CS_HIGH() HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET)
发送:
SPI_CS_LOW();
HAL_SPI_TransmitReceive_IT(&hspi1, tx_buf, rx_buf, 8);
完成回调:
SPI_CS_HIGH();
六、完整 SPI 读取流程示例
SPI 是同步全双工通信,在读取的同时,也同时接收。
但是从机不可能在 SPI 读的同时就知道他要读哪个寄存器,从而在收到第一个字节的同时返回寄存器数据。
因此一般 SPI 主机先发送寄存器地址,但不进行读取,然后发送要一定长度的虚拟字节,再让从机返回寄存器数据。
例如读取 SPI Flash:
命令 0x03
地址 0x000010
时序:
MOSI : 03 00 00 10 FF FF FF
MISO : XX XX XX XX D1 D2 D3
代码通常写成:
uint8_t cmd[4]={0x03,0x00,0x00,0x10};
uint8_t data[3] = {0}
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
HAL_SPI_Receive(&hspi1, data, 3, 100);
或者:
uint8_t tx[7]={0x03,0x00,0x00,0x10,0xFF,0xFF,0xFF};
uint8_t rx[7]={0};
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 7, 100);
License:
CC BY 4.0