ZZZ-03-关于寄存器

“控制硬件”的本质就是“读写寄存器”。我们可以把 STM32 想象成一个巨大的控制中心,而寄存器(Register)就是控制中心面板上密密麻麻的旋钮和开关。

1. 什么是寄存器

从物理上看,寄存器就是一小块极速存储单元,通常是 32 位(因为 STM32 是 32 位机),每一位(Bit)就是一个开关。

  • 它的位置: 它们就在外设(如 GPIO、定时器、串口)的内部。
  • 它的门牌号: 就像 [[ZZZ-02-Flash和SRAM]] 中写的,每个寄存器都有一个唯一的内存地址。
  • 它的作用:
    • 配置寄存器: 像“设置菜单”。比如:设置这个引脚是做输入还是输出?
    • 数据寄存器: 像“数据通道”。比如:我想让引脚输出高电平,就往这里写 1。
    • 状态寄存器: 像“仪表盘”。比如:串口收到数据了吗?看看这个寄存器的某一位是不是变成了 1。

案例说明

当你执行 *(uint32_t *)0x4001080C = 0x01; 时,发生了以下神奇的事情:

  1. 软件层面: 你把内存地址 0x4001080C 对应的那个 32 位“小盒子”里的第 0 位改成了 1
  2. 硬件逻辑层面: 这个寄存器物理上连接着一堆逻辑电路(锁存器、MOS 管等)。硬件电路检测到寄存器这一位变成了 1
  3. 电气层面: 内部的 MOS 管导通,将芯片内部的 VDD(通常是 3.3V) 电源连接到那个物理引脚(Pin)上。
  4. 结果: 引脚上出现了 3.3V 的电压,也就是我们说的“高电平”。

所以,你控制的是寄存器里的“逻辑电平(0 和 1)”,硬件电路根据你的逻辑,输出真实的“物理电平(0V 和 3.3V)”。

寄存器编程 vs. 库函数编程

虽然底层全是寄存器,但现在的开发者很少直接写 0x4001080C 这种魔术数字了。

方式 A:寄存器操作(硬核)

1
GPIOA->ODR |= (1 << 0); // 将 GPIOA 的数据寄存器第 0 位置 1
  • 优点: 速度极快,代码体积最小。
  • 缺点: 极难阅读,翻手册翻到头秃。

方式 B:HAL 库操作(主流)

1
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 设置 PA0 为高电平
  • 优点: 像人类语言,好理解,换个芯片也好移植。
  • 本质: HAL_GPIO_WritePin 这个函数的内部,最终还是去操作了那几个寄存器地址。

总结

  • 引脚(Pin): 工厂的出货口。
  • 寄存器(Register): 控制出货口的开关闸门。
  • 地址(Address): 找到那个闸门的唯一路径。
  • 库函数(Library): 开关的操作手册和自动机械臂。

进阶思考

既然一切都是寄存器,那么“串口发送数据”是怎么回事呢? 其实就是:

  1. 检查“状态寄存器”,看上一个字发完没。
  2. 如果发完了,把你要发的字符丢进“数据寄存器”。
  3. 芯片内部的硬件电路会自动把这个寄存器里的 8 位数据变成一串高低电平,从引脚“嗖”地射出去。
Licensed under CC BY-NC-SA 4.0