如何将 read/write 转换为 unsigned char 的特定位

How to read/write into specific bits of a unsigned char

我想根据下面的 table 读写 from/to 一个无符号字符:

例如我有以下变量:

unsigned char hsi_div = 0x01; /* HSI/2 */
unsigned char cpu_div = 0x05; /* Fmaster/32 */

我想将 hsi_div 写入第 4,3 位,将 cpu_div 写入第 2,1,0 位(假设整个字符被命名为 CLK_DIVR):

CLK_DIVR |= hsi_div << 4; //not correct!
CLK_DIVR |= cpu_div << 2; //not correct!

假设我想回读寄存器以确保我做的是正确的:

if( ((CLK_DIVR << 4) - 1) & hsi_div) ) { /* SET OK */ }
if( ((CLK_DIVR << 2) - 1) & cpu_div) ) { /* SET OK */ }

我的位运算有问题吗!?我没有得到正确的行为。

对于您想要的 HSIDIV 位字段:

hw_register = (hw_register & 0x18) | (hsi_value & 0x03) << 0x03;

这会将值屏蔽为 2 位宽,然后移动到位位置 3 和 4。

CPUDIV 字段是:

hw_register = (hw_register & 0x7) | (cpu_value & 7);

读取寄存器:

hsi_value = (hw_register & 0x18) >> 3;
cpu_value = hw_register & 0x07;

我假设 CLK_DIVR 是一个硬件外设寄存器,应该限定 volatile。此类寄存器应设置为尽可能少的写入。您更改了所有可写位,所以只需

 CLK_DIVR = (uint8_t)((hsi_div << 3) | (cpu_div << 0));

注意使用固定宽度的字体。这使得它不再是一个 8 位寄存器。根据摘录,高位是只读的,因此写入时不会更改。强制转换可防止编译器发出截断警告,这是建议始终启用的警告之一(包含在 -Wconversion for gcc 中)。

移位计数实际上是字段开始的位(最低位)。 0 的移位计数表示 "no shifting",因此不需要移位运算符。我仍然用它来澄清我的意思是该字段从 0 位开始。就让编译器去优化吧,专心写可维护的代码。


注意:您的代码位或寄存器中已有的任何内容。 Bit-or 只能置位,不能清零。此外,班次计数错误。


不确定,但如果摘录是针对 ARM Cortex-M CPU(STM32Fxxxx?),减少外部总线周期变得更重要,因为 ARM 可能需要相当多的周期来进行访问。

刚刚

CLK_DIVR |= hsi_div << 3;
CLK_DIVR |= cpu_div << 0;

由于 hsi_div 是一个 2 位二进制数,您必须将它移动三个位置才能跳过 CPUDIV 字段。 cpu_div 已经在字段的末尾。