作者:Stephen Du
免责声明: 本文为个人学习笔记及总结,仅代表个人观点,尽可能保证内容准确性。复制/转发请注明来源/作者。
欢迎添加微信交流学习。
AUTOSAR MCAL MCU模块解析
1. 简介
MCU驱动程序提供微控制器初始化,掉电功能,复位和微控制器其他MCAL软件模块所需的特定功能的服务(这里主要指那些公共寄存器的设置)。需要注意的是,启动代码和用于升级的Bootloader是不在AUTOSAR负责范围内的,启动代码是特定于MCU的(不同MCU的启动代码都不一样,见下章节),如下图所示。
MCU驱动程序直接访问微控制器硬件,位于微控制器抽象层(MCAL)中。
MCU驱动包含如下功能:
- 初始化MCU时钟,PLL,时钟预分频器和MCU时钟分配。
- RAM初始化。
- 低功耗模式**。
- MCU复位。
- 提供从硬件获取复位原因的服务/API。
通常,在AUTOSAR标准中,不强制**和配置MCU低功耗模式。
启用/禁用ECU/MCU电源也不是MCU驱动程序的任务。这将由上层管理模块来处理。
配置内容如下图所示:
2. 启动代码
MCU初始化函数执行前,必须先执行一些MCU的基本初始化。通常这些特殊的初始化代码我们叫启动代码。
MCU的启动代码应该在上电以及任何单片机复位源复位以后执行。它通常只执行那些非常基本的和微控制器的一些特殊要求的初始化代码,并且应保持简短,因为这个时候MCU时钟和PLL尚未初始化。
如果执行太臃肿的代码肯定会影响效率,而汽车里面对系统启动时间有比较高的要求。
通常启动代码应涵盖那些没有在MCU模块服务以及其他MCAL驱动里面包含的功能。以下总结了启动代码中将包含的基本功能。该列表仅做参考,因为某些功能可能并非在所有MCU中都支持,或者某些特殊功能未涵盖。
- 启动代码应初始化中断和陷阱向量表的基地址。这些基地址可通过配置参数或链接器/定位器的设置提供。
- 启动代码将初始化用户堆栈指针。用户堆栈指针基地址和堆栈大小。同上,这些信息可通过配置参数或链接器/定位器设置提供。
- 如果MCU支持上下文保存操作,则启动代码应初始化用于上下文保存操作的内存。连续上下文保存操作的最大数量/大小通过配置参数或链接器/定位器设置提供。
- 启动代码应确保在MCAL看门狗驱动程序初始化之前看门狗不溢出。例如,你可以通过增加看门狗服务时间或直接关闭看门狗(如果可以的话)来完成。
- 如果MCU支持用于数据和/或代码的高速缓存,则应在启动代码中对其进行初始化和启用。
- 启动代码应针对内部存储器初始化MCU的特定功能,例如,存储器/内存保护。
- 如果使用外部存储器,则应在启动代码中初始化相应存储器。启动代码应准备好根据代码位置的不同支持不同的内存配置。从外部/内部存储器执行代码时,应考虑不同的配置选项。不同存储器的设置应作为配置参数提供给启动代码。
- 在启动代码中,应执行MCU时钟系统的默认初始化,包括全局时钟预分频器。
- 如果MCU支持,启动代码应启用特殊功能寄存器(SFR)的保护机制。
- 启动代码将初始化所有必要的一次性写入类型寄存器。如果某些寄存器被多个驱动程序共有,并且这些寄存器我们只希望写入一次,而不是每个驱动程序初始化的时候都去写一次,这种情况下也非常推荐将这些寄存器放在启动代码里执行。
- 启动代码应初始化最小数量的RAM,以便正确执行MCU驱动程序服务和这些服务的调用方。
- 如果支持及需要使用,你还应该初始化好安全校验模块,比如CRC校验,以免发生位错误。
注意:启动代码取决于ECU和MCU。 规范的详细信息将在MCU的设计规范中进行描述。
3. 重要概念及API详解
3.1 复位
MCU驱动提供软件触发硬件复位的服务,但不是任何用户都可以使用该服务,只有那些经过授权的用户才能调用该复位服务。
在一个ECU中,可能会有多种原因导致复位,在不同的应用场景中,每次MCU重新初始化后可能需要知道具体的复位原因,如果硬件支持复位源查询功能,则MCU驱动也提供获取上一次复位原因的服务。
3.2 时钟
MCU驱动提供使能及设置MCU时钟的服务。比如:CPU时钟,外设时钟,时钟分频器,时钟倍频器等相关设置。MCU模块还为其他BSW模块提供必备的时钟参考点(McuClockReferencePoint)。需要在MCU里面**并配置好相关模块的时钟参考源。
3.3 模式
MCU驱动提供**MCU节能模式的服务,也就是我们平时说的低功耗模式。这些服务通常是直接访问并操作MCU硬件寄存器。现在很多芯片对节能模式还划分了很多等级,支持多个不同等级模式,那么需要根据你的项目需求在MCU模块里面配置好你需要使用的模式。
在典型运用中,ECU运行期间,MCU低功耗模式的进入或退出可能会频繁切换,在这种情况下,任何一个MCAL模块的**唤醒都会执行唤醒动作。
配置节能模式通常会影响到PLL,内部晶振,CPU时钟,外设时钟,内核时钟等。
MCU的正常模式**或电源切断由上层负责。
某些MCU唤醒只能通过硬件复位来实现。
3.4 Mcu_Init
在Mcu_Init函数执行后,那些配置数据才允许被相关函数访问和使用,比如Mcu_InitRamSection。总的来说,MCU初始化函数对寄存器的操作需符合以下条款:
- 对于某些寄存器硬件只允许使用一种场景,那些这些寄存器由实现相关功能的模块负责。这里标准原文不大好理解,简单解释就是,比如那些外设控制寄存器(SPI,ADC等等),通常这种寄存器只属于该外设(与其他模块没任何关系),那么很显然这些寄存器有相关外设来负责(SPI,ADC…)。
- 假如某个寄存器会影响多个硬件模块,并且该寄存器不属于I/O寄存器,则MCU模块负责(如果属于I/O模块,由PORT模块负责)。
- 对于某些寄存器只能写一次,这种寄存器由启动代码负责(在前文相关章节已经提及)。
- 所有其他在前面未被提及类型的寄存器都由启动代码负责。
3.5 Mcu_InitRamSection
该函数负责从指定起始地址开始写入指定总长度的指定默认值,每次写入长度也是配置指定的。共涉及下列配置信息:
- McuRamSectionBaseAddress :指定起始地址
- McuRamSectionSize :指定写入总长度
- McuRamDefaultValue :指定写入默认值
- McuRamSectionWriteSize :指定每次写入长度
该函数必须在Mcu_Init函数执行后才能被调用。
3.6 Mcu_InitClock
该函数负责初始化PLL及其他配置的外设时钟等。该函数执行完后立马退出,不会等PLL锁相/稳定(Locked)再退出,所以通常在该函数调用后需要用户自行调用Mcu_GetPllStatus接口判断时钟是否已锁相/稳定。必须在Mcu_Init函数后调用。该函数是否有效受McuInitClock开关管控。
3.7 Mcu_DistributePllClock
该函数只有在McuNoPll
设置为FALSE时才有效。该函数负责分发PLL时钟,并释放当前的时钟源(通常为内部晶振)。
MCU从上电到时钟初始化这段时间会使用默认时钟(芯片内部自带),所以当新的时钟PLL锁相/稳定后,需要释放这个默认时钟。
只有在调用Mcu_GetPllStatus函数判断PLL时钟锁相/稳定后(Locked)才能调用该函数。如果PLL没有锁相/稳定就调用,它会立即返回错误。
3.8 Mcu_GetResetReason
如果硬件支持查询获取复位原因,则该函数返回实际复位原因;如果硬件不支持,则返回值固定为上电复位(MCU_POWER_ON_RESET)。如果该函数在Mcu_Init函数之前调用,则返回未定义复位源(MCU_RESET_UNDEFINED)。
当复位原因被读出后,用户需要保证复位源被清除掉,以防止获取到多个复位原因。当多次调用该函数,每次返回结果应该是一样的。这意味着什么呢?也就是说软件层面有一个管理机制,在保证多次调用返回相同结果的同时又得清理掉相应的硬件复位源标记。
3.9 Mcu_SetMode
该函数假定在调用它之前已经禁用了所有中断。它会保证不会丢掉唤醒中断事件,通常通过后述方式来实现的,在真正设置掉电模式之前去检查有没有相关唤醒中断处于pending状态,以此来保证不会丢掉唤醒中断。