如何读取/写入unsigned char的特定位

时间:2016-09-27 21:06:28

标签: c bit-manipulation bitwise-operators

我想根据下表读取和写入unsigned char: enter image description here

例如我有以下变量:

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

我想将hsi_div写入位4,3,将cpu_div写入位2,1,0(想象整个char被命名为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 */ }

我的按位操作有问题吗??我没有得到正确的行为。

3 个答案:

答案 0 :(得分:2)

我认为CLK_DIVR是硬件外设寄存器,应该是合格的volatile。这些寄存器应尽可能少地写入。你改变了所有可写位,所以只需

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

注意使用固定宽度类型。这使得它不需要8位寄存器。根据摘录,高位是只读的,因此在写入时不会改变它们。强制转换使编译器不会发出截断警告,这是始终启用的建议警告之一(包括在-Wconversion中用于gcc)。

移位计数实际上是字段开始的位(LSbit)。移位计数0表示“无移位”,因此不需要移位运算符。我仍然用它来澄清我的意思是该字段从位0开始。只需让编译器优化,专注于编写可维护的代码。

注意:您的代码位或寄存器中的任何内容。位或只能设置位,但不能清除它们。另外,班次计数是错误的。

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

答案 1 :(得分:1)

对于您想要的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;

答案 2 :(得分:0)

只需

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

由于hsi_div是一个2位二进制文​​件,因此您必须将其移动三个位置才能跳过CPUDIV字段。 cpu_div已经在该领域的最后。