2013-12-20 28 views
1

假設在存儲器(DDRAM)中存在被定義爲TIMER及其32位地址TIMER_ADDR的32位寄存器。在C程序中讀取硬件寄存器

struct timer { 
    uint32_t start:1; 
    uint32_t mode: 3; 
    uint32_t init: 4; 
    uint32_t value:24 
} 

後來我定義了一個局部變量loc_timer爲:

struct timer loc_timer; 

我怎麼能讀這個寄存器在本地註冊

uint32_t TIMER_ADDR; // 32 bits address declared as uint32_t 

爲定時器的佈局定義該程序讓我可以修改內容

loc_timer.mode = 4; 
loc_timer.init = 10; 

並將其重新寫回到註冊TIMER

(*(uint32_t *))&loc_timer = (*((uint32_t *)(TIMER_ADDR))); // read 
(*((uint32_t *)(TIMER_ADDR))) = (*(uint32_t *))&loc_timer; // write 

,但它不工作:-(

+0

可能是你的興趣:表示C/C++的硬件(http://www.open-std.org/jtc1/sc22/wg21/docs /ESC_SF_02_465_paper.pdf) – Devolus

+0

@Devolus,是的,我確實有這個。 –

回答

3

像這樣:

struct timer loc_timer = *(struct timer *)TIMER_ADDR; // read register 
loc_timer.mode = 4;         // modify fields 
loc_timer.init = 10; 
*(struct timer *)TIMER_ADDR = loc_timer;    // write register back again 

注意,因爲這是一個內存映射寄存器,你應該把它當作volatile,例如

volatile struct_timer * const timer = (struct timer *)TIMER_ADDR; 
struct timer loc_timer = *timer;      // read register 
loc_timer.mode = 4;         // modify fields 
loc_timer.init = 10; 
*timer = loc_timer;         // write register back again 
+0

該字段應該是易變的。 – Devolus

+0

@Devolus:您的評論與我的編輯重疊,但是,使用'volatile'是最安全的,儘管在這種情況下可能不需要。 –

+0

嗨保羅,編譯器警告我轉換爲不同大小的整數指針。我試圖在Linux gcc中模擬這個,雖然它不是正確的方法,因爲我還沒有硬件。 –

1

問題1:寄存器未被聲明爲volatile,因此從中讀取或寫入該寄存器將不起作用。編譯器可能會決定將讀取優化爲不同於預期的值,直到後來纔將其讀取,或者完全跳過它。

問題2:該寄存器是一個稱爲「TIMER_ADDR」的32位變量。它是否包含地址,如果是,爲什麼它沒有被聲明爲指針?不可能告訴讀者。

問題3:出於多種原因,您不能使用位域來硬件寄存器的位映射。 See this

問題4:您不能使用結構或位域來映射硬件寄存器,但不保證不啓用填充。您需要編譯器特定的編譯指示或編譯時靜態檢查來防止這種情況發生。


如果我們忽略以上,也使猜測,TIMER_ADDR確實是一個變量,而不是一個地址之一,那麼解決辦法是:

struct timer loc_timer = *(struct timer*) &TIMER_ADDR; 
// and the other way around: 
TIMER_ADDR = *(uint32_t*) &loc_timer; 

從形式上看,這樣的轉換都是不確定的行爲編譯可能會警告他們。實際上,只要我們確定沒有對齊或填充問題,它就會工作。

+0

嗨@Lundin,我編輯了原帖,TIMER_ADDR的確是TIMER的32位地址。這不是一個變量。 –

+0

Hi @Lundin,由於硬件要求,我需要將寄存器值複製到本地變量,進行修改,然後再次將新值寫回完整的32位寄存器,而不是像Dan Saks那樣使用指針方法修改字段流行的白皮書。 –

0

經過一番調查,事實上,PROGRAMMER知道TIMER_ADDR指向什麼類型的數據(即struct timer),因此他/她應該能夠正確地解引用它。否則這個練習是毫無意義的,會打印垃圾!

所以在我們的例子:

struct timer loc_timer; 
loc_timer = *(struct timer *)TIMER_ADDR; 

// Modify some struct members 

// Copy back loc_timer to register 
*(struct timer *)TIMER_ADDR = local_timer; 

// Print new content of TIMER register 
printf("new value = %08x\n", *(struct timer *)TIMER_ADDR);