首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【嵌入式】SDCC中的原子操作详解

【嵌入式】SDCC中的原子操作详解

作者头像
LuckiBit
发布2025-08-15 08:45:02
发布2025-08-15 08:45:02
2690
举报
文章被收录于专栏:C语言C语言

1. 什么是原子操作?为什么需要原子操作?

  • 定义 原子操作(atomic operation)指执行过程中不会被中断或打断的操作,要么全部完成,要么完全不做,不会产生中间状态。
  • 目的 保证多任务或中断环境下共享数据的一致性和完整性,防止数据竞争和破坏。
  • 背景 在8051单片机中,指令是一条条顺序执行,中断可随时发生,如果一个操作由多条指令组成,被中断打断后继续执行,会造成数据不一致。

2. 不使用原子操作的后果与示例

代码语言:javascript
复制
volatile unsigned int counter = 0;
volatile __bit flag = 0;  // 标志位,bit类型

void ISR() __interrupt(1) {
    counter++;   // 计数器非原子操作,风险大
    flag = 1;    // 标志位设置不需要原子保护
}
  • counter++由多条汇编指令组成(读、加、写),中断可能发生在中间,导致计数错误。
  • 结果是计数丢失,系统不稳定。

3. SDCC中实现原子操作的三种方式


3.1 __critical关键字(推荐简单安全)
  • 描述 修饰函数或代码块,自动关闭中断进入临界区,执行完自动恢复之前的中断状态。
  • 优点 自动保存/恢复中断状态,支持嵌套调用,代码简洁,减少人为错误。
  • 缺点 关闭中断时间较长可能增加中断延迟。
语法示例
  • 修饰函数
代码语言:javascript
复制
int foo() __critical {
    // 进入函数自动关闭中断
    // 退出函数恢复中断状态
}
  • 修饰代码块
代码语言:javascript
复制
void bar(void) {
    __critical {
        // 多条语句自动临界区执行
        i++;
        // 其他代码
    }
}
代码示例
代码语言:javascript
复制
volatile unsigned int counter = 0;
volatile __bit flag = 0;

void increment_counter() __critical {
    counter++;   // 自动中断保护
}

void ISR() __interrupt(1) {
    increment_counter();
    flag = 1;    // 标志位设置无需保护
}

void ISR2() __interrupt(1) {
    __critical {
        counter++;
    }
    flag = 1;
}

3.2 手动保存和恢复中断使能位(EA)
  • 描述 程序员手动保存当前EA状态,关闭中断执行临界区,操作完恢复中断。
  • 优点 灵活,适合长或复杂临界区。
  • 缺点 易出错,忘恢复中断会导致系统死锁。
语法示例
代码语言:javascript
复制
void func() {
    bit ea_save = EA;  // 保存中断使能位
    EA = 0;            // 禁用中断

    // 临界区代码

    EA = ea_save;      // 恢复中断使能位
}
代码示例
代码语言:javascript
复制
volatile unsigned int counter = 0;
volatile __bit flag = 0;

void ISR() __interrupt(1) {
    bit ea_save = EA;
    EA = 0;

    counter++;

    EA = ea_save;
    flag = 1;
}

3.3 利用JBC指令的原子位操作(适合锁机制)
  • 描述 利用8051的单条JBC(Jump if Bit set and Clear)硬件指令,实现位的原子测试与清零。
  • 优点 速度快,内存消耗极小,适合单个位锁和状态标志。
  • 缺点 只能操作单个位,不能保护多字节变量。
语法示例
代码语言:javascript
复制
volatile __bit lock = 1;  // 锁标志位,1表示未锁定

if (lock) {
    lock = 0;  // 加锁(JBC指令自动完成测试并清零)
    // 临界区
    lock = 1;  // 解锁
}
代码示例
代码语言:javascript
复制
volatile __bit lock = 1;
volatile unsigned int counter = 0;
volatile __bit flag = 0;

void ISR() __interrupt(1) {
    if (lock) {
        lock = 0;   // JBC原子操作加锁

        counter++;

        lock = 1;   // 解锁
    }
    flag = 1;
}

4. 性能和内存占用对比

方法

内存占用

执行速度

代码复杂度

适用场景

非原子操作

最小

最高(无额外指令)

简单

无多任务或中断保护需求

__critical

较多(保存中断状态)

中等

简单自动

常规临界区保护

手动EA保存恢复

类似__critical

视实现灵活

复杂易错

复杂长临界区或特殊控制

JBC指令锁

极小(1bit位)

最高(单条指令)

需要硬件支持

简单锁机制,单个位保护


5. 使用注意事项及常见错误

  • 临界区内代码应尽量简短,避免关闭中断过久影响响应。
  • 手动操作EA时,必须确保中断状态正确恢复,避免死锁。
  • __critical支持嵌套,调用多次会额外占用栈空间。
  • JBC原子位操作只适用于单个位,不能保护多字节数据。
  • 不当使用原子操作(过长临界区)影响系统实时性能。
  • SDCC不同架构支持差异,部分架构不支持自动恢复中断。

6. 汇编角度简析

6.1 __critical伪汇编流程
代码语言:javascript
复制
push EA          ; 保存中断使能状态
clr EA           ; 关闭中断
; 临界区代码
pop EA           ; 恢复中断使能状态
ret              ; 返回
6.2 手动控制中断
代码语言:javascript
复制
mov A, EA        ; 保存EA到累加器
clr EA           ; 关闭中断
; 临界区代码
mov EA, A        ; 恢复EA状态
ret
6.3 JBC指令示例(单条原子指令)
代码语言:javascript
复制
jbc lock_bit, skip  ; 测试并清零lock_bit,跳转到skip失败(锁被占用)
; 成功获得锁的代码
skip:
; 临界区代码
setb lock_bit       ; 解锁
ret

7. 实际项目中选择和使用建议

  • 简单且安全:优先用__critical,自动管理,减少错误。
  • 复杂长临界区:手动保存和恢复EA,但要确保代码严谨。
  • 锁保护单个位变量:利用JBC指令原子操作,性能极佳。
  • 测试时:务必验证临界区是否完整,是否有遗漏恢复中断。
  • 多嵌套临界区:注意栈空间和中断响应时间。

8. 总结

  • 原子操作是多任务嵌入式开发保障数据一致性的关键。
  • SDCC提供了多种支持方式,满足不同需求。
  • 理解不同实现的优缺点,结合项目实际灵活选用。
  • 严格遵守临界区管理规则,保证系统稳定和响应性能。
  1. 本节内容已经全部介绍完毕,希望通过这篇文章,大家对原子操作有了更深入的理解和认识。
  2. 感谢各位的阅读和支持,如果觉得这篇文章对你有帮助,请不要吝惜你的点赞和评论,这对我们非常重要。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 什么是原子操作?为什么需要原子操作?
  • 2. 不使用原子操作的后果与示例
  • 3. SDCC中实现原子操作的三种方式
    • 3.1 __critical关键字(推荐简单安全)
      • 语法示例
      • 代码示例
    • 3.2 手动保存和恢复中断使能位(EA)
      • 语法示例
      • 代码示例
    • 3.3 利用JBC指令的原子位操作(适合锁机制)
      • 语法示例
      • 代码示例
  • 4. 性能和内存占用对比
  • 5. 使用注意事项及常见错误
  • 6. 汇编角度简析
    • 6.1 __critical伪汇编流程
    • 6.2 手动控制中断
    • 6.3 JBC指令示例(单条原子指令)
  • 7. 实际项目中选择和使用建议
  • 8. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档