在C程序中读取硬件寄存器
假设内存(DDRAM)中有一个定义为在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
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流行的白皮书那样使用指针方法修改字段。