一、系统时钟初始化流程(clock.c)
阶段 1:为什么系统一开始必须初始化时钟?
目的 / 作用
- 决定 CPU 主频
- 决定 AHB / IPG 总线频率
- 为 GPT、GPIO、UART、GIC 等外设提供时钟源
- 保证延时、定时、中断时序是"准确的"
❗如果不初始化:
- GPT 计数不准
- delay_us 完全不可信
- 外设可能根本不工作
阶段 2:ARM 核心时钟配置(PLL1)
代码
- CCM->CCSR &= ~(1 << 8);
- CCM->CCSR |= (1 << 2);
做了什么?
| 位 | 含义 |
|---|---|
| CCSR[2] | 临时切换到 step clock |
| CCSR[8] | PLL1 bypass |
目的
👉 切换 ARM 内核时钟源到安全时钟
👉 避免在改 PLL 时 CPU 跑飞
阶段 3:ARM 时钟分频(CACRR)
- CCM->CACRR &= ~(7 << 0);
- CCM->CACRR |= (1 << 0);
作用
- 设置 ARM 核心时钟预分频
- 1 表示 不分频 / 2 分频(依芯片定义)
注意点
- ARM 核心最终频率 = PLL1 / CACRR
- 分频过小 → CPU 太快 → 功耗高
- 分频过大 → 系统整体变慢
阶段 4:配置 ARM PLL(PLL1)
- unsigned int t = CCM_ANALOG->PLL_ARM;
- t &= ~(3 << 14);
- t |= (1 << 13);
- t &= ~(0x7F << 0);
- t |= (88 << 0);
- CCM_ANALOG->PLL_ARM = t;
做了什么?
| 位段 | 含义 |
|---|---|
| bit[13] | 使能 PLL |
| bit[6:0] | 倍频系数 |
👉 这里配置的是:
PLL1 = 24MHz × 88 = 1056MHz
阶段 5:切回 PLL1 作为 CPU 时钟
CCM->CCSR &= ~(1 << 2);
作用
👉 ARM 核心正式使用 PLL1 输出的高速时钟
阶段 6:配置 528PLL / 480PLL(外设时钟源)
CCM_ANALOG->PFD_528 = ...
CCM_ANALOG->PFD_480 = ...
目的
- 给 AHB、IPG、GPT、EPIT、UART 等外设提供稳定时钟
- 不同 PFD → 不同频率
注意点
- GPT 使用的是 IPG_CLK
- IPG_CLK 又来自 AHB
阶段 7:配置 AHB / IPG / PERCLK
- CCM->CBCMR
- CCM->CBCDR
- CCM->CSCMR1
时钟关系链(非常重要)
PLL → AHB → IPG → GPT / GPIO / IOMUX
👉 你这里设置的是 经典推荐配置
阶段 8:使能所有外设时钟门控
clock_cg_init();
CCM->CCGRx = 0xFFFFFFFF;
作用
👉 打开所有模块的时钟
注意点
- 方便调试
- 实际产品中不推荐(功耗大)
二、GPT 定时器工作流程(gpt.c)
GPT = 一个"不断递增的硬件计数器"
阶段 1:为什么要用 GPT?
目的 / 作用
- 实现 精确 us / ms 延时
- 不依赖 CPU 空转频率
- 和系统主频解耦
对比:
- while(t--) ❌ 不准
- GPT 计数 ✅ 准确
阶段 2:GPT1 复位
- GPT1->CR |= (1 << 15);
- while ((GPT1->CR & (1 << 15)) != 0);
作用
- 让 GPT 回到硬件初始状态
- 清除所有历史配置
注意点
- 必须等待硬件复位完成
阶段 3:配置 GPT 控制寄存器 CR
- t = GPT1->CR;
- t &= ~(7 << 26); // 时钟源
- t &= ~(3 << 18); // 工作模式
- t |= (1 << 9); // Free-run
- t &= ~(7 << 6);
- t |= (1 << 6); // 外设时钟
- t &= ~(1 << 1); // 关闭中断
- GPT1->CR = t;
关键配置解释
| 位 | 作用 |
|---|---|
| bit9 | 自由运行模式 |
| bit6 | 使用 IPG_CLK |
| bit1 | 不启用中断 |
👉 GPT 现在是 纯计数器模式
阶段 4:设置 GPT 预分频器
- GPT1->PR &= ~(0xFFF << 0);
- GPT1->PR |= (65 << 0);
作用
- 将 IPG_CLK 分频
- 假设 IPG = 66MHz
66MHz / (65+1) ≈ 1MHz
👉 CNT 每加 1 = 1μs
阶段 5:启动 GPT
GPT1->CNT = 0;
GPT1->CR |= (1 << 0);
作用
- 清零计数器
- 开始计数
阶段 6:delay_us 的完整工作流程
- old_count = GPT1->CNT;
- while (1)
- {
- new_count = GPT1->CNT;
- if (new_count != old_count)
- {
- if (new_count > old_count)
- count += new_count - old_count;
- else
- count += 0xFFFFFFFF - old_count + new_count;
- }
- if (count >= us)
- return ;
- old_count = new_count;
- }
这是在干什么?
👉 用硬件计数差值计算经过的时间
为什么要处理回绕?
- GPT 是 32 位计数器
- 会从 0xFFFFFFFF → 0
- 必须处理溢出
阶段 7:delay_ms 的工作方式
- delay_ms(ms)
- {
- while (ms--)
- delay_us(1000);
- }
👉 本质还是 GPT
三、main.c 中的整体配合流程
- system_interrupt_init();
- clock_init();
- led_init();
- beep_init();
- key_init();
- gpt1_init();
顺序为什么这样?
1️⃣ 中断系统先准备好
2️⃣ 时钟先跑稳
3️⃣ GPIO / 外设再初始化
4️⃣ 定时器最后启动
while(1) 中发生了什么?
delay_us(1000 * 1000);
led_nor();
👉 GPT 在后台跑
👉 CPU 通过 CNT 判断时间
👉 精确 1 秒翻转 LED