08-SysTick与通用定时器TIM.md 5.7 KB


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 延时实现(查询方式)

// 微秒延时(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

#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

#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
预分频 不可配(固定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