STM32 RCC 模块标准库全解析(三)
参考资料:STM32F10x Reference Manual、STM32F10x Standard Peripheral Library Source Code 适用芯片:STM32F103 系列 笔记目标:理解 RCC 标准库函数的内部实现逻辑,能够自信地配置系统时钟
目录
- 寄存器位带(Bit-Band)访问
- mask 掩码与 reset 操作
- 断言机制 assert
- RCC_DeInit()
- RCC_HSEConfig()
- RCC_WaitForHSEStartUp()
- RCC_AdjustHSICalibrationValue()
- RCC_HSICmd()
- RCC_PLLConfig()
- RCC_SYSCLKConfig()
1. 寄存器位带(Bit-Band)访问
什么是位带访问?
Cortex-M3 提供了一种把寄存器的每一个 bit 映射到一个独立的 32-bit 地址的机制,叫做 Bit-Band(位带)。
- 普通写法:读-改-写,3 步,存在被中断打断的风险
- 位带写法:直接对单个 bit 的地址写 0 或 1,原子操作,1 步完成
两个位带区域
| 区域 | 原始地址范围 | 位带别名地址范围 |
|---|---|---|
| SRAM | 0x2000_0000 ~ 0x200F_FFFF(1MB) | 0x2200_0000 ~ 0x23FF_FFFF(32MB) |
| 外设 | 0x4000_0000 ~ 0x400F_FFFF(1MB) | 0x4200_0000 ~ 0x43FF_FFFF(32MB) |
位带地址计算公式
别名地址 = 基地址 + (字节偏移 × 32) + (位号 × 4)
示例:操作 RCC_CR 寄存器(地址 0x4002_1000)的第 0 位(HSION):
字节偏移 = 0x40021000 - 0x40000000 = 0x21000
别名地 址 = 0x42000000 + (0x21000 × 32) + (0 × 4)
= 0x42000000 + 0x420000 + 0
= 0x42420000
直接对 0x42420000 写 1 → HSION 置 1(打开 HSI)
库中如何使用?
STM32 标准库大量使用的是普通掩码写法,位带访问更多出现在对性能要求极高或对原子性要求严格的场合(如 GPIO 操作)。了解位带机制有助于理解底层。
2. mask 掩码与 reset 操作
掩码是什么?
掩码(mask)就是一个二进制模板,配合与(&)、或(|)、异或(^)操作,精准修改寄存器中的特定位,而不影响其他位。
三种常见操作
// ① 置位(set):把某些位强制设为 1,其他位不变
REG |= MASK;
// ② 清零(clear / reset):把某些位强制设为 0,其他位不变
REG &= ~MASK;
// ③ 先清后写(read-modify-write):修改某个字段为特定值
REG = (REG & ~FIELD_MASK) | (VALUE << FIELD_POS);
实际例子:操作 RCC_CFGR 的 SW 字段(系统时钟源选择,bit[1:0])
#define RCC_CFGR_SW_Mask ((uint32_t)0xFFFFFFFC) // 末两位清零掩码
// ~0xFFFFFFFC = 0x00000003 → 只保留 bit[1:0]
// 第一步:读出当前 CFGR,把 SW 字段清零
uint32_t tmpreg = RCC->CFGR & RCC_CFGR_SW_Mask;
// 第二步:填入新的时钟源选择值(如 0x02 = PLL)
tmpreg |= RCC_SYSCLKSource_PLLCLK; // 0x00000002
// 第三步:写回
RCC->CFGR = tmpreg;
记忆口诀:
& ~MASK清字段,| VALUE写字段,两步合一次写回。
reset 操作
在 RCC_DeInit() 中大量出现直接赋值,将寄存器恢复到复位默认值:
RCC->CR |= (uint32_t)0x00000001; // 先确保 HSION = 1(内部 HSI 开启)
RCC->CFGR = 0x00000000; // 系统时钟切回 HSI,所有分频器归默认
RCC->CR &= (uint32_t)0xFEF6FFFF; // 清除 HSEON、CSSON、PLLON(保留其他位)
3. 断言机制 assert
assert 是什么?
assert(断言)是 C 语言中用于参数合法性检查的机制。
- 断言为真(参数合法):什么都不做,程序继续执行
- 断言为假(参数非法):触发报错,提示开发者去修改
在 STM32 标准库中的形式
库函数中使用的是 assert_param(expr),定义在 stm32f10x_conf.h:
/* 使能断言时 */
#define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* 关闭断言时(发布版本,节省代码空间) */
#define assert_param(expr) ((void)0)
assert_failed() 需要自己实现
assert_failed() 只有声明,函数体需要用户在 main.c 或专用文件中自己写:
void assert_failed(uint8_t* file, uint32_t line)
{
/* 方式一:死循环,配合调试器查看 file 和 line 变量 */
while (1) { }
/* 方式二:串口打印(推荐调试阶段使用) */
printf("Assert failed: file %s on line %d\r\n", file, line);
while (1) { }
}