You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

针对上述现象,提出两个疑问:

  1. STM32库的该操作是否因目标地址为寄存器而非SRAM才有效?(曾在FMC的SDRAM地址0xC0000000处成功执行类似操作)
  2. 为何指针赋值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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:30:22