目录
定时器的本质:有规律的计数
有规律:计数速度来自时钟频率
计数 :定时器中可控计数器
定时器形成时间的原理:(时间)
通过时钟源可知1s计数次数(计数频率)
通过要定时的秒数 * 每秒计数的次数 来决定定时器中的计数器所需要计数的次数
一、什么是系统滴答定时器
系统滴答定时器又称为内核级定时器
作用:延时、定时中断(给实时操作系统提供时钟节拍-->任务调度)
系统滴答定时器是CM4核心内部集成的一个简单定时器硬件模块,SysTick(系统滴答定时器)属于NVIC一部分
注意:
延时和定时中断的区别
延时:阻塞内核执行程序,等时间到了才能继续执行
必须要CPU原地等待的情况
定时中断:通过定时器所定的时间,时间未到内核执行主程序;时间到了,内核执行执行紧急事件
每隔多久要做什么事
二、如何配置系统滴答定时器
1、系统滴答定时器框图
系统滴答定时器是一个24bit递减定时器 24bit --->设置数数大小 0 - 16777215
时钟源的选择
系统内部时钟-HSI经过PLL锁相环得到系统时钟频率,再提供给内核(系统滴答定时器) 168MHZ
168MHZ == 168000000次/s == 168000次/ms == 168次/us
计数器的最大值;2^24 == 16777216
计时最大值: 16777216 / 168 == 99864us == 99ms
系统外部时钟---HSE经过PLL锁相环得到系统时钟频率,再经过8分频后提供给系统滴答定时器 21MHZ
21MHZ == 21000000次/s = 21000次/ms == 21次/us
计数器的最大值;2^24 == 16777216
计时最大值: 16777216 / 21 == 798915us == 798ms
注意:一般情况下我们使用HSE经过PLL锁相环输出的频率来作为系统时钟,它可以用来驱动APB,DMA,Cortex等系统
2、3个寄存器控制SysTick时钟
3、程序思路
延时:
在时钟树里可以看到,对于Cortex时钟,是选用经过八分频的外部时钟源 21Mhz
计1μs:我们向计数器里放入21
计1ms:我们向计数器里放入21000
1ms(毫秒)=1μs(微秒)
配置滴答时钟步骤如下:
//注意:系统滴答不需要打开时钟
//1、时钟源选择 控制及状态寄存器的2号位 外部时钟 21M
//2、写入重载值 通过延时的时间写入重载值
//3、清空当前值寄存器 随便写一个值到当前值寄存器
//4、使能计数器 控制及状态寄存器的0号位
//5、等待计数器计数完成 控制及状态寄存器的16号位
//6、关闭定时器 控制及状态寄存器的0号位
中断定时:
系统滴答定时器中断初始化配置函数(触发定时中断的时间)
{
/*系统滴答定时器配置*/
//时钟源选择
//重载值寄存器
//当前值清零
//中断使能
/*NVIC控制器配置*/
//优先级分组
//计算编码值
//设置具体中断源
//使能计数器
}
系统滴答定时中断服务函数
{
//清除中断标志位
//紧急事件
}
为什么系统滴答定时器不用使能NVIC响应通道?
因为系统滴答定时器属于内核的,而NVIC模块响应的是片上外设中断源的开关
三、具体使用系统滴答定时器
需求1:延时
/***************************************
*函数名 :SysTick_delay_ms
*函数功能 :系统滴答毫秒级延时函数
*函数参数 :u16 ms
*函数返回值 :无
*函数描述 :用21M时钟,21000/ms
最大延时毫秒数798ms
****************************************/
void SysTick_delay_ms(u16 ms)
{
//时钟源选择
SysTick->CTRL &= ~(1 << 2);
//写入重装载值
SysTick->LOAD = ms * 21000 - 1;
//清空当前值
SysTick->VAL = 0xff;
//使能计数器
SysTick->CTRL |= 1 << 0;
//等待计数完成
while(!(SysTick->CTRL & (1 << 16)));
//关闭计数器
SysTick->CTRL &= ~(1 << 0);
}
需求2:定时中断
每301ms 切换LED1 状态
每522ms 切换LED2转态
每655ms 切换LED3转态
每900ms 切换LED4转态
主程序按键控制蜂鸣器开关
分析:
每1ms进一次定时中断,在定时中断里面计数,
计数变量到301的时候,改变LED1的状态
计数变量到522的时候,改变LED2的转态
.......
系统滴答毫秒级定时中断函数:
/***************************************
*函数名 :SysTick_delay_ms
*函数功能 :系统滴答毫秒级定时中断函数
*函数参数 :u16 ms
*函数返回值 :无
*函数描述 :用21M时钟,21000/ms
最大延时毫秒数798ms
****************************************/
void SysTick_interrupt_ms_init(u16 ms)
{
/*系统滴答定时器控制器配置*/
//时钟源选择
SysTick->CTRL &= ~(1 << 2);
//写入重装载值
SysTick->LOAD = ms * 21000 - 1;
//清空当前值
SysTick->VAL = 0xff;
//中断使能
SysTick->CTRL |= 1 << 1;
/*NVIC控制器配置*/
u32 pri = NVIC_EncodePriority (5, 1,2);//计算优先级编码值,设置抢占和响应的级别值
NVIC_SetPriority(SysTick_IRQn, pri);//设置具体某个中断源的优先级
//使能计数器
SysTick->CTRL |= 1 << 0;
}
中断服务函数:
/*
函数名:SysTick_IRQHandler
函数功能:系统时钟中断服务函数
返回值:void
形参:void
函数说明:
*/
//用数组分别存放四个LED中断触发的次数
u16 Sys_cnt[5];
void SysTick_Handler(void)
{
//清除中断标志位
//SysTick->VAL = 0xff;
SysTick->CTRL &= ~(1 << 16);
//紧急事件
Sys_cnt[1]++;
Sys_cnt[2]++;
Sys_cnt[3]++;
Sys_cnt[4]++;
if(Sys_cnt[1] == 301)
{
Sys_cnt[1] = 0;
LED1_OVERTURN;
}
if(Sys_cnt[2] == 522)
{
Sys_cnt[2] = 0;
LED2_OVERTURN;
}
if(Sys_cnt[3] == 655)
{
Sys_cnt[3] = 0;
LED3_OVERTURN;
}
if(Sys_cnt[4] == 900)
{
Sys_cnt[4] = 0;
LED4_OVERTURN;
}
}
系统滴答定时器最主要的是给实时操作系统提供时钟节拍-->任务调度,所以这些基本配置可以使用库函数,方法如下
调用系统库函数实现系统滴答1毫秒级中断
原型:
uint32_t SysTick_Config(uint32_t ticks)
二次封装:
SysTick_timer_ms(u8 timer)
{
SysTick_Config(168000*timer)
}
注意:
使用的时钟源是168MHZ
此函数相当于系统滴答定时器初始化