|
汽车零部件采购、销售通信录 填写你的培训需求,我们帮你找 招募汽车专业培训老师
AUTOSAR入门-EcuM模块与系统启动
“日出而作,日落而息”,白天要干活,晚上就要睡觉,两种状态的变化组成了很多人的生活。人的一生要经历生和死,也是状态的变迁,一个人从出生到童年、少年、青年、中年、老年,再到去世。人的活动可以看成是很多状态的变迁,有活动就有状态,就会产生状态变迁,程序也是活动的,是人脑活动的延伸,模拟人脑的机制去自动运行,执行人脑设计的程序。典型的程序状态有:启动、运行、睡眠、唤醒、关机。这五种状态也可以描述我们要做的任何一件事情,可以帮助我们更加科学的去规划事情的发展。
下面进入正题,EcuM全称为(ECU State Management)顾名思义,指的就是ECU 的状态管理,我们以启动流程为主来看分析一下AS代码:https://github.com/thatway1989/as。通过本篇文章可以让你了解到AS代码是怎么启动的,各个模块是怎么被初始化运行起来的,这在软件分析中是很重要的。好比我们说上帝创造了人,那我们要苦苦的去寻找上帝,问上帝你是怎么创造出来的人。
1. 状态机
上图中有五种状态Startup,Shutdown,RUN,Sleep,Wakeup的状态组成以及状态切换的过程,其中OFF,Sleep,RUN是稳态,而Startup跟Wakeup则是暂态。
在Startup阶段,同样按照Flexible 模式中开启OS为界限,分为Startup I与Startup II两个阶段;
当唤醒事件能够控制CPU供电时,则需要进入Wakeup阶段验证Wakeup Event是否有效,相反如果不带电源控制,则直接进入RUN阶段。
若进入到RUN阶段,可分为两个阶段:RUN II与RUN III两个阶段。其中RUN II指的是正常运行阶段,RUN III则是SW-C为即将进入到ShutDown所需要做的前提准备。
若进入到ShutDown阶段,首先会进入到PreShutDown阶段,然后按照Shutdown的目标不同,可以分为reset,OFF,Sleep三条路径。
如果Target为Sleep,则进入到Go Sleep阶段,若在该阶段检测到唤醒事件,那么直接跳转至Wakeup Validation阶段。
如果Target为OFF或Reset,则需经历Go OFF I与Go OFF II两个阶段,reset则会重新跳转至Startup阶段,而OFF则是直接关闭ECU。
若进入到Wakeup阶段,则需要进行四个阶段的唤醒源验证,主要分为Wakeup I,Wakeup Validation,Wakeup Reaction,Wakeup II阶段;
若进入到Sleep阶段,则可以分为两种Sleep模式:Sleep I 与Sleep II,一般两者选其一。其中Sleep I阶段(Halt),此阶段不运行代码,等待唤醒事件,然后跳转至Wakeup阶段;
其中Sleep II阶段则为Polling阶段,这个阶段则会低功耗运行代码,并且等待唤醒事件,如果存在,则进入到Wakeup阶段。
2. 系统启动
上面EcuM的5种状态是概念还比较空洞,下面就结合AS的代码,来看下开机启动的过程。
ECU启动时,首先通过中断向量表运行引导程序(俗称BootLoader),Bootloader在满足一定条件下跳转至APP程序中的C_Init处并指向main函数。Main()函数在
release/ascore/app/main.c中定义:
在这个main()函数之前的代码是汇编写的,在BootLoader中,我们在编译的时候,见AUTOSAR入门-AS开源代码编译过程详解 中2.5 生成TINIX.IMG:生成的TINIX.IMG有三部分:boot.bin、loader.bin、x86-》kernel.bin
boot.bin、loader.bin的源码在:
as/com/as.infrastructure/arch/x86/boot/
x86就是我们用Scons编译出来的目标程序,都是c语言写的,main入口函数就是上面说的release/ascore/app/main.c中定义。
进入EcuM_Init()函数后,首先启动第一阶段的初始化,之后启动OS,再进行第二启动阶段的初始化,最后就是进入StartPostOS阶段,如完成BswM模块的初始化,进而将控制权转交给BswM模块。
整个系统,BootLoader运行完成后,就有EcuM模块接管运行起来OS,初始化各种模块,到最后状态机的运行,都是有EcuM完成的。具体由EcuM_Init()函数完成,在代码com/as.infrastructure/system/EcuM/EcuM.c中。
2.1 EcuM_StateType状态
首先就是设置ECU的状态,执行代码如下:
set_current_state(ECUM_STATE_STARTUP_ONE);
可以找到状态的定义如下:
typedefenum { ECUM_STATE_APP_RUN = 0x32, //!< ECUM_STATE_APP_RUN ECUM_STATE_SHUTDOWN = 0x40, //!< ECUM_STATE_SHUTDOWN ECUM_STATE_WAKEUP = 0x20, //!< ECUM_STATE_WAKEUP ECUM_SUBSTATE_MASK = 0x0F, //!< ECUM_SUBSTATE_MASK ECUM_STATE_WAKEUP_WAKESLEEP = 0x25, //!< ECUM_STATE_WAKEUP_WAKESLEEP ECUM_STATE_WAKEUP_ONE = 0x21, //!< ECUM_STATE_WAKEUP_ONE ECUM_STATE_OFF = 0x80, //!< ECUM_STATE_OFF ECUM_STATE_STARTUP = 0x10, //!< ECUM_STATE_STARTUP ECUM_STATE_PREP_SHUTDOWN = 0x44, //!< ECUM_STATE_PREP_SHUTDOWN ECUM_STATE_RUN = 0x30, //!< ECUM_STATE_RUN ECUM_STATE_STARTUP_TWO = 0x12, //!< ECUM_STATE_STARTUP_TWO ECUM_STATE_WAKEUP_TTII = 0x26, //!< ECUM_STATE_WAKEUP_TTII ECUM_STATE_WAKEUP_VALIDATION = 0x22,//!< ECUM_STATE_WAKEUP_VALIDATION ECUM_STATE_GO_SLEEP = 0x49, //!< ECUM_STATE_GO_SLEEP ECUM_STATE_STARTUP_ONE = 0x11, //!< ECUM_STATE_STARTUP_ONE ECUM_STATE_WAKEUP_TWO = 0x24, //!< ECUM_STATE_WAKEUP_TWO ECUM_STATE_SLEEP = 0x50, //!< ECUM_STATE_SLEEP ECUM_STATE_WAKEUP_REACTION = 0x23, //!< ECUM_STATE_WAKEUP_REACTION ECUM_STATE_APP_POST_RUN = 0x33, //!< ECUM_STATE_APP_POST_RUN ECUM_STATE_GO_OFF_TWO = 0x4e, //!< ECUM_STATE_GO_OFF_TWO ECUM_STATE_RESET = 0x90, //!< ECUM_STATE_RESET ECUM_STATE_GO_OFF_ONE = 0x4d//!< ECUM_STATE_GO_OFF_ONE} EcuM_StateType;
2.2 EcuM_AL_DriverInitZero()
//Initialize drivers that are needed to determine PostBuild configurationASLOG(ECUM,("!!!EcuM_AL_DriverInitZero!\n")); EcuM_AL_DriverInitZero();
初始化和启动Det(DefaultError Tracer) 默认的错误追踪器,必须在最开始初始化
2.3 InitOS();
1.初始化全局变量sys_tos_sys,这个变量用于表示操作系统内部的状态信息
2.初始化计数器
3.初始化调度表
4.在ram中建立pcb,并把rom中的pcb解压到ram中,并串接成链。
2.4 Os_IsrInit()
建立中断表:
Os_IsrInit();-》Irq_Init();
2.5 EcuM_World全局变量
AUTOSAR方法论中介绍:代码有一部分固定的代码和一部分配置代码组成。配置代码在AS中的主要形式就是全局变量,由scons studio工具生成,路径在:as/build/posix/x86/ascore/config
这些全局变量要生效就要加载到内存中,这里
//Determine PostBuild configurationEcuM_World.config = EcuM_DeterminePbConfiguration();
就是加载各个模块配置的全局变量的,这些配置也叫PostBuildconfiguration,就是说在编译后也可以改变的配置,这部分配置在内存中存在,可以被程序所修改。
EcuM_World可以看作是ECU的控制块,它是一个EcuM_GobalType结构体,定义如下:
EcuM_GlobalType EcuM_World;
typedefstruct{booleaninitiated; //是否已经启动EcuM_ConfigType* config; //ECU上各个硬件部分的控制信息,如:Ecu Default Shutdown Target,Ecu Default Shutdown Mode, Ecu Default App ModeEcuM_StateTypeshutdown_target;#if (defined(USE_ECUM_FLEXIBLE))EcuM_ShutdownCauseTypeshutdown_cause;#endifuint8sleep_mode;AppModeTypeapp_mode;EcuM_StateTypecurrent_state;#if defined(USE_COMM) || (defined(USE_ECUM_COMM) && (ECUM_AR_VERSION < 40000))uint32run_comm_requests;#endifuint32run_requests;uint32postrun_requests;/*Events set by EcuM_SetWakeupEvent */uint32wakeupEvents;uint32wakeupTimer;uint32validationTimer;uint32nvmReadAllTimer;/*Events set by EcuM_ValidateWakeupEvent */uint32validEvents;booleankillAllRequest;}EcuM_GlobalType;
2.6 EcuM_AL_DriverInitOne()
完成无需OS支持的底层硬件驱动的初始化或者其他低水平的初始化,将这部分驱动的初始化称为Init Block 1;
#if defined(USE_DEM) // Preinitialize DEM NO_DRIVER(Dem_PreInit(ConfigPtr->DemConfigPtr)); #endif
这里有DEM的预初始化。
初始化的模块:
// Preinitialize DEM NO_DRIVER(Dem_PreInit());// Setup Port Port_Init(ConfigPtr->PortConfig);// Setup the GPT Gpt_Init(ConfigPtr->GptConfig);
Wdg_Init(ConfigPtr->WdgConfig);
NO_DRIVER(WdgM_Init(ConfigPtr->WdgMConfig));// Setup DMA Dma_Init(ConfigPtr->DmaConfig);// Setup ADC Adc_Init(ConfigPtr->AdcConfig);// Setup PWM Pwm_Init(ConfigPtr->PwmConfig);// Setup PWM Ipc_Init(&Ipc_Config);
NO_DRIVER(VirtQ_Init(&VirtQ_Config));
NO_DRIVER(RPmsg_Init(&RPmsg_Config));
SHELL_Init();
2.7 KSM_INIT();
见AUTOSAR入门-SoAd模块和TcpIp模块 3.3 TaskLwip激活中,把KSM_Config数组里面的任务轮询执行。
代码见:build/posix/x86/ascore/config/ksm_cfg.c
2.8 StartOS(OSDEFAULTAPPMODE);
如果没有初始化正确,就进入了noooo()死循环,从而触发看门狗reset。
二是检查os_strat()是否被退出,如果退出,则会调用assert(0)从而引发可捕获的异常。
StratOS()这个系统API调用了os_start()内部函数,它在\system\kernel\init.c
这个函数首先获得处于ready队列优先级最高的任务,然后切换到该任务,其间又调用了用户的StartupHook(),启动Alarm、Counter和系统Tick。
首次任务切换后操作系统就开始正式运行,至此系统启动完毕。
EcuM_enter_run_mode();进入运行模式。
3.操作系统运行
操作系统的运行核心就在于如何组织任务抢占CPU的时间片。在OSEK OS中,任务被触发执行可能的情况有四种:
被其他任务、中断服务程序调用SetEvent()或ActivateTask()触发。任务还可以调用ChainTask()触发切换。
其他任务释放资源。
被报警器(Alarm)触发。
通信通知。
其中第3种情况是最为常见的,因为任务通常是以周期执行的形式体现。
含有RTE应用程序的操作系统可分为五个部分:内核、任务体、构件、RTE和基础软件,RTE在ArcticCore中是一个API的转换层,构件只能运行自己的算法或调用Rte中的函数,每一个项目都有用户自己定义的Rte模块将构件的调用转换为BSW的API。整个过程如下图所示:
3.1 Bsw启动
os启动后会执行任务,com/as.infrastructure/system/SchM/SchM.c中有任务列表
TASK(SchM_Startup){EcuM_StartupTwo();TerminateTask();
3.2 EcuM_StartupTwo()
在开启OS的初始化函数中调用EcuM_StartupTwo进行第二启动阶段的初始化,最后就是进入StartPostOS阶段,如完成BswM模块的初始化,进而将控制权转交给BswM模块。
// Initialize drivers that don't need NVRAMdataEcuM_AL_DriverInitTwo(EcuM_World.config);(void)Rte_Start();EcuM_AL_DriverInitThree(EcuM_World.config);EcuM_enter_run_mode(); //会就进入运行状态。
3.3 EcuM_AL_DriverInitTwo()
初始化不需要NVRAM数据的模块
#if defined(USE_DCM) EcuM_CheckValidation NO_DRIVER(Dcm_Init(ConfigPtr->DcmConfigPtr)); // Setup DCM EcuM_CheckWakeup#endif
可以看到DCM的初始化
3.4 EcuM_AL_DriverInitThree()
初始化需要NVRAM数据的模块
5. EcuM其他几种状态分析
EcuM的AUTOSAR官方文档为:《AUTOSAR_SWS_ECUStateManager.pdf》其他几种状态可以参考:
https://zhuanlan.zhihu.com/p/508403009
自己对照官方文档看一遍,这里偷懒就不说明了。
后记:
最近比较忙些,每周一更还是要坚持,写点有用的东西,拓展嵌入式和汽车软件的知识。如果对汽车软件感兴趣的朋友,这里可以加我微信thatway1989,备注进群。然后拉你进本公众号的交流群:OS与AUTOSAR研究-交流群,可以讨论汽车软件最新技术,一起学习。
Talkis cheap,show methe code,后续会继续更新,纯干货分析,无广告,不打赏,欢迎转载,欢迎评论交流!
往期见话题:AUTOSAR入门 |
|