uCOSii的任务延时和软件定时器
1、心跳节拍
操作系统的心跳节拍称为一个Tick。uCOSii中有一个专用的心跳节拍函数:OSTimeTick(),每调用一次,系统时间计数器OSTime计数器就会加1次。为了能调用这个心跳节拍函数,我们使用CPU的滴答时钟(SysTick)。
2、STM32在OS下的滴答时钟配置
#include "delay.h"
#define SYSTEM_SUPPORT_OS 1 //1表示支持OS,0表示不支持OS
#if SYSTEM_SUPPORT_OS
#include "includes.h" //includes.h包含了ucos使用到的所有头文件
#endif
static u8 fac_us=0;//us延时倍乘数
static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数
#if SYSTEM_SUPPORT_OS
//如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了
//当delay_us()和delay_ms()需要支持OS的时候,需要定义3个宏和3个函数。
//首先3个宏定义:
//RTOS_Running:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//RTOS_TicksPerSecond:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始化滴答时钟(SysTick)
//RTOS_InterruptNesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
//RTOS_SchedLock()用于锁定OS任务调度,禁止调度
//RTOS_SchedUnlock()用于解锁OS任务调度,重新开启调度
//RTOS_TimeDelay()用于OS延时,可以引起任务调度.
//支持UCOSII
#ifdef OS_CRITICAL_METHOD
//OS_CRITICAL_METHOD定义了,说明要支持UCOSII
#define RTOS_Running OSRunning //0表示OS不运行;1表示OS在运行
#define RTOS_TicksPerSecond OS_TICKS_PER_SEC //OS时钟节拍,即每秒调度次数
#define RTOS_InterruptNesting OSIntNesting //中断嵌套级别,即中断嵌套次数
#endif
//支持UCOSIII
#ifdef CPU_CFG_CRITICAL_METHOD
//CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII
#define RTOS_Running OSRunning //0表示OS不运行;1表示OS在运行
#define RTOS_TicksPerSecond OSCfg_TickRate_Hz //OS时钟节拍,即每秒调度次数
#define RTOS_InterruptNesting OSIntNestingCtr //中断嵌套级别,即中断嵌套次数
#endif
//函数功能:us级延时时,关闭任务调度(防止打断us级延迟)
void RTOS_SchedLock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //当前操作系统使用的是UCOSIII
OS_ERR err;
OSSchedLock(&err); //UCOSIII的方式,禁止调度,防止打断us延时
#else //当前操作系统使用的是UCOSII
OSSchedLock(); //UCOSII的方式,禁止调度,防止打断us延时
#endif
}
//函数功能:us级延时时,恢复任务调度
void RTOS_SchedUnlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD //当前操作系统使用的是UCOSIII
OS_ERR err;
OSSchedUnlock(&err); //UCOSIII的方式,恢复调度
#else //当前操作系统使用的是UCOSII
OSSchedUnlock(); //UCOSII的方式,恢复调度
#endif
}
//函数功能:调用OS自带的延时函数延时
//ticks:延时的节拍数
void RTOS_TimeDelay(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD //当前操作系统使用的是UCOSIII
OS_ERR err;
OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err); //UCOSIII延时ticks个节拍
#else //当前操作系统使用的是UCOSII
OSTimeDly(ticks); //UCOSII延时ticks个节拍
#endif
}
//函数功能:CPU的滴答时钟(SysTick)中断服务函数
void SysTick_Handler(void)
{
if(RTOS_Running==1)//OS开始跑了,才执行正常的调度处理
{
OSIntEnter();
//进入中断时,用OSIntNesting来统计中断嵌套次数
//告知uCOSii系统,当前中断服务程序正在执行;
OSTimeTick();//调用ucos的心跳节拍函数
OSIntExit();
//退出中断时,用OSIntNesting来统计中断嵌套次数
//告知uCOSii系统,当前的中断已经处理完成,触发任务切换软中断
}
}
#endif
//函数功能:CPU的滴答时钟(SysTick)初始化
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init(void)
{
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
u32 reload;
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//选择外部时钟HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
reload=SystemCoreClock/8000000; //每秒钟的计数次数 单位为K
reload*=1000000/RTOS_TicksPerSecond;
//根据RTOS_TicksPerSecond设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右
fac_ms=1000/RTOS_TicksPerSecond; //代表OS可以延时的最小单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/RTOS_TicksPerSecond秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启CPU的滴答时钟(SysTick)
#else
fac_ms=(u16)fac_us*1000;//不支持OS,代表每个ms需要的systick时钟数
#endif
}
#if SYSTEM_SUPPORT_OS //如果需要支持OS.
//函数功能:延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
tcnt=0;
RTOS_SchedLock();//阻止OS调度,防止打断us延时
told=SysTick->VAL; //刚进入时的计数器值
while(1)
{
tnow=SysTick->VAL;
if(tnow!=told)
{
if(tnow<told)tcnt+=told-tnow;
//这里注意一下SYSTICK是一个递减的计数器就可以了.
else tcnt+=reload-tnow+told;
told=tnow;
if(tcnt>=ticks)break;//时间超过/等于要延迟的时间,则退出.
}
};
RTOS_SchedUnlock();//恢复OS调度
}
//函数功能:延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{
if(RTOS_Running&&RTOS_InterruptNesting==0)
{//如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)
if(nms>=fac_ms) //延时的时间大于OS的最小时间周期
{
RTOS_TimeDelay(nms/fac_ms); //OS延时
}
nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000));//普通方式延时
}
#else //不用OS时
//函数功能:延时nus
//nus为要延时的us数.
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; //清空计数器
}
//函数功能:延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
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))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
#endif
3、uCOSii任务延时
在使用OSTimeDlyHMSM()和OSTimeDly()之前,先要在os_cfg.h文件中设置好OS_TICKS_PER_SEC,如下:
#define OS_TICKS_PER_SEC 1000u /*设置OSTimeDlyHMSM()函数中每秒的节拍数*/
const char TEST1_TASK_rn_REG[]="\r\n";
const char TEST1_TASK_REG[]="TEST1_TASK";
//TEST1_TASK任务
void TEST1_TASK(void *pdata)
{
u32 time;
(void)pdata;
while(1)
{
printf("%s",TEST1_TASK_rn_REG);
printf("%s",TEST1_TASK_REG);
// OSTimeDlyHMSM(0,0,0,500);//当前任务延时0.5秒
OSTimeDly(500);
//当前任务延时500个节拍
//当OS_TICKS_PER_SEC=1000时,这里就是0.5秒
OSTimeDlyResume(TEST2_TASK_PRIORITY);
//唤醒一个“因OSTimeDly()或OSTimeDlyHMSM()而挂起的任务”
time=OSTimeGet();//读取OSTime的值
printf("%s",TEST1_TASK_rn_REG);
printf("time=%u",time);
OSTimeSet(0);//设置OSTime=0
}
}
const char TEST2_TASK_rn_REG[]="\r\n";
const char TEST2_TASK_REG[]="TEST2_TASK";
//TEST2_TASK任务
void TEST2_TASK(void *pdata)
{
(void)pdata;
while(1)
{
printf("%s",TEST2_TASK_rn_REG);
printf("%s",TEST2_TASK_REG);
OSTimeDlyHMSM(0,0,5,0);//当前任务延时5秒,合计5000个节拍
}
}
测试结果:
4、uCOSii软件定时器
在使用“软件定时器”之前,先要在os_cfg.h文件中设置好OS_TMR_CFG_TICKS_PER_SEC,如下:
#define OS_TMR_CFG_TICKS_PER_SEC 1000u /*设置软件定时器的时钟源频率为0.001Hz*/
#include "TEST1_TASK.h"
#include "stdio.h"
//getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "My_Task_Priority.h"
OS_TMR *MySoftTimer1;//软件定时器1
void TEST1_TASK(void *pdata);
const char TEST1_TASK_rn_REG[]="\r\n";
const char MySoftTimer1_TASK_REG[]="MySoftTimer1_TASK";
//函数功能:MySoftTimer1软件定时器的回调函数
void MySoftTimer1_Callback(OS_TMR *ptmr,void *p_arg)
{
printf("%s",TEST1_TASK_rn_REG);
printf("%s",MySoftTimer1_TASK_REG);
}
//TEST1_TASK任务
void TEST1_TASK(void *pdata)
{
u8 err;
(void)pdata;
//#define OS_TMR_CFG_TICKS_PER_SEC 1000u
MySoftTimer1=OSTmrCreate( 0,\
1000,\
OS_TMR_OPT_PERIODIC,\
(OS_TMR_CALLBACK)MySoftTimer1_Callback,\
0,\
(u8*)"tmr1",\
&err);
//1000ms执行一次,自动重
OSTmrStart(MySoftTimer1,&err);//启动软件定时器MySoftTimer1
while(1)
{
OSTimeDlyHMSM(0,0,0,10);//当前任务延时0.01秒,合计10个节拍
}
}