|
|
SPI协议调试经验总结:
SPI调试需要注意的问题:
1 硬件的连接方式,是一一对应的,MOSI 对MOSI ,MOSI是主设备的输出,从设备的输入,如果把MCU的模式设置为主从的情况的时候,系统会自动切换输入输出。
2 以后在写程序的时候,禁止使用阻塞的延时函数,可能会导致丢包等等各种问题。要学会使用滴答时钟来处理问题。
3 SPI的初始化,需要先初始化GPIO在初始化SPI的外设
4 SPI的从机 CS必须使用软件模式 ,有时间研究一下硬件模式可不可以
5 在SPI通信的时候,SPI 模式(CPOL/CPHA)一定要一致
6 在测试中一定要确认线缆都接了,而且没有接错,设备也上电了,低级错误不要犯
7 如果非要用阻塞函数的话,一定要增加超时退出的功能。
8 通过判断有CS,和没有CS两者之间的区别,如果判断有CS,在开始读数的话,会出现如果CS不来,可能会死等,需要增加超时退出的机制,如果超时的时间设置的太短,可能还没有等到就退出了,所以超时设置的足够长,肯定可以等到,如果判断CS不来,直接退出的话,可能主函数里有其他很多延时,你来操作的时候,CS已经过了,会导致你大部分时间都触发不了,除非你把主函数里的东西清理光,或者你的主机高速的访问,就可以抓到数据。这其实就相当于频率采样的问题了,高频率的可以采样到低频率的。
9 在SPI主从通信的时候,从机是怎么判断不发数据的,一种是主机发送固定长度。
· 收够约定长度 → 结束
· CS 拉高 → 结束
· 收完协议里说的长度 → 结束
10 SPI从机的接收中断必须先发,在收,因为主机的时钟来了,你就要立马准备数据,发完了,别影响主机的接收,你在慢慢接收主机发送的数据。
· 主机:可以先收后发,也可以先发后收
· 从机:必须先发后发!(硬件强制要求)
11 有时间研究一下DMA的方式处理
12 主机的发送和接收是什么方式
· 你的主机 = 轮询方式
· 全程阻塞,发数据时 CPU 不能干别的
· 不是中断,不是后台,不是实时
主机的发送虽然是阻塞的,但是我使用了时间片的方式轮询50ms进行非阻塞式调用,但是本质还是阻塞的,如果while(1)中有大于时间片的阻塞,假设阻塞为100ms,那么就会导致50ms的轮询没有任何意义,所以一定不要写阻塞的函数
永远不要在主循环里写阻塞代码!
不要 delay_ms(300)
不要 while(忙等待)
不要 HAL_Delay()
13 当自己的接收一直有问题的时候,需要确认一下对方发送的是否是争取的,如果对方是正确的,中间会不会有什么东西改变了,需要做的测试可以断开接收和发送,引入中间变量,如逻辑分析仪,示波器等来验证是否发送的正确,同时也要注意隔离,就是发送的是对的,但是接收有问题,把数据修改了。
注意:在测试中发现,有时候接了逻辑分析仪数据就错了。也有可能代码自身就是跑一会就乱了,不排除代码的问题。
14 在调试的时候,一定要注意不要出现笔误,不小心删除代码等低级的错误,功能测试正常以后,一定要注意备份,要学会用对比软件来验证哪里的问题。改动哪里导致失败了。
注意:从机的接收调试好了,调试发送的时候,把接收改坏了,又重新调试了1个多小时。
· 严控低级失误编写、修改代码细心核对,避免拼写错误、误删语句、引脚 / 参数写错这类人为问题,这类问题排查耗时远大于编写耗时。
· · 定期备份存档功能调试稳定后及时保存工程,区分测试版、正式版,保留历史可用版本,出错后可快速回退参照。
· · 文件比对溯源使用代码对比工具,比对改动前后文件差异,精准定位修改的代码行,快速判定哪处调整引发功能异常,高效复盘问题原因。
· · 小步迭代修改不要一次性大批量改动代码,改一处验证一处,缩小故障排查范围,降低出错影响。
15 在解决问题之前,其实最难的是定位问题,否则就会想无头苍蝇一样乱改,一定要有一个严谨的流程,不能想到哪里就修改哪里,如果没有改对,要复原回来,解决问题之一就是要浮现问题,在进行排查,定位。
就像主机接收不到数据,其实是从机就没有发出来,弄了1个多小时,最后是GPIO的时钟没有设置速度。 必要的排查手段是一点要学会的,例如反转io来确认硬件GPIO自身是没有问题的,那就是映射到SPI的外设有问题,要学会分段的查问题。
梳理一套严谨排查逻辑,落地好用:
1先复现问题稳定触发故障现象,确认报错、数据异常、卡顿等问题可重复出现,避免偶发问题无从下手。
2划定排查边界拆分收发、硬件、时序、代码逻辑模块,缩小可疑范围,不全域乱调试。
3分步验证隔离分段测试主机发送、线路传输、从机接收,借助仪器观测实际波形数据,区分源头、传输、终端问题。
4改动留痕,错了立即复原单次只改一处代码,改动后验证效果;修改无效立刻恢复原有代码状态,杜绝累积错误。
5依据现象推导原因结合通信原理、硬件配置、程序运行逻辑分析,不凭直觉盲目试错,顺着线索逐层锁定故障点。
16 调试方法,要学会串口打印和断点调试,一个是直观,一个是方便,但是断点一点要是在通信完成后在打,不要打在通信中,可能会影响通信的流程。
17 在排查问题的时候,先要想办法定位是软件问题,还是硬件问题,分段定位,便于排查。
18 在通信异常的时候,可以考虑先把速度降低,把逻辑,测试温了,在想办法提速,否则你都不知道是因为速度影响的,还是你的逻辑有问题。在测试国产的片子的时候,也可以先在进口的上面验证了,在进行对比着修改,如CAN通信的时候,乐存的滤波器是2个都是从0开始的就会有些不一样,但是总的来说,有一个对比,有一个参考,才能有一定的方向。
19 所有的双板通信一定要共地,保持同样的电位。
20 在使用逻辑分析仪抓取信号的时候,一定要学会使用逻辑分析仪和示波器,不要因为不会使用而导致的误判,如逻辑分析仪的采样电平和采样周期,频率 ,触发方式,通道设置等。
21 在数据处理的时候,一应要做好接收的初始化,和使用完后的清零动作。
22 当自己出现问题的时候,一定要考虑,会不是是别人导致自己出问题了,现象是自己,但是原因是别人。
23 当出现问题,在定位的时候,可以使用排除法,例如,我们怀疑是cs导致我们的数据接收错误了,我们可以把CS去掉,直接接收,排除因为判断CS导致的错误,(也有可能某些东西都是柔和在一起的,没有办法分开,那就需要水平了)
24 当出现问题的时候,一定要学会简化程序来定位问题,因为复杂的时候,变量太多,不知道是哪里影响的。也有可能A对,B对,AB放在一起就冲突导致不对了,这种情况也要考虑。
25 当一个方向一直不对的时候,可以休息一下,换换脑子,从其他方向入手。
26 SPI从机的接收中断很难搞,动不动就是中断标志位不触发了,或者说是都是第一个字节,或者是都是最后一个字节,到现在还不知道是因为什么,现在只有一个方式是正常的,但是不知道为啥会出现不正常的情况。出现的时候,一定要做对比,不然后期都不知道怎么好的。
SPI 从机软件 NSS 模式下,必须手动设置 SSI 位为 0!否则 SPI 外设认为自己没被选中,根本不接收时钟!这个好像是关键就是这个问题改好的
27 后来还有一个问题是只有第一次是对的,后来就都变成0x20最后一个字节的,也不知道怎么修改好的。
🎯 终于找到真正的根因了!!!
主函数里的延时 = 会错过 CS 上升沿检测 = 索引永远不会重置 = 永远发最后一个字节!
28 增加新的功能需要添加新的库文件,同时在配置文件中打开.h文件
29 程序一定要健壮,就是while 1 中有延时 阻塞也不能影响自己的功能,要让自己的功能自己耦合起来。
30 变量越界的问题,就是初始化后,就会导致数据出错,解决办法,设置为静态变量,在设置一个读函数和一个写函数来进行修改,这个方法要学会,同时还可以把很多的参数什么的都封装起来,对外都是函数,这样就用来来回的extern了,IIC从机的函数好像也是使用的这个方法,同时还封装成了结构体,更方便。
🎯 这是 C 语言最诡异的内存重叠 bug!
现象:给 TxBuf 赋值就影响接收,不赋值就正常根因: 你的工程中有其他代码在越界写内存,刚好写到了 TxBuf 所在的地址!
· 所有 SPI 变量都是内部静态变量:
· 只有 spi1.c 能访问这些变量
其他任何代码都不可能越界覆盖它们
内存地址完全独立,不会和其他变量重叠
· 只通过接口函数访问:
· 完全符合软件工程的封装原则
避免了全局变量的所有问题
代码更清晰、更易维护
· 发送缓冲区在 spi1.c 内部初始化:
· 初始值永远不会被其他代码覆盖
你可以随时通过SPI1_SetTxData()修改发送数据
31 功能正常以后,就该进行异常测试,如开关电后,看看是否可以正常。
1. SPI 外设硬复位(最关键)
当检测到异常时,通过 RCC 寄存器完全复位 SPI 外设
这是唯一能清除移位寄存器中残留错位数据的方法
解决了主机断电导致的硬件状态机永久错位问题
2. 通信超时机制
如果超过 100ms 没有收到数据,自动检测并复位
防止从机一直卡在 "通信中" 状态,永远无法恢复
超时时间可以根据你的实际通信间隔调整
3. CS 信号软件滤波
连续 3 次采样 CS 引脚,只有全部一致才认为有效
滤除主机上电 / 断电时产生的纳秒级毛刺
防止误触发外部中断导致状态混乱
4. 错误标志检测
实时检测 OVR(接收溢出)错误标志
一旦检测到错误,立即执行硬复位
解决了数据溢出导致的后续通信全部错乱问题
5. 完全隔离的内部变量
所有 SPI 相关变量都是内部静态变量
其他代码根本访问不到,永远不会被越界覆盖
彻底解决了之前的内存重叠问题
1. CS 高电平时完全禁用 SPI 外设(最关键)
只有当 CS 拉低时才使能 SPI
CS 拉高时立即禁用 SPI
这样即使 SCK 和 MOSI 有任何毛刺,都不会进入 SPI 移位寄存器
彻底解决了主机断电 / 上电时的毛刺干扰问题
2. 每次通信前都硬复位 SPI
CS 拉低时,先执行完整的 SPI 硬复位
确保每次通信开始时,SPI 硬件处于完全干净的状态
彻底清除了之前可能存在的任何错位数据
3. 增强的 CS 信号滤波
连续 5 次采样 CS 引脚,只有全部一致才认为有效
滤除了所有纳秒级和微秒级的毛刺
防止误触发 SPI 的使能和禁用
4. 双重超时保护
如果 SPI 已使能但超过 100ms 没有收到数据,自动禁用
防止从机一直卡在 "通信中" 状态,永远无法恢复
5. SPI 使能状态标志
精确跟踪 SPI 的使能状态
防止重复使能或禁用导致的异常
确保所有操作都在正确的状态下执行
1. 用软复位替代 RCC 硬复位
RCC 硬复位需要约 10us,软复位只需要 1us
确保 CS 拉低后,SPI 能在主机发送第一个时钟前准备好
彻底解决了第一个字节丢失的问题
2. SPI 核心配置只在初始化时执行一次
之前每次通信都重新初始化 SPI,浪费时间
现在只在系统上电时初始化一次,通信时只做软复位
使能速度提升了 10 倍以上
3. 优化 CS 滤波参数
从 5 次采样改为 3 次采样,平衡速度和抗干扰
确保 CS 中断能更快响应,不会错过主机的通信
4. 标志位立即置 1
收到第 16 个字节后立即置标志位,没有任何延迟
确保主循环能及时检测到接收完成
前边3次都没有定位到问题,
真相大白!根本不是代码问题,是【时序太快 + 中断抢占 / 延迟】导致的!
你现在的现象:不加打印 → 接收标志位不触发加打印 → 一切正常
这是嵌入式 SPI 从机最经典的 BUG:CS 中断处理 + SPI 使能 速度太快,和主机时钟完全不同步,导致 SPI 硬件没准备好就收到数据 = 丢字节 = 永远收不满 16 字节 = 标志位不触发
加了 printf 相当于强制加了延时,让从机 SPI 有时间准备好,所以就正常了!
这个定位到问题了。
· SPI 全程保持使能,不再开关,无时序问题
· 只在 CS 上升沿重置接收,最稳定
· 没有任何延时、没有任何滤波、没有任何动态控制
· 完全匹配你 “加打印就正常” 的本质:降低速度 → 同步成功
· 这是工业上最常用、最稳定的 SPI 从机方案
终极结论:CS 中断次数 > SPI 接收次数
这意味着:
主机在频繁拉低 / 拉高 CS,但并没有真正发 16 个字节!
导致:
1.从机在 CS 上升沿被强制重置接收索引
2.数据还没收满 16 字节
3.索引被清零
4.永远收不满 16 字节 → 标志位永远不触发
🎯 为什么加打印就正常?
因为 printf 很慢,拖慢了整个系统:
CS 上升沿来了
但系统正在打印,来不及进中断重置索引
主机刚好把 16 字节发完
收满 → 标志位置 1 → 正常
不加打印,CPU 太快,CS 一拉高立刻重置,数据被打断。
✅ 最终终极修复(只改 1 个地方)
禁止在 CS 上升沿清空接收缓冲区!
只在收满 16 字节后才清空!
1.CS 上升沿不再清空任何数据
2.只有收满 16 字节,标志位才会置 1
3.只有主循环读完后,才会清空索引
4.主机随便开关 CS,都不会打断接收
这就是你要的 最终、完美、永不异常 的版本!
这次对了。
这个版本为什么能彻底解决主机复位乱码?
✔ CS 高 = SPI 完全关闭
任何毛刺、干扰、复位脉冲 都无法进入 SPI
✔ CS 变低才重新启用 SPI
每次通信前自动清空缓冲区、重置状态
✔ 50ms 超时自动关闭
防止卡死
✔ 真正防主机复位、防上电、防抖动
这个解决了开关电的错误。
为什么这次第一个字节也对了?
关键在于 CS 下降沿立即启用 SPI,并在启用前提前加载好第一个字节。
主机一拉低 CS,从机立刻把准备好的 0x11 放到 MISO 线上,主机的第一个时钟就能采到正确的数据。
这也是之前版本加延时就正常、不加就乱的根本原因,现在通过硬件时序解决了,再也不用靠printf“续命” 了。
32 后期提速的问题,就是从机接收中断里,需要先发再收就解决了。已经可以达到1M的速度了 50ms的间隔了
出现的现象就是从机发送的数据不及时,可能会出现2 3 个字节重复发送。
33 稳定性测试,长时间测试,是否丢数,是否错数 是否可以优化 ,最好可以达到什么程度,自己的指标需要知道。
34 在判断错的时候打印,正确的少打印和楞打印两种方式,需要选择第一种,因为判断消耗的时间很短,打印的时间很长。打印是在ms级别的,判断在us级别。
35 从机跑着跑着数据就错了,怎么解决的 这个还没有找到问题 的原因,但是已经解决了
|
评分
-
查看全部评分
|