|
|
@@ -0,0 +1,193 @@
|
|
|
+---
|
|
|
+tags: [source-summary]
|
|
|
+type: source
|
|
|
+source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — 第1章:定时器 + 配套代码16~19"
|
|
|
+author: "尚硅谷研究院"
|
|
|
+date: 2026-07-15
|
|
|
+created: 2026-07-15
|
|
|
+---
|
|
|
+
|
|
|
+# SysTick与通用定时器TIM
|
|
|
+
|
|
|
+> **用生活理解**:SysTick 就像秒表倒计时——设定一个时间(LOAD值),它倒数到 0 就提醒你(中断)。TIM通用定时器就像高级秒表,可以预分频(把太快的时间变成慢的)、自动重装(倒计时到0自动重新开始),适合精确控制 LED 闪烁频率。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## SysTick 系统定时器
|
|
|
+
|
|
|
+SysTick 是 Cortex-M3 内核自带的**24 位递减计数器**,属于内核外设(不依赖具体芯片厂商)。
|
|
|
+
|
|
|
+### SysTick 寄存器
|
|
|
+
|
|
|
+| 寄存器 | 地址偏移 | 功能 |
|
|
|
+|--------|---------|------|
|
|
|
+| CTRL | 0x00 | 控制与状态寄存器 |
|
|
|
+| LOAD | 0x04 | 重装载数值寄存器 |
|
|
|
+| VAL | 0x08 | 当前数值寄存器 |
|
|
|
+| CALIB | 0x0C | 校准数值寄存器(很少用) |
|
|
|
+
|
|
|
+**CTRL 寄存器位说明**:
|
|
|
+| 位 | 名称 | 说明 |
|
|
|
+|----|------|------|
|
|
|
+| 0 | ENABLE | 1=开启定时器,0=关闭 |
|
|
|
+| 1 | TICKINT | 1=计数到0时产生SysTick异常 |
|
|
|
+| 2 | CLKSOURCE | 1=AHB时钟(72MHz),0=AHB/8(9MHz) |
|
|
|
+| 16 | COUNTFLAG | 计数到0时硬件置1,读后自动清0 |
|
|
|
+
|
|
|
+**LOAD 寄存器**:24 位有效(最大值 16,777,215)。
|
|
|
+`VAL 从 LOAD 值开始递减 → 到 0 时产生 COUNTFLAG → 自动重新装载 LOAD 值`
|
|
|
+
|
|
|
+### SysTick 延时实现(查询方式)
|
|
|
+
|
|
|
+```c
|
|
|
+// 微秒延时(AHB=72MHz)
|
|
|
+void Delay_us(uint32_t us)
|
|
|
+{
|
|
|
+ SysTick->LOAD = 72 * us; // 72MHz下,1us需72次计数
|
|
|
+ SysTick->VAL = 0; // 清当前值
|
|
|
+ SysTick->CTRL = 0x05; // ENABLE=1, CLKSOURCE=1(AHB)
|
|
|
+
|
|
|
+ while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG)) // 等待计数到0
|
|
|
+ {
|
|
|
+ }
|
|
|
+ SysTick->CTRL &= ~SysTick_CTRL_ENABLE; // 关闭定时器
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+> **注意**:LOAD 最大 24 位(16,777,215),最大单次延时 = 16,777,215 / 72 ≈ 233ms
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 通用定时器 TIM
|
|
|
+
|
|
|
+STM32F103ZET6 有多个定时器:
|
|
|
+
|
|
|
+| 定时器 | 类型 | 位数 | 主要特性 |
|
|
|
+|--------|------|------|---------|
|
|
|
+| TIM1 | 高级 | 16 | 带互补输出、死区插入、刹车功能 |
|
|
|
+| TIM2~5 | 通用 | 16 | 时基、PWM、捕获/比较 |
|
|
|
+| TIM6~7 | 基本 | 16 | 仅时基(无PWM/捕获) |
|
|
|
+
|
|
|
+### TIM 时基单元结构
|
|
|
+
|
|
|
+```
|
|
|
+预分频器(PSC) → 计数器(CNT) → 自动重装载(ARR) → 更新事件(UEV)
|
|
|
+```
|
|
|
+
|
|
|
+**计数器时钟频率**:`CK_CNT = CK_PSC / (PSC + 1)`
|
|
|
+
|
|
|
+**溢出时间**:`Tout = (ARR + 1) × (PSC + 1) / CK_PSC`
|
|
|
+
|
|
|
+**示例**:CK_PSC=72MHz, PSC=7199, ARR=9999
|
|
|
+`CK_CNT = 72MHz / 7200 = 10KHz`
|
|
|
+`Tout = 10000 × 10KHz = 1s`(1秒溢出一次)
|
|
|
+
|
|
|
+### TIM 关键寄存器
|
|
|
+
|
|
|
+| 寄存器 | 功能 |
|
|
|
+|--------|------|
|
|
|
+| TIMx_PSC | 预分频器(16位,0~65535) |
|
|
|
+| TIMx_CNT | 当前计数值 |
|
|
|
+| TIMx_ARR | 自动重装载值 |
|
|
|
+| TIMx_CR1 | 控制寄存器1(CEN使能、DIR方向、ARPE自动重装载预装载) |
|
|
|
+| TIMx_DIER | 中断使能寄存器(UIE更新中断、CCxIE捕获比较中断) |
|
|
|
+| TIMx_SR | 状态寄存器(UIF更新标志、CCxIF捕获比较标志) |
|
|
|
+
|
|
|
+> **参考**:参考手册 §14(通用定时器寄存器描述)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 实验:SysTick/TIM 定时控制 LED 闪烁
|
|
|
+
|
|
|
+### 软件设计(SysTick 中断版)
|
|
|
+
|
|
|
+**文件:`stm32_base/16_systick_led_twinkle_register/Hardware/SysTick/systick.c`**
|
|
|
+
|
|
|
+```c
|
|
|
+#include "systick.h"
|
|
|
+
|
|
|
+// SysTick 初始化:配置1ms中断一次
|
|
|
+void Systick_Init(void)
|
|
|
+{
|
|
|
+ // 72MHz / 72000 = 1KHz = 1ms中断一次
|
|
|
+ // LOAD = 72000 - 1 = 71999
|
|
|
+ SysTick->LOAD = 72000 - 1;
|
|
|
+ SysTick->VAL = 0;
|
|
|
+ // CTRL: ENABLE=1, TICKINT=1(中断使能), CLKSOURCE=1(AHB)
|
|
|
+ SysTick->CTRL |= (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_CLKSOURCE_Msk);
|
|
|
+}
|
|
|
+
|
|
|
+// SysTick 中断服务函数(在启动文件中注册)
|
|
|
+void SysTick_Handler(void)
|
|
|
+{
|
|
|
+ static uint16_t tick = 0;
|
|
|
+ tick++;
|
|
|
+ if (tick >= 1000) // 1000ms = 1s
|
|
|
+ {
|
|
|
+ LED_Toggle(LED_1); // 翻转LED
|
|
|
+ tick = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 软件设计(TIM 中断版)
|
|
|
+
|
|
|
+**文件:`stm32_base/18_tim_led_twinkle_register/Hardware/TIM5/tim5.c`**
|
|
|
+
|
|
|
+```c
|
|
|
+#include "tim5.h"
|
|
|
+
|
|
|
+void TIM5_Init(void)
|
|
|
+{
|
|
|
+ // 1. 开启TIM5时钟(APB1总线)
|
|
|
+ RCC->APB1ENR |= RCC_APB1ENR_TIM5EN;
|
|
|
+
|
|
|
+ // 2. 配置预分频器:72MHz / (7199+1) = 10KHz
|
|
|
+ TIM5->PSC = 7200 - 1;
|
|
|
+
|
|
|
+ // 3. 配置自动重装载值:10KHz / (10000+1) ≈ 1秒溢出一次
|
|
|
+ TIM5->ARR = 10000 - 1;
|
|
|
+
|
|
|
+ // 4. 清计数器
|
|
|
+ TIM5->CNT = 0;
|
|
|
+
|
|
|
+ // 5. 使能更新中断
|
|
|
+ TIM5->DIER |= TIM_DIER_UIE;
|
|
|
+
|
|
|
+ // 6. 使能TIM5中断(NVIC)
|
|
|
+ NVIC_EnableIRQ(TIM5_IRQn);
|
|
|
+
|
|
|
+ // 7. 使能计数器(CEN=1)
|
|
|
+ TIM5->CR1 |= TIM_CR1_CEN;
|
|
|
+}
|
|
|
+
|
|
|
+// TIM5 中断服务函数
|
|
|
+void TIM5_IRQHandler(void)
|
|
|
+{
|
|
|
+ if (TIM5->SR & TIM_SR_UIF) // 检查更新中断标志
|
|
|
+ {
|
|
|
+ LED_Toggle(LED_1); // 翻转LED
|
|
|
+ TIM5->SR &= ~TIM_SR_UIF; // 清标志(写0清除)
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 核心速查表
|
|
|
+
|
|
|
+| 操作 | SysTick | TIM通用定时器 |
|
|
|
+|------|---------|--------------|
|
|
|
+| 初始化 | `SysTick->CTRL = 0x07` | `TIMx->CR1 |= TIM_CR1_CEN` |
|
|
|
+| 预分频 | 不可配(固定AHB或AHB/8) | `TIMx->PSC = value` |
|
|
|
+| 重装载 | `SysTick->LOAD = value` | `TIMx->ARR = value` |
|
|
|
+| 中断使能 | CTRL.TICKINT=1 | DIER.UIE=1 + NVIC |
|
|
|
+| 清标志 | 读CTRL自动清 | `SR &= ~UIF` |
|
|
|
+| 停止 | `CTRL &= ~ENABLE` | `CR1 &= ~CEN` |
|
|
|
+
|
|
|
+## 常见问题与避坑
|
|
|
+
|
|
|
+1. **SysTick 中断不触发** → 检查 CTRL.TICKINT 是否置 1、NVIC 优先级是否设置
|
|
|
+2. **TIM 溢出时间不对** → 检查 PSC/ARR 计算、确认时钟源频率(APB1=36MHz 还是 72MHz?)
|
|
|
+3. **AHB/APB1 时钟差异** → TIM2~5 挂载 APB1,若 APB1 分频≠1 则定时器时钟 = APB1×2
|
|
|
+4. **更新标志不清导致只中断一次** → ISR 中必须手动清除 SR.UIF
|