在C和C ++代码中,特别是对于嵌入式系统,我经常偶然发现具有以下形状的作业:
A |= B;
A &= B;
不确定是否相关,但A和B是寄存器。在这里查看示例: http://processors.wiki.ti.com/index.php/Interrupt_Nesting_on_C28x#Example_Code 出现以下行:
IER |= 0x002;
IER &= 0x002;
但是,这些后续分配似乎与单分配相同
A = B;
除了理论上,前者在某些情况下可以在两条线之间中断,但这似乎在大多数代码中都不起作用。
使用前者优于后者是否有优势,还是我看不到其他差异?
答案 0 :(得分:6)
当然,以下两个命令的顺序:
A |= 0x02;
A &= 0x02;
与:
相同A = 0x02;
除非A
不是变量,而是硬件寄存器。在这种情况下,您需要参考MCU / CPU(或映射外设)手册来检查为什么需要这个序列。
更新
变量与硬件寄存器
在上面的评论中,OP询问如何区分变量和寄存器。
这很容易。您需要做的就是查看定义。虽然典型的变量将被定义为:
unsigned char A;
硬件寄存器定义类似于:
#define A (*(volatile uint16_t *)(0x1234))
这里,A
被定义为硬件寄存器的值,映射到0x1234
的地址。每个微控制器或CPU都有自己独特的硬件寄存器集, 不仅会在不同类型的架构和模型之间变化,而且会在不同制造商之间变化。如果没有详细记录源代码,告诉硬件数据表的唯一方法是查看硬件数据表。此外,一些高级架构可以将来自某些外设的硬件寄存器映射到CPU地址空间中,因此可以以相同的方式访问外部组件的硬件寄存器。
请注意volatile
关键字。来自wiki:
此关键字可防止优化编译器优化后续读取或写入,从而错误地重用过时值或省略写入。易失性值主要出现在硬件访问(内存映射I / O)中,其中读取或写入内存用于与外围设备通信,以及在线程中,其中不同的线程可能已修改了值。
答案 1 :(得分:1)
如果变量是硬件寄存器(似乎是这种情况),则变量为volatile
。写一点然后清除一点,就像写一个值一样。
硬件寄存器的行为并不总是普通的RAM变量。写零的不确定是否有点清楚。标志和状态寄存器尤其可以具有某些条件,例如“通过写入该位来清除该标志,然后读取寄存器”。在其他情况下,只需读取寄存器即可清除标志。
这对于串行通信外设(如SPI或UART)的标志/状态寄存器用户来说非常常见。
另请注意,无法保证A = B;
会产生单条指令。更有可能导致:“加载B”,“将B存储在A中”。如果你需要原子的东西,你必须总是反汇编代码,看看你实际上最终得到了什么。
答案 2 :(得分:1)
可能存在硬件寄存器的情况,其中某些位序列必须具有“特殊”行为,尽管很难看出在这个特定实例中可能存在什么。
不要低估您在互联网上找到的代码无意义的可能性,即使它来自芯片供应商的网站。看一下文档,似乎作者已经读到了这个:
并且对OR IER / AND IER的引用感到困惑 - 它们启用和禁用中断,但MOV IER指令原子也是如此。这是直接任务的作用。
在同一页面上有一个后来的例子:
IER |= M_INT2;
IER &= MINT2; // Set "global" priority
操作数不同的地方;所以也许作者只是有一个普遍的模式并坚持下去。
答案 3 :(得分:-2)
A | = 0x02; 和...一样 A = 0x02;
A& = 0x2仍然等于零(A = 0假设)
如果A和B都是一个字节值。但是在某些情况下,它不等于一个字节,在这些情况下,所有这些运算符都具有不同的属性。
我以4字节值为例。
A = 0x12345600
A | = 0x2等于0x12345602 而 A& = 0x2等于0x12345600 和 A = 0x2等于0x00000002