STM32 定时器

概述

用来定时的器件

定时器的基本原理

在 STM32 上,一般来说,定时器由三个部分组成

(1)时基单元:Time Base Unit

定时器的基本部件。所有的定时器都有这个部件。

  由一个“计数器”组成。“计数器”可以设置为从一个值按照  
  一定的时钟频率递减到0,也可以设置从0按照一定的时钟  
  频率递增到一个值。然后产生一个溢出事件/中断,从而达到  
  定时的功能。

例子1

(2)输入捕获单元:可以对一个或多个输入信号进行处理

有些定时器没有。
可以捕获一个或多个输入信号(看你这个定时器有几个输入通道 CHx_IN)
有什么用呢?如: 可以计算输入信号的频率

  输入信号经过“输入捕获阶段”(数字滤波、多路复用、预分频等等),  
  到信号检测,当检测到需要的信号状态变化时(上升沿/下降沿),  
  就会把定时器时基单元中计数器的值,锁定到“捕获/比较寄存器”  
  这样,就可能根据预先设定的定时器的参数(时钟频率,N值等等),  
  就可以算出从开始到捕获这个信号状态的这一段的时间。

(3)输出比较单元:可以输出一个或多个信号

有些定时器没有。

  定时器还可以输出一个电平、  
  并且可以根据“比较寄存器”的值,翻转电平的状态。  
  如:	 CCR: Capture Compare Register捕获/比较寄存器  
  		定时器的第(2)输入捕获单元 和第(3) 输出比较单元  
  			不同时用。

  	CNT >= CCR 输出一个高电平  
  	CNT < CCR	输出一个低电平

  	PWM方波

STM32F4xx 定时器概述

SysTick2

系统定时器, 所有 Cortex M3/M4 中都会内置于 NVIC 一个 SysTick定时器
这个定时器,被称为系统定时器。这个定时器只有一个时基单元
并且在溢出时,会产生一个 SysTick 异常(中断)

为什么要内置一个这个玩意?

因为大多数操作系统,都需要一个硬件定时器来产生操作系统需要的
时钟嘀哒中断。用于操作系统计时。这个定时器作为整个系统的基本
的时间单元。

假设我不跑操作系统呢? 裸奔呢
延时 -> 可以用 SysTick 来实现

SysTick 定时器说明及寄存器设置: 《ARM Cortex M4 权威指南》

  It is integrated as a part of the NVIC and can generate the SysTick  
  exception (exception type #15). The SysTick timer is a simple decrement 24-bit  
  timer, and can run on processor clock frequency or from a reference clock frequency  
  (normally an on-chip clock source).

  它被内置于NVIC中,它能够产生一个SysTick异常(异常类型 #15).这个SysTick定时器  
  是一个简单的24bits的递减定时器,它可以运行在处理器的时钟频率上,也可以运行在一个  
  外部的时钟频率上(通常在片上的时钟源)

  ……。

例子

基本定时器

TIM6, TIM7:

  • 只有时基单元
  • 无输入输出引脚
  • 计数器只有 16bits,而且只支持递增模式

通用定时器

TIM2~TIM5:

  • 16bits(TIM3,TIM4)或 32bits(TIM2,TIM5)计数器,可以配置为递增,递减,先递增后递减(0->N->0)的模式

  • 多达 4 个独立通道,可用于:

    • 输入捕获
    • 输出比较
    • PWM 生成(边沿或中心对齐模式)
    • 单脉冲模式输出

TIM9~TIM14:

  • 16bits 自动加载递增计数器

  • 2 个独立通道,可用于:

    • 输入捕获
    • 输出比较
    • PWM 生成(边沿或中心对齐模式)
    • 单脉冲模式输出

高级定时器

TIM1, TIM8:

  • 16bits 计数器,可以可以配置为递增,递减,先递增后递减(0->N->0)的模式

  • 多达 4 个独立通道,可用于:

    • 输入捕获
    • 输出比较
    • PWM 生成(边沿或中心对齐模式)
    • 单脉冲模式输出

“高级”
重复计数器(TIMx_RCR) : Repeation Counter Register
如果使用重复计数,则当计数器重复溢出的次数达到设定的
重复计数器的值 +1 时,才会产生溢出事件/中断。

如果不用重复计数器,在每次计数器溢出时都会产生事件/中断。
这个时候,和通用定时器没有区别。

....
信号处理方面有高级的地方...

看门狗 watch dog

单片机的独立看门狗(Independent Watchdog Timer,简称 IWDG)是一种用于监控和保护微控制器(MCU)系统的硬件组件。 它的主要功能是检测系统是否在正常工作,并在发现系统出现故障时自动进行复位操作,以确保系统的稳定性和可靠性。

本质:一个能够产生复位信号的计数器。

复位就是使 MCU 回到初始状态。对于单片机来说,开机的时候需要复位,以便使得 CPU 和其他功能部件处于一个正确的初始状态,并以此为起点开始工作,当出现死机的情况也应当对其进行复位,用以摆脱死机状态。

系统复位方式共有 5 种,分别为:

1、硬件复位。

2、WWGD 复位。

3、IWDG 复位。

4、软件复位。

5、低功耗复位。

独立看门狗复位:这种方式使用独立的看门狗来监控单片机系统的工作状态,当单片机工作异常时,看门狗会产生复位信号,将单片机系统复位。

特征:1、独立看门狗是一个递减计数器产生的复位。2、时钟信号是有独立 RC 振荡器产生。3、可以在待机和停止模式下运行。4、当看门狗被激活后,当递减为 0 是产生复位。

喂狗:如果在计数没减到 0 之前,重置计数器的值的话,那么就不会产生复位信号,这个动作我们称为。

作用:检测外界电磁干扰或硬件导致程序跑飞的问题。

工作原理

image

IWDG 有一个输入(时钟 LSI) ,为内部专门的 40Khz 低速时钟(LSI) 驱动 ,经过分频器进行预分频,分频为工作时钟,然后提供给递减计数器,当递减计数器减为 0 时产生复位信号。为递减计数器赋值的过程称为喂狗,喂狗的作用就是不产生复位信号。

实例

(1) 配置定时器的通道(输入/输出)引脚
		TIM通道引脚是由GPIO口来复用的
		1.1 RCC_AHB1... =>使能GPIO分组的时钟
		1.2 GPIO_Init   => 配置GPIO引脚的功能   AF: Alternate Function 复用功能
		1.3 选择GPIO口具体的复用功能
			GPIO_PinAFConfig
			void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, 
					uint16_t GPIO_PinSource, 
					uint8_t GPIO_AF);
				GPIOx: 指定具体的GPIO分组
				GPIO_PinSource:指定具体的GPIO引脚编号
				GPIO_AF: 指定具体的复用功能 
	
	(2) 定时器时基单元(Time Base Unit)配置
		1.1  RCC_APBx => 使能定时器的时钟
		1.2  TIM_TimeBaseInit  => 初始化定时器的时基单元
			void TIM_TimeBaseInit(TIM_TypeDef* TIMx, 
					TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
				TIMx: 指定要初始化的定时器,如:
							TIM1, TIM2,....
			
				TIM_TimeBaseInitStruct: 指向要初始化的结构体。
				
				typedef struct
				{
				  uint16_t TIM_Prescaler;       
						指定定时器的预分频
						TIM_CLK: 计数器(定时器)的时钟
					
						TIM_CLK = Fin(定时器的输入时钟频率)/ (TIM_Prescaler + 1)
					
						if APBx prescaler == 1 :
							Fin = APBx BUS clock
						else
							Fin = APBx Bus clock * 2
						
						TIM13 在总线APB1上,
							APB1 prescaler == 4
							APB1 BUS clock = 168M/4 = 42M
						so
							Fin = 42M*2 = 84M

				  uint16_t TIM_CounterMode;    
							指定计数器模式(递增,递减,先递增后递减 ....)
							TIM_CounterMode_Up  递增 0 --> N
							TIM_CounterMode_Down 递减 N --> 0
								
										0 ---> N --> 0
										0 ---> n-1 -> 0
										0 ----> N + 1 --> 0
										有一点细微的区别,大家可以查看datasheet
							TIM_CounterMode_CenterAligned1 中心对齐模式1
							TIM_CounterMode_CenterAligned2 中心对齐模式2
							TIM_CounterMode_CenterAligned3 中心对齐模式3
				  
				  uint32_t TIM_Period;        
							指定自动加载的起始值。 N值, 定时器周期。
				  
				  uint16_t TIM_ClockDivision;   
							指定输入捕获阶段的数字滤波的采样频率, 分频值
							可以是如下值:
								TIM_CKD_DIV1  数字滤波采样频率=定时器频率 
								TIM_CKD_DIV2 数字滤波采样频率=定时器频率*2
								TIM_CKD_DIV4  数字滤波采样频率=定时器频率*4

				  uint8_t TIM_RepetitionCounter;  
							计数器溢出重复次数,只有高级定时器TIM1TIM8才有。
							计数器溢出重复次数是指只当把“计数器”溢出的次数=重复的次数时
								才会产生一个定时器的溢出中断。

				} TIM_TimeBaseInitTypeDef; 

	(3) 定时器输出通道/输入通道配置
		(3.1) TIM_OC1Init:
				Output Channel 1: 输出通道1
			void TIM_OC1Init(TIM_TypeDef* TIMx, 
					TIM_OCInitTypeDef* TIM_OCInitStruct);
				TIMx: 指定定时器,如: TIM1, TIM2, ...
				TIM_OCInitStruct: 指向OC1的初始化结构体
					typedef struct
					{
					  uint16_t TIM_OCMode;     
							指定输出通道的模式
							TIM_OCMode_Timing               
								输出比较寄存器(TIMx_CCRn)与计数器(TIMx_CNT)的比较不影响输出
						
							TIM_OCMode_Active               
								TIMx_CNT = TIMx_CCRn时,输出高电平						
						
							TIM_OCMode_Inactive             
								TIMx_CNT = TIMx_CCRn时,输出低电平
						
							TIM_OCMode_Toggle                
								toggle:翻转
								TIMx_CNT = TIMx_CCRn时,翻转电平
						
							TIM_OCMode_PWM1             
								TIMx_CNT < TIMx_CCRn时, 输出高电平
								否则,输出低电平
						
							TIM_OCMode_PWM2   
								TIMx_CNT < TIMx_CCRn时, 输出低电平
								否则,输出高电平
						
					  
					  
					  uint16_t TIM_OutputState;   
							输出信号 状态(enable/disable)
							TIM_OutputState_Disable 不输出信号
							TIM_OutputState_Enable  输出信号
						
										  
					  
					  uint16_t TIM_OutputNState;  
							互补输出信号的 状态(enable/disable)
							"互补输出信号" 只针对高级定时器 TIM1, TIM8
							TIM_OutputNState_Disable 不输出互补信号
							TIM_OutputNState_Enable  输出互补信号
					  
					  
					  uint32_t TIM_Pulse;       
							指定要比较的值
			
					  uint16_t TIM_OCPolarity;   
						Polarity: 极性
							指定输出极性
						TIM_OCPolarity_High: 高电平为有效电平
						TIM_OCPolarity_Low: 低电平有效电平
					  
					  uint16_t TIM_OCNPolarity;   
							指定互补输出信号的极性,仅针对TIM1,TIM8
					  
					  
					  uint16_t TIM_OCIdleState;   
							Idle: 空闲。
							输出引脚在空闲状态的输出,仅针对TIM1TIM8
					  
					  
					  
					  uint16_t TIM_OCNIdleState;  
							互补输出引脚在空闲状态的输出,仅针对TIM1TIM8

					  } TIM_OCInitTypeDef;




		(3.2)	使能/禁止预装载 
			TIM_OC1PreloadConfig
		void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
					TIM_OCPreload: 
							TIM_OCPreload_Enable 每次自动加载比较寄存器的值
							TIM_OCPreload_Disable	不自动加载比较寄存器的值
			是否使能 每次自动加载比较寄存器
		
		
	(4) 定时器使能
		(4.1) 使能自动加载 ARR,计数值
			void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
				TIMx:指定定时器,如:TIM1, TIM2,...
				NewState:
						ENABLE
						DISABLE
	
		(4.2) TIM_Cmd
	
			void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
				TIMx:指定定时器,如:TIM1, TIM2,...
				NewState:
						ENABLE
						DISABLE


  1. 例子

    假设一个定时器 TIM 的时钟频率为 f, (1/f)s 它就会振动一次
    设定的计数值为 N,按照递减的方式来计数

    			每隔 (1/f)s 就会 -1
    
    				N -> N-1  (1/f)s  
    				N-1 -> N-2 (1/f)s  
    				...
    
    			N -> 0 => (N+1) * (1/f) s
    
    		到0,产生一个中断,  
    			时间为: (N+1) * (1/f) s
    
    			=>就可以达到“定时”功能。
    
    		------  
    		假设:  
    			一个定时器的时钟频率为 8M HZ, 1s之后,  
    			我有一个非常重要的事情,请到时通知我,  
    			该如何设置这个定时器?
    
    			f = 8M Hz  
    			T = (1/f)s = (1/8M) s = (1/8) us
    
    			N =?  
    				N * (1/8M)s = 1s  
    				=> N = 8000000  
    					N = 8000000 - 1 = 799999
    
    	Time Base Unit 核心部件就是一个“计数器”,该“计数器”  
    	内部有三个寄存器:  
    		TIMx_ARR: Auto Reload Register 自动加载寄存器 <- N  
    		TIMx_CNT: Counter 计数器  
    		TIMx_PSC: PreSCaler 时钟预分频
    
    		“溢出事件/中断”  
    			 单向递增 0 -> N  => 产生溢出事件/中断  
    			 单向递减 N -> 0  => 产生溢出事件/中断
    
  2. Systick 系统定时器

    概述

    定时器是 STM32 中非常常用的一个外设,对于 STM32 来说,提供多种定时器供用户使用,比如高级定时器、通用定时器、基本定时器在使用时比较繁琐,所以内核就提供一款定时器,叫做系统定时器,也被称为 Systick 定时器(嘀嗒定时器)。

    Systick 定时器是属于内核中的一个外设,内嵌在 NVIC 中,在 Cortex M3/M4 内核中都存在,方便用户在使用不同类型的芯片的时候进行移植。

    image

    Systick 定时器是一个 24bit 的倒计时(向下计数)定时器,功能就是实现简单的延时。

    • 裸机模式:提供简单的延时 实现微秒、毫秒级别的延时 闹钟、秒表、洗衣机、微波炉。

    • 操作系统:提供给操作系统一个稳定的时基,因为操作系统的架构是并行的。

    系统定时器的时钟源

    Systick 定时器有两个时钟源可以选择,可以选择内部时钟(168MHZ),或者外部时钟(8 分频之后的系统时钟 168MHZ/8 = 21MHZ)。

    选择不同的时钟源会影响延时时间的长短

    如果选择 168MHZ 作为 Systick 定时器的时钟源:2^24-1 最大延时时间为 99.86ms

    1s (1000000us)振荡 168000000 次,振荡 1 次的周期是 1/168us,也就是振荡 168 次花费 1us

    如果选择 21MHZ 作为 Systick 定时器的时钟源:2^24-1 最大延时时间为 798.9ms

    1s (1000000us)振荡 21000000 次,振荡 1 次的周期是 1/21us,也就是振荡 21 次花费 1us

    image

    image

    内核中提供了一个函数接口可修改 Systick 的时钟源,函数 SysTick_CLKSourceConfig()

    /**
      * @brief  Configures the SysTick clock source.
      * @param  SysTick_CLKSource: specifies the SysTick clock source.
      *   This parameter can be one of the following values:
      *     @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.
      *     @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.
      * @retval None
      */
    void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
    
    函数原型
    void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
    
    函数参数
    参数一:SysTick_CLKSource	选择时钟源  一般选SysTick_CLKSource_HCLK_Div8   21MHZ
    

    Systick 定时器的应用

    内核提供两种方案来使用 systick 定时器,分别是 中断方式 + 非中断方式

    • 中断方式

      image

      1. 设置 Systick 定时器的中断周期

        image

      2. 编写延时函数

        image

    • 非中断方式

      直接操作 Systick 定时器的 4 个寄存器

      image

      image

      image

  • 待分类

    用户发帖时如果不填标签,则默认加上“待分类”。这样做是为了减少用户发帖的负担,同时也减少运营维护的工作量。具有帖子更新权限的用户可以帮助社区进行帖子整理,让大家可以更方便地找到所需内容。这里是关于这样设计的一些思考,欢迎讨论。

    2 引用 • -279 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...