【问题标题】:Using bit fields for AVR ports使用 AVR 端口的位域
【发布时间】:2013-07-05 18:22:37
【问题描述】:

我希望能够使用这样的东西来更清楚地访问我的端口:

typedef struct {
    unsigned rfid_en: 1;
    unsigned lcd_en: 1;
    unsigned lcd_rs: 1;
    unsigned lcd_color: 3;
    unsigned unused: 2;
} portc_t;

extern volatile portc_t *portc;

但它安全吗?它对我有用,但是...

1) 是否有可能出现竞争条件?

2) gcc 是否为修改单个字段的代码生成读-修改-写周期?

3) 是否有更新多个字段的安全方法?

4) 钻头包装和顺序是否有保证? (在这种情况下,我不关心可移植性,因此特定于 gcc 的选项可以做到我的意思。)

【问题讨论】:

    标签: c bit-fields


    【解决方案1】:
    1. 必须通过操作系统级别的调用来处理竞争条件(这确实会使用 read-modify-writes),GCC 不会这样做。

    2. 同上,没有 GCC 不会为 volatile 生成读-修改-写指令。但是,CPU 通常会以原子方式进行写入(仅仅是因为它是一条指令)。例如,如果位域保持在int 内,则这适用,但这取决于 CPU/实现;我的意思是有些人可以保证最多 8 字节的值,而其他人只能保证最多 4 字节的值。所以在这种情况下,不能混淆位(即一些从一个线程写入,而其他从另一个线程不会发生)。

    3. 同时设置多个字段的唯一方法,就是将这些值设置在一个中间变量中,然后将此变量赋值给volatile

    4. C 标准规定将位打包在一起(开始混合类型时似乎可能会有例外,但我从未见过;每个人都使用unsigned ...)。

    注意:定义 volatile 不会导致编译器生成读取-修改-写入。 volatile 所做的是告诉编译器必须始终对该指针/地址进行分配,并且不能优化掉。

    Here's another post 大致相同的主题。我发现还有很多其他地方可以找到更多详细信息。

    【讨论】:

    • volatile 之所以存在,是因为主执行流程和中断处理程序都可以更改端口。
    • 顺便说一下,(4)与比赛条件无关。
    • @Derecho 你说得对,我错过了(已经很晚了)。
    • 您可以使用联合为端口创建位字段和字节/字组合结构,但恕我直言,它有点混乱。如果您谈论的是嵌入式代码,其中一些硬件中断也可能在您背后进行读取-修改-写入,那么竞争条件可能是一种风险。 Read-modify-write 也比向端口的 SET 或 CLEAR 寄存器写入 1 或 0 慢。
    【解决方案2】:

    关键字 volatile 与竞争条件或访问代码的线程无关。该关键字告诉编译器不要将值缓存在寄存器中。它告诉编译器生成代码,以便每次访问都转到分配给变量的位置,因为每次访问可能会看到不同的值。内存映射外设就是这种情况。如果您的 MPU 有自己的缓存,这将无济于事。内存映射通常有特殊指令或未缓存区域,以确保读取位置,而不是缓存副本。 至于线程安全,请记住,即使是内存访问也可能不是线程安全的,因为它是在两条指令中完成的。例如。在 8051 汇编器中,您必须一次获得一个字节的 16 位值。指令序列可能被 IRQ 或其他线程中断,并且第二个字节被读取或写入,可能会损坏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-18
      相关资源
      最近更新 更多