SysTick——操作系统的心跳:
SysTick简单的来说,就相当于操作系统的心跳, 只要使用了时基的系统都必须要有一个硬件定时器来产生所需要的滴答中断, 而systick就相当于这个硬件定时器。 在ucos中,作为操作系统的心跳,他是整个操作系统的核心部件, 操作系统依据systick,可以为多个任务分配不同的时间片段, 从而不让任务霸占系统。 因此,用户最好不要随意访问修改systick的寄存器。
Systick工作分析:
Systick他是一个24位寄存器, 即一次最多可以计数2的24次方个时钟脉冲, 计数脉冲保保存在当前计数值寄存器STK_VAL中。如图所示:
只能向下计数,每接收到一个时钟脉冲STK_Val值就向下减一,直到0. 由硬件自动把重载寄存器STK_LOAD寄存器产生中断,出发异常, 就可以在中断服务程序中处理定时事件了。
接下来介绍下,STK_CTRL寄存器中的关键位, COUNTFLAG, CLKSOURCE, TICKINT, ENABLE。
- ENABLE 位于第一位, 该位为使能定时器, 为0关闭定时器。
- TICKINT 为1时,计数到0会引发异常中断, 为0 便不会。
- CLKSOURCE 时钟选择位,1 使用AHB时钟, 为0 则使用AHB的8分频, AHB时钟为72MHz;
- COUNTFLAG :若计数到0, 则此位为1.
延时分析:
使用SysTick可以进行精确的延时, 理论上他的精度为1/72000000秒, 72分之一微妙。足以满足大部分需求。
代码如下:
#include "stm32f10x.h"
#include "SysTick.h"
#include "led.h"
uint32_t ticktime;
/*
* 函数名:main
* 描述 :主函数
* 输入 :无
* 输出 :无
*/
int main(void)
{
/* LED 端口初始化 */
LED_GPIO_Config();
/* 配置SysTick 为10us中断一次 */
SysTick_Init();
for(;;)
{
LED1( 0 );
Delay_us(50000); // 50000 * 10us = 500ms
LED1( 1 );
LED2( 0 );
Delay_us(50000);// 50000 * 10us = 500ms
LED2( 1 );
LED3( 0 );
Delay_us(50000);// 50000 * 10us = 500ms
LED3( 1 );
}
}
#include "SysTick.h"
static __IO u32 TimingDelay;
void SysTick_Init(void)
{
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
//if (SysTick_Config(SystemFrequency / 100000))// ST3.0.0库版本
if (SysTick_Config(SystemCoreClock / 100000))// ST3.5.0库版本
{
/* Capture error */
while (1);
}
// 关闭滴答定时器
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
void Delay_us(__IO u32 nTime)
{
TimingDelay = nTime;
// 使能滴答定时器
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
while(TimingDelay != 0);
}
*/
void TimingDelay_Decrement(void)
{
if (TimingDelay != 0x00)
{
TimingDelay--;
}
}
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
extern void TimingDelay_Decrement(void);
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}
代码分析:
从main函数开始说起:SysTick_init Delay_us 这两个函数配置好Systick定时器并且使之精确延时。进入SysTick_init函数中, 其中 SysTick_Config就是配置延时时间长短的函数,如果成功就返回1.
当计数到0进入中断函数,Systick_handler函数中时, 就调用一次imingDelay_Decrement,这样累计总共调用他的次数,就可以算出最终延迟的时间。
这段代码的实验现象就是可以看到实验板上的led灯以500ms的频率闪烁。