7

在 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;

除了理论上前者在某些情况下可以在两行之间中断之外,但这似乎在大多数代码中并不起重要作用。

使用前者比后者有优势吗,还是我看不到另一个区别?

4

4 回答 4

6

当然,以下两个命令的序列:

A |= 0x02;
A &= 0x02;

等同于:

A = 0x02;

除非A不是变量,而是硬件寄存器。在这种情况下,您需要参考 MCU/CPU(或映射的外围设备)手册来检查为什么需要这个序列。


更新

变量与硬件寄存器

在上面的评论中,OP 询问了如何区分 variables 和 registers

这很容易。您需要做的就是查看定义。虽然典型的变量将被定义为:

unsigned char A;

硬件寄存器定义类似于:

#define A (*(volatile uint16_t *)(0x1234))

在这里,A定义为硬件寄存器的值,映射到地址0x1234。每个微控制器或 CPU 都有自己独特的一组硬件寄存器,它不仅因不同类型的架构和型号而异,而且会因不同制造商而异。如果源代码没有很好地记录,那么了解特定硬件寄存器的唯一方法是查看硬件数据表。此外,一些高级架构可以将一些外设的硬件寄存器映射到 CPU 地址空间,这样就可以以相同的方式访问外部组件的硬件寄存器。

注意volatile关键字。从维基

此关键字可防止优化编译器优化后续读取或写入,从而错误地重用陈旧值或忽略写入。易失性值主要出现在硬件访问(内存映射 I/O)中,其中读取或写入内存用于与外围设备进行通信,以及线程中,不同的线程可能已经修改了值。

于 2016-11-16T10:56:27.607 回答
1

如果变量是硬件寄存器(似乎是这种情况),则变量是volatile. 写入一个位然后清除一个位,与仅写入一个值不同。

硬件寄存器的行为并不总是像普通的 RAM 变量。不确定写入零是否会清除一点。特别是标志和状态寄存器可以具有某些条件,例如“通过写入该位来清除该标志,然后读取寄存器”。在其他情况下,可以通过简单地读取寄存器来清除标志。

这对于用于串行通信外围设备(如 SPI 或 UART)的标志/状态寄存器用户来说非常常见。

另外,请注意,不能保证会A = B;产生一条指令。它更有可能导致:“加载 B”、“将 B 存储在 A 中”。如果您需要原子化,则必须始终反汇编代码以查看实际结果。

于 2016-11-16T14:40:06.080 回答
1

在某些情况下,硬件寄存器可能需要某些位序列具有“特殊”行为,尽管很难看出在这种特定情况下可能是什么。

不要低估您在 Internet 上找到的代码是胡说八道的可能性,即使它来自芯片供应商的网站。查看文档,作者似乎已经阅读了以下内容:

在此处输入图像描述

并且对 OR IER/AND IER 的引用感到困惑 - 它们确实启用和禁用中断,但 MOV IER 指令原子地也是如此。这就是直接分配要做的事情。

在同一页面上有一个稍后的示例:

IER |= M_INT2;
IER &= MINT2;                         // Set "global" priority

操作数不同的地方;所以也许作者只是有一个普遍的模式并坚持下去。

于 2016-11-17T16:51:48.470 回答
-2

A |= 0x02; 与 A = 0x02 相同;

A &=0x2 仍然等于零(A=0 假设)

如果 A 和 B 都是一个字节值。但是在某些情况下它不等于一个字节,在这些情况下,所有这些运算符都有不同的属性。

我以 4 字节值为例。

A=0x12345600

A |= 0x2 等于 0x12345602 而 A &= 0x2 等于 0x12345600 并且 A = 0x2 等于 0x00000002

于 2016-11-16T11:17:48.897 回答