一、SysTick(体系滴答守时器)概述
操作体系需求一个滴答守时器周期性发生中止,以发生体系运转的节拍。在中止服务程序里,依据优先级调度的操作体系会依据进程优先级切换使命,依据时刻片轮转体系会依据时刻片切换使命。总归,滴答守时器是一个操作体系的“心跳”。
Cortex-M3在内核部分封装了一个滴答守时器–SysTick,在之前的ARM内核通常是不会把守时器做进内核,守时器都是SOC厂商自己制造的外设。明显,Cortex-M3封装了这么一个守时器,关于将操作体系移植到不同SOC厂商出产的Cortex-M3系类MCU上,带来了极大的便利。Cortex-M3内核一致了这样的一个体系滴答守时器,移植操作体系的时分可以运用内核的守时器,而疏忽掉不同厂商出产守时器带来的不合。
二、SysTick control and status register(STK_CTRL)
SysTick的操控是极端简略的,它的操控和状况都汇聚在同一个寄存器STK_CTRL上。
STK_CTRL的每一位的意义英文解说都是很明晰的,不用多说。需求额定评论的是COUNTFLAG标志位,这个标志位代表的意义是:当计数为0时,也即STK_VAL计数至0时,此标志方位1。
经过笔者一番探索,对此位有更多的观点。
COUNTFLAG:
1、此位与SysTick的中止无关,不是中止标志位,可以算作事情标志位(计数至0事情)。
2、此位可以用作软件查询
3、读写此寄存器都会硬件主动更新COUNTFLAG的值,当然此位的值软件也是可以改写的。也便是说,假使咱们轮训检查COUNTFLAG是否置1(也便是计数是否完毕)。当SysTick硬件上计数为0,COUNTFLAG因而硬件主动置1。在咱们软件读取STK_CTRL的时分,其实SysTick的STK_VAL的值现已不是0(咱们主动重装,并且或许计时几个CLK了)。这个时分咱们读取到了COUNTFLAG的标志位的1,一起也将COUNTFLAG主动清零。
三、滴答守时器应用之精准延时函数
1、函数完成思路
函数完成运用“轮询状况位COUNTFLAG”完成精准延时节拍10us。
在运用的时分,首要调用函数SysTick_Init装备SysTick的守时周期为10us。在延时函数中,当发动守时器后,就调用函数SysTick_GetFlagStatus轮询是否守时10us完毕,假如完毕就更新一下延时节拍变量nTime。
咱们SysTick守时器主动重装计数器初值,并且SysTick_GetFlagStatus在检测到SET的时分,COUNTFLAG也主动整理。所以软件不用装守时器初值,也不用手动铲除标志位COUNTFLAG。
2、函数完成代码
#include "bsp_sysTick.h"/*** @brief 读取SysTick的状况位COUNTFLAG* @param 无* @retval The new state of USART_FLAG (SET or RESET).*/static FlagStatus SysTick_GetFlagStatus(void) {if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) {return SET;}else{return RESET;}}/*** @brief 铲除SysTick的状况位COUNTFLAG* @param 无* @retval 无*/static void SysTick_ClearFlag(void){ SysTick->CTRL &= ~ SysTick_CTRL_COUNTFLAG_Msk;}/*** @brief 装备体系滴答守时器 SysTick* @param 无* @retval 1 = failed, 0 = successful*/uint32_t SysTick_Init(void){/* SystemFrequency / 1 1ms中止一次* SystemFrequency / 100 10us中止一次* SystemFrequency / 1 1us中止一次*//* 设置守时周期为10us */if (SysTick_Config(SystemCoreClock / 100)) { /* Capture error */ return (1);}/* 封闭滴答守时器且制止中止 */SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk SysTick_CTRL_TICKINT_Msk); return (0);}/*** @brief us延时程序,10us为一个单位* @param * @arg nTime: Delay_us( 1 ) 则完成的延时为 1 * 10us = 10us* @retval 无*/void Delay_us(__IO uint32_t nTime){ /* 清零计数器并使能滴答守时器 */ SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; for( ; nTime > 0 ; nTime--){/* 等候一个延时单位的完毕 */while(SysTick_GetFlagStatus() != SET);}/* 封闭滴答守时器 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;}
View Code
3、函数的长处和缺点
长处:
运用轮询法完成精准延时是牢靠的,咱们硬件主动重装守时器初值,只需咱们鄙人一次计数完毕(为0)之前,将节拍计数变量nTime更新,那么这个延时函数便是牢靠的。
运用轮询法的另一个优点是没有用到全局变量,完全是局部变量搞定了所需功用。假使运用中止延时,有必要运用全局变量给精准延时函数传递参数。
缺点:
咱们运用的是轮询法,有或许被其他的中止打断,假定其他的中止的服务时刻有很长,使得“鄙人一次计数完毕(为0)之前,没有将节拍计数变量nTime更新”,那么延时的时刻酿制增加。
4、留意
此延时函数的最小分辨率不能设置为1us,最好设置为>=10us,这是咱们轮训的周期和1us比较具有可比性,时刻误差太大。
四、滴答守时器应用之程序段计时
1、函数完成思路
首要对滴答守时器初始化,计时节拍数是计数器的最大值。在感兴趣的程序段开端处,发动守时器,在程序段的完毕处封闭守时器。假使这段时刻很长,超过了计数器的计数最大值,就会在中止函数中对溢出次数进行计数。终究的程序段时刻决定于计数器的数据寄存器SysTick->VAL中的剩余值和中止溢出次数。
别的为了使程序可以对不同的程序段或许不同情况下的程序段进行计时,运用了一个结构体界说保存计时数据的结构体类型。在对程序段进行计时的时分,经过一个运转指针指向所要保存的变量中。
2、函数代码
① User_SysTick.c
/*********************************************************************************计时最小单位:1/72M s*计时最大长度:2^32/72M = 59.65 s*运用方法:*(1) 界说一个保存计时数据的TimingVarTypeDef类型变量Time*(2) 初始化* SysTick_Time_Init(&Time);*(3) 在while循环中放置发动/中止函数* while(1){* SysTick_Time_Start();* 测验运转时刻的代码* SysTick_Time_Stop();* }*******************************************************************************//* 界说保存未运用DMA时测验程序段运转时刻的变量 */TimingVarTypeDef Time;/* 指针指向当时保存时刻的变量 */TimingVarTypeDef * CurrentTimingVar; /* 体系滴答守时器的中止次数 */uint32_t TimeupTimes;/*** @brief 装备体系滴答守时器 SysTick* @param 无* @retval 1 = failed, 0 = successful*/uint32_t SysTick_Init(void){/* 设置守时周期为最大守时数SysTick_LOAD_RELOAD_Msk */if (SysTick_Config(SysTick_LOAD_RELOAD_Msk)) { /* Capture error */ return (1);}/* 封闭滴答守时器且制止中止 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk; return (0);} /*** @brief 滴答守时器 SysTick 计时初始化* @param 初始化计时变量的成员--计时次数* @retval 无*/void SysTick_Time_Init(TimingVarTypeDef * TimingVar){/* 指针指向当时保存时刻的变量 */CurrentTimingVar = TimingVar;/* 计时次数初始化 */ CurrentTimingVar->SetSaveTimesNum = SaveTimesBufNum - 2;}/*** @brief 滴答守时器 SysTick 计时发动* @param 无* @retval 无*/void SysTick_Time_Start(void){/* 判别现已计时次数是否到达设置的计时次数 */if(CurrentTimingVar->SaveTimesTemp < CurrentTimingVar->SetSaveTimesNum){/* 滴答守时器的数据寄存器清零 */SysTick->VAL = 0;/* 滴答守时器中止次数清零 */TimeupTimes = 0;/* 发动滴答守时器 */SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; }}/*** @brief 滴答守时器 SysTick 计时中止并保存处理数据* @param 无* @retval 无*/void SysTick_Time_Stop(void){/* 保存现已计时次数 */ uint32_t TimesTemp = CurrentTimingVar->SaveTimesTemp;/* 保存设置计时总次数 */uint32_t SetSaveTimesNum = CurrentTimingVar->SetSaveTimesNum;uint32_t i,TimeWidthAverageTemp = 0; /* 保存设置计时总次数 */ if(SysTick->CTRL & SysTick_CTRL_ENABLE_Msk){/* 封闭滴答守时器 */SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;/* 核算计时总时刻 */CurrentTimingVar->TimeWidth[TimesTemp] = SysTick_LOAD_RELOAD_Msk * TimeupTimes \+ (SysTick_LOAD_RELOAD_Msk - SysTick->VAL + 1);/* 判别计时次数是否满 */if((++TimesTemp) == SetSaveTimesNum){/* 核算均匀值 */for(i = 0;i < SetSaveTimesNum; i++){TimeWidthAverageTemp += CurrentTimingVar->TimeWidth[i];}CurrentTimingVar->TimeWidthAvrage = TimeWidthAverageTemp/SetSaveTimesNum; }/* 现已计时次数变量加1 */CurrentTimingVar->SaveTimesTemp++; }}
View Code
② User_SysTick.h
#define SaveTimesBufNum 4 /* 计时存储区的巨细 */typedef struct {uint32_t SetSaveTimesNum; /* 设置计时总次数 */uint32_t SaveTimesTemp; /* 现已计时的次数 */uint32_t TimeWidth[SaveTimesBufNum]; /* 计时存储区 */uint32_t TimeWidthAvrage; /* 均匀计时长度 */} TimingVarTypeDef; /* 计时变量类型 */extern TimingVarTypeDef Time;extern uint32_t TimeupTimes;extern uint32_t SysTick_Init(void);extern void SysTick_Time_Init(TimingVarTypeDef * TimingVar);extern void SysTick_Time_Start(void);extern void SysTick_Time_Stop(void);
View Code
③ stm32f10x_it.c
/*** @brief This function handles SysTick Handler.* @param None* @retval None*/void SysTick_Handler(void){TimeupTimes++;}
View Code
参考资料:《STM32F10xxx Cortex-M3 programming manual.pdf》
《STM32库开发实战攻略》