假设存储器中有一个32位寄存器TIMER
及其32位地址TIMER_ADDR
。
uint32_t TIMER_ADDR; // 32 bits address declared as uint32_t
TIMER的布局定义为:
struct timer {
uint32_t start:1;
uint32_t mode: 3;
uint32_t init: 4;
uint32_t value:24
}
后来我将本地var loc_timer定义为:
struct timer loc_timer;
如何将该寄存器读入程序中的本地寄存器,以便我可以修改内容
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 :(得分: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
答案 1 :(得分: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;
正式地说,这些强制转换是未定义的行为,编译可能会警告它们。在实践中,只要我们确定没有对齐或填充问题,它就会起作用。
答案 2 :(得分: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);