树莓派C++直接访问GPIO寄存器时如何实现写操作立即刷新无需轮询
树莓派3用户态GPIO寄存器写入同步解决方案
核心结论:你遇到的是ARM架构下外设写入的写缓冲延迟问题,不需要使用MRC/MCR指令,直接调用ARM架构的内存屏障指令即可解决,无需轮询回读电平。
问题根因
树莓派3使用的ARMv8-A架构中,即使是映射为非缓存属性的外设寄存器地址,CPU的写入操作会先进入总线写缓冲,不需要等待写入真正抵达GPIO外设硬件就会继续执行后续指令,你后续的软件延时逻辑会先于引脚电平更新运行,就会出现你观察到的高低电平时长错位的问题。
实现方案
在GPIO寄存器写入操作后插入数据同步屏障(DSB) 指令即可,用户态可以直接调用GCC内置的汇编语句触发该指令,不需要内核权限。
优化后的writegpio函数如下:
static uint32_t volatile *gpiopage; // 已经通过mmap /dev/gpiomem初始化 void PhysLib::writegpio (uint32_t value) { uint32_t mask = 0xFFF000; // [11:00] 输入引脚 // [23:12] 输出引脚 // 所有引脚经过反相器实现3.3V转5V gpiopage[GPIO_CLR0] = value; gpiopage[GPIO_SET0] = ~value; // 数据同步屏障,等待所有之前的写入操作完成抵达硬件 __asm__ __volatile__ ("dsb sy" : : : "memory"); }
指令说明
dsb sy是全系统数据同步屏障,会阻塞CPU直到所有之前发起的内存访问、外设寄存器访问操作全部完成,才会执行后续的代码"memory"约束是告诉编译器不要把屏障前后的内存访问操作重排序,避免编译优化导致逻辑错误- 你已经将
gpiopage声明为volatile,已经避免了编译器将寄存器访问优化掉,不需要额外修改
注意事项
MCR/MRC是操作ARM系统控制协处理器的特权指令,用户态没有权限调用,直接使用会触发段错误,不需要尝试这类指令。
内容的提问来源于stack exchange,提问作者mrieker
相关产品推荐
相关产品推荐

