PIC16F877A中断临界区保护解决方案(无需禁用中断)
解决PIC16F877A中多字节计数器的中断数据不一致问题
你的问题本质是8位MCU上16位变量的非原子更新导致的竞态条件:counter--会被编译为多条8位操作指令,中断可能在这些指令之间触发,覆盖更新值,最终导致计数器数值错误。下面给你几个简单基础的解决方案,都不需要在if条件的业务代码执行期间禁用中断:
方案1:短暂禁用中断完成计数器的读取与更新
这是最直接的方法,只在读取和修改计数器的瞬间禁用全局中断(中断响应延迟微乎其微,几乎不影响系统),业务代码执行全程保持中断开启:
static int counter = 0; // 封装PIC的中断开关宏(以XC8编译器为例,可根据你的环境调整) #define DISABLE_INTERRUPTS() INTCONbits.GIE = 0 #define ENABLE_INTERRUPTS() INTCONbits.GIE = 1 void main() { while(1) { int current_count; // 原子读取当前计数器值,避免中断打断读取过程 DISABLE_INTERRUPTS(); current_count = counter; ENABLE_INTERRUPTS(); if (current_count > 0) { // 确认计数器未被中断修改后,原子更新值 DISABLE_INTERRUPTS(); if (counter == current_count) { counter = current_count - 1; ENABLE_INTERRUPTS(); // 执行你的业务代码,这里无需禁用中断 // --- 你的业务code --- } else { // 计数器已被中断修改,放弃本次更新,下次循环再处理 ENABLE_INTERRUPTS(); } } } } void InterruptSerial() { counter++; // --- 你的中断业务code --- }
方案2:使用8位计数器(如果业务允许)
如果你的计数器范围不需要超过255,可以把counter改成unsigned char类型。PIC16F877A的8位递减/递增操作会被编译为单条原子指令,从根源上避免竞态条件:
static unsigned char counter = 0; void main() { while(1) { if (counter != 0) { counter--; // 编译为单条原子指令,不会被中断打断 // --- 你的业务code --- } } } void InterruptSerial() { counter++; // 8位递增同样是原子操作 // --- 你的中断业务code --- }
方案3:无中断禁用的"读取-验证"循环
如果你完全不想操作中断开关,可以用循环读取计数器,直到连续两次读取的值一致,确保获取到的是未被中断干扰的完整数值,再进行更新:
static int counter = 0; void main() { while(1) { int count1, count2; // 循环读取直到两次值一致,说明读取过程未被中断打断 do { count1 = counter; count2 = counter; } while (count1 != count2); if (count1 > 0) { // 尝试更新,若更新前值未变则执行业务代码 if (counter == count1) { counter = count1 - 1; // --- 你的业务code --- } // 若值已被中断修改,本次循环跳过,下次读取最新值 } } } void InterruptSerial() { counter++; // --- 你的中断业务code --- }
这个方案的缺点是极端情况下可能会多循环几次,但胜在完全不用操作中断开关,适合对中断响应延迟要求极高的场景。
内容的提问来源于stack exchange,提问作者IDEN
相关产品推荐
相关产品推荐

