使用ucos目的:实现多任务,因此必须保证多个任务都能得到CPU时间运行,所以程序中必须要有任务切换调度。
任务调度切换发生时机:
1.主动进行任务切换:
一般在任务中调用延时函数,比如OSTimeDly(ticks),此时会主动让出CPU,执行一次任务调度,选择最高优先级且为就绪状态的任务运行。
2.在滴答时钟中断中:
多任务的延时是以滴答时钟延时为基本单位,每次产生滴答时钟中断,会遍历每个任务,如果有任务存在延时,则会将相应的延时减1.
滴答时钟中断退出后,会执行OSIntExit(); 此时会发生任务切换,执行一次任务调度,选择最高优先级且为就绪状态的任务运行。
所以程序中设计应该关注以下几点:
1.合理分配滴答时钟中断时间,不能太长,也不能太短,一般选取10ms左右。即1秒内产生100次中断,有一百次任务切换机会。
2.合理分配任务优先级,每个任务应当在合适的时机主动让出CPU,因此每个任务要主动调用OSTimeDly(ticks)。
3.每个任务的堆栈大小安排要合理,任务中如果使用大量局部数组,可能会造成堆栈溢出,可以考虑加大堆栈容量。
4.堆栈一板设置如下:
OS_STK START_TASK_STK[START_STK_SIZE];
但是,如果使用了Microlib 库后,printf 等函数对浮点数的处理会出现非预期值, 这时候必须在使用的堆栈前8字节对齐__align(8)。
__align(8) OS_STK START_TASK_STK[START_STK_SIZE];
其中滴答时钟中断设置函数如下:
以STM32为例:系统时钟频率为72MHZ,实现5ms的滴答延时。
SystemCoreClock = 72000000 // 72MHZ
OS_TICKS_PER_SEC = 200 //1秒内200次中断,即每次5ms
void SysTick_Init(void)
{
u32 reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8,即 SysTick_CLK = 72/8 = 9MHZ
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8 这里fac_us = 9,即1us计数9次
reload=SystemCoreClock/8000000; //92000000/8000000=9 相当于 1us 计数9次
reload*=1000000/OS_TICKS_PER_SEC;//根据OS_TICKS_PER_SEC设定溢出时间 , 此时reload = 9*5000 次, 除以9 = 5000us=5ms, 即 定时器中断为5ms
//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右
fac_ms=1000/OS_TICKS_PER_SEC;//代表ucos可以延时的最少单位 这里fac_ms = 5
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/OS_TICKS_PER_SEC秒中断一次 ,即5ms中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
两个延时函数:
void delay_ms(u16 nms)
{
if(OSRunning==TRUE)//如果os已经在跑了
{
if(nms>=fac_ms)//延时的时间大于ucos的最少时间周期
{
OSTimeDly(nms/fac_ms);//ucos延时,里面会进行任务调度
}
nms%=fac_ms; //ucos已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时,此时ucos无法启动调度.
}
如果使用ms级延时必须要在5ms以上的延时才能做到任务切换,也就是延时必须要大于一个滴答中断周期。
void delay_us(u32 nus)
{
u32 ticks;
u32 told,tnow,tcnt=0;
u32 reload=SysTick->LOAD; //LOAD的值
ticks=nus*fac_us; //需要的节拍数
tcnt=0;
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;//时间超过/等于要延迟的时间,则退出.
}
};
}
中断函数:
void SysTick_Handler(void)
{
OSIntEnter(); //进入中断
OSTimeTick(); //调用ucos的时钟服务程序,遍历任务,将任务的等待延时减1
OSIntExit(); //触发任务切换软中断,会进行任务调度
}