YIE002开发探索04-定时器

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)

定时器的最基本的功能是用来周期性的定时,当然,它还用来实现如下功能:
1) 输入捕获。脉冲计数,在上升沿或下降沿检测,已经PWM的输入检测;
2) 输出比较。脉冲输出和步进电机的控制;
3) PWM生成。电压输出控制、直流减速电机控制等;
4) 编码器接口、霍尔传感器接口等。

当然,根据所用芯片不同,定时器还能提供更多的功能,比如与DMA的配合等。本篇准备用定时器产生的时间中断,周期性地控制LED灯的亮灭,以期对定时器的编程有基本的理解。

1 STM32的定时器

YIE002-STM32所用的芯片为STM32F103C8T6,属于STM32F1系列,在此系列,STM32提供了大量的定时器:2个基本定时器(TIM6、TIM7)、4个通用定时器(TIM2、TIM3、TIM4、TIM5)、2个高级控制定时器(TIM1和TIM8),以及4个特定功能定时器(SysTick、IWDG、WWDG、RTC),共有12个。另外,超大容量系列的产品中,又增加了5个通用寄存器TIM9~TIM14。

从功能上来说,高级控制通用器的功能是通用定时器的超集、通用定时器的功能是基本定时器的超集。比较通用定时器和高级控制定时器,可知它们的功能非常接近,高级控制定时器只是针对电动机的控制增加了一些功能。

因此,本篇准备使用通用定时器TIM3,构建演示程序。

1.1 定时器的时钟

TIM1和TIM8是高级控制定时器,时钟由APB2输出产生;TIM2至于TIM5是通用定时器,TIM6和TIM7是基本定时器,时钟由APB1输出产生。

从前几篇中可以知道,F103C8T6的系统时钟为72MHZ。如图1所示的时钟树,给出了定时器的时钟频率。


图1 定时器的时钟树

图1是以STM32F103C8T6为例绘制的。APB1为36MHZ,APB2为72MHZ。在我们的配置中,APB1 Prescaler是2分频;APB2 Prescaler是1分频。从图中可以看出,不论是通过挂在APB1下、还是挂在APB2下的时钟,其时钟都是72MHZ。

也即TIMxCLK均为72MHZ,与系统时钟等同。

1.2 定时器的中断时间(TIM3)

STM32F1的通用定时器(本例中使用的TIM3)是通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成,其基本的配置过程包括:
1) TIM3时钟使能;
2) 初始化定时器参数,设置自动重装值、分配系数、计数方式等;
3) 允许TIM3更新中断;
4) 设置TIM3的中断优先级;
5) 使能TIM3;
6) 编写中断服务函数。

使用CubeMX编程,上述6个步骤的前5个都是通过图形配置、自动生成代码的,只有中断服务函数需要编写。

编写代码之前,我们需要考虑定时器以多长的时间定时中断。计算中断时间,与TIMx_PSC寄存器(预分频寄存器)和TIMx_ARR寄存器(自动重装载寄存器)有关。具体的细节可以查看《STM32参考手册》,我们主要关注CubeLibrary中如何操作。在stm32f1xx_hal_tim.h中定义了定时器初始化的数据结构:

/** @defgroup TIM_Exported_Types TIM Exported Types
  * @{
  */

/**
  * @brief  TIM Time base Configuration Structure definition
  */
typedef struct
{
  uint32_t Prescaler;         

  uint32_t CounterMode;      

  uint32_t Period;            

  uint32_t ClockDivision;     

  uint32_t RepetitionCounter;  
                                    
  uint32_t AutoReloadPreload;  
} TIM_Base_InitTypeDef;

数据结构TIM_Base_InitTypeDef的成员变量Prescaler对应预分频寄存器、成员变量Period对应自动重装载寄存器。注意,这两个值可设置的值为[0,65535],注意不要越界。

假设TimClk为定时器时钟频率(单位:MHZ),TimOut为中断时间(也即溢出时间,单位:秒),则计算公式为:

TimOut = ((Prescaler + 1) * (Period + 1)) /TimClk;

TIM3的时钟频率TimClk为72MHZ,如果需要1秒的中断时间,则可以设置

Prescaler=7200-1; 
Period=10000-1;

计算可得TimOut = 1 秒。

常用的几个溢出时间:

Period =9999  Prescaler =7199   1000ms
Period =4999  Prescaler =7199   500ms
Period =2499  Prescaler =7199   250ms
Period =1999  Prescaler =7199   200ms
Period =999   Prescaler =7199   100ms

有了这些基本概念,就可以进入编程步骤了。

2 YIE002-STM32的定时器编程

如之前一样,分为图形配置然后自动生成代码,以及手动编写代码。

2.1 Cube MX的图形配置

本篇的例子,是在03ExtIO上修改的。原来的配置没有修改,主要添加定时器TIM3的配置。如图2所示。

图2 配置TIM3

在Pinout & Configuration菜单下,选择Timers中的TIM3,将其“Internal Clock”的选项勾选上。同时设置Prescaler值为7200-1,Counter Period值设置为10000-1。

然后设置TIM3中断的优先级,可在System Core的NVIC中设置,如图3所示。

图3 设置TIM3中断优先级

之前设置的外部中断的优先级,可以不必修改,保留即可。

完成设置后,生成代码。

2.2 编写TIM3中断服务函数

定时的中断服务处理函数,在startup_stm32f103xb.s中可以看到。

 EXPORT  TIM1_BRK_IRQHandler        
 EXPORT  TIM1_UP_IRQHandler         
 EXPORT  TIM1_TRG_COM_IRQHandler    
 EXPORT  TIM1_CC_IRQHandler         
 EXPORT  TIM2_IRQHandler            
 EXPORT  TIM3_IRQHandler            
 EXPORT  TIM4_IRQHandler   

我们需要处理的中断函数是TIM3_IRQHandler。跟踪此函数,可以知道,Cube Library提供了回调函数HAL_TIM_PeriodElapsedCallback()供用户处理定时器中断。

因此需要处理的工作包括两项:

一是在初始化函数MX_TIM3_Init()中,添加对TIM3的中断使能函数,代码如下:

/* USER CODE BEGIN TIM3_Init 2 */
	//robin add 20210725
	if(HAL_TIM_Base_Start_IT(&htim3)!=HAL_OK)
	{
		Error_Handler();
	}
 /* USER CODE END TIM3_Init 2 */  

二是添加对回调函数HAL_TIM_PeriodElapsedCallback()的实现,代码如下(添加在USER CODE BEGIN 4中):

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim ==(&htim3))    
        HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
}

编译下载,就可以看到实际效果了。所实现的代码,将以1秒为周期,驱动灯LED1亮灭。

415 total views, 1 views today

发表评论

电子邮件地址不会被公开。