PC端C与STM32寄存器地址赋值差异及相关疑问
关于STM32寄存器操作与PC端C语言模拟的疑问解答
背景
在使用STM32 HAL驱动时,发现其通过结构体映射硬件寄存器地址的方式实现寄存器赋值,核心代码如下:
#define __IO volatile typedef struct { __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ __IO uint32_t RTOR; /*!< USART Receiver Time Out register, Address offset: 0x14 */ __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ __IO uint32_t PRESC; /*!< USART clock Prescaler register, Address offset: 0x2C */ } USART_TypeDef; #define D2_APB1PERIPH_BASE (0x40000000UL) #define UART4_BASE (D2_APB1PERIPH_BASE + 0x4C00UL) #define UART4 ((USART_TypeDef *) UART4_BASE) UART4->CR1 = whatever; // 修正原代码笔误
尝试在PC端C语言中模拟该操作,编写代码如下:
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #define startaddress ((mytype*) (0x6d9ff93b)) typedef struct { volatile uint32_t myTypeValue; }mytype; int main() { mytype* inst; inst = startaddress; inst->myTypeValue = 0x10; printf("address is : %x\n",inst); return 0; }
运行后程序崩溃,错误码为-1073741819 (0xC0000005);若注释掉inst->myTypeValue = 0x10;这一行,程序可正常输出地址:
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #define startaddress ((mytype*) (0x6d9ff93b)) typedef struct { volatile uint32_t myTypeValue; }mytype; int main() { mytype* inst; inst = startaddress; // inst->myTypeValue = 0x10; printf("address is : %x\n",inst); return 0; }
输出:
address is : 6d9ff93b Process finished with exit code 0
针对上述现象,提出两个疑问:
- STM32库的该操作是否因目标地址为寄存器而非SRAM才有效?(曾在FMC的SDRAM地址0xC0000000处成功执行类似操作)
- 为何指针赋值
inst = startaddress;时程序未崩溃,仅访问结构体成员时才崩溃?
解答
问题1解答
STM32的这种操作不是因为目标是寄存器才有效,本质是STM32的统一内存映射机制:
- STM32作为单片机,整个地址空间是统一编址的,寄存器、SRAM、SDRAM、Flash等都被映射到同一个32位地址空间里,只要是地址空间中被硬件规划为可访问的区域(不管是寄存器还是SDRAM),都可以通过指针直接读写。
- 你在SDRAM地址0xC0000000处成功操作,就是因为该地址属于STM32硬件规划的合法可访问区域;而PC端的操作系统(比如Windows)采用虚拟内存机制,用户程序只能访问系统分配给自己的虚拟地址空间,你指定的
0x6d9ff93b大概率不在当前进程的合法访问范围内,甚至可能是系统内核空间或未映射的地址,因此写入会触发内存访问错误。
问题2解答
指针赋值inst = startaddress;只是把一个数值(地址值)赋值给指针变量,这个过程只是在进程自己的栈内存里修改一个变量的值,并没有访问该地址指向的内存区域,所以不会触发内存保护机制。
只有当你执行inst->myTypeValue = 0x10;时,程序才会尝试去访问0x6d9ff93b这个地址对应的内存,而该地址不在当前进程的合法访问范围内,操作系统的内存管理单元(MMU)会检测到非法访问,直接终止进程,也就是你看到的崩溃。
内容的提问来源于stack exchange,提问作者S2G
相关产品推荐
相关产品推荐

