历史上的今天
今天是:2025年01月12日(星期日)
2021年01月12日 | 一步步写STM32 OS【四】OS基本框架
2021-01-12 来源:eefocus
一、上篇回顾
上一篇文章中,我们完成了两个任务使用PendSV实现了互相切换的功能,下面我们接着其思路往下做。这次我们完成OS基本框架,即实现一个非抢占式(已经调度的进程执行完成,然后根据优先级调度等待的进程)的任务调度系统,至于抢占式的,就留给大家思考了。上次代码中Task_Switch实现了两个任务的切换,代码如下:
void Task_Switch()
{
if(g_OS_Tcb_CurP == &TCB_1)
g_OS_Tcb_HighRdyP=&TCB_2;
else
g_OS_Tcb_HighRdyP=&TCB_1;
OSCtxSw();
}
我们把要切换任务指针付给跟_OS_Tcb_HighRdyP,然后调用OSCtxSw触发PendSV异常,就实现了任务的切换。如果是多个任务,我们只需找出就绪任务中优先级最大的切换之即可。
二、添加任务调度功能
为了实现这一目标我们至少需要知道任务的状态和时间等数据。我们定义了一个任务状态枚举类型OS_TASK_STA,方便添加修改状态。在OS_TCB结构体中添加了两个成员TimeDly和State,TimeDly是为了实现OS_TimeDly,至于State与优先级一起是作为任务切换的依据。
typedef enum OS_TASK_STA
{
TASK_READY,
TASK_DELAY,
} OS_TASK_STA;
typedef struct OS_TCB
{
OS_STK *StkAddr;
OS_U32 TimeDly;
OS_TASK_STA State;
}OS_TCB,*OS_TCBP;
说到任务切换,我们必须面对临界区的问题,在一些临界的代码两端不加临界区进去和退出代码,会出现许多意想不到的问题。以下地方需要特别注意,对关键的全局变量的写操作、对任务控制块的操作等。进入临界区和退出临界区需要关闭和开启中断,我们采用uCOS中的一部分代码:
PUBLIC OS_CPU_SR_Save
PUBLIC OS_CPU_SR_Restore
OS_CPU_SR_Save
MRS R0, PRIMASK
CPSID I
BX LR
OS_CPU_SR_Restore
MSR PRIMASK, R0
BX LR
#define OS_USE_CRITICAL OS_U32 cpu_sr;
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#define OS_PendSV_Trigger() OSCtxSw()
一个OS至少要有任务表,我们可以用数组,当然也可以用链表。为了简单,我们使用数组,使用数组下表作为优先级。当然,必要的地方一定要做数组越界检查。
#define OS_TASK_MAX_NUM 32
OS_TCBP OS_TCB_TABLE[OS_TASK_MAX_NUM];
为了使OS更完整,我们定义几个全局变量,OS_TimeTick记录系统时间,g_Prio_Cur记录当前运行的任务优先级,g_Prio_HighRdy记录任务调度后就绪任务中的最高优先级。
OS_U32 OS_TimeTick;
OS_U8 g_Prio_Cur;
OS_U8 g_Prio_HighRdy;
下面三个函数与PendSV一起实现了任务的调度功能。
OS_Task_Switch函数功能:找出已就绪最高优先级的任务,并将其TCB指针赋值给g_OS_Tcb_HighRdyP,将其优先级赋值g_Prio_HighRdy。注意其中使用了临界区。
void OS_Task_Switch(void)
{
OS_S32 i;
OS_TCBP tcb_p;
OS_USE_CRITICAL
for(i=0;i tcb_p=OS_TCB_TABLE[i]; if(tcb_p == NULL) continue; if(tcb_p->State==TASK_READY) break; } OS_ENTER_CRITICAL(); g_OS_Tcb_HighRdyP=tcb_p; g_Prio_HighRdy=i; OS_EXIT_CRITICAL(); } OS_TimeDly至当前任务为延时状态,并将延时时间赋值给当前TCB的TimeDly成员,并调用OS_Task_Switch函数,然后触发PendSV进行上下文切换。OS_Task_Switch找到就绪状态中优先级最高的,并将其赋值相关全局变量,作为上下文切换的依据。 void OS_TimeDly(OS_U32 ticks) { OS_USE_CRITICAL OS_ENTER_CRITICAL(); g_OS_Tcb_CurP->State=TASK_DELAY; g_OS_Tcb_CurP->TimeDly=ticks; OS_EXIT_CRITICAL(); OS_Task_Switch(); OS_PendSV_Trigger(); } SysTick_Handler实现系统计时,并遍历任务表,任务若是延时状态,就令其延时值减一,若减完后为零,就将其置为就绪状态。 void SysTick_Handler(void) { OS_TCBP tcb_p; OS_S32 i; OS_USE_CRITICAL OS_ENTER_CRITICAL(); ++OS_TimeTick; for(i=0;i tcb_p=OS_TCB_TABLE[i]; if(tcb_p == NULL) continue; if(tcb_p->State==TASK_DELAY) { --tcb_p->TimeDly; if(tcb_p->TimeDly == 0) tcb_p->State=TASK_READY; } } OS_EXIT_CRITICAL(); } 当所有任务都没就绪怎么办?这时就需要空闲任务了,我们把它设为优先级最低的任务。WFE指令为休眠指令,当来中断时,退出休眠,然后看看有没有已就绪的任务,有则调度之,否则继续休眠,这样可以减小功耗哦。 void OS_Task_Idle(void) { while(1) { asm("WFE"); OS_Task_Switch(); OS_PendSV_Trigger(); } } 当一个任务只运行一次时(例如下面main.c的task1),结束时就会调用OS_Task_End函数,此函数会调用OS_Task_Delete函数从任务表中删除当前的任务,然后调度任务。 void OS_Task_Delete(OS_U8 prio) { if(prio >= OS_TASK_MAX_NUM) return; OS_TCB_TABLE[prio]=0; } void OS_Task_End(void) { printf("Task of Prio %d Endn",g_Prio_Cur); OS_Task_Delete(g_Prio_Cur); OS_Task_Switch(); OS_PendSV_Trigger(); } 三、OS实战 下面是完整的main.c代码: 复制代码 #include "stdio.h" #include "stm32f4xx.h" #define OS_EXCEPT_STK_SIZE 1024 #define TASK_1_STK_SIZE 128 #define TASK_2_STK_SIZE 128 #define TASK_3_STK_SIZE 128 #define TASK_IDLE_STK_SIZE 1024 #define OS_TASK_MAX_NUM 32 #define OS_TICKS_PER_SECOND 1000 #define OS_USE_CRITICAL OS_U32 cpu_sr; #define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();} #define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);} #define OS_PendSV_Trigger() OSCtxSw() typedef signed char OS_S8; typedef signed short OS_S16; typedef signed int OS_S32; typedef unsigned char OS_U8; typedef unsigned short OS_U16; typedef unsigned int OS_U32; typedef unsigned int OS_STK; typedef void (*OS_TASK)(void); typedef enum OS_TASK_STA { TASK_READY, TASK_DELAY, } OS_TASK_STA; typedef struct OS_TCB { OS_STK *StkAddr; OS_U32 TimeDly; OS_U8 State; }OS_TCB,*OS_TCBP; OS_TCBP OS_TCB_TABLE[OS_TASK_MAX_NUM]; OS_TCBP g_OS_Tcb_CurP; OS_TCBP g_OS_Tcb_HighRdyP; OS_U32 OS_TimeTick; OS_U8 g_Prio_Cur; OS_U8 g_Prio_HighRdy; static OS_STK OS_CPU_ExceptStk[OS_EXCEPT_STK_SIZE]; OS_STK *g_OS_CPU_ExceptStkBase; static OS_TCB TCB_1; static OS_TCB TCB_2; static OS_TCB TCB_3; static OS_TCB TCB_IDLE; static OS_STK TASK_1_STK[TASK_1_STK_SIZE]; static OS_STK TASK_2_STK[TASK_2_STK_SIZE]; static OS_STK TASK_3_STK[TASK_3_STK_SIZE]; static OS_STK TASK_IDLE_STK[TASK_IDLE_STK_SIZE]; extern OS_U32 SystemCoreClock; extern void OSStart_Asm(void); extern void OSCtxSw(void); extern OS_U32 OS_CPU_SR_Save(void); extern void OS_CPU_SR_Restore(OS_U32); void task_1(void); void task_2(void); void task_3(void); void OS_Task_Idle(void); void OS_TimeDly(OS_U32); void OS_Task_Switch(void); void OS_Task_Create(OS_TCB *,OS_TASK,OS_STK *,OS_U8); void OS_Task_Delete(OS_U8); void OS_Task_End(void); void OS_Init(void); void OS_Start(void); void task_1(void) { printf("[%d]Task 1 Runing!!!n",OS_TimeTick); OS_Task_Create(&TCB_2,task_2,&TASK_2_STK[TASK_2_STK_SIZE-1],5); OS_Task_Create(&TCB_3,task_3,&TASK_3_STK[TASK_3_STK_SIZE-1],7); } void task_2(void) { while(1) { printf("[%d]Task 2 Runing!!!n",OS_TimeTick); OS_TimeDly(1000); } } void task_3(void) { while(1) { printf("[%d]Task 3 Runing!!!n",OS_TimeTick); OS_TimeDly(1500); } } void OS_Task_Idle(void) { while(1) { asm("WFE"); OS_Task_Switch(); OS_PendSV_Trigger(); } } void OS_TimeDly(OS_U32 ticks) { OS_USE_CRITICAL OS_ENTER_CRITICAL(); g_OS_Tcb_CurP->State=TASK_DELAY; g_OS_Tcb_CurP->TimeDly=ticks; OS_EXIT_CRITICAL(); OS_Task_Switch(); OS_PendSV_Trigger(); } void OS_Task_Switch(void) { OS_S32 i; OS_TCBP tcb_p; OS_USE_CRITICAL for(i=0;i tcb_p=OS_TCB_TABLE[i]; if(tcb_p == NULL) continue; if(tcb_p->State==TASK_READY) break; } OS_ENTER_CRITICAL(); g_OS_Tcb_HighRdyP=tcb_p; g_Prio_HighRdy=i; OS_EXIT_CRITICAL(); } void OS_Task_Delete(OS_U8 prio) { if(prio >= OS_TASK_MAX_NUM) return; OS_TCB_TABLE[prio]=0; } void OS_Task_End(void) { printf("Task of Prio %d Endn",g_Prio_Cur); OS_Task_Delete(g_Prio_Cur); OS_Task_Switch(); OS_PendSV_Trigger(); } void OS_Task_Create(OS_TCB *tcb,OS_TASK task,OS_STK *stk,OS_U8 prio) { OS_USE_CRITICAL OS_STK *p_stk; if(prio >= OS_TASK_MAX_NUM) return; OS_ENTER_CRITICAL(); p_stk = stk; p_stk = (OS_STK *)((OS_STK)(p_stk) & 0xFFFFFFF8u); *(--p_stk) = (OS_STK)0x01000000uL; //xPSR *(--p_stk) = (OS_STK)task; // Entry Point *(--p_stk) = (OS_STK)OS_Task_End; // R14 (LR) *(--p_stk) = (OS_STK)0x12121212uL; // R12 *(--p_stk) = (OS_STK)0x03030303uL; // R3 *(--p_stk) = (OS_STK)0x02020202uL; // R2 *(--p_stk) = (OS_STK)0x01010101uL; // R1 *(--p_stk) = (OS_STK)0x00000000u; // R0 *(--p_stk) = (OS_STK)0x11111111uL; // R11 *(--p_stk) = (OS_STK)0x10101010uL; // R10 *(--p_stk) = (OS_STK)0x09090909uL; // R9 *(--p_stk) = (OS_STK)0x08080808uL; // R8 *(--p_stk) = (OS_STK)0x07070707uL; // R7 *(--p_stk) = (OS_STK)0x06060606uL; // R6 *(--p_stk) = (OS_STK)0x05050505uL; // R5
史海拾趣
|
看图,该图是一音频的低音加重电路中的一部份,前面已有过不少低频滤波电路,全不是这种形式,这又加一这样的电路,真不明白是啥意思。 说明:这电路中的电阻R67和R66是同一可调电阻,另串一固定电阻,在这里为了仿真全改为固定电阻了。R10也是。 ...… 查看全部问答> |
|
这个“时髦”的电话拥有一个曲柄的摇棒,还有一个滑出的数字键盘,听筒和话筒基本上移植了古老的电话形式,但是这个手机需要一个50磅重的电池提供能量,看完之后小编觉得超级的雷人啊!… 查看全部问答> |
|
module count60(qout,cout,data,load,cin,reset,clk);output[7:0] qout;output cout;input[7:0] data;input load,cin,clk,reset;reg[7:0] qout;always @(posedge clk) //clk 上升沿时刻计数beginif (reset) qout<=0; //同步复位else if(load) qou ...… 查看全部问答> |
|
wince6下,建立一个模拟器,BSP为DEVICEEMULATOR,加了CELLCORE组件,使用ENFORA.在调用lineopen时返回LINEERR_OPERATIONFAILED。 不知是什么原因,应该从哪方面入手来解决这个问题? 请各位大侠给予帮助和指导,谢谢。… 查看全部问答> |
|
tinyxml.lib(tinystr.obj) : module machine type \'MIPS\' conflicts with target machine type \'X86\',怎么办呢,谢谢… 查看全部问答> |
|
最近在做一个录音系统,想法录音文件存在sd卡上,参考st IAR-STM32-SKMP3_player的范例,发现只有读取文件没有写入文件,按照自己的理解增加代码,搞了好几天结果总是写不成功,仿真代码运行又都正常,请香主指导一下。我的代码如下:&nb ...… 查看全部问答> |




