STM32-定时器与PWM
一、定时器介绍
(一)STM32 定时器概述
- STM32 系列芯片有多种定时器,包括基本定时器(TIM6 和 TIM7)、通用定时器(TIM2 – TIM5)和高级定时器(TIM1 和 TIM8)。这些定时器可以用于多种功能,如定时中断、输入捕获、输出比较和 PWM(脉冲宽度调制)生成等。
- 定时器的核心是一个 16 位或 32 位的计数器,它根据预分频器(PSC)的值对内部时钟(一般由系统时钟提供)进行分频后计数。例如,系统时钟频率为 72MHz,预分频器设置为 71,那么计数器的计数频率就是 72MHz/(71 + 1)=1MHz,即每 1 微秒计数器加 1。
(二)基本定时器(TIM6 和 TIM7)
- 功能特点:基本定时器主要用于简单的定时功能,产生定时中断。它相对比较简单,只有一个 16 位的自动重装载计数器和一个 16 位的预分频器。
- 应用场景:例如,在一个简单的定时任务系统中,使用 TIM6 定时器来产生 1ms 的定时中断,以更新系统中的时间戳或者周期性地执行一些后台任务,如检查传感器状态等。配置步骤大致如下:
(三)通用定时器(TIM2 – TIM5)
- 功能特点:通用定时器在基本定时器的基础上增加了更多功能。它们具有输入捕获和输出比较功能。输入捕获可以用于测量外部信号的脉冲宽度、周期等参数。输出比较可以用于在计数器达到特定值时改变输出电平,从而生成 PWM 信号等。
- 应用场景:在电机控制领域,利用通用定时器的输出比较功能来生成 PWM 信号控制电机转速。以控制直流电机为例,通过改变 PWM 信号的占空比来调节电机两端的平均电压,从而控制电机的转速。在信号测量方面,输入捕获功能可用于测量外部脉冲信号的频率。假设要测量一个方波信号的频率,将该信号接入通用定时器的输入捕获引脚,通过记录两次上升沿(或下降沿)之间的计数器值和计数频率,就可以计算出信号的频率。
(四)高级定时器(TIM1 和 TIM8)
- 功能特点:高级定时器在通用定时器的基础上增加了一些高级特性,如死区时间插入、互补输出等。死区时间插入主要用于防止在电机控制等应用中,上下桥臂同时导通而造成短路。互补输出可以用于驱动三相电机等复杂的负载。
- 应用场景:在三相交流电机的变频调速控制中,高级定时器的互补输出和死区时间插入功能就显得尤为重要。通过生成具有合适死区时间的三相 PWM 信号来控制电机的三相绕组,实现电机的平稳调速和高效运行。
二、PWM介绍
(一)定义
(二)基本原理
(三)优点及应用范围
1、优点:
2、应用范围:
3、主要参数
(四)PWM 的产生
(五)PWM 工作原理
(六)PWM 输出的模式区别
(七)PWM 的计数模式
(八)PWM 相关配置寄存器
1、捕获 / 比较模式寄存器(TIMx_CCMR1):
2、捕获 / 比较使能寄存器(TIMx_CCER):
3、捕获 / 比较寄存器(TIMx_CCR1):
三、定时器项目配置
(一)使用Stm32 Cube Mx创建项目
(1)新建工程
(2)芯片选择
(3)配置SYS
选择调试接口,点System Cor,选择SYS。,在右侧弹出的菜单栏中选Serial Wire。
(4)配置RCC
点System Cor,选择RCC,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator
(5)定义IO口输出
选择PA1作为LED灯的输出,将其选为GPIO-OUT
(6)配置定时器2和定时器3
这里我们使用定时器2和定时器3来实现定时的功能。如图所示,依次点击位置1,选中定时器2;位置2,配置定时器2的时钟源为内部时钟;位置3,分频系数为71,向上计数模式,计数周期为5000,使能自动重载模式。
TIM2:
TIM3:
(7)配置中断
如下图所示,开启定时器2和定时器3的中断。
(8)配置USART
选择Connectivity,点开USART1,Mode选择异步通信Asynchronous
(9)配置时钟
(10)项目配置
然后创建工程项目。
(二)keil代码的编译
(1)定时器
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
该函数表示启动相应的定时器,“h”表示HAL库,“tim2”表示定时器2。所以这行代码的意思就是启动定时器2和定时器3。
(2)串口通信
uint8_t hello[20]="hello windows!\r\n";
(3)定时器中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time_cnt =0;
static uint32_t time_cnt3 =0;
if(htim->Instance == TIM2)
{
if(++time_cnt >= 400)
{
time_cnt =0;
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_1);
}
}
if(htim->Instance == TIM3)
{
if(++time_cnt3 >= 1000)
{
time_cnt3 =0;
HAL_UART_Transmit(&huart1,hello,20,100000);
}
}
}
该函数为定时器的中断回调函数,当产生定时中断的时候,会自动调用这个函数。在函数内部定义了定时器的一个静态变量:time_cnt与定时器3 的time_cnt3。
例如time_cnt,当它大于等于100的时候,才会执行if里面的代码。也就是说需要发生400次中断,才会让LED的状态翻转。前面已经算过了,一次定时中断的时间是0.005秒,所以400次中断的时间是0.005400=2秒。也就是说每隔2秒,LED的状态翻转一次。
(三)成果展示
(1)任务一:实际成果演示
(2)任务二:串口输出
四、PWM输出完成呼吸灯
(一)Stm32 Cube Mx创建项目工程
(1)新建项目项
(2)选择芯片
(3)配置RCC
点System Cor,选择RCC,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator
(4)配置SYS
选择调试接口,点System Cor,选择SYS。,在右侧弹出的菜单栏中选Serial Wire。
(5)配置定时器3和定时器4
这里我们选择定时器3和定时器4来实现定时的功位置3,分频系数为71,向上计数模式,计数周期为500,使能自动重载模式。通道1选择:PWM Generation CH1(PWM输出通道1)
设置分频系数为71,计数周期为500,其它默认。
设置占空比初始值为10。
定时器4
我们也选择PWM Generation CH1(PWM输出通道1),计数周期根据自己需要进行设置
(6)时钟配置
(7)项目设置
(8)创建工程项目
(二)keil代码编译
(1)设置占空比
打开工程,主要修改main.c文件。首先定义一个变量,用来存储占空比:初值设为10。
uint16_t duty_num3 = 10;
uint16_t duty_num4 = 10;
(2)开启PWM信道
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_1);
(3)调用代码
这里我们设置为每隔50毫秒,占空比加10,如果超过500(也就是PWM周期),自动变成0。(即灯会从亮倒暗,逐渐变化)
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(50);
duty_num3 = duty_num3 + 10;
duty_num4 = duty_num4 + 10;
if(duty_num3 > 500)
{
duty_num3 = 0;
}
__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_1,duty_num3);
if(duty_num4 > 500)
{
duty_num4 = 0;
}
__HAL_TIM_SetCompare(&htim4,TIM_CHANNEL_1,duty_num4);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
(三)成果展示
(1)实际成果展示
(2)PWM输出波形
五、总结与体会
定时器在 STM32 中犹如一个精准的时钟管理者。它基于内部时钟源,通过设置预分频器和自动重装载寄存器的值来确定计数频率和周期。例如,对于一个系统时钟为 72MHz 的 STM32 芯片,若要设置一个合适的计数频率以实现 5 秒和 2 秒的定时,需要精心计算预分频器的值。以 5 秒定时器为例,若选择合适的预分频使计数频率为 1Hz(即每秒计数一次),那么自动重装载寄存器的值设置为 5 即可实现 5 秒的定时。这一过程让我对定时器的计数机制和时间控制原理有了透彻的理解,不再局限于以往简单的循环延时或 delay 函数那种粗略的时间控制方式。
通过本次实验,在 STM32 定时器的应用方面取得了显著的进步。不仅掌握了定时器的多种配置方式和应用场景,如多任务并发控制和 PWM 信号生成,还提高了自己的硬件调试能力和问题解决能力。在未来的学习和实践中,可以进一步探索定时器在更复杂系统中的应用,如电机控制中的精确调速、多传感器数据采集的定时触发等。同时,也意识到在嵌入式开发中,深入理解硬件原理和软件算法的结合是构建高效、稳定系统的关键所在。这次实验为我今后深入学习嵌入式系统开发奠定了坚实的基础,也激发了我进一步探索 STM32 更多功能和应用的热情。
作者:森林里的林