您的位置 首页 基础

一步步写STM32 OS【四】OS根本结构

上一篇文章中,我们完成了两个任务使用PendSV实现了互相切换的功能,下面我们接着其思路往下做。这次我们完成OS基本框架,即实现一个非抢占式(已经调度的进程执行完成,然后根据优先级调度等待的进程)

  上一篇文章中,咱们完结了两个使命运用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 End\n",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 End\n",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

  *(–p_stk) = (OS_STK)0x04040404uL; // R4

  tcb->StkAddr=p_stk;

  tcb->TimeDly=0;

  tcb->State=TASK_READY;

  OS_TCB_TABLE[prio]=tcb;

  OS_EXIT_CRITICAL();

  }

  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();

  }

  void OS_Init(void)

  {

  int i;

  g_OS_CPU_ExceptStkBase = OS_CPU_ExceptStk + OS_EXCEPT_STK_SIZE – 1;

  asm("CPSID I");

  for(i=0;i

  OS_TCB_TABLE[i]=0;

  OS_TimeTick=0;

  OS_Task_Create(&TCB_IDLE,OS_Task_Idle,&TASK_IDLE_STK[TASK_IDLE_STK_SIZE-1],OS_TASK_MAX_NUM-1);

  }

  void OS_Start(void)

  {

  OS_Task_Switch();

  SystemCoreClockUpdate();

  SysTick_Config(SystemCoreClock/OS_TICKS_PER_SECOND);

  OSStart_Asm();

  }

  int main()

  {

  OS_Init();

  OS_Task_Create(&TCB_1,task_1,&TASK_1_STK[TASK_1_STK_SIZE-1],2);

  OS_Start();

  return 0;

  }

 

  os_port.asm改变不大,具体内容能够下载文章结尾供给的工程参阅。

  老规矩,下载调试,全速运转,调查Terminal IO窗口:

    

QQ截图20131103215553

 

  从输出来看,咱们现已完结了方针。但不确保安稳性,可能有不少Bugs。至此,能够说其实写一个OS并不难,难的是写一个安稳安全高效的OS。所以,现在仅仅走了一小步,想要完结一个老练的OS,还需求不断测验,不断优化。例如,咱们选用数组存储使命表,也能够选用链表,各有优缺点。咱们只要一个使命表,也能够分红多个表,例如就续表,等候表等等。咱们的使命调度部分运转时刻不确定,关于实时OS,这是不能够的,怎样修正呢,例如像uCOS的查找表法那样。现在咱们的体系只能创立并调度使命,还未参加其他功用,例如信号量、邮箱、行列、内存办理等。其实到了这儿,咱们完全能够发挥自己的创造力,参照本文开发自己的OS。假如今后有时刻的话,还会再写几篇文章持续完善咱们的OS。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/jichu/114978.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部