第8章 高级定时器介绍及应用

第八章 高级定时器介绍及应用

1. 高级定时简介

高级定时器的框图和通用定时器框图很类似,只是添加了其它的一些功能,如:重复计数器、带死区控制的互补输出通道、断路输入等。这些功能在高级定时器框图的位置如下:

屏幕截图 20250715 153841png

上图中,框出来三个部分,这是和通用定时器不同的地方,下面来分别介绍它们。

1.1 重复计数器

在 F4 系列中,高级定时器 TIM1 和 TIM8 都有重复计数器。下面来介绍一下重复计数器有什么作用?在学习基本定时器和通用定时器的时候,我们知道定时器发生上溢或者下溢时,会直接生成更新事件。但是有重复计数器的定时器并不完全是这样的,定时器每次发生上溢或下溢时,重复计数器的值会减一,当重复计数器的值为 0 时,再发生一次上溢或者下溢才会生成定时器更新事件。如果我们设置重复计数器寄存器 RCR 的值为 N,那么更新事件将在定时器发生 N+1 次上溢或下溢时发生。

这里需要注意的是重复计数器寄存器是具有影子寄存器的,所以 RCR 寄存器只是起缓冲的作用。 RCR 寄存器的值会在更新事件发生时,被转移至其影子寄存器中,从而真正生效。

重复计数器的特性,在控制生成 PWM 信号时很有用,后面会有相应的实验。

1.2 输出比较

高级定时器输出比较部分和通用定时器相比,多了带死区控制的互补输出功能。

IMx_CH1N、 TIMx_CH2N 和 TIMx_CH3N 分别是定时器通道 1、通道 2 和通道 3的互补输出通道,通道 4 是没有互补输出通道的。 DTG 是死区发生器,死区时间由 DTG[7:0]位来配置。 如果不使用互补通道和死区时间控制,那么高级定时器 TIM1 和 TIM8 和通用定时器的输出比较部分使用方法基本一样,只是要注意 MOE 位得置 1 定时器才能输出。

如果使用互补通道,那么就有一定的区别了,具体我们在高级定时器互补输出带死区控制实验小节再来介绍

1.3 断路功能

断路功能也称刹车功能,一般用于电机控制的刹车。 F4 系列有一个断路通道,断路源可以是刹车输入引脚(TIMx_BKIN),也可以是一个时钟失败事件。时钟失败事件由复位时钟控制器中的时钟安全系统产生。系统复位后,断路功能默认被禁止, MOE 位为低。

使能断路功能的方法:将 TIMx_BDTR 的位 BKE 置 1。断路输入引脚 TIMx_BKIN 的输入有效电平可通过 TIMx_BDTR 寄存器的位 BKP 设置。

使能刹车功能后:由 TIMx_BDTR 的 MOE、 OSSI、 OSSR 位, TIMx_CR2 的 OISx、 OISxN位, TIMx_CCER 的 CCxE、 CCxNE 位控制 OCx 和 OCxN 输出状态。无论何时, OCx 和 OCxN输出都不能同时处在有效电平。

当发生断路输入后,会怎么样?

  1. MOE 位被异步地清零, OCx 和 OCxN 为无效、空闲或复位状态(由 OSSI 位选择)。

  2. OCx 和 OCxN 的状态:由相关控制位状态决定,当使用互补输出时:根据情况自动控制输出电平,参考《STM32F4xx 参考手册_V4(中文版).pdf》手册第 382 页的表 73 具有断路功能的互补通道 Ocx 和 OcxN 的控制位。

  3. BIF 位置 1,如果使能了 BIE 位,还会产生刹车中断;如果使能了 TDE 位,会产生 DMA请求。

  4. 如果 AOE 位置 1,在下一个更新事件 UEV 时, MOE 位被自动置 1。

2. 高级定时器使用示例

2.1 输出指定个数脉冲

要实现定时器输出指定个数脉冲,只需要掌握下面几点内容:

第一,如果大家还不清楚定时器是如何输出 PWM 的,请回顾通用定时器 PWM 输出实验的内容,这部分的知识是一样的。但是需要注意的是:我们需要把 MOE 位置 1,这样高级定时器的通道才能输出。

第二,要清楚重复计数器特性,设置重复计数器寄存器 RCR 的值为 N,那么更新事件将在定时器发生 N+1 次上溢或下溢时发生。换句话来说就是,想要指定输出 N 个 PWM,只需要把N-1 写入 RCR 寄存器。因为在边沿对齐模式下,定时器溢出周期对应着 PWM 周期,我们只要在更新事件发生时,停止输出 PWM 就行。

第三,为了保证定时器输出指定个数的脉冲后,定时器马上停止继续输出,我们使能更新中断,并在定时器中断里关闭计数器。

2.1.1 TIM8相关参数宏定义

#ifndef __ATIM_H
#define __ATIM_H

#include <sys.h>

/* 使用TIM8通道1输出PWM */
#define PWM_TIMER               TIM8
#define PWM_CHANNEL            TIM_CHANNEL_1
#define PWM_GPIO_PORT          GPIOC
#define PWM_GPIO_PIN           GPIO_PIN_6
#define PWM_GPIO_AF            GPIO_AF3_TIM8
#define PWM_IRQn               TIM8_UP_TIM13_IRQn
#define PWM_IRQHandler         TIM8_UP_TIM13_IRQHandler

/* 函数声明 */
void tim8_pwm_init(uint16_t arr, uint16_t psc);
void tim8_set_pulse_count(uint32_t count);

#endif /* __ATIM_H */

2.1.2 PWM初始化

/* PWM初始化 */
void tim8_pwm_init(uint16_t arr, uint16_t psc) {
    GPIO_InitTypeDef gpio_init = {0};
    TIM_OC_InitTypeDef oc_init = {0};

    /* 1. 使能时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_TIM8_CLK_ENABLE();

    /* 2. 配置GPIO */
    gpio_init.Pin = PWM_GPIO_PIN;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_PULLUP;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init.Alternate = PWM_GPIO_AF;
    HAL_GPIO_Init(PWM_GPIO_PORT, &gpio_init);

    /* 3. 配置定时器 */
    tim8_handle.Instance = PWM_TIMER;
    tim8_handle.Init.Prescaler = psc;
    tim8_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    tim8_handle.Init.Period = arr;
    tim8_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    tim8_handle.Init.RepetitionCounter = 0;
    HAL_TIM_PWM_Init(&tim8_handle);

    /* 4. 配置PWM通道 */
    oc_init.OCMode = TIM_OCMODE_PWM1;
    oc_init.Pulse = arr / 2;  // 默认50%占空比
    oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
    HAL_TIM_PWM_ConfigChannel(&tim8_handle, &oc_init, PWM_CHANNEL);

    /* 5. 配置中断 */
    HAL_NVIC_SetPriority(PWM_IRQn, 1, 3);
    HAL_NVIC_EnableIRQ(PWM_IRQn);
    __HAL_TIM_ENABLE_IT(&tim8_handle, TIM_IT_UPDATE);

    /* 6. 启动PWM */
    HAL_TIM_PWM_Start(&tim8_handle, PWM_CHANNEL);
}

2.1.3 设置脉冲数

void tim8_set_pulse_count(uint32_t count) {
    if (count == 0) return;

    pulse_remain = count;
    HAL_TIM_GenerateEvent(&tim8_handle, TIM_EVENTSOURCE_UPDATE);
    __HAL_TIM_ENABLE(&tim8_handle);
}

2.1.4 TIM中断处理函数

/* 定时器中断处理 */
void PWM_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&tim8_handle, TIM_FLAG_UPDATE)) {
        uint16_t pulses = 0;

        if (pulse_remain > 0) {
            pulses = (pulse_remain > 256) ? 256 : pulse_remain;
            pulse_remain -= pulses;

            PWM_TIMER->RCR = pulses - 1;  // 设置重复计数器
            HAL_TIM_GenerateEvent(&tim8_handle, TIM_EVENTSOURCE_UPDATE);
            __HAL_TIM_ENABLE(&tim8_handle);
        } else {
            PWM_TIMER->CR1 &= ~TIM_CR1_CEN;  // 关闭定时器
        }

        __HAL_TIM_CLEAR_IT(&tim8_handle, TIM_IT_UPDATE);
    }
}

2.1.4 主函数测试

#include <bsp_init.h>
#include <atim.h>
#include <stdio.h>

int main(void) {
    uint8_t key_value = 0;
    uint8_t i = 0;
    GPIO_InitTypeDef GPIO_InitStructure;
    bsp_init();
    GPIO_InitStructure.Pin = LED1_GPIO_Pin;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    GPIO_InitStructure.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStructure);
    tim8_pwm_init(10000-1, 8400-1);
    TIM8->CCR1 = 5000; // 占空比为50%
    tim8_set_pulse_count(5); // 设置脉冲数为5
    while(1)
    {
        key_value = key_scan(0);
        if(key_value == KEY0_Press)
        {
            tim8_set_pulse_count(10); // 设置脉冲数为10
        }
        i++;
        delay_ms(10);
        if(i>50)
        {
            i=0;
            LED_TOGGLE(LED0_GPIO_Pin);
        }
    }
}

2.2 输出比较模式

该程序使用STM32的TIM8定时器在翻转模式(Toggle Mode)下生成四路50%占空比但相位不同的PWM波。关键机制如下:

  1. 定时器配置
  • 定时器时钟源频率 = APB2时钟 × 2(如HAL_RCC_GetPCLK2Freq() * 2

  • 预分频器PSC = 168-1 → 分频后计数器频率 = 定时器时钟 / 168

  • 自动重装载值ARR = 1000-1 → 计数周期为1000个时钟(0~999)

  1. 翻转模式(TIM_OCMODE_TOGGLE)
  • 当计数器值等于比较寄存器CCRx时,输出电平翻转

  • 每个通道需要两次翻转(上升沿+下降沿)才能形成一个完整周期

  • 输出信号周期 = 2 × 定时器周期 = 2000个计数时钟

  1. 相位控制原理
  • 相位由比较值CCRx决定:上升沿出现的位置

  • 相位差 = (CCRx2 - CCRx1) / 定时器周期 × 360°

  • 通过设置不同的CCRx值实现相位偏移:

    • CH1: CCR1=249 → 相位 ≈ 89.6°(≈90°)

    • CH2: CCR2=499 → 相位 ≈ 179.6°(≈180°)

    • CH3: CCR3=749 → 相位 ≈ 269.6°(≈270°)

    • CH4: CCR4=998 → 相位 ≈ 359.3°(≈0°)

  1. 占空比固定为50%
  • 无论CCRx为何值,高低电平时间各占1000个计数时钟

  • 原因:两次翻转间隔总是1000个时钟(见下方时序分析)

PWM参数详解

参数 符号 作用 本例设置
时钟频率 f_clk 定时器时钟源频率 APB2×2
预分频器 PSC 降低计数器频率 168-1
计数周期 ARR 计数器从0计到ARR后重置 1000-1
比较值 CCRx 触发翻转/捕获事件的计数值 249/499/749/998
输出模式 - 比较输出行为(翻转/PWM) TOGGLE
输出频率 f_out 实际信号频率 = f_clk/(PSC+1)/(2×(ARR+1)) ~238 Hz
占空比 - 高电平时间/总周期 固定50%
相位 φ 上升沿相对于周期起点的偏移 90°间隔

2.2.1 TIM8相关参数宏定义

#ifndef __ATIM_H
#define __ATIM_H

#include <sys.h>

void TIM8_PWM_Init(uint16_t arr, uint16_t psc);

#define TIM8_CH1_CCRx TIM8->CCR1
#define TIM8_CH2_CCRx TIM8->CCR2
#define TIM8_CH3_CCRx TIM8->CCR3
#define TIM8_CH4_CCRx TIM8->CCR4

#endif /* __ATIM_H */

2.2.2 四路PWM配置输出比较模式

#include <atim.h>

TIM_HandleTypeDef TIM8_Handler;

void TIM8_PWM_Init(uint16_t arr, uint16_t psc)
{
    TIM_OC_InitTypeDef sConfigOC;
    /* TIM8 参数配置 */
    TIM8_Handler.Instance = TIM8;
    TIM8_Handler.Init.Prescaler = psc;
    TIM8_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    TIM8_Handler.Init.Period = arr;
    TIM8_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    TIM8_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    HAL_TIM_OC_Init(&TIM8_Handler);
    /* PWM CHx配置 */
    sConfigOC.OCMode = TIM_OCMODE_TOGGLE; // 比较输出模式翻转功能
    sConfigOC.Pulse = 250-1; // 输出比较寄存器的值
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 输出比较极性高
    HAL_TIM_OC_ConfigChannel(&TIM8_Handler, &sConfigOC, TIM_CHANNEL_1); // 配置通道1
    __HAL_TIM_ENABLE_OCxPRELOAD(&TIM8_Handler, TIM_CHANNEL_1); // 使能通道1预装载寄存器
    sConfigOC.Pulse = 500-1;
    HAL_TIM_OC_ConfigChannel(&TIM8_Handler, &sConfigOC, TIM_CHANNEL_2); // 配置通道2
    __HAL_TIM_ENABLE_OCxPRELOAD(&TIM8_Handler, TIM_CHANNEL_2);
    sConfigOC.Pulse = 750-1;
    HAL_TIM_OC_ConfigChannel(&TIM8_Handler, &sConfigOC, TIM_CHANNEL_3); // 配置通道3
    __HAL_TIM_ENABLE_OCxPRELOAD(&TIM8_Handler, TIM_CHANNEL_3);
    sConfigOC.Pulse = 1000-1;
    sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; // 输出比较空闲状态复位
    HAL_TIM_OC_ConfigChannel(&TIM8_Handler, &sConfigOC, TIM_CHANNEL_4); // 配置通道4
    __HAL_TIM_ENABLE_OCxPRELOAD(&TIM8_Handler, TIM_CHANNEL_4);
    HAL_TIM_OC_Start(&TIM8_Handler, TIM_CHANNEL_1); // 开启通道1
    HAL_TIM_OC_Start(&TIM8_Handler, TIM_CHANNEL_2); // 开启通道2
    HAL_TIM_OC_Start(&TIM8_Handler, TIM_CHANNEL_3); // 开启通道3
    HAL_TIM_OC_Start(&TIM8_Handler, TIM_CHANNEL_4); // 开启通道4
}

// GPIO底层驱动
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance==TIM8)
    {
        GPIO_InitTypeDef GPIO_Initure;

        __HAL_RCC_TIM8_CLK_ENABLE();
        __HAL_RCC_GPIOC_CLK_ENABLE();

        GPIO_Initure.Pin = GPIO_PIN_6;
        GPIO_Initure.Mode = GPIO_MODE_AF_PP;
        GPIO_Initure.Pull = GPIO_NOPULL;
        GPIO_Initure.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_Initure.Alternate = GPIO_AF3_TIM8;
        HAL_GPIO_Init(GPIOC, &GPIO_Initure);
        GPIO_Initure.Pin = GPIO_PIN_7;
        HAL_GPIO_Init(GPIOC, &GPIO_Initure);
        GPIO_Initure.Pin = GPIO_PIN_8;
        HAL_GPIO_Init(GPIOC, &GPIO_Initure);
        GPIO_Initure.Pin = GPIO_PIN_9;
        HAL_GPIO_Init(GPIOC, &GPIO_Initure);
    }
}

2.2.3 主函数测试

#include <bsp_init.h>
#include <atim.h>
#include <stdio.h>

int main(void) {
    bsp_init();
    TIM8_PWM_Init(1000-1, 168-1);
    TIM8_CH1_CCRx = 250 - 1; /* 通道 1 相位 25% */
    TIM8_CH2_CCRx = 500 - 1; /* 通道 2 相位 50% */
    TIM8_CH3_CCRx = 750 - 1; /* 通道 3 相位 75% */
    TIM8_CH4_CCRx = 999 - 1; /* 通道 4 相位 99% */
    while(1) {
        LED_TOGGLE(LED0_GPIO_Pin);
        HAL_Delay(500);

        // 添加串口心跳包,确认程序运行
        static uint32_t counter = 0;
        if((counter++ % 10) == 0) {
            printf("System running... %lus\r\n", counter/2);
        }
    }
}

2.3 互补输出带死区控制

屏幕截图 20250717 211207png

上图中, CH1 输出黄色的 PWM,它的互补通道 CH1N 输出绿色的 PWM。通过对比,可以知道这两个 PWM 刚好是反过来的, CH1 的 PWM 为高电平期间, CH1N 的 PWM 则是低电平,反之亦然,这就是互补输出。

下面来看一下什么是带死区控制的互补输出?

屏幕截图 20250717 211318png

上图中, CH1 输出的 PWM 和 CH1N 输出的 PWM 在高低电平转换间,插入了一段时间才实现互补输出。这段时间称为死区时间,可以通过 TIMx_BDTR 寄存器的 DTG[7:0]位配置控制死区时间的长度,后面会详细讲解如何配置死区时间。上图中,箭头指出的两段死区时间的长度是一样的,因为都是由同一个死区发生器产生。

下面来看一下定时器的死区时间是怎么计算并设置的?死区时间是由 TIMx_CR1 寄存器的CKD[1:0]位和 TIMx_BDTR 寄存器的 DTG[7:0]位来设置,如下图所示

屏幕截图 20250717 211521png

死区时间计算分三步走:

第一步:通过 CKD[1:0]位确定 tDTS。根据 CKD[1:0]位的描述,可以得到下面的式子:

屏幕截图 20250717 211545png

其中:

CKD[1:0]: CKD[1:0]位设置的值。Tclk: 定时器的时钟源频率(单位为 MHz)。

假设定时器时钟源频率是 168MHz,我们设置 CKD[1:0]位的值为 2,代入上面的式子可得:

屏幕截图 20250717 211625png

通过上式可得 tDTS 约等于 23.81ns,本实验例程中我们也是这样设置的。

第二步:根据 DTG[7:5]选择计算公式。

第三步:代入选择的公式计算。

下面给大家举个例子,假设定时器时钟源频率是 168MHz,我们设置 CKD[1:0]位的值为 2, DTG[7:0]位的值为 250。从上面的例子知道 CKD[1:0]位的值为 2,得到的 tDTS=23.81ns。下面来看一下 DTG[7:0]位的值为 250,应该选择 DTG[7:0]位描述中哪条公式? 250 的二进制数为11111010,即 DTG[7:5]为 111,所以选择第四条公式: DT=(32+ DTG[4:0]) * t dtg, 其中 t dtg = 16 * tDTS。可以看到手册上的式子符号大小写乱乱的,这里大小写不敏感。由手册的公式可以得到 DT = (32+ DTG[4:0]) * 16 * tDTS = (32+ 26) * 16 * 23.81ns = 22095.68ns = 22.01us,即死区时间为 22.01us。

  1. 利用 TIM1_CH1(PE9)输出 70%占空比的 PWM 波,它的互补输出通道(PE8)则是输出 30%占空比的 PWM 波。

  2. 刹车功能,当给刹车输入引脚(PE15)输入高电平时,进行刹车,即 PE8 和 PE9 停止输出 PWM 波。

2.3.1 TIM1模式配置

/**
  * @brief  初始化高级定时器1的互补PWM输出
  * @param  arr: 自动重装载值 (决定PWM周期)
  * @param  psc: 预分频值 (决定定时器时钟频率)
  * @note   定时器频率 = 系统时钟 / (psc + 1)
  *         PWM频率 = 定时器频率 / (arr + 1)
  *         配置引脚: PE9(OCy), PE8(OCyN), PE15(BKIN)
  */
void ATIM_PWM_Init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_OC_InitTypeDef TIM_OCInitStructure;
    HAL_StatusTypeDef status;

    printf("[ATIM] 开始初始化互补PWM输出...\r\n");

    /* 步骤1: 使能时钟 */
    printf("[ATIM] 使能TIM1和GPIOE时钟...\r\n");
    __HAL_RCC_TIM1_CLK_ENABLE();      // 使能TIM1时钟
    __HAL_RCC_GPIOE_CLK_ENABLE();     // 使能GPIOE时钟

    /* 步骤2: 配置GPIO引脚 */
    printf("[ATIM] 配置PE8(OCyN), PE9(OCy), PE15(BKIN)为复用推挽输出...\r\n");
    GPIO_InitStructure.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_15;  // PE8, PE9, PE15
    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;      // 复用推挽输出
    GPIO_InitStructure.Pull = GPIO_PULLDOWN;        // 下拉电阻
    GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式
    GPIO_InitStructure.Alternate = GPIO_AF1_TIM1;   // 复用为TIM1功能
    HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);      // 应用配置

    /* 步骤3: 配置定时器基础参数 */
    printf("[ATIM] 配置定时器基础参数(ARR=%u, PSC=%u)...\r\n", arr, psc);
    timx_pwm_handle.Instance = TIM1;                // 使用TIM1
    timx_pwm_handle.Init.Prescaler = psc;           // 预分频值
    timx_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    timx_pwm_handle.Init.Period = arr;              // 自动重装载值
    timx_pwm_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV4; // 时钟4分频
    timx_pwm_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 自动重载预装载使能

    // 应用定时器配置
    status = HAL_TIM_PWM_Init(&timx_pwm_handle);
    if(status != HAL_OK) {
        printf("[错误] 定时器初始化失败: %d\r\n", status);
        return;
    }

    /* 步骤4: 配置PWM通道 */
    printf("[ATIM] 配置PWM通道1参数...\r\n");
    TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;   // PWM模式1
    TIM_OCInitStructure.Pulse = 0;                  // 初始占空比为0
    TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH; // 输出极性高
    TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH; // 互补输出极性高
    TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET; // 空闲状态输出高
    TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_SET; // 互补输出空闲状态高

    // 应用PWM通道配置
    status = HAL_TIM_PWM_ConfigChannel(&timx_pwm_handle, &TIM_OCInitStructure, TIM_CHANNEL_1);
    if(status != HAL_OK) {
        printf("[错误] PWM通道配置失败: %d\r\n", status);
        return;
    }

    /* 步骤5: 配置刹车和死区时间 */
    printf("[ATIM] 配置刹车和死区参数...\r\n");
    sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;   // 运行模式关闭状态禁用
    sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;  // 空闲模式关闭状态禁用
    sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;        // 无锁定
    sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;        // 使能刹车输入
    sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; // 刹车高电平有效
    sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE; // 自动输出使能
    sBreakDeadTimeConfig.DeadTime = 0;                         // 初始死区时间为0

    // 应用刹车和死区配置
    status = HAL_TIMEx_ConfigBreakDeadTime(&timx_pwm_handle, &sBreakDeadTimeConfig);
    if(status != HAL_OK) {
        printf("[错误] 刹车/死区配置失败: %d\r\n", status);
        return;
    }

    /* 步骤6: 启动PWM输出 */
    printf("[ATIM] 启动PWM主输出和互补输出...\r\n");
    HAL_TIM_PWM_Start(&timx_pwm_handle, TIM_CHANNEL_1);      // 启动主输出
    HAL_TIMEx_PWMN_Start(&timx_pwm_handle, TIM_CHANNEL_1);   // 启动互补输出

    printf("[ATIM] PWM初始化成功!\r\n");
}

2.3.2 计算死区时间

/**
  * @brief  计算死区时间并打印结果
  * @param  dtg: 死区时间生成值 (DTG寄存器值)
  * @note   根据STM32F4参考手册计算实际死区时间
  *         死区时间取决于DTG值和tDTS(定时器分频后时钟周期)
  */
static void calc_dead_time(uint8_t dtg) 
{
    /* 死区时间计算基础:
     * tDTS = 1 / (定时器输入时钟 / 时钟分频系数)
     * 本例: 系统时钟168MHz, CKD[1:0]=2(4分频) -> 42MHz
     * tDTS = 1 / 42MHz ≈ 23.81ns
     */
    const float tDTS = 23.81f; // 单位: 纳秒(ns)
    float dead_time = 0;       // 计算得到的死区时间

    /* 提取DTG的高3位(决定计算模式) */
    uint8_t mode = (dtg >> 5) & 0x07;

    /* 根据模式选择不同的计算公式 */
    if(mode <= 3) { 
        // 模式0xx: 死区时间 = dtg[7:0] * tDTS
        dead_time = dtg * tDTS;
        printf("[ATIM] 模式0xx: DT = DTG * tDTS\r\n");
    } 
    else if(mode == 4 || mode == 5) { 
        // 模式10x: 死区时间 = (64 + dtg[6:0]) * 2 * tDTS
        uint8_t dtg_val = dtg & 0x7F; // 取低7位
        dead_time = (64.0f + dtg_val) * 2.0f * tDTS;
        printf("[ATIM] 模式10x: DT = (64 + DTG[6:0]) * 2 * tDTS\r\n");
    }
    else if(mode == 6) { 
        // 模式110: 死区时间 = (32 + dtg[5:0]) * 8 * tDTS
        uint8_t dtg_val = dtg & 0x3F; // 取低6位
        dead_time = (32.0f + dtg_val) * 8.0f * tDTS;
        printf("[ATIM] 模式110: DT = (32 + DTG[5:0]) * 8 * tDTS\r\n");
    }
    else if(mode == 7) { 
        // 模式111: 死区时间 = (32 + dtg[5:0]) * 16 * tDTS
        uint8_t dtg_val = dtg & 0x3F; // 取低6位
        dead_time = (32.0f + dtg_val) * 16.0f * tDTS;
        printf("[ATIM] 模式111: DT = (32 + DTG[5:0]) * 16 * tDTS\r\n");
    }

    /* 打印DTG值的二进制表示(带格式) */
    printf("[ATIM] DTG=0x%02X (二进制: ", dtg);
    for(int i = 7; i >= 0; i--) {
        printf("%d", (dtg >> i) & 1);
        if(i == 5) printf(" "); // 在DTG[5]后加空格分组
    }
    printf(")\r\n");

    /* 打印计算结果 */
    printf("[ATIM] 计算死区时间 = %.2f ns (约 %.2f μs)\r\n", 
           dead_time, dead_time / 1000.0f);
}

2.3.3 设置死区时间和占空比

/**
  * @brief  设置死区时间和PWM占空比
  * @param  ccr: 捕获比较值 (决定PWM占空比)
  * @param  dtg: 死区时间生成值 (0-255)
  * @note   占空比 = ccr / (arr + 1)
  *         死区时间由dtg值根据特定公式计算得出
  */
void ATIM_PWM_SetDeadZone(uint16_t ccr, uint8_t dtg)
{
    HAL_StatusTypeDef status;

    printf("\r\n[ATIM] 设置死区时间 (CCR=%u, DTG=0x%02X)\r\n", ccr, dtg);

    /* 计算并打印死区时间 */
    calc_dead_time(dtg);

    /* 更新死区时间配置 */
    sBreakDeadTimeConfig.DeadTime = dtg;
    status = HAL_TIMEx_ConfigBreakDeadTime(&timx_pwm_handle, &sBreakDeadTimeConfig);
    if(status != HAL_OK) {
        printf("[错误] 死区时间更新失败: %d\r\n", status);
        return;
    }

    /* 设置捕获比较值(CCR) */
    TIM1->CCR1 = ccr;

    /* 使能主输出(Main Output Enable) */
    __HAL_TIM_MOE_ENABLE(&timx_pwm_handle);

    printf("[ATIM] 死区时间设置成功\r\n");
}

2.3.4 主函数测试

#include <bsp_init.h>
#include <atim.h>

int main(void) 
{
    bsp_init();
    printf("\r\n===== PWM Complementary Output Demo =====\r\n");
    // PWM初始化: 1kHz波形
    ATIM_PWM_Init(1000-1, 168-1); 
    // 设置死区时间: 占空比30%,死区时间100
    ATIM_PWM_SetDeadZone(300, 100);
    printf("\r\nSystem running...\r\n");
    while(1) {
        LED_TOGGLE(LED0_GPIO_Pin);
        printf("[SYS] Heartbeat\r\n");
        delay_ms(1000);
    }
}

2.3.5 死区时间计算示例

1.关键参数
  • tDTS:定时器分频后时钟周期

    tDTS = 1 / (定时器输入时钟 / 时钟分频系数)

    本例中:

    • 系统时钟 = 168 MHz

    • 时钟分频系数 = 4 (TIM_CLOCKDIVISION_DIV4)

    • 定时器输入时钟 = 168 MHz / 4 = 42 MHz

    • tDTS = 1 / 42 MHz ≈ 23.81 ns

2.死区时间计算公式

死区时间由DTG寄存器(Dead Time Generator)的值决定,根据DTG[7:5]的值分为四种模式:

DTG[7:5] 公式 死区时间范围
0xx DT = DTG[7:0] × tDTS 0~127 × tDTS
10x DT = (64 + DTG[6:0]) × 2 × tDTS 128~254 × tDTS
110 DT = (32 + DTG[5:0]) × 8 × tDTS 256~2032 × tDTS
111 DT = (32 + DTG[5:0]) × 16 × tDTS 512~4064 × tDTS
3.计算示例
  • DTG = 250 (十六进制0xFA,二进制11111010)

  • DTG[7:5] = 111 → 使用模式111

  • DTG[5:0] = 111010 (二进制) = 58 (十进制)

  • 计算:

DT = (32 + 58) × 16 × 23.81 ns
   = 90 × 16 × 23.81 ns
   = 1440 × 23.81 ns
   ≈ 34286.4 ns ≈ 34.29 μs
  • DTG = 100 (十六进制0x64,二进制01100100)

  • **DTG[7:5]=011 → 模式0xx

DT = DTG[7:0] × tDTS
   = 100 × 23.81 ns

2.4 PWM输入模式

本小节我们来学习使用高级定时器 PWM 输入模式, 此模式是输入捕获模式的一个特例。PWM 输入模式经常被应用于测量 PWM 脉宽和频率。

屏幕截图 20250718 134718png

  1. 确定定时器时钟源。本实验中我们使用内部时钟(CK_INT), F4 系列高级定时器挂载在 APB2 总线上,按照 sys_stm32_clock_init 函数的配置,定时器时钟频率为 2 倍的 APB2 总线时钟频率,即 168MHz。计数器的计数频率确定了测量的精度。

  2. 确定 PWM 输入的通道。 PWM 输入模式下测量 PWM, PWM 信号输入只能从通道1(CH1)或者通道 2(CH2)输入。

  3. 确定 IC1 和 IC2 的捕获边沿。这里以通道 1(CH1)输入 PWM 为例,一般我们习惯设置 IC1 捕获边沿为上升沿捕获, IC2 捕获边沿为下降沿捕获。

  4. 选择触发输入信号(TRGI)。这里也是以通道 1(CH1)输入 PWM 为例,那么我们就应该选择 TI1FP1 为触发输入信号。如果是通道 2(CH2)输入 PWM,那就选择 TI2FP2 为触发输入信号。可以看到这里并没有对应通道 3(CH3)或者通道 4(CH4)的触发输入信号,所以我们只选择通道 1 或者通道 2 作为 PWM 输入的通道。

  5. 从模式选择:复位模式。复位模式的作用是:在出现所选触发输入 (TRGI) 上升沿时,重新初始化计数器并生成一个寄存器更新事件。

  6. 读取一个 PWM 周期内计数器的计数个数,以及高电平期间的计数个数,再结合计数器的计数周期(即计一个数的时间),最终通过计算得到输入的 PWM 周期和占空比等参数。以通道 1(CH1)输入 PWM,设置 IC1 捕获边沿为上升沿捕获, IC2 捕获边沿为下降沿捕获为例,那么 CCR1 寄存器的值+1 就是 PWM 周期内计数器的计数个数, CCR2 寄存器的值+1 就是PWM 高电平期间计数器的计数个数。通过这两个值就可以计算出 PWM 的周期或者占空比等参数。

首先通过 TIM14_CH1(PF9)输出 PWM 波。然后把 PF9 输出的 PWM 波用杜邦线接入 PC6 (定时器 8 通道 1),最后通过串口打印 PWM 波的脉宽和频率等信息。

2.4.1 TIM8模式配置

  // 初始化定时器8通道1为PWM输入模式
void tim8_pwmin_init(void)
{
    GPIO_InitTypeDef gpio_init;
    TIM_SlaveConfigTypeDef slave_config;
    TIM_IC_InitTypeDef ic_config;
    // 启用时钟
    __HAL_RCC_TIM8_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    // 配置GPIO-PC6
    gpio_init.Pin = GPIO_PIN_6;
    gpio_init.Mode = GPIO_MODE_AF_PP;
    gpio_init.Pull = GPIO_PULLDOWN;
    gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
    gpio_init.Alternate = GPIO_AF3_TIM8;
    HAL_GPIO_Init(GPIOC, &gpio_init);
    // 配置定时器基础参数
    g_tim8_pwmin_handle.Instance = TIM8;
    g_tim8_pwmin_handle.Init.Prescaler = 0;
    g_tim8_pwmin_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tim8_pwmin_handle.Init.Period = 0xFFFF;
    g_tim8_pwmin_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&g_tim8_pwmin_handle);
    // 从模式配置:复位模式
    slave_config.SlaveMode = TIM_SLAVEMODE_RESET;
    slave_config.InputTrigger = TIM_TS_TI1FP1;
    slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
    slave_config.TriggerFilter = 0;
    HAL_TIM_SlaveConfigSynchro(&g_tim8_pwmin_handle, &slave_config);
    // 通道1配置:直接捕获上升沿
    ic_config.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    ic_config.ICSelection = TIM_ICSELECTION_DIRECTTI;
    ic_config.ICPrescaler = TIM_ICPSC_DIV1;
    ic_config.ICFilter = 0;
    HAL_TIM_IC_ConfigChannel(&g_tim8_pwmin_handle, &ic_config, TIM_CHANNEL_1);
    // 通道2配置:间接捕获下降沿
    ic_config.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
    ic_config.ICSelection = TIM_ICSELECTION_INDIRECTTI;
    HAL_TIM_IC_ConfigChannel(&g_tim8_pwmin_handle, &ic_config, TIM_CHANNEL_2);
    // 配置NVIC中断
    HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);
    HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
    HAL_NVIC_SetPriority(TIM8_UP_TIM13_IRQn, 1, 3);
    HAL_NVIC_EnableIRQ(TIM8_UP_TIM13_IRQn);
    // 启动捕获
    __HAL_TIM_ENABLE_IT(&g_tim8_pwmin_handle, TIM_IT_UPDATE);
    HAL_TIM_IC_Start_IT(&g_tim8_pwmin_handle, TIM_CHANNEL_1);
    HAL_TIM_IC_Start_IT(&g_tim8_pwmin_handle, TIM_CHANNEL_2);
}

2.4.2 重启PWM捕获

// 重启PWM捕获
void restart_pwmin_capture(void)
{
    __disable_irq();
    
    // 重置状态
    PWMIN_STA = 0; // 重置PWM输入状态
    PWMIN_PSC = 0; 
    __HAL_TIM_SET_PRESCALER(&g_tim8_pwmin_handle, 0); // 以最大的计数频率采集
    __HAL_TIM_SET_COUNTER(&g_tim8_pwmin_handle, 0); // 重置计数器
    __HAL_TIM_ENABLE_IT(&g_tim8_pwmin_handle, TIM_IT_CC1|TIM_IT_UPDATE); // 重新使能捕获中断和更新中断
    __HAL_TIM_ENABLE(&g_tim8_pwmin_handle);
    __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1|TIM_FLAG_CC2|TIM_FLAG_UPDATE); // 清除捕获中断和更新中断标志
    __enable_irq();
}

2.4.3 PWM输入捕获中断

// PWM输入模式中断处理函数
static void tim8_pwmin_irq_handler(void)
{
    static uint8_t sflag = 0; // 启动PWM输入检测标志
    if(PWMIN_STA)
    {
        PWMIN_PSC = 0;
        __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1|TIM_FLAG_CC2|TIM_FLAG_UPDATE);
        __HAL_TIM_SET_COUNTER(&g_tim8_pwmin_handle, 0);
        return;
    }
    if(__HAL_TIM_GET_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_UPDATE)) // 更新中断
    {
        __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_UPDATE); // 清除更新中断标志
        if(__HAL_TIM_GET_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1) == 0) // 如果没有发生捕获中断,且捕获未完成
        {
            sflag = 0;
            if(PWMIN_PSC == 0)
            {
                PWMIN_PSC++;
            }
            else
            {
                if(PWMIN_PSC == 65535) // 已经最大,可能是无输入状态
                {
                    PWMIN_PSC = 0;
                }
                else if(PWMIN_PSC > 32767) // 限制
                {
                    PWMIN_PSC = 65535;
                }
                else
                {
                    PWMIN_PSC += PWMIN_PSC;
                }
            }
            __HAL_TIM_SET_PRESCALER(&g_tim8_pwmin_handle, PWMIN_PSC); // 设置分频系数
            __HAL_TIM_SET_COUNTER(&g_tim8_pwmin_handle, 0); // 重置计数器
            __HAL_TIM_CLEAR_IT(&g_tim8_pwmin_handle, TIM_IT_CC1|TIM_IT_CC2|TIM_IT_UPDATE); // 清除捕获中断和更新中断标志
            return;
        }
    }
    if(sflag == 0) // 第一次采集到捕获中断
    {
        if(__HAL_TIM_GET_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1)) // 捕获中断1
        {
            sflag = 1;
        }
        __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1|TIM_FLAG_CC2|TIM_FLAG_UPDATE);
        return;
    }
    if(PWMIN_STA == 0) // 还没有成功捕获
    {
        if(__HAL_TIM_GET_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1))
        {
            PWMIN_Highvlaue = HAL_TIM_ReadCapturedValue(&g_tim8_pwmin_handle, TIM_CHANNEL_2)+1; // 读取高电平时间
            PWMIN_CycleTime = HAL_TIM_ReadCapturedValue(&g_tim8_pwmin_handle, TIM_CHANNEL_1)+1; // 读取周期时间
            if(PWMIN_Highvlaue < PWMIN_CycleTime) // 周期时间必定大于高电平时间
            {
                PWMIN_STA = 1; // 成功捕获到一次PWM输入
                PWMIN_PSC = TIM8->PSC; // 记录分频系数
                if(PWMIN_PSC == 0)
                {
                    PWMIN_Highvlaue++;
                    PWMIN_CycleTime++;
                }
                sflag = 0; // 捕获完成,准备下一次捕获
                /* 停止捕获 */
                TIM8->CR1 &= ~(1<<0);
                __HAL_TIM_DISABLE_IT(&g_tim8_pwmin_handle, TIM_IT_CC1|TIM_IT_CC2|TIM_IT_UPDATE); // 停止捕获中断和更新中断
                __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1|TIM_FLAG_CC2|TIM_FLAG_UPDATE); // 清除捕获中断和更新中断标志
            }
            else
            {
                restart_pwmin_capture(); // 周期时间小于高电平时间,可能是无输入状态,重新捕获
            }
        }
    }
    __HAL_TIM_CLEAR_FLAG(&g_tim8_pwmin_handle, TIM_FLAG_CC1|TIM_FLAG_CC2|TIM_FLAG_UPDATE); // 清除捕获中断和更新中断标志
}

2.4.4 TIM14输出PWM

/* TIM14_CH1作为PWM输出 */
TIM_HandleTypeDef g_tim14_pwmout_handle;

void tim14_pwmout_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    TIM_OC_InitTypeDef oc_init;
    
    // 启用时钟
    __HAL_RCC_TIM14_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    
    // 配置GPIO
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF9_TIM14;  
    HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);  

    // 配置定时器
    g_tim14_pwmout_handle.Instance = TIM14;
    g_tim14_pwmout_handle.Init.Prescaler = psc;
    g_tim14_pwmout_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    g_tim14_pwmout_handle.Init.Period = arr;
    g_tim14_pwmout_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_PWM_Init(&g_tim14_pwmout_handle);
    
    // 配置PWM通道
    oc_init.OCMode = TIM_OCMODE_PWM1;
    oc_init.Pulse = arr / 2;  // 默认50%占空比
    oc_init.OCPolarity = TIM_OCPOLARITY_HIGH;
    oc_init.OCFastMode = TIM_OCFAST_DISABLE;
    HAL_TIM_PWM_ConfigChannel(&g_tim14_pwmout_handle, &oc_init, TIM_CHANNEL_1);
    
    // 启动PWM
    HAL_TIM_PWM_Start(&g_tim14_pwmout_handle, TIM_CHANNEL_1);
}

2.4.5 主函数测试

#include <bsp_init.h>
#include <atim.h>
#include <stdio.h>

/* 状态变量 0,无,1,有 */
extern uint8_t  PWMIN_STA;// PWM输入状态
extern uint16_t PWMIN_PSC;// PWM输入分频系数
extern uint32_t PWMIN_Highvlaue; // PWM输入高电平时间
extern uint32_t PWMIN_CycleTime; // PWM输入周期时间

int main(void) 
{
    uint8_t t = 0;
    double High_time, Cycle_time, PWM_Freq, PWM_PSC;

    bsp_init();
    // 先初始化PWM输出
    // 100kHz PWM: 84MHz/((84-1+1)*(10-1+1)) = 84,000,000 / 840 = 100,000 Hz
    tim14_pwmout_init(10-1, 84-1);
    // 设置20%占空比 (周期10个计数单位,高电平2个)
    TIM14->CCR1 = 2;
    // 初始化PWM输入捕获
    tim8_pwmin_init();
    printf("PWM Capture Demo Started\r\n");
    while(1)
    {
        delay_ms(10);
        t++;
        if(t > 100)
        {
            if(PWMIN_STA)
            {
                printf("\r\n");
                /* 直接打印捕获值 */
                printf("PWM PSC       :%d\r\n", PWMIN_PSC);
                printf("PWM Highvlaue :%d\r\n", PWMIN_Highvlaue);
                printf("PWM Cyclevlaue:%d\r\n", PWMIN_CycleTime);
                /* 计算 */
                PWM_PSC = ((double)PWMIN_PSC+1)/168;
                High_time = PWMIN_Highvlaue * PWM_PSC;
                Cycle_time = PWMIN_CycleTime * PWM_PSC;
                PWM_Freq = (1/Cycle_time)*1000000;
                printf("PWM High Time :%.3fus\r\n", High_time);
                printf("PWM Cycle Time:%.3fus\r\n", Cycle_time);
                printf("PWM Duty      :%.2f%%\r\n", (High_time/Cycle_time)*100);
                printf("PWM Freq      :%.3fHz\r\n", PWM_Freq);
                restart_pwmin_capture();
            }
            LED_TOGGLE(LED1_GPIO_Pin);
            t = 0;
        }
    }
}

3. 高级定时器常见函数(HAL库)

3.1 死区时间与刹车功能配置

HAL_TIMEx_ConfigDeadTime()

函数原型:

HAL_StatusTypeDef HAL_TIMEx_ConfigDeadTime(
    TIM_HandleTypeDef *htim,
    TIM_BreakDeadTimeConfigTypeDef *sDeadTimeConfig)

参数结构体:

typedef struct {
    uint32_t OffStateRunMode;      // 运行模式关闭状态: TIM_OSSR_ENABLE/DISABLE
    uint32_t OffStateIDLEMode;     // 空闲模式关闭状态: TIM_OSSI_ENABLE/DISABLE
    uint32_t LockLevel;            // 锁定级别: TIM_LOCKLEVEL_OFF/1/2/3
    uint32_t DeadTime;             // 死区时间值 (0-0xFF)
    uint32_t BreakState;           // 刹车输入使能: TIM_BREAK_ENABLE/DISABLE
    uint32_t BreakPolarity;        // 刹车输入极性: TIM_BREAKPOLARITY_LOW/HIGH
    uint32_t BreakFilter;          // 刹车输入滤波器 (0x0-0xF)
    uint32_t Break2State;          // 刹车2输入使能 (仅TIM8)
    uint32_t Break2Polarity;       // 刹车2输入极性
    uint32_t Break2Filter;         // 刹车2输入滤波器
    uint32_t AutomaticOutput;      // 自动输出使能: TIM_AUTOMATICOUTPUT_ENABLE/DISABLE
} TIM_BreakDeadTimeConfigTypeDef;

功能: 配置死区时间和刹车参数
示例配置:

TIM_BreakDeadTimeConfigTypeDef sDeadTimeConfig = {0};
sDeadTimeConfig.DeadTime = 54;  // 约1μs死区 @84MHz
sDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
sDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sDeadTimeConfig.BreakFilter = 6;  // 中等滤波
sDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
HAL_TIMEx_ConfigDeadTime(&htim1, &sDeadTimeConfig);

3.2 互补输出控制

HAL_TIMEx_PWMN_Start()

函数原型:

HAL_StatusTypeDef HAL_TIMEx_PWMN_Start(
    TIM_HandleTypeDef *htim,
    uint32_t Channel)

通道选项:

  • TIM_CHANNEL_1

  • TIM_CHANNEL_2

  • TIM_CHANNEL_3

功能: 启动互补PWM输出
示例:

// 启动TIM1通道1的互补输出
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);

HAL_TIMEx_PWMN_Start_IT()

函数原型:

HAL_StatusTypeDef HAL_TIMEx_PWMN_Start_IT(
    TIM_HandleTypeDef *htim,
    uint32_t Channel)

功能: 启动互补PWM并启用相关中断
中断类型:

  • 刹车中断 (TIM_IT_BREAK)

  • COM事件中断 (TIM_IT_COM)

  • 触发事件中断 (TIM_IT_TRIGGER)

3.3 刹车功能回调

HAL_TIMEx_BreakCallback()

函数原型:

__weak void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim)

功能: 刹车事件回调函数(用户需重写)
示例实现:

void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM1) {
        // 紧急处理:关闭所有输出
        HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);
        HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
        
        // 添加系统保护逻辑
        SystemProtectionHandler();
    }
}

3.4 定时器同步配置

HAL_TIM_SlaveConfigSynchronization()

函数原型:

HAL_StatusTypeDef HAL_TIM_SlaveConfigSynchronization(
    TIM_HandleTypeDef *htim,
    TIM_SlaveConfigTypeDef *sSlaveConfig)

参数结构体:

typedef struct {
    uint32_t SlaveMode;        // 从模式: 
                              //   TIM_SLAVEMODE_DISABLE/RESET/GATED/TRIGGER/EXTERNAL1
    uint32_t InputTrigger;     // 输入触发源: 
                              //   TIM_TS_ITR0/ITR1/ITR2/ITR3/TI1F_ED/TI1FP1/TI2FP2/ETRF
    uint32_t TriggerPolarity;  // 触发极性: TIM_TRIGGERPOLARITY_RISING/FALLING/BOTHEDGE
    uint32_t TriggerFilter;    // 触发滤波器 (0x0-0xF)
    uint32_t TriggerPrescaler; // 触发预分频: TIM_TRIGGERPRESCALER_DIV1/2/4/8
} TIM_SlaveConfigTypeDef;

功能: 配置定时器主从同步
示例配置 (TIM2作为主定时器触发TIM1):

TIM_SlaveConfigTypeDef sSlaveConfig = {0};
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_ITR1;  // TIM2 -> ITR1 -> TIM1
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 4;
HAL_TIM_SlaveConfigSynchronization(&htim1, &sSlaveConfig);

3.5 换相事件控制

HAL_TIMEx_GenerateCommutEvent()

函数原型:

HAL_StatusTypeDef HAL_TIMEx_GenerateCommutEvent(
    TIM_HandleTypeDef *htim)

功能: 强制生成换相事件(用于无刷电机驱动)
使用场景:

// 在电机控制算法中
void UpdateMotorCommutation() {
    // 计算新的换相角度
    CalculateNextCommutationAngle();
    
    // 生成硬件换相事件
    HAL_TIMEx_GenerateCommutEvent(&htim1);
}

3.6 重复计数器操作

__HAL_TIM_SET_RCR()

宏定义:

#define __HAL_TIM_SET_RCR(__HANDLE__, __RCR__) \
    ((__HANDLE__)->Instance->RCR = (__RCR__))

功能: 设置重复计数器值 (0-255)
示例:

// 每4个PWM周期产生一次更新事件
__HAL_TIM_SET_RCR(&htim1, 3);

3.7 主输出使能控制

__HAL_TIM_MOE_ENABLE()

宏定义:

#define __HAL_TIM_MOE_ENABLE(__HANDLE__) \
    ((__HANDLE__)->Instance->BDTR |= TIM_BDTR_MOE)

功能: 使能主输出(必须调用才能输出PWM)
使用位置:

// 在PWM启动后调用
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
__HAL_TIM_MOE_ENABLE(&htim1);  // 关键步骤!

3.8 关键计算函数

3.8.1 死区时间计算

/**
 * 计算死区时间寄存器值
 * @param desired_deadtime_ns 期望死区时间(ns)
 * @param timer_clock_mhz 定时器时钟频率(MHz)
 * @return 死区时间寄存器值 (0-255)
 */
uint8_t CalculateDeadTime(uint32_t desired_deadtime_ns, uint32_t timer_clock_mhz) {
    // 死区时间 = (DTS / fDTS) * DTG[7:0]
    // DTS = 定时器时钟 / (2 * CKD[1:0])
    // 简化公式: DT_val ≈ (desired_deadtime_ns * timer_clock_mhz) / 1000
    uint32_t dt_val = (desired_deadtime_ns * timer_clock_mhz) / 1000;
    return (dt_val > 255) ? 255 : dt_val;
}1

3.8.2 中心对齐模式周期计算

/**
 * 计算中心对齐模式下的PWM周期
 * @param timer_clock 定时器时钟(Hz)
 * @param prescaler 预分频值
 * @param period 自动重载值
 * @return PWM周期(秒)
 */
float CalculateCenterAlignedPeriod(uint32_t timer_clock, uint32_t prescaler, uint32_t period) {
    // 中央对齐模式:完整周期 = 2 * (ARR + 1) * (PSC + 1) / TIM_CLK
    return (2.0f * (period + 1) * (prescaler + 1)) / timer_clock;
}

3.9 高级定时器专用中断

3.9.1 中断服务函数示例

/* TIM1刹车中断 */
void TIM1_BRK_TIM9_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim1);
    // 添加特定处理代码...
}

/* TIM1更新中断 */
void TIM1_UP_TIM10_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim1);
    // 添加特定处理代码...
}

/* TIM1触发和通信中断 */
void TIM1_TRG_COM_TIM11_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim1);
    // 添加特定处理代码...
}

3.9.2 回调函数实现

void HAL_TIMEx_BreakCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM1) {
        // 刹车事件处理
        HandleBrakeEvent();
    }
}

void HAL_TIMEx_ComCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM1) {
        // COM事件处理
        HandleCommutationEvent();
    }
}

posted @ 2025-07-18 19:03  hazy1k  阅读(39)  评论(0)    收藏  举报