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

stm32超轻量 *** 作系统之抢占式内核,第1张

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

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} //依次d出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产生异常。

2systick定时器中断。

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

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

2 增加优先级功能

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

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

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

在任务切换时,首先找到一个目前优先级最高的任务,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的开发板

链接:>

单片机执行程序的过程,实际上就是执行我们所编制程序的过程。即逐条指令的过程。计算机每执行一条指令都可分为三个阶段进行。即取指令-----分析指令-----执行指令。

取指令的任务是:根据程序计数器PC中的值从程序存储器读出现行指令,送到指令寄存器。

分析指令阶段的任务是:将指令寄存器中的指令 *** 作码取出后进行译码,分析其指令性质。如指令要求 *** 作数,则寻找 *** 作数地址。

计算机执行程序的过程实际上就是逐条指令地重复上述 *** 作过程,直至遇到停机指令可循环等待指令。

一般计算机进行工作时,首先要通过外部设备把程序和数据通过输入接口电路和数据总线送入到存储器,然后逐条取出执行。但单片机中的程序一般事先我们都已通过写入器固化在片内或片外程序存储器中。因而一开机即可执行指令。

下面我们将举个实例来说明指令的执行过程:

开机时,程序计算器PC变为0000H。然后单片机在时序电路作用下自动进入执行程序过程。执行过程实际上就是取出指令(取

出存储器中事先存放的指令阶段)和执行指令(分析和执行指令)的循环过程。

例如执行指令:MOV A,#0E0H,其机器码为“74H E0H”,该指令的功能是把 *** 作数E0H送入累加器,

0000H单元中已存放74H,0001H单元中已存放E0H。当单片机开始运行时,首先是进入取指阶段,其次序是:

1 程序计数器的内容(这时是0000H)送到地址寄存器;

2 程序计数器的内容自动加1(变为0001H);

3 地址寄存器的内容(0000H)通过内部地址总线送到存储器,以存储器中地址译码电跟,使地址为0000H的单元被选中;

4 CPU使读控制线有效;

5 在读命令控制下被选中存储器单元的内容(此时应为74H)送到内部数据总线上,因为是取指阶段,所以该内容通过数据总线被送到指令寄存器。

至此,取指阶段完成,进入译码分析和执行指令阶段。

由于本次进入指令寄存器中的内容是74H( *** 作码),以译码器译码后单片机就会知道该指令是要将一个数送到A累加器,而该数是在这个代码的下一个存储单元。所以,执行该指令还必须把数据(E0H)从存储器中取出送到CPU,即还要在存储器中取第二个字节。其过程与取指阶段很相似,只是此时PC已为0001H。指令译码器结合时序部件,产生74H *** 作码的微 *** 作系列,使数字E0H从0001H单元取出。因为指令是要求把取得的数送到A累加器,所以取出的数字经内部数据总线进入A累加器,而不是进入指令寄存器。至此,一条指令的执行完毕。单片机中PC=0002H,PC在CPU每次向存储器取指或取数时自动加1,单片机又进入下一取指阶段。这一过程一直重复下去,直至收到暂停指令或循环等待指令暂停。CPU就是这样一条一条地执行指令,完成所有规定的功能。

以上就是关于stm32超轻量 *** 作系统之抢占式内核全部的内容,包括:stm32超轻量 *** 作系统之抢占式内核、需要stm32的c语言设计程序、关于程序是怎样在STM32里运行的,有点困惑等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/9877831.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-02
下一篇 2023-05-02

发表评论

登录后才能评论

评论列表(0条)

保存