简介这是一份面向嵌入式初学者与STM32开发者的SPI双板通信例程聚焦两块STM32板子之间通过SPI协议完成主从数据交互的完整实现并附带液晶显示相关代码帮助读者理解同步串行通信在实际项目中的落地方式。资源包共60个文件约450KB包含c与h源码、uvproj工程文件、map与lst编译列表、hex与axf可执行文件、sct分散加载文件及readme说明等覆盖从工程配置到编译输出的完整链路代码中配有详细注解便于对照寄存器配置理解每一步操作。内容涉及SPI工作模式选择、时钟极性与相位配置、CR1/CR2/SR/DR等寄存器使用、GPIO复用初始化以及液晶屏命令与数据传输等知识点。目前已有3086人学习下载适合希望从零掌握STM32主从通信、并借助注解快速排错与二次开发的读者参考。1. 两块板子之间跑 SPI从“能亮屏”到“能通信”的那道坎很多人第一次调 STM32 的 SPI都是拿一块板子驱动一块液晶屏接线上电、刷个例程屏亮了就觉得 SPI 通了。可一旦把场景换成两块 STM32 板子互连——一块发、一块收问题立刻冒出来主机发出去的数据从机收不到从机回的应答主机读回来全是 0xFF时钟极性怎么设都不对NSS 到底软件管还是硬件管折腾半天示波器上波形看着挺正常逻辑就是不通。这份“两块板子之间的 STM32 SPI 通信例程”针对的正是这个场景它不是单机驱动外设的演示而是双机对等通信的完整代码带详细注解并且保留了液晶显示这条验证链路让你能直观看到收发结果。适合已经点过灯、调过串口想真正把 SPI 主从机制吃透的嵌入式从业者。下面我按自己拆包复现的顺序把这份例程讲清楚。2. 先搞懂双机 SPI 的物理层与模式匹配为什么单机例程直接搬会翻车单机驱动液晶时SPI 参数是围着屏的手册转的你只要满足屏的时序就行。双机通信不一样两端都是 STM32参数必须双向对齐任何一端设错通信直接死。这一章先把物理层和模式匹配讲透再落到具体配置。2.1 四线制的接线逻辑与 NSS 的两种管法标准 SPI 四线是 SCK、MISO、MOSI、NSS。双机互连时主机和从机是交叉对接主机引脚方向从机引脚说明SCK输出SCK时钟由主机产生MOSI输出MOSI主机发、从机收MISO输入MISO从机发、主机收NSS/CS输出NSS/CS片选低有效新手最容易翻车的是 NSS。STM32 的 SPI 有硬件 NSS 和软件 NSS 两种模式。硬件 NSS 在多主机场景下会自动拉低拉高但在双机点对点里如果从机 NSS 配成硬件输入却没被真正拉低从机根本不会进入通信状态主机发再多也是石沉大海。我一般直接两端都配软件 NSS主机用一个普通 GPIO 手动拉低拉高做片选从机把 NSS 内部管理关掉SSM1、SSI1这样最可控也最好排查。提示从机如果配了硬件 NSS 输入而片选线悬空或没拉低现象就是“主机波形完美、从机毫无反应”这个坑我见过太多次。2.2 CPOL/CPHA 四种模式两端必须逐位对齐SPI 有四种模式由 CPOL时钟极性和 CPHA时钟相位组合模式CPOLCPHA采样沿Mode 000第一个上升沿Mode 101第二个下降沿Mode 210第一个下降沿Mode 311第二个上升沿双机通信的铁律是主机和从机的 CPOL、CPHA 必须完全一致。单机驱动液晶时你按屏手册选双机时两端都是你自己写的代码反而容易一边抄了个 Mode 0、另一边抄了个 Mode 3波形看着有 clock数据却全错位。我一般统一用Mode 0因为它在示波器上最直观——空闲低电平第一个上升沿采样肉眼就能对上数据位。2.3 时钟分频与数据位宽别让从机跟不上主机 SCK 频率由分频系数决定比如 72MHz 总线、分频 8就是 9MHz。双机通信里从机接收是靠 SCK 边沿触发的如果主机跑太快、从机主频低或者中断占用多从机可能来不及把 DR 里的数据读走导致 OVR溢出标志置位后续数据全乱。常见做法是先降速分频设大一点比如 64 或 128等通信稳定了再往上提。数据位宽两端也要一致一般用 8 位如果一端 8 位一端 16 位帧就对不上。// 主机 SPI 初始化关键片段以常见 HAL 风格为例 hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; // 主机模式 hspi1.Init.Direction SPI_DIRECTION_2LINES; // 全双工 hspi1.Init.DataSize SPI_DATASIZE_8BIT; // 8 位数据两端必须一致 hspi1.Init.CLKPolarity SPI_POLARITY_LOW; // CPOL0 hspi1.Init.CLKPhase SPI_PHASE_1EDGE; // CPHA0即 Mode 0 hspi1.Init.NSS SPI_NSS_SOFT; // 软件 NSS片选自己管 hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_64; // 先降速 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; // 高位先出两端一致这段配置里Mode决定主从角色DataSize、CLKPolarity、CLKPhase、FirstBit这四项是两端必须逐项对齐的任何一项不一致都会导致数据错位或收不到。BaudRatePrescaler只影响主机从机不需要设分频但要从机主频足够高。NSS设成SPI_NSS_SOFT后片选就交给普通 GPIO 控制后面收发时手动拉低拉高。3. 收发流程与液晶显示验证把“看不见的通信”变成“看得见的结果”参数对齐只是前提真正让通信跑起来还得把收发时序、片选控制和数据校验串起来。这份例程保留液晶显示价值就在于它把抽象的收发结果变成了屏上可见的数字调试时不用一直盯着示波器猜。3.1 主机发送与从机接收的完整时序SPI 是全双工主机每发一个字节同时会从 MISO 读回一个字节。双机通信的典型流程是主机拉低片选 → 发送数据 → 等待发送完成 → 读回从机应答 → 拉高片选。从机则要在片选有效期间把接收到的数据存下来并把要回的数据写进 DR。// 主机发送一个字节并读回从机应答 uint8_t SPI_Master_Transfer(uint8_t txData) { uint8_t rxData 0; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); // 拉低片选 HAL_SPI_TransmitReceive(hspi1, txData, rxData, 1, 100); // 全双工收发 HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); // 拉高片选 return rxData; // 返回从机发来的数据 }这里HAL_SPI_TransmitReceive是关键它同时完成发和收超时设 100ms 防止死等。片选在传输前后手动翻转保证从机只在有效窗口内响应。如果只用HAL_SPI_Transmit主机就收不到从机回的数据这是双机通信和单机发送最大的区别。从机侧一般用中断或轮询接收// 从机接收中断回调HAL 风格 void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI2) { rxBuffer[rxIndex] spiRxData; // 存接收数据 spiTxData rxBuffer[rxIndex - 1] 1; // 回一个递增应答便于验证 HAL_SPI_TransmitReceive_IT(hspi2, spiTxData, spiRxData, 1); // 继续收发 } }从机回一个“收到值 1”的应答主机收到后就能判断链路是否正常。这种带校验的应答机制比单纯发固定值靠谱得多一旦某字节对不上立刻能定位。3.2 用液晶显示把收发结果可视化例程里液晶显示这条链路本质是把主机收到的应答、从机收到的命令实时打到屏上。常见做法是主机每发一轮就把txData和rxData拼成字符串刷到 LCD。char dispBuf[32]; sprintf(dispBuf, TX:%02X RX:%02X, txData, rxData); LCD_ShowString(10, 40, dispBuf); // 刷到液晶指定坐标sprintf把发送值和接收值格式化成十六进制字符串LCD_ShowString负责显示。这样你一眼就能看出主机发 0x01从机回 0x02下一轮发 0x02回 0x03。如果屏上 RX 一直是 0xFF 或 0x00说明从机没响应或模式没对齐排查方向立刻明确。液晶在这里不是装饰而是最省事的调试窗口比串口打印还直观因为不用切窗口。3.3 数据校验与帧格式别让偶发错位骗过你裸 SPI 没有帧头帧尾也没有校验一旦错位后面全错。工程上常见做法是加一个简单帧格式帧头如 0xAA 长度 数据 校验和。主机发一帧从机校验通过才回 ACK否则回 NAK。字段长度说明帧头1 字节固定 0xAA用于对齐长度1 字节数据段字节数数据N 字节实际载荷校验1 字节前面所有字节异或校验和用异或最简单实现快对短帧足够。如果数据量大可以换成 CRC8。加了帧格式后偶发的错位会被帧头重新对齐不会一路错到底。这一步在双机通信里几乎是必做项单机驱动屏时反而用不上。4. 避坑与排查双机 SPI 不通时按这个顺序查双机 SPI 的坑集中在“参数、时序、硬件”三块。下面这几条是我自己踩过、也帮别人定位过的典型问题按现象、原因、解决写清楚。4.1 现象主机波形正常从机完全没反应原因从机 NSS 配置错误最常见是配了硬件 NSS 输入但片选线没真正拉低或者从机 SPI 根本没使能。另一个可能是从机引脚复用没配对SCK/MOSI 还挂在默认 GPIO 上。解决从机改软件 NSSSSM1、SSI1确认片选线在传输期间确实被拉低用万用表或示波器量从机 SCK、MOSI 引脚确认有信号进来检查 GPIO 复用配置确保引脚真的连到了 SPI 外设。4.2 现象能收到数据但全是 0xFF 或 0x00原因0xFF 通常是 MISO 被拉高、从机没驱动0x00 可能是从机没写 DR 或模式错位。如果主机读回固定值先怀疑从机没真正参与收发。解决确认从机在片选有效期间执行了发送写 DR检查两端 CPOL/CPHA 是否一致把主机速率降到最低再试排除从机来不及响应。4.3 现象前几个字节对后面开始错位原因从机接收溢出OVR数据没及时读走或者主机片选拉高太早最后一个字节还没发完。解决从机用中断或 DMA 接收别用轮询死等主机发送后等TXE和BSY标志都清了再拉高片选降低 SCK 频率。4.4 现象单步调试正常全速跑就丢数据原因单步时 CPU 慢从机有充足时间响应全速时中断延迟或主循环占用导致从机错过采样窗口。解决从机接收走中断或 DMA别放在主循环里轮询提高从机中断优先级主机加适当延时或改用硬件流控如果支持。4.5 现象液晶显示乱码或刷新卡顿原因液晶和 SPI 通信共用总线却没做互斥或者sprintf在中断里调用导致重入。解决液晶刷新放在主循环别放中断如果液晶和从机 SPI 共用外设加互斥标志sprintf换成更轻量的格式化函数避免栈溢出。注意排查顺序建议从硬件接线 → NSS → 模式 → 速率 → 帧格式由粗到细别一上来就改代码逻辑。5. 进阶把双机 SPI 做成稳定链路的一个习惯例程跑通只是起点真正上线要考虑的是稳定性。我自己的习惯是任何双机 SPI 链路先跑通“固定值收发”再跑通“带帧头校验”最后才接业务数据。这三步走完基本不会出现“演示正常、现场翻车”的情况。一个具体技巧是加超时重传。主机发一帧后等从机 ACK超时没收到就重发重发三次仍失败就报错。这样偶发的干扰或从机忙不会让整条链路卡死。uint8_t SPI_SendWithRetry(uint8_t *frame, uint8_t len, uint8_t retry) { while (retry--) { SPI_Master_SendFrame(frame, len); // 发一帧 if (SPI_WaitAck(50) ACK_OK) { // 等 ACK超时 50ms return 1; // 成功 } } return 0; // 三次都失败 }retry控制重传次数SPI_WaitAck内部等从机回 ACK 或超时。这个模式在工业现场特别有用因为 SPI 本身没有应答机制全靠上层补。验证方法上我一般会做一个回环测试主机发 0x00 到 0xFF 共 256 个字节从机原样回传主机比对。全部一致才算链路合格。这个测试能一次性暴露模式错位、溢出、片选时序等大部分问题。测试项方法合格标准单字节收发发固定值读应答应答 发送值 1全量回环发 0x00~0xFF256 字节全部一致压力测试连续发 10000 帧无丢帧、无错位异常恢复中途拔插片选线能重传恢复从那以后我每次调双机 SPI都强制先跑一遍 256 字节回环再上业务逻辑。这个习惯帮我省了太多现场返工的时间。希望帮到你。本文还有配套的精品资源点击获取