全部例程

TIM - 高级定时器

W55MH32

更新于 2026年8月13日

本篇总结

使用 TIM1,输出带死区与刹车保护的互补 PWM。

🔑 KEIL MDK、UART调试、TIM

本示例演示 W55MH32L 高级控制定时器 TIM1 的三项核心能力:互补 PWM 输出死区时间插入刹车输入保护。TIM1 在 CH1(PA8)与 CH1N(PB13)上输出极性相反、带死区延时的 PWM 波形,PB12 配置为刹车输入,触发后硬件立即关断所有输出——这是电机驱动、开关电源等场景中防止上下桥臂直通的标准做法。
全程由硬件完成输出与保护,CPU 只需初始化后空转。

TIM 简介

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

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

本例程使用 TIM1 通道 1:主输出 PA8 (CH1) 与互补输出 PB13 (CH1N) 组成一对带死区的互补 PWM,刹车输入 PB12 (BKIN) 触发后硬件封锁输出。

前置工具准备

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

  • WIZ UartTool V1.0:串口日志观察工具:点击下载
  • 示波器或逻辑分析仪:观察 PA8/PB13 互补 PWM 波形与死区延时(必需,串口看不到波形)

硬件连接

本例程使用 TIM1 的 CH1 / CH1N / BKIN 三个引脚输出互补 PWM 并监测刹车输入。USART1(PA9/PA10)用于打印状态日志。示波器探头分别接 PA8 与 PB13 即可观察带死区的互补波形;将 PB12 短接到 3V3 可触发刹车。

引脚功能方向模式
PA8TIM1_CH1(主输出)MCU → 示波器复用推挽输出 (AF_PP)
PB13TIM1_CH1N(互补输出)MCU → 示波器复用推挽输出 (AF_PP)
PB12TIM1_BKIN(刹车输入)外部 → MCU上拉输入 (IPU)
PA9USART1_TX开发板 → PC复用推挽输出 (AF_PP)
PA10USART1_RXPC → 开发板浮空输入 (IN_FLOATING)

工程结构

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

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

步骤1:GPIO 初始化

文件:USER/main.c | 函数:TIM_Adv_GPIO_Configuration() — 配置 PA8(CH1)、PB13(CH1N) 为复用推挽输出,PB12(BKIN) 为上拉输入。


void TIM_Adv_GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    /* 开启 GPIOA / GPIOB 时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

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

    /* PB13: TIM1_CH1N → 复用推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* PB12: TIM1_BKIN → 上拉输入(拉高触发刹车) */
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

互补输出两路引脚都必须配置为复用推挽输出(AF_PP),否则 CH1N 不会有波形。BKIN 配置为上拉输入,外部拉高即触发刹车。

如果你想自定义

  • 换用其他通道对 → CH2/CH2N 对应 PA9/PB14,CH3/CH3N 对应 PA10/PB15。注意 PA9/PA10 与 USART1 冲突,本例选 CH1 避开串口
  • 改刹车极性 → 将 BKIN 改为下拉输入(GPIO_Mode_IPD),并在 BDTR 配置中改 TIM_BreakPolarity_Low

步骤2:TIM1 时基与互补 PWM 配置

函数:TIM_Adv_Configuration() — 配置 TIM1 时基(1kHz PWM)与 CH1 互补输出比较(PWM1 模式,占空比 25%)。


void TIM_Adv_Configuration(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;

    /* 开启 TIM1 时钟(TIM1 挂在 APB2 上) */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    /* 时基:PSC=71 → CK_CNT=1MHz;ARR=999 → PWM 周期 1ms (1kHz) */
    TIM_TimeBaseStructure.TIM_Period            = 999;                    // ARR
    TIM_TimeBaseStructure.TIM_Prescaler         = 71;                     // PSC,72MHz/72=1MHz
    TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;     // 向上计数
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;                      // 每次溢出都产生更新事件
    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    /* CH1 互补输出比较:PWM1 模式,占空比 25%(CCR=250) */
    TIM_OCInitStructure.TIM_OCMode      = TIM_OCMode_PWM1;                // CNT<CCR 时输出有效电平
    TIM_OCInitStructure.TIM_OutputState  = TIM_OutputState_Enable;        // 主输出 OCx 使能
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;       // 互补输出 OCxN 使能
    TIM_OCInitStructure.TIM_Pulse       = 250;                            // CCR,占空比 = 250/1000 = 25%
    TIM_OCInitStructure.TIM_OCPolarity  = TIM_OCPolarity_High;            // 主输出高电平有效
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;           // 互补输出高电平有效
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;          // 空闲时主输出低
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;        // 空闲时互补输出低
    TIM_OC1Init(TIM1, &TIM_OCInitStructure);

    /* 使能 CCR1 预装载,运行期改 CCR 会在下个更新事件生效 */
    TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM1, ENABLE);
}

PWM 频率 = 72MHz / (71+1) / (999+1) = 1kHz,占空比 = 250 / 1000 = 25%TIM_OutputNState_Enable 是互补输出关键,漏写则 CH1N 引脚无波形。TIM_RepetitionCounter 是高级定时器独有,本例设 0 表示每次溢出都更新。

如果你想自定义

  • 改 PWM 频率 → 修改 TIM_Prescaler(PSC)和 TIM_Period(ARR)。如 20kHz:PSC=0、ARR=3599
  • 改占空比 → 修改 TIM_Pulse(CCR)。运行期可调用 TIM_SetCompare1(TIM1, x) 实时改占空比
  • 改为中央对齐 PWMTIM_CounterMode_CenterAligned1,频率减半,适合电机控制

步骤3:BDTR 死区与刹车配置

函数:TIM_Adv_BDTR_Configuration() — 配置死区时间、刹车极性、自动恢复,并使能主输出 MOE 启动定时器。这是高级定时器独有步骤。


void TIM_Adv_BDTR_Configuration(void)
{
    TIM_BDTRInitTypeDef TIM_BDTRInitStructure;

    /* 死区时间 DTG=0xFF:在 tDTS=72MHz 下约 1.75us(最大档位) */
    TIM_BDTRInitStructure.TIM_OSSRState       = TIM_OSSRState_Enable;     // 运行模式下空闲时输出由 OCx 决定
    TIM_BDTRInitStructure.TIM_OSSIState       = TIM_OSSIState_Enable;     // 空闲模式下输出由 OISx 决定
    TIM_BDTRInitStructure.TIM_LOCKLevel       = TIM_LOCKLevel_OFF;        // 不锁定,可随时改配置
    TIM_BDTRInitStructure.TIM_DeadTime        = 0xFF;                     // 死区发生器,最大死区
    TIM_BDTRInitStructure.TIM_Break           = TIM_Break_Enable;         // 使能刹车输入
    TIM_BDTRInitStructure.TIM_BreakPolarity   = TIM_BreakPolarity_High;   // BKIN 高电平有效(拉高触发刹车)
    TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; // 刹车解除后自动恢复输出
    TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

    /* 使能主输出 MOE(高级定时器必须)+ 启动计数器 */
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
    TIM_Cmd(TIM1, ENABLE);
}

死区时间 DTG 是 10 位编码值,0xFF 对应 约 1.75μs(tDTS = 1/72MHz),示波器可清晰观察到 CH1 下降沿与 CH1N 上升沿之间的"死区"无脉冲区间。TIM_CtrlPWMOutputs(TIM1, ENABLE)高级定时器最容易漏掉的一步——它置位 BDTR.MOE,否则无论 OC 怎么配,引脚都没有输出。

如果你想自定义

  • 改死区时间 → DTG 取值与死区关系:0x00无死区;0x80约 1.0μs;0xFF约 1.75μs(72MHz 下)
  • 刹车后手动恢复而非自动TIM_AutomaticOutput_Disable,故障解除后需软件调用 TIM_CtrlPWMOutputs(TIM1, ENABLE) 重新使能

步骤4:主函数逻辑

函数:main() — 初始化延时与 UART → 依次调用 GPIO / 时基+OC / BDTR 配置 → 进入循环周期性打印刹车状态。


int main(void)
{
    delay_init();
    UART_Configuration(115200);

    printf("\nTIM1 Advanced Complementary PWM Test.\n");
    printf("CH1=PA8, CH1N=PB13, BKIN=PB12\n");

    /* 依次初始化:GPIO → 时基+OC → BDTR+启动 */
    TIM_Adv_GPIO_Configuration();
    TIM_Adv_Configuration();
    TIM_Adv_BDTR_Configuration();

    while (1)
    {
        /* 读取 BDTR.MOE 主输出使能位:1=正常输出,0=刹车触发 */
        if (TIM1->BDTR & TIM_BDTR_MOE)
            printf("PWM Running  (MOE=1)\n");
        else
            printf("Break Active (MOE=0) -- PB12 pulled high!\n");

        delay_ms(500);
    }
}

主循环每 500ms 读取一次 BDTR.MOE 位:正常输出时为 1,PB12 被拉高触发刹车后硬件自动清 0,解除后(自动恢复模式下)又置回 1。PWM 波形由硬件持续输出,CPU 不参与。

步骤5: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_Advanced.uvprojxF7 编译,确认 0 Error(s)。连接 DEBUG USB 口到 PC,F8 下载。下载成功后按 RESET 键启动新固件。

运行验证

打开 WIZ UartTool V1.0 ,选择开发板对应 COM 口,波特率 115200 / 8N1,打开串口。同时用示波器双通道探头分别接 PA8PB13 。按 RESET 启动后,预期串口持续打印 "PWM Running (MOE=1)",示波器上看到 1kHz、25% 占空比的互补 PWM,两路上升/下降沿之间有约 1.75μs 死区。

TIM1 Advanced Complementary PWM Test.
CH1=PA8, CH1N=PB13, BKIN=PB12
PWM Running  (MOE=1)
PWM Running  (MOE=1)
Break Active (MOE=0) -- PB12 pulled high!   ← 此时将 PB12 接 3V3
Break Active (MOE=0) -- PB12 pulled high!
PWM Running  (MOE=1)                         ← 断开 PB12 后自动恢复
PWM Running  (MOE=1)

PB12 短接到 3V3 (高电平有效)→ 串口立即打印 "Break Active",示波器上 PA8/PB13 同时变低无输出;断开 PB12 让其回到上拉默认电平后的下个更新事件,PWM 自动恢复。

常见问题

Q: PA8 完全没有 PWM 波形输出

最常见原因是漏掉 TIM_CtrlPWMOutputs(TIM1, ENABLE)。高级定时器必须置位 BDTR.MOE 主输出使能位才会输出,这是与通用定时器最大的区别。另外确认 TIM_Cmd(TIM1, ENABLE) 也已调用。

Q: PA8 有波形但 PB13(CH1N)一直是低电平

三个排查点:① TIM_OutputNState 是否设为 TIM_OutputNState_Enable;② PB13 的 GPIO 是否配置为复用推挽输出(AF_PP);③ GPIOB 时钟是否开启(RCC_APB2Periph_GPIOB)。

Q: 示波器上观察不到死区延时

死区时间通常只有几百纳秒到几微秒,需用示波器时基调到 1μs/div 以下并展开边沿。本例 DTG=0xFF 在 72MHz 下约 1.75μs,若设为 0x00 则无死区,CH1 与 CH1N 同时翻转。死区计算公式较复杂,详见参考手册 BDTR 寄存器 DTG 字段说明。

Q: PB12 触发刹车后 PWM 再也恢复不了

检查 TIM_AutomaticOutput 是否设为 TIM_AutomaticOutput_Enable。若设为 Disable,则刹车解除后 MOE 不会自动置位,需软件调用 TIM_CtrlPWMOutputs(TIM1, ENABLE) 手动恢复。另外 BKIN 极性若配反(实际电平与 TIM_BreakPolarity 不符),会一直处于刹车状态。

Q: 换用 TIM8 后无 PWM 输出

TIM8 与 TIM1 寄存器结构相同但引脚映射不同:TIM8_CH1 在 PC6、CH1N 在 PA7、BKIN 在 PA6。需同步修改 GPIO 初始化中的引脚和端口,并将 RCC_APB2Periph_TIM1 改为 RCC_APB2Periph_TIM8,所有 TIM1 替换为 TIM8

总结

本文基于 W55MH32L 的 TIM1 实现了高级定时器的互补 PWM + 死区 + 刹车三件套:

  • GPIO:PA8(CH1)、PB13(CH1N) 复用推挽,PB12(BKIN) 上拉输入
  • 时基 + 输出比较:PSC=71、ARR=999 → 1kHz PWM;CH1 PWM1 模式 25% 占空比,TIM_OutputNState_Enable 使能互补输出
  • BDTR:DTG=0xFF 死区约 1.75μs,BKIN 高电平有效,TIM_AutomaticOutput_Enable 故障解除自动恢复
  • 关键调用TIM_CtrlPWMOutputs(TIM1, ENABLE) 置位 MOE,是高级定时器输出必经一步

下载本章例程

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

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

Andy

W5500 技术支持专家