在C程序中读取硬件寄存器

在C程序中读取硬件寄存器,c,hardware-programming,C,Hardware Programming,假设内存(DDRAM)中有一个定义为TIMER的32位寄存器及其32位地址TIMER\u ADDR 计时器的布局定义为: struct timer { uint32_t start:1; uint32_t mode: 3; uint32_t init: 4; uint32_t value:24 } 后来,我将本地var loc_计时器定义为: struct timer loc_timer; 如何将该寄存器读取到程序中的本地寄存器,以便修改内容 loc_time

假设内存(DDRAM)中有一个定义为
TIMER
的32位寄存器及其32位地址
TIMER\u ADDR

计时器的布局定义为:

struct timer {
    uint32_t start:1;
    uint32_t mode: 3;
    uint32_t init: 4;
    uint32_t value:24
}
后来,我将本地var loc_计时器定义为:

struct timer loc_timer;
如何将该寄存器读取到程序中的本地寄存器,以便修改内容

loc_timer.mode = 4;
loc_timer.init = 10;
并将其写回寄存器
计时器

差不多

(*(uint32_t *))&loc_timer = (*((uint32_t *)(TIMER_ADDR))); // read
(*((uint32_t *)(TIMER_ADDR))) = (*(uint32_t *))&loc_timer; // write
但它不起作用:-(

像这样:

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 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
像这样:

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 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:寄存器未声明为易失性,因此从它读取或写入它将不起作用。编译器可能决定将读取优化为与预期不同的值,将其发送到以后,或者完全跳过它

问题2:寄存器是一个名为“TIMER_ADDR”的32位变量。它是否包含地址,如果是,为什么不声明为指针?读者无法判断

问题3:由于多种原因,不能使用位字段进行硬件寄存器的位映射

问题4:如果不保证未启用填充,则无法使用结构或位字段来映射硬件寄存器。您需要编译器特定的pragma或编译时静态检查来防止这种情况


如果我们忽略上述内容,并猜测TIMER_ADDR确实是一个变量,而不是一个地址,那么解决方案将是:

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

从形式上讲,此类强制转换是未定义的行为,编译器可能会对此发出警告。在实践中,只要我们确定没有对齐或填充问题,它就可以工作。

问题1:寄存器未声明为易失性,因此从它读取或写入不会工作。编译器可能会决定将读取优化为不同的值ent值比预期值高,请稍后再发送,或完全跳过

问题2:寄存器是一个名为“TIMER_ADDR”的32位变量。它是否包含地址,如果是,为什么不声明为指针?读者无法判断

问题3:由于多种原因,不能使用位字段进行硬件寄存器的位映射

问题4:如果不保证未启用填充,则无法使用结构或位字段来映射硬件寄存器。您需要编译器特定的pragma或编译时静态检查来防止这种情况


如果我们忽略上述内容,并猜测TIMER_ADDR确实是一个变量,而不是一个地址,那么解决方案将是:

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

从形式上讲,这种类型转换是未定义的行为,编译可能会对此发出警告。在实践中,只要我们确定没有对齐或填充问题,这种转换就会起作用。

经过一些调查,事实是,程序员知道TIMER\u 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);   

经过一些调查,这里是事实,程序员知道什么类型的数据计时器\u ADDR是指向(即结构计时器),所以他/她应该能够正确地取消引用它。否则这个练习是没有意义的,将打印垃圾

因此,在我们的案例中:

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);   

这些字段很可能是易失性的。@Devolus:您的注释与我的编辑重叠,但是是的,使用
易失性是最安全的,即使在这种特殊情况下可能不需要它。嗨,Paul,编译器警告我从不同大小的整数转换为指针。我尝试在Linux gcc中模拟这一点,尽管这不是正确的转换方法o因为我还没有硬件。@Lundin:
volatile
并不总是需要的,但出于防御性编程的利益,这始终是一种良好的做法。是的,我完全意识到这一点,我并不反对一般使用
volatile
——我只是学究,因为有些情况下实际上并不需要它,例如。值在读取之间从不改变的寄存器(即与内存行为相同的寄存器)。字段很可能是易失性的。@Devolus:您的注释与我的编辑重叠,但是是的,使用
易失性是最安全的,即使在这种特殊情况下可能不需要它。嗨,Paul,编译器警告我从不同大小的整数转换为指针。我尝试在Linux gcc中模拟这一点,尽管这不是正确的转换方法因为我还没有硬件。@Lundin:
volatile
并不总是需要的,但出于防御性编程的利益,它总是一种好的做法。是的,我完全意识到这一点,我并不反对一般使用
volatile
——我只是学究,因为在某些情况下它实际上并不需要,例如.在读取之间值从不改变的寄存器(即,以与内存相同的方式运行的寄存器)。你可能感兴趣:@Devolus,是的,我确实有。你可能感兴趣:@Devolus,是的,我确实有。嗨@Lundin,我编辑了原始帖子,TIMER_ADDR确实是TIMER的32位地址。它不是一个变量。嗨@Lundin,由于硬件要求,我需要将寄存器值复制到一个局部变量,进行修改,然后写入新值再次以完整的32位返回寄存器,而不是像Dan Saks流行白皮书那样使用指针方法修改字段。Hi@Lundin,我编辑了原始帖子,TIMER_ADDR确实是TIMER的32位地址。它不是一个变量。Hi@Lundin,由于硬件要求,我需要将寄存器值复制到局部变量do修改,并将新值重新写入完整的32位寄存器,而不是像Dan Saks流行的白皮书那样使用指针方法修改字段。