【LiteOS】小白进阶之常用 LiteOS 任务接口与基础原理详解(一)

举报
产品人卫朋 发表于 2021/10/30 01:26:17 2021/10/30
【摘要】 LiteOS 的任务是调度执行的最小单元,主要按照优先级抢占,辅以时间片轮转的方式进行调度。 任务的操作和维护保护创建、删除、调用、挂起、恢复、切换、sleep 等。 任务间的交互可以通过消息或事件进行,同时支持使用互斥锁 MUX 或信号量 semphore来 进行活动的互斥同步。 任务可以分为不同的优先级,优先级用 0-31...

LiteOS 的任务是调度执行的最小单元,主要按照优先级抢占,辅以时间片轮转的方式进行调度。

任务的操作和维护保护创建、删除、调用、挂起、恢复、切换、sleep 等。

任务间的交互可以通过消息或事件进行,同时支持使用互斥锁 MUX 或信号量 semphore来 进行活动的互斥同步。

任务可以分为不同的优先级,优先级用 0-31 表示,其中 0 为最高优先级。

1、系统的启动

从 vendor.s 文件 __vector_table 开始运行,首先初始化 MSP 的地址,然后调用 Reset_Handler 函数,在 reset 函数中跳转 __iar_program_start,然后进入 main 函数,在 main 函数中进行了内存、任务等初始化,随后启动系统任务开始调度。

vendor.s 只是系统刚启动时运行,而对于中断向量函数表,在初始化时配置 OS_NVIC_VTOR 寄存器进行重新定位。

2、关于中断

在 CortexM4 上存在256中断号,支持多个优先级配置,16-255为可配置的IRQ,0-15依次分别为栈顶位置(MSP)、复位、NMI(不可屏蔽中断)、硬件错误、memory管理错误、总线错误、使用错误(指令或处理器状态转换)、7-10保留、SVC、调用监控(用于软件调试)、13保留、PendSV(用于任务上下文切换)、sysTick(系统时间片调度)。

为了某些特殊情况下禁止中断的响应,M4有三个特殊的寄存器PRIMASK\FAULTMASK\BASEPRI,能够在特权模式下访问;

PRIMASK可用来禁止除了NMI和HardFault以外中断;

FAULTMASK用来屏蔽除了NMI以外的中断;

BASEPRI用于禁止一些优先级比较低的中断。

3、任务优先级实现

#define LOS_PRIORITY_QUEUE_PRIORITYNUM 32

LITE_OS_SEC_BSS LOS_DL_LIST 

*g_pstLosPriorityQueueList;//g_pstLosPriorityQueueList通过malloc对应LOS_PRIORITY_QUEUE_PRIORITYNUM大小的存储块。

LiteOS实现中对每一个优先级的任务使用了一个链表,当任务需要切换时,则从g_pstLosPriorityQueueList高优先级任务队列开始查找需要执行的任务。

4、时间片轮转

关于时间片:基于Tick来处理,Tick时钟在Cortex M4环境中通过SysTick Control的相关寄存器来配置。

SysTick是基于clk来计数,所以配置SysTick时根据系统的主频以及自定义的Tick时长来计算。

在正确配置SysTick寄存器的基础上,每个Tick会产生一个中断,中断号为15。

在Tick的中断服务程序中,进行时间片的计算,软件定时器的处理等,所以软件定时器的精度和Tick的精度有一定依赖性。

在任务运行达到系统设定的时间片Tick数目时,可进行显示的放权,进而调用其他任务,已达到时间片轮转的目的。

5、互斥锁与信号量

liteOS中都采用PV操作,即PV操作(pend post)。主要差别在于信号量允许多个任务重入,在创建信号量时,可以设置最大重入次数。

6、任务上下文的切换

所谓任务的上下文切换,理解上是在任务切换时,对任务当前环境变量进行保存,以便于任务切换回来时恢复现有状态。

任务的上下文切换,在中断 PendSV 的服务程序中实现,理解上好处:在中断处理避免了在线程中处理时,中断打断上下文切换的需要处理情况。

ICSR(Interrupt Control and State Register)使得用户在需要进行上下文切换时可以配置相应寄存器,以达到触发 PendSV 中断的目的。

7、任务堆栈的实现

在 cortexM4 系统中存在两种堆栈:MSP、PSP,亦即主堆栈指针及进程堆栈指针,在进入中断处理服务程序时用MSP,而在用户线程运行时需要使用PSP。

在cortexM4中可以通过配置CONTROL寄存器的方式来选择MSP或PSP。

CONTROL寄存器的定义:

CONTROL[0]:0-特权级线程,1-用户级线程,在中断处理服务程序中用于为特权级线程;CONTROL[1]:0-选择主堆栈指针MSP,1-选择进程堆栈指针PSP。

对于特殊寄存器的控制需要使用特殊的MSR/MRS指令进行操作,所以一般相关代码需要汇编实现,在liteOS中最底层任务相关汇编函数放在dispatch.s中。

8、消息队列与事件

消息队列顾名思义就是先进先出FIFO,LiteOS中消息队列可以用来在不同任务之间进行数据的交互。而事件event用来各个任务的同步,相对每个任务只能读取一个消息队列的限制,事件没有这样的要求,但同时事件不提供任务间的数据传输功能。理解上事件更像与多个任务的独立的存在,而消息队列却具有一定的相关性。在LiteOS中消息队列和事件都支持超时机制,当然超时可以设置成两种极限情形:立即响应和用于等待。

9、任务调度详解

系统有两个任务调度函数:


  
  1. VOID osSchedule(VOID)
  2. VOID LOS_Schedule(VOID)

这两个函数最终会调用到汇编编写的 osTaskSchedule() 函数,在移植的时候要根据不同的内核来修改,在 COTEX-M3 函数内核中用的是 pendsv 中断来切换任务。


  
  1. osTaskSchedule
  2.     LDR     R0, =OS_NVIC_INT_CTRL
  3.     LDR     R1, =OS_NVIC_PENDSVSET
  4.     STR     R1, [R0]
  5.     BX      LR

该函数就是直接操作寄存器触发 pendsv 异常:


  
  1. PendSV_Handler
  2.     MRS     R12, PRIMASK
  3.     CPSID   I
  4.     LDR     R2, =g_pfnTskSwitchHook
  5.     LDR     R2, [R2]
  6.     CBZ     R2, TaskSwitch
  7.     PUSH    {R12, LR}
  8.     BLX     R2
  9.     POP     {R12, LR}

异常直接调用 TaskSwitch 函数:


  
  1. TaskSwitch
  2.     MRS     R0, PSP
  3.     STMFD   R0!, {R4-R12}
  4.     VSTMDB  R0!, {D8-D15}
  5.     LDR     R5, =g_stLosTask
  6.     LDR     R6, [R5]
  7.     STR     R0, [R6]
  8.     LDRH    R7, [R6 , #4]
  9.     MOV     R8,#OS_TASK_STATUS_RUNNING
  10.     BIC     R7, R7, R8
  11.     STRH    R7, [R6 , #4]
  12.     LDR     R0, =g_stLosTask
  13.     LDR     R0, [R0, #4]
  14.     STR     R0, [R5]
  15.     LDRH    R7, [R0 , #4]
  16.     MOV     R8,  #OS_TASK_STATUS_RUNNING
  17.     ORR     R7, R7, R8
  18.     STRH    R7,  [R0 , #4]
  19.     LDR     R1,   [R0]
  20.     VLDMIA  R1!, {D8-D15}
  21.     LDMFD   R1!, {R4-R12}
  22.     MSR     PSP,  R1
  23.     MSR     PRIMASK, R12
  24.     BX      LR

任务调度函数就是通过操作全局变量 g_stLosTask,把当前任务的寄存器入栈,其实就是存在任务控制块中 pStackPointer 指向的内存里面,然后把最新的就绪任务弹出来,就实现了任务切换。


  
  1. typedef struct tagTaskCB
  2. {
  3.     VOID                        *pStackPointer;             /**< Task stack pointer                 */
  4.     UINT16                      usTaskStatus;
  5.     UINT16                      usPriority;
  6.     UINT32                      uwStackSize;                /**< Task stack size                 */
  7.     UINT32                      uwTopOfStack;               /**< Task stack top               */
  8.     UINT32                      uwTaskID;                  /**< Task ID                     */
  9.     TSK_ENTRY_FUNC              pfnTaskEntry;               /**< Task entrance function               */
  10.     VOID                        *pTaskSem;                  /**< Task-held semaphore           */
  11.     VOID                        *pThreadJoin;               /**< pthread adaption            */
  12.     VOID                        *pThreadJoinRetval;         /**< pthread adaption            */
  13.     VOID                        *pTaskMux;                  /**< Task-held mutex           */
  14.     UINT32                      auwArgs[4];                 /**< Parameter, of which the maximum number is 4          */
  15.     CHAR                        *pcTaskName;                /**< Task name                     */
  16.     LOS_DL_LIST                 stPendList;
  17.     LOS_DL_LIST                 stTimerList;
  18.     UINT32                      uwIdxRollNum;
  19.     EVENT_CB_S                  uwEvent;
  20.     UINT32                      uwEventMask;                /**< Event mask               */
  21.     UINT32                      uwEventMode;                /**< Event mode               */
  22.     VOID                        *puwMsg;                    /**< Memory allocated to queues          */
  23. } LOS_TASK_CB;

 

文章来源: blog.csdn.net,作者:简一商业,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/liwei16611/article/details/86581096

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。