SPI — 全双工主机(中断方式)(LL 库)
HAL vs LL:PUYA 固件库提供 HAL 和 LL 两套 API。
- HAL 库:封装度高,可移植性强,适合快速开发
- LL 库:接近寄存器操作,代码精简效率高,适合性能敏感场景 本文使用 LL 库版本。
固件库: PY32F0xx_Firmware V1.5.0
芯片: PY32F030(PY32F002A / F003 同库可参考)
源文件:PUYA/PY32F0xx_Firmware_V1.5.0/Projects/PY32F030-STK/Example_LL/SPI/SPI_TwoBoards_FullDuplexMaster_IT_Init/Src/main.c
功能简介
本示例演示 SPI 全双工主机(Master),使用中断方式:按键后使能 SPI1,通过 TXE/RXNE 中断逐字节发送 15 字节,同时接收对方回环的数据,结束后比对收发一致性——一致则点亮 LED,否则 LED 闪烁。
说明:PUYA 的 LL 示例库里 SPI 只有 DMA 和中断(IT)两种版本,没有轮询(Polling)版。你点名要的
SPI_TwoBoards_FullDuplexMaster_Polling在 LL 中不存在,这里用最接近、也最能体现 LL 风格的..._IT_Init(中断方式)来替代。HAL 版则有..._Master_DMA,可作为对照。
与 HAL 版(HAL_SPI_TransmitReceive)相比,LL 版把“每个字节的收发”交给中断回调函数 APP_SpiTxCallback() / APP_SpiRxCallback() 完成,主循环只需等传输完成——这正是 LL 在“不阻塞 CPU”场景下的典型写法。
硬件准备
- 两块 PY32F030 开发板(一块主机、一块从机,跑对应 Slave 示例做回环)
- SPI 连线:主机 PB3(SCK) ↔ 从机 SCK,PB4(MISO) ↔ MISO,PB5(MOSI) ↔ MOSI
- 主机板载按键(KEY)、绿 LED
完整代码
|
|
代码分段讲解
1. GPIO 复用与 SPI 参数
PB3/4/5 设为 LL_GPIO_MODE_ALTERNATE + Alternate = LL_GPIO_AF_0 接到 SPI1。SPI 参数结构体 LL_SPI_InitTypeDef 与 HAL 的 SPI_HandleTypeDef.Init 字段几乎一一对应:全双工、主机、8 位、CPOL/CPHA、软件 NSS、波特率分频、MSB 优先。
2. 中断方式的核心:TXE / RXNE 回调
HAL 的 HAL_SPI_TransmitReceive() 内部要么轮询、要么用 DMA;LL 这里走中断:
- 使能
LL_SPI_EnableIT_TXE():发送缓冲区空时进中断 →APP_SpiTxCallback()写一个字节,SPI 硬件同时自动收一个字节; - 使能
LL_SPI_EnableIT_RXNE():收到一个字节进中断 →APP_SpiRxCallback()读走它。
因为 SPI 是全双工,每发一个必然同时收一个,所以两个索引独立推进,主循环只需等两者都到头。
3. FIFO 阈值
LL_SPI_SetRxFIFOThreshold(..., LL_SPI_RX_FIFO_TH_QUARTER) 让 RXNE 在“收到 1 个字节”时就置位(而不是等 FIFO 半满),保证逐字节中断行为正确。这是 LL 才需要你显式关心的底层细节。
4. 为什么 LL 没有“轮询 SPI”示例
SPI 全双工收发天然适合“发一个收一个”的流式处理,用中断/DMA 比忙等更高效;而 HAL 为了 API 对称提供了 HAL_SPI_TransmitReceive(轮询版)。LL 把“怎么搬”的选择权完全交给你——你可以轻松把本例的 IT 改成轮询(去掉中断、在主循环里查 LL_SPI_IsActiveFlag_TXE)。
实验现象
- 主从板 SPI 四线连好,从机跑 Slave 示例。
- 主机按下 KEY,SPI1 使能,开始全双工收发 15 字节。
- 收发结束,数据比对一致 → 绿 LED 常亮;不一致 → 绿 LED 闪烁。
常见问题
-
收发数据对不上?
- 确认主从 CPOL/CPHA 一致(本例 极性高/相位1边沿)。
- 确认 NSS 处理:本例用软件 NSS(
LL_SPI_NSS_SOFT),硬件 NSS 引脚未参与,接线时从机片选要正确拉低。
-
只进 TX 中断不出 RX?
- 多半是 MISO 线没连通或主从没共地;SPI 收不到字节,RXNE 永远不会置位。
-
想用 DMA?
- 参考
SPI_TwoBoards_FullDuplexMaster_DMA_Init,用LL_DMA_*把缓冲区交给 DMA 搬运,CPU 零参与。
- 参考
小结
- LL 的 SPI 全双工主机 = 配 GPIO 复用 + 配 SPI 参数 + 选中断/DMA/轮询搬运 + 使能。
- 相比 HAL 的一行
HAL_SPI_TransmitReceive,LL 让你自己决定“字节怎么搬”,中断回调写法最贴近硬件。 - SPI 没有 LL 轮询示例,但把 IT 版本去掉中断、改轮询标志即可等价实现。
拓展应用
- 把 IT 改成纯轮询(查
LL_SPI_IsActiveFlag_TXE/RXNE),代码更简单便于理解。 - 改用 DMA 搬运长帧,提升吞吐(参考
_DMA_Init)。 - 接真实 SPI 器件(LCD、Flash、传感器),把回环逻辑换成器件寄存器读写。
- 增加超时判断,避免从机异常时主循环死等。