RTOS51

来源:百度文库 编辑:神马文学网 时间:2024/04/30 01:43:29
信号量
Rtos51的信号量是一个数组,第一个字节存放信号的计数,第二个字节存放等待此信号的任务标志(如果大于9个使用2个字节,1是等待,2是不等待)。
函数
OSSemPend(uint8 index, uint8 tick)
是等待一个信号量,先把此任务对应的等待信号标志置成1(这一点有点不理解,完全可以直接判断当前信号量是否大于1,如果是的话直接得到信号不就行了),然后保存此信号量的索引到堆栈(这一点没有切身的体会,所以不特别的肯定,老觉得蹦来蹦去的有点乱,需要深入了解,暂时搁置),清除此任务的就绪标志(OSClearSignal(uing8 taskID)),切换任务,返回时把sp出栈,检查等待时间是否到,不到的话一直执行上面的操作,当任务返回时,清除等待标志(必需的,因为只是在发送信号量时清除此标志,如果是因为超时或原来就有信号量的话,等待标志就不会被清除,就会出错)检查信号量的大小,如果大于1说明是收到信号执行的,否则是因为超时执行的。
因为很多地方都调用了OSScheld()函数,所以他一定是可重入函数。
函数OSSemIntPost(uint8 index)
给制定的信号量发信号,首先把信号计数加1,然后按照优先级从高到底(bit 0 to bit 7)依次检查此信号量的等待列表,当检查到有任务等待时,清除此等待标志,并向此任务发送就绪标志(OSSendSignal(uint8 taskID))。
函数uint8 OSSemPost(uint8 index)
此函数调用OSSemIntPost()然后切换任务。
消息队列
发现一个问题,可能昨天试验信号量的时候就有只是没有发现,就是在建立一个消息队列时要放到创建任务的前面,否则很可能还没有创建消息队列,在任务中就已经开始使用了,造成程序执行不正常。同样对于信号量。
OSQPend(uint8 *ret, uint8 OS_Q_MEM_SEL *buf, uint8 tick)
处理过程和信号量的相似,只是如果有消息的话会把消息内容放到ret指向的空间返回。
当tick是0的话他就是无限制的等待一个消息(信号),因为在void OSTimeTick(void)中首先判断等待时间是否是0,在不是的情况下减一,然后判断是0的话使任务就绪,如果原来就是0的话,就不能通过超时进入就绪态,故只能是靠消息或信号来进入就绪态。
用系统函数作比较精确的定时,SMALL RTOS51的滴答可以配制成各种中断中加1,默认配置成定时中断0,观察发现没有设置初值,故一次中断的计数0x10000个机器周期,在6m晶振大约是130ms,很难实现定时1s的要求。
Q1:系统时钟节拍怎么定义,怎么调用?
A1: 各个任务运行都需要一个周期性的信号指示,即每一个任务允许运行的时间片是多少,必须规定好。这个时间片就是系统时钟节拍。
在Small RTOS51 中时钟节拍服务函数是OSTimeTick()。此函数在定义在OS_core.c文件中。注意,由于时间片是靠定时器来产生,所以必须相对应的有一个定时器时间节拍中断服务程序OSTickISR( ),这个定时器时间中断服务程序的格式是:
Void OSTickISR(void)  interrupt OS_TIME_ISR
可以看出,这是keil c 中中断服务程序。其中中断入口地址OS_TIME_ISR是在OS_CPU.h文件中定义的,OS_TIME_ISR取值必须是1或者3或者其他(1为Timer0,3为Timer1,其他则根据单片机类型来定)。
由于系统必须周期性的调用OSTimeTick(),以此保证时间片产生的准时。有两个方法来保证:1.由于定时器中断服务程序调用是系统响应中断时候就进入,不需要我们人工干涉,所以在定时器时钟中断服务程序 OSTickISR()来调用OSTimeTick(); 2.由于Small RTOS51具有一个发送信号函数OSIntSendsignal ( Task ID),此函数能使任务ID=Task ID的任务无条件处于就绪状态,并且优先级最高,所以在定时器时钟中断服务程序中调用函OSIntSendsignal(Task ID),使任务ID=Task ID的任务无条件处于就绪状态,退出中断以后任务ID=Task ID的任务能够立即执行,我们再在此任务中调用OSTimeTick()函数即可。
注意:
1.       由于OSIntSendsignal(Task ID)能改变任务的优先级,所以建议在优先级最高的任务中调用OSTimeTick()函数。也就是任务ID=0的任务。
2.       由于用户函数调用时钟节拍服务函数OSTimeTick(),则同时要通过某种方法启动任务调度程序,一般是以K_SIG为参数调用函数OSWait()。这两个函数形成固定组合.
例如:任务中调用时钟节拍服务函数示例
Void Task(void)
{ while(1)
{······
OSTimeTick();
OSWait(K_SIG, 0);/*这里的OSWait()函数中第二个参数0没有任何意义*/
······}
}
3.  用户在编写一个程序时候,必须根据所选用的定时器,修改OS_TIME_ISR的取值。
Q2:系统节拍定时器规定了时间片的大小,由于硬件定时器定时有限,假如我要规定系统节拍为1S,有没有办法实现呢?
A2:有,由于Small RTOS51的系统时间片分配程序为OSTimeTick(),而不是系统定时器中断服务程序OSTickISR();所以只要定义一个计数器,这个计数器放在定时器中断服务程序OSTickISR()里面进行计数;再在OSTimeTick()上根据计数器的值来判断,就可以实现长时间片的实现。
在Small RTOS51中有一个宏TICK_TIMER_SHARING  这个宏是定义进入硬件中断多少次为一个系统定时器软中断,也就是进入定时器多少次中断为一个系统时间片。 Small RTOS51 中规定:TICK_TIMER_SHARING取值为1,则表示进入定时器中断1次为一个系统定时器软中断,即时间片为1个定时器定时时间。
OSTickISR(Void)interrupt OS_TIMER_ISR 函数在OS_CPU.C文件中定义。
对于 OSTickISP(void)interrupt OS_TIMER_ISR定时器硬件中断服务函数中定义
# if TICK_TIMER_SHARING>1
Static  uint8  TickSum=0;
#endif
这个变量 TickSum这个变量放在中断函数里面定义,我是不能理解。我理解为每次进入中断函数内部,uint TickSum都会重新赋值。永远是TickSum=0。我理解TickSum应该定义为一个全局变量并且赋值为0。以后每次进入定时器硬件中断服务函数以后,就执行TickSum=+1的语句。
并且再判断TickSum+1是否等于TICK_TIMER_SHARING。假如等于则说明分配的系统时间片大小已经到了,要把TickSum重新赋值为0。
将程序OSTickISP(void)interrupt OS_TIMER_ISR中的(5)(6)句作些修改。
#if TICK_TIMER_SHARING >1
TickSum = (TickSum + 1) % TICK_TIMER_SHARING;
if (TickSum != 0)
{
Return ;
}
else
{TickSum=0;}
#endif
那么最后的整体程序修改如下:
#if EN_OS_INT_ENTER >0
#pragma disable                                        /* 除非最高优先级中断,否则,必须加上这一句                 */
#endif
void OSTickISR(void) interrupt OS_TIME_ISR
{
···········
这里开始应该写上定时器重新赋值的语句。假如不是自动重装的话
··············
#if EN_USER_TICK_TIMER > 0
UserTickTimer();                     /* 用户函数    */
#endif
#if TICK_TIMER_SHARING >1
TickSum=+1;
If (TickSum = = TICK_TIMER_SHARING)
{TickSum = 0;}
Else
{
return;
}
#endif
#if EN_OS_INT_ENTER > 0
OS_INT_ENTER();     /* 中断开始处理*/
#endif
#if EN_TIMER_SHARING > 0
OSTimeTick(); /* 调用系统时钟处理函数,处理系统任务调度任务 */
#else
OSIntSendSignal(TIME_ISR_TASK_ID);   /* 唤醒ID为TIME_ISR_TASK_ID的任务*/
#endif
OSIntExit();                                        /* 中断结束处理  */
}
注意:
1。程序中有 If EN_USER_TICK_TIMER>0  UserTickTimer( ) #endif  这样的语句。中
EN_USER_TICK_TIMER的意思是禁止(0)或者允许(1)系统定时中断服务程序中调用用户