如何借助双缓冲保证单SPI连接的双带锁存IO扩展芯片数字输入读取一致性?现有方案有何潜在问题?
该双缓冲方案存在的核心问题
指针操作无原子保护,存在野指针风险
- 代码中
ready_data、shadow_data、temp的读写、指针交换都是拆分多步完成的非原子操作。在8/16位微控制器上,一个指针本身需要2~4个字节存储,读写时跨多个总线周期完成。 - 主循环正在执行指针交换流程时,若上层应用同步读取
ready_data的地址,一旦读取中途指针值被修改,上层会拼接出高低字节混杂的无效野地址,直接导致程序跑飞或内存随机篡改。 - 哪怕在32位内核上,指针交换的三步赋值流程(
temp = ready_data; ready_data = shadow_data; shadow_data = temp;)也不是原子操作,中途被中断或RTOS任务调度打断时,会出现指针指向错位,后续的读写操作全部错乱。
缺少轮次配对校验,易拼接跨采样时刻的错配数据
- 方案仅靠两个传输完成标志的先后出现判断双字节是否来自同一采样,没有任何匹配校验机制。
- 当主循环处理速度慢于SPI事务节奏,或是两次SPI读操作之间因为调度延迟插入了新的锁存动作时,会出现典型的错配时序:
第一次锁存的chip0值完成传输、置位chip0标志后,等待chip1传输的间隙里,系统触发了第二次锁存采样,后续传输回来的chip1值实际是第二次锁存的结果。此时主循环直接将第一次的chip0值、第二次的chip1值拼入shadow缓冲区,交换为上层可读的ready数据——两个值完全来自不同采样时刻,彻底破坏输入状态的一致性。
- 若某次SPI传输出现丢包、没有对应触发完成标志,方案没有任何错误检测逻辑,会直接将缓冲区中残留的上一轮旧值与新收到的字节拼接输出,同样产生跨周期的错配数据。
- 背靠背的连续SPI事务会让两个中断标志同时置位,主循环在单个周期内就完成两个值的写入和指针交换,但如果写入
di_state_chip0的过程中被更高优先级的中断打断、全局值被更新,最终拼入缓冲区的依然是跨轮次的错配值。
缓冲区写入无所有权隔离,存在半更新泄露风险
- 双缓冲区的写入是分步执行的:先向shadow缓冲区写入chip0字节,等收到chip1的传输完成标志后再写入第二个字节,最后才交换指针将缓冲区暴露给上层。
- 方案没有任何保护机制确保shadow缓冲区在写完完整的双字节值之前,不会被错误地交换为ready状态。一旦出现程序跳转异常、中断优先级错乱等问题提前触发指针交换,上层就会读到“新chip0值+旧chip1值”的半更新数据。
全局共享变量缺少访问约束
- 代码中的
di_state_chip0、di_state_chip1、两个中断标志都是被SPI中断服务程序、主循环、上层应用同时访问的全局变量,既没有添加volatile修饰避免编译器做值缓存优化,也没有在临界区保护下执行读写操作,极易出现主循环读到编译器缓存的旧值、中断标志被意外覆盖丢失等问题。
内容的提问来源于stack exchange,提问作者Professor Jimatura
相关产品推荐
相关产品推荐

