第五篇,STM32系统定时器和通用定时器编程

1.定时器概念

定时器本质上是一个计数器,从某个值开始计数,当计数值达到某个值时定时时间到。定时的时间由计数的范围(初始计数值)和计数的快慢(当前计数值)来决定。 计数的快慢,需要系统提供一个时钟过来参考。所以定时器一定会从时钟系统那里获取一路时钟。

1M =1000kHZ=1000000HZ, 一秒内数1000000次,这频率实在是太快了,所以一般不会拿着原的时钟频率作为参考。一般会把原始时钟分频,决定计数的快慢。当前计数值会储存起来,一个计数周期结束后,会把另外一个寄存器储存的初始计数值取出来,再进行下一轮的计数。

用定时器做延时或者心跳时钟,主要步骤包括启动,确定参考时钟频率,确定初始计数值,查询超时标志,如下图所示。参考频率决定周期的长端,从而决定了计数的快慢。

—————————————————————————————————

2.systick ------ 系统定时器

systick是一个简单定时器,属于ARM核,常用于精准延时,也可以用作系统心跳时钟。

(1)cortex-M4内核的系统定时器(参考权威指南9.5节)

systick是一个向下计数24位(存储计数值的长度,最大计数值0xffffff),具有自动重装载功能(在一个定时时间结束后,自动写入一个初始计数值,不需要开发人员在重新初始化赋值)的简单定时器。时钟源由外部提供(ARM核没有时钟),可以选择RCC馈送的8分频的AHB时钟(168MHz,分频后是21MH在)或者HCLK时钟(168MHz)。

————————————————————————————————

Systick寄存器组织结构如下,通常需要配置的寄存器为控制和状态寄存器(CTRL),重装载值寄存器(LOAD),当前值寄存器(VAL)这三个。

控制和状态寄存器(CTRL),地址 : 0xE0000E010

重装载值寄存器(LOAD),地址 : 0xE0000E014

当前值寄存器(VAL), 地址 : 0xE0000E018 

这个是ARM公司出的,直接表示地址,不用加偏移量。

虽然控制和状态寄存器(CTRL)是32位的,但是只使用了4位,第16位是一个状态位,当不产生中断只做延时的时候,我们要去读取这一位,看看是否为1,就知道计时时间到没到(读取一次,自动归0)

———————————————————————————————————————————

3.使用systick实现精准延时要做什么

第一步,选择参考时钟(21MHzAHB时钟或者HLCK时钟);

第二步,设置初始计数值来决定定时器周期(延时时间);

第三步,使能定时器;

第四步,查询超时的标志等待延时时间到。

———————————————————————————————————————————

4.Sydtick定时器代码实现

参考频率为21MHz已经确定,现在改周期只能改初始计数值。

计算初始值,放入初始值寄存器里面,21MHz,数多少次是一毫秒?

数一次,1/21us ——>1um=21, 1ms=21000

数21000是一毫秒

毫秒延时 1ms = 21000 n<798 ------ (2^24)/21000

void delay_init(void)

{      

  //SYSTICK控制和状态寄存器第二位置0,选择21MAHB时钟作为时钟源

     SysTick->CTRL &= ~(0x01<<2);   
    // (0x01<<2)有宏定义SysTick_CTRL_CLKSOURCE_Msk
    //SysTick->CTRL &= ~SysTick_CTRL_CLKSOURCE_Msk;

         //&&&&&&&&&&&&&&&&&&库函数写法&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

        // SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

}

//选择21MHz, 1um=21, 1ms=21000

//毫秒延时 1ms = 21000  n<798 ------ (2^24)/21000

  

void delay_ms(unsigned int nms)

{
         //计算初始值放入初始值寄存器,从初始值开始计算
         SysTick->LOAD = 21000*nms-1;
         SysTick->VAL = 0;//当前计数值为0    

         //启动systick开始计时
         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//使能     

         //等待计数值为0,控制寄存器的16位变1
         while(!(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk));
       
         //关闭systick
         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

}
——————————————————————————————————————————————————————————————————————————————————————5.通用定时器

(1)介绍

    定时器的基本功能是定时,通用定时器属于外设,通常和其他硬件配合使用,比如周期性采集传感器数据,周期性ADC转换,周期性上报数据,将通用定时器和GPIO结合产生更多的功能(PWM,脉冲检测.....)。

    stm32f407有14个外设定时器,TIM2~TIM5 TIM9~TIM14属于通用定时器,TIM1和TIM8属于高级定时器,TIM6和TIM7属于基本定时器。

以TIM2~TIM5为例,了解通用定时器的使用

三种计数模式:

向上计数(递增)     0 ~ 初始计数值

向下计数(递减)     初始计数值 ~ 0

双向计数(递增/递减)     初始计数值 ~ 0 ~ 初始计数值

通用定时器的原始时钟频率取决于所在总线和总线的分频系数

———————————————————————————————————————————

6.定时器(TIM2)的库函数编程实现

    在工程中加入定时器的库函数源码

=======================================================

(1)开启定时器2的时钟

从下图可知,总线时钟是APB1=42M

//使能TIME2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

(2)初始化定时器

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); 参数:     TIMx - 哪个定时器     
          TIM_TimeBaseInitStruct - 初始化结构     
          typedef struct {   
                  uint16_t TIM_Prescaler;       /*!< 预分频系数 0x0000 and 0xFFFF */                        
                  uint16_t TIM_CounterMode;     /*!< 计数模式 @ref TIM_Counter_Mode */                        
                  uint32_t TIM_Period;          /*!< 初始计数值 0x0000 and 0xFFFF.  */                        
                  uint16_t TIM_ClockDivision;     /*!< 时钟因子 @ref 
                                                               TIM_Clock_Division_CKD */   
                  uint8_t TIM_RepetitionCounter;  /*!< 高级定时器功能,忽略 */ 
           } TIM_TimeBaseInitTypeDef;    

(3)初始化NVIC

超时后触发中断

NVIC_Init(...);

(4)开启定时器中断使能

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); 
参数:    
 TIMx - 哪个定时器     
 TIM_IT - 哪个中断 TIM_IT_Update    
 NewState - 使能/禁止 ENABLE

(5)实现定时器中断处理函数

void TIM2_IRQHandler(void) 
{     
    if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET)
    {         //......                  //清除中断标志         
             TIM_ClearITPendingBit(TIM2, TIM_IT_Update);    
    }
}

(6)启动定时器

TIM_Cmd(TIM2,ENABLE);

==================================================

代码实现

timer.h

#ifndef _TIME_H_
#define _TIME_H_
void time2_init(void);

#endif

timer.c

#include <stm32f4xx.h>
#include <timer.h>

void timer2_init(void)
{   

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
   
    //1.开启timer2时钟
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIME2,ENABLE);
   
   //2.初始化TIME2 84M    
   //8400分频=10khz        
    TIM_TimeBaseInitStruct.TIM_Prescaler = 8400 - 1;   /*!< 预分频系数 0x0000 and 0xFFFF */                    
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Down;  /计数模式/                        
    TIM_TimeBaseInitStruct.TIM_Period = 1000;     /*!< 初始计数值 0x0000 and 0xFFFF.  */                        
    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIME_CKD_DIV1;     /*!< 时钟因子 /   
    //TIM_TimeBaseInitStruct.TIM_RepetitionCounter;  /*!< 高级定时器功能,忽略 */ 
    TIM_TimeBaseInit(TIME2,&TIM_TimeBaseInitStruct);     
    
    //3.初始化NVIC
    NVIC_InitStruct.NVIC_IRQChannal = TIME2_IRQn ;//TIME2通道
    NVIC_InitStruct.NVIC_IRQChannalPreemptionPriority = 0x2;//抢占优先级
    NVIC_InitStruct.NVIC_IRQChannalSPriority = 0x1;
    NVIC_InitStruct.NVIC_IRQChannalCmd = ENABLE; 
    NVIC_Init(&NVIC_InitStruct);  

    //4.开启定时器2的超时中断
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); 
   
    //5.启动定时器2
    TIM_Cmd(TIM2,ENABLE);
}


void TIM2_IRQHandler(void) 
{     
    if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET)
    {         
          D1 = ~D; 

         //......                  //清除中断标志               
         TIM_ClearITPendingBit(TIM2, TIM_IT_Update);    
    }
}
   


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肖爱Kun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值