我写这篇文章笔记也是当作学习来看的,带着以下几个问题去翻阅许多参考资料:
什么是系统定时器? 为什么会有系统定时器? 系统定时器又有什么用?
一、系统定时器
由arm公司提供的
1. 简介
SysTick 叫做系统滴答时钟、系统定时器,属于 Cortex-M4 内核中的一个外设(外围设备),也是属于NVIC的一部分,并且是24bit向下递减 的计数器。 最大计数:2^24-1 向下计数 往下-1到0 然后触发中断。
《STM32中文参考手册》P108
- RCC 向 Cortex 系统定时器 (
SysTick ) 馈送 8 分频的 AHB 时钟 (HCLK)——》21MHZ。SysTick 可使用此时钟。 - 作为时钟源,也可使用 HCLK 作为时钟源——》168MHZ,具体可在 SysTick 控制和状态寄存器中配置。
注意: 定时器的位数越多,定时时间更长。 通过计数值间接计算定时时间,不能像操作系统直接调用函数实现延时或定时功能。
计数值,就是要进行多少个计数。 系统定时器不能用库函数 arm公司 cotex-m外设 系统定时器和NVIC (嵌套中断管理)
方式一 使用寄存器
2.工作原理
假设计数值2^24 - 1,采取递减方式,根据f=1/T,得hz:次/秒,即1Hz。 计数到0 再减 1 就触发中断。
3.频率的概念
物质在1s内完成周期性变化的次数叫做频率,常用f表示。为了纪念德国物理学家赫兹的贡献,人们把频率的单位命名为赫兹,简称“赫”,符号为HZ。 周期与频率的关系:f=1/T 。(其中f为频率,T为周期)。 周期,物体作往复运动或物理量作周而复始的变化时,重复一次所经历的时间。物体或物理量(如交变电流、电压等)完成一次振动(或振荡)所经历的时间。hz:次/秒。
二、库函数SysTick定时器操作
参考文档:《Cortex M3与M4权威指南.pdf》第314页
系统定时器配置
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
该函数将SysTick 中断间隔设置为ticks ,使能计数器使用处理器时钟,然后设置SysTick 异常为最低优先级。 示例1: 若要在30MHz的时钟频率下产生lkHz的SysTick异常,则可以使用:
SysTick_Config( SystemCoreClock I 1000);
变量SystemCoreClock 应该存放正确的时钟频率数值,也就是30×10^6,另外只需要使用:
SysTick_Config(30000);
SysTick_Handler( void)的触发频率就变成了lkHz
若SysTick_Config函数的输入 参数不满足24位重加载数值寄存器(大于OxFFFFFF), SysTick_Config函数返回1,否则会返回0。
示例2:
SysTick_Config(SystemCoreClock/1000);
SysTick_Config(168000);
一毫秒产生一次中断。 系统定时器中断 一般用在操作系统的延时,实现精准延迟一般使用非中断模式 。
注:若发现定时不准确,检查PLL配置是否正确。
许多情况下,可能会使用参考时钟或者不想使能SysTick中断,那么就不要使用SysTick _Config函数。 此时需要直接操作SysTick寄存器 ,推荐使用下面的流程。
三、寄存器SysTick定时器操作
1.系统定时器的用途
没有操作系统:只用于延时 有操作系统(ucos2 ucos3 freertos…):为操作系统提供精准的定时 中断(1ms~50ms)
2.寄存器
1.控制和状态寄存器 当systic为计数为0 则成1,外部参考时钟源 21mhz bit[2]为0 内核时钟168MHZ bit[2] 为1,使能定时器中断为1,当计数产生为0产生中断,或者将bit[1]0使能位(0关闭,1打开)
2. 重装载寄存器 当计数值到0,会重装载。 3. 当前数值寄存器 数值会一直-1
以上寄存器总览
在设置控制和状态寄存器的第0位使能该计数器后,当前值寄存器在每个处理器时钟周期或参考时钟的上升沿都会减小。若计数减至0,它会从重加载寄存器中加载数值并继续运行。
3.官方示例
(1)SysTick定时器寄存器操作l流程
(1)将0写入SysTick->CTRL禁止SysTick定时器。 这个操作是可选的。 若重用了其他代码,则由于SysTick之前可能已经使能过了,因此本操作是推荐使用的。 (2)将新的重加载值写人SysTick-> LOAD,重加载值应该为周期数减1。 (3)将任何数值写人SysTick当前值寄存器SysTick-> VAL,该寄存器会被清零。 (4)写入SysTick控制和状态寄存器SysTick->CTRL启动SysTick定时器。 由于SysTick定时器向下计数到0,因此,若要设置Sy日Tick周期为1000,则应该将重加载值(SysTick>LOAD)设置为999。
(2)SysTick定时器寄存器主要操作代码 If you want to use the SysTick timer in polling mode, you can use the count flag in the SysTick Control and Status Register (SysTick->CTRL) to determine when the timer reaches zero. For example, you can create a timed delay by setting the SysTick timer to a certain value and waiting until it reaches zero: 如果你想在轮询模式下使用SysTick计时器,你可以使用SysTick控制和状态寄存器(SysTick->CTRL)中的计数标志来确定计时器何时达到零。例如,你可以通过设置SysTick定时器的某个值来创建定时延迟,并等待它达到零:
SysTick->CTRL = 0;
SysTick->LOAD = 0xFF;
SysTick->VAL = 0;
SysTick->CTRL = 5;
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;
若要将SysTick中断用作在一定时间后触发的单发操作,则可以将重加载值减小12个周 期,以补偿中断等待时间。 例如,要使SysTick定时器在300个时钟周期后执行:
volatile int SysTickFired,
SysTick > CTRL = 0;
SysTick->LOAD = (300-12);
SysTick->VAL = 0;
SysTickFired = 0;
SysTick->CTRL = 0x7;
while (SysTickFired == 0);
SysTick->CTRL = 0;
在上述代码中所需的处理任务花费的时间太长而导致挂起状态再次置位,则可能还需要清除SysTick的挂起 状态: 该函数时定时中断函数,可以到嵌入式开发中断全解(2)Hard Fault的诊断
void SysTick Handler( void)
{
SysTick - > CTRL = OxO;
...
SCB > ICSR j = 1<<25;
SysTickFired++;
return,
}
4.毫秒级延时函数
void delay_ms(uint32_t ms)
{
while(ms --)
{
SysTick->CTRL = 0;
SysTick->LOAD = 21000;
SysTick->VAL = 0;
SysTick->CTRL = 1;
while ((SysTick->CTRL & (1<<16))==0);
SysTick->CTRL = 0;
}
}
5.微秒和毫秒延时的优化[拓展]
官方代码没有想到意外关闭的问题
int32_t delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->CTRL = 0;
SysTick->LOAD = (nus*21)-1;
SysTick->VAL = 0;
SysTick->CTRL = 1;
while(1)
{
temp=SysTick->CTRL;
if(temp & 0x00010000)
break;
if((temp & 0x1)==0)
return -1;
}
SysTick->CTRL = 0;
return 0;
}
int32_t delay_ms(uint32_t nms)
{
uint32_t t = nms;
uint32_t temp;
while(t--)
{
SysTick->CTRL = 0;
SysTick->LOAD = 21000-1;
SysTick->VAL = 0;
SysTick->CTRL = 1;
while(1)
{
temp=SysTick->CTRL;
if(temp & 0x00010000)
break;
if((temp & 0x1)==0)
return -1;
}
}
SysTick->CTRL = 0;
return 0;
}
四、最大定时时间
1.确定最大的计数值2^24 -1,若计算到0,则进行2^24次计数。
1000ms Tmax -------- = -------- 168000000 2^24
Tmax = 2^24 *1000ms/168000000 = 99.86ms。
2.测试结果
初始化系统定时器,1S 内核触发 1000 次中断,说白了定时 1ms,能够成功 SysTick_Config(SystemCoreClock/1000);
初始化系统定时器,1S 内核触发 10 次中断,说白了定时 100ms,现象失败 SysTick_Config(SystemCoreClock/10);
初始化系统定时器,1S 内核触发 11 次中断,说白了定时 90.90ms,能够成功 SysTick_Config(SystemCoreClock/11);
总结:填写中断频率值不能小于11,否则定时时间不准确。 SysTick_Config(SystemCoreClock/11);
五、代码例程
链接:https://pan.baidu.com/s/1Ey2f6lUhC5bqHnbU6MWbkA 提取码:baba
总结
系统定时器是什么?
答:它提供一种简单的,24位写清零、递减、自装载同时具有可灵活控制机制的计数器。
为什么会有系统定时器?
答:在现代操作系统中, 需要一个周期性的中断来定期触发OS内核,如用于任务管理和上下文切换,处理器也可以在不同时间片内处理不同任务。处理器设计还需要确保运行在非特权等级的应用任务无法禁止该定时器,否则任务可能会禁止SysTick定时器并锁定整个系统。之所以在处理器内增加-个定时器,除了以上防止任务之间的时间冲突,还提高软件的可移植性。
系统定时器有什么用?
答:1、没有操作系统可以用作延时;2、计数器可用作实时系统(RTOS) 的滴答定时器或一个简单的计数器。
在网上多找一些例程,多找一些例程玩一下,后面自己在配置一下,写一下,不断的跳转到函数声明,看多了写多了,将报错解决了就会了。
|