OS能够支持多任务,能够以周期性地完成上下文的切换,以并行的架构处理任务,单一任务的崩溃并不会牵连到整个系统。上下文周期性切换需要一个定时器能够打断程序执行,SysTick定时器就可以提供必要的时钟节拍,为OS的任务调度提供一个有节奏的“心跳”。
SysTick定时器即系统滴答定时器,也称“心跳定时器”,它是一个24 位的倒计数定时器,计到0 时,将从重装载寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息,即使在睡眠模式下也能工作。
SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号:15),SysTick中断的优先级也可以设置。
它会根据节拍来工作,把整个时间段分成很多小小的时间片,而每个任务每次只能运行一个时间片的时间长度,超时就退出给别的任务运行,这样可以确保任何一个任务都不会霸占操作系统提供的各种定时功能,都与这个滴答定时器有关。
当启用时,定时器从重载值递减计数到零,在下一个时钟周期将重装载SYST\_RVR 中的值,然后在后续时钟周期递减。将零值写入 SYST\_RVR 会在下一次回调时禁用计数器。当计数器变为零时,COUNTFLAG 状态位设置为 1。读取 SYST\_CSR 将 COUNTFLAG 位清除为 0。写入 SYST\_CVR 将清除寄存器和 COUNTFLAG 状态位为 0,写入不会触发 SysTick 异常逻辑,读取该寄存器会在访问时返回其值。
SysTick定时器主要由4个寄存器组成:
在CMSIS中系统控制寄存器结构体:
typedef struct { __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */ __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */ __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */ __I uint32_t CALIB;/*!< Offset: 0x0C SysTick Calibration Register */ } SysTick_Type;
在Arm官方资料中4个寄存器的命名分别是SYST\_CSR、SYST\_RVR、SYST\_CVR和SYST\_CALIB,但是在CMSIS中进行了简化命名,更加清晰明了。
CSR寄存器用到的位有4个,bit0用于是否开启定时器,置1表示使能SysTick定时器;bit1用于控制是否产生中断,该位置为1为允许产生中断;bit2用于设置定时器的时钟源,设为1,定时器的时钟源为主时钟,反之设为0的话定时器的时钟源为主时钟的四分之一。
MM32F0130系列的SysTick的HCLK来源于AHB总线经过硬件4分频,FCLK直接来源于AHB时钟总线。
当 SysTick 定时器从1计到0时,它将把COUNTFLAG位置位;
而下述方法可以清零:
读取 SysTick 控制及状态寄存器(STCSR)
往 SysTick 当前值寄存器(STCVR)中写任何数据
开始
禁止SysTick
设置重装值寄存器
清除当前值寄存器
使能SysTick
完成
SysTick操作相关函数有:
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks); void SysTick_CLKSourceConfig(u32 systick_clk_source); void RCC_SystickDisable(void); void RCC_SystickEnable(u32 sys_tick_period);
下面的代码演示启用 SysTick 的基本程序:; 使能SysTick定时器,并且使能SysTick异常 LDR R0, =0xE000E010 ; 加载STCSR的地址 MOV R1, #0 STR R1, [R0] ; 先停止SysTick,以防意外产生异常请求 LDR R1, =0x3FF ; 让SysTick每1024周期计完一次。因为是从1023数到 ; 0,总共数了1024个周期,所以加载值为0x3FF STR R1, [R0,#4] ; 写入重装载的值 STR R1, [R0,#8] ; 往STCVR中写任意的数,以确保清除COUNTFLAG标志 MOV R1, #0x7 ; 选择FCLK作为时钟源,并使能SysTick及其异常请求 STR R1, [R0] ; 写入数值,开启定时器
在CMSIS库中有定义对应的配置函数:__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) { /* Reload value impossible */ if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); } /* set reload register */ SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set Priority for Systick Interrupt */ NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* Load the SysTick Counter Value */ SysTick->VAL = 0UL; /* Enable SysTick IRQ and SysTick Timer */ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Function successful */ return (0UL); }
SysTick可以通过轮询或者中断方式进行操作,使用轮询的程序可以读取SysTick控制和状态寄存器,检查COUNTFLAG,如果该位置位,则表明SysTick计数已减到0。中断方式延时参考程序:static __IO uint32_t TimingDelay; void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while(TimingDelay != 0); } void SysTick_Handler(void) { if (TimingDelay != 0x00) { TimingDelay--; } } int main(void) { //systick时钟为HCLK,中断时间间隔1ms if (SysTick_Config(SystemCoreClock / 1000)) { while (1); } while(1) { Delay(200);//200ms } }
轮询方式延时参考程序:void delay_init() { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div4); //选择外部时钟HCLK/4 //为系统时钟的1/4,实际上也就是在计算1usSysTick的VAL减的数目 fac_us=SystemCoreClock/4000000; //代表每个ms需要的systick时钟数,即每毫秒SysTick的VAL减的数目 fac_ms=(u16)fac_us*1000; } void delay_ms(u16 nms) { u32 temp; SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit) SysTick->VAL =0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达,看CTRL的第16位(COUNTFLAG)是否为1,看STRL的第0位(ENABLE)是否为1 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 } void delay_us(u32 nus) { u32 temp; SysTick->LOAD=nus*fac_us; //时间加载 SysTick->VAL=0x00; //清空计数器 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数 do { temp=SysTick->CTRL; }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器 SysTick->VAL =0X00; //清空计数器 }
SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如时间测量、定时或者闹铃等。
原文标题:技术分享 | Cortex-M0中断控制和系统控制(五)
文章出处:【微信公众号:安芯教育科技】欢迎添加关注!文章转载请注明出处。
全部0条评论
快来发表一下你的评论吧 !