如何将 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
已经在字段的末尾。
我想根据下面的 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
已经在字段的末尾。