例如我有以下变量:
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 */ }
我的按位操作有问题吗??我没有得到正确的行为。
答案 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
已经在该领域的最后。