嵌入式编程中变量初始化时为什么要用“或”位运算?

Why is `OR`bit operation is used when initializing a variable in embedded programming?

此信息来自 YOUTUBE 的嵌入编程教程。

讲师建议使用OR操作为某个内存位置赋值。

SYSCTL_RCGCGPIO_R    |=   (1U<<5); 

我的问题是为什么不只是,

SYSCTL_RCGCGPIO_R    =   (1U<<5); 

SYSCTL_RCGCGPIO_R的定义是

#define SYSCTL_RCGCGPIO_R       (*((volatile unsigned long *)0x400FE608))

假设SYSCTL_RCGCGPIO_R内存位置的值为0, 我知道这两个任务是平等的。

但是第一次赋值不会造成不必要的位操作吗?

在向特定内存位置写入值时使用OR位运算是否有特殊原因?

OR 与赋值不同。使用和或,只有在掩码中设置的位会在目标中设置,而其他的保持不变。通过分配,所有 位都设置为掩码的值。

考虑以下几点:

unsigned x = 0, y = 0;
x = (1<<5);
y = (1<<5);
printf("x=%x, y=%x\n", x, y);

x = 0x00;
y = 0x80;
x |= (1<<5);
y |= (1<<5);
printf("x=%x, y=%x\n", x, y);

输出:

x=20, y=20
x=20, y=a0

从这个例子可以看出,如果源值为 0 那么结果是等价的。但如果它不为零,则它们不是。

一般来说,如果您要设置位,您应该使用逻辑或,即使源值为 0。这样您就可以安全,以防万一不是出于某种原因。

SYSCTL_RCGCGPIO_R |= (1U<<5);

相当于:

SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R | (1U<<5);

其中 | 是按位或运算符。

第一个:

(1U<<5) = 00100000
A |= B is equal to A = A | B

当你这样做时

SYSCTL_RCGCGPIO_R    =   (1U<<5); 

你正在调整变量的所有位,并将其设置为 00100000

通过 OR 运算,您仅将目标位(第 5 位)设置为:

previous: xxXxxxxx OR
(1<<5):   00100000 = 
Result:   xx1xxxxx

类似的方式:

previous: xxXxxxxx AND
~(1<<5):  11011111 = 
Result:   xx0xxxxx

用于清除寄存器中的单个位

本教程之所以建议您使用 OR 指令而不是直接赋值,是因为在您无法控制的情况下,目标值可能不为零,而您不希望修改第 6 位以外的任何位。

是的,如果您知道寄存器由于其他原因重置为零 and/or,您知道寄存器为零,那么读取-修改-写入是多余的开销。你是对的。

所以一种观点是,不知道事情是如何运作的,只是爆炸数据而不考虑那里有什么。不好。

另一个是作为初始化代码部分的一部分,执行该块的重置,将块置于已知状态,然后您可以 assume/know 这些寄存器的值并进行写入而不是读取-修改写入。

另一种方法是假设此代码是该外围设备上的第一件事 运行,您可以进行写入而不是读取-修改-写入,因为您知道这些寄存器的 post 重置状态。

最终你得到的思想流派是做更少的假设,只改变我想改变的位,理想情况下保持其余不变,所以读-修改-写。甚至想要让 PA3 成为输出的痛苦,所以读-修改-写一个方向寄存器来改变一位,然后想要让 PA4 成为输出,所以读-修改-写寄存器来改变下一位,可以有在一次读-修改-写中完成,但通过库分层等。

最简单的初始化是强制或以其他方式知道您正在post重置并根据您对重置的了解进行初始化,您可能不会对外围设备使用中断,因此您不会触及中断启用寄存器或清除中断寄存器。这一切都很好。 if/when 你得到了它的工作,如果你出于某种原因现在需要一些可以变热的东西,你需要改变 init 来接触所有的寄存器,在某些情况下这意味着没有读-修改-写的写,所以我们回到那个问题。

是的,如果你之前知道寄存器为零,你是对的,读-修改-写是多余的代码,没有必要,很浪费。但是,作为一种习惯,最好只通过读取-修改-写入来弄乱您正在使用的位(gpio 的控制位只会弄乱您正在设置的一个 gpio 引脚,不要弄乱其他引脚)。在极少数情况下,可能会有一些未记录的内容,如果您以错误的方式编写会使事情无法正常工作,通常文档会在该内容上有一些额外的文字,说明它已保留并且不要更改(其他内容中的其他内容)寄存器可能会说保留,应该为零)。

正如其他人已经指出的那样,该值可能不是零或者它们可能会改变(尤其是当它是你所说的寄存器时)在这种情况下你需要执行读取 - 修改 - 写入以保留其他位的原始值。这很可能会分解为三个单独的汇编指令,即使您可以将其设为 C/C++ 中的 "one-liner"。话虽如此,如果您碰巧在这些指令之间被抢占(当您使用 RTOS 时),或者碰巧发生中断并且在返回 "write" 步骤之前寄存器的值发生变化,您将覆盖其间更改的位。

既然您谈到了嵌入式编程和寄存器,这可能会产生一些非常糟糕的后果,因为简单地向寄存器写入 0 或 1 可能会触发一些硬件操作。这对您来说可能非常耗时。

寄存器可能具有硬件定义的复位值(可能为零也可能不为零);在 assignment 之前的代码中,或者重置本身很可能已经修改或设置了寄存器中的其他位,而该赋值不应修改。这就是所谓的读取-修改-写入操作。