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

上图中,框出来三个部分,这是和通用定时器不同的地方,下面来分别介绍它们。
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输出都不能同时处在有效电平。
当发生断路输入后,会怎么样?
-
MOE 位被异步地清零, OCx 和 OCxN 为无效、空闲或复位状态(由 OSSI 位选择)。
-
OCx 和 OCxN 的状态:由相关控制位状态决定,当使用互补输出时:根据情况自动控制输出电平,参考《STM32F4xx 参考手册_V4(中文版).pdf》手册第 382 页的表 73 具有断路功能的互补通道 Ocx 和 OcxN 的控制位。
-
BIF 位置 1,如果使能了 BIE 位,还会产生刹车中断;如果使能了 TDE 位,会产生 DMA请求。
-
如果 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波。关键机制如下:
- 定时器配置:
-
定时器时钟源频率 = APB2时钟 × 2(如
HAL_RCC_GetPCLK2Freq() * 2) -
预分频器PSC = 168-1 → 分频后计数器频率 = 定时器时钟 / 168
-
自动重装载值ARR = 1000-1 → 计数周期为1000个时钟(0~999)
- 翻转模式(TIM_OCMODE_TOGGLE):
-
当计数器值等于比较寄存器CCRx时,输出电平翻转
-
每个通道需要两次翻转(上升沿+下降沿)才能形成一个完整周期
-
输出信号周期 = 2 × 定时器周期 = 2000个计数时钟
- 相位控制原理:
-
相位由比较值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°)
-
- 占空比固定为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 互补输出带死区控制

上图中, CH1 输出黄色的 PWM,它的互补通道 CH1N 输出绿色的 PWM。通过对比,可以知道这两个 PWM 刚好是反过来的, CH1 的 PWM 为高电平期间, CH1N 的 PWM 则是低电平,反之亦然,这就是互补输出。
下面来看一下什么是带死区控制的互补输出?

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

死区时间计算分三步走:
第一步:通过 CKD[1:0]位确定 tDTS。根据 CKD[1:0]位的描述,可以得到下面的式子:

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

通过上式可得 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。
-
利用 TIM1_CH1(PE9)输出 70%占空比的 PWM 波,它的互补输出通道(PE8)则是输出 30%占空比的 PWM 波。
-
刹车功能,当给刹车输入引脚(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 脉宽和频率。

-
确定定时器时钟源。本实验中我们使用内部时钟(CK_INT), F4 系列高级定时器挂载在 APB2 总线上,按照 sys_stm32_clock_init 函数的配置,定时器时钟频率为 2 倍的 APB2 总线时钟频率,即 168MHz。计数器的计数频率确定了测量的精度。
-
确定 PWM 输入的通道。 PWM 输入模式下测量 PWM, PWM 信号输入只能从通道1(CH1)或者通道 2(CH2)输入。
-
确定 IC1 和 IC2 的捕获边沿。这里以通道 1(CH1)输入 PWM 为例,一般我们习惯设置 IC1 捕获边沿为上升沿捕获, IC2 捕获边沿为下降沿捕获。
-
选择触发输入信号(TRGI)。这里也是以通道 1(CH1)输入 PWM 为例,那么我们就应该选择 TI1FP1 为触发输入信号。如果是通道 2(CH2)输入 PWM,那就选择 TI2FP2 为触发输入信号。可以看到这里并没有对应通道 3(CH3)或者通道 4(CH4)的触发输入信号,所以我们只选择通道 1 或者通道 2 作为 PWM 输入的通道。
-
从模式选择:复位模式。复位模式的作用是:在出现所选触发输入 (TRGI) 上升沿时,重新初始化计数器并生成一个寄存器更新事件。
-
读取一个 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();
}
}

浙公网安备 33010602011771号