STM32定时器详解:从基础原理到PWM控制应用
STM32定时器详解:从基础原理到PWM控制应用
STM32定时器是单片机开发中一个非常重要的组件,它不仅能够提供精确的时间控制,还能实现各种复杂的定时和计数功能。本文将详细介绍STM32定时器的基本原理、高精度计时方法以及PWM控制的应用,帮助读者全面理解定时器的工作机制。
一、定时器基本操作原理
STM32系列单片机包含多种类型的定时器,包括系统定时器(SysTick)、高级定时器(TIM1和TIM8)、通用定时器(TIM2/3/4/5)和基本定时器(TIM6和TIM7)。这些定时器的功能从复杂到简单依次递减,理解高级定时器的原理可以推及其他类型的定时器。
1.定时器就是计数器
定时器最基本的功能是计数。以高级定时器为例,其内部包含一个16位的计数器(CNT counter),每当接收到一个时钟信号(CK_CNT),计数值就会累加,从0累加到0xFFFF。如果时钟频率是72MHz,那么从0累加到0xFFFF的时间大约是910us。
计数器的时钟来源有多种选择:
- 内部时钟:用于普通的定时器功能,如系统定时器
- ETR(External Trigger Input):用于统计外部触发信号
- ITR0
ITR3(Internal Trigger03):内部触发信号,来自其他定时器的TRGO信号,用于定时器级联 - TIMx_CH1/2/3/4:外部输入信号
2.定时器时钟源
STM32F103C8定时器的时钟源可以是内部时钟(HSI)或外部时钟(HSE)。通用定时器有四种时钟源:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入引脚(TIx),x=1,2
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发时钟:使用一个定时器作为另一定时器的预分频器
定时器时钟TIMxCLK通常由APB1预分频器提供。如果APB1预分频系数等于1,则频率不变;否则频率乘以2。在库函数中,APB1预分频系数通常是2,因此PCLK1=36M,定时器时钟TIMxCLK=72M。
3.PWM的概念
PWM(Pulse Width Modulation)即脉宽调制,是一种常用的定时器输出波形。通过控制脉冲的占空比(duty cycle),可以实现对输出信号的精确控制。占空比定义为一个脉冲周期内高电平时间与整个周期时间的比例,取值范围为0%到100%。
定时器中用于PWM控制的主要寄存器包括:
- CNT:计数寄存器,每接收一个时钟信号,值加一
- ARR(Auto Reload Register):当CNT值达到ARR设定的值时,CNT从0开始,并且GPIO引脚的电平反转
- CCR(Capture/Compare Register):当CNT值达到CCR设定的值时,GPIO引脚的电平反转
假设CNT值不断累加,这几个寄存器的值与GPIO电平的关系如下图所示:
二、高精度计时
1.Systick高精度计时
Systick定时器是STM32内核提供的一个系统级定时器,可以用于实现高精度的延时和时间测量。以下是一个使用Systick实现us级和ms级延时的代码示例:
#include "driver_timer.h"
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
void udelay(int us)
{
uint32_t told = SysTick->VAL;
uint32_t tnow;
uint32_t load = SysTick->LOAD;
uint32_t ticks = us * (load + 1) / 1000;
uint32_t cnt = 0;
while (1)
{
tnow = SysTick->VAL;
if (told >= tnow)
cnt += told - tnow;
else
cnt += told + load + 1 - tnow;
told = tnow;
if (cnt >= ticks)
break;
}
}
void mdelay(int ms)
{
for (int i = 0; i < ms; i++)
udelay(1000);
}
uint64_t system_get_ns(void)
{
uint64_t ns = HAL_GetTick();
ns = ns * 1000000;
uint32_t tnow = SysTick->VAL;
uint32_t load = SysTick->LOAD;
uint64_t cnt = load + 1 - tnow;
ns += cnt * 1000000 / (load + 1);
return ns;
}
2.定时器高精度计时
除了使用Systick,也可以使用通用定时器(如TIM4)实现高精度计时。以下是一个使用TIM4实现us级和ms级延时的代码示例:
#include "driver_timer.h"
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
extern TIM_HandleTypeDef htim4;
void udelay(int us)
{
uint32_t told = __HAL_TIM_GET_COUNTER(&htim4);
uint32_t tnow;
uint32_t load = __HAL_TIM_GET_AUTORELOAD(&htim4);
uint32_t ticks = us * (load + 1) / 1000;
uint32_t cnt = 0;
while (1)
{
tnow = __HAL_TIM_GET_COUNTER(&htim4);
if (tnow >= told)
cnt += tnow - told;
else
cnt += load + 1 - told + tnow;
told = tnow;
if (cnt >= ticks)
break;
}
}
void mdelay(int ms)
{
for (int i = 0; i < ms; i++)
udelay(1000);
}
uint64_t system_get_ns(void)
{
uint64_t ns = HAL_GetTick();
ns = ns * 1000000;
uint32_t tnow = __HAL_TIM_GET_COUNTER(&htim4);
uint32_t load = __HAL_TIM_GET_AUTORELOAD(&htim4);
uint64_t cnt = tnow;
ns += cnt * 1000000 / (load + 1);
return ns;
}
三、使用PWM控制三色灯
PWM(脉宽调制)是定时器的一个重要应用,可以用于控制LED的亮度。以下是一个使用定时器控制RGB三色灯的示例代码:
#include "driver_color_led.h"
#include "stm32f1xx_hal.h"
#include "cmsis_version.h"
extern TIM_HandleTypeDef htim2;
#define COLOR_LED_R TIM_CHANNEL_3
#define COLOR_LED_G TIM_CHANNEL_1
#define COLOR_LED_B TIM_CHANNEL_2
void ColorLED_Init(void)
{
HAL_TIM_PWM_Start(&htim2, COLOR_LED_R);
HAL_TIM_PWM_Start(&htim2, COLOR_LED_G);
HAL_TIM_PWM_Start(&htim2, COLOR_LED_B);
}
void ColorLED_SetColor(uint32_t color)
{
TIM_OC_InitTypeDef sConfigOC_R = {0};
TIM_OC_InitTypeDef sConfigOC_G = {0};
TIM_OC_InitTypeDef sConfigOC_B = {0};
sConfigOC_R.OCMode = TIM_OCMODE_PWM1;
sConfigOC_R.Pulse = ((color >> 16) & 0xff) * 1999 / 255;
sConfigOC_R.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC_R.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_R, COLOR_LED_R) != HAL_OK)
Error_Handler();
sConfigOC_G.OCMode = TIM_OCMODE_PWM1;
sConfigOC_G.Pulse = ((color >> 8) & 0xff) * 1999 / 255;
sConfigOC_G.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC_G.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_G, COLOR_LED_G) != HAL_OK)
Error_Handler();
sConfigOC_B.OCMode = TIM_OCMODE_PWM1;
sConfigOC_B.Pulse = ((color >> 0) & 0xff) * 1999 / 255;
sConfigOC_B.OCPolarity = TIM_OCPOLARITY_LOW;
sConfigOC_B.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC_B, COLOR_LED_B) != HAL_OK)
Error_Handler();
}
在主函数中,可以使用以下代码控制RGB三色灯:
ColorLED_Init();
ColorLED_SetColor(0x00ff0000);
HAL_Delay(1000);
ColorLED_SetColor(0x00ff00);
HAL_Delay(1000);
ColorLED_SetColor(0x00ff);
HAL_Delay(1000);
实验现象:全彩LED灯红绿蓝延迟1s交替显示。
四、总结
定时器在单片机开发过程中具有相对重要的作用,合理配置相关的定时寄存器,能够为我们提供精确的时间控制和计数功能。