全部例程

TIM - 基本定时器

W55MH32

更新于 2026年8月13日

本篇总结

使用 TIM3 时基模式,定时触发更新中断。

🔑 KEIL MDK、UART调试、TIM

本示例演示 W55MH32L 的定时器(TIM)外设的基本用法:将 TIM3 配置为基本时基模式,设置预分频器与自动重装载值使更新事件每 10ms 产生一次,使能更新中断,在中断服务函数中打印函数名以验证定时周期。

TIM 简介

W55MH32 的定时器分为三类:高级控制定时器(TIM1/TIM8,支持互补 PWM、死区插入)、通用定时器(TIM2-5/TIM9-14,支持输入捕获、输出比较、PWM)、基本定时器(TIM6/TIM7,仅时基功能)。三者核心都是 16 位计数器 + 预分频器,基本定时器最简单,通用与高级定时器在其基础上扩展了通道逻辑。

定时器分辨率计数模式预分频DMA 请求通道数互补输出
TIM1、TIM816位上/下/上下1~65536可以4
TIM2、3、4、516位上/下/上下1~65536可以4没有
TIM9、TIM1216位向上1~65536不可以2没有
TIM10、11、13、1416位向上1~65536不可以1没有
TIM6、TIM716位向上1~65536可以0没有

本例程使用 TIM3(通用定时器)配置为基本时基模式:仅使用计数器 + 预分频器 + 自动重装载值,不涉及捕获/比较通道,等同于基本定时器的用法。时基核心公式:更新周期 = (PSC+1) × (ARR+1) / TIMxCLK

前置工具准备

开始本例程前,请确认以下工具已安装:

  • WIZ UartTool V1.0:串口日志观察工具:点击下载

硬件连接

本例程为纯内部定时器演示,无需外部连线。定时器更新中断在芯片内部触发,中断服务函数通过 USART1(PA9/PA10)打印日志。仅需一根 Micro USB 线连接开发板 DEBUG USB 口到 PC 即可观察串口输出。

引脚功能方向模式
PA9USART1_TX开发板 → PC复用推挽输出 (AF_PP)
PA10USART1_RXPC → 开发板浮空输入 (IN_FLOATING)

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\TIM\TIM_Basic\,核心文件如下:

TIM_Basic/
├── USER/
│   ├── main.c            ← TIM初始化 + 中断服务函数 + printf 重定向 + 主流程
│   ├── w55mh32_conf.h    ← 外设头文件集中包含
│   ├── w55mh32_it.c/h    ← 中断服务函数(系统异常部分)
│   ├── system_w55mh32.c/h← 系统初始化(时钟配置)
│   └── startup_w55mh32.s ← 启动文件
└── SYSTEM/
    └── delay/
        └── delay.c/h      ← SysTick 延时函数

步骤1:TIM 定时器初始化(含 NVIC 中断配置)

文件:USER/main.c | 函数:TIM_Configuration() — 配置 TIM3 时基、使能更新中断、配置 NVIC 优先级并启动定时器。


void TIM_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    NVIC_InitTypeDef        NVIC_InitStructure;

    /* 开启 TIM3 时钟(TIM3 挂在 APB1 上) */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    /* 时基配置:预分频使 CK_CNT = 1MHz,周期 10000 计数 = 10ms */
    TIM_TimeBaseStructure.TIM_Period        = 9999;                                 // 自动重装载值 ARR
    TIM_TimeBaseStructure.TIM_Prescaler     = clocks.SYSCLK_Frequency / 1000000 - 1; // 预分频 PSC,SYSCLK/1M-1
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                         // 时钟分频(基本定时器不用)
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;                   // 向上计数
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* 使能 TIM3 更新中断 */
    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    /* NVIC 中断优先级配置 */
    NVIC_InitStructure.NVIC_IRQChannel                   = TIM3_IRQn;   // TIM3 中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;           // 抢占优先级 0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 3;           // 子优先级 3
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;      // 使能中断通道
    NVIC_Init(&NVIC_InitStructure);

    /* 启动定时器 */
    TIM_Cmd(TIM3, ENABLE);
}

定时周期计算:CK_CNT = SYSCLK / (PSC+1) = 72MHz / 72 = 1MHz,更新事件周期 = (ARR+1) / CK_CNT = 10000 / 1MHz = 10ms。五步完成:开时钟 → 时基参数配置 → 使能更新中断 → NVIC 优先级配置 → 启动定时器。

如果你想自定义

  • 改定时周期 → 修改 TIM_Period(ARR)和 TIM_Prescaler(PSC)。如要 1 秒中断:PSC = 7199(10kHz)、ARR = 9999
  • 换用 TIM6/TIM7(基本定时器) → 修改 RCC_APB1Periph_TIM6TIM6TIM6_IRQn,并去掉 CounterMode(TIM6 仅向上计数)
  • 改中断优先级 → 修改 NVIC_IRQChannelPreemptionPriority(0~15,数值越小优先级越高)

步骤2:中断服务函数

函数:TIM3_IRQHandler() — TIM3 更新中断触发时进入,先检查中断标志,再清除标志,最后打印函数名。


/* TIM3 中断服务函数 */
void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)   /* 检查是否发生更新中断 */
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);      /* 清除更新中断标志 */
        printf("%s\n", __FUNCTION__);                     /* 打印中断函数名 */
    }
}

中断服务函数固定命名 TIM3_IRQHandler,与启动文件中的中断向量表一致。必须先清除中断标志再执行业务逻辑,否则会反复进入中断导致主循环无法执行。

注意

在中断中调用 printf 仅供演示。实际项目中 printf 耗时较长(尤其高波特率下),高频中断会严重影响实时性。建议中断中只置标志位,在主循环中处理打印。

步骤3:主函数逻辑

函数:main() — 初始化延时和 UART(115200) → 打印时钟频率 → 配置 TIM3 → 进入空循环等待中断。


RCC_ClocksTypeDef clocks;

int main(void)
{
    delay_init();
    UART_Configuration(115200);
    RCC_GetClocksFreq(&clocks);

    printf("SYSCLK: %3.1fMhz, HCLK: %3.1fMhz, PCLK1: %3.1fMhz, "
           "PCLK2: %3.1fMhz, ADCCLK: %3.1fMhz\n",
           (float)clocks.SYSCLK_Frequency / 1000000,
           (float)clocks.HCLK_Frequency / 1000000,
           (float)clocks.PCLK1_Frequency / 1000000,
           (float)clocks.PCLK2_Frequency / 1000000,
           (float)clocks.ADCCLK_Frequency / 1000000);
    printf("TIM Basic Test.\n");

    TIM_Configuration();

    while (1);
}

主循环为空 while(1),所有工作由 TIM3 更新中断驱动。clocks 为全局变量,供 TIM_Configuration() 计算 PSC 时引用 SYSCLK 频率。

步骤4:printf 重定向

重写 fputc() 将 printf 输出重定向到 USART1,并自动在 \n 前补 \r 确保串口工具正确换行。


int SER_PutChar(int ch)
{
    while (!USART_GetFlagStatus(USART_TEST, USART_FLAG_TC));
    USART_SendData(USART_TEST, (uint8_t)ch);
    return ch;
}

int fputc(int c, FILE *f)
{
    if (c == '\n')
        SER_PutChar('\r');         // 补回车,确保串口工具正确换行
    return (SER_PutChar(c));
}

编译与下载

Keil MDK 中打开 TIM_Basic.uvprojxF7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。下载成功后按 RESET 键启动新固件。

运行验证

打开 WIZ UartTool V1.0,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。按 RESET 启动后,预期串口先打印时钟信息与 "TIM Basic Test.",随后每 10ms 打印一行 "TIM3_IRQHandler"。

SYSCLK: 72.0Mhz, HCLK: 72.0Mhz, PCLK1: 36.0Mhz, PCLK2: 72.0Mhz, ADCCLK: 36.0Mhz
TIM Basic Test.
TIM3_IRQHandler
TIM3_IRQHandler
TIM3_IRQHandler
TIM3_IRQHandler
...

看到 "TIM3_IRQHandler" 持续打印即说明定时器更新中断按预期每 10ms 触发一次。

常见问题

Q: 编译报错 "cannot open source file w55mh32.h"

检查 Project → Options → C/C++ → Include Paths,确保 SDK 的 Libraries 目录已添加。

Q: 串口只打印了时钟信息和 "TIM Basic Test.",但没有 "TIM3_IRQHandler"

中断未触发。按顺序检查:TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE) 是否调用 → NVIC_Init 是否调用且 NVIC_IRQChannelCmd=ENABLETIM_Cmd(TIM3, ENABLE) 是否调用。三者缺一不可。

Q: 中断打印频率不对(不是 10ms 一次)

先看串口打印的 SYSCLK 频率是否正确(应为 72.0Mhz)。PSC = SYSCLK/1000000 - 1 是为了让 CK_CNT = 1MHz,若 SYSCLK 异常则 PSC 计算错误。也可直接写死 TIM_Prescaler = 71(72MHz 下)。

Q: 程序反复复位或串口输出卡死

中断服务函数中未清除中断标志(TIM_ClearITPendingBit),导致退出中断后立即再次进入,主循环无法执行。确认清除标志的语句在中断函数最前面。另外中断中 printf 耗时较长,若中断频率过高也会导致系统卡死。

Q: 换用 TIM6/TIM7 后编译报错或中断不触发

TIM6/TIM7 是基本定时器,无 TIM_CounterMode 成员(仅向上计数),需删除该行赋值。中断号需改为 TIM6_IRQn / TIM7_IRQn,中断函数名改为 TIM6_IRQHandler / TIM7_IRQHandler

总结

本文基于 W55MH32L 的 TIM3 实现了基本定时器时基与更新中断:

  • TIM3 配置为向上计数模式,PSC = SYSCLK/1M-1(CK_CNT=1MHz)、ARR = 9999,更新中断周期 10ms
  • NVIC 配置 TIM3 中断通道(抢占优先级 0、子优先级 3),TIM_ITConfig 使能更新中断
  • 中断服务函数 TIM3_IRQHandler 中检查标志 → 清除标志 → 打印函数名
  • 关键公式:更新周期 = (PSC+1) × (ARR+1) / TIMxCLK,掌握此公式即可配置任意定时周期

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5 配套开发板

Andy

W5500 技术支持专家