通俗易懂讲明白定时器(stm32)

这篇具有很好参考价值的文章主要介绍了通俗易懂讲明白定时器(stm32)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

定时器是嵌入式开发中极其重要的一员,它可以分为软件定时器和硬件定时器。

软件定时器很不精准,通过循环语句粗略的去计算延时的时间,对时序要求较高的场景是完全不适用的;

硬件定时器在stm32中种类也是比较多的,基本定时器、通用定时器、高级定时器、低功耗定时器等,它们的基本特性相差无几,更多的是应用场景的不一样,例如高级定时器特性会更好一点(带可编程死区的互补输出、输出通道数更多等),意味着它的应用场景更高级,低功耗定时器功耗更低,可以在除待机模式以外的所有电源模式下保持运行,即使没有内部时钟源也可以运行。根据自己的需求去选择合适的定时器,这里仅对通用定时器进行讲解。

1. 通用定时器特性

16 /32位递增、递减和递增/递减自动重载计数器(递增、递减只是计数方式而已,往ARR寄存器中写入计数值,为空时计数器不工作);

② 16 位可编程预分频器,用于对计数器时钟频率进行分频(可在运行时修改),分频系数介于 1 到 65535 之间

③ 独立通道可用于:输入捕获/PWM 生成/输出比较/单脉冲模式输出等

④ 发生如下事件时生成中断/DMA 请求:(有些定时器不具备DMA请求

        更新:计数器上溢/下溢、计数器初始化;

        触发事件(计数器启动、停止、初始化或通过内部/外部触发计数);

        输入捕获;

        输出比较;

⑤ 触发输入作为外部时钟(可以作为其它定时器的外部时钟源);

2. 定时器框图

查看手册可以知道各个定时器挂载在哪条总线下(APB1/APB2)。

需要注意的是,定时器挂载在APB1总线上时,当 APB1的时钟不分频的时候,通用定时器 TIMx的时钟就等于 APB1的时钟,否则定时器 TIMx的时钟是 APB1时钟的 2倍;挂载在APB2的定时器时钟就等同于APB2时钟。

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件
图1

 通过TIMx_SMCR寄存器的相关位来设置具体选择哪个时钟源。通常不需要去配置,直接使用内部时钟源即可。 

位 2:0 SMS:从模式选择 (Slave mode selection)
选择外部信号时,触发信号 (TRGI) 的有效边沿与外部输入上所选的极性相关(请参见输入控
制寄存器和控制寄存器说明)。
0000:禁止从模式——如果 CEN =“1”,预分频器时钟直接由内部时钟提供。
0001:编码器模式 1——计数器根据 TI1FP2 电平在 TI2FP1 边沿递增/递减计数。
0010:编码器模式 2——计数器根据 TI2FP1 电平在 TI1FP2 边沿递增/递减计数。 

0111:外部时钟模式 1——由所选触发信号 (TRGI) 的上升沿提供计数器时钟......

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件
图2

3. 计数模式

向上、向下计数也叫递增、递减计数;

递增计数:从0计数到设定的ARR值,然后产生溢出事件;

递减计数:与递增计数相反;

中心对齐计数:也就是结合递增和递减两种模式,需要注意的是它的溢出值。

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件

4. 时基单元

4.1  计数器寄存器 (TIMx_CNT)

存储当前计数器计数值,由上图2可知,计数器时钟由PSC预分频器输出的 CK_CNT提供,仅当 TIMx_CR1 寄存器中的计数器启动位(CEN) 置 1 时,才会启动计数器。

位 0 CEN:计数器使能 (Counter enable)
0:禁止计数器
1:使能计数器

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件
图3

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件

4.2 自动重载寄存器 (TIMx_ARR)

预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件 (UEV)时传送到影子寄存器,这取决于 TIMx_CR1 寄存器中的自动重载预装载使能位 (ARPE),当需要动态去修改ARR寄存器值的时候就设置ARPE为1,具有缓冲功能,可以减少误差。

当定时器触发事件的时候,且当TIMx_CR1 寄存器中的 UDIS 位为0 时,将发送更新事件。

位 7- ARPE:自动重载预装载使能 (Auto-reload preload enable)
0:TIMx_ARR 寄存器不进行缓冲
1:TIMx_ARR 寄存器进行缓冲

位 1 UDIS:更新禁止 (Update disable)(此位由软件置 1 和清零,用以使能/禁止 UEV 事件生成)
0:使能 UEV。更新 (UEV) 事件可通过以下事件之一产生:
        – 计数器上溢/下溢
        – 将 UG 位置 1
        – 通过从模式控制器生成的更新事件
1:禁止 UEV

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件
图4

 

4.3 预分频器寄存器 (TIMx_PSC)

预分频器可对计数器时钟频率进行分频,分频系数介于 1 和 65536 之间。该预分频器基于16 位/32 位寄存器(TIMx_PSC 寄存器)所控制的 16 位计数器。由于该控制寄存器具有缓冲功能,因此预分频器可实现实时更改。而新的预分频比将在下一更新事件发生时被采用

举个例子:假如定时器挂载在APB2(108Mhz)总线下,定时器的预分频寄存器设置为107(真实的预分频数为PSC寄存器的值加1),则定时器间隔1us就计一个数。

溢出时间计算公式为T =  (ARR+1)*(PSC+1) / Ft时钟源频率

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件

4.4  时序图分析

下图5时序图为预分频器分频由1变为2:

使能CEN开始计数,从计数器寄存器数值可以看出当前为递增式计数;

分频为1,且未产生溢出事件时,每间隔一个时钟周期计数器寄存器就计一次数;

在发生溢出事件前,动态更改预分频器分频为2,在更新事件后,预分频器的缓冲区中将重新装载预装载值,每间隔2个时钟周期(预分频器计数器计两个数)计数器计时器计一次数。

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件
图5

5. 定时器示例

示例使用定时器3:挂载在APB1下,其定时器时钟频率为108MHz;

目的是将它配置为间隔1000ms触发溢出事件。

5.1 重点寄存器

首先,需要重点关注如下几个寄存器:

控制寄存器1 (TIMx_CR1):第0位置 1 表示使能计数器;

DMA/中断使能寄存器( TIMx_DIER):第0位 置 1,来允许由于更新事件所产生的中断;

预分频寄存器( TIMx_PSC):设置对时钟进行分频,然后提供给计数器,作为计数器的时钟;

自动重装载寄存器(TIMx_ARR):设置计数值,达到后触发溢出事件。

5.2 软件配置流程

定时器结构体如下:

typedef struct {

        TIM_TypeDef *Instance;                //是寄存器基地址

        TIM_Base_InitTypeDef Init;           //初始化配置结构体

         HAL_TIM_ActiveChannel Channel;        //通道选择

         DMA_HandleTypeDef *hdma[7];            //DMA传输相关

         HAL_LockTypeDef Lock;

         __IO HAL_TIM_StateTypeDef State;        //状态标识

}TIM_HandleTypeDef;

预分频设为10800分频,计数10000次,即为溢出时间为1000ms;

配置好结构体变量后,调用hal库函数HAL_TIM_Base_Init()进行初始化;

HAL_TIM_Base_Start_IT()函数开启定时器并开启更新中断,对相应寄存器操作,该函数已经封装好的了,直接调用就很省事。

TIM_HandleTypeDef TIME3_Handler;      //定时器句柄 

void TIM3_Init()
{  
    TIME3_Handler.Instance=TIM3;                          //通用定时器3
    TIME3_Handler.Init.Prescaler=10800-1;                 //分频
    TIME3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器
    TIME3_Handler.Init.Period=10000-1;                    //自动装载值
    TIME3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
    HAL_TIM_Base_Init(&TIME3_Handler);
    
    HAL_TIM_Base_Start_IT(&TIME3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE    
}

系统运行中会产生很多中断,所以需要去设置定时器中断的优先级;

HAL_NVIC_SetPriority(TIM3_IRQn,2,1);    //设置中断优先级,抢占优先级2,子优先级1

HAL_NVIC_EnableIRQ(TIM3_IRQn);         //开启ITM3中断  

定时器产生中断,意味着就需要中断处理函数,中断处理函数中编写控制逻辑,但别去做耗时的操作,也尽量别添加打印输出,不然会影响实时性。

定时器3的中断服务函数TIM3_IRQHandler(),在startup_xxx.s的汇编文件里可以看到,知道就好了,不需要格外关注;

tim_it_cc3,STM32,硬件知识,stm32,单片机,嵌入式硬件

 调用hal库函数HAL_TIM_IRQHandler()处理更新中断等,

常用的回调函数:

__weak  void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//更新中断
__weak  void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim);//输出比较
__weak  void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//输入捕获
__weak  void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim);//触发中断

这些都是弱函数,需要我们去重写,就是说把自己的中断处理逻辑写到这里。文章来源地址https://www.toymoban.com/news/detail-758383.html

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
  /* Capture compare 1 event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !=RESET)
    {
      {
        __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
        htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;
        
        /* Input capture event */
        if((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00)
        {
          HAL_TIM_IC_CaptureCallback(htim);
        }
        /* Output compare event */
        else
        {
          HAL_TIM_OC_DelayElapsedCallback(htim);
          HAL_TIM_PWM_PulseFinishedCallback(htim);
        }
        htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
      }
    }
  }
  /* Capture compare 2 event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2);
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2;
      /* Input capture event */
      if((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00)
      {          
        HAL_TIM_IC_CaptureCallback(htim);
      }
      /* Output compare event */
      else
      {
        HAL_TIM_OC_DelayElapsedCallback(htim);
        HAL_TIM_PWM_PulseFinishedCallback(htim);
      }
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
    }
  }
  /* Capture compare 3 event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3);
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3;
      /* Input capture event */
      if((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00)
      {          
        HAL_TIM_IC_CaptureCallback(htim);
      }
      /* Output compare event */
      else
      {
        HAL_TIM_OC_DelayElapsedCallback(htim);
        HAL_TIM_PWM_PulseFinishedCallback(htim); 
      }
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
    }
  }
  /* Capture compare 4 event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4);
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4;
      /* Input capture event */
      if((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00)
      {          
        HAL_TIM_IC_CaptureCallback(htim);
      }
      /* Output compare event */
      else
      {
        HAL_TIM_OC_DelayElapsedCallback(htim);
        HAL_TIM_PWM_PulseFinishedCallback(htim);
      }
      htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
    }
  }
  /* TIM Update event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE);
      HAL_TIM_PeriodElapsedCallback(htim);
    }
  }
  /* TIM Break input event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK);
      HAL_TIMEx_BreakCallback(htim);
    }
  }
  
    /* TIM Break input event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK2) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK);
      HAL_TIMEx_BreakCallback(htim);
    }
  }

  /* TIM Trigger detection event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER);
      HAL_TIM_TriggerCallback(htim);
    }
  }
  /* TIM commutation event */
  if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET)
  {
    if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) !=RESET)
    {
      __HAL_TIM_CLEAR_IT(htim, TIM_FLAG_COM);
      HAL_TIMEx_CommutationCallback(htim);
    }
  }
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&TIME3_Handler);
}

//定时器3中断更新函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim==(&TIM3E_Handler))
    {
        //中断控制逻辑编写
    }
}

到了这里,关于通俗易懂讲明白定时器(stm32)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM-32:TIM定时中断—定时器定时中断/定时器外部时钟

    定时器可以对输入的时钟进行计数,并在达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等

    2024年02月09日
    浏览(58)
  • STM32 MCU 定时器详解(3)--高级定时器

    16位递增、递减、中心对齐计数器(计数值:0~65535) 16位预分频器(分频系数:1~65536) 可用于触发DAC、ADC 在更新事件、触发事件、输入捕获、输出比较时,会产生中断/DMA请求 4个独立通道,可用于:输入捕获、输出比较、输出PWM、单脉冲模式 使用外部信号控制定时器且可实

    2024年04月17日
    浏览(49)
  • 定时器详解 -- 定时器中断、PWM输出 --stm32

    STM32F103系列芯片拥有多种定时器,包括基本定时器、通用定时器和高级定时器,每种定时器都具有一些特定的功能。 向上计数:计数器从0计数到自动重装载值(ARR),然后重新从0开始计数并且产生一个计数器溢出事件。 向下计数:计数器从自动重装载值(ARR)开始向下计数

    2024年02月11日
    浏览(63)
  • STM32 MCU 定时器详解(1)--基本定时器

    定时器是一种电子组件,主要用于定时控制,具备精确计时的能力。它可以在设定的时间间隔内触发特定的操作,如发送数据、采集传感器信息、检测输入信号或产生规律性输出波形。这种灵活性使定时器在多个行业中得到广泛应用,支持各种复杂功能的实现,是现代电子系

    2024年02月22日
    浏览(52)
  • stm32 定时器部分(定时中断)

    一.定时中断(概念部分) 定时中断主要包含两种中断一种是更新中断还有一种是输入捕获中断 更新中断 :更新中断通常用于定时器的基本定时功能。当定时器计数器溢出并重新从零开始计数时,会触发更新中断。你可以配置定时器的计数周期和预分频器来控制定时器的计时

    2024年03月15日
    浏览(60)
  • STM32定时器

    目录 一 定时器的基本介绍 二 定时器的原理框图 1、定时器时钟 2 时基单元 3 捕获输入 4 PWM输出。 三 定时器的应用 1 定时器的基础定时计数功能 2 PWM比较输出 3 外部脉冲计数                                 a、外部触发输入(ETR--外部时钟模式2)                   

    2024年02月16日
    浏览(43)
  • stm32——定时器

    TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码

    2024年02月06日
    浏览(46)
  • STM32中TIM定时器定时功能详解(适用基本,通用,高级定时器)

    定时器有高级定时器、通用定时器、基本定时器三种类型。具体功能如下。 上面是每种定时器所具有的功能。 我们可以看到每种定时器都有一个定时功能,(可能是名字的由来吧)。当然,每个定时器都可以来使用定时功能,但是我们往往在基本定时器和通用定时器上面使用

    2024年01月19日
    浏览(63)
  • STM32入门——定时器

    内容为江科大STM32标准库学习记录 TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时(这里计算就是72MHz主频通过预分频继续分频(假设分频

    2024年02月14日
    浏览(33)
  • STM32—系统定时器

    SysTick是一种系统定时器,可以用于实现操作系统、任务调度、时钟管理等功能。它通常集成在微控制器中,并且是硬件实现的,具有高精度和可靠性。 在ARM Cortex-M处理器中,SysTick定时器可以配置为计数器模式或外部时钟模式。在计数器模式下,SysTick定时器使用内部时钟作为

    2024年02月21日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包