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);
}
}