• # STM32定时器


    文章目录


    泉水

    Stm32F407的介绍

    F4的定时器资源概述:F4开发指南,P207
    在这里插入图片描述
    在这里插入图片描述
    如图F407有12个16位定时器,2个32位定时器

    三种定时器区别

    在这里插入图片描述

    STM32通用定时器

    • STM32F1 的通用定时器是通过可编程预分频器(PSC)驱动的 16 位自动装载计数器(CNT)构成。
      STM32F1有TIM2、TIM3、TIM4 和 TIM5五个通用定时器
      STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
      使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
      STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。
      在这里插入图片描述

    CH即CLOCK Hours

    F407的通用定时器工作过程

    在这里插入图片描述
    通用定时器的四个时钟来源:F4开发指南,13.1,P208
    在这里插入图片描述

    TIx时钟源

    实际上是来源于输入捕获通道,之后的TI1FP1

    ITR1时钟源

    来自其它定时器的级联时钟源

    CK_INT时钟源

    CK_INT的时钟来源及大小:F4开发指南,13.1,P208
    在这里插入图片描述
    也就是说一般CK_INT的时钟是APB1的2倍。
    在这里插入图片描述

    TIMx_ETR时钟源

    TIM2_CH1_ETR表示两个功能选一个,分别是TIM2_CH1和TIM2_ETR,TIM2_CH1表示让这个引脚作为TIM2的第一通道对应引脚;TIM2_ETR表示让这个引脚作为TIM2外部时钟提供引脚,这种功能有两种模式,如下图
    在这里插入图片描述

    通用定时器的功能

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

    更新事件

    在这里插入图片描述

    通用定时器函数

    TIM_SetCounter

    /**
      * @brief  Sets the TIMx Counter Register value
      *设置定时器的计数值。可以用来复位值。
      * @param  TIMx: where x can be 1 to 14 to select the TIM peripheral.
      * @param  Counter: specifies the Counter register new value.
      * @retval None
      */
    void TIM_SetCounter(TIM_TypeDef* TIMx, uint32_t Counter)
    {
      /* Check the parameters */
       assert_param(IS_TIM_ALL_PERIPH(TIMx));
    
      /* Set the Counter Register value */
      TIMx->CNT = Counter;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    TIM_OC1PolariryConfig

    单独设置极性的函数。

    TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); 
    
    • 1

    TIM_SetCompare1

    设置定时器x的CCR1寄存器(捕获比较寄存器)的值,也就是比较点的值。
    每个定时器都有CCR寄存器 4 个,对应 4 个输通道 CH1~4。要想设置的话可以看TIM_SetCompare2等。

    /**
      * @brief  Sets the TIMx Capture Compare1 Register value
      * @param  TIMx: where x can be 1 to 14 except 6 and 7, to select the TIM peripheral.
      * @param  Compare1: specifies the Capture Compare1 register new value.
      * @retval None
      */
    void TIM_SetCompare1(TIM_TypeDef* TIMx, uint32_t Compare1)
    {
      /* Check the parameters */
      assert_param(IS_TIM_LIST1_PERIPH(TIMx));
    
      /* Set the Capture Compare1 Register value */
      TIMx->CCR1 = Compare1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    延时函数

    卡住计数

    u8 ucErrTime=0;
    while(READ_SDA)
    {
    	ucErrTime++;
    	if(ucErrTime>250)
    	{
    		IIC_Stop();
    		return 1;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    通用定时器的寄存器

    TIMx_CR1控制寄存器 1

    中文参考手册15.4.1,P424
    在这里插入图片描述
    在这里插入图片描述

    第 5,6位是设置计数对齐方式的。

    9-8 CLK时钟分频因子

    设置定时器的时钟分频因子为 1,2,4倍的定时器频率,在后面输入捕获时使用的比较多。

    7 ARPE自动重载预装载使能

    在这里插入图片描述

    • 二者的区别如下,ARPE=1,ARR立即生效。APRE=0,ARR下个比较周期生效。
    • PWM模式下必须使能预装载,自动重载预装载和输出比较预装载。才能及时改变占空比。
      在这里插入图片描述

    4 DIR计数方向

    在这里插入图片描述

    1 UDIS更新事件禁止位

    在这里插入图片描述

    0 CEN计数器使能位

    计数器使能位,该位必须置 1,才能让定时器开始计数。

    TIMx_DIER DMA/中断使能寄存器

    DMA interrupt enable register
    一个 16 位的寄存器
    在这里插入图片描述

    14 TED触发DMA请求使能

    在这里插入图片描述

    8 更新DMA请求使能

    在这里插入图片描述

    6 TIE触发信号中断使能

    114个IO口

    1 CC1IE捕获/比较1中断使能

    在这里插入图片描述

    0 UIE更新中断使能

    在这里插入图片描述

    TIMx_PSC预分频寄存器

    Prescaler
    预分频器值寄存器TIMx_PSC也存在影子寄存器,起作用的也正是影子寄存器(官方翻译为缓冲功能),所以在定时器启动后更改TIMx_PSC的值并不会立即影响当前定时器的时钟频率。要等到下一个更新事件(UEV)发生时才会生效。在这里插入图片描述

    一个16位正整数值,该寄存器用设置将定时器时钟源进行分频输出,然后提供给计数器,作为计数器的时钟。
    在这里插入图片描述
    预分频器的工作的工作原理是,定时器时钟源每tick一次,预分频器计数器值+1,直到达到预分频器的设定值,然后再tick一次后计数器归零,同时,CNT计数器值+1。

    由此可以看出,因为达到最大值后还要再tick一次才归零,所以定时器时钟频率应该为Fosc/(PSC+ 1)。其中Fosc是定时器的时钟源。比如想对时钟源进行72分频,那么预分频器的值就应该设置为71。

    TIMx_CNT计数寄存器

    Count
    该寄存器是定时器的计数器,该寄存器存储了当前定时器的计数值。

    常用命令

    TIM7->CNT = 0; 定时器计数清零。

    TIMx_ARR 自动重装载寄存器

    Automatic reload register
    该寄存器在物理上实际对应着 2 个寄存器。

    • 一个是程序员可以直接操作的,另外一个是程序员看不到的影子寄存器。事实上真正起作用的是影子寄存器。
    • 根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。
      在这里插入图片描述

    TIMx_SR状态寄存器

    State Register
    中文参考手册14.4.5,P372
    该寄存器用来标记当前与定时器相关的各种事件/中断是否发生。
    在这里插入图片描述### 12 捕获比较4 重复捕获标志
    在这里插入图片描述

    6 TIF触发中断标记

    在这里插入图片描述

    2 CC2iF 捕获比较2 中断标记

    在这里插入图片描述

    0 UIF更新中断标记

    在这里插入图片描述

    常用命令

    • TIM7->SR = 0xFE 等价于 TIM_ClearITPendingBit(TIM7,TIM_IT_Update)定时器中断清除函数(1111 1110)也就是说清除了更新中断。

    TIMx_CCMR1/2捕获/比较模式寄存器

    Capture compare mode register
    该寄存器总共有 2 个,TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 2,而 TIMx_CCMR2 控制 CH3 和 4。

    中文参考手册15.4.7,P432
    在这里插入图片描述

    上图把寄存器分了 2层,上面一层对应输出,而下面的则对应输入捕获模式。

    7-4 IC1F(输入)捕获1滤波器

    在这里插入图片描述

    6-4 OC1M(输出)模式设置位

    总共可以配置成 7 种模式,我们使用的是 PWM 模式,所以这 3 位必须设置为 110/111。这两种PWM 模式的区别就是输出电平的极性相反。

    • 所谓有效电平要靠CCER->CC1P来设置
      在这里插入图片描述

    3 OC1PE(输出)输出比较的预装载使能

    在这里插入图片描述

    • PWM模式下必须使能预装载,自动重载预装载和输出比较预装载。才能及时改变占空比。

    2 OC1FE(输出) 输出比较使能

    在这里插入图片描述

    3-2 ICPSC(输入) 预分频器

    在这里插入图片描述

    1-0 CC1S通道方向设置

    CCxS 用于设置通道的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。
    在这里插入图片描述

    TIMx_CCER捕获/比较使能寄存器

    Capture compare enable register
    该寄存器控制着各个输入输出通道的开关。
    在这里插入图片描述

    1 CC1P

    在这里插入图片描述

    0 CC1E

    该位是输入/捕获 1 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1。

    TIMx_CCR1~4捕获/比较寄存器

    Capture compare register
    该寄存器总共有 4 个,对应 4 个输通道 CH1~4。因为这 4 个寄存器都差不多,TIMx_CCR1 为例介绍
    在这里插入图片描述

    所有位,比较点装载

    在这里插入图片描述

    • 在输出模式下,该寄存器的值与 CNT 的值比较,根据比较结果产生相应动作。通过修改这个寄存器的值,就可以控制 PWM 的输出脉宽了。
      如果使用的是 TIM3的通道 2,需要修改 TIM3_CCR2 以实现脉宽控制 DS0 的亮度。

    • 在输入模式下,触发捕获的意义就在于将定时器的计数值加载到输入捕获比较寄存器。

    系统定时器SysTick

    • 系统定时器是属于 CM3 内核中的一个外设,内嵌在 NVIC 中。因为所有的 CM3 芯片都带有这个定时器,软件在不同 CM3 器件间的移植工作得以化简。

    • 系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 72M。

      • 该定时器的时钟源可以是内部时钟,或者是外部时钟。不过,STCLK 的具体来源则由芯片设计者决定
    • 当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。

      • SysTick 设定初值并使能后,每经过 1 个系统时钟周期,计数值就减 1。计数到 0 时,SysTick 计数器自动重装初值并继续计数,同时内部的 COUNT FLAG 标志会置位,触发中断 (如果中断使能情况下)。
    • 只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
      在这里插入图片描述在M4内核编程手册中这样说道:SysTick是一个24位系统定时器,它将重加载值递减到零。再在下一个时钟沿将该值重新装载到STK_LOAD寄存器中,然后根据随后的时钟进行递减。

    滴答定时器寄存器

    Systick 部分内容属于 NVIC 控制部分,一共有 4 个寄存器,名称和地址分别是:

    STK_CTRL       0xE000E010  --  控制寄存器	
    //貌似这个才是控制寄存器
    STK_LOAD     0xE000E014  --  重载寄存器	
    STK_VAL      0xE000E018  --  当前值寄存器	
    STK_CALRB    0xE000E01C  --  校准值寄存器
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    STK_CTRL 控制寄存器

    在这里插入图片描述
    寄存器内有 4 个位具有意义

    • 第 0 位:ENABLE,Systick 使能位(0:关闭 Systick 功能;1:开启 Systick 功能)
    • 第 1 位:TICKINT,Systick 中断使能位(0:关闭 Systick 中断;1:开启 Systick 中断)
    • 第 2 位:CLK SOURCE,Systick 时钟源选择(0:使用 HCLK/8 作为 Systick 时钟;1:使用 HCLK 作为 Systick 时钟)
    • 第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。
      在这里插入图片描述

    STK_LOAD 重载寄存器

    在这里插入图片描述

    Systick 是一个递减的定时器,当定时器递减至0 时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24 位的寄存器最大计数0xFFFFFF。

    STK_VAL 当前值寄存器

    在这里插入图片描述

    24 位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。

    STK_CALRB 校准值寄存器

    SysTick calibration value register
    在这里插入图片描述

    • 位31 NOREF:= 1 没有外部参考时钟(STCLK 不可用),= 0 外部参考时钟可用。
    • 位30 SKEW:= 1 校准值不是准确的1ms,= 0校准值是准确的1ms

    初始化 Systick 定时器

    在这里插入图片描述

    SysTick_Init

    void SysTick_Init(u8 SYSCLK);
    SysTick_Init(72);

    //SYSTICK的时钟固定为AHB时钟的1/8
    //SYSCLK:系统时钟频率
    void SysTick_Init(u8 SYSCLK)
    {
    	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
    	fac_us=SYSCLK/8;					
    	fac_ms=(u16)fac_us*1000;				   
    }								    
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    系统时钟是72/8M,计数一次时间1/9000000秒,换算成us就是1/9us,则计数72/8次也就是9次就是1us。

    SysTick_CLKSourceConfig

    设置SysTick的时钟源。可选项如下:

    #define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
    #define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)
    #define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
                                           ((SOURCE) == SysTick_CLKSource_HCLK_Div8))
    
    • 1
    • 2
    • 3
    • 4

    HCLK的介绍

    delay_us寄存器

    void delay_us(u32 nus)
    {	 
    	u32 temp;
    	SysTick->LOAD = 9*nus;
    	SysTick->VAL=0X00;//清空计数器
    	SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
    	do
    	{
    		temp=SysTick->CTRL;//读取当前倒计数值
    	}while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
    	//使能且没有计数到0就一直循环
    	SysTick->CTRL=0x00; //关闭计数器
    	SysTick->VAL =0X00; //清空计数器
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    9*nus :假设外设频率为 9M,也就是经过 8 分频,那么计数 9 次是 1us,乘以 9 的意义就是参数的时间对应的次数,也就是重装载值

    • 这里看一下判断条件,第一位是使能,16位是计数到0。
      第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。

    delay_us库函数

    //延时nus
    //注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
    void delay_us(u32 nus)
    {		
    	u32 temp;	    	 
    	SysTick->LOAD=nus*fac_us; 				//时间加载	  		 
    	SysTick->VAL=0x00;        				//清空计数器
    	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 	 
    	do
    	{
    		temp=SysTick->CTRL;
    	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
    	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
    	SysTick->VAL =0X00;       				//清空计数器 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    delay_xms短时

    
    //延时nms
    //SysTick->LOAD为24位寄存器,所以,最大延时为:
    //nms<=0xffffff*8*1000/SYSCLK
    //SYSCLK单位为Hz,nms单位为ms
    //对168M条件下,nms<=798ms 
    void delay_xms(u16 nms)
    {	 		  	  
    	u32 temp;		   
    	SysTick->LOAD=(u32)nms*fac_ms;			//时间加载(SysTick->LOAD为24bit)
    	SysTick->VAL =0x00;           			//清空计数器
    	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;          //开始倒数 
    	do
    	{
    		temp=SysTick->CTRL;
    	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达   
    	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;       //关闭计数器
    	SysTick->VAL =0X00;     		  		//清空计数器	  	    
    } 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    delay_ms长时

    
    //延时nms 
    //nms:0~65535
    void delay_ms(u16 nms)
    {	 	 
    	u8 repeat=nms/540;						//这里用540,是考虑到某些客户可能超频使用,
    											//比如超频到248M的时候,delay_xms最大只能延时541ms左右了
    	u16 remain=nms%540;
    	while(repeat)
    	{
    		delay_xms(540);
    		repeat--;
    	}
    	if(remain)delay_xms(remain);
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    常用函数

    SysTick_Config

    设置重装载值。该函数的内部就是个:SysTick->LOAD = 9*nus;
    SysTick_Config 的参数是一个时钟次数,意思就是我要多少个1/fosc 时间后中断一下。

    static void BSP_CoreClockInit(void)
    { 
    	SysTick_Config(SystemCoreClock / 100);      //10ms
    	//SysTick_Config(720000);    				//10ms
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    SystemCoreClock / 100 代表的是时钟次数,即放入重装载寄存器中的值,每计时一个数需要 1/72000000s 的时间,那么计SystemCoreClock / 100 (SystemCoreClock 为 72M)个数的时间就是 10ms,通过在中断中设置标志位达到 systick 定时中断的功能。
    函数实现

    __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
    {
      if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */
    
      SysTick->LOAD  = ticks - 1;                                  /* set reload register */
      NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */
      SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
      SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                       SysTick_CTRL_TICKINT_Msk   |
                       SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
      return (0);                                                  /* Function successful */
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在外设的中断优先级设置里,外设的硬件编号都是大于等于零的,内核外设的编号都小于零,相当于一个IP。

    • __NVIC_PRIO_BITS=4,因为F4使用了四位。
    • 中断处理函数IP:
    typedef enum IRQn
    {
    /******  Cortex-M4 Processor Exceptions Numbers ****************************************************************/
      NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                                          */
      MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M4 Memory Management Interrupt                           */
      BusFault_IRQn               = -11,    /*!< 5 Cortex-M4 Bus Fault Interrupt                                   */
      UsageFault_IRQn             = -10,    /*!< 6 Cortex-M4 Usage Fault Interrupt                                 */
      SVCall_IRQn                 = -5,     /*!< 11 Cortex-M4 SV Call Interrupt                                    */
      DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M4 Debug Monitor Interrupt                              */
      PendSV_IRQn                 = -2,     /*!< 14 Cortex-M4 Pend SV Interrupt                                    */
      SysTick_IRQn                = -1,     /*!< 15 Cortex-M4 System Tick Interrupt                                */
    /******  STM32 specific Interrupt Numbers **********************************************************************/
      WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                                         */
      PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt                         */
      TAMP_STAMP_IRQn             = 2,      /*!< Tamper and TimeStamp interrupts through the EXTI line             */
      RTC_WKUP_IRQn               = 3,      /*!< RTC Wakeup interrupt through the EXTI line                        */
      FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                                            */
      RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                              */
      EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                              */
      EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                              */
      EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                              */
      EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                              */
      EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                              */
      DMA1_Stream0_IRQn           = 11,     /*!< DMA1 Stream 0 global Interrupt                                    */
      DMA1_Stream1_IRQn           = 12,     /*!< DMA1 Stream 1 global Interrupt                                    */
      DMA1_Stream2_IRQn           = 13,     /*!< DMA1 Stream 2 global Interrupt                                    */
      DMA1_Stream3_IRQn           = 14,     /*!< DMA1 Stream 3 global Interrupt                                    */
      DMA1_Stream4_IRQn           = 15,     /*!< DMA1 Stream 4 global Interrupt                                    */
      DMA1_Stream5_IRQn           = 16,     /*!< DMA1 Stream 5 global Interrupt                                    */
      DMA1_Stream6_IRQn           = 17,     /*!< DMA1 Stream 6 global Interrupt                                    */
      ADC_IRQn                    = 18,     /*!< ADC1, ADC2 and ADC3 global Interrupts   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    内核外设和普通外设的中断优先级比较,虽然二者不是在同一个寄存器中,但是在比较时,也是把内核外设的优先级寄存器配置按照普通的解读。

    实现定时器中断

    介绍

    定时器相关的库函数主要集中在固件库文件 stm32f10x_tim.h 和 stm32f10x_tim.c 文件中

    TIM3 时钟使能

    TIM3 是挂载在 APB1 之下,所以我们通过 APB1 总线下的使能使能函数来使能 TIM3。调用函数:

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    
    • 1

    TIM_TimeBaseInit

    在库函数中,定时器的初始化参数是通过初始化函数 TIM_TimeBaseInit 实现的:
    在正式使用时,可以自己定义Time3_Init()这样的函数。关键的变量留出来,一般就是psc和arr就可以了。

    void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
    
    • 1

    第一个参数是确定是哪个定时器。
    第二个参数是定时器初始化参数结构体指针,结构体类型为 TIM_TimeBaseInitTypeDef
    结构体的定义:

    typedef struct
    {
    uint16_t TIM_Prescaler;
    uint16_t TIM_CounterMode;
    uint16_t TIM_Period;
    uint16_t TIM_ClockDivision;
    uint8_t TIM_RepetitionCounter;
    } TIM_TimeBaseInitTypeDef;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个结构体一共有 5 个成员变量,对于通用定时器只有前面四个参数有用,
    最后一个参数 TIM_RepetitionCounter 是高级定时器才有用的,这里不多解释。

    TIM_Prescaler

    设置分频系数的。prescaler 是用来分频来自APBx的时钟频率,然后提供给定时器,作为定时器的心跳。一个16位正整数值

    • 预分频寄存器(TIMx_PSC)
      在这里插入图片描述

    TIM_CounterMode计数器模式

    设置计数方式,可以设置为向上计数,向下计数方式还有中央对齐计数方式。

    • F407取值范围
    #define TIM_CounterMode_Up  //向上计数模式                ((uint16_t)0x0000)
    #define TIM_CounterMode_Down //向下计数模式              ((uint16_t)0x0010)
    #define TIM_CounterMode_CenterAligned1     ((uint16_t)0x0020)
    #define TIM_CounterMode_CenterAligned2     ((uint16_t)0x0040)
    #define TIM_CounterMode_CenterAligned3     ((uint16_t)0x0060)
    #define IS_TIM_COUNTER_MODE(MODE) (((MODE) == TIM_CounterMode_Up) ||  \
                                       ((MODE) == TIM_CounterMode_Down) || \
                                       ((MODE) == TIM_CounterMode_CenterAligned1) || \
                                       ((MODE) == TIM_CounterMode_CenterAligned2) || \
                                       ((MODE) == TIM_CounterMode_CenterAligned3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    向下计数模式时序图:
    向上计数模式

    • CK_CNT是时钟

    • CNT_EN使能

    • 计数到0溢出

    • 触发中断

    • 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。

    • 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

    TIM_Period

    第三个参数是设置自动重载计数周期值。

    TIM_ClockDivision

    设置时钟分频因子。ClockDivision是对于输入的分频,在输入捕获的时候要用到,决定数字滤波器采样频率的参数。

    • 控制寄存器 1(TIMx_CR1)
      在这里插入图片描述之后在使用输入捕获滤波器时这些参数会被用到,可以根据硬件情况配置滤波。
      在这里插入图片描述

    • F103取值范围:

    #define TIM_CKD_DIV1                       ((uint16_t)0x0000)
    #define TIM_CKD_DIV2                       ((uint16_t)0x0100)
    #define TIM_CKD_DIV4                       ((uint16_t)0x0200)
    #define IS_TIM_CKD_DIV(DIV) (((DIV) == TIM_CKD_DIV1) || \
                                 ((DIV) == TIM_CKD_DIV2) || \
                                 ((DIV) == TIM_CKD_DIV4))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    TIM3 初始化范例代码格式:

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 5000;
    TIM_TimeBaseStructure.TIM_Prescaler =7199;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    中断时间计算

    根据TIM3 的时钟和我们设计的 arr 和 psc 的值,就可以计算中断时间了。
    计算公式如下:
    Tout= ((arr+1)*(psc+1))/Tclk;
    其中:
    Tclk:TIM3 的时钟频率(单位为 Mhz)。
    Tout:TIM3 溢出时间(单位为 us)。

    Tclk/(psc+1)是频率,频率分之一是时钟,也就是秒/次,多少次?arr+1次,最终得到时间

    TIM_ITConfig

    设置 TIM3_DIER 寄存器的相应位使能更新中断。
    在库函数里面定时器中断使能是通过 TIM_ITConfig 函数来实现的:

    void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
    • 1

    例如我们要使能 TIM3 的更新中断,格式为:

    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );
    
    • 1

    TIMx

    选择定时器号

    • F103取值为 TIM1~TIM17
    • F407的取值如下
    #define IS_TIM_ALL_PERIPH(PERIPH) (((PERIPH) == TIM1) || \
                                       ((PERIPH) == TIM2) || \
                                       ((PERIPH) == TIM3) || \
                                       ((PERIPH) == TIM4) || \
                                       ((PERIPH) == TIM5) || \
                                       ((PERIPH) == TIM6) || \
                                       ((PERIPH) == TIM7) || \
                                       ((PERIPH) == TIM8) || \
                                       ((PERIPH) == TIM9) || \
                                       ((PERIPH) == TIM10) || \
                                       ((PERIPH) == TIM11) || \
                                       ((PERIPH) == TIM12) || \
                                       (((PERIPH) == TIM13) || \
                                       ((PERIPH) == TIM14)))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    TIM_IT

    使能的定时器中断的类型,定时器中断的类型有很多种,包括更新中断 TIM_IT_Update,触发中断 TIM_IT_Trigger,以及输入捕获中断等等。

    • F103
    #define TIM_IT_Update                      ((uint16_t)0x0001)
    #define TIM_IT_CC1                         ((uint16_t)0x0002)
    #define TIM_IT_CC2                         ((uint16_t)0x0004)
    #define TIM_IT_CC3                         ((uint16_t)0x0008)
    #define TIM_IT_CC4                         ((uint16_t)0x0010)
    #define TIM_IT_COM                         ((uint16_t)0x0020)
    #define TIM_IT_Trigger                     ((uint16_t)0x0040)
    #define TIM_IT_Break                       ((uint16_t)0x0080)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • F407的取值如下
    #define TIM_FLAG_Update                    ((uint16_t)0x0001)
    #define TIM_FLAG_CC1                       ((uint16_t)0x0002)
    #define TIM_FLAG_CC2                       ((uint16_t)0x0004)
    #define TIM_FLAG_CC3                       ((uint16_t)0x0008)
    #define TIM_FLAG_CC4                       ((uint16_t)0x0010)
    #define TIM_FLAG_COM                       ((uint16_t)0x0020)
    #define TIM_FLAG_Trigger                   ((uint16_t)0x0040)
    #define TIM_FLAG_Break                     ((uint16_t)0x0080)
    #define TIM_FLAG_CC1OF                     ((uint16_t)0x0200)
    #define TIM_FLAG_CC2OF                     ((uint16_t)0x0400)
    #define TIM_FLAG_CC3OF                     ((uint16_t)0x0800)
    #define TIM_FLAG_CC4OF                     ((uint16_t)0x1000)
    #define IS_TIM_GET_FLAG(FLAG) (((FLAG) == TIM_FLAG_Update) || \
                                   ((FLAG) == TIM_FLAG_CC1) || \
                                   ((FLAG) == TIM_FLAG_CC2) || \
                                   ((FLAG) == TIM_FLAG_CC3) || \
                                   ((FLAG) == TIM_FLAG_CC4) || \
                                   ((FLAG) == TIM_FLAG_COM) || \
                                   ((FLAG) == TIM_FLAG_Trigger) || \
                                   ((FLAG) == TIM_FLAG_Break) || \
                                   ((FLAG) == TIM_FLAG_CC1OF) || \
                                   ((FLAG) == TIM_FLAG_CC2OF) || \
                                   ((FLAG) == TIM_FLAG_CC3OF) || \
                                   ((FLAG) == TIM_FLAG_CC4OF))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    NewState

    第三个参数就很简单了,就是失能还是使能。

    TIM3 中断优先级设置

    在定时器中断使能之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,设置中断优先级。

    使能 TIM3

    开启定时器通过 TIM3_CR1 的 CEN 位来设置。
    在固件库里面使能定时器的函数是通过 TIM_Cmd 函数来实现的:

    void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState)
    
    • 1

    这个函数非常简单,比如我们要使能定时器 3,方法为:

    TIM_Cmd(TIM3, ENABLE); //使能 TIMx 外设
    等价于 TIMx->CR1 |= TIM_CR1_CEN;
    等价于 TIMx->CR1 |= 0x0001; 相加
    失能即 TIMx->CR1 &= (uint16_t)~TIM_CR1_CEN;
    等价于 TIMx->CR1 &= (uint16_t)FFFE; 相乘
    
    • 1
    • 2
    • 3
    • 4
    • 5

    编写中断服务函数

    • 中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。
    • 这里使用的是更新(溢出)中断,所以在状态寄存器 SR 的最低位。
    • 在处理完中断之后应该向 TIM3_SR 的最低位写 0,来清除该中断标志。

    在固件库函数里面,用来读取中断状态寄存器的值判断中断类型的函数是:

    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);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    比如,我们要判断定时器 3 是否发生更新(溢出)中断,方法为:

    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){} 
    等价于 TIM3->SR & TIM_FLAG_Update != RESET
    
    • 1
    • 2

    固件库中清除中断标志位的函数是,比如我们在TIM3 的溢出中断发生后,我们要清除中断标志位:

    TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
    
    • 1
    • 固件库还提供了两个函数用来判断定时器状态以及清除定时器状态标志位的函数
      • TIM_GetFlagStatus 和 TIM_ClearFlag,他们的作用和前面两个函数的作用类似。

    在 TIM_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而TIM_GetFlagStatus 直接用来判断状态标志位。

    代码

    主函数

    int main(void)
    { 
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    	delay_init(168);  //初始化延时函数
    	LED_Init();				//初始化LED端口
    
     	TIM3_Int_Init(5000-1,8400-1);	//定时器时钟84M,分频系数8400,所以84M/8400=10Khz的计数频率,计数5000次为500ms     
    	while(1)
    	{
    		LED0=!LED0;//DS0翻转
    		delay_ms(200);//延时200ms
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    中断处理函数

    void TIM3_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
    	{
    		LED1=!LED1;//DS1翻转
    	}
    	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    LED初始化函数

    void LED_Init(void)
    {    	 
      GPIO_InitTypeDef  GPIO_InitStructure;
    
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
    
      //GPIOF9,F10初始化设置
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
      GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
    	
    	GPIO_SetBits(GPIOF,GPIO_Pin_9 | GPIO_Pin_10);//GPIOF9,F10设置高,灯灭
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    Python数据分析与机器学习46-时间序列案例
    Making Anti-Palindromes
    ubuntu dhcp无法配置路由、setting找不到network问题解决办法
    Jetpack DataBinding使用--Jetpack系列
    百亿规模京东实时浏览记录系统的设计与实现
    SpringMvc(零)- Tomcat启动
    模拟LinkedList实现的链表(无哨兵)
    flask入门教程之请求与响应
    模拟滤波器和经典的低通滤波器
    centos7 Kafka安装
  • 原文地址:https://blog.csdn.net/qq_45578181/article/details/126557438