AUTOSAR入门-EcuM模块与系统启动
原创 thatway 那路谈OS与SoC嵌入式软件 2022-06-22 07:30
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);
可以找到状态的定义如下:
typedef enum {
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 configuration
ASLOG(ECUM,
("!!!EcuM_AL_DriverInitZero!\n"));
EcuM_AL_DriverInitZero();
初始化和启动Det(Default Error Tracer) 默认的错误追踪器,必须在最开始初始化
2.3 InitOS();
1.初始化全局变量sys_t os_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 configuration
EcuM_World.config = EcuM_DeterminePbConfiguration();
就是加载各个模块配置的全局变量的,这些配置也叫PostBuild
configuration,就是说在编译后也可以改变的配置,这部分配置在内存中存在,可以被程序所修改。
EcuM_World可以看作是ECU的控制块,它是一个EcuM_GobalType结构体,定义如下:
EcuM_GlobalType EcuM_World;
```
typedef struct { boolean initiated; //是否已经启动 EcuM_ConfigType * config; //ECU上各个硬件部分的控制信息,如:Ecu Default Shutdown Target,Ecu Default Shutdown Mode, Ecu Default App Mode EcuM_StateType shutdown_target;
if (defined(USE_ECUM_FLEXIBLE))
EcuM_ShutdownCauseType shutdown_cause;
endif
uint8 sleep_mode;
AppModeType app_mode;
EcuM_StateType current_state;
if defined(USE_COMM) || (defined(USE_ECUM_COMM) && (ECUM_AR_VERSION < 40000))
uint32 run_comm_requests;
endif
uint32 run_requests;
uint32 postrun_requests;
/* Events set by EcuM_SetWakeupEvent */
uint32 wakeupEvents;
uint32 wakeupTimer;
uint32 validationTimer;
uint32 nvmReadAllTimer;
/* Events set by EcuM_ValidateWakeupEvent */
uint32 validEvents;
boolean killAllRequest;
} 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在Arctic Core中是一个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 NVRAM
data
EcuM_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研究-交流群,可以讨论汽车软件最新技术,一起学习。
Talk
is cheap
,show me
the code
,后续会继续更新,
纯干货
分析,无广告,不打赏,欢迎
转载
,欢迎
评论交流
!
往期见话题:
AUTOSAR入门