问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

STM32定时器详解:从基础原理到PWM控制应用

创作时间:
作者:
@小白创作中心

STM32定时器详解:从基础原理到PWM控制应用

引用
CSDN
1.
https://blog.csdn.net/weixin_62897522/article/details/140698295

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):用于统计外部触发信号
  • ITR0ITR3(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交替显示。

四、总结

定时器在单片机开发过程中具有相对重要的作用,合理配置相关的定时寄存器,能够为我们提供精确的时间控制和计数功能。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号