STM32HAL库通用定时器学后笔记 - 实践

通用定时器简介

1.通用定时器有哪些?

        TIM2/TIM3/TIM4/TIM5

2.通用定时器的主要特性

        16位递增、递减、中心对齐计数器(计数值0~65535)

        16位预分频器(分频系数:1~65535)

        可用于触发DAC\ADC

        在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求

4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式

        使用外部信号控制定时器且可实现多个定时器互连的同步电路

        支持编码器和霍尔传感器电路等

***********************通用定时器具备基本定时器所有功能**************************

3.通用定时器框图

1. 时钟源(Clock Sources)

通用定时器的时钟源有四种选择:

  1. 内部时钟(CK_INT)

    • 来自APB总线的时钟
    • 通用定时器的时钟频率计算:CK_INT = APB1时钟 × 2(当APB1分频系数为2时)
    • 例如:系统时钟72MHz,APB1时钟36MHz,APB1分频系数为2,则通用定时器时钟为72MHz
  2. 外部时钟模式1

    • 从TIx引脚输入(TIMx_CH1/2/3/4)
    • TI1F_ED:来自CH1,未经边沿检测的双边沿信号
    • TI1FP1:来自CH1,经边沿检测后的信号(可选择上升沿或下降沿)
    • TI2FP2:来自CH2,经边沿检测后的信号
  3. 外部时钟模式2

    • 从ETR引脚输入(TIMx_ETR)
    • 经过极性选择、边沿检测、预分频器和输入滤波后,可作为外部时钟源
    • 用于将定时器作为脉冲计数器
  4. 内部触发输入(ITRx)

    • 其他定时器的触发输出(TRGO)
    • 可用于定时器级联(一个定时器的溢出事件触发另一个定时器的计数)

2. 控制器(Controller)

  1. 从模式控制器(Slave Mode Controller)

    • 控制计数器复位、启动、递增/递减、计数
    • 用于定时器级联功能
  2. 触发控制器(Trigger Controller,TRGO)

    • 提供触发信号给其他外设
    • 可连接到其他定时器的ITR0-ITR3,实现定时器级联
    • 为DAC/ADC的触发转换提供信号
  3. 编码器接口

    • 用于读取旋转编码器的脉冲和方向信息
    • 支持正交编码器模式

3. 时基单元(Time Base Unit)

时基单元是定时器的核心部分,包括:

  1. 计数器(Counter)

    • 16位计数器,可以向上、向下或中央对齐计数
    • 用于计数时钟脉冲
  2. 预分频器(Prescaler)

    • 用于对输入时钟进行分频
    • 预分频系数范围:1~65536
    • 有预分频器缓冲器(影子寄存器),防止计数过程中改变分频值导致频率不一致
  3. 自动重装载寄存器(Auto Reload Register)

    • 设置计数器的上限值
    • 当计数器达到自动重装载值时,产生更新事件并重新计数

4. 输入捕获电路(Input Capture Circuit)

输入捕获电路用于测量输入信号的周期和占空比:

  1. 输入信号处理

    • 信号经过异或门(电机控制中常用)
    • 进入输入滤波器(过滤高频噪声)
    • 通过边沿检测器(检测上升沿或下降沿)
  2. 捕获比较寄存器

    • 捕获事件发生时,将计数器值转移到捕获比较寄存器
    • 用于计算外部信号的时间(如周期、脉宽)

5. 输出比较电路(Output Compare Circuit)

输出比较电路用于生成PWM波形和特定波形:

  1. 比较功能

    • 当计数器值等于捕获比较寄存器值时,产生输出事件
    • 可设置输出电平翻转、置位或复位
  2. PWM生成

    1.通过设置占空比和频率,生成PWM信号
    2.用于LED调光、电机控制等

    3.计数器时钟源

                    内部时钟(CK_INT)                                设置TIMx_SMCR的SMS=000

                    外部时钟模式1:外部输入引脚(TIX)         设置TIMx_SMCR的SMS=111

                    外部时钟模式2:外部触发输入(ETR)        设置TIMx_SMCR的SMS=111

                    内部触发输入(ITRx)                                   比较复杂,设置需要看参考手册

            外部时钟模式1                

                    处理路径:

    [外部时钟信号] → [IO引脚] → [TIMx_CH1/CH2] → [输入捕获滤波器] → [边沿检测器] → [触发输入选择器] → [模式选择器] → [预分频器] → [计数器]

    #include "./BSP/TIMER/gtim.h"
    #include "./BSP/LED/led.h"
    TIM_HandleTypeDef g_timx_handle; /* 定时器x句柄 */
    void gtim_timx_int_init(uint16_t arr, uint16_t psc)
    {
        GTIM_TIMX_INT_CLK_ENABLE();                                 /* 使能TIMx时钟 */
        g_timx_handle.Instance = GTIM_TIMX_INT;                     /* 通用定时器x */
        g_timx_handle.Init.Prescaler = psc;                         /* 预分频系数 */
        g_timx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;        /* 递增计数模式 */
        g_timx_handle.Init.Period = arr;                            /* 自动装载值 */
        HAL_TIM_Base_Init(&g_timx_handle);
        HAL_NVIC_SetPriority(GTIM_TIMX_INT_IRQn, 1, 3);             /* 设置中断优先级,抢占优先级1,子优先级3 */
        HAL_NVIC_EnableIRQ(GTIM_TIMX_INT_IRQn);                     /* 开启ITMx中断 */
        HAL_TIM_Base_Start_IT(&g_timx_handle);                      /* 使能定时器x和定时器x更新中断 */
    }
    void GTIM_TIMX_INT_IRQHandler(void)
    {
        /* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
        if(__HAL_TIM_GET_FLAG(&g_timx_handle, TIM_FLAG_UPDATE) != RESET)
        {
            LED1_TOGGLE();
            __HAL_TIM_CLEAR_IT(&g_timx_handle, TIM_IT_UPDATE);  /* 清除定时器溢出中断标志位 */
        }
    }

    通用定时器PWM输出实验

    捕获/比较通道的输出部分(通道1)

    通用定时器输出PWM原理

    通用定时器PWM输出实验配置步骤

    **相关HAL库函数介绍

    HAL_TIM_PWM_Init()        CR1\ARR\PSC        初始化定时器基础参数

    HAL_TIM_PWM_MspInit()        无                        存放NVIC\CLOCK\GPIO初始化代码

    HAL_TIM_PWM_ConfigChannel() CCMRx\CCRx\CCER    配置PWM模式、比较值、输出极性

    HAL_TIM_PWM_Start()        CCER\CR1                使能输出比较并启动计数器

    __HAL_TIM_SET_COMPARE()        CCRx        修改比较值

    __HAL_TIM_ENABLE_OCxPRELOAD()        CCER        使能通道预装载

    TIM_OC_InitTypeDef结构体

    typedef struct
    {
      uint32_t OCMode;       /*输出比较模式选择*/ 8种

      uint32_t Pulse;         /*设置比较值*/  CCRx

      uint32_t OCPolarity;    /*设置输出比较值*/  CCxP

      uint32_t OCNPolarity;   /*设置互补输出比较性*/  *高级定时器,通用定时器没有

      uint32_t OCFastMode;    /*使能或失能输出比较快速模式*/
      uint32_t OCIdleState;   /*空闲状态下OC1输出*/

      uint32_t OCNIdleState;  /*空闲状态下OC1N输出*/
    } TIM_OC_InitTypeDef;

    HAL_TIM_PWM_Start()函数

    HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)

    TIM_HandleTypeDef *htim(句柄)                uint32_t Channel(通道)

    该函数的核心只有两行

    TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

    使能定时器的指定捕获/比较通道(例如通道1、2、3或4),使该通道能够输出PWM信号或进行比较操作。

    • htim->Instance:定时器的外设指针(如TIM2、TIM3等)
    • Channel:通道编号(TIM_CHANNEL_1、TIM_CHANNEL_2等)
    • TIM_CCx_ENABLE:使能通道的标志(使能状态)

    这个函数会执行以下操作:

    1. 验证定时器是否支持比较输出(通过IS_TIM_CC1_INSTANCE检查)
    2. 验证通道号是否合法(通过IS_TIM_CHANNELS检查)
    3. 计算位掩码(如通道1的掩码是TIM_CCER_CC1E << 0
    4. 先清零对应通道的CCxE位(确保通道先被关闭)
    5. 根据TIM_CCx_ENABLE设置新状态(将CCxE位设置为1)

    作用

    • 使能指定通道的输出,使该通道的引脚能够输出PWM信号
    • CCxE = 1:引脚输出PWM/OC信号(启动PWM、单脉冲输出)
    • CCxE = 0:引脚变为高阻态或静态电平(紧急关闭电机驱动)

    __HAL_TIM_ENABLE(htim);

    功能

    使能定时器,启动定时器计数。

    详细解析

    • __HAL_TIM_ENABLE(htim):使能定时器的宏
    • 实际操作:((__HANDLE__)->Instance->CR1|=(TIM_CR1_CEN))
    • 这个宏将定时器的CR1寄存器的第0位(CEN位)设置为1

    作用

    • 启动定时器计数
    • 使定时器开始按照配置的时钟频率计数
    • TIM_CCxChannelCmd配合使用,确保定时器在通道使能后开始工作

    两行代码的执行顺序和目的

    这两行代码通常按此顺序执行,目的是:

    1. 先配置好定时器通道(使能通道)
    2. 再使能定时器(启动计数)

    这样,当定时器开始计数后,当计数器值与比较寄存器匹配时,就会在指定通道上产生PWM输出或比较事件。

    为什么需要这个顺序?

    如果先使能定时器再使能通道,可能会导致在通道还未使能时,定时器已经开始了计数,从而产生意外的输出。先使能通道再使能定时器可以确保定时器启动时,通道已经处于正确的配置状态。


    __HAL_TIM_SET_COMPARE()函数

    #define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
      (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
       ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
       ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
       ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))
    该函数整体为宏,使用了C语言的三元条件运算符?:)实现多路选择,根据通道号(__CHANNEL__)选择对应的CCR寄存器进行赋值。

    1. 通道1处理

    ((__CHANNEL__) == TIM_CHANNEL_1) ?
      ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) : ...
    • 如果通道号等于TIM_CHANNEL_1(通道1)
    • 则将比较值(__COMPARE__)写入(__HANDLE__)->Instance->CCR1(通道1的捕获/比较寄存器)

    2. 通道2处理

    ((__CHANNEL__) == TIM_CHANNEL_2) ?
      ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) : ...
    • 如果通道号等于TIM_CHANNEL_2(通道2)
    • 则将比较值(__COMPARE__)写入(__HANDLE__)->Instance->CCR2(通道2的捕获/比较寄存器)

    3. 通道3处理

    ((__CHANNEL__) == TIM_CHANNEL_3) ?
      ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) : ...
    • 如果通道号等于TIM_CHANNEL_3(通道3)
    • 则将比较值(__COMPARE__)写入(__HANDLE__)->Instance->CCR3(通道3的捕获/比较寄存器)

    4. 通道4处理(默认情况)

    ((__HANDLE__)->Instance->CCR4 = (__COMPARE__))
    • 如果以上三个条件都不满足(即通道号不是1、2或3)
    • 则将比较值(__COMPARE__)写入(__HANDLE__)->Instance->CCR4(通道4的捕获/比较寄存器)

    原理

     定时器CCR寄存器的作用

    • CCR(Capture/Compare Register)是定时器的捕获/比较寄存器
    • 在PWM模式下,CCR值决定PWM的占空比
    • 例如:如果ARR=1000,CCR=500,则占空比=500/1000=50%

      重要注意事项

    • 通道有效性

      • 通用定时器(TIM2-TIM5)有4个通道(CH1-CH4)
      • 高级定时器(TIM1、TIM8)有6个通道(CH1-CH4和CH1N-CH3N)
      • 对于高级定时器的互补通道,应使用__HAL_TIM_SET_COMPARE_N
    • 使用顺序

      • 必须在__HAL_TIM_ENABLE(使能定时器)之前设置CCR值
      • 如果先使能定时器再设置CCR,可能会导致PWM输出不稳定
    • 与HAL库函数的关系

      • HAL_TIM_PWM_Start等高级函数中,会自动调用这个宏
      • 例如:HAL_TIM_PWM_Start内部会调用__HAL_TIM_SET_COMPARE设置CCR值

    __HAL_TIM_ENABLE_OCxPRELOAD()函数

    #define __HAL_TIM_ENABLE_OCxPRELOAD(__HANDLE__, __CHANNEL__)    \
      (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC1PE) :\
       ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCMR1 |= TIM_CCMR1_OC2PE) :\
       ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC3PE) :\
       ((__HANDLE__)->Instance->CCMR2 |= TIM_CCMR2_OC4PE))

    该宏的作用是:使能指定定时器通道的输出比较预装载功能(Output Compare Preload Enable)。

    为什么需要预装载功能?

    问题场景

    假设我们正在输出PWM,当前CCR值为500,ARR=1000(50%占空比)。如果我们在计数过程中(比如CNT=300)直接将CCR改为600,会导致:

    • 当前周期的PWM占空比突然从50%变为60%
    • 产生输出毛刺(不期望的波形突变)

    预装载解决方案

    1. 先将新值(600)写入预装载寄存器
    2. 当计数器达到ARR值(1000)时,触发更新事件
    3. 更新事件发生时,预装载寄存器的值(600)被复制到实际CCR寄存器
    4. 下一个周期开始时,PWM占空比变为60%

    这样保证了PWM输出的平滑过渡,避免了输出毛刺。

    重要注意事项

    1. 必须在设置CCR之前使能预装载

      // 错误顺序:会导致立即生效,可能产生毛刺
      __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 600);
      __HAL_TIM_ENABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_1); // 晚了!CCR已生效
    2. 预装载与更新事件

      • 预装载功能只在更新事件(Update Event)发生时生效
      • 更新事件由计数器达到ARR值触发(在向上计数模式下)
    3. 与HAL库函数的关系

      • HAL_TIM_PWM_Start内部已经包含了使能预装载和设置CCR的操作
      • 通常不需要手动调用这个宏,除非需要在启动PWM前动态设置CCR

    为什么这个宏如此重要?

    在PWM应用中,预装载功能是保证输出质量的关键

    • 消除PWM波形中的毛刺
    • 实现平滑的占空比调整
    • 避免电机驱动等应用中的电流冲击
    • 保证控制系统的稳定性

    与知识库的关联

    • 这个宏直接关联到定时器的"捕获/比较寄存器"部分
    • 是实现稳定PWM输出的核心机制
    • 与之前讨论的__HAL_TIM_SET_COMPARE__HAL_TIM_ENABLE共同构成完整的PWM配置流程

    总结

    __HAL_TIM_ENABLE_OCxPRELOAD宏的作用是使能定时器通道的输出比较预装载功能,它通过设置CCMR寄存器的对应位来实现。这个功能确保了PWM占空比的改变在下一个周期开始时生效,而不是在当前周期中突然改变,从而避免了输出波形的毛刺,是实现高质量PWM输出的必要步骤。

    在STM32的PWM应用中,这个宏的正确使用对于系统的稳定性和性能至关重要。理解这个宏的工作原理,有助于开发者编写更健壮、更高质量的PWM控制代码。


    捕获/比较通道的输入部分

    通用定时器输入捕获实验配置步骤

    相关HAL库函数介绍


    TIM_IC_InitTypeDef结构体

    typedef struct
    {
      uint32_t  ICPolarity;  /*输入捕获触发方式选择,比如上升、下降捕获*/

      uint32_t ICSelection;  /*输入捕获选择,用于设置映射关系*/

      uint32_t ICPrescaler;  /*输入捕获分频系数*/

      uint32_t ICFilter;     /*输入捕获滤波器设置*/
    } TIM_IC_InitTypeDef;

    posted on 2025-11-20 15:17  ljbguanli  阅读(0)  评论(0)    收藏  举报