ucos
2024-12-02 12:22:30 0 举报
AI智能生成
ucosI代码分析
作者其他创作
大纲/内容
替换调度方式
<b><u>变量描述:</u></b><br>OSTCBCur :是一个全局变量,它是一个指针,指向当前正在运行的任务的任务控制块(TCB)。<br>OSTCBList : 是一个全局变量,它定义了一个指向任务控制块(TCB)的指针,用于管理所有已创建的任务。<br>OSTCBStat :是 TCB的一个重要成员,用于表示任务的当前状态。<br>OSTCBHighRdy :是一个全局变量,它指向当前最高优先级且处于就绪状态的任务的任务控制块(TCB)。<br><br><br><br><br>// 轮询调度器的当前任务索引<br><font color="#ec7270">static INT8 OSRdyIdx = 0;</font><br><br>// 轮询调度器选择下一个任务<br><font color="#ec7270">void OSSchedRoundRobin(void)<br>{<br> OS_TCB *next_tcb;<br> OS_ENTER_CRITICAL();<br> next_tcb = OSTCBList; // 假设OSTCBList按优先级排序<br> while (next_tcb != (OS_TCB *)0)<br> {<br> if (next_tcb->OSTCBStat == OS_STAT_RDY && next_tcb->OSTCBDly == 0)<br> {<br> OSTCBCur = next_tcb;<br> break;<br> }<br> next_tcb = next_tcb->OSTCBNext;<br> OSRdyIdx++;<br> if (OSRdyIdx >= OS_MAX_TASKS)<br> {<br> OSRdyIdx = 0; // 回到列表的开始<br> }<br> }<br> OS_EXIT_CRITICAL();<br> OSSched(); // 调用原有的调度函数进行上下文切换<br>}</font><br><br><b><u>功能描述:</u></b><br>OSSchedRoundRobin 函数 :<br><br>进入临界区:OS_ENTER_CRITICAL() 确保在调度过程中不会被中断<br><br>选择下一个任务:从任务列表 OSTCBList 开始遍历,查找下一个状态为就绪 (OS_STAT_RDY) 且没有延迟 (OSTCBDly == 0) 的任务<br><br>更新当前任务:一旦找到符合条件的任务,将其设置为当前任务 OSTCBCur<br><br>更新任务索引:如果当前任务是最后一个任务,OSRdyIdx 重置为 0,以便从列表的开始处继续轮询<br><br>退出临界区:OS_EXIT_CRITICAL() 结束临界区<br><br>调用原有调度函数:调用 OSSched() 执行上下文切换<br><font color="#7f7f7f"><br>轮询调度:轮询调度是一种简单的调度策略,它按照固定的顺序依次检查每个任务,给每个任务平等的执行机会。<br><br>时间片管理:在这个实现中,通过 OSTCBDly 字段间接管理。如果任务需要延迟,它的 OSTCBDly 会被设置为非零值,这样它就不会在下一次轮询中被选中。<br><br>任务选择:调度器选择下一个任务时,会跳过那些不在就绪状态或有延迟的任务。<br><br>循环索引:通过 OSRdyIdx 实现循环索引,确保即使在任务数量少于 OS_MAX_TASKS 时,调度器也能正确地循环遍历任务列表。</font><br><br><br><br><font color="#ec7270">void OSStart(void)<br>{<br> UBYTE x, y, p;<br> y = OSUnMapTbl[OSRdyGrp];<br> x = OSRdyTbl[y];<br> p = (y << 3) + OSUnMapTbl[x];<br> OSTCBHighRdy = OSTCBPrioTbl[p];<br> OSTCBCur = OSTCBHighRdy;<br> OSRunning = 1;<br> OSSchedRoundRobin(); // 使用轮询调度替换原有的调度<br>}</font><br><br><b><u>功能描述:</u></b><br>OSStart 函数<br>初始化操作系统并启动轮询调度:<br><br>初始化任务索引和优先级:通过 OSUnMapTbl 和 OSRdyTbl 计算最高优先级的任务。<br><br>设置当前任务:将最高优先级的任务设置为当前任务 OSTCBCur。<br><br>标记操作系统为运行状态:OSRunning = 1。<br><br>启动轮询调度:调用 OSSchedRoundRobin() 开始轮询调度。<br><br><b><u><br>算法逻辑:</u></b><br>1.定义轮询调度器的当前任务索引:OSRdyIdx用于跟踪当前执行的任务。<br><br>2.实现轮询调度器选择下一个任务的函数:OSSchedRoundRobin()遍历任务列表,选择下一个就绪的任务。<br><br>3.修改OSStart()函数:在OSStart()中调用OSSchedRoundRobin()来初始化轮询调度。<br><br>4.实现时间片管理:为每个任务分配时间片,并在任务执行期间跟踪时间片。<br><br>5.实现上下文切换:在OSSched()中实现上下文切换的逻辑。<br><br>
<u>轮询调度(Polling Scheduling)</u>是一种简单的任务调度策略,通常用于单核操作系统或嵌入式系统中。在轮询调度中,调度器按照固定的顺序(轮询顺序)依次检查每个任务,并为每个任务分配一段CPU时间。以下是轮询调度的一些关键特点:<br><br><b>顺序执行</b>:调度器按照预定的顺序依次执行每个任务,每个任务轮流获得CPU时间。<br><br><b>时间片</b>:在轮询调度中,每个任务通常被分配一个固定的时间片(时间量子),即每个任务在轮询周期中可以运行的时间长度。<br><br><b>循环检查</b>:调度器会不断地循环检查任务列表,为每个任务提供执行机会。<br><br><b>上下文切换</b>:当一个任务的时间片用完时,调度器会进行上下文切换,保存当前任务的状态,并恢复下一个任务的状态,然后继续执行下一个任务。<br><br><b>公平性</b>:轮询调度确保每个任务都能获得CPU时间,因此它是一种公平的调度策略。<br><br><b>实时性</b>:对于实时系统,轮询调度可以提供可预测的响应时间,因为每个任务都知道它将在下一个轮询周期中获得CPU时间。<br><br><b>简单性</b>:轮询调度的实现相对简单,不需要复杂的调度算法。<br><br><b>局限性</b>:<br> 如果任务数量较多,每个任务获得的CPU时间可能会非常短,导致频繁的上下文切换,增加系统的开销。<br> 对于计算密集型任务,轮询调度可能导致某些任务无法及时完成,因为它必须等待轮询周期再次到达。<br> 轮询调度适用于任务数量较少、任务执行时间较短、对实时性要求不是特别高的系统。在更复杂的系统中,可能会采用优先级调度、时间片轮转调度或其他更高级的调度算法来提高系统的效率和响应性。 <br><br><br><br>
TEST1.C
void far Task(void *data)
<font color="#ec7270">setvect(0x08, (void interrupt (*)(void))OSTickISR);</font><br><br><br>作用:设置定时器中断服务<br><br>这行代码将中断向量0x08(通常是系统时钟中断)<br><br>设置为指向 OSTickISR 函数,是时钟节拍中断服务例程。<br><br>setvect 函数用于设置中断向量。
<font color="#ec7270">OSTimeDly(1);</font><br><br>作用:延时<br><br>OSTimeDly 函数使当前任务延时一个时钟节拍。
<font color="#ec7270">putch(*(char *)data);</font><br><br>作用:显示字符<br><br>putch 函数在光标当前位置显示一个字符。<br><br>这个字符是从传递给任务的 data 参数中获取的,data 被强制转换为 char 指针,然后解引用以获取字符。
<font color="#ec7270">gotoxy(rand() % 79 + 1, rand() % 25 + 1);</font><br><br>作用:随机定位光标<br><br>gotoxy 函数将屏幕光标移动到随机位置。<br><br>rand() % 79 + 1 和 rand() % 25 + 1 分别生成1到79和1到25之间的随机数,代表屏幕上的列和行。
<font color="#ec7270">if (kbhit()) {<br><br> setvect(0x08, OldTickISR);<br><br> exit(0);<br><br>}</font><br><br>作用:检查键盘输入<br><br>如果检测到键盘输入(kbhit 函数返回非零值),则将中断向量0x08恢复为原来的中断服务例程(OldTickISR),然后退出任务(exit(0))。
代码
void far Task(void *data)<br><br>{<br><br> setvect(0x08, (void interrupt (*)(void))OSTickISR);<br><br> while (1) {<br><br> OSTimeDly(1);<br><br> gotoxy(rand() % 79 + 1, rand() % 25 + 1);<br><br>putch(*(char *)data);<br><br> if (kbhit()) {<br><br> setvect(0x08, OldTickISR);<br><br> exit(0);<br><br> }<br><br> }<br><br>}
void main(void)
UBYTE err;<br><br>声明了一个无符号字节类型的变量 err,用于存储函数返回的错误代码。
<font color="#ec7270">OSStart</font>();<br><br><br><br>OSStart 函数启动操作系统的多任务处理。一旦调用此函数,操作系统将开始调度任务执行。
<font color="#ec7270">clrscr();</font><br><br>clrscr 函数用于清除屏幕,这是在控制台应用程序中常见的操作,用于清除之前的输出。
OldTickISR = <font color="#ec7270">getvect</font>(0x08);<br><br><font color="#ec7270">setvect</font>( UCOS, (void interrupt (*)(void)) OSCtxSw );<br><br><font color="#ec7270">setvect</font>( 0xF2, OldTickISR );<br><br>作用:获取和设置中断向量<br><br><br><br>getvect(0x08) 获取当前中断向量0x08(通常是时钟中断)的地址,并将其存储在 OldTickISR 变量中。<br><br>setvect(UCOS, (void interrupt (*)(void))OSCtxSw) 将中断向量 UCOS 设置为 OSCtxSw 函数,这是uCOS的上下文切换中断服务。<br><br>setvect(0xF2, OldTickISR) 将中断向量0xF2设置为原来的时钟中断服务例程,OldTickISR。
<font color="#ec7270">OSInit</font>(&OSIdleTaskStk[STK_SIZE], OS_MAX_TASKS);<br><br><br><br>OSInit 函数初始化操作系统,设置空闲任务的堆栈和最大任务数。
<font color="#ec7270">OSTaskCreate</font>(Task, (void *)&Data1, (void *)&Stk1[STK_SIZE], 1);<br><br><font color="#ec7270">OSTaskCreate</font>(Task, (void *)&Data2, (void *)&Stk2[STK_SIZE], 2);<br><br><font color="#ec7270">OSTaskCreate</font>(Task, (void *)&Data3, (void *)&Stk3[STK_SIZE], 3);<br><br><font color="#ec7270">OSTaskCreate</font>(Task, (void *)&Data4, (void *)&Stk4[STK_SIZE], 4);<br><br><font color="#ec7270">OSTaskCreate</font>(Task, (void *)&Data5, (void *)&Stk5[STK_SIZE], 5);<br><br><br><br>OSTaskCreate 函数创建新任务。<br><br>每个任务都使用相同的函数 Task,但传递不同的数据和堆栈。任务的优先级从1到5不同。
代码
void main(void)<br><br>{<br><br> UBYTE err;<br><br><br><br><br><br> clrscr();<br><br> OldTickISR = getvect(0x08);<br><br> setvect(UCOS, (void interrupt (*)(void))OSCtxSw);<br><br> setvect(0xF2, OldTickISR);<br><br> OSInit(&OSIdleTaskStk[STK_SIZE], OS_MAX_TASKS);<br><br> OSTaskCreate(Task, (void *)&Data1, (void *)&Stk1[STK_SIZE], 1);<br><br> OSTaskCreate(Task, (void *)&Data2, (void *)&Stk2[STK_SIZE], 2);<br><br> OSTaskCreate(Task, (void *)&Data3, (void *)&Stk3[STK_SIZE], 3);<br><br> OSTaskCreate(Task, (void *)&Data4, (void *)&Stk4[STK_SIZE], 4);<br><br> OSTaskCreate(Task, (void *)&Data5, (void *)&Stk5[STK_SIZE], 5);<br><br> OSStart();<br><br>}<br><br>
TEST2.C
void main(void)
这个主函数的流程是应用程序的启动流程,它展示了如何准备环境、初始化资源、创建任务,并最终启动多任务处理。展示了如何初始化操作系统、设置同步机制以及创建和启动多个任务。
<font color="#ec7270">OSInit</font>(&OSIdleTaskStk[STK_SIZE], OS_MAX_TASKS);<br><br>OSInit函数初始化操作系统,设置空闲任务的堆栈空间和最大任务数。
<font color="#ec7270">OSSemInit</font>(&Sem, 1);<br><font color="#ec7270">OSMboxInit</font>(&Mbox, (void *)0);<br><font color="#ec7270">OSQInit</font>(&Q, &QData[0], Q_SIZE);<br><br>OSSemInit(&Sem, 1); 初始化一个信号量Sem,初始计数设置为1。<br>OSMboxInit(&Mbox, (void *)0); 初始化一个邮箱Mbox,初始消息指针设置为NULL。<br>OSQInit(&Q, &QData[0], Q_SIZE); 初始化一个队列Q,指定队列的存储开始地址和大小。
<font color="#ec7270">OSTaskCreate</font>(DispTask, (void *)0, (void *)&DispStk[STK_SIZE], 0);<br><font color="#ec7270">OSTaskCreate</font>(KeyTask, (void *)0, (void *)&KeyStk[STK_SIZE], 1);<br><font color="#ec7270">OSTaskCreate</font>(Task1, (void *)0, (void *)&Task1Stk[STK_SIZE], 10);<br><font color="#ec7270">OSTaskCreate</font>(Task2, (void *)0, (void *)&Task2Stk[STK_SIZE], 20);<br><font color="#ec7270">OSTaskCreate</font>(Task3, (void *)0, (void *)&Task3Stk[STK_SIZE], 30);<br><br>OSTaskCreate函数用于创建新任务,每个任务都有自己的任务函数、传递给任务的数据、堆栈空间和优先级。<br>任务的优先级从0(最高)到30(最低)。
OSStart();<br>OSStart函数启动操作系统的多任务处理。
代码
void main(void)<br>{<br> clrscr();<br> OldTickISR = getvect(0x08);<br> setvect(UCOS, (void interrupt (*)(void))OSCtxSw);<br> setvect(0xF2, OldTickISR);<br> OSInit(&OSIdleTaskStk[STK_SIZE], OS_MAX_TASKS);<br> OSSemInit(&Sem, 1);<br> OSMboxInit(&Mbox, (void *)0);<br> OSQInit(&Q, &QData[0], Q_SIZE);<br> OSTaskCreate(DispTask, (void *)0, (void *)&DispStk[STK_SIZE], 0);<br> OSTaskCreate(KeyTask, (void *)0, (void *)&KeyStk[STK_SIZE], 1);<br> OSTaskCreate(Task1, (void *)0, (void *)&Task1Stk[STK_SIZE], 10);<br> OSTaskCreate(Task2, (void *)0, (void *)&Task2Stk[STK_SIZE], 20);<br> OSTaskCreate(Task3, (void *)0, (void *)&Task3Stk[STK_SIZE], 30);<br> OSStart();<br>}
void far KeyTask(void *data)
<font color="#ec7270">setvect</font>(0x08, (void interrupt (*)(void))OSTickISR);<br>这行代码将中断向量0x08(通常是系统时钟中断)设置为 OSTickISR 函数。
<font color="#ec7270">if (kbhit()) {</font><br>kbhit 函数用于检查键盘是否有输入。如果返回非零值,表示有键盘输入。
<font color="#ec7270">switch (getch()) {<br></font>getch 函数获取按下的字符,然后使用 switch 语句来处理不同的按键:<br><br>如果按下的是 '1',则调用 OSMboxPost 函数向邮箱 Mbox 发送一个消息((void *)1)。<br>如果按下的是 '2',则调用 OSQPost 函数向队列 Q 发送一个消息((void *)1)。<br>如果按下的是 'x' 或 'X',则将中断向量0x08恢复为原来的中断服务例程(OldTickISR),然后退出任务(exit(0))。<font color="#ec7270"></font>
<font color="#ec7270">setvect</font>(0x08, OldTickISR);<br>exit(0);<br>当按下 'x' 或 'X' 时,这些行代码将中断向量恢复并退出任务,允许操作系统进行清理和资源回收。
这个任务的主要作用是监听键盘输入,并根据输入向邮箱或队列发送消息,或者在特定输入下退出任务。这是一个典型的实时操作系统中的任务,用于处理外部事件(如用户输入)并与其他系统组件(如同步机制)交互。
代码
void far KeyTask(void *data)<br>{<br> UBYTE i;<br><br><br> setvect(0x08, (void interrupt (*)(void))OSTickISR);<br> while (1) {<br> OSTimeDly(1);<br> if (kbhit()) {<br> switch (getch()) {<br> case '1': OSMboxPost(&Mbox, (void *)1);<br> break;<br> case '2': OSQPost(&Q, (void *)1);<br> break;<br> case 'x':<br> case 'X': setvect(0x08, OldTickISR);<br> exit(0);<br> break;<br> }<br> }<br> }<br>}<br><br>
void far Task1(void *data)
这段代码定义了一个名为 Task1 的任务函数,它是用于从邮箱中接收消息,并更新一个计数器。 <br>
<font color="#ec7270">UBYTE </font>err;<br>这行代码声明了一个无符号字节类型的变量 err,用于存储函数返回的错误代码。
<font color="#ec7270">OSMboxPend</font>(&Mbox, 36, &err);<br><br>OSMboxPend 函数使任务等待(挂起)直到从 Mbox 邮箱接收到消息。36 是超时时间(以时钟节拍为单位),&err 是用于存储操作结果的指针。如果邮箱在超时时间内变为非空,任务将接收消息并继续执行;如果超时时间到达而邮箱仍然为空,err 将被设置为 OS_TIMEOUT。
<font color="#ec7270">OSSemPend</font>(&Sem, 0);<br><br>OSSemPend 函数使任务等待信号量 Sem。0 表示任务将无限期等待信号量,直到它可用。如果信号量可用,任务将继续执行;如果信号量不可用,任务将被挂起,直到信号量被另一个任务释放。
Ctr1++;<br>简单地将全局计数器 Ctr1 增加1。这个计数器用于跟踪任务从邮箱接收消息的次数。
<font color="#ec7270">OSSemPost</font>(&Sem);<br>OSSemPost 函数释放(或信号)信号量 Sem,允许等待此信号量的其他任务继续执行。
代码
void far Task1(void *data)<br>{<br> UBYTE err;<br><br> while (1) {<br> OSMboxPend(&Mbox, 36, &err);<br> OSSemPend(&Sem, 0);<br> Ctr1++;<br> OSSemPost(&Sem);<br> }<br>}
void far Task2(void *data)
这个任务的主要作用是从队列接收消息,并在每次成功接收后更新计数器。它还展示了如何使用信号量来同步任务的执行。这种模式在需要协调多个任务访问共享资源或事件时非常有用。<br><br>Task2 与 Task1 类似,但它们从不同的同步机制(<b><font color="#ec7270">队列Q和邮箱Mbox</font></b>)接收消息。
<font color="#ec7270">OSQPend</font>(&Q, 72, &err);<br>OSQPend 函数使任务等待(挂起)直到从 Q 队列接收到消息。72 是超时时间(以时钟节拍为单位),&err 是用于存储操作结果的指针。如果队列在超时时间内变为非空,任务将接收消息并继续执行;如果超时时间到达而队列仍然为空,err 将被设置为 OS_TIMEOUT。
<font color="#ec7270">OSSemPend</font>(&Sem, 0);<br>OSSemPend 函数使任务等待信号量 Sem。0 表示任务将无限期等待信号量,直到它可用。如果信号量可用,任务将继续执行;如果信号量不可用,任务将被挂起,直到信号量被另一个任务释放。
Ctr2++;<br>简单地将全局计数器 Ctr2 增加1。这个计数器用于跟踪任务从队列接收消息的次数。
<font color="#ec7270">OSSemPost</font>(&Sem);<br>OSSemPost 函数释放(或信号)信号量 Sem,允许等待此信号量的其他任务继续执行。
代码
void far Task2(void *data)<br>{<br> UBYTE err;<br><br> while (1) {<br> OSQPend(&Q, 72, &err);<br> OSSemPend(&Sem, 0);<br> Ctr2++;<br> OSSemPost(&Sem);<br> }<br>}
void far Task3(void *data)
这个任务的主要作用是模拟时钟的秒和分钟计数。<br>它通过延时和信号量同步来确保每次只更新一次计数器,并且更新操作是原子的,即不会被其他任务中断。<br>这种同步机制对于确保多个任务共享资源时数据的一致性非常重要。
<font color="#ec7270">OSTimeDly</font>(18);<br>OSTimeDly 函数使当前任务延时18个时钟节拍。一个时钟节拍通常是由系统的定时器中断生成的。<br>这里延时18个节拍意味着任务每18个时钟节拍执行一次循环体。
<font color="#ec7270">OSSemPend</font>(&Sem, 0);<br>OSSemPend 函数使任务等待信号量 Sem。0 表示任务将无限期等待信号量,直到它可用。如果信号量可用,任务将继续执行;如果信号量不可用,任务将被挂起,直到信号量被另一个任务释放。
Sec++;<br>这行代码将全局秒计数器 Sec 增加1。
if (<font color="#ec7270">Sec > 59</font>) {<br> Sec = 0;<br> Min++;<br>}<br>如果秒计数器 Sec 超过59,这表示一分钟已经过去。因此,代码将 Sec 重置为0,并增加分钟计数器 Min。
<font color="#ec7270">OSSemPost</font>(&Sem);<br>OSSemPost 函数释放(或信号)信号量 Sem,允许等待此信号量的其他任务继续执行。
代码
void far Task3(void *data)<br>{<br> while (1) {<br> OSTimeDly(18);<br> OSSemPend(&Sem, 0);<br> Sec++;<br> if (Sec > 59) {<br> Sec = 0;<br> Min++;<br> }<br> OSSemPost(&Sem);<br> }<br>}
void far DispTask(void *data)
这个任务的主要作用是在屏幕上显示时钟和计数器的值,并且通过信号量 Sem 同步对共享资源的访问,以确保在显示时数据的一致性和准确性。
UWORD ctr1, ctr2;<br>UBYTE min, sec;<br>声明了用于存储计数器和时钟值的变量。
<font color="#ec7270">OSTimeDly</font>(6);<br>OSTimeDly 函数使当前任务延时6个时钟节拍。<br>这允许其他任务有机会运行,而不是 DispTask 独占CPU。
<font color="#ec7270">OSSemPend</font>(&Sem, 0);<br>OSSemPend 函数使任务等待信号量 Sem。0 表示任务将无限期等待信号量,直到它可用。<br>这是为了确保在读取计数器和时钟值时不会被其他任务中断。
读取共享资源:<br>ctr1 = Ctr1;<br>ctr2 = Ctr2;<br>min = Min;<br>sec = Sec;<br>读取全局计数器 Ctr1 和 Ctr2,以及分钟和秒计数器 Min 和 Sec 的值。
<font color="#ec7270">OSSemPost</font>(&Sem);<br>OSSemPost 函数释放(或信号)信号量 Sem,允许等待此信号量的其他任务继续执行。
gotoxy(1, 9);<br>printf("Clock = %02d:%02d", min, sec);<br>gotoxy(1, 10);<br>printf("Ctr1 = %4d", ctr1);<br>gotoxy(1, 11);<br>printf("Ctr2 = %4d", ctr2);<br>使用 gotoxy 函数将光标移动到屏幕上的特定位置,并使用 printf 函数打印时钟和计数器的值。<br>%02d 格式化字符串确保分钟和秒以两位数字显示。
代码
void far DispTask(void *data)<br>{<br> UWORD ctr1, ctr2;<br> UBYTE min, sec;<br><br> while (1) {<br> OSTimeDly(6);<br> OSSemPend(&Sem, 0);<br> ctr1 = Ctr1;<br> ctr2 = Ctr2;<br> min = Min;<br> sec = Sec;<br> OSSemPost(&Sem);<br> gotoxy(1, 9);<br> printf("Clock = %02d:%02d", min, sec);<br> gotoxy(1, 10);<br> printf("Ctr1 = %4d", ctr1);<br> gotoxy(1, 11);<br> printf("Ctr2 = %4d", ctr2);<br> }<br>}
UCOS186C.C
UBYTE OSTaskCreate(void (far *task)(void *dptr), void *data, void *pstk, UBYTE p)
这段代码是uCOS操作系统中用于创建新任务的函数 OSTaskCreate 的实现。<br>这个函数负责初始化一个新的任务控制块(TCB),设置任务的堆栈,并将其添加到就绪任务列表中。<br>实现创建新任务的关键部分,它负责设置任务的堆栈和TCB,并确保新任务能够被操作系统调度和执行。
<font color="#000000">1.函数声明:</font><br>UBYTE <font color="#ec7270">OSTaskCreate</font>(void (far *task)(void *dptr), void *data, void *pstk, UBYTE p)<br>这个函数接受四个参数:任务函数的指针 task,传递给任务的数据 data,任务的堆栈指针 pstk,以及任务的优先级 p。函数返回一个无符号字节,表示创建任务的结果。
<font color="#000000">2.检查任务是否已存在:</font><br><br>if (<font color="#ec7270">OSTCBPrioTbl</font>[p] == (OS_TCB *)0) {<br>检查优先级为 p 的任务是否已经存在。<br>如果已经存在,则不创建新任务,并返回错误代码 OS_PRIO_EXIST。<br><br>
<font color="#000000">3.获取空闲的任务控制块(TCB):</font><br><br>ptr =<font color="#ec7270"> OSTCBGetFree</font>();<br>OSTCBGetFree 函数从空闲TCB列表中获取一个空闲的TCB。
<font color="#000000">4.初始化TCB:</font><br>ptr->OSTCBPrio = (UBYTE)p;<br>ptr->OSTCBStat = OS_STAT_RDY;<br>ptr->OSTCBDly = 0;<br>这些行代码初始化TCB的优先级、状态(就绪)和延迟时间(0)。
<font color="#000000">5.设置任务堆栈:</font><br>stk = (UWORD *)pstk;<br>*--stk = (UWORD)FP_OFF(data);<br>*--stk = (UWORD)FP_SEG(task);<br>*--stk = (UWORD)FP_OFF(task);<br>*--stk = (UWORD)0x0200; // PSW = Int. En.<br>*--stk = (UWORD)FP_SEG(task);<br>*--stk = (UWORD)FP_OFF(task);<br>*--stk = (UWORD)0x0000; // AX = 0<br>*--stk = (UWORD)0x0000; // CX = 0<br>*--stk = (UWORD)0x0000; // DX = 0<br>*--stk = (UWORD)0x0000; // BX = 0<br>*--stk = (UWORD)0x0000; // SP = 0<br>*--stk = (UWORD)0x0000; // BP = 0<br>*--stk = (UWORD)0x0000; // SI = 0<br>*--stk = (UWORD)0x0000; // DI = 0<br>*--stk = (UWORD)0x0000; // ES = 0<br>这些行代码设置任务的堆栈,包括数据段、代码段、程序状态字(PSW)、寄存器值等。<br>这是为任务切换时保存和恢复上下文做准备。
<font color="#000000">6.更新TCB和就绪任务列表:</font><br>ptr->OSTCBStkPtr = (void *)stk;<br>OSTCBPrioTbl[p] = ptr;<br>更新TCB的堆栈指针,并将TCB添加到优先级表中。
<font color="#000000">7.添加任务到就绪任务列表:</font><br><font color="#ec7270">OS_ENTER_CRITICAL</font>();<br>ptr->OSTCBNext = OSTCBList;<br>ptr->OSTCBPrev = (OS_TCB *)0;<br>if (OSTCBList != (OS_TCB *)0) {<br> OSTCBList->OSTCBPrev = ptr;<br>}<br>OSTCBList = ptr;<br>OSRdyGrp |= OSMapTbl[p >> 3];<br>OSRdyTbl[p >> 3] |= OSMapTbl[p & 0x07];<br>OS_EXIT_CRITICAL();<br><br>将新任务添加到就绪任务列表中,并更新就绪任务组和表。
<font color="#000000">8.调度: </font><br>if (OSRunning) {<br> OSSched();<br>}<br>如果操作系统已经在运行,调用 OSSchedule 函数进行任务调度。
代码
#include "INCLUDES.H"<br>#pragma inline<br><br>extern UBYTE OSMapTbl[];<br>extern UBYTE OSUnMapTbl[];<br><br>/*<br>***********************************************************<br>* CREATE A TASK<br>***********************************************************<br>*/<br><br>UBYTE OSTaskCreate(void (far *task)(void *dptr), void *data, void *pstk, UBYTE p)<br>{<br> OS_TCB *ptr;<br> UWORD *stk;<br><br><br> if (OSTCBPrioTbl[p] == (OS_TCB *)0) { /* Avoid creating task if already exist */<br> ptr = OSTCBGetFree();<br> ptr->OSTCBPrio = (UBYTE)p;<br> ptr->OSTCBStat = OS_STAT_RDY;<br> ptr->OSTCBDly = 0;<br><br> stk = (UWORD *)pstk; /* 80186/80188 Small Model */<br> *--stk = (UWORD)FP_OFF(data);<br> *--stk = (UWORD)FP_SEG(task);<br> *--stk = (UWORD)FP_OFF(task);<br> *--stk = (UWORD)0x0200; /* PSW = Int. En. */<br> *--stk = (UWORD)FP_SEG(task);<br> *--stk = (UWORD)FP_OFF(task);<br> *--stk = (UWORD)0x0000; /* AX = 0 */<br> *--stk = (UWORD)0x0000; /* CX = 0 */<br> *--stk = (UWORD)0x0000; /* DX = 0 */<br> *--stk = (UWORD)0x0000; /* BX = 0 */<br> *--stk = (UWORD)0x0000; /* SP = 0 */<br> *--stk = (UWORD)0x0000; /* BP = 0 */<br> *--stk = (UWORD)0x0000; /* SI = 0 */<br> *--stk = (UWORD)0x0000; /* DI = 0 */<br> *--stk = (UWORD)0x0000; /* ES = 0 */<br> ptr->OSTCBStkPtr = (void *)stk; /* Load SP in TCB */<br> OSTCBPrioTbl[p] = ptr;<br> OS_ENTER_CRITICAL();<br> ptr->OSTCBNext = OSTCBList;<br> ptr->OSTCBPrev = (OS_TCB *)0;<br> if (OSTCBList != (OS_TCB *)0) { /* Rev. A, This line was missing */<br> OSTCBList->OSTCBPrev = ptr;<br> }<br> OSTCBList = ptr;<br> OSRdyGrp |= OSMapTbl[p >> 3];<br> OSRdyTbl[p >> 3] |= OSMapTbl[p & 0x07];<br> OS_EXIT_CRITICAL();<br> if (OSRunning) {<br> OSSched();<br> }<br> return (OS_NO_ERR);<br> } else {<br> return (OS_PRIO_EXIST);<br> }<br>}<br>
UCOS.C
核心部分,包含了操作系统的初始化、任务调度、同步机制(信号量、邮箱、队列)的实现。
UBYTE OSMboxInit(OS_MBOX *pmbox, void *msg);<br>void *OSMboxPend(OS_MBOX *pmbox, UWORD timeout, UBYTE *err);<br>UBYTE OSMboxPost(OS_MBOX *pmbox, void *msg);<br>这些函数用于初始化、等待和发送邮箱消息。
UBYTE OSQInit(OS_Q *pq, void **start, UBYTE size);<br>void *OSQPend(OS_Q *pq, UWORD timeout, UBYTE *err);<br>UBYTE OSQPost(OS_Q *pq, void *msg);<br>这些函数用于初始化、等待和发送队列消息。
UBYTE OSSemInit(OS_SEM *psem, WORD cnt);<br>UBYTE OSSemPend(OS_SEM *psem, UWORD timeout);<br>UBYTE OSSemPost(OS_SEM *psem);<br>这些函数用于初始化、等待和释放信号量。
TABLES
UBYTE const <font color="#ec7270">OSMapTbl</font>[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};<br>UBYTE const <font color="#ec7270">OSUnMapTbl</font>[] = {...};<br>OSMapTbl 和 OSUnMapTbl 是两个映射表,用于将优先级映射到就绪表和组中,以及反向映射。
OS_TCB *OSTCBCur;<br>OS_TCB *OSTCBHighRdy;<br>OS_TCB *OSTCBList;<br>OS_TCB *OSTCBPrioTbl[64];<br>BOOLEAN OSRunning;<br>UBYTE OSRdyGrp;<br>UBYTE OSRdyTbl[8];<br>UBYTE OSLockNesting;<br>UBYTE OSIntNesting;<br>OS_TCB *OSTCBFreeList;<br>这些全局变量用于跟踪当前任务、就绪任务列表、任务优先级表、运行状态、就绪组和表、锁定嵌套和中断嵌套等。
void <font color="#ec7270">OSInit</font>(void *idle_task_stk, UBYTE maxtasks)<br>初始化操作系统,包括设置全局变量、初始化任务控制块列表和空闲任务。
<font color="#ec7270"><b>全局变量初始化:</b></font><br><br>OSTCBCur = (OS_TCB *)0; 初始化当前任务指针为 NULL。<br>OSTCBList = (OS_TCB *)0; 初始化任务列表头指针为 NULL。<br>OSIntNesting = 0; 初始化中断嵌套层数为0。<br>OSLockNesting = 0; 初始化调度锁定嵌套层数为0。<br>OSRunning = 0; 初始化操作系统运行状态为0(停止状态)。<br><br><b><font color="#ec7270">就绪表初始化:</font></b><br>OSRdyGrp = 0; 初始化就绪组为0。<br>for (i = 0; i < 8; i++) { OSRdyTbl[i] = 0; } 初始化8个就绪表字节为0。<br><br><b><font color="#ec7270">任务优先级表初始化:</font></b><br>for (i = 0; i < 64; i++) { OSTCBPrioTbl[i] = (OS_TCB *)0; } 初始化64个任务优先级表项为 NULL。<br><br><b><font color="#ec7270">任务控制块链表初始化:</font></b><br>for (i = 0; i < (maxtasks - 1); i++) { OSTCBTbl[i].OSTCBNext = &OSTCBTbl[i+1]; } 将任务控制块数组链接成一个链表。<br>OSTCBTbl[maxtasks-1].OSTCBNext = (OS_TCB *)0; 将链表的最后一个节点的 OSTCBNext 设置为 NULL。<br><br><font color="#ec7270"><b>空闲任务控制块初始化:</b></font><br>OSTCBFreeList = &OSTCBTbl[0]; 初始化空闲任务控制块列表头指针为任务控制块数组的第一个元素。<br><br><b><font color="#ec7270">创建空闲任务:</font></b><br>OSTaskCreate(OSTaskIdle, (void *)0, idle_task_stk, OS_LO_PRIO); 创建空闲任务,这是操作系统在没有其他任务运行时执行的任务。<br><br>这个初始化函数是操作系统启动前的重要步骤,它确保了操作系统的核心数据结构被正确设置,为后续的任务创建和调度提供了基础。在调用 OSInit 之后,操作系统才能开始调度任务执行。
static void far <font color="#ec7270">OSTaskIdle</font>(void *data);<br>定义了空闲任务,这是一个空循环,用于在系统没有其他任务运行时占用CPU。
void <font color="#ec7270">OSStart</font>(void)<br>启动多任务处理,选择最高优先级的任务开始执行。
<b>1. 局部变量:</b><br> UBYTE x, y, p;:定义了三个无符号字节(UBYTE)类型的变量,用于存储中间计算结果。<br> 这些变量在函数内部被用来遍历任务就绪表和优先级映射表。<br><b>2. 选择最高优先级的就绪任务:</b><br><font color="#ec7270">y = OSUnMapTbl[OSRdyGrp];:</font><br> 从就绪组(OSRdyGrp)的映射表中获取一个索引值。就绪组是一个表示哪些优先级的任务组处于就绪状态的<b>位图</b>。<br><font color="#ec7270">x = OSRdyTbl[y];:</font><br> 使用这个索引值从就绪表中获取另一个索引值。就绪表是一个数组,每个元素指向一个包含特定优先级下就绪任务位图的表。<br><font color="#ec7270">p = (y << 3) + OSUnMapTbl[x];:<br></font> 通过一系列的计算,得到最高优先级就绪任务的优先级。这里 (y << 3) 是将就绪组的索引左移3位(因为每个就绪组包含8个优先级,所以左移3位相当于乘以8),然后加上从就绪表中获取的具体优先级的偏移量。<br><b>3. 设置当前任务和操作系统运行状态:</b><br><font color="#ec7270">OSTCBHighRdy = OSTCBPrioTbl[p];:</font><br> 根据计算出的优先级,从优先级表中获取对应任务的任务控制块(TCB),并将其设置为最高优先级就绪任务的任务控制块。<br><font color="#ec7270">OSTCBCur = OSTCBHighRdy;:</font><br> 将当前任务(OSTCBCur)设置为最高优先级就绪任务。<br><font color="#ec7270">OSRunning = 1;:</font><br> 将操作系统运行状态(OSRunning)设置为1,表示操作系统已经开始运行。<br><b>4. 启动最高优先级就绪任务:</b><br>OSStartHighRdy();:这个函数用于实际启动最高优先级就绪任务。<br><br>OSStart 函数通常只在操作系统初始化期间调用一次,以启动多任务环境。一旦调用 OSStart,控制权通常会转移到最高优先级就绪任务,并且只有在任务主动放弃CPU时,才会再次回到调度器。<br><br>
void <font color="#ec7270">OSSched</font>(void)<br>任务调度函数,选择最高优先级的任务执行,并进行上下文切换。
<b>1. 函数定义:</b>void OSSched(void) 是一个没有返回值和参数的函数,用于操作系统的调度逻辑。<br><b><br>2. 局部变量:</b><br> UBYTE x, y, p; :<br> 定义了三个无符号字节(UBYTE)类型的变量,用于存储中间计算结果。<br><b><br>3. 进入临界区:</b><br> OS_ENTER_CRITICAL(); <br> 这条语句用于进入临界区,保证在执行调度逻辑时不会被其他中断或任务打断,以保证数据的一致性和完整性。<br><br><b>4. 检查锁定和中断嵌套级别:</b><br> if (OSLockNesting == 0 && OSIntNesting == 0):<br> 这个条件判断确保在没有任务锁定(OSLockNesting)和中断嵌套(OSIntNesting)的情况下才进行任务调度。这是为了避免在不应该调度的时候进行调度,比如在中断处理过程中或任务被锁定时。<br><br><b>5. 选择最高优先级的任务:</b><br>y = OSUnMapTbl[OSRdyGrp];: 从就绪组(OSRdyGrp)的映射表中获取一个索引值。<br>x = OSRdyTbl[y];: 使用这个索引值从就绪表中获取另一个索引值。<br>p = (y << 3) + OSUnMapTbl[x];: 通过一系列的计算,得到最高优先级任务的优先级。<br>OSTCBHighRdy = OSTCBPrioTbl[p];: 根据计算出的优先级,从优先级表中获取对应任务的任务控制块(TCB)。<br><br><b>6. 任务切换:</b><br>if (OSTCBHighRdy != OSTCBCur): 如果最高优先级的任务不是当前正在执行的任务,则需要进行任务切换。<br>OS_TASK_SW();: 执行任务切换操作,切换到最高优先级的任务。<br><b>7. 退出临界区:</b>OS_EXIT_CRITICAL(); 退出临界区,允许其他中断或任务继续执行。
void <font color="#ec7270">OSIntEnter</font>(void);<br>void <font color="#ec7270">OSIntExit</font>(void);<br>这些函数用于处理中断的进入和退出,确保在中断处理期间不进行任务调度。
1. <b>进入临界区</b>:<br><font color="#ec7270">OS_ENTER_CRITICAL();</font> <br>这条语句用于进入临界区。<br>2. <b>增加中断嵌套计数器</b>:<br><font color="#ec7270">OSIntNesting++; <br></font>这条语句将中断嵌套计数器(OSIntNesting)的值增加1。<br>中断嵌套计数器用于跟踪当前中断的嵌套级别。当操作系统在处理一个中断时,如果另一个中断发生,中断嵌套计数器就会增加,以确保在中断处理过程中不会进行任务调度(因为任务调度通常不应该在中断处理过程中进行)。<br>3. <b>退出临界区</b>:<br><font color="#ec7270">OS_EXIT_CRITICAL(); <br></font>退出临界区后,其他任务或中断服务程序可以访问被保护的共享资源。
void<font color="#ec7270"> OSTimeDly</font>(UWORD ticks)<br>使当前任务延时指定的时钟节拍数。
<b>1. 函数定义</b>:<br>void OSTimeDly(UWORD ticks) <br>是一个接受一个无符号字(UWORD)类型参数 ticks 的函数,该参数指定了任务延迟的时间片数。函数没有返回值。<br><b>2. 局部变量:</b><br>UBYTE p;:<br>定义了一个无符号字节(UBYTE)类型的变量 p,用于存储当前任务的优先级。<br><b>3. 进入临界区:</b><br>OS_ENTER_CRITICAL(); <br>这条语句用于进入临界区,以确保在修改任务就绪表和优先级表时不会被其他任务或中断打断。<br><b>4. 获取并修改当前任务的优先级:</b><br><font color="#ec7270"><b>p = OSTCBCur->OSTCBPrio;</b></font>:<br>从当前任务的任务控制块(TCB)中获取当前任务的优先级,并将其存储在变量 p 中。<br><font color="#ec7270"><b>if ((OSRdyTbl[p >> 3] &= ~OSMapTbl[p & 0x07]) == 0)</b></font>:<br>这一行代码首先通过位运算获取当前优先级在就绪表中的位置,然后使用位与和位取反操作将该优先级对应的位清零(即从就绪表中移除当前任务)。如果清零后的就绪表项为0,表示该优先级下没有就绪任务。<br><b><font color="#ec7270">OSRdyGrp &= ~OSMapTbl[p >> 3]</font></b>;:<br>如果当前优先级下没有就绪任务,则将从就绪组中对应的位也清零,表示该组中没有就绪任务。<br><b>5. 设置当前任务的延迟时间:</b><br>OSTCBCur->OSTCBDly = ticks;:<br>将当前任务的延迟时间设置为参数 ticks 指定的值。<br><b>6. 退出临界区:</b>OS_EXIT_CRITICAL(); <br><b>7. 调用调度函数:</b>OSSched(); <br>调用任务调度函数,以重新评估哪些任务是就绪的,并选择一个新的最高优先级就绪任务来执行。由于当前任务已经被挂起 ,因此调度函数可能会选择另一个任务来执行。
void <font color="#ec7270">OSTimeTick</font>(void)<br>处理系统时钟滴答,更新任务的延时和就绪状态。
<b>1. 局部变量:</b><br>UBYTE p;: 定义了一个无符号字节(UBYTE)类型的变量 p,用于存储任务的优先级。<br>OS_TCB *ptcb;: 定义了一个指向任务控制块(TCB)的指针 ptcb,用于遍历任务列表。<br><b>2. 遍历任务列表:</b><br>ptcb = OSTCBList;: 将 ptcb 初始化为指向任务列表的起始位置。<br><font color="#ec7270"><b>while (ptcb->OSTCBPrio != OS_LO_PRIO)</b></font>: 遍历任务列表,直到遇到最低优先级的任务(OS_LO_PRIO)。这里任务列表是按优先级顺序排列的,且最低优先级的任务作为列表的结束标志。<br><b>3. 处理每个任务的延迟:</b><br>OS_ENTER_CRITICAL();: 进入临界区,以确保在修改任务就绪表和延迟时间时不会被其他任务或中断打断。<br>if (ptcb->OSTCBDly != 0): 检查当前任务的延迟时间是否不为0。<br>if (--ptcb->OSTCBDly == 0): 如果延迟时间大于0,则将其减1,并检查是否减为0。<br> 如果延迟时间减为0,表示任务延迟到期。<br>p = ptcb->OSTCBPrio;: 获取当前任务的优先级。<br>OSRdyGrp |= OSMapTbl[p >> 3];: 将就绪组中对应的位设置为1,表示该组中有就绪任务。<br>OSRdyTbl[p >> 3] |= OSMapTbl[p & 0x07];:将就绪表中对应的位设置为1,表示该优先级下有就绪任务。<br>OS_EXIT_CRITICAL();: 退出临界区,允许其他任务或中断继续执行。<br><b>4. 移动到下一个任务:</b><br>ptcb = ptcb->OSTCBNext;: 将 ptcb 更新为指向下一个任务的指针。
OS_TCB *<font color="#ec7270">OSTCBGetFree</font>(void)<br>获取空闲TCB、更新空闲列表、分配TCB给任务、链接到已分配列表。
<b>1. 函数定义:</b>OS_TCB *OSTCBGetFree(void) 是一个返回 OS_TCB 类型指针的函数,该指针指向一个空闲的任务控制块。函数没有参数。<br><b>2. 局部变量:</b><br>OS_TCB *ptcb;:定义了一个指向 OS_TCB 类型的指针 ptcb,用于存储从空闲列表中获取的 TCB 的地址。<br><b>3. 进入临界区</b>:<br>OS_ENTER_CRITICAL();:这条语句用于进入临界区,以确保在访问和修改 TCB 空闲列表时不会被其他任务或中断打断。临界区的实现可能依赖于禁用中断、使用锁或其他同步机制。<br><b>4. 获取空闲 TCB</b>:<br>ptcb = OSTCBFreeList;:将 ptcb 设置为指向当前空闲列表的起始 TCB(即 OSTCBFreeList 指向的 TCB)。<br>OSTCBFreeList = ptcb->OSTCBNext;:更新空闲列表的起始位置,使其指向下一个空闲的 TCB。这里 OSTCBNext 字段在 TCB 结构中用于链接空闲列表中的 TCB。<br><b>5. 退出临界区:</b><br>OS_EXIT_CRITICAL();:这条语句用于退出临界区,允许其他任务或中断继续执行。<br><b>6. 返回空闲 TCB:</b><br>return (ptcb);:返回指向获取的空闲 TCB 的指针。
void <font color="#ec7270">OSLock</font>(void)<br>锁定任务调度,增加调度锁计数(原子性操作)。
<b>增加嵌套锁定计数器:</b><br>OSLockNesting++;:在临界区内,将 OSLockNesting 变量的值增加1。<br>这个变量用于跟踪锁的嵌套级别,即同一个任务或中断处理程序已经获取了多少次锁。
void OSLock(void)<br>{<br> OS_ENTER_CRITICAL();<br> OSLockNesting++;<br> OS_EXIT_CRITICAL();<br>}
void <font color="#ec7270">OSUnlock</font>(void)<br>解锁任务调度,允许任务调度器重新进行任务切换。
<b>减少嵌套锁定计数器:</b><br>OSLockNesting--;:在临界区内,将 OSLockNesting 变量的值减少1。<br>这个变量用于跟踪锁的嵌套级别,即同一个任务或中断处理程序已经释放了多少次锁。<br>如果 OSLockNesting 变为0,则表示所有通过 OSLock 获取的锁都已经被释放。<br><b>重新调度:</b><br>OSSched();:在退出临界区后,调用 OSSched 函数来检查是否有更高优先级的任务已经就绪,并可能需要运行。如果系统处于多任务环境,OSSched 函数将确保任务调度器运行,并根据需要切换任务。
void OSUnlock(void)<br>{<br> OS_ENTER_CRITICAL();<br> OSLockNesting--;<br> OS_EXIT_CRITICAL();<br> OSSched();<br>}
UBYTE <font color="#ec7270">OSChangePrio</font>(UBYTE newp)<br>改变任务优先级。
UBYTE oldp;:用于存储当前任务的旧优先级。<br><br><font color="#314aa4">1. </font><font color="#ec7270">if (OSTCBPrioTbl[newp] != (void *)0):</font><br>检查优先级表(OSTCBPrioTbl)中是否已经存在一个任务具有指定的新优先级。<br>如果存在,则退出临界区并返回 OS_PRIO_EXIST。<br><br><b>2. 更改优先级:</b><br>如果新优先级未被占用,则继续执行以下步骤:<br><font color="#ec7270"><b>oldp = OSTCBCur->OSTCBPrio;:</b></font> 获取当前任务的旧优先级。<br>更新就绪表和就绪组,以反映当前任务从旧优先级到新优先级的移动。这包括清除旧优先级在就绪表和就绪组中的标记,并设置新优先级在就绪表和就绪组中的标记。<br><b><font color="#ec7270">OSTCBCur->OSTCBPrio = newp;: </font></b>更改当前任务的优先级。<br>更新优先级表,将当前任务与新优先级关联,并清除旧优先级的关联。<br><b>3.重新调度:</b><br>OSSched();:调用任务调度器以检查是否有更高优先级的任务已经就绪,并可能需要运行。<br><br>OSTCBCur 是一个指向当前正在运行的任务的任务控制块(TCB)的指针。<br>OSTCBPrioTbl 是一个数组,用于将优先级映射到对应的任务控制块。<br>OSRdyTbl 和 OSRdyGrp 分别表示就绪表和就绪组,它们用于跟踪哪些优先级的任务已经就绪。<br>OSMapTbl 是一个映射表,用于将优先级映射到就绪表和就绪组中的位。 <br>OS_ENTER_CRITICAL 和 OS_EXIT_CRITICAL 的实现依赖于禁用中断、使用原子操作或获取互斥锁等同步机制。
UBYTE OSChangePrio(UBYTE newp)<br>{<br> UBYTE oldp;<br><br> OS_ENTER_CRITICAL();<br> if (OSTCBPrioTbl[newp] != (void *)0)<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_PRIO_EXIST);<br> }<br> else<br> {<br> oldp = OSTCBCur->OSTCBPrio;<br> if ((OSRdyTbl[oldp >> 3] &= ~OSMapTbl[oldp & 0x07]) == 0)<br> {<br> OSRdyGrp &= ~OSMapTbl[oldp >> 3];<br> }<br> OSRdyGrp |= OSMapTbl[newp >> 3];<br> OSRdyTbl[newp >> 3] |= OSMapTbl[newp & 0x07];<br> OSTCBCur->OSTCBPrio = newp;<br> OSTCBPrioTbl[newp] = OSTCBCur;<br> OSTCBPrioTbl[oldp] = (void *)0;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> return (OS_NO_ERR);<br> }<br>}
void <font color="#ec7270">OSTaskDelete</font>(void)<br>删除一个任务。
<b>1. 进入临界区:</b><br>OS_ENTER_CRITICAL();:进入临界区以确保在删除任务时不会被其他任务或中断打断。<br><b>2. 获取当前任务的优先级:</b><br>p = OSTCBCur->OSTCBPrio;:从当前任务的任务控制块(TCB)中获取优先级。<br><b>3. 从优先级表中删除任务:</b><br>OSTCBPrioTbl[p] = (OS_TCB *)0;:将优先级表中的对应条目设置为空,表示该优先级不再与任何任务关联。<br><b>4. 更新就绪表和就绪组:</b><br>通过位操作清除当前任务在就绪表和就绪组中的标记。如果清除后对应的就绪表项为0,则还需要清除就绪组中的对应位。<br><b>5. 从任务链表中删除当前任务:</b><br>如果当前任务没有前一个任务(OSTCBCur->OSTCBPrev == (OS_TCB *)0),则它必须是任务链表的头节点。此时,将头节点更新为当前任务的下一个任务,并将下一个任务的 OSTCBPrev 设置为空。<br>如果当前任务有前一个任务,则需要更新前一个任务和下一个任务之间的链接,以绕过当前任务。<br><b>6. 将当前任务添加到空闲任务链表:</b><br>将当前任务的 OSTCBNext 指向当前的空闲任务链表头(OSTCBFreeList),然后将空闲任务链表头更新为当前任务。这样,当前任务就变成了可用的空闲任务,可以在将来被重新分配。<br><b>7. 退出临界区:</b><br>OS_EXIT_CRITICAL();:退出临界区以允许其他任务或中断继续执行。<br><b>8. 重新调度:</b><br>OSSched();:调用任务调度器以检查是否有其他任务已经就绪,并可以运行。由于当前任务已被删除,任务调度器将选择一个新的最高优先级的就绪任务来运行。
void OSTaskDelete(void)<br>{<br> UBYTE p;<br><br> OS_ENTER_CRITICAL();<br> p = OSTCBCur->OSTCBPrio;<br> OSTCBPrioTbl[p] = (OS_TCB *)0;<br> if ((OSRdyTbl[p >> 3] &= ~OSMapTbl[p & 0x07]) == 0)<br> {<br> OSRdyGrp &= ~OSMapTbl[p >> 3];<br> }<br> if (OSTCBCur->OSTCBPrev == (OS_TCB *)0)<br> {<br> OSTCBCur->OSTCBNext->OSTCBPrev = (OS_TCB *)0;<br> OSTCBList = OSTCBCur->OSTCBNext; /* Rev. A, This line was missing */<br> }<br> else<br> {<br> OSTCBCur->OSTCBPrev->OSTCBNext = OSTCBCur->OSTCBNext;<br> OSTCBCur->OSTCBNext->OSTCBPrev = OSTCBCur->OSTCBPrev;<br> }<br> OSTCBCur->OSTCBNext = OSTCBFreeList;<br> OSTCBFreeList = OSTCBCur;<br> OS_EXIT_CRITICAL();<br> OSSched();<br>}
UBYTE <font color="#ec7270">OSSemInit</font>(OS_SEM *psem, WORD cnt)<br>初始化一个信号量的数据结构。
<b>1. 检查初始计数:</b><br>if (cnt >= 0):检查提供的初始计数是否非负。如果是负数,则初始化失败。<br><b>2. 初始化信号量结构体:</b><br>psem->OSSemCnt = cnt;: 将信号量的当前计数设置为提供的初始计数。<br>psem->OSSemGrp = 0x00;: 初始化信号量的就绪组为0。就绪组通常用于跟踪哪些任务或线程正在等待信号量。<br>psem->OSSemTbl[...] = 0x00;: 初始化信号量的就绪表为0。就绪表是一个位数组,用于表示哪些任务或线程已经等待信号量。这里初始化了8个字节(64位)。<br><b>3. 返回结果:</b><br>如果初始计数非负且信号量成功初始化,则返回无错误代码(OS_NO_ERR)。<br>如果初始计数为负数,则返回信号量错误代码(OS_SEM_ERR)。<br><br>*在临界区的操作
UBYTE OSSemInit(OS_SEM *psem, WORD cnt)<br>{<br> OS_ENTER_CRITICAL();<br> if (cnt >= 0)<br> {<br> psem->OSSemCnt = cnt;<br> psem->OSSemGrp = 0x00;<br> psem->OSSemTbl[0] = 0x00;<br> psem->OSSemTbl[1] = 0x00;<br> psem->OSSemTbl[2] = 0x00;<br> psem->OSSemTbl[3] = 0x00;<br> psem->OSSemTbl[4] = 0x00;<br> psem->OSSemTbl[5] = 0x00;<br> psem->OSSemTbl[6] = 0x00;<br> psem->OSSemTbl[7] = 0x00;<br> OS_EXIT_CRITICAL();<br> return (OS_NO_ERR);<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_SEM_ERR);<br> }<br>}
UBYTE <font color="#ec7270">OSSemPend</font>(OS_SEM *psem, UWORD timeout)<br>等待(pend)一个信号量 wait()
<b>1. 函数定义:</b><br>UBYTE OSSemPend(OS_SEM *psem, UWORD timeout):<br>函数返回一个 UBYTE 类型的错误代码,并接受一个指向信号量结构体的指针 psem 和一个超时时间 timeout作为参数。<br><b>2. 进入临界区:</b><br><b>3. 尝试获取信号量</b>:<br>if (psem->OSSemCnt-- > 0):<br>如果信号量的当前计数大于0,则减少计数并立即返回无错误代码(OS_NO_ERR),表示成功获取信号量。<br><b>4. 任务挂起:</b><br>如果信号量的计数为0,则需要将当前任务挂起以等待信号量。<br><b><font color="#ec7270">OSTCBCur->OSTCBStat |= OS_STAT_SEM;:</font></b>设置当前任务的状态为等待信号量。<br><b><font color="#ec7270">OSTCBCur->OSTCBDly = timeout;:</font></b>设置当前任务的延迟(超时)时间。<br><b>5. 更新就绪表和就绪组:</b><br>通过位操作清除当前任务在就绪表和就绪组中的标记,并将其添加到信号量的等待表中。<br><b>6. 退出临界区并调度:</b><br>OS_EXIT_CRITICAL();:退出临界区。<br>OSSched();:调用任务调度器以选择另一个就绪的任务来运行。由于当前任务被挂起,调度器将选择一个具有最高优先级的就绪任务。<br><b>7. 重新进入临界区并检查任务状态:</b><br>OS_ENTER_CRITICAL();:重新进入临界区以检查当前任务的状态。<br>如果当前任务的状态仍然是等待信号量(OS_STAT_SEM),则检查是否发生了超时。<br>如果超时发生,则清除当前任务在信号量等待表中的标记,并将其状态设置为就绪(OS_STAT_RDY)。<br>返回超时错误代码(OS_TIMEOUT)。<br>如果当前任务的状态不再是等待信号量,这意味着它在等待期间被信号量唤醒(即信号量变得可用),因此可以退出临界区并返回无错误代码(OS_NO_ERR)。
UBYTE OSSemPend(OS_SEM *psem, UWORD timeout)<br>{<br> UBYTE x, y, bitx, bity;<br><br> OS_ENTER_CRITICAL();<br> if (psem->OSSemCnt-- > 0)<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_NO_ERR);<br> }<br> else<br> {<br> OSTCBCur->OSTCBStat |= OS_STAT_SEM;<br> OSTCBCur->OSTCBDly = timeout;<br> y = OSTCBCur->OSTCBPrio >> 3;<br> x = OSTCBCur->OSTCBPrio & 0x07;<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((OSRdyTbl[y] &= ~bitx) == 0)<br> OSRdyGrp &= ~bity;<br> psem->OSSemTbl[y] |= bitx;<br> psem->OSSemGrp |= bity;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> OS_ENTER_CRITICAL();<br> if (OSTCBCur->OSTCBStat & OS_STAT_SEM)<br> {<br> if ((psem->OSSemTbl[y] &= ~bitx) == 0)<br> {<br> psem->OSSemGrp &= ~bity;<br> }<br> OSTCBCur->OSTCBStat = OS_STAT_RDY;<br> OS_EXIT_CRITICAL();<br> return (OS_TIMEOUT);<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_NO_ERR);<br> }<br> }<br>}
UBYTE <font color="#ec7270">OSSemPost</font>(OS_SEM *psem)<br>释放(post)一个信号量的函数 signal()
<b>1. 函数定义:</b><br>UBYTE OSSemPost(OS_SEM *psem):函数返回一个 UBYTE 类型的错误代码,并接受一个指向信号量结构体的指针 psem 作为参数。<br><b>2. 进入临界区:</b><br>OS_ENTER_CRITICAL();:进入临界区以确保在修改信号量状态和任务状态时不会被其他任务或中断打断。<br><b>3. 检查并更新信号量计数:</b><br>if (psem->OSSemCnt < 32767):首先检查信号量的当前计数是否小于32767(这是为了防止计数溢出)。<br>if (psem->OSSemCnt++ >= 0):然后增加信号量的计数,并检查增加后的计数是否非负。这个条件始终为真,因为前面的检查确保了计数小于32767,所以增加1后不会变成负数。<br><font color="#a23c73">**这里的条件判断似乎是多余的,因为无论计数是否非负,后续的代码都需要执行。</font><br><b>4. 处理等待的任务:</b><br>如果信号量的等待组(OSSemGrp)不为0,表示有任务在等待该信号量。<br>通过查找等待表和就绪组来确定哪个任务在等待信号量,并清除其在等待表中的标记。<br>如果清除标记后,该优先级的等待表为空,则还需要清除对应的就绪组标记。<br>通过计算得到等待任务的优先级,并将其从等待信号量状态更改为就绪状态,同时清除其延迟时间。<br>更新就绪组和就绪表,以反映任务现在已就绪。<br><b>5. 退出临界区并调度:</b><br>OS_EXIT_CRITICAL();:退出临界区。<br>OSSched();:调用任务调度器以选择另一个就绪的任务来运行。由于可能有任务被唤醒,调度器将选择一个具有最高优先级的就绪任务。<br><b>6. 返回结果</b>:<br>如果信号量计数成功增加且没有溢出,则返回无错误代码(OS_NO_ERR)。<br>如果信号量计数达到或超过32767,则返回信号量溢出错误代码(OS_SEM_OVF)。
UBYTE OSSemPost(OS_SEM *psem)<br>{<br> UBYTE x, y, bitx, bity, p;<br><br> OS_ENTER_CRITICAL();<br> if (psem->OSSemCnt < 32767)<br> {<br> if (psem->OSSemCnt++ >= 0)<br> {<br> OS_EXIT_CRITICAL();<br> }<br> else<br> {<br> if (psem->OSSemGrp != 0)<br> { /* Rev. A, This line was missing */<br> y = OSUnMapTbl[psem->OSSemGrp];<br> x = OSUnMapTbl[psem->OSSemTbl[y]];<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((psem->OSSemTbl[y] &= ~bitx) == 0)<br> {<br> psem->OSSemGrp &= ~bity;<br> }<br> p = (y << 3) + x;<br> OSTCBPrioTbl[p]->OSTCBDly = 0;<br> OSTCBPrioTbl[p]->OSTCBStat &= ~OS_STAT_SEM;<br> OSRdyGrp |= bity;<br> OSRdyTbl[y] |= bitx;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> }<br> }<br> return (OS_NO_ERR);<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_SEM_OVF);<br> }<br>}
UBYTE <font color="#ec7270">OSMboxInit</font>(OS_MBOX *pmbox, void *msg)<br>初始化一个邮箱。
<b>初始化消息邮箱:</b><br><font color="#ec7270">pmbox->OSMboxMsg = msg;:</font><br>将传入的消息指针 msg 赋值给消息邮箱结构体的 OSMboxMsg 成员,这是邮箱当前存储的消息。<br><br><font color="#ec7270">pmbox->OSMboxGrp = 0x00;:</font><br>将消息邮箱的就绪组(OSMboxGrp)初始化为0。就绪组是一个用于表示哪些优先级的任务在等待该消息邮箱的位掩码。<br><br><font color="#ec7270">pmbox->OSMboxTbl[...] = 0x00;:</font><br>将消息邮箱的等待表(OSMboxTbl)的所有元素初始化为0。等待表是一个数组,用于记录哪些优先级的任务在等待该消息邮箱的具体位。<br><br><font color="#a23c73">**消息邮箱通常用于在任务之间传递简单的数据或消息。与信号量不同,消息邮箱可以传递实际的数据内容,而不仅仅是信号或事件。</font>
UBYTE OSMboxInit(OS_MBOX *pmbox, void *msg)<br>{<br> OS_ENTER_CRITICAL();<br> pmbox->OSMboxMsg = msg;<br> pmbox->OSMboxGrp = 0x00;<br> pmbox->OSMboxTbl[0] = 0x00;<br> pmbox->OSMboxTbl[1] = 0x00;<br> pmbox->OSMboxTbl[2] = 0x00;<br> pmbox->OSMboxTbl[3] = 0x00;<br> pmbox->OSMboxTbl[4] = 0x00;<br> pmbox->OSMboxTbl[5] = 0x00;<br> pmbox->OSMboxTbl[6] = 0x00;<br> pmbox->OSMboxTbl[7] = 0x00;<br> OS_EXIT_CRITICAL();<br> return (OS_NO_ERR);<br>}
void *<font color="#ec7270">OSMboxPend</font>(OS_MBOX *pmbox, UWORD timeout, UBYTE *err)<br>从邮箱接收消息。
<b>1. 函数定义:</b><br>void *OSMboxPend(OS_MBOX *pmbox, UWORD timeout, UBYTE *err):<br>函数返回一个指向消息的指针,并接受三个参数:一个指向消息邮箱结构体的指针 pmbox,一个超时值 timeout(以系统时钟滴答数表示),以及一个指向错误代码的指针 err。<b><br>2. 进入临界区:</b><br><b>3. 检查消息邮箱</b>:<br>如果消息邮箱中有消息(pmbox->OSMboxMsg != (void *)0),则立即获取消息,将消息邮箱的消息指针设置为空,退出临界区,并返回无错误代码(OS_NO_ERR)。<br><b>4. 挂起当前任务</b>:<br>如果消息邮箱中没有消息,则将当前任务的状态设置为等待消息(OS_STAT_MBOX),并设置超时值。<br>计算当前任务的优先级在就绪表和就绪组中的位置,并清除其在就绪表中的标记。如果清除后该优先级的就绪表为空,则还需要清除对应的就绪组标记。<br>将当前任务的优先级添加到消息邮箱的等待表中,并设置对应的就绪组标记。<br><b>5. 退出临界区并调度:</b><br>OS_EXIT_CRITICAL();:退出临界区。<br>OSSched();:调用任务调度器以选择另一个就绪的任务来运行。由于当前任务被挂起等待消息,调度器将选择一个具有最高优先级的就绪任务。<br><b>6. 重新进入临界区并检查任务状态:</b><br>OS_ENTER_CRITICAL();: 重新进入临界区以检查当前任务的状态。<br>如果当前任务仍然处于等待消息的状态(OSTCBCur->OSTCBStat & OS_STAT_MBOX),则表示超时发生或消息尚未到达。此时,清除任务在消息邮箱等待表中的标记,并根据需要清除就绪组标记。将任务状态设置为就绪(OS_STAT_RDY),返回空消息指针,并设置超时错误代码(OS_TIMEOUT)。<br><br>如果当前任务的状态不再是等待消息,则表示在调度期间有消息到达。此时,获取消息,将消息邮箱的消息指针设置为空,退出临界区,并返回无错误代码(OS_NO_ERR)。<br><b>7. 返回消息:</b><br>函数返回指向消息的指针(如果有消息到达)或空指针(如果超时发生)
void *OSMboxPend(OS_MBOX *pmbox, UWORD timeout, UBYTE *err)<br>{<br> UBYTE x, y, bitx, bity;<br> void *msg;<br><br> OS_ENTER_CRITICAL();<br> if ((msg = pmbox->OSMboxMsg) != (void *)0)<br> {<br> pmbox->OSMboxMsg = (void *)0;<br> OS_EXIT_CRITICAL();<br> *err = OS_NO_ERR;<br> }<br> else<br> {<br> OSTCBCur->OSTCBStat |= OS_STAT_MBOX;<br> OSTCBCur->OSTCBDly = timeout;<br> y = OSTCBCur->OSTCBPrio >> 3;<br> x = OSTCBCur->OSTCBPrio & 0x07;<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((OSRdyTbl[y] &= ~bitx) == 0)<br> {<br> OSRdyGrp &= ~bity;<br> }<br> pmbox->OSMboxTbl[y] |= bitx;<br> pmbox->OSMboxGrp |= bity;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> OS_ENTER_CRITICAL();<br> if (OSTCBCur->OSTCBStat & OS_STAT_MBOX)<br> {<br> if ((pmbox->OSMboxTbl[y] &= ~bitx) == 0)<br> {<br> pmbox->OSMboxGrp &= ~bity;<br> }<br> OSTCBCur->OSTCBStat = OS_STAT_RDY;<br> msg = (void *)0;<br> OS_EXIT_CRITICAL();<br> *err = OS_TIMEOUT;<br> }<br> else<br> {<br> msg = pmbox->OSMboxMsg;<br> pmbox->OSMboxMsg = (void *)0;<br> OS_EXIT_CRITICAL();<br> *err = OS_NO_ERR;<br> }<br> }<br> return (msg);<br>}
UBYTE <font color="#ec7270">OSMboxPost</font>(OS_MBOX *pmbox, void *msg)<br>向消息邮箱发送消息。
<b>1. 参数说明:</b><br>OS_MBOX *pmbox:指向一个消息箱控制块的指针,该控制块包含了消息箱的状态信息和相关的消息。<br>void *msg:指向要发送到消息箱的消息的指针。<br><br><b>2. 函数开始:</b><br>使用 OS_ENTER_CRITICAL(); 进入临界区,保护共享资源免受并发访问的干扰。<br><br><b>3. 检查消息箱是否已满:<br></b>如果 pmbox->OSMboxMsg 不等于 (void *)0,即消息箱中已经有一个消息,则函数通过 OS_EXIT_CRITICAL(); <br>退出临界区,并返回 OS_MBOX_FULL 表示消息箱已满。<br><br><b>4. 存储消息:</b><br>如果消息箱为空,将 msg 赋值给 pmbox->OSMboxMsg,准备存储消息。<br><br><b>5. 处理消息箱关联的任务组:</b><br> 如果 pmbox->OSMboxGrp 不为0,表示有任务组与这个消息箱关联。<br> 使用 OSUnMapTbl 和 OSMapTbl 这两个映射表来转换任务组和任务表索引。<br> 清除相应的位(bitx)在任务表(pmbox->OSMboxTbl[y])中,如果这导致整个任务组的该任务表索引变为0,则也清除任务组中的相应位(bity)。<br> 通过计算 p = (y << 3) + x; 来确定关联任务的优先级。<br> 清除该任务的 OS_STAT_MBOX 状态位,表示消息已被处理。<br> 将任务的延迟时间设置为0。<br> 更新就绪组(OSRdyGrp)和任务表(OSRdyTbl[y]),使关联的任务变为就绪状态。<br><br><b>6. 调度:</b><br>调用 OSSched(); 进行任务调度,这可能会触发一次任务切换,以运行因接收到消息而变为就绪的任务。<br><br><b>7. 返回:</b><br>函数返回 OS_NO_ERR 表示消息已成功发送到消息箱。
UBYTE OSMboxPost(OS_MBOX *pmbox, void *msg)<br>{<br> UBYTE x, y, bitx, bity, p;<br><br> OS_ENTER_CRITICAL();<br> if (pmbox->OSMboxMsg != (void *)0)<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_MBOX_FULL);<br> }<br> else<br> {<br> pmbox->OSMboxMsg = msg;<br> if (pmbox->OSMboxGrp != 0)<br> { /* Rev. A, This line was missing */<br> y = OSUnMapTbl[pmbox->OSMboxGrp];<br> x = OSUnMapTbl[pmbox->OSMboxTbl[y]];<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((pmbox->OSMboxTbl[y] &= ~bitx) == 0)<br> {<br> pmbox->OSMboxGrp &= ~bity;<br> }<br> p = (y << 3) + x;<br> OSTCBPrioTbl[p]->OSTCBStat &= ~OS_STAT_MBOX;<br> OSTCBPrioTbl[p]->OSTCBDly = 0;<br> OSRdyGrp |= bity;<br> OSRdyTbl[y] |= bitx;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> }<br> return (OS_NO_ERR);<br> }<br>}
UBYTE <font color="#ec7270">OSQInit</font>(OS_Q *pq, void **start, UBYTE size)<br>初始化一个消息队列。
<b>1. 参数说明:</b><br>OS_Q *pq: 指向队列控制块的指针,该控制块包含了队列的状态信息和指向队列存储空间的指针。<br>void **start: 指向队列存储空间起始地址的指针的指针。这里使用指针的指针是因为队列的存储空间可能是一个动态分配的数组,而我们需要一个能够修改这个数组起始地址的指针(即,我们需要一个能够“指向指针”的指针,以便在函数内部改变它指向的地址)。然而,在实际应用中,这个参数通常被传递为一个已经分配好的数组的指针的地址,因此在这个函数内部,start 主要被用作一个普通的指针来访问队列的存储空间。<br>UBYTE size:队列能够存储的元素数量。<br><b>2. 函数开始:</b><br>使用 OS_ENTER_CRITICAL(); 进入临界区。<br><b>3. 初始化队列控制块:</b><br>pq->OSQStart:设置为队列存储空间的起始地址(即 start 指向的地址)。<br>pq->OSQEnd: 设置为队列存储空间的结束地址之后的一个位置(即 &start[size],这里 start 被当作一个指向队列元素类型的指针来处理,因此 &start[size] 指向的是队列存储空间之后的第一个位置)。<br>pq->OSQIn 和 pq->OSQOut:都设置为队列存储空间的起始地址,表示队列为空,没有元素被加入,也没有元素被取出。<br>pq->OSQSize:设置为队列能够存储的元素数量。<br>pq->OSQEntries:设置为0,表示队列当前为空,没有存储任何元素。<br>pq->OSQGrp 和 pq->OSQTbl[0-7]:这些字段用于与队列相关联的任务组的就绪状态管理。在这个初始化函数中,它们都被设置为0,表示没有任务组与队列相关联。<br><b>4. 退出临界区:</b><br>使用 OS_EXIT_CRITICAL(); <br><b>5. 返回:</b><br>函数返回 OS_NO_ERR,表示队列已成功初始化。
UBYTE OSQInit(OS_Q *pq, void **start, UBYTE size)<br>{<br> OS_ENTER_CRITICAL();<br> pq->OSQStart = start;<br> pq->OSQEnd = &start[size];<br> pq->OSQIn = start;<br> pq->OSQOut = start;<br> pq->OSQSize = size;<br> pq->OSQEntries = 0;<br> pq->OSQGrp = 0x00;<br> pq->OSQTbl[0] = 0x00;<br> pq->OSQTbl[1] = 0x00;<br> pq->OSQTbl[2] = 0x00;<br> pq->OSQTbl[3] = 0x00;<br> pq->OSQTbl[4] = 0x00;<br> pq->OSQTbl[5] = 0x00;<br> pq->OSQTbl[6] = 0x00;<br> pq->OSQTbl[7] = 0x00;<br> OS_EXIT_CRITICAL();<br> return (OS_NO_ERR);<br>}
void *<font color="#ec7270">OSQPend</font>(OS_Q *pq, UWORD timeout, UBYTE *err)<br>从消息队列中等待(pend)消息的函数 wait()
<b>进入临界区:</b><br>使用OS_ENTER_CRITICAL()。<br><br><b>检查队列是否为空:</b><br> 如果pq->OSQEntries(队列中的消息数)不为0,则队列中有消息可用。<br> 从pq->OSQOut(队列输出指针)指向的位置检索消息,并将其存储在局部变量msg中。<br> 更新pq->OSQOut以指向下一个消息的位置。<br> 如果pq->OSQOut等于pq->OSQEnd(队列末尾指针),则将其重置为pq->OSQStart(队列起始指针),实现队列的循环。<br> 递减pq->OSQEntries以反映队列中消息数的减少。<br> 退出临界区。<br> 设置*err为OS_NO_ERR,表示没有错误发生。<br> 返回检索到的消息。<br><b><br>队列为空时的处理:</b><br> 将当前任务(OSTCBCur)的状态设置为等待队列(OS_STAT_Q)。<br> 设置当前任务的延迟时间(OSTCBCur->OSTCBDly)为timeout。<br> 计算当前任务优先级对应的就绪表和就绪组索引(y和x),并使用OSMapTbl找到对应的位掩码(bity和bitx)。<br> 从全局就绪表(OSRdyTbl)和就绪组(OSRdyGrp)中移除当前任务。<br> 将当前任务与队列相关联,通过修改pq->OSQTbl和pq->OSQGrp。<br> 退出临界区。<br> 调用OSSched()函数重新调度任务。<br> 再次进入临界区以检查当前任务的状态。<br> 如果当前任务仍然处于等待队列状态,表示超时发生。<br> 从队列的等待表中移除当前任务。<br> 将当前任务的状态设置为就绪(OS_STAT_RDY)。<br> 将msg设置为NULL((void *)0),表示没有检索到消息。<br> 设置*err为OS_TIMEOUT,表示超时错误。<br> 退出临界区并返回NULL。<br> 如果当前任务不再处于等待队列状态,表示已经成功从队列中检索到消息。<br> 重复之前的步骤来检索消息、更新指针和计数器。<br> 设置*err为OS_NO_ERR。<br> 退出临界区并返回检索到的消息。
void *OSQPend(OS_Q *pq, UWORD timeout, UBYTE *err)<br>{<br> UBYTE x, y, bitx, bity;<br> void *msg;<br><br> OS_ENTER_CRITICAL();<br> if (pq->OSQEntries != 0)<br> {<br> msg = *pq->OSQOut++;<br> pq->OSQEntries--;<br> if (pq->OSQOut == pq->OSQEnd)<br> {<br> pq->OSQOut = pq->OSQStart;<br> }<br> OS_EXIT_CRITICAL();<br> *err = OS_NO_ERR;<br> }<br> else<br> {<br> OSTCBCur->OSTCBStat |= OS_STAT_Q;<br> OSTCBCur->OSTCBDly = timeout;<br> y = OSTCBCur->OSTCBPrio >> 3;<br> x = OSTCBCur->OSTCBPrio & 0x07;<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((OSRdyTbl[y] &= ~bitx) == 0)<br> {<br> OSRdyGrp &= ~bity;<br> }<br> pq->OSQTbl[y] |= bitx;<br> pq->OSQGrp |= bity;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> OS_ENTER_CRITICAL();<br> if (OSTCBCur->OSTCBStat & OS_STAT_Q)<br> {<br> if ((pq->OSQTbl[y] &= ~bitx) == 0)<br> {<br> pq->OSQGrp &= ~bity;<br> }<br> OSTCBCur->OSTCBStat = OS_STAT_RDY;<br> msg = (void *)0;<br> OS_EXIT_CRITICAL();<br> *err = OS_TIMEOUT;<br> }<br> else<br> {<br> msg = *pq->OSQOut++;<br> pq->OSQEntries--;<br> if (pq->OSQOut == pq->OSQEnd)<br> {<br> pq->OSQOut = pq->OSQStart;<br> }<br> OS_EXIT_CRITICAL();<br> *err = OS_NO_ERR;<br> }<br> }<br> return (msg);<br>}
UBYTE <font color="#ec7270">OSQPost</font>(OS_Q *pq, void *msg)<br>向消息队列发送消息。
<b>1. UBYTE OSQPost(OS_Q *pq, void *msg);<br></b>UBYTE:一个无符号字节类型,通常用于表示小的整数值或状态码。<br>OS_Q *pq:指向队列控制块(Queue Control Block, QCB)的指针,该控制块包含了队列的状态信息和指针。<br>void *msg:指向要发送到队列的消息的指针。<br><br><b>2. 返回值</b><br>OS_NO_ERR:表示消息成功发送到队列。<br>OS_Q_FULL:表示队列已满,无法发送消息。<br><br><b>3. 函数执行流程</b><br><i>(1) 进入临界区:</i><br>OS_ENTER_CRITICAL();保护队列的访问,防止并发操作导致的冲突。<br><br>(2) <i>检查队列是否已满:</i><br>if (pq->OSQEntries >= pq->OSQSize)<br>{<br> OS_EXIT_CRITICAL();<br> return (OS_Q_FULL);<br>}<br>如果队列中的消息数(pq->OSQEntries)已经达到或超过了队列的容量(pq->OSQSize),则队列已满,函数将退出临界区并返回OS_Q_FULL。<br><br>(3)<i>将消息发送到队列:</i><br>*pq->OSQIn++ = msg;<br>pq->OSQEntries++;<br>if (pq->OSQIn == pq->OSQEnd)<br>{<br> pq->OSQIn = pq->OSQStart;<br>}<br>将消息msg存储在pq->OSQIn指向的位置,然后递增pq->OSQIn以指向下一个存储位置。如果pq->OSQIn达到了队列的末尾(pq->OSQEnd),则将其重置为队列的起始位置(pq->OSQStart),实现队列的环绕。<br><br>(4)<i>处理与队列相关联的任务:</i><br>如果pq->OSQGrp不为0,表示有任务组与队列相关联。<br>通过OSUnMapTbl和OSMapTbl这两个映射表,找到与队列相关联的具体任务。<br>清除该任务在任务组中的等待队列标志位。<br>将该任务设置为就绪状态,并更新就绪组和就绪表。<br><br>(5)<i>退出临界区:</i><br>在处理完与队列相关联的任务或队列为空(没有任务组与之关联)的情况下,退出临界区。<br><br>(6)<i>返回成功:</i><br>函数返回OS_NO_ERR,表示消息成功发送到队列。
UBYTE OSQPost(OS_Q *pq, void *msg)<br>{<br> UBYTE x, y, bitx, bity, p;<br><br> OS_ENTER_CRITICAL();<br> if (pq->OSQEntries >= pq->OSQSize)<br> {<br> OS_EXIT_CRITICAL();<br> return (OS_Q_FULL);<br> }<br> else<br> {<br> *pq->OSQIn++ = msg;<br> pq->OSQEntries++;<br> if (pq->OSQIn == pq->OSQEnd)<br> {<br> pq->OSQIn = pq->OSQStart;<br> }<br> if (pq->OSQGrp != 0)<br> { /* Rev. A, This line was missing */<br> y = OSUnMapTbl[pq->OSQGrp];<br> x = OSUnMapTbl[pq->OSQTbl[y]];<br> bity = OSMapTbl[y];<br> bitx = OSMapTbl[x];<br> if ((pq->OSQTbl[y] &= ~bitx) == 0)<br> {<br> pq->OSQGrp &= ~bity;<br> }<br> p = (y << 3) + x;<br> OSTCBPrioTbl[p]->OSTCBStat &= ~OS_STAT_Q;<br> OSTCBPrioTbl[p]->OSTCBDly = 0;<br> OSRdyGrp |= bity;<br> OSRdyTbl[y] |= bitx;<br> OS_EXIT_CRITICAL();<br> OSSched();<br> }<br> else<br> {<br> OS_EXIT_CRITICAL();<br> }<br> return (OS_NO_ERR);<br> }<br>}<br>
0 条评论
下一页