NXP S32系列MCU官方AUTOSAR OS分析(一)

1. 概述

我之前介绍过如何用Keil编译NXP官方提供的AUTOSAR OS,想要了解的朋友可以去翻一下之前的文章,本文来简要介绍下AUTOSAR OS的代码实现。

我使用的单片机时S32K144,AUTOSAR的版本是4.0.3

2. 源码分析

(1)StartOS

StartOS()函数是AUTOSAR OS的入口,在main函数中做一些硬件和应用层的初始化,之后进入StartOS。

在这个函数中,会对各种对象(Object)进行初始化,如Application、Task、Alarm、ISR、Timer、Stack、Loc、ScheduleTable、Counter等。

初始化完成后,OS会启动AutoStart的任务,系统开始运行。

(2)对象初始化

上文所述的初始化,每种对象会对应一个配置结构体和一个控制结构体(下文称控制块),配置结构体由EB生成,固化在MCU的Flash中,控制块位于RAM中,在程序运行过程中实时记录和控制该对象的状态。在对象的初始化函数中,会把配置结构体中的对象属性赋给控制块,作为初始化状态。

在AUTOSAR OS 中,所有的对象都是程序编译时定义好了的,不支持动态创建任务等对象。

以Task为例,EB中定义了如下几个Task(官方测试例程):

NXP S32系列MCU官方AUTOSAR OS分析(一)_第1张图片
EB生成的Task配置结构体如下:

/* Task Configuration table */
const    OSTSK   OsTaskCfgTable[OSNTSKS] = 
{
    {
        3U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKRCV1, /* entry point of task */
        OSTASKRCV1STKTOS, /* top of task stack */
        0U | OSTSKEXTENDED | OSTSKACTIVATE, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        0U, /* task id (task number in the task table) */
        0U, /* application identification value */
    }, /* TASKRCV1 */
    {
        3U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKRCV2, /* entry point of task */
        0U, /* top of task stack */
        0U, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        1U, /* task id (task number in the task table) */
        0U, /* application identification value */
    }, /* TASKRCV2 */
    {
        2U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKSND1, /* entry point of task */
        0U, /* top of task stack */
        0U, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        2U, /* task id (task number in the task table) */
        1U, /* application identification value */
    }, /* TASKSND1 */
    {
        2U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKSND2, /* entry point of task */
        0U, /* top of task stack */
        0U, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        3U, /* task id (task number in the task table) */
        1U, /* application identification value */
    }, /* TASKSND2 */
    {
        2U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKCNT, /* entry point of task */
        0U, /* top of task stack */
        0U | OSTSKNONPREEMPT, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        4U, /* task id (task number in the task table) */
        1U, /* application identification value */
    }, /* TASKCNT */
    {
        4U, /* Application identification mask value */
        (OSTASKENTRY) &FuncTASKSTOP, /* entry point of task */
        0U, /* top of task stack */
        0U | OSTSKNONPREEMPT, /* properties of task OSTSKACTIVATE, OSTSKEXTENDED, OSTSKNONPREMPT, OSTSKFLOATINGPOINT */
        5U, /* task id (task number in the task table) */
        2U, /* application identification value */
    }, /* TASKSTOP */
};

在初始化代码中,定义了配置结构体指针task_cfg和控制块指针task_cb,截取的部分代码如下:

    OSTSKCBPTR task_cb;
    const OSTSK *task_cfg;

	for (i = 0U; i < OSNTSKS; i++)
    {
        task_cb = &(OsTaskTable[i]);
        task_cfg = &(OsTaskCfgTable[i]);



        #if defined(OSAPPLICATION)
        task_cb->appMask = task_cfg->appMask;   /* copy application identification mask value */
        task_cb->appId = task_cfg->appId;       /* copy application identification value */
        #endif

        task_cb->entry = task_cfg->entry;       /* entry point of task */
        task_cb->taskId = task_cfg->taskId;

        #if defined(OSINRES)
        task_cb->runprio = (OSBYTE)task_cfg->runprio;
        #endif

        #if defined(OSRESOURCE) || defined(OSINRES)
        /*
        * @violates @ref Os_task_c_REF_17_4_1 MISRA 2004 Required Rule 17.4, pointer arithmetic other than array indexing used
        */
        OsPrioLink[task_cfg->taskId] = task_cb;
        #endif

    }

(3)任务调度

所涉及的变量:

OsRunning:任务控制块指针,指向当前运行的任务的控制块。
OsSchedulerVector1:就绪任务向量
OsSchedulerVectorMask1:就绪任务向量掩码
OsPrioLink:按优先级排序的Task表,成员是任务控制块指针,数量是Task数量+1
OsTaskTable:存储所有任务的任务控制块的表,成员是任务控制块,数量是Task数量+1
OsTaskCfgTable:存储所有任务配置信息的表

关于任务的初始化我们上面简单说了一下,总的来说就是把任务配置表中的初始化配置赋给任务控制块,同时会初始化OsPrioLink,按优先级将任务排序,以便于之后根据优先级来查找任务。

EB生成的任务配置表中,每个任务的配置按任务优先级数值从大到小排序,并从0开始为每个任务按顺序分配TaskID,因此优先级和TaskID在数值上的顺序是相反的,也就是说优先级越高的任务,优先级数值越大,排在配置表的前面,TaskID数值越小

在OS运行过程中,TaskID起优先级的作用,无论是任务配置表还是控制块中,都没有任务优先级了,所以EB中的任务优先级只是为配置表中的排序提供依据,是仅用来比较大小关系的相对的数值,因此也不允许配置为相同的优先级。后文提到的代码中的优先级的概念,都指TaskID。

任务就绪:

当一个任务转为就绪态时,调用以下接口函数:

OSTask2Ready:该函数设置相应的Task状态为Ready,例如Task优先级(TaskID)为3,那么就把OsSchedulerVector1的左数第4位设置为1,代码如下:

#define OSSETBITNUM2MASK(taskprio)  ( OsSchedulerVector1 |=  (OSDWORD)( OSDWORDMSB >> (taskprio) ) )
优先级判断:

OS在计算最高优先级的时候,会计算OsSchedulerVector1最左侧的0的个数(clz汇编指令),例如上面说的第4位为1,则前面有3个0,即最高的优先级为3。用该优先级去OsPrioLink链表中调取相应的任务控制块。

如果有一个优先级为2的Task就绪,则会先查找到优先级为2的Task,因此TaskID越小,优先级越高。

查找最高优先级的函数接口为 OSGETMAXPRIOTASK

任务调度接口:

OS内部的任务调度接口函数是OSDISPATCH,该函数内部又调用OSTaskForceDispatch函数,真正执行优先级查找和上下文切换。

OS中在以下函数中会执行任务调度:
NXP S32系列MCU官方AUTOSAR OS分析(一)_第2张图片
此外还有中断任务调度函数:OSInterruptDispatcher()

开放给用户的任务调度接口函数是Schedule()

(4)上下文切换

上下文切换发生在OSTaskForceDispatch()和OSInterruptDispatcher()等会执行任务调度的函数中,执行保存环境的函数是OSSetJmp(context),恢复环境的函数是OSLongJmp (OsRunning->pcontext),当然这只是两个主要的上下文切换函数,还有一些寄存器控制函数就不展开讲了。

这些函数与MCU底层架构强相关,而且对效率要求很高,所以都是用汇编编写的,这些函数基本都在Os_hw_core.c中。

(5)中断处理

在EB中配置中断后,会生成一个中断配置表:

const    OSISRCFGTYPE   OsIsrCfg[OSNISR + 1] =  /*Interrupts config table*/
{
    {
        OS_isr_ISR1, /* actual ISR function */
        OSTRUSTEDISR2, /* ISR type */
        116U,  
        4U, /* ISR PRIORITY */
        2U, /* application identification value */
    }, /* ISR1 */
    {
        OSISRSystemTimer, /* actual ISR function */
        OSSYSINTERRUPT, /* ISR type */
        115U, /* index in OsIsr */
        2U, /* Interrupt priority */
        OSINVALID_OSAPPLICATION, /* appId */
    }, /* SysTimer */
    {
        OSISRSecondTimer, /* actual ISR function */
        OSSYSINTERRUPT, /* ISR type */
        15U, /* index in OsIsr */
        4U, /* Interrupt priority */
        OSINVALID_OSAPPLICATION, /* appId */
    }, /* SecondTimer */
    {
        OSISRException, /* actual ISR function */
        OSSYSINTERRUPT, /* ISR type */
        OSISREXCEPTIONIDX, /* Index of interrupt */
        OSISREXCEPTIONPRIO, /* Interrupt priority */
        OSINVALID_OSAPPLICATION, /* appId */
    }, /* Exception */
};

中断配置表中有中断入口函数、中断优先级等,在初始化函数OSInitializeISR (void)中,配置表中的配置被赋给OsIsrTable,OsIsrTable控制和记录OS运行过程中中断的状态,其定义如下:

/* ISR control structure */
struct tagISRTYPE
{
    #if defined(OSTERMINATION)
    OSBYTE isKilled;            /* set when the ISR is killed via ProtectionHook or via TerminateApplication() */
    #endif
    #if defined(OSUSERESLINK)
    OSRefResType resources;     /* pointer to the resources */
    #endif
    OSVOIDFUNCVOID userISR;     /* actual ISR function */
    OSISRTYPE type;             /* type of the ISR */
    OSWORD index;               /* Index of interrupt in the external interrupts table */
    OSBYTE isrPrio;             /* Interrupt priority */
    #if defined(OSAPPLICATION)
    OSAPPLICATIONTYPE appId;    /* application identification value */
    #endif
};

参数Os_ArmNvicNestingLevel用来记录中断嵌套等级。
Os_ArmSavedIRQCtx用来保存中断上下文环境,包括两个寄存器:XPSR和LR。其中LR(R14)是链接寄存器,它存储从子程序调用、函数调用和中断处理后返回的信息。XPSR是程序状态寄存器,包含三部分:

  • 应用程序状态寄存器(APSR)
  • 中断程序状态寄存器(IPSR)
  • 执行状态寄存器(EPSR)

这三部分在XPSR寄存器中的位置如下图所示:

NXP S32系列MCU官方AUTOSAR OS分析(一)_第3张图片

在初始化过程中OSPlatformIntcInit函数用来初始化中断上下文环境为空。

在启动文件startup_S32K144_OS.s中,定义中断向量表如下:

__Vectors       DCD     |Image$$ARM_LIB_STACK$$ZI$$Limit| ; Top of Stack
                DCD     Reset_Handler  ; Reset Handler
                DCD     OSNmiException                      ;NMI Handler
                DCD     OSHardFaultException                ;Hard Fault Handler
                DCD     OSReservedException                 ;MPU Fault Handler
                DCD     OSBusFaultException                 ;Bus Fault Handler
                DCD     OSUsageFaultException               ;Usage Fault Handler
                DCD     OSReservedException                 ;Reserved
                DCD     OSReservedException                 ;Reserved
                DCD     OSReservedException                 ;Reserved
                DCD     OSReservedException                 ;Reserved
                DCD     OSSVCallException                   ;SVCall Handler
                DCD     OSDebugMonitorException             ;Debug Monitor Handler
                DCD     OSReservedException                 ;Reserved
                DCD     OSPendSVException                   ;PendSV Handler
                DCD     OSInterruptDispatcher               ;SysTick Handler
                
                ;External Interrupts
                DCD     OSInterruptDispatcher     ;DMA channel 0 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 1 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 2 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 3 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 4 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 5 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 6 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 7 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 8 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 9 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 10 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 11 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 12 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 13 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 14 transfer complete
                DCD     OSInterruptDispatcher     ;DMA channel 15 transfer complete

我们可以看到,SystemTick中断以及外部中断都指向了OSInterruptDispatcher()这个函数,OSInterruptDispatcher()是用汇编写的,调用了C函数OSInterruptDispatcher1(),在该函数中再对各个中断进行进一步判断和处理,并进入最终的中断处理函数。OSInterruptDispatcher1()是OS中断处理最重要的一个函数。

你可能感兴趣的:(S32,嵌入式,NXP,S32,AUTOSAR,OS,单片机)