c语言中如何往硬件地址处写值
在 C 语言里往硬件地址写值,本质其实就是把某个固定地址当成“内存单元”来操作,这种方式在嵌入式里非常常见,比如控制 LED、GPIO、串口寄存器等等。
硬件寄存器通常不会通过普通变量访问,而是直接映射在某个固定地址上,比如 0x40021018 这种地址,这些地址在芯片手册里都会明确给出。
最直接的写法,就是把这个地址强制转换成指针,然后再解引用赋值:
*(volatile unsigned int *)0x40021018 = 0x1;
这行代码看起来有点“暴力”,但逻辑很简单:先把 0x40021018 当成一个指向 unsigned int 的指针,再通过 * 去写入数据。
实际工程里一般不会直接裸写数字地址,会稍微封装一下:
#define REG(addr) (*(volatile unsigned int *)(addr))
REG(0x40021018) = 0x1;
这样可读性会好一点,也方便后面维护。
这里有个关键点经常被忽略,就是 volatile。这个关键字不是装饰用的,而是必须加的。因为编译器在优化时,可能会认为“这段代码没必要重复访问内存”,然后直接帮你省掉操作。但对硬件寄存器来说,每一次读写都可能对应真实外设动作,比如点灯、发数据、清中断,所以必须强制告诉编译器:这个地址不能优化,每次都要真实访问。
很多嵌入式代码里还会看到另一种写法,用结构体来映射一整片寄存器区域,比如 STM32 里:
typedef struct {
volatile unsigned int CR;
volatile unsigned int DR;
} GPIO_TypeDef;
#define GPIOA ((GPIO_TypeDef *)0x50000000)
GPIOA->DR = 0x01;
这种方式本质上是在做“地址空间建模”,把一堆寄存器当成一个结构体来看,写起来更像普通 C 代码,但底层仍然是在操作固定内存地址。
需要特别注意的一点是,这种写法只适用于裸机或者驱动开发环境。在 Windows 或 Linux 普通应用层程序里,直接访问这种地址基本都会崩溃,因为那是内核或物理地址空间,用户态没有权限碰。如果在 Linux 里想做类似事情,通常要通过 mmap 或驱动来映射。
说到底,这一套操作的核心就是一句话:把“地址”当成“变量”用,但这个变量背后不是内存,而是硬件。

- 上一篇:cout 到底是什么?别只会用,顺便把它搞明白
- 下一篇:Composer 是什么
