2016-09-27 81 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