You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:12:01