全部例程

TIM-高级定时器

W55MH32

更新于 2026年9月4日

本篇总结

使用 TIM3,输出 PWM 波形。

🔑 KEIL MDK、UART调试、TIM

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示 W55MH32 使用 TIM3 输出 PWM 波形。TIM3 在 CH2(PA7)上以 PWM2 模式输出固定频率与占空比的方波,通过 TIM_CtrlPWMOutputs 使能主输出后由硬件持续输出,CPU 初始化完成后空转。

TIM 简介

W55MH32 的高级控制定时器 TIM1/TIM8 在通用定时器基础上增加了三项专为电机/电源控制设计的能力:可编程死区互补输出(OCx + OCxN 双路带延时反相输出)、重复计数器 RCR(每 N 次溢出才产生一次更新事件)、刹车输入 BKIN(硬件立刻关断所有输出)。时基核心公式:PWM 周期 = (ARR+1) × (PSC+1) / TIMxCLK

定时器类型互补输出死区发生器刹车输入 BKIN重复计数器
TIM1、TIM8高级控制有(CHx/CHxN)有(10 位)有(8 位)
TIM2、3、4、5通用
TIM6、TIM7基本

本例程

前置工具准备

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

  • WIZ UartTool V1.0:串口调试工具:点击下载
  • 示波器或逻辑分析仪(可选):观察 PA7 的 PWM 波形

硬件连接

本例程使用 TIM3 的 CH2(PA7)输出 PWM 波形。USART1(PA9/PA10)通过板载 USB 转串口芯片打印状态日志。示波器探头接 PA7 即可观察 PWM 波形。仅需一根 Micro USB 线连接开发板 DEBUG USB 口到 PC。

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

工程结构

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

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

步骤1:TIM3 PWM 输出初始化

文件:USER/main.c | 函数:TIM_Configuration() — 配置 PA7 为复用推挽输出,TIM3 时基 1MHz、ARR=99(PWM 周期 100μs / 10kHz),CH2 输出 PWM2 模式、占空比 50%,使能预装载与主输出。

void TIM_Configuration(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;

    /* 开启 TIM3 及 GPIOA/AFIO 时钟 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    /* PA7: TIM3_CH2 → 复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 时基:PSC 使 CK_CNT=1MHz,ARR=99 → PWM 周期 100μs(10kHz) */
    TIM_TimeBaseStructure.TIM_Period        = 99;
    TIM_TimeBaseStructure.TIM_Prescaler     = clocks.SYSCLK_Frequency / 1000000 - 1;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    /* 输出比较:PWM2 模式,CH2,Pulse=49 → 占空比 49%,低电平有效 */
    TIM_OCInitStructure.TIM_OCMode       = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState  = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_Pulse        = 99 / 2;
    TIM_OCInitStructure.TIM_OCPolarity   = TIM_OCPolarity_Low;
    TIM_OCInitStructure.TIM_OCNPolarity  = TIM_OCNPolarity_Low;
    TIM_OCInitStructure.TIM_OCIdleState  = TIM_OCIdleState_Set;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);

    /* 使能预装载寄存器:运行中改 CCR/ARR 会在下个更新事件生效 */
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM3, ENABLE);

    /* 启动定时器 + 使能主输出 */
    TIM_Cmd(TIM3, ENABLE);
    TIM_CtrlPWMOutputs(TIM3, ENABLE);
}

六步完成:开时钟 → GPIO 复用推挽 → 时基参数(PSC=71 使 CK_CNT=1MHz、ARR=99)→ 输出比较参数(PWM2、Pulse=49、低电平有效)→ 预装载使能 → 启动定时器与主输出。PWM 周期 = (ARR+1)/CK_CNT = 100/1MHz = 100μs(10kHz),占空比 = Pulse/(ARR+1) = 49/100 = 49%TIM_CtrlPWMOutputs(TIM3, ENABLE) 使能主输出 MOE,是 PWM 输出必经一步。

如果你想自定义

  • 改 PWM 频率 → 修改 TIM_Period(ARR)和 TIM_Prescaler(PSC)。如要 1kHz:PSC=71(1MHz)、ARR=999
  • 改占空比 → 修改 TIM_Pulse(CCR)。如要 30% 占空比:TIM_Pulse = 99 * 3 / 10(即 29)
  • 换用 CH1/CH3/CH4 → 同步修改 GPIO 引脚(CH1=PA6、CH3=PA8、CH4=PA9)和 TIM_OCxInit / TIM_OCxPreloadConfig 的通道号

步骤2:RCC 时钟配置与主函数

函数:RCC_ClkConfiguration()main() — 先配置系统时钟(HSE 8MHz × 9 = 72MHz),再初始化延时与 UART(115200) → 打印时钟频率 → 配置 TIM3 PWM → 进入空循环,波形由硬件持续输出。

void RCC_ClkConfiguration(void)
{
    RCC_DeInit();

    RCC_HSEConfig(RCC_HSE_ON);
    while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);

    RCC_PLLCmd(DISABLE);
    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); /* HSE 8MHz × 9 = 72MHz */

    RCC_PLLCmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    RCC_HCLKConfig(RCC_SYSCLK_Div1);
    RCC_PCLK1Config(RCC_HCLK_Div2);
    RCC_PCLK2Config(RCC_HCLK_Div1);

    RCC_LSICmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
    RCC_HSICmd(ENABLE);
    while (RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
}

RCC_ClocksTypeDef clocks;

int main(void)
{
    RCC_ClkConfiguration();
    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 Out Test.\n");

    TIM_Configuration();

    while (1);
}

PWM 一旦配置完成即由硬件持续输出,while(1) 中无需任何干预。clocks 为全局变量,供 TIM_Configuration() 计算 PSC 时引用 SYSCLK 频率,保证换主频时无需改代码。

步骤3: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));
}

运行验证

打开 WIZ UartTool V1.0,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。按 RESET 启动后,串口打印时钟信息与 "TIM Out Test.";用示波器探头接 PA7,可观察到 10kHz、49% 占空比的方波。

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

看到 "TIM Out Test." 打印后,示波器上 PA7 应出现稳定的 PWM 波形。PWM 频率 = 72MHz / (71+1) / (99+1) = 10kHz,占空比 = 49/100 = 49%

常见问题

Q: PA7 没有 PWM 波形输出

按顺序检查:① GPIO 是否配为 GPIO_Mode_AF_PP(复用推挽,不是普通推挽);② TIM_OC2InitTIM_OutputState 是否为 Enable;③ TIM_Cmd(TIM3, ENABLE) 是否调用;④ TIM_CtrlPWMOutputs(TIM3, ENABLE) 是否调用——这是使能主输出 MOE 的关键步骤;⑤ 示波器探头地线是否接到开发板 GND。

Q: PWM 占空比与预期相反(该高的时候低)

PWM 模式与极性配合问题。本例用 PWM2 + OCPolarity_Low:CNT>CCR 时输出有效(低电平),所以 Pulse 越小、低电平时间越长。若想要"常规"高电平占空比,可改为 TIM_OCMode_PWM1 + TIM_OCPolarity_High,此时 Pulse/(ARR+1) 即为高电平占比。

Q: 换用 CH1/CH3/CH4 后编译报错或无输出

通道号必须三处一致:① GPIO 引脚(CH1=PA6、CH2=PA7、CH3=PA8、CH4=PA9,注意 CH4 与 USART1_TX 冲突);② TIM_OCxInitTIM_Channel_x;③ 预装载 TIM_OCxPreloadConfig。三处通道号不一致会导致编译通过但运行异常。

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

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

Q: 串口输出乱码

先看串口打印的时钟频率是否正常(SYSCLK 应为 72.0Mhz)。若频率异常,检查 RCC_ClkConfiguration 中 HSE_VALUE 是否与实际晶振一致。

总结

本文基于 W55MH32 的 TIM3 实现了 PWM 输出:

  • GPIO:PA7(TIM3_CH2)配置为复用推挽输出(AF_PP)
  • 时基 + 输出比较:PSC = SYSCLK/1M-1(CK_CNT=1MHz)、ARR=99,输出 10kHz / 49% 占空比方波;PWM2 模式、低电平有效
  • 预装载TIM_OC2PreloadConfig 使能 CCR 预装载,运行中可安全修改占空比
  • 关键调用TIM_CtrlPWMOutputs(TIM3, ENABLE) 使能主输出 MOE,是 PWM 输出必经一步
  • 核心公式:PWM 频率 = TIMxCLK / (PSC+1) / (ARR+1)占空比 = CCR / (ARR+1)

下载本章例程

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

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

Andy

W5500 技术支持专家