2016-09-27 79 views
1

我想读,并根据下表写从/到unsigned char类型一个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(想象整个焦炭名为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 */ } 

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

+1

你想要使用8位的uint8_t。无符号字符*至少为8位*,但可能更多。 –

+1

使用固定宽度类型(请参阅'stdiont.h'),而不是标准整数类型。并选择你使用的语言。 C和C++是不同的语言! – Olaf

+0

@ThomasMatthews对于我使用的编译器和平台无关紧要 –

回答

3

我假设CLK_DIVR是一个硬件外设寄存器,应该是合格的volatile。这些寄存器应尽可能少地写入。您更改所有可写位,所以只需

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

注意使用固定宽度类型。这使得提到它是一个8位寄存器是不必要的。根据摘录,高位是只读的,所以在写入时不会改变。该转换可防止编译器发出截断警告,该警告是始终启用的推荐警告之一(包含在gcc的-Wconversion中)。

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


注意:您的代码位或已存在于寄存器中的任何代码。位或只能设置位,但不能清除它们。此外,轮班计数是错误的。


不知道,但如果摘录是一个ARM的Cortex-M CPU(STM32Fxxxx?),从而减少了外部总线周期变得更加重要,因为ARM能参加的访问相当长的一段周期。

+0

谢谢,它是STM8,我不认为它是基于ARM的......而是ST自己的核心 –

+0

@SaeidYazdani:它不是ARM,但名称与STM32时钟模块中的名称类似不同的布局),只是在黑暗中拍摄的,这些陈述仍然适用,如果回答了你的问题,请记住upvote和/或接受答案 – Olaf

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; 
+0

'register'(作为关键字)是变量的错误名称;) –

+0

@JesperJuhl:感谢提醒。在硬件方面,被访问的东西也被称为寄存器。 :-( –

0

只是

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

由于hsi_div是2位的二进制,你必须把它移到三个位置跳过CPUDIV领域。 cpu_div已经在该领域的末尾。

+0

看到我的答案为什么这是错误的方法(多种原因)。 – Olaf