美文网首页
stm32超轻量操作系统之抢占式内核

stm32超轻量操作系统之抢占式内核

作者: 生若夏花_1ad0 | 来源:发表于2018-11-21 17:27 被阅读0次

这一章比上一章内容多了不少。第一章完成了任务切换功能,这一章在任务切换功能上增加了以下几个功能。

1.改变特权级,加入SVC异常

2.增加优先级,使得内核可以抢占。

3.增加了滴答定时器中断功能,使得同优先级的任务以时间片方式调度。

4.增加延时函数。

接下来一项一项介绍。

1. 改变特权级

第一章中,所有的程序都是运行在特权级下。在中断中时切换到内核模式,在任务中切换到线程模式(Thread),但是权限都是特权级,意味着程序对内存中所有的数据都有修改的权限。此处加入两张图,通过修改寄存器,可以在退出中断后,在任务中运行即线程模式时权限是非特权级,这样在线程模式时就不能够更改内核模式下的数据了,程序的更具有健壮性。

图一.特权级与优先级运行状态说明

更改特权级只能在特权级下更改,所以通过在PendSV最后加入红色字体部分来更改了特权级。

__ASM void PendSVPopData()

{

extern PendSVFirst;

extern currTCB;

extern nextTCB;

PRESERVE8

LDR R0,=currTCB //R0=currTCB 

LDR R1,=nextTCB //R1=nextTCB

LDR R1,[R1] //R1=*R1

STR R1,[R0] //*R0=R1 currTCB=nextTCB完成了指向新的任务TCB的工作

LDR R0,[R1] //R0=*R1 R0保存了TCB堆栈栈顶的指针

LDMIA R0!,{R4-R11} //依次弹出R4-R11,完成后R0=R0+0x20

MSR PSP,R0 //PSP=R0

ORR LR,LR,#0x04 //LR=LR|0x04,表示函数返回后使用PSP指针

//使程序运行在非特权级,设置CONTROL[0]位为1,这样内核级和用户级就分开了,即是某个线程出现了问题也不会影响内核

LDR R1,=0x3

MSR CONTROL,R1

ISB

CPSIE I

BX LR

nop

}

同时要注意在非特权级(用户级)下只有触发异常才可以进入重新进入特权级,因此额外加入了SVC异常来主动触发中断。

还记得我们上一章讲过的发起任务调度的两种方式吗?

1.产生异常。

2.systick定时器中断。

其中第一条就是SVC异常中断。SVC异常用于产生系统调用,它的优先级最高,一经调用立刻进入此异常,因此,通过调用SVC异常,OS就进入了内核态(handler模式),在此就可以进行任务切换了。这种方式发起任务调度就不必等到systick定时器中断产生再发生任务调度了。

在任务中调用SvcTaskSwitch()就可以进行一次任务切换,这是自己定义的函数。SVC具体细节可以在网上查看相关资料。

2. 增加优先级功能

优先级功能参考了FreeRTOS的设计,通过列表和列表项的方式实现。

简单来讲,为了实现这个功能,我们设置了不同的列表,如果我们设置优先级共有8个,则新建8个优先级列表priorityReadyList[8]存储处于就绪态的任务,新建一个delayedList,存储处于延时的任务,新建一个pendList,存储处于挂起态的任务。所有的任务必然存在于这三种列表中。

列表又由列表项组成,列表中的列表项以双向链表的形式组合在一起,每一个列表项又存储了一个任务的TCB,这样就可以找到相应的任务。

图二.FreeRTOS列表结构说明

在任务切换时,首先找到一个目前优先级最高的任务,OSFindMaxPriorityTask()。如果找到的这个任务跟当前任务的优先级相同,那么就指向当前任务在列表中的下一个任务,来做切换,如果找到的任务优先级更高,那么直接切换高优先级的任务

3. 滴答定时器

滴答定时器的中断时间时1ms,也是时间片的时间。同优先级的任务每个运行1ms再切换。

systemTicks是任务运行的时间,系统初始化时赋值为0。在此我们不考虑溢出的问题,因为systemTicks是一个uint32_t的值,在此最大值代表约50天,因此在这个简易系统中不考虑溢出的情况。

其中需要注意的一点是判断任务的延时是否到达。其实很简单,如果有一个任务的延时时间到了,在延时列表中删除这个任务,在就绪列表中增加这个任务。我们怎么在延时列表中找到这个任务呢?因为列表中的列表项是按照readyTime升序排列的,因此通过我们前面在列表中特意设的最后列表的最后一项可以轻易的根据previous找到列表中的第一项。更新完任务后不要忘了更新下一项延时任务将要到达的时间。

在初始话列表的过程中我们给列表最后一项赋予了一个很大的值0x3ffffffff,因此可以任务永远不会到达这个时间。在列表中添加的项都会在它的前面。

4. 增加延时函数。

在第一章,我们时通过无意义的while(i--)来达到延时的目的,但这是阻塞运算,程序会卡在这个地方,满足不了实际的需要。OS中的延时函数会发生任务的调度,这样才不会浪费资源。

OSDelay的主要功能就是把当前任务挂起,在就绪列表中删除当前的任务,把任务加入到延时的列表中,更新其将要延时的时间,然后寻找下一个优先级最高的任务,进行切换。

最后设计了几个小实验,来验证程序是否正确运行。

设计了几个实验

1.两个小灯优先级相同,验证同优先级下的时间片调度。ok

void Task1()

{

int j = 0;

    while(1)

    {

          j=20000000;

         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);

         while(j!=0)

         j--;

         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);

         j=20000000;

         while(j!=0)

         j--;

   }

}

void Task2()

{

     int i = 0;

     while(1)

     {

        i=10000000;

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);

        while(i!=0)

        i--;

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);

        i=10000000;

        while(i!=0)

        i--;

    }

}

2.两个任务优先级相同,验证延时功能是否有效 ok

void Task1()

{

     while(1)

     {

        OSDelay(2000);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);

        OSDelay(2000);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);

      }

}

void Task2()

{

      while(1)

     {

          OSDelay(1000);

          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);

          OSDelay(1000);

          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);

     }

}

3.验证优先级不同,任务一优先级高,任务二优先级低,任务一一直运行,小灯暗灭切换,可以看到任务二不运行,小灯一直亮

void Task1()

{

      int i = 0;

      while(1)

      {

           i = 20000000;

          while(i!=0)

          i--;

          //OSDelay(2000);

         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);

         i = 20000000;

         while(i!=0)

         i--;

         //OSDelay(2000);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);

     }

}

void Task2()

{

      while(1)

      {

          OSDelay(1000);

          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);

          OSDelay(1000);

          HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);

      }

}

4.任务一优先级高,任务二优先级低,任务一程序中有挂起3秒。可以看到在挂起的这段时间内任务二会切换,一但不挂起,任务二灯不会切换

void Task1()

{

     int i = 0;

     while(1)

     { 

          i = 100000000;

          while(i!=0)

          i--;

          //OSDelay(2000);

         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);

         i = 100000000;

         while(i!=0)

         i--;

         OSDelay(5000);

         HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);

      }

}

void Task2()

{

    while(1)

    {

        OSDelay(1000);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET);

       OSDelay(1000);

        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET);

    }

}

程序链接如下,实验用的是STM32F407的开发板

链接:https://pan.baidu.com/s/1my2HPG6shXB7QiwbR47Dnw

提取码:j5hw

stm32超轻量操作系统之任务调度

stm32超轻量操作系统之信号量与互斥量

相关文章

  • stm32超轻量操作系统之任务调度

    这是简单STM32 OS的第一章stm32超轻量操作系统之抢占式内核 在这个最简程序中只有两个任务交替执行,任务一...

  • stm32超轻量操作系统之抢占式内核

    这一章比上一章内容多了不少。第一章完成了任务切换功能,这一章在任务切换功能上增加了以下几个功能。 1.改变特权级,...

  • 进程管理(三)进程调度

    (一) 调度器: 触发调度(轮转): ① 非抢占式调度:进程自己发起 ② 抢占式调度:操作系统内核引起。容易引起系...

  • 1. 操作系统的实时性

    核心在于,内核是否可以无条件的被中断被抢占。 真正的实时操作系统内核是可中断可抢占的。而非实时的操作系统通常...

  • stm32超轻量操作系统之信号量与互斥量

    互斥量就是二元信号量,因此在一章里面介绍。 这一章比较简单,废话不多说,直接进入正题 信号量可以控制任务的执行顺序...

  • golang-协程 Coroutine

    轻量级 “线程”!非抢占式多任务处理,由协程主动交出控制权! 抢占式:是有操作系统主动切换线程执行非抢占式:是由我...

  • Linux内核进程调度

    [TOC] ## 多任务操作系统的两种形式: 1. 抢占式多任务操作系统 2. 协同式多任务操作系统 ## 进程分...

  • go 的并发调度(一) GMP 模型

    协程和线程的历史关系? 抢占式和协同式抢占式就是线程无法决定自己执行多久,由操作系统(或其他分配系统)来分配一个线...

  • 全新发布含昭系统2021冬至版

    含昭操作系统(Handril OS),是西安景域汉家之阁含昭科技司计划研发的,基于含元微内核的分布式全场景操作系统...

  • 8. UCOS下优先级反转问题

    1. 写在前面: 1.1. ucos的进程调度是基于抢占式的,优先级较高的任务可以抢占系统的内核。 1.2. 只是...

网友评论

      本文标题:stm32超轻量操作系统之抢占式内核

      本文链接:https://www.haomeiwen.com/subject/lpwcqqtx.html