STM32H743 SPI DMA双机通信从机端实战:从配置到避坑

发布时间:2026/10/28 23:13:19
STM32H743 SPI DMA双机通信从机端实战:从配置到避坑 简介这份源码包面向已掌握STM32与SPI基础的嵌入式开发者聚焦STM32H743从机端SPI DMA双机通信的完整实现可用于工业控制、板间高速数据交互等场景的二次开发与学习参考。压缩包共1355个文件约21.65MB以599个c源文件与321个h头文件为主体配合147个icf、113个s启动文件及21个sct分散加载文件覆盖Keil、IAR等多套工程配置另含数学与PDM滤波库、hex固件及说明文档便于直接编译验证。资源围绕SPI接口初始化、DMA收发配置、传输完成回调、数据包封装校验与超时错误处理等环节展开从机侧接收响应逻辑完整可帮助读者理解无CPU干预的连续数据流传输机制并对照手册排查时钟极性、相位与DMA地址映射等常见问题。目前已有330人学习下载适合需要快速搭建双机通信验证环境的开发者参考。1. STM32H743 SPI DMA 双机通信从机端为什么值得单独啃两块 STM32H743 之间用 SPI 做板间通信主机好写从机才是真正让人头大的那一端。主机想发就发时钟由它掌控从机只能被动等 NSS 拉低、等 SCK 边沿、在极短的窗口里把数据塞进 DR 或者从 DR 取走稍慢一步就 OVR 溢出读到的全是上一帧的残影。标题里的「SPI DMA 双机通信从机」要解决的就是这件事让 H743 的 SPI 从机模式配合 DMA把收发从 CPU 轮询里彻底解放出来做到高吞吐、低抖动、不丢帧。这套方案适合谁做多轴运动控制板间总线、做采集前端与主控分离、做双核或多板协同的嵌入式工程师。H743 主频 480MHz、SPI 时钟可以跑到很高但 SPI 从机模式下外设时钟不能超过内核时钟的一半这个约束决定了你 DMA 配置的节奏。下面按「先立住原理、再动手复现、最后避坑」的顺序讲透源码结构以 HAL 库 CubeMX 生成的框架为基准你可以直接对照自己的工程改。2. SPI 从机 DMA 的底层逻辑与 CubeMX 配置2.1 从机模式下 SPI 的收发时序到底卡在哪SPI 从机的核心矛盾是数据到达和离开的时刻完全由主机时钟决定而从机没有主动权。全双工时主机每发一个字节同时也在从 MISO 上收一个字节从机必须在 SCK 的每个采样边沿前把要发的数据准备好同时在移位寄存器满之前把收到的数据读走。用轮询你就是在跟主机的时钟赛跑用中断每字节一次中断高波特率下 CPU 直接被中断淹没。DMA 的价值就在这里SPI 的 RXNE 和 TXE 事件可以直接触发 DMA 请求DMA 控制器替你把数据从 DR 搬到内存、从内存搬到 DRCPU 只在整帧收发完成时被叫醒一次。H743 的 DMA1/DMA2 都支持 SPI 外设请求SPI1~SPI6 的请求映射在不同 DMA 流上选错流就是「配置全对但一个字节都不动」的经典翻车。从机模式还有一个容易被忽略的点NSS 的管理方式。硬件 NSS 由外部主机拉低从机自动进入使能软件 NSS 需要你在代码里手动控制 SSI 位。双机通信里主机通常用软件片选控制 GPIO从机这边建议用硬件 NSS 输入模式让外设自己判断帧边界减少软件干预。2.2 CubeMX 里从机 SPI 的关键参数怎么设打开 CubeMX选好 H743 的具体型号进入 SPI 配置页。模式选 Full-Duplex Slave这一项选错成 Master 后面全白搭。下面是几个必须盯死的参数参数推荐值说明Frame FormatMotorola标准 SPITI 模式少用Data Size8 Bits与主机一致改 16 位要两边同步Clock Polarity (CPOL)Low与主机严格一致Clock Phase (CPHA)1 Edge与主机严格一致NSSHardware NSS Input从机用硬件片选Baud Rate Prescaler不适用从机时钟由主机给此项无效First BitMSB First与主机一致CPOL/CPHA 组合是双机通信第一大玄学来源。主机 CPOL0、CPHA0 意味着空闲低电平、第一个边沿采样从机必须完全一致否则你收到的数据会整体移位一位看起来像「数据全错但偶尔对」。我一般会在示波器上先确认 SCK 空闲电平和第一个跳变沿再回 CubeMX 对参数。DMA 配置在 DMA Settings 标签页。给 SPI 的 RX 和 TX 各加一个 DMA 请求方向分别是 Peripheral To Memory 和 Memory To Peripheral。模式选 Normal 还是 Circular 取决于你的协议定长帧用 Normal每帧重新启动流式数据用 Circular但要注意缓冲区翻转的同步问题。优先级建议 RX 高于 TX因为接收溢出是不可恢复的发送慢一点只是延迟。NVIC 里把对应的 DMA 流中断打开SPI 全局中断可以不开DMA 模式下用不上。生成代码后HAL 会把MX_SPI1_Init和MX_DMA_Init都准备好注意MX_DMA_Init必须在MX_SPI1_Init之前调用否则 DMA 时钟没开SPI 初始化里的 DMA 关联会失败。3. 从机端 DMA 收发代码落地从初始化到整帧回调3.1 启动一次从机 DMA 收发的完整流程从机不能主动发起传输所以代码结构是「先挂起接收等主机来时钟」。标准做法是用HAL_SPI_TransmitReceive_DMA同时挂起收发让 DMA 在 NSS 拉低、时钟到来时自动搬运。下面是我常用的启动函数/* 从机端挂起一次全双工 DMA 收发 * hspi: SPI 句柄 * tx_buf: 待发送数据从机要回给主机的 * rx_buf: 接收缓冲存主机发来的 * len: 本帧字节数收发等长 */ HAL_StatusTypeDef SPI_Slave_StartTransfer(SPI_HandleTypeDef *hspi, uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len) { HAL_StatusTypeDef ret; /* 关键先启动接收 DMA再启动发送避免第一个字节丢失 */ ret HAL_SPI_TransmitReceive_DMA(hspi, tx_buf, rx_buf, len); if (ret ! HAL_OK) { /* 记录错误码常见为 HAL_BUSY上一帧还没结束 */ Error_Handler(); } return ret; }逻辑说明HAL_SPI_TransmitReceive_DMA内部会先使能 TX DMA 再使能 RX DMA最后置 SPE 位启动 SPI。对从机来说SPE 置位后外设就等 NSS 和时钟了。参数上len必须和主机约定的一致从机不知道主机要发多少只能靠协议固定帧长或者用 NSS 上升沿判断帧结束。如果帧长可变就得改用 Circular 模式加空闲检测复杂度上一个台阶建议双机通信初期先固定帧长。3.2 收发完成回调里该做什么、不该做什么DMA 传输完成会触发HAL_SPI_TxRxCpltCallback这是你处理整帧数据的唯一入口。注意这个回调在中断上下文里跑不能放耗时操作/* 整帧收发完成回调中断上下文 */ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { /* 只做标记和缓冲区切换解析放到主循环 */ frame_ready 1; /* 双缓冲切换到另一块 buffer 继续挂起接收 */ if (active_buf 0) { SPI_Slave_StartTransfer(hspi, tx_buf1, rx_buf1, FRAME_LEN); active_buf 1; } else { SPI_Slave_StartTransfer(hspi, tx_buf0, rx_buf0, FRAME_LEN); active_buf 0; } } }逻辑说明回调里立刻重新挂起下一帧保证从机始终处于「等待接收」状态这是不丢帧的关键。参数FRAME_LEN是协议约定的固定帧长。缓冲区切换用双缓冲主循环处理rx_buf0时 DMA 在填rx_buf1互不干扰。如果你在回调里直接解析协议、做浮点运算下一帧的 DMA 启动就会被推迟主机连续发帧时必然溢出。3.3 错误回调与溢出恢复从机最容易出的错是 OVROverrun现象是HAL_SPI_ErrorCallback被调用错误码HAL_SPI_ERROR_OVR。溢出后 SPI 外设的 DR 里是脏数据必须清标志再重新挂起void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { /* 清 OVR 标志读 DR 再读 SR 的固定序列 */ __HAL_SPI_CLEAR_OVRFLAG(hspi); /* 中止当前 DMA重新挂起 */ HAL_SPI_DMAStop(hspi); SPI_Slave_StartTransfer(hspi, tx_buf0, rx_buf0, FRAME_LEN); ovr_count; /* 调试用计数器正常应为 0 */ } }逻辑说明__HAL_SPI_CLEAR_OVRFLAG是 HAL 提供的清溢出宏内部执行读 DR、读 SR 的序列。HAL_SPI_DMAStop停掉可能还在跑的 DMA 流避免状态机卡死。ovr_count在调试阶段非常有用如果它持续增长说明你的回调处理太慢或者主机发得太快需要优化而不是掩盖。4. 双机联调避坑从机端 5 个血泪教训4.1 现象数据整体移位一位偶尔全对原因CPOL/CPHA 与主机不一致或者主机在 NSS 拉低后第一个时钟沿之前就发了数据。从机在 NSS 有效后的第一个采样沿才锁存如果主机时序偏早首位会丢。解决示波器同时抓 SCK 和 MOSI确认 NSS 拉低到第一个 SCK 沿之间有足够建立时间。CubeMX 里 CPHA 选 1 Edge 还是 2 Edge 必须和主机逐位对齐改完两边一起烧录验证。4.2 现象第一帧正常后续帧全错或 OVR原因回调里没有及时重新挂起 DMA或者重新挂起时上一帧的 DMA 流还没完全关闭HAL_SPI_TransmitReceive_DMA返回 HAL_BUSY。解决在回调里先确认HAL_SPI_GetState回到 READY 再启动下一帧如果频繁 BUSY检查是不是在回调里做了串口打印这类阻塞操作。我一般会把重新挂起放在回调最前面解析逻辑全部挪到主循环。4.3 现象DMA 配置全对但一个字节都不动原因DMA 流选错。H743 的 SPI1_RX 固定在 DMA2 Stream 0 或 Stream 2取决于具体型号的请求映射表SPI1_TX 在 DMA2 Stream 3 或 Stream 5。CubeMX 一般会自动分配但手动改过之后容易错位。解决对照参考手册的 DMA 请求映射表逐项核对或者干脆让 CubeMX 重新生成 DMA 配置。另一个隐蔽原因是MX_DMA_Init调用顺序在MX_SPI1_Init之后DMA 时钟没开。4.4 现象低速正常波特率一高就丢帧原因从机 SPI 外设时钟超过内核时钟一半或者 DMA 传输带宽被其他外设抢占。H743 的 SPI 从机模式有明确时钟上限超了就是随机丢。解决查数据手册确认 SPI 内核时钟源和分频从机模式下SPI_ker_ck不能超过spi_ker_ck上限的一半。同时检查 DMA 仲裁优先级把 SPI 的 DMA 流优先级设为 Very High避免被 LCD、SDMMC 这类大带宽外设挤掉。4.5 现象NSS 用软件模式帧边界判断错乱原因软件 NSS 下从机靠 SSI 位手动控制主机片选拉低拉高的时刻和从机 SSI 操作不同步导致多收或少收字节。解决从机一律用硬件 NSS 输入模式让外设自己根据 NSS 引脚电平判断帧起止。如果 NSS 引脚被其他功能占用换一个 SPI 实例别在软件 NSS 上硬扛。5. 用 DMA 双缓冲 空闲检测把可变长帧也吃下来固定帧长能解决大部分双机通信场景但真实协议里经常有「命令帧短、数据帧长」的混合情况。这时候纯 Normal 模式就不够了我的做法是 Circular DMA 加 SPI 空闲检测IDLE配合双缓冲让从机既能收定长也能收变长。思路是这样RX 用 Circular 模式持续搬运到一块大缓冲TX 也用 Circular 但发送内容由协议层动态填充。利用 H743 SPI 的 TSIZE 和 DMA 的 NDTR 寄存器在 NSS 上升沿触发的外部中断里读取当前 NDTR算出这一帧实际收了多少字节。下面是一个简化实现/* NSS 上升沿外部中断帧结束计算本帧长度 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin NSS_PIN) { /* NDTR 是剩余待搬运数总数减剩余就是已收字节 */ uint16_t received RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(hdma_spi1_rx); /* 记录本帧长度主循环按长度解析 */ last_frame_len received; frame_ready 1; /* 重置 DMA 计数器准备下一帧 */ __HAL_DMA_DISABLE(hdma_spi1_rx); hdma_spi1_rx.Instance-NDTR RX_BUF_SIZE; __HAL_DMA_ENABLE(hdma_spi1_rx); } }逻辑说明__HAL_DMA_GET_COUNTER返回 NDTR 当前值即还没搬的字节数总数减它得到已收长度。NSS 上升沿表示主机结束本帧此时读 NDTR 最准。重置 NDTR 前要先 disable DMA否则写 NDTR 可能不生效。参数RX_BUF_SIZE要大于协议里最长帧留 20% 余量。验证方法主机端发不同长度的帧从机打印last_frame_len和主机发送长度逐一比对。如果长度总是偏大检查 NSS 上升沿中断有没有被其他中断延迟如果偏小检查 Circular 模式下 DMA 有没有在帧中间回绕。这个方案比固定帧长复杂但一旦跑通协议灵活性提升明显。我自己的习惯是双机通信第一版永远先用固定帧长把链路跑通确认 DMA、NSS、CPOL/CPHA 全部无误后再上可变长。跳过第一步直接搞可变长出问题时你分不清是时序错还是长度算错排查成本翻倍。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询