一、系统滴答定时器概述
传统定时器:如手机闹钟,闹钟等就是一个简单地计数器。
定时器概念:由时钟源+计数器+计数值组成的计数单元。
系统嘀嗒定时器首先是存在于内核里,系统嘀嗒时钟假如用的是同一个内核那么里面相关的配置,可能不同的就是主频。
定时器概述
平时数数的时候,每次数的时间不一致
定时器:可以帮助我们进行有规律的计数
可以知道每数一次的时间都是固定的
定时器的本质 = 数一次的时间 * 数多少次
系统滴答定时器的概述
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号: 15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。
例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问,以维持操作系统“心跳”的节律。
后面用到操作系统的时候就可以使用系统嘀嗒作为时基单元(5ms)
SysTick(系统滴答)器原理:SysTick本质就是一个定时器。每来一个时钟脉冲计数一次,从规定数值递减到零时,表示定时时间到。
SysTick作用:
1为搭载操作系统的芯片提供心跳节拍:由于芯片搭载操作系统便于维护程序,很多产品都会搭载操作系统,操作系统需要一个心跳节拍。
2如果是裸机使用系统滴答定时器时,可以将系统滴答定时器当做普通定时器使用。
二、系统滴答定时器框架
因为SysTick是属于内核的一部分,其被捆绑在NVIC中,用于产生SYSTICK异常。
滴答定时器介绍:
SysTick 定时器是一个简单的递减 24 位定时器,可以在处理器时钟频率或参考时钟频率上运行
(1)递减:定时器的计数器是向下递减的。1000-》0
(2) 定时器是24位:计数器的计数范围。
(3) 定时器时钟来源:处理器时钟频率(168MHZ)或参考时钟频率(168/8 =21MHZ)
有上图得知滴答定时器是作为内核中NVIC的一部分的一部分,
定时时间计算问题:
- 24位递减计数器最大值224 = 16777216 = 798,915us
- 选择21M参考时钟AHB经过8分频得到 最大计数时间ms = 16777216/21*000(1毫秒计数个数) = 799ms
三、系统滴答定时器相关寄存器
SysTick的控制与状态寄存器
SysTick重载值寄存器
SysTick当前值寄存器
SysTick校准值寄存器
该寄存器用于校准滴答定时器,所以必须要有一个参考时钟,写入TENMS中。
注意点:如果使用参考时钟,必须写入一校准值(厂家在芯片出场的时候已经写入);
作为查询方式的配置步骤:(作为延时函数)
属于内核的没有时钟使能
- 选择时钟源
- 清空递减计数值
- 写入重载值
- 开启递减计数器
- 等待标志位
- 关闭递减计数器
作为中断(搭载操作系统)的配置流程
Void SysTick_IRQ_Init(void)
{
①时钟源选择
②配置自动重装载寄存器
③清除当前计数器的值
④使能中断(模块级中断打开就行,核心机中断不用,SYStickz在内核里面不用使能,NVIC必须响应)
⑤设置中断优先级
⑥打开定时器
}
u32 fac_us = 0; u32 fac_ms = 0; //#ifdef SYSINter /************************************ 函数功能:系统嘀嗒定时器初始化 函数形参:u32 nms 函数返回值:void 函数说明: 选择21M的时钟源 总的计数时间=记一次数的时间*LOAD的值 1/21*21*1000*nms 作者: 日期: ************************************/ void Systick_Interrupt_Init(u32 nms,char nus) { //1. 先选择时钟源(一般选择STCLK -- 21M)-- 选择好了记一次数的时间,1/21M s SysTick->CTRL &= ~(0x1 << 2); //2. 往重装载寄存器写值(记多少次) SysTick->LOAD = 21 * pow(1000,nus) * nms; //3. 对VAL寄存器执行写操作(就可以把重装载值加载到计数器里) SysTick->VAL = 0; //4. 使能对应的中断标志 SysTick->CTRL |= 0x1 << 1; //5. 配置中断优先级 NVIC_SetPriority(SysTick_IRQn,NVIC_EncodePriority(7-2,2,2)); //6. 使能计数器 SysTick->CTRL |= 0x1 << 0; } //7. 编写中断服务函数 void SysTick_Handler(void) { //清除标志位 if((SysTick->CTRL & 0x1 << 16)) { if(fac_us>0) fac_us--; if(fac_ms>0) fac_ms--; // printf("123456\r\n"); } } //#else /************************************ 函数功能:延时ms 函数形参:u32 nms 函数返回值:void 函数说明: 选择21M的时钟源 总的计数时间=记一次数的时间*LOAD的值 1/21*21*1000*nms 作者: 日期: ************************************/ void Delay_Ms(u32 nms,u32 nus) { //1. 先选择时钟源(一般选择STCLK -- 21M)-- 选择好了记一次数的时间,1/21M s SysTick->CTRL &= ~(0x1 << 2); //2. 往重装载寄存器写值(记多少次) SysTick->LOAD = 21 * 1000 * nms; //3. 对VAL寄存器执行写操作(就可以把重装载值加载到计数器里) SysTick->VAL = 0; //4. 使能计数器 SysTick->CTRL |= 0x1 << 0; //5. 等待计数时间到达 while(!(SysTick->CTRL & (0x1 << 16))) { } //5. 关闭计数器 SysTick->CTRL &= ~(0x1 << 16); } /************************************ 函数功能:延时ms 函数形参:u32 nms 函数返回值:void 函数说明: 选择21M的时钟源 总的计数时间=记一次数的时间*LOAD的值 1/21*21*1000*nms 作者: 日期: ************************************/ void DElay_US(u32 nms, u32 nus) { if(nus<=500) { fac_us=1; Systick_Interrupt_Init(nms,nus); } else { fac_us = nus; Systick_Interrupt_Init(1,nus); } SysTick->CTRL &= ~(0x1 << 16); } /************************************ 函数功能:延时ms 函数形参:u32 nms 函数返回值:void 函数说明: 选择21M的时钟源 总的计数时间=记一次数的时间*LOAD的值 1/21*21*1000*nms 作者: 日期: ************************************/ void DElay_MS(u32 nms, u32 nus) { if(nms<=500) { u32 fac_ms = 0; Systick_Interrupt_Init(nms,1); } else { fac_ms = nms; Systick_Interrupt_Init(nms,1); } while(fac_ms !=0); SysTick->CTRL &= ~(0x1 << 16); }