STM32微控制器以其丰富的外设和强大的性能,在嵌入式领域得到了广泛应用。其中,定时器作为其核心外设之一,在实现精确时间控制、波形生成、事件测量等方面发挥着不可替代的作用。本教程将深入探讨STM32定时器的分类、工作原理、主要寄存器配置以及常见应用,旨在帮助读者全面理解并熟练运用STM32定时器。
STM32系列微控制器通常包含以下三类定时器:
STM32定时器的核心是一个可编程的计数器,通过对时钟信号进行计数来实现定时功能。其基本工作原理和主要寄存器如下:
所有STM32定时器都包含一个时基单元,这是实现定时功能的基础。时基单元主要由以下几个核心寄存器组成:
PSC + 1
。通过调整PSC的值,可以改变计数器的计数速度,从而控制定时周期。STM32定时器中的一些重要寄存器(如ARR、PSC)具有“影子寄存器”机制。这意味着我们对这些寄存器进行写入操作时,实际上是写入到其“预装载寄存器”(Preload Register,也称为本体寄存器),而不是直接写入到实际工作的“影子寄存器”。只有在特定事件(如更新事件UEV)发生时,预装载寄存器中的值才会被传输到影子寄存器中,从而生效。
这种机制的目的是为了防止在定时器运行过程中,由于修改寄存器值而导致的不确定性或毛刺。例如,在PWM输出过程中,如果直接修改ARR或PSC,可能会导致PWM波形出现异常。通过影子寄存器,可以确保在当前周期结束后,新的配置才会在下一个周期开始时生效,从而保证波形的平滑过渡。
定时器的中断周期(或溢出时间)可以通过以下公式计算:
中断周期 = (ARR + 1) * (PSC + 1) / 定时器时钟频率
其中:
定时器时钟频率
:通常是APB1或APB2总线时钟经过倍频后的频率。PSC
:预分频器寄存器的值。ARR
:自动重载寄存器(周期)的值。例如,如果定时器时钟频率为72MHz,PSC设置为7199,ARR设置为4999,则中断周期为:
(4999 + 1) * (7199 + 1) / 72,000,000 = 5000 * 7200 / 72,000,000 = 36,000,000 / 72,000,000 = 0.5 秒
这意味着每0.5秒会产生一次定时器中断。
以STM32 HAL库为例,配置一个基本定时器(如TIM6)实现定时中断的步骤如下:
__HAL_RCC_TIM6_CLK_ENABLE();
TIM_HandleTypeDef htim6;
htim6.Instance = TIM6;
htim6.Init.Prescaler = 7199; // 预分频器,例如,如果时钟为72MHz,分频后为10KHz
htim6.Init.Period = 4999; // 自动重载值,例如,配合预分频器实现0.5秒中断
htim6.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
htim6.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟不分频
HAL_TIM_Base_Init(&htim6);
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 0, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); // 使能中断
HAL_TIM_Base_Start_IT(&htim6);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM6)
{
// 在这里编写定时器中断服务代码,例如翻转LED
}
}
在基本定时器的基础上,通用定时器和高级定时器提供了更丰富的功能:
输入捕获功能用于测量外部信号的脉冲宽度、频率或周期。当外部信号的边沿(上升沿、下降沿或双边沿)发生时,定时器会将当前计数器的值锁存到捕获/比较寄存器(TIMx_CCR)中,并触发中断。通过两次捕获事件之间计数器值的差值,可以计算出信号的周期或脉宽。
输出比较功能用于在特定时间点产生输出事件,可以控制GPIO引脚电平翻转,或生成单脉冲。通过将计数器的值与捕获/比较寄存器(TIMx_CCR)中的预设值进行比较,当两者相等时,会触发输出事件。
PWM(Pulse Width Modulation,脉冲宽度调制)是利用定时器的输出比较功能实现的。通过周期性地改变输出方波的占空比,可以模拟模拟信号的输出。广泛应用于电机调速、LED亮度控制、D/A转换等。通用定时器和高级定时器都可以生成PWM波形。
HAL库PWM生成示例:
以下是一个使用STM32 HAL库配置通用定时器(如TIM3)生成PWM波的示例:
__HAL_RCC_GPIOC_CLK_ENABLE(); // 假设PWM输出引脚为PC6 (TIM3_CH1)
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_6; // PC6
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3; // PC6复用为TIM3_CH1
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
TIM_HandleTypeDef htim3;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 71; // 预分频器,例如,如果时钟为72MHz,分频后为1MHz
htim3.Init.Period = 999; // 自动重载值,例如,周期为1ms (1MHz / (999+1) = 1KHz)
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM模式1
sConfigOC.Pulse = 499; // 占空比,例如,50%占空比 (499+1)/(999+1) = 0.5
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 输出极性高
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
通过修改 sConfigOC.Pulse
的值,可以动态调整PWM的占空比,从而实现调光、调速等功能。
高级定时器通常支持编码器接口模式,可以直接解码正交编码器信号。通过监测编码器A、B相信号的相位差和脉冲数,定时器可以自动更新计数器的值,从而精确测量旋转位置和速度。
这是高级控制定时器特有的功能,主要用于电机控制。在驱动半桥或全桥电路时,为了避免上下桥臂同时导通造成短路(直通),需要在开关管关断和导通之间插入一个短暂的延时,即死区时间。高级定时器可以自动生成带有死区时间的互补PWM输出,简化了电机驱动的设计。
高级控制定时器还支持刹车输入功能。当外部刹车信号有效时,定时器可以立即停止PWM输出,并将所有输出引脚置于预设的安全状态,用于紧急停机或故障保护。