历史上的今天
返回首页

历史上的今天

今天是: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

推荐阅读

史海拾趣

Dae Ryung Electronic Co Ltd公司的发展小趣事

在快速发展的过程中,Dae Ryung Electronic Co Ltd公司始终注重产品质量和品牌建设。公司建立了严格的质量管理体系,从原材料采购到产品生产、检测等各个环节都进行严格把关。同时,公司还注重品牌形象的塑造和宣传,通过广告宣传、公关活动等方式提升品牌知名度和美誉度。这些努力使得公司的产品在市场上获得了良好的口碑和认可。

G24 Innovations公司的发展小趣事

进入21世纪第二个十年,电子行业迎来了前所未有的变革。新技术、新产品的不断涌现,给传统企业带来了巨大挑战。远阳公司敏锐地意识到这一点,迅速启动了转型升级战略。公司加大了对新技术、新工艺的研发投入,不断提升产品的技术含量和附加值。同时,远阳还积极探索智能制造、绿色生产等新模式,推动企业向高质量发展迈进。这一系列的举措,使远阳在激烈的市场竞争中保持了领先地位。

Global Connector Technology公司的发展小趣事
确保选用的继电器适用于当前的电动机和电源系统。
Deltron公司的发展小趣事

Deltron公司成立于XXXX年,初期以生产基础电子元器件为主。创始人XXX凭借其对电子技术的深刻理解和对市场趋势的敏锐洞察,带领团队进行了一系列技术创新。通过引进先进的生产设备和研发技术,Deltron成功开发出了一系列具有竞争力的产品,逐渐在电子行业中崭露头角。

Filtran Ltd公司的发展小趣事

为了寻找新的增长点,Filtran Ltd开始探索跨界合作的可能性。公司与一家领先的物联网解决方案提供商建立了战略合作关系,共同开发面向智能家居市场的无线连接模块。这一合作不仅将Filtran Ltd的滤波器技术应用于新的领域,还为公司带来了全新的市场机遇。通过跨界合作,Filtran Ltd成功实现了从单一产品供应商向综合解决方案提供商的转变。

Dionics Inc公司的发展小趣事

进入21世纪后,随着物联网、人工智能等技术的快速发展,电子行业也面临着深刻的变革。Dionics Inc敏锐地捕捉到了这一趋势,并开始积极布局相关领域。通过持续的技术创新和产品升级,公司成功推出了一系列具有竞争力的新产品,并在市场上取得了不俗的成绩。同时,公司还加强了与高校、科研机构的合作,共同推动电子行业的创新发展。

问答坊 | AI 解惑

再问模拟电路问题,真是第一次见过这样的电路。

看图,该图是一音频的低音加重电路中的一部份,前面已有过不少低频滤波电路,全不是这种形式,这又加一这样的电路,真不明白是啥意思。 说明:这电路中的电阻R67和R66是同一可调电阻,另串一固定电阻,在这里为了仿真全改为固定电阻了。R10也是。 ...…

查看全部问答>

有点复古有点雷人的概念摇棒手机

这个“时髦”的电话拥有一个曲柄的摇棒,还有一个滑出的数字键盘,听筒和话筒基本上移植了古老的电话形式,但是这个手机需要一个50磅重的电池提供能量,看完之后小编觉得超级的雷人啊!…

查看全部问答>

【FPGA代码】模为60 的BCD 码加法计数器

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 TAPI问题

wince6下,建立一个模拟器,BSP为DEVICEEMULATOR,加了CELLCORE组件,使用ENFORA.在调用lineopen时返回LINEERR_OPERATIONFAILED。 不知是什么原因,应该从哪方面入手来解决这个问题? 请各位大侠给予帮助和指导,谢谢。…

查看全部问答>

EVC

EVC 下的dialog based的app没有最小化方法,前面发了一个帖子,有人建议处理ACTIVE消息,但是我在ACTIVE中使用showwindow来最小化,还是不行。 我还修改了dialog的属性,加入了WS_MINIMIZEBOX,dialog出现了一个_图标,但是点击之后不能实现最小化 ...…

查看全部问答>

在evc下编程,link的时候出错了

tinyxml.lib(tinystr.obj) : module machine type \'MIPS\' conflicts with target machine type \'X86\',怎么办呢,谢谢…

查看全部问答>

ADS编译问题

ADS编译问题: 我定义了一个中断函数   void __irq time0Up(void); 编译是报错如下:__irq FUNCTION need no argument and no return 这是为什么呢?…

查看全部问答>

关于flash:25P80的写入问题.

                                 每次写入必须擦除整页吗? 那怕我只在此页写入一个字节.然后要擦出整页的数据? 我的理解是这个样子,因为d ...…

查看全部问答>

请教香主:sd写文件总是失败,结果电脑不识别

最近在做一个录音系统,想法录音文件存在sd卡上,参考st IAR-STM32-SKMP3_player的范例,发现只有读取文件没有写入文件,按照自己的理解增加代码,搞了好几天结果总是写不成功,仿真代码运行又都正常,请香主指导一下。我的代码如下:&nb ...…

查看全部问答>

求msp430f5529的例程

开发用到5529,希望有相关例程学习交流,谢谢…

查看全部问答>