STM32-定时器系统原理

目录

 

STM32的定时器概述

通用定时器

通用定时器简介

主要功能

模块框图

时钟来源

通用定时器寄存器

控制寄存器 1(TIMx_CR1)

DMA/中断使能寄存器(TIMx_DIER)

时基单元寄存器

计数器模式

向上计数模式

向下计数模式

定时周期的计算

库函数代码配置定时器

配置步骤

状态查看函数

代码范例


STM32的定时器概述

STM32F1有TIME1和TIME8 等高级定时器,也有TIME2~TIME5等通用定时器,还有TIME6和TIME7等基本定时器

关于高级定时器、通用定时器、基本定时器的区别:

基本定时器:具有最基本的计时功能,与通用和高级定时器的16位自动重装载计数器不同,他的计数器是一个累加计数器!基本定时器独有一个其他定时器没有的DAC的同步电路!

通用定时器:能满足大部分使用需求的定时器。

高级定时器:高级的骚操作比较多的定时器,但是一般不常用到,用到的时候可以再根据参考手册深入研究。

STM32-定时器系统原理_第1张图片

我们一般常用到的就是通用定时器。

通用定时器

通用定时器简介

主要功能

  • 16位向上、向下、向上/向下自动装载计数器
  • 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
  • 4个独立通道:
    • 输入捕获
    • 输出比较
    • PWM生成(边缘或中间对齐模式) 
    • 单脉冲模式输出
  • 使用外部信号控制定时器和定时器互连的同步电路
  • 如下事件发生时产生中断/DMA:
    • 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发) 
    • 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数) 
    • 输入捕获
    • 输出比较
  • 支持针对定位的增量(正交)编码器和霍尔传感器电路
  • 触发输入作为外部时钟或者按周期的电流管理

加色处理的功能都是非常重要的常用功能!

模块框图

通用定时器框图如下所示,红色框是计数器部分,当仅使用计数器模式的时候,只涉及这部分。在定时器的基础上,还有下方的捕获/比较通道,输入通道可用于输入捕获(蓝色部分)和输出通道可用于输出PWM(绿色部分),与上面主要功能的颜色对应。本文只涉及到红色部分,也就是定时器的计数单元!输入捕获、PWM和DMA在后文写。

STM32-定时器系统原理_第2张图片

时钟来源

通用定时器的时钟来源有四个,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置。

  • 内部时钟(CK_INT)
  • 外部时钟模式1:外部输入脚(TIx)
  • 外部时钟模式2:外部触发输入(ETR)
  • 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器

不过,一般我们使用内部时钟

在时钟树可以看到,定时器2~7的内部时钟源是APB1。库函数版本默认使用SystemInit函数初始化系统该时钟的情况下:

SYSCLK=72M
AHB=72M
APB1=36M

也就是说APB1的分频系数为2,所以定时器2~7(包括通用定时器)时钟内部时钟频率为2*36M=72M。

STM32-定时器系统原理_第3张图片

另外需要注意的是定时器1和定时器8的时钟源是APB2!

当使用外部时钟的时候,其框图如下

STM32-定时器系统原理_第4张图片

通用定时器寄存器

通用定时器的寄存器有,包括

  • 控制寄存器 1(TIMx_CR1)
  • 控制寄存器 2(TIMx_CR2)
  • 从模式控制寄存器(TIMx_SMCR)
  • DMA/中断使能寄存器(TIMx_DIER)
  • 状态寄存器(TIMx_SR)
  • 事件产生寄存器(TIMx_EGR)
  • 捕获/比较模式寄存器 1(TIMx_CCMR1)
  • 捕获/比较模式寄存器 2(TIMx_CCMR2)
  • 捕获/比较使能寄存器(TIMx_CCER)
  • 计数器(TIMx_CNT)
  • 预分频器(TIMx_PSC)
  • 自动重装载寄存器(TIMx_ARR)
  • 捕获/比较寄存器 1~4(TIMx_CCR1~4)
  • DMA控制寄存器(TIMx_DCR)
  • 连续模式的DMA地址(TIMx_DMAR)

加黑的几个寄存器是使用定时器基本功能的必须了解的几个寄存器!这里也只叙述这几个寄存器。

控制寄存器 1(TIMx_CR1)

DIR:计数方向
0:计数器向上计数
1:计数器向下计数
当计数器配置为中央对齐模式或编码器模式时,该位为只读。

CEN:使能计数器
0:禁止计数器;
1:使能计数器。

CMS[1:0]:选择中央对齐模式
00:边沿对齐模式。计数器依据方向位(DIR)向上或向下计数。
01:中央对齐模式1。计数器交替地向上和向下计数。配置为输出的通道(TIMx_CCMRx寄存器
中CCxS=00)的输出比较中断标志位,只在计数器向下计数时被设置。
10:中央对齐模式2。计数器交替地向上和向下计数。配置为输出的通道(TIMx_CCMRx寄存器
中CCxS=00)的输出比较中断标志位,只在计数器向上计数时被设置。
11:中央对齐模式3。计数器交替地向上和向下计数。配置为输出的通道(TIMx_CCMRx寄存器
中CCxS=00)的输出比较中断标志位,在计数器向上和向下计数时均被设置。
当在计数器开启时(CEN=1),不允许从边沿对齐模式转换到中央对齐模式。

其他的位,请查看《STM32中/英文参考手册》

DMA/中断使能寄存器(TIMx_DIER)

UIE:允许更新中断
0:禁止更新中断
1:允许更新中断,计数寄存器中的数据更新时触发中断请求。

CCxIE:允许捕获/比较x中断 (Capture/Compare 1 interrupt enable) 
0:禁止捕获/比较x中断;
1:允许捕获/比较x中断。
这个寄存器用于使能捕获中断(后面会写)

时基单元寄存器

时基单元包含:

  • 预分频器寄存器 (TIMx_PSC)
  • 自动装载寄存器 (TIMx_ARR)
  • 计数器寄存器 (TIMx_CNT) 

预分频器寄存器(TIMx_PSC)

PSC[15:0]:预分频器的值

该寄存器存放的是预分频器的值。预分频器对定时器的时钟源进行分频,然后提供给计数器。前文已经提到,通用定时器的时钟源有四个,可以通过TIMx_SMCR 寄存器的的相关为进行设置。

为什么要进行分频?

答:因为一般时钟源太快了。以系统时钟源为例,72M的频率,如果不分频的话,那16位定时器的计时周期就65536/72M秒,约等于0.91ms,时间太短了。

自动重装载寄存器(TIMx_ARR)

ARR[15:0]: 自动重装载的值

ARR包含了将要传送至实际的自动重装载寄存器的数值。

计数寄存器(TIMx_CNT)

CNT[15:0]:计数器的值

计数器模式

向上计数模式

计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。

向下计数模式

计数器从自动装入的值(TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件。

定时周期的计算

定时周期与时基单元密切相关,首先确定定时器时钟源的频率Tclk(使用内部时钟源,一般是72M),通过设定分频器的值PSC可以确定最大计时周期,然后通过设定自动重装载寄存器的值ARR,可以确定具体的溢出时间!计算公式如下:

Tout(溢出时间)= (ARR+1)(PSC+1)/Tclk

比如,要定时500ms,Tclk为72M,令PSC=7199,ARR=4999,即可。

库函数代码配置定时器

配置步骤

  • 使能定时器时钟
    • 要明确定时器使用的时钟源!如果使用内部时钟,通用定时器和基本定时器是在APB1下!应该调用
      void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
      高级定时器是在APB2下,调用
      void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
  • 初始化定时器
    • void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
  • 开启定时器更新中断,配置NVIC中断控制器
    • void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//开启定时器更新中断
    • void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//中断优先级配置
  • 使能定时器
    • void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
  • 编写中断服务函数
    • 根据使用的定时器,开启相应的中断函数,注意在中断函数中判断和清除中断标志位

状态查看函数

FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

代码范例

timer.c

 void Tim3_Init(u16 arr,u16 psc)
 {
	 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitstructure;
	 NVIC_InitTypeDef NVIC_Initstructure;
	 
	 /*************************定时器时钟源使能*********************************/
	 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	 
	 /*************************定时器基本配置*********************************/
	 TIM_TimeBaseInitstructure.TIM_Prescaler = psc;//预分频系数
	 TIM_TimeBaseInitstructure.TIM_Period = arr;//自动装在值
	 TIM_TimeBaseInitstructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
	 TIM_TimeBaseInitstructure.TIM_ClockDivision = TIM_CKD_DIV1;//不知道是什么
	 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitstructure);
	 
	 /*************************定时器中断配置使能*********************************/
	 TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
	 
	 /*************************中断号、优先级配置*********************************/
	 NVIC_Initstructure.NVIC_IRQChannel = TIM3_IRQn;
	 NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE; 
	 NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0;
	 NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;
	 NVIC_Init(&NVIC_Initstructure);
	 
	 /*************************使能TIM3计数器*********************************/
	 TIM_Cmd(TIM3,ENABLE);
 }
 
 
 void TIM3_IRQHandler(void)
 {
	if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)
	{
		LED0 = !LED0;
		TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
	}
 }

main.c

 int main(void)
 {	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	LED_Init(); 
	Tim3_Init(TIM3_CRR,TIM3_PSC);
	while(1)
	{
		//其他操作
	}
 }

 

你可能感兴趣的:(STM32,定时器,STM32,嵌入式,物联网)