AUTOSAR大全

1、AUTOSAR介绍

AUTOSAR 是针对特定的汽车电子这一领域,提出的一套开放式软件结构。其主体思想是使得软件设计开发更易于管理,软件系统更易于移植、裁剪,以及更好的维护性和质量保证。

1.1 AUTOSAR目的

挑战 解决方法 好处
不成熟的过程,因为 ad-hoc 模式/缺少对功能需要的追踪能力。缺少兼容的工具(供应商、OEM) 标准化的规范交换格式 对规范的改进(格式、内容)提供无缝的工具链。
浪费在实现和优化组件上的努力,而顾客并不承认这些努力的价值。 基础软件核(BSW, Basic Software) 软件质量的加强。将工作集中在有价值的功能上。
微控制器模型缺乏可用性,很难适应现有软件。(由新功能引起的)微控制器性能的扩展需求所导致的升级需要(如重新设计)。 微控制器抽象(MCAL) 微控制器能在不需要改变更高软件层的情况下调换。
重定位 ECU 之间的功能时需要做大量的工作。功能重用时也需要做大量的工作。 运 行 时 环 境(RTE, RunTime Environment) 功能封装导致的通信技术的独立性。通过标准化机制,使得通信更加简单。使功能分区和功能重定位变得可能。
非竞争性功能必须适应OEM的特定环境。因为需要从其它组件供应接口需要很多功夫,所以哪怕是很微小的革新,也需要做很多工作。基础软件和模型生成的代码间缺少清晰的接口。 接口标准化 减少/避免 OEM 和供应商之间的接口。通过使用通用接口目录,使独立于软件功能的硬件实现所耗费的工作量。简化模型驱动的开发,允许使用标准化的AUTOSAR代码生成工具。OEM 间的模型的可重用性。不同供应商之间模块的可交换性。

1.2 AUTOSAR架构

  • 精简版
AUTOSAR总体图

BSW可分为:

I/O:访问传感器、执行器、ECU板载总线

Memery:访问内部/外部存储器(非易失性存储器

Crypto:访问加密原语(基本密码概念,如加密、签名、Hash等算法)

Communication:访问汽车网络系统、ECU板载通信系统、ECU内部软件(SW)

Off-board Communication:访问汽车-X通信、汽车内无线通信系统、ECU板外通信系统

System:提供标准化(操作系统、时钟、error memory错误存储)和ECU特定服务(状态管理、看门狗)和库函数

  • 细化版
AUTOSAR总体图(较清楚细节)
  • 完整版
AUTOSAR总体图(最清楚细节)

1.3 AUTOSAR方法

AUTOSAR总体图(最清楚细节)

1.定义 System Configuration Input,选择软、硬件组件,标识系统总体限制

2.活动 Configure System 主要是将软件组件映射到关于资源和计时要求的 ECU 上

3.Configure System 的输出是 System Configuration Description:系统信息(如总线映射、拓扑等)和关于软件组件定位到哪个 ECU 的映射

4.活动 Extract ECU-Specific Information 从 System Configuration Description 中提取特定 ECU 所需的信息。

5.提取的信息输出到 ECU Extract of System Configuration

6.活动 Configure ECU 为实现添加了所有必需的信息,如任务调度、必需的 BSW(基础软件)模块、BSW 的配置、任务中可运行实体的赋值等。

7.活动 Configure ECU 的结果将输出给 ECU Configuration Description,它负责收集所有关于特定 ECU 的局部信息。通过这些信息可以构建该特定 ECU 的可执行软件。

8.在最后一步中,活动 Generate Executable 根据从 ECU Configuration Description中得到的信息生成可执行软件。这一步通常涉及生成代码(如为 RTE 和 BSW 生成代码)、编译代码(编译生长的代码或编译软件组件的源代码)、将所有编译后的代码连接成为可执行软件。

9.得到可执行 ECU 软件。

以上来自《AUTOSAR技术分析报告》

流程分为四个步骤(来自硕士论文《基于AUTOSAR标准的VFB仿真工具》)

(1)系统配置

先编写系统配置的输入文件——XML格式(软件组件描述文件),可手写,可通过软件组件开发工具生成。

系统配置定义ECU资源和系统约束,然后把软件组件分配到ECU,并进行总线信号映射。

(2)ECU抽取

从系统配置描述中抽取单个ECU需要的所有信息,包括组件信息,ECU信息,信号信息等,构成一个完整系统。

(3)ECU配置

ECU配置主要进行与代码实现相关的配置,包括基础服务模块的配置和RTE代码生成所需要的配置。

(4)可执行代码生成

生成OS配置代码、RTE通信代码等粘合代码,最后与软件组件实现代码和基础服务一起编译运行。

综上所述,需要四个开发工具支持:软件组件设计工具系统配置工具ECU配置工具RTE代码生成工具

1.4 缩写词

  • CANSM = CAN State Manager

1.5 AUTOSAR 元模型和XML序列化

1.5.1 AUTOSAR元模型

AUTOSAR元模型是一种基于Oracle Java和Eclipse的EMF(Eclipse Modeling Framework)的模型驱动开发技术(MDD, Model-Driven Development),表达AUTSOAR工程信息,比如系统描述、通信矩阵、基础软件定义、ECU参数配置,可以通过Java/EMF的API进行操作(get、set、add、remove)。

**Java的作用 **

AUTOSRA的元模型基于Java,可以简单地通过Eclipse的Plug-ins或者丰富的客户端应用。

EMF的作用

EMF帮助AUTOSAR工具实现模型驱动开发技术(MDD),使得AUTOSAR元模型应用具备一些能力(改变后通知、验证、事务操作、reflective access),并且简化Eclipse Plug-ins和客户端应用。

1.5.2 XML序列化

AUTOSAR XML序列化为AUTOSAR模型提供基于文件的持久化(persistence)。在内存中的AUTOSAR模型序列化存放在XML文件中,并符合AUTOSAR的XSD规范。

*补充:*持久化是将数据存放在数据库/文件系统中,不会因为掉电而消失。

AUTOSAR规范验证

 <AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="(AUTOSAR namespace) (SYSTEM-Identifier)" xmlns="(AUTOSAR namespace)">

使用二进制资源

XML相比于其他格式,非常冗长,导致AUTOSAR XML解析复杂和缓慢。Artop通过二进制格式解决该问题,空间占用降低至11.%。该方法通过IAutosarPrefenceConstants.PREF_USE_BINARY_RESOURCE使能或者失能。

1.5.3 拓展AUTOSAR模型

Extending AUTOSAR models,通过SGD(Special Data Groups)来存储拓展数据(Externded Data)。org.artop.aal.extender实现了该功能。

  <ADMIN-DATA>
     <LANGUAGE>FOR-ALL</LANGUAGE>
     <SDGS>
        <SDG GID="Properties"><!-- 1 创建一个对象,名称为Properties -->;
           <SD GID="_mdlFile">ECLAIRER_EXTERIEUR_SWC_ExpDT.mdl</SD><!-- 2添字符串属性,名称为_mdlFile,数值是"ECLAIRER_EXTERIEUR_SWC_ExpDT.mdl" -->
           <SD GID="_topLevelRef">/AR_ECLAIRER_EXT_SWC/ECLAIRER_EXT</SD><!-- 3定义字符串属性,名称为"_topLevelRef",数值为"/AR_ECLAIRER_EXT_SWC/ECLAIRER_EXT" -->
        </SDG>
     </SDGS>
  </ADMIN-DATA>

2、BSW-MCAL

MicroController Abstraction Layer,BSW最底层,直接和芯片接触,对应AUTOSAR架构的红色部分。

2.1 微控制器驱动

Microcontroller Drivers

2.2 存储器驱动

Memory Drivers

2.3 加密驱动

Crypto Drivers

2.4 无线通信驱动

Wireless Communication Drivers

2.5 通信驱动

Communication Drivers

2.6 IO驱动

I/O Drivers

3、BSW-ECU抽象层

ECU Abstraction Layer,这一层与微控制器抽象层进行对接。它也包含了外部设备的驱动,不需要管外设位置、同芯片连接情况(端口、针脚、接口类型)。

任务:让更高层的软件层和ECU硬件独立。

3.1 板载设备抽象

Onboard Device Abstraction

3.2 存储硬件抽象

Memory Hardware Abstraction

3.3 加密硬件抽象

Crypto Hardware Abstraction

3.4 无线通信硬件抽象

Wireless Communication Hardware Abstraction

3.5 通信硬件抽象

Communication Hardware

3.5.1 CAN Interface

名词解释在SWS_CANInterface文件的P14.

数据接收和发送的函数调用过程见SWS_CANInterface文件的P68和P70

流程图SWS_CANInterface文件的P129

3.5.1.1 CAN通信的依赖
AUTOSAR CAN的依赖
3.5.1.2 HOH

定义在Can_PBcfg.c中。

HOH = Hardware object handles,用于发送(HTH)和接收(HRH),是CAN邮箱结构的抽象引用。CAN邮箱结构包含了CAN相关的参数,比如CanId,DLC和data。HOH用于作为调用CanDrv接口服务时的参数,用于CAN邮箱通信缓存区的标识。CanIf是HOH的用户,但是独立于硬件(?原文:The HOH is used as a parameter in the calls of CanDrv’s interface services and is provided by CanDrv’s configuration and used by CanDrv as identifier for communication buffers of the CAN mailbox.)。CanIf通过HOH的参数来调用CanDrv接口服务,和硬件抽象层保持了独立性。

如果使用多个HRH,则每个HRH都要属于至少一个或者一组(fixed group)接收的L-SDU(CanRxPduIds)。一个HRH可以配置成:1.接收单个CanId数据(FullCAN);2.接收一组单个CanIds(BasicCan),列表模式?;3.接收一个范围内Id的数据;4.接收所有数据。具体见下图。CanIf用户定义了多个PUD(3个接收,2个发送),分别交给CanIf使用。CanIf将PDU分成了3个通道(maybe每个硬件can对应一个Channel),并和Can Driver的配置文件(在工程MultiCan中的Can_PBcfg.c)的HOH(HRH和HTH,CanHardwareObjectConfig_CanController结构体)关联。

AUTOSAR CANIF Channel Group

HRH:Hardware Receive Handle,由Can Driver定义,每个HRH对应一个硬件对象(Hardware Object)。

HTH:Hardware Transmit Handle,由Can Driver定义,每个HTH代表1个或者多个CAN硬件对象。

3.5.1.3 L-PDU和L-SDU

CAN L-PDU:CAN Protocol Data Unit,包括ID、数据长度、数据(SDU),应该是CAN总线上完整的一帧数据(包括帧头和数据),在MultiCan工程中是RxMessage

CAN L-SDU:CAN Service Data Unit,表示CAN L-PDU传输的数据。

3.5.2 CAN Transceiver Driver

3.5.3 Driver for ext. CAN ASIC

3.6 IO硬件抽象

I/O Hardware Abstraction,目标是使数据通过RTE来传输,而不完全依赖于ECU硬件。

4、BSW-服务层

4.1 介绍

Services Layer,BSW最高层,(任务)为应用、RTE和BSW提供基础服务

服务层和应用软件有关联,当对IO信号访问ECU抽象层时,提供:

  • 操作系统功能
  • 车辆网络通信及管理服务
  • 存储管理(NVRAM 管理)
  • 诊断服务(包括 UDS 通信及错误内存)
  • ECU 状态管理

4.2 通信服务

Communication Services

通信服务架构

4.2.1 DCM

Diagnostic Com. Manager,在开发过程中,可用外部的诊断工具使用(通过通信调用和返回?)该模块进行调试。

使用DCM

4.2.2 CAN TP

4.2.2.1 CAN TP模块和ISO 15765介绍

注意:ISO 15765标准的缩写及其他说明见具体文件(重点是ISO_15765-2文件)。 提取码: syab

TP = TransPort,CAN TP是PDUR和CanIf模块之间,主要用于对超过8字节的IPDU的分段和重组。CAN TP只由事件触发模式下运行。

AUTOSAR通信协议栈

I-:和AUTOSAR的交互层相关;N-:CanTp层相关,等同于OSI的网络层(加上IP);L-:CanIf模块相关,等同于逻辑链路控制LLC(数据链路层MAC的上一层)。

CAN N-PDU:CAN Transport layer的PDU,包括唯一的ID、数据长度和数据(协议控制信息、N-SDU或者一部分N-SDU)

CANTP基于ISO 15765标准实现,同J1939类似。以下是两者的对比:

15765和J1939的对比
  • ISO 15765标准规定的N_PDU

    3个域: N_AI(地址信息)、N_CPI(协议控制信息)、N_DATA(数据域)

    • N_AI

      用于标记对等网络实体间的通信,具体而言是确定信息发送者和接收者的源地址(N_SA)、目标地址(N_TA)、目标地址类型(N_TAtype)、网络地址拓展(N_AE)。对于拓展协议,N_SAN_TA可能会放在CAN标准协议的数据部分的第0个字节,比如拓展CF帧第0字节会放N_SA(告诉接收者我是谁,以便控制数据发送速率),其他拓展帧第0字节会放N_TA

    • N_CPI

      单帧(SF),第0个字节被用于控制([7:4] N_PCItype = 0[3:0] 数据长度SF_DL),数据只需要1帧来传输

      首帧(FF),第0和1个字节被用于控制(Byte0的[7:4] N_PCItype = 1,其余12位用于数据长度FF_DL),数据需要多个帧来传输,FF是多个帧的第1帧

      连续帧(CF),第0字节被用于控制([7:4] N_PCItype = 2[3:0] SN连续帧编号,0到15,如果到达15,下一个重置为0),首帧FF后面的帧是连续帧

      流控(FC),第0字节到2字节被用于控制(具体见标准),目的是调整CF N_PDUs发送的速率。

    • N_DATA

      存放数据

4.2.2.2 CAN TP模块工作流程

1. 单帧

下图是一个CAN IF模块将数据发送到CAN TP模块,CAN TP模块组织管理CAN数据帧(单帧SF)的过程。

单帧过程

PduR_<LoTp>StartOfReception @ PduR_CanTp.c用于向上层COM模块请求Buffer。

PduR_<LoTp>CopyRxData @ PduR_Logic.c用于拷贝数据到COM模块的Buffer,并将剩余可用Buffer空间写入到参数(调用者提供)。

2. 多帧

下图是一个CAN IF模块将数据发送到CAN TP模块,CAN TP模块组织管理CAN数据帧(多帧,包括首帧FF和连续帧CF)的过程。

多帧过程

拷贝过程类似单帧,区别是多帧管理的时候,会有currentPosition来指明目前Buffer已经用掉了多少,下次连续帧过来时,拷贝到可利用的Buffer空间(从currentPosition开始)。

4.2.3 J1939TP

4.2.3.1 J1939TP模块和J1939介绍

注意:J1939标准见具体文件(重点是J1939-21文件) 提取码: p4ra

J1939协议在卡车领域有非常广泛的应用,J1939描述了数据链路层和传输层,主要包括两个传输层协议变种:

  1. BAM (Broadcast Announce Message)用于广播信息;
  2. CMDT (Connection Mode Data Transfer)用于点对点连接。

传输协议功能是数据链路层的一部分,主要完成消息的拆装和重组以及连接管理。长度大于8字节的消息无法使用单个CAN数据帧来传输,因此必须被拆为很多个小的数据包,然后根据标准使用单个的数据帧对这个长消息进行多帧传输,这就要求接收方必须能够接收这些单个的数据帧,然后在重组成原始的消息。

  • 连接管理(TP.CM, Connection Management)

    以下内容见SAE J1939-21.pdf5.10.2连接管理。

    连接管理是用于处理在特定目标地址传输时节点间虚拟连接的打开、使用和关闭。

    • 多组消息广播

      首先要发送一条广播公告消息(BAM)。BAM 消息包含了即将广播的长消息的参数群编号、消息大小和它被拆装的数据包的数目。将要接收该数据的那些节点需要分配好接收和重组数据所需的资源。然后,使用数据传输 PGN(PGN=60160)来发送相关的数据。

    • 连接的开始

      当某个节点传送一条请求发送消息给一个目标地址时,连接就开始了。请求发送消息包含了整个消息的字节大小,要传送的独立消息的数目,以及它设定的参数群编号。如果选择拒绝连接,响应者将发送一条放弃连接消息。连接被拒绝可以有很多种原因,例如,缺少资源、存储空间等等。当发送者(例如,RTS 设备)接收到来自响应者(例如,CTS 设备)的相应的 CTS 消息,那么可以认为已经为发送者建立了连接。当响应者已经成功传送了它的 CTS 消息作为对一个 RTS 消息的响应,那么可以认为已经为响应者建立了连接。

    • 数据传输

      当连接的发送者接收到准备发送消息后,数据传输正式开始。当节点发送了“广播公告消息”后开始数据传输,这时,不需要使用准备发送消息。在传输指定目标地址消息时,由响应者负责调整节点间的数据流控制。在一个开放的连接中,如果响应者想即刻停止数据流,它必须使用准备发送消息把它要接收的数据包数目设置为零。当数据流传输需要停止几秒时,响应者必须每 0.5 秒重复发送一次准备发送消息,来确告发送者连接没有中断。其他所有剩余的数据域都设为 1(无关重要)。

    • 连接关闭

      在传输没有错误的情况下,有两种关闭连接的情形。第一种是在发送给全局目标地址时,第二种是在发送给一个指定目标地址时。

    TP.CM: 传输协议——连接管理

    TP.CM_RTS: 连接模式下的请求发送(指定目标地址)

    TP.CM_CTS: 连接模式下的准备发送(指定目标地址)

    TP.CM_EndofMsgAck: 消息结束应答(指定目标地址)

    TP.CM_Abort: 放弃连接(指定目标地址)

    TP.CM_BAM: 广播公告消息(全局目标地址)

    TP.DT: 数据帧,数据在[1:7]

    —— SAE J1939-21.pdf

    RSV表示保留,数值是255
    
    CM_RTS
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
              16            PDU字节长度        数据帧个数                PGN1  PGN2 PGN3
              
    CM_CTS
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
               17     NumPackets NextPacketSeqNum RSV     RSV         PGN1  PGN2 PGN3
    说明:NumPackets:发送者在下一个CTS发送之前,可以发送的数据帧个数
         NextPacketSeqNum:下一个数据帧的序列号
         
    CM_EndofMsgAck
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
               19           总数据长度      已收到DT数据帧个数  RSV        PGN1  PGN2 PGN3
    
    CM_BAM
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
               32          PDU字节长度        数据帧个数      RVS         PGN1  PGN2 PGN3
               
    CM_Abort
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
               255       原因        RSV       RSV        RSV          PGN1  PGN2 PGN3 
    
    DT
    byte: |    0     |    1     |    2     |    3     |    4     |    5     |...
    data: |----------|----------|----------|----------|----------|----------|...
             序列号     后面是7字节数据
    
  • 传输协议连接管理消息

    参考SAE J1939-21.pdf5.10.3 传输协议连接管理消息。

  • J1939消息类型

    J1939支持5种类型消息,分别为命令、请求、广播/响应、确认和群拓展。**特定消息类型由其分配的参数群编号识别。**对于CAN数据帧中数据域的多字节参数,首先放在低字节,如要将2字节参数存放在CAN数据帧的字节7和8,LSB要被放在字节7,MSB要放在字节8。

    • 命令

      此消息类型包括那些从某个源地址命令特定目的地或全局目的地的参数群,目的地接收到命令类型的消息后应该采取特定的动作。命令类型的消息可能包括传动控制、地址请求、扭矩/速度控制等等。

    • 请求

      此消息类型规定了从全局范围或从特定目的地请求信息的功能。

    • 广播/响应

      此消息类型可能是某设备主动提供的消息广播,也可能是命令或请求的响应。

    • 确认

      第一种是 CAN 协议规定的,它由确认消息已被至少一个节点接收的“帧内”确认组成。

      第二种形式的确认由应用层规定,是对于特定命令、请求的“普通广播”或“ACK”或“NACK”响应。

      具体见标准。

    • 群拓展

      此消息类型用于一组特殊功能(如专用功能、网络管理功能、多组传输功能等)。

  • J1939的信息交互流程

    注意:

    1. 每个数据帧间会设置最大间隔,具体实现为:

    J1939Tp_Internal_StartTimer(&(ChannelInfoPtr->RxState->TimerInfo), J1939TP_T1_TIMEOUT_MS) @ J1939Tp.c

    1. 我的观点:J1939Tp中,BAM和CMDT不能同时支持DIRECT,所以不要同时配置这两种类型。
    • CMDT点对点

      CMDT过程
      1. CM_RTS:我有3个数据帧,共16字节数据
      2. CM_CTS:好的,你先发2个数据帧过来
      3. CM_DT:发送2次,共14个字节数据
      4. CM_CTS:再发1个过来
      5. CM_DT:发送1次,共2个字节数据
    • BAM广播

    BAM过程
    1. CM_BAM:我有16个字节数据,大家准备好
    2. DT:发送3次,共16字节数据
    • DIRECT直接发送

      不需要用到TP.CMTP.DT数据帧,源地址往往包含在CAN数据帧ID中

      BAM过程
4.2.3.2 J1939TP技术
  • 介绍

    AUTOSAR的J1939Tp模块的目的是分段和重组长度超过8个字节的J1939 PGN(N-SDU),对应J1939-21标准。

    J1939协议的收发

    以下是J1939TP的文件结构,

    J1939TP的文件结构

    需要的我们自己完成的文件:J1939Tp_Cfg.hJ1939Tp_MemMap.hJ1939Tp_Lcfg.cJ1939Tp_PBcfg.cJ1939Tp_Lcfg.c可以不需要,将所有的结构体都放在J1939Tp_PBcfg.c

  • 接收处理流程

    • Direct PG

      8字节数据帧,不需要分帧

      PduR_J1939TpStartOfReception @ PduR_Logic.c实现了COM模块的Buffer请求。

    • BAM广播

      BAM
    • CMDT点对点通信

      CMDT
  • 发送处理流程

    pecification of a Transport Layer for SAE J1939

  • J1939Tp MainFunction

    J1939Tp需要运行函数J1939Tp_MainFunction @ J1939Tp.c,设计成一个任务或者使用SCHM模块来调度。

    具体流程为:

    如果J1939TP模块开启(globalState.State == J1939TP_ON),则对每一个J1939TP的通道(在J1939中,一个通道是指具有相同SA源地址和TA目标地址的PDU,可以有不同的多个PG)进行以下操作

    1. 通道方向是发送

      1. 计时器自增

      2. 判断是否超时

        如果超时,针对不同的状态,进行不同操作,具体见J1939Tp_MainFunction @ J1939Tp.c

    2. 通道方向是接收

      1. 定时器自增

      2. 判断是否超时

        超时则重置通道,并发送连接断开信息

4.2.3.3 J1939TP的配置
  • Channels、PGs和Relations

    J1939TP顶层的配置结构体是J1939Tp_ConfigType,如下

    typedef struct {
          
          
        const J1939Tp_RxPduInfoRelationsType* RxPduRelations;
        const J1939Tp_ChannelType*            Channels;
        const J1939Tp_PgType*                 Pgs;
    } J1939Tp_ConfigType;
    

    RxPduRelations可以对应到每个PDU ID,也就是说,在发送、接收的时候,都是索引到某一个RxPduRelations,然后再发送出去。

    Channels对应到DA(目的地址)和SA(源地址)相同的所有PDU(无MetaData)。

    Pgs对应不同的参数组,一个Channel(也就是相同的发送者和接收者)中可以有多个PG。

  • 基于CMDT的数据收发过程

    (1)Sender send RTS, Receiver receive RTS
    Receiver:
    	1.根据CAN IF模块的索引,找到Relaitions[i]
    	2.Relaitions[i]对应到多个RxPdus,进行逐个遍历,具体为CM(连接管理)、DT(数据传输)、DIRECT(直接数据传输)等,而且RxPdus每个都会配置对应到哪个Channel
    	3.因为是RTS,所以索引到CM时,就可以对应
    	4.在CM对应的处理函数中,取出RTS数据域中的PGN(Byte[5:7])
    	5.根据上述PGN遍历Channel中的PGs,找到对应的PG
    	6.J1939TP管理的ChannelInfoPtr结构体变量(每个Channel都会有一个对应的)进行初始化,准备开始接受,具体包括已接收DT数据帧个数、剩下需要接收的DT数据帧个数、总数据字节个数、currentPgPtr等
    	7.请求COM准备Buffer,如果有错误就会断开此次连接,否则进行下面
    	8.开始计时器,发送CTS(会指定到下次发送CTS前,可以发送多少帧DT)
    
    (2)Receiver send CTS, Sender receive CTS
    Sender:
    	1.根据CTS设置的发送数据帧个数,来逐个发送DT数据帧
    

4.2.4 PDU Router

PDUR流程图在SWS_PDURouter.pdf文件P87

PDUR配置内容在SWS_PDURouter.pdf文件P108

PDUR = Protocal Data Unit Router。PDUR通过识别IPDU的ID,将AUTOSAR COM和DCM IPDU部署为不同的通信协议(CAN、LIN、FlexRay等)。PDUR也用于确定一个传输协议是否已经被使用。当没有速率转换时,作为网关。PDUR是AUTOSAR通信结构的中心,见下图。

PDUR和其他模块的关系

PDUR模块包含两个部分:

  • PDUR routing tables

    静态路由表描述了每个IPDU打包成某一中通信协议(CAN、LIN等)。路由表可以在Post-build后更新(在运行时更新?)或者选择初始化PDU的时候选择。

  • PDUR引擎

    • 实现IPDU的打包到目的地,如上层(Upper Layer)COM、DCM、LDCOM等、接口模块(IF Module)CANIF、LINIF、FRIF等、传输协议层(TP)CANTP、LINTP等,具体见ARC_PduR_ModuleType的具体定义。具体分发位置(接收)PduR_ARC_RxIndication和(发送)后续补充
    • 将IPDU的ID打包到目标空间(比如PduR_Transmit函数到CanIf_Transmit函数,PduR_CanIfTxConfirmation函数到Com_TxConfirmation函数)
    • 提供最小路由能力,即使PDUR路由表损坏或者没有编程也可以通过DCM来激活ECU的Bootloader

PDUR模块可以实现多种传输方式:

PDUR的传输方式

SF:Single Frame,单帧,传输协议术语(Transport Protocol term)

FF:First Frame, 首帧,传输协议术语

CF:Consecutive Frame, 连续帧,传输协议术语

4.3 系统服务

System Services

服务层架构

4.3.1 BswM

4.3.2 EcuM

以EcuM为中心,系统启动分为3大步:OS启动前配置(StartPreOS Sequence)、OS启动后的配置(StartPostOS Sequence)、任务正式运行。

EcuM启动过程
  • PreOS

    OS启动前的配置

    操作说明见下图,

    OS启动前的其他配置

    PreOs阶段初始化了两类,

    第0和1阶段初始化内容
  • PostOS

    OsStartupTask任务调用(运行1次)EcuM_StartupTwo函数

    StartPostOS_Sequencea

5、BSW-复杂驱动层

Complex Drivers Layer,涉及到从硬件到RTE,任务

  • 在AUTOSAR中没有定义的芯片特殊驱动
  • 高实时限制

6、RTE

Runtime Environment,是 AUTOSAR 虚拟功能总线(Virtual Function Bus,VFB)的接口(针对某个特定 ECU)的实现,为应用程序(软件组件)之间的通信提供了基本的服务,同时也便于访问包含 OS 的基本软件组件。

为了满足系统设计者所做的一些限制,应用程序组件能够在系统配置期间被映射到任何有效的 ECU 上。RTE 负责确保这些(应用程序)组件能够通信,提供了在 AUTOSAR 软件组件间通信的基础服务。

7、App

顶层应用层包括软件组件SWC和端口Ports两部分。

应用层功能

8、面向功能

8.1 CAN

几个重点函数和文件:EcuM_AL_DriverInitTwo@EcuM_Callout_Stubs.c(初始化CAN、CanIf、CanSM等)、EcuMConfig@EcuM_PBcfg.cEcuM_World.config指向的参数)

8.1.1 CAN通信架构

CAN通信栈

CAN通信栈
  • 属性
    • 通用网络管理接口(Generic NM Interface)只包含一个dispatcher(?)。可以实现同步(?)同一种或者不同类型的网络
    • CAN网络管理(CAN NM)专门用于CAN网络
    • CAN状态管理(CAN State Manager)处理通信系统的开启和关闭。控制COM的不同选项,来发送PDU并管理信号超时。

J1939通信栈

J1939协议栈拓展了CAN协议,用于重型车辆。

CAN通信栈拓展J1939

**在CAN通信栈中,有两个传输一些模型——CanTp和J1939Tp,可以单独选择一个使用或者在不同的通道使用。**CanTp:ISO Diagnostics(DCM),PDU在标准CAN总线传输。J1939Tp:J1939 Diagnostics,PDU在J1939驱动的CAN总线传输。

  • 属性
    • 支持动态帧ID
    • J1939 NM可以为每个ECU分配唯一的地址,但是不支持睡眠/唤醒操作和partial networking(?)的概念
    • 提供 J1939诊断和对应操作。

8.1.2 CAN通信流程

下面流程只关注CAN相关内容,其他内容略过。详细内容见BswM小节。

  • 第一阶段:OS启动前配置

    整个系统从int main(void)@os_init.c开始,调用EcuM_InitEcuM_Init函数会给EcuM_World.config 赋值(EcuM_World.config = EcuM_DeterminePbConfiguration()),指向了后期OsStartupTask任务需要使用的所有配置参数(一个结构体EcuMConfig@EcuM_PBcfg.c)。

  • 第二阶段:Os启动后配置

    OsStartupTask任务调用(运行1次)EcuM_StartupTwo函数,进而调用EcuM_AL_DriverInitTwo(EcuM_World.config)来初始化COM、CanIf等软件模块(EcuM_AL_DriverInitOne在第一阶段完成,初始化PORT、USART等硬件相关内容)。CanCtrlPwm工程使用到的模块包括:BSWM CAN CANIF CANSM COM COMM CRC DET DIO ECUM_FIXED IOHWAB KERNEL MCU OS_DEBUG PDUR PORT PWM RAMLOG RTE USART

    以下是EcuM_AL_DriverInitTwo(const EcuM_ConfigType* ConfigPtr)中的CAN通信相关内容:

    • CanTrcv_Init函数 - USE_CANTRCV

      CAN收发器,目前没有用到

    • Can_Init函数 - USE_CAN

      Can_Init(ConfigPtr->PostBuildConfig->CanConfigPtr)初始化CAN(PostBuildConfig@EcuM_PBHeader.c中定义了BSWM、CAN、CANIF、CANNM、CANTP、COM、COMM、PDUR、CANTRCV、FIM信息,其中CanConfigPtr指向CanConfigData@Can_PBcfg.c

    • CanIf_Init - USE_CANIF

      CanIf_Init(ConfigPtr->PostBuildConfig->CanIfConfigPtr)@CanIf.c初始化CanIf(PostBuildConfig@EcuM_PBHeader.cCanIfConfigPtr指向CanIf_Config@CanIf_PBcfg.c

  • 第三阶段:任务周期运行

    CAN数据接收

    数据接收内容,具体见20191202(实际开会日期20191201)组会PPT,提取码: gjbb。

    数据接受内容补充,具体见20191209(实际开会日期20191208)组会PPT(含动图),提取码:3kz0

    CAN数据发送

    以PWM控制为例。

    (1)RTE任务阶段

    • RTE任务调用PWM设置管理任务,实现数据获取、处理和CAN发送

      具体分为PRE、MAIN和POST三部分,PRE主要实现将CAN数据从COM的数据缓存区获取PWM信息(Com_ReceiveSignal @ Rte_Internal_PwmSetManager.c);MAIN实现接受到的数据处理,并保存到相关数据结构中;POST实现将处理后的数据放到BUFF中,供其他的SWC使用,并且将该数值发送到CAN总线(Com_SendSignal(ComConf_ComSignal_PwmDutyOut, &value) @ Rte_Internal_PwmSetManager.c),以反馈给其他ECU。

    • Com_SendSignal @ Com_Com.c

      Com_SendSignal有两个参数,第一个是信号ID(用于找到具体的PDU),第二个是要发送的数据指针,主要实现的是将数据放到PDU中,共后期BSW任务发送。在数据放到PDU之前,还会判断PDUBuffer是否正在被使用,如果是,则返回BUSY。

      • Com_Misc_WriteSignalDataToPdu

        将数据放到PDU Buffer中。参数包括SignalDataPtr(数据源)、signalType(信号数据类型,对应ComSignalType @ ConSignal ,比如32位数据COM_UINT32、N个字节数据COM_UINT8_N等,我们真正使用的时候,CAN数据帧中有多中数据,所以采用COM_UINT8_N)、comIPduDataPtr(IPDU Buffer起始地址,即Arc_IPdu->ComIPduDataPtr)、bitPosition(对应ComBitPosition @ Com_PbCfg.c,实现将数据放到相对于IPDU Buffer起始位置增加OFFSET的位置)、bitSize(对应ComBitSize @ Com_PbCfg.c ,表示存多少bits的数据,一般对于CAN而言就是64bits,共8bytes)、endian(大小端)、dataChanged(表示数据最后有没有存放进去)。

      • Com_Misc_TriggerTxOnConditions

        实现条件触发,可以在内部直接发送或者其他任务周期性发送。参数包括ComIPduHandleId(对应IPDU的ID,进而可以找到特定的IPDU位置)、dataChanged(上一步传下来的数据,要发送的数据是否存进去了)、ComTransferProperty(对应ComTransferProperty @ Com_PbCfg.c,指定TX条件触发类型)。

        *如何实现数据的直接发送和周期性发送?*见ComTransferProperty

    (2)BSW任务阶段

    • Com_MainFunctionTx @ Com_Sched.c

      本函数会遍历所有IPDU,依次将数据发送出去。对于ComTxModeMode @ Com_PbCfg.c = COM_DIRECT的配置直接发送,。对于ComTxModeMode @ Com_PbCfg.c = COM_PERIODIC||COM_MIXED的配置需要周期性发送,也就是BSW任务周期的倍数(通过ComTxModeTimePeriodFactor @ Com_PbCfg.c配置)。

    • PduR_ARC_Transmit @ PduR_Logic.c

      本函数内部可以找到路径RoutingPaths[PduId] @ PduR_PbCfg.c,然后通过for循环来对这条路径配置的多个目的地来进行分发。

      for (uint8 i = 0; route->PduRDestPdus[i] != NULL; i++){
              
              
          const PduRDestPdu_type * destination = route->PduRDestPdus[i];
          status = PduR_ARC_RouteTransmit(destination, PduInfo);// 数据发送
          if( (PDUR_E_REJECTED == totalStatus) || (PDUR_E_REJECTED == status)) {
              
              
          totalStatus = PDUR_E_REJECTED;
          } else if(PDUR_E_OK == status) {
              
              
          totalStatus = PDUR_E_OK;
          } else {
              
              
          /* Rejected */
          }
      }
      

8.1.3 CAN关键配置参数

我使用了[MULTI CAN NEED CHANGE]标记来注明需要修改的地方。

于此同时,在修改代码的时候,使用[MULTI CAN CHANGE]来标注代码修改了哪些地方。

  • 通用配置

    EcuM_AL_DriverInitOne调用Mcu_Init@Mcu_stm32.c实现时钟初始化(参数@Mcu_PBcfg.c),调用Port_Init@Port_stm32.c实现引脚配置如remap、pin、port(参数@Port_PBcfg.c

  • CAN硬件配置Can_stm32和英飞凌结构不同,直接使用EB生成的

    CAN相关配置(Can_Init @ Can_stm32.c),检查CAN个数是否匹配、CAN中断、波特率、HTH,会用到Can_cfg.hCan_PBcfg.c中的参数。下面列出一些重要的配置参数和说明:

    • CAN_ARC_CTRL_CONFIG_CNT @ Can_Cfg.h

      CAN配置个数,配2个就要设置为2,进而遍历下面的CanControllerConfigData来配置每个CAN。

    • Can_ControllerConfigType CanControllerConfigData[] @ Can_PBcfg.c

      每个CAN都要配置一个Can_ControllerConfigType类型的结构体,包括:

      1. 两个CAN的哪一个CanControllerId

      2. HOH配置信息Can_Arc_Hoh

      3. 使用的哪个FIFO Can_Arc_Fifo

      4. 默认的波特率CanControllerDefaultBaudrate,默认波特率必须在后面的CanControllerSupportedBaudrates中存在,Can_ChangeBaudrate设置波特率的时候会在Can_CheckBaudrate中校验,并通过CanControllerDefaultBaudrate找到CanControllerSupportedBaudrates中所有支持的波特率配置信息中的一个来进行CAN的波特率改变,CAN运行时候也可以通过Can_ChangeBaudrate实现波特率的修改

      5. 多个波特率的配置信息(进而可以计算出bs1、bs2、sjw、CAN_Prescaler等)CanControllerSupportedBaudrates

      6. 支持多少种波特率CanControllerSupportedBaudratesCount,和上面的CanControllerSupportedBaudrates波特率及其配置信息数目相同。

      注:CAN默认使用掩码模式,可以通过软件ID滤波

    • Can_SupportedBaudrates_CanController @ Can_PBcfg.c

      支持的波特率,每个波特率为一个结构体,包括波特率CanControllerBaudRate和其他信息(进而可以计算出bs1、bs2、sjw、CAN_Prescaler等)。Can_ChangeBaudrate设置波特率的时候会通过对比设置的波特率和CanControllerBaudRate来查找对应的波特率设置结构体。

    • CanHardwareObjectConfig_CanController @ Can_PBcfg.c

      定义接收和发送的HOH(HRH和HTH)。在EB配置中,TX的CanObjectId必须高于RX。

      • CanObjectId

        HOH的ID号,用于后期索引CanIf的配置信息。比如,在MultiCan这个工程中,第一个hrh的配置为,

        	{
                  
                  
        		.CanObjectId	=	CanConf_CanHardwareObject_CanHardwareObjectRx,//CanHardwareObjectRx,
        		.CanHandleType	=	CAN_ARC_HANDLE_TYPE_BASIC,  //[scc] 分为Basic和Full,Basic:接收一组单个Can Id数据,应该是列表模式;Full:接收单个Can Id数据
        		.CanIdType		=	CAN_ID_TYPE_STANDARD,
        		.CanObjectType	=	CAN_OBJECT_TYPE_RECEIVE,
        		.CanHwFilterCode =  0x0,
        		.CanHwFilterMask =	0x0,
        		
        		.Can_Arc_EOL	= 	0   // [scc] 此处表示配置没有结束,在Can_Init中根据Can_Arc_EOL是否等于1来判断一个CAN是否配置完毕
        		
        	},
        

        在中断到来时,调用CanIf的接口CanIf_RxIndication(Can_HwHandleType hrh, Can_IdType canId, uint8 canDlc,const uint8 *canSduPtr),其中hrh参数为CanObjectIdCanIf_RxIndication @ CanIf.c会先通过CanIfHohConfigData->CanHohToCanIfHrhMap来找到要使用的HOH的配置信息的索引,在通过该索引找到HOH的配置信息。

  • CAN IF配置

    CANIF相关配置(CanIf_Init @ CanIf.c),包括后期运行使用到的参数。在CanIf_Init @ CanIf.c中初始化了非常少量的内容,因为CANIF_PUBLIC_PN_SUPPORTCANIF_PUBLIC_WAKEUP_CHECK_VALIDATION_SUPPORTCANIF_PUBLIC_TX_BUFFERINGCANIF_ARC_TRANSCEIVER_API等都没开。收发器目前搁置,需要对应特定的芯片。

    • CanIf_Config @ CanIf_PBCfg.c

      总体的CanIf配置信息

      • InitCofig

        存放初始化信息, 比如收发个数、TX的Buffer个数等,引用CanIfInitConfig @ CanIf_PBCfg.c

      • CanIfTransceiverConfig

        CANIF的收发器配置。

      • Arc_ChannelConfig

        存放CanIf的通道配置,目前来看一个硬件CAN对应一个通道,引用CanIf_Arc_ChannelConfig @ CanIf_PBCfg.c

    • CanIfInitConfig @ CanIf_PBCfg.c

      [MULTI CAN NEED CHANGE]

      • CanIfConfigSe:未使用
      • CanIfNumberOfCanRxPduIds:接收的CAN数据帧ID(范围)个数,对应为CanIf_RxPduConfigType HrhRxPdu_CanIfHrhCfg @ CanIf_PBCfg.c中的RX配置信息个数。
      • CanIfHohConfigPtr:保存了HOH信息,指向CanIf_InitHohConfigType CanIfHohConfigData @ CanIf_PBCfg.c
      • CanIfNumberOfCanTXPduIds:发送的CAN数据帧ID及配置信息个数,对应为CanIf_TxPduConfigType CanIfTxPduConfigData @ CanIf_PBCfg.c中TX配置信息个数。
      • CanIfTxPduConfigPtr:保存了发送的CAN数据帧ID及配置信息,指向CanIf_TxPduConfigType CanIfTxPduConfigData
      • CanIfNumberOfDynamicCanTXPduIds:未使用
      • CanIfNumberOfTxBuffers:未使用。CANIF_PUBLIC_TX_BUFFERING设置为STD_ON才会在CanIf_Init中对该Buffer进行初始化(map)和使用。
      • CanIfBufferCfgPtr:未使用。CANIF_PUBLIC_TX_BUFFERING设置为STD_ON才会在CanIf_Init中对该Buffer进行初始化(map)和使用。
    • CanIf_Arc_ChannelConfigType CanIf_Arc_ChannelConfig @ CanIf_PBCfg.c

      CanIf的Channel信息。见下图:

      AUTOSAR AUTOSAR CANIF Channel Group

      每一个硬件CAN都对应一个CanIf Channel(CANIF_CHANNEL_CNT @ CanIf.h定义CanIf Chennel个数 [MULTI CAN NEED CHANGE]),进而上层可以进行相应的控制和状态管理。

      • CanControllerId:CanController的编号,在Can_Cfg.h中 定义。
      • NofTxBuffers:未使用
      • TxBufferRefList:未使用
      • CanIfCtrlWakeUpSupport:设置为STD_OFF
      • CanIfCtrlWakeUpSrc
      • CanIfCtrlPnFilterSetSTD_OFF
    • CanIf_InitHohConfigType CanIfHohConfigData @ CanIf_PBCfg.c

      //TODO

    • CanIf_TxPduConfigType CanIfTxPduConfigData @ CanIf_PBCfg.c

      //TODO

    • CanIf_RxPduConfigType HrhRxPdu_CanIfHrhCfg @ CanIf_Cfg.h

      [MULTI CAN NEED CHANGE]

      CanIf_RxPduConfigType数据类型的数组变量HrhRxPdu_CanIfHrhCfg连接了下面的MCAL和上层服务层模块,比如PDUR、CANTP、J1939TP、XCP等。具体见下面的成员变量,

      SECTION_POSTBUILD_DATA const CanIf_RxPduConfigType HrhRxPdu_CanIfHrhCfg[] = {
              
              
      	{
              
              
      		.CanIfCanRxPduId 			= PDUR_PDU_ID_PDURX,  // 指定下一个模块的PDU编号,在对应模块中定义,如果下一个是PDUR,可以用来决定RoutingPath
          	.CanIfCanRxPduLowerCanId 	= 1,   // [scc] CAN ID下界
          	.CanIfCanRxPduUpperCanId 	= 1,   // [scc] CAN ID上界
          	.CanIfCanRxPduDlc 			= 8,   // [scc] 字节长度,在中断到来后会调用canDlc < rxPduCfgPtr->CanIfCanRxPduDlc来判断是否出错
          	.CanIdIsExtended	 		= false, // [scc] 标准帧或者拓展帧
          	.CanIfUserRxIndication 		= PDUR_CALLOUT,  // [scc] 指定CanIf向上输出到PDUR,还有其他的CANNM、CANTP、J1939TP
      	},
      };
      

      具体的CANIF分发流程(CanIf_RxIndication @ CanIf.c):

      1. 寻找CAN数据ID对应的配置信息,即HrhRxPdu_CanIfHrhCfg数组中的一个

        具体实现方式见下方CANIF过滤方式 @ CanIf_Cfg.h

      2. 根据配置信息的CanIfUserRxIndication成员找到对应的下一个模块

        CanIfUserRxIndications[rxPduCfgPtr->CanIfUserRxIndication](rxPduCfgPtr->CanIfCanRxPduId, &pduInfo);其中CanIfUserRxIndications是一个包含了很多模块调用函数指针结构体,见下面代码

        const CanIfUserRxIndicationType CanIfUserRxIndications[] = {
                  
                  
        #if defined(USE_CANNM)	
        	CanNm_RxIndication,
        #else
        	NULL,
        #endif	
        #if defined(USE_CANTP)	
        	CanTp_RxIndication,
        #else
        	NULL,
        #endif
        #if defined(USE_J1939TP)
        	J1939Tp_RxIndication,
        #else
        	NULL,
        #endif
        #if defined(USE_PDUR)	
        	PduR_CanIfRxIndication,
        #else
        	NULL,
        #endif
        #if defined(USE_XCP)	
        	Xcp_CanIfRxIndication,
        #else
        	NULL,
        #endif	
        	Arc_T1_Rx_Cbk,
        
        };
        

        举例而言,我们这边配置了PDUR_CALLOUT,调用了PDUR模块。具体实现见XXX_CALLOUT @ CanIf_Cfg.h

      3. 输入PduId来实现下一步分发(具体见PDUR配置

        以CanIf发送到PDUR为例,CanIf调用PduR_CanIfRxIndication(PduIdType PduId, NotifResultType Result, uint8 serviceId) @ PduR_Logic.c,并会输入参数CanIfCanRxPduId(见上方)作为PduId,从而可以找到PduR配置的分发路径。实现的代码如下:

        PduRConfig->RoutingPaths[PduId]->PduRDestPdus[i]
        ...
        
    • CANIF_PUBLIC_TX_BUFFERING @ CanIf_Cfg.h

      开启发送L-PDU缓存区,每次CanIf_Init()需要初始化每个分配到CANIF的Transmit L-PDU Buffer(req SWS_CANIF_00387)。目前可设置为STD_OFF

    • CANIF过滤方式 @ CanIf_Cfg.h

      CANIF可以针对CAN数据的ID来搜索对应的配置信息(即HrhRxPdu_CanIfHrhCfg数组中的一个

      ),进而找到下一个要分发的模块(详细过程见HrhRxPdu_CanIfHrhCfg @ CanIf_Cfg.h )。具体的查询模式包括两种,线性查找模式

      CANIF_PRIVATE_SOFTWARE_FILTER_TYPE_LINEAR和二分查找模式CANIF_PRIVATE_SOFTWARE_FILTER_TYPE_BINARY,在二分查找模式中需要将HrhRxPdu_CanIfHrhCfg数组的成员按照CAN ID上下界大小来排序,进而可以使用二分查找配置信息。

      具体如何配置:#define CANIF_PRIVATE_SOFTWARE_FILTER_TYPE_LINEAR或者#define CANIF_PRIVATE_SOFTWARE_FILTER_TYPE_BINARY即可。

    • XXX_CALLOUT @ CanIf_Cfg.h

      CANNM_CALLOUTCANTP_CALLOUTJ1939TP_CALLOUTPDUR_CALLOUTXCP_CALLOUT等定义了CANIF传输信息到下一个模块所对应的编号(详细过程见HrhRxPdu_CanIfHrhCfg @ CanIf_Cfg.h )。CanIfUserRxIndications这个函数指针结构体中包含了很多模块的调用函数,顺序分别是CanNm、CanTp、J1939Tp、PduR、Xcp,所以CANNM_CALLOUTCANTP_CALLOUTJ1939TP_CALLOUTPDUR_CALLOUTXCP_CALLOUT分别定义为0、1、2、3、4,进而可以搜索到对应模块。

    • CanIfInitConfig @ CanIf_PBCfg.c

      CanIf_RxIndication @ CanIf.c中用CanIfInitConfig找到特定的HRH。比如,CanIfInitConfig中的一条语句:

      CanIf_ConfigPtr->InitConfig->CanIfHohConfigPtr->CanIfHrhConfig[CanIf_ConfigPtr->InitConfig->CanIfHohConfigPtr->CanHohToCanIfHrhMap[hrh]]
      

      这里的InitConfig指向CanIfInitConfig,而hrh参数是CAN中断调用了CanIfInitConfig后输入的hrh编号。

      SECTION_POSTBUILD_DATA const CanIf_InitConfigType CanIfInitConfig =
      {
              
              
      	.CanIfConfigSet 					= 0, // Not used  
      	.CanIfNumberOfCanRxPduIds 			= 1, // [scc] CAN 接收PDU个数
      	.CanIfNumberOfCanTXPduIds 			= 1, // [scc] CAN 发送PDU个数
      	.CanIfNumberOfDynamicCanTXPduIds	= 0, // Not used
      	.CanIfNumberOfTxBuffers				= 1,
      
      // Containers
      	.CanIfBufferCfgPtr					= CanIfBufferCfgData,
      	.CanIfHohConfigPtr 					= CanIfHohConfigData,
      	.CanIfTxPduConfigPtr 				= CanIfTxPduConfigData, // 发送PDU配置信息
      };
      

      具体而言,在中断到来时,Can中断服务函数调用CanIf的接口CanIf_RxIndication(Can_HwHandleType hrh, Can_IdType canId, uint8 canDlc,const uint8 *canSduPtr),其中hrh参数为CanObjectIdCanIf_RxIndication @ CanIf.c会先通过CanIfHohConfigData->CanHohToCanIfHrhMap来找到要使用的HOH的配置信息的索引,在通过该索引找到HOH的配置信息。

      • CanIfTxPduConfigPtr指向CanIfTxPduConfigData

        [MULTI CAN NEED CHANGE]

        1. CanIfTxPduId:如果CANIF无法发送,比如设置为CANIF_GET_OFFLINE_ACTIVE或者CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE,则通过这个CanIfTxPduId来将信息反馈给PDUR->COM
        2. CanIfCanTxPduIdCanId:发送CAN的数据帧ID
        3. CanIfCanTxPduIdDlc:CAN数据帧长度
        4. CanIfTxPduIdCanIdType:CAN数据帧ID的类型,包括CANIF_CAN_ID_TYPE_29CANIF_CAN_FD_ID_TYPE_29CANIF_CAN_ID_TYPE_11CANIF_CAN_FD_ID_TYPE_11
        5. CanIfUserTxConfirmation:PDUR调用了CanIf_Transmit后,(如果无法发送,比如设置为CANIF_GET_OFFLINE_ACTIVE或者CANIF_GET_OFFLINE_ACTIVE_RX_ONLINE)会反馈信息给PDUR
  • PDUR配置

    • PduR_Config @ PduR_PbCfg.c

      • PduRConfigurationId

      • RoutingPaths

        RoutingPaths包含了PDUR的分发多个条分发路径,可以通过PduR_ARC_RxIndication输入的PduId来确定具体的路径。

        举例来说,CanIf调用PduR_CanIfRxIndication(PduIdType PduId, NotifResultType Result, uint8 serviceId) @ PduR_Logic.c,并会输入参数CanIfCanRxPduId(见上方)作为PduId,从而可以找到PduR配置的分发路径。

  • COM配置

    Com_Init @ Com.c对COM模块进行初始化。在Com_Init函数中会对每个COM模块的IPDU进行配置(具体配配置信息为ComIPdu @ Com_PbCfg.cArcIPud是一一对应的,所以在配置的时候通过GET_IPdu(i)GET_ArcIPdu(i)分别得到IPDU配置信息、包含ComSignalComIPduArc_IPdu)。

    • ComIPdu @ Com_PbCfg.c

      • ArcIPduOutgoingId

      • ComRxIPduCallout

      • ComTxIPduCallout

      • ComTriggerTransmitIPduCallout

      • ComIPduSignalProcessing

        枚举类型Com_IPduSignalProcessingMode,包括COM_IMMEDIATECOM_DEFERREDCOM_IMMEDIATE表示将数据立即处理,而不拷贝到Deferred Buffer,COM_DEFERRED表示需要先将数据拷贝到Deferred Buffer,再进行相应的处理。

        DeferredBuffer的作用
      • ComIPduSize

        存储通信数据的单个IPDU存储空间大小。Com_Init @ Com.c 函数会根据ComIPduSize来分配每个IPDU的大小。比如,下图

        IPDU如何分配

        ComIPduSize配置为64bytes,那么单个IPDU会配置专门用于数据刚接收时存放的空间IPdu_Rx、开启信号组后(在Arcore中,未开启)需要配置的Shadow_Buff_RxDeferred_IPdu

        其实对于CAN而言只需要8bytes即可

      • ComIPduDirection

        枚举类型Com_IPduDirection,包括COM_RECEIVE(IPDU用于接收数据)和COM_SEND(IPDU用于发送数据)。

      • ComIPduSignalRef

        指向Signal结构体数组(可以包含多个Signal)。

      • ComIPduGroupRefs

        **具体作用待进一步确认。**指向一个ComIPduGroup_type类型数组,只包含一个ComIPduGroupHandleId

      • ComTxIPdu

        1. ComTxIPduMinimumDelayFactor

        2. ComTxIPduUnusedAreasDefault:发送的IPDU赋初始,一般配置为0

        3. ComTxIPduClearUpdateBit:定义什么时候IPDU更新update标志位(接收的IPDU不需要配置),使用枚举类型ComTxIPduClearUpdateBitType

        4. ComTxModeTrue

          [MULTI CAN NEED CHANGE]

          1. ComTxModeMode:发送模式配置,枚举类型ComTxModeModeType,包括COM_DIRECTCOM_MIXEDCOM_NONECOM_PERIODIC。接收的IPDU配置为COM_NONE

            COM_PERIODIC:周期性发送,其他任务调用Com_MainFunctionTx

            COM_DIRECTCOM_MIXED:在Com_Misc_TriggerTxOnConditions中会判断COM_DIRECTCOM_MIXED,貌似会直接发出去,而COM_PERIODIC是周期性发送的,由其他任务实现

          2. ComTxModeNumberOfRepetitions

            一个数据重复发送的次数,一般配置为0。

          3. ComTxModeRepetitionPeriodFactor

          4. ComTxModeTimeOffsetFactor

          5. ComTxModeTimePeriodFactor

      • 其他省略

    • Com_Signal @ Com_PbCfg.c

      Com_Signal中包含所有signal,每个signal是一个结构体数组中的结构体,下面是每个结构体中的成员信息:

      • ComHandleId

        所在结构体在Com_Signal中的索引,比如第0个是等于0,第1个等于1。该成员在Com_Init @ Com.c中用于查找对应的ArcSignalGET_ArcSignal(Signal->ComHandleId)获取)。

      • ComIPduHandleId

        可以找到COM中对应的IPDU

      • ComFirstTimeoutFactor

        待进一步确定作用。在Com_Init中判断ComTimeoutFactor大于0而且该IPDU是COM_RECEIVE,用ComFirstTimeoutFactorArc_Signal->Com_Arc_DeadlineCounter赋值。如果是COM_SEND,则需要用下面的ComTimeoutFactor赋值。

      • ComNotification

      • ComTimeoutFactor

        待进一步确定作用。在Com_Init中判断ComTimeoutFactor大于0而且该IPDU是COM_SEND,用ComTimeoutFactorArc_IPdu->Com_Arc_TxDeadlineCounter赋值。

      • ComTimeoutNotification

      • ComErrorNotification

      • ComTransferProperty

        发送

        包括COM_PENDINGCOM_TRIGGEREDCOM_TRIGGERED_WITHOUT_REPETITIONCOM_TRIGGERED_ON_CHANGE_WITHOUT_REPETITIONCOM_TRIGGERED_ON_CHANGE等枚举类型。

        触发发送条件的时候,调用Com_Misc_TriggerTxOnConditions(uint16 pduHandleId, boolean dataChanged, ComTransferPropertyType transferProperty) @ Com_misc.cComTransferProperty作为该参数来实现触发TX,如果transferPropertyCOM_TRIGGEREDCOM_TRIGGERED_WITHOUT_REPETITIONCOM_TRIGGERED_ON_CHANGE_WITHOUT_REPETITIONCOM_TRIGGERED_ON_CHANGE其中一个,而且ComTxModeMode @ Com_PbCfg.cCOM_DIRECT或者COM_MIXED,可以直接发送(所谓直接发送指的是,Com_SendSignal函数调用了Com_Misc_TriggerTxOnConditions直接发送数据),而不需要经过其他的任务。实际上就是BSW任务的周期为发送周期。

        如果是COM_PENDING则要通过其他的任务来发送,比如ComTxModeMode @ Com_PbCfg.c配置成COM_PERIODIC,则可以在BSW Main任务中调用Com_MainFunctionTx @ Com_Sched来周期性或者直接(实际上还是周期性,只是周期和BSW Main任务的周期相同)发送数据。具体为如下代码:

        /* If IPDU has periodic or mixed transmission mode.*/
        if ( (txModePtr->ComTxModeMode == COM_PERIODIC)
            || (txModePtr->ComTxModeMode == COM_MIXED) ) {
                  
                  
            Com_ProcessMixedOrPeriodicTxMode(i,dmTimeOut);
            /* If IPDU has direct transmission mode.*/
        } else if (txModePtr->ComTxModeMode == COM_DIRECT) {
                  
                  
            Com_ProcessDirectTxMode(i,dmTimeOut);
        }
        

        ComTransferProperty是和ComIPduComTxModeMode配合使用的,具体见Com_Misc_TriggerTxOnConditions @ Com_misc.c函数

        接收

      • ComUpdateBitPositionComSignalArcUseUpdateBit

        Com_Init @ Com.c中给IPDU清零,即

        #define CLEARBIT(dest,bit)	( *( (uint8 *)dest    + (bit / 8) ) &= ~(uint8)(1u << (bit % 8u)) )
        if (TRUE == Signal->ComSignalArcUseUpdateBit) {
                  
                  
            CLEARBIT(Arc_IPdu->ComIPduDataPtr, Signal->ComUpdateBitPosition);
        }
        

        CLEARBIT函数是将低3位(0至7)作为清零位,高位作为偏移位,即是8的几倍就针对dest地址加几的地址空间进行清零。

        ComSignalArcUseUpdateBit如果是True,则会启用这一功能,将这一位作为signal更新位,如果有数据要发送,则会通过Com_SendSignal @ Com_Com.c中的SETBIT(comIPduDataPtr, Signal->ComUpdateBitPosition)来对特定位进行设置。

      • ComSignalInitValue

        初始化数值(指向一个空间),Com_Init @ Com.c会调用Com_Misc_WriteSignalDataToPdu @ Com_misc.c,并将ComSignalInitValue作为参数来对IPDU初始化,初始化的长度由ComBitSize决定。

      • ComBitPosition

        用于定位数据存放在IPDU的哪一位开始。比如,ComBitPosition = 8(一般都是0)

        ComBitPosition / 8 = 1,也就是index = 1
        |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-...-|-|-|  共64bytes
          |
        index
        

        不过一般都是将ComBitPosition配置为0。

      • ComBitSize

        数据位数,数据存入IPDU时拷贝这么多空间,即memcmp(pduBufferBytes, SignalDataPtr, ComBitSize)ComBitSize大小决定了以ComSignalInitValue为起始地址,向后拷贝的空间大小。比如,

        ComBitSize = 32,  ComBitPosition = 0
        |----|  ComSignalInitValue(-表示1个byte)
        |----------------....---| IPDU
        

        这里4bytes(32位)的数据拷贝到IPDU Buffer中,也就是说其实只用了4个byte,CAN数据帧其实有8个byte。

      • ComSignalEndianess

        大小端模式,枚举类型ComSignalEndianess_type,包括COM_BIG_ENDIANCOM_LITTLE_ENDIANCOM_OPAQUE

      • ComSignalType

        [MULTI CAN NEED CHANGE]

        数据类型,分为8位、16位、32位等。如果想要1个字节1个字节的赋值,则配置为COM_UINT8_N(多个uint8);如果是其他的只需要赋值一个数值如16位的、32位的,则填对应位数,这样会直接给IPDU赋初值1个数。具体见Com_Misc_WriteSignalDataToPdu @ Com_misc.c实现方法。

        举例,如何实现配置8个字节(CAN数据帧)?

        ComSignalInitValue = // 指向一块8字节的初始值空间
        ComBitSize = 8 * 8 (64) // CAN一帧8个字节
        ComSignalType = COM_UINT8_N  // N个bytes的形式
        

        [Q]那么如何将数据提取出来呢?

        一股脑8bytes全部取出来,再进行拆分。

    • COM_MAX_N_IPDUS @ Com_Cfg.h

      [MULTI CAN NEED CHANGE]

      定义Com_Arc_IPdu @ Com.c(指针Com_Arc_Config.ComIPdu指向该空间)和Com_BufferPduState @ Com.c的长度,有几个IPDU就定义为几,和ComIPdu @ Com_PbCfg.c数组中结构体数目相同。

      注:Com_BufferPduState用于管理IPDU的Buffer当前的位置和是否被锁定([Q] 具体的作用是什么?),在Com_Init @ Com.c中初始化,结构如下:

      typedef struct {
              
              
          PduLengthType currentPosition;
          boolean locked;
      } Com_BufferPduStateType;
      
    • COM_MAX_N_SIGNALS @ Com_Cfg.h

      定义Com_Arc_Signal @ Com.c的长度,有几个信号就定义为几,和ComSignal @ Com_PbCfg.c数组中结构体数目相同。

    • COM_MAX_BUFFER_SIZE @ Com_Cfg.h

      [MULTI CAN NEED CHANGE]

      所有IPDU大小,用于创建Com_Arc_Buffer @ Com.c。所有的IPDU都是在Com_Init @ Com.c中初始化,即Com_Arc_Config.ComIPdu->ComIPduDataPtrCom_Arc_Config.ComIPdu指向Com_Arc_IPdu数组)指针指向Com_Arc_Buffer数组的某一个uint8空间作为该IPDU的开始。具体见下面的PDU配置部分。

    • Com_Arc_Config.ComIPdu @ Com.c(非用户配置)

      Com_Arc_Config.ComIPdu指向了Com_Arc_IPdu @ Com.c,而Com_Arc_IPdu定义为static Com_Arc_IPdu_type Com_Arc_IPdu[COM_MAX_N_IPDUS]

      配置信息包括:

      1. Arc_IPdu->Com_Arc_DynSignalLength:动态信号长度,Com_ReceiveDynSignal(Com_SignalIdType SignalId, void* SignalDataPtr, uint16* Length) @ Com_Com.cCom_SendDynSignal(Com_SignalIdType SignalId, const void* SignalDataPtr, uint16 Length) @ Com_Com.c会用到该动态信号长度,分别实现接收动态信号和发送动态信号。
      2. Arc_IPdu->Com_Arc_IpduRxDMControl:boolean,是否开启接受Deadline Monitor,截止日期监控
      3. Arc_IPdu->Com_Arc_TxIPduTimers.ComTxDMTimer:DM的发送计时监视器。
      4. Arc_IPdu->Com_Arc_TxIPduTimers.ComTxModeTimePeriodTimer:提供COM周期数据发送功能,每次调用Com_ProcessMixedOrPeriodicTxMode @ Com_Sched.c都会递减,直到等于0,而且Arc_IPdu->Com_Arc_TxIPduTimers.ComTxIPduMinimumDelayTimer有需要等于0,就会发送数据。Com_ProcessMixedOrPeriodicTxMode也会给这两个变量赋一个初值。
      5. Arc_IPdu->Com_Arc_TxIPduTimers.ComTxIPduMinimumDelayTimer:只有延时达到才会发送(也就是给发送周期限定了一个最小的间隔)。每次调用Com_MainFunctionTx @ Com_Sched.c 都会递减,Com_MainFunctionTx @ Com_Sched.c 还会调用Com_ProcessMixedOrPeriodicTxMode @ Com_Sched.c
    • ComConfiguration @ Com_PbCfg.c

      COM模块总的配置信息。具体包括:

      • ComConfigurationId

        COM模块配置信息的ID

      • ComNofIPdus

        IPDU的个数,和ComIPdu @ Com_PbCfg.c结构体数组中的结构体数目对应,在Com_Init函数中会判断ComIPdu个数是否超过了这个数字

      • ComNofSignals

        COM模块信号个数,和ComSignal @ Com_PbCfg.c结构体数组中的信号结构体数目对应。

      • ComIPdu(见上方说明)

      • ComSignal(见上方说明)

      • 其他省略

    • PDU配置

      PDU实体在Com_Init @ Com.c配置,大小由COM_MAX_BUFFER_SIZE配置(总大小,包括所有通道),配置的时候按照ComIPdu.ComIPduSize @ Com_PbCfg.c的大小逐个配置。比如:

      	|--------------|------------|------------|   Com_Arc_Buffer
      	|              |            |
      发送通道开始  Deferred Buffer  接收通道开始
      

      如果IPDU是COM_DEFERRED(使用Deferred Buffer缓存数据)且COM_RECEIVE(接收模式),则要专门分配一段Deferred Buffer区域,用于缓存。

  • 重要的标志

    • ComIPdu[i].ArcIPduOutgoingId

    • ComSignal[i].ComHandleId

      ComSignal[i]的成员包含在ComIPdu[i].ArcIPduOutgoingId中。

9、Core 21.0.0学习

make架构

9.1 工程架构

工程方案1.工程文件和Arctic Core分开

简单版本

<anydir>                               - 工程
|--- config
|    |--- [config files]               - Overrides default module configurations
|    '--- <board>
|         '--- [config files]          - Overrides all other module configurations
|
|--- makefile
|--- [build_config.mk]  
'--- obj-<arch>
 
<Arctic Core>
|--- makefile
|--- boards
|    '--- <board>
|         |--- [config files]          - Default module configurations
|         '--- build_config.mk         - Configuration for the board
|
'--- scrips
     |--- config.mk
     |--- rules.mk
     '--- cc_gcc.mk

详细功能版本

<examples>               - 工程
|--- boards              - [ECL]  ecipse读取板子arxml
|--- HelloWorld
     |--- dbc            - [ECL]  描述CAN网络通信信号的文件
     |--- HelloWorld     - 具体工程
     |    |--- .settings - [ECL]  eclipse配置文件
     |    |--- swcs      - [ECL]  SWC描述文件
     |    |--- src       - [SWC]  软件组件  
     |    '--- config    - [RTE]  板子配置和RTE配置、接口、映射
     ...
|--- OsSimple
...
 
<Arctic Core>            - core
|--- .settings           - [ECL]    eclipse配置文件
|--- arxml               - [ECL]    ecipse读取ECU配置
|--- makefile            - [MAKE]   make顶层文件
|--- scrips              - [MAKE]   脚本(编译、链接)
|--- stylesheets         - [MAKE]   代码风格检查
|--- base                - [MAKE]   编译器基本定义
|--- diagnostic          - [SL]     诊断[Dlt Det Dcm..]
|--- system              - [SL]     操作系统[Os BswM EcuM SchM..]
|--- communication       - [ECU_AL] 通信HWA
|--- memory              - [ECU_AL] memory HWA
|--- Peripherals         - [ECU_AL] IO HWA, 会从examples读取参数
|--- safety_security     - [ECU_AL] 安全库和WDG
|--- cdd                 - [CDD]    Complex Device Driver
|--- drivers             - [MCAL]   驱动(CAN Fr等)
|--- arch                - [MCAL]   内有CAN收发器的SPI驱动
|--- mcal                - [MCAL]   mcal
|--- boards              - [MCAL]   具体板子配置结构体(WDG EEP FLS)
|--- clib                - [BASE]   c库函数
|--- common              - [BASE]   基础函数(log shell等)
|--- include             - [BASE]   内核inc文件给上层使用
|--- libs                - [BASE]   存放生成的libs
'--- datastructures      - [BASE]   数据结构(队列和安全队列)给RTE

工程方案2.工程和Arctic Core在一起

<Arctic Core>
|--- makefile
|--- boards
|    '--- <board>
|         |--- [config files]          - Default module configurations
|         '--- build_config.mk         - Configuration for the board
|
|--- <anydir>
|    |--- config
|    |    |--- [config files]          - Overrides default module configurations
|    |    '--- <board>
|    |          '--- [config files]    - Overrides all other module configurations
|    |
|    |--- makefile
|    |--- [build_config.mk]  
|    '--- obj-<arch>
|
'--- scrips
     |--- config.mk
     |--- rules.mk
     '--- cc_gcc.mk

9.2 工程make

make命令

# BOARDDIR:电路板;BDIR:工程样例目录
make BOARDDIR=mpc5516it BDIR=<anydir>[,<anydir>] all

一些特定的变量

  • MOD_AVAIL

    一些可用模块,例如ADC CAN DIO MCU FLS PORT PWM GPT EA

  • CFG

    配置信息,例如ARM ARMV7E_M ARM_CM4 HW_FLOAT THUMB

    在文件中的冲突问题没有想明白。CFG=会不会影响其他文件中的CFG,但是CFG又没有export

  • MOD_USE

    需要使用的模块,例如MCUKERNEL、**RTE**等,RTE的路径在project_default.mk中加入

  • COMPILER

    编译器名称,如gcc

  • CROSS_COMPILE

    编译器地址,如/usr/lib/gcc-arm-none-eabi-4_9-2015q2/bin/arm-none-eabi-,后面会加上gcc

    在文件/core/scripts/guess_cc.sh中运行了export CROSS_COMPILE=$COMPILER,在这里可以设置成和镜像默认的编译器。如wzh-ubuntu镜像的默认gcc版本是 5.4.0版本的,而4.9.3版本在`/usr/lib/gcc-arm-none-eabi-4_9-2015q2/bin/arm-none-eabi-gcc```

  • ASFLAGS - assembler flags

    汇编flags

  • ASOUT-how to generate output file

    如何生成输出文件

  • LDFLAGS-linker flags

  • LDOUT-how to Generate linker output file

  • LDMAPFILE - How to generate mapfile

  • AROUT-archiver flags

    归档器的flags

  • obj-y : list of object files to build.

  • VPATH : list of directories in which to look for source files.

  • inc-y : list of directories in which to look for header files.

  • libitem-y : list of libraries to include.

  • build-exe-y : the name of build the output file.

  • ldcmdfile-y: path to linkscript, used when building “build-exe-y”

make调用顺序

顶层(core/下)的makefile会(进入目录<anydir>/obj_<arch>)调用core/scripts/rules.mk

9.3 模块相关代码

9.3.1 EcuM

core/system/EcuM/src/EcuM_Generated_Types.h:定义EcuM需要的(模块接口配置)数据结构。例子:

if defined(USE_SPI)
     const Spi_ConfigType* SpiConfigPtr;
endif

9.3.2 OS任务

GEN_TASK_HEAD中定义所有任务

#define GEN_TASK_HEAD const OsTaskConstType  Os_TaskConstList[OS_TASK_CNT]

9.3.3 初始化

未完待续

graph LR;
main["main()@/core/system<br>/Os/rtos/src/os_init.c"]  --> EcuM_Init["EcuM_Init()@/core/system<br>/EcuM/src/EcuM.c"]
	EcuM_Init --1--> OS_CORE_IS_ID_MASTER["OS_CORE_IS_ID_MASTER(GetCoreID())<br>@/core/system/Os/rtos/inc/Os.h<br>如果多核,验证是否是主核<br>是主核运行2-11."]
	EcuM_Init --2--> SetCurrentState["SetCurrentState(ECUM_STATE_STARTUP_ONE)<br>@/core/system/EcuM/src/EcuM_Main.c<br>切换为STARTUP_TWO状态"]
	EcuM_Init --3--> EcuM_AL_DriverInitZero["EcuM_AL_DriverInitZero():初始化DET<br>@/core/system/EcuM/src/EcuM_Callout_Stubs.c<br>Ddefault Error Tracker(DET)初始化"]
	EcuM_Init --4--> InitOS["InitOS()<br>@/core/system/Os/rtos/src/os_init.c<br>Os初始化,添加Task"]
	EcuM_Init --5--> Os_IsrInit["Os_IsrInit()<br>@/core/system/Os/rtos/src/os_isr.c<br>中断初始化"]
	EcuM_Init --6--> EcuM_DeterminePbConfiguration["EcuM_World.config=EcuM_DeterminePbConfiguration()<br>@EcuM_Callout_Stubs.c<br>包含所有PostBuild配置文件"]
	EcuM_Init --7--> EcuM_AL_DriverInitOne["EcuM_AL_DriverInitOne(EcuM_World.config)<br>@EcuM_Callout_Stubs.c<br>OsStar之前的驱动初始化<br>MCU (DEM) PORT DIO (GPT) (WDG) (WDGM) <br>(DMA) (ADC) BSWM (STBM) PWM (OCU) (SHELL) USART<br>(..)代表本工程不使用"]
	EcuM_Init --8--> Mcu_GetResetReason["Mcu_GetResetReason()<br>@/core/mcal/Mcu/src/Mcu.c"<br>决定复位方式]
	EcuM_Init --9--> EcuM_SelectShutdownTarget["EcuM_SelectShutdownTarget()<br>@/core/system/EcuM/src/EcuM.c<br>选择低功耗的模式??-EcuM_OFF or SLEEP"]
	EcuM_Init --11.--> StartOS["StartOs(EcuM_World.config->EcuMDefaultAppMode)<br>@/core/system/Os/rtos/src/os_init.c"]

9.3.4 CAN调用过程

说明1Github不支持mermaid将以下mermaid代码复制到在线mermaid查看器Typora等软件查看具体流程图。

说明2:CAN需要的底层接口包括Can_Init( const Can_ConfigType *Config )Can_ReturnType Can_Write( Can_HwHandleType Hth, const Can_PduType *PduInfo )

:Can_Init(ConfigPtr->PostBuildConfig->CanConfigPtr):CanConfigPtr参数在Can_PBcfg.c

  • 两个重要的IPdu和Signal

    需要修改的内容:一些名称,如ComIPduGroupRefs对应的数值等

    IPduArc_IPudSignal区别IPdu = &(ComConfig->ComIPdu[IPduId])Signal = &(ComConfig->ComSignal[SignalId])Arc_IPdu = &(Com_Arc_Config.ComIPdu[IPduId])IPdu[IPduId]类型ComIPdu_type(在examples中定义),Arc_IPdu[IPduId]类型Com_Arc_IPdu_type(在core中定义,由COM_MAX_N_IPDUS定义数组元素个数);IPdu中定义了发送或者接收模式、IPdu大小(64字节),还指向了Signal[SignalId]等,Signal[SignalId]定义了大小端、对应的Arc_IPdu元素下标(数据发送或者接收都要经过此Arc_IPdu)、初始值等;

    *2019-8-13新理解补充:*IPDU相关数据结构中的配置信息包括通信方向(接收和发送)、IPDU大小等信息,主要进行底层的数据收发;Signal相关数据结构的配置信息包括初始化值、大小端模式等信息,主要进行上层的数据收发。具体而言,RTE任务根据Signal相关数据结构将数据拷贝至IPdu_Tx,以待BSW任务根据IPDU相关数据结构发送出去;CAN中断服务函数根据IPDU相关数据结构将数据拷贝至IPdu_Rx,BSW任务根据IPDU相关数据结构将IPdu_Rx中的数据拷贝至Deferred_IPdu中,RTE任务根据Signal相关数据结构将Deferred_IPdu中数据取出。

    IPdu、Arc_IPud和Signal关系
// GET_IPdu(IPduId)
IPdu = &(ComConfig->ComIPdu[0]);
// GET_Signal(SignalId)
Signal = &(ComConfig->ComSignal[0]);
// @ /examples/CanCtrlPwm/CanCtrlPwm/config/stm32_stm3211.c/Com_PbCfg.c
 SECTION_POSTBUILD_DATA const Com_ConfigType ComConfiguration = {
    
    
	.ComConfigurationId 			= 1,
	.ComNofIPdus					= 2,
	.ComNofSignals					= 2,
	.ComNofGroupSignals				= 0,
	.ComIPdu 						= ComIPdu,  //指向本文件的ComIPdu
	.ComIPduGroup 					= ComIPduGroup,
	.ComSignal						= ComSignal, //即Signal,指向ComSignal
	.ComGroupSignal					= ComGroupSignal,
	.ComGwMappingRef				= ComGwMapping,
	.ComGwSrcDesc					= ComGwSourceDescs,
	.ComGwDestnDesc					= ComGwDestinationDescs
};   
// @ /examples/CanCtrlPwm/CanCtrlPwm/config/stm32_stm3211.c/Com_PbCfg.c
SECTION_POSTBUILD_DATA const ComIPdu_type ComIPdu[] = {
    
    	
	{
    
     // DoorStatusPdu  CAN接收
		.ArcIPduOutgoingId			= PDUR_REVERSE_PDU_ID_PDURX,
		.ComRxIPduCallout			= COM_NO_FUNCTION_CALLOUT,
		.ComTxIPduCallout			= COM_NO_FUNCTION_CALLOUT,
		.ComTriggerTransmitIPduCallout = COM_NO_FUNCTION_CALLOUT,
		.ComIPduSignalProcessing 	= COM_DEFERRED,//指定为DEFERRED,IPdu需要先拷贝至Deferred_IPdu
		.ComIPduSize				= 64,  //定义了Arc_IPdu中数据的长度,IPdu:64;Shadow_Buff:64;Deferred_IPdu:64,共192——对应COM_MAX_BUFFER_SIZE
		.ComIPduDirection			= COM_RECEIVE,  //接收模式
		.ComIPduGroupRefs			= ComIpduGroupRefs_DoorStatusPdu,
		.ComTxIPdu = {
    
    
			.ComTxIPduMinimumDelayFactor	= 0,
			.ComTxIPduUnusedAreasDefault	= 0,
			.ComTxModeTrue = {
    
    
				.ComTxModeMode						= COM_NONE,
				.ComTxModeNumberOfRepetitions		= 0,
				.ComTxModeRepetitionPeriodFactor	= 0,
				.ComTxModeTimeOffsetFactor			= 0,
				.ComTxModeTimePeriodFactor			= 0,
			},
			.ComTxModeFalse = {
    
    
				.ComTxModeMode						= COM_NONE,
				.ComTxModeNumberOfRepetitions		= 0,
				.ComTxModeRepetitionPeriodFactor	= 0,
				.ComTxModeTimeOffsetFactor			= 0,
				.ComTxModeTimePeriodFactor			= 0,
			},
		},
		.ComIPduSignalRef			= ComIPduSignalRefs_DoorStatusPdu,  //指向Signal[n],见下方ComIPduSignalRefs_DoorStatusPdu
		.ComIPduDynSignalRef		= NULL,
		.ComIpduCounterRef			= NULL,
		.ComIPduGwMapSigDescHandle	= NULL,
		.ComIPduGwRoutingReq		= FALSE,
		.Com_Arc_EOL				= 0
	},
	{
    
     // LightStatusPdu  CAN发送
		.ArcIPduOutgoingId			= PDUR_PDU_ID_PDUTX,
		.ComRxIPduCallout			= COM_NO_FUNCTION_CALLOUT,
		.ComTxIPduCallout			= COM_NO_FUNCTION_CALLOUT,
		.ComTriggerTransmitIPduCallout = COM_NO_FUNCTION_CALLOUT,
		.ComIPduSignalProcessing 	= COM_DEFERRED,
		.ComIPduSize				= 64,
		.ComIPduDirection			= COM_SEND,
		.ComIPduGroupRefs			= ComIpduGroupRefs_LightStatusPdu,
		.ComTxIPdu = {
    
    
			.ComTxIPduMinimumDelayFactor	= 0,
			.ComTxIPduUnusedAreasDefault	= 0,
			.ComTxIPduClearUpdateBit		= TRANSMIT,
			.ComTxModeTrue = {
    
    
				.ComTxModeMode						= COM_PERIODIC,
				.ComTxModeNumberOfRepetitions		= 0,
				.ComTxModeRepetitionPeriodFactor	= 0,
				.ComTxModeTimeOffsetFactor			= 0,
				.ComTxModeTimePeriodFactor			= 11.,
			},
			.ComTxModeFalse = {
    
    
				.ComTxModeMode						= COM_NONE,
				.ComTxModeNumberOfRepetitions		= 0,
				.ComTxModeRepetitionPeriodFactor	= 0,
				.ComTxModeTimeOffsetFactor			= 0,
				.ComTxModeTimePeriodFactor			= 0,
			},
		},
		.ComIPduSignalRef			= ComIPduSignalRefs_LightStatusPdu,
		.ComIPduDynSignalRef		= NULL,
		.ComIpduCounterRef			= NULL,
		.ComIPduGwMapSigDescHandle	= NULL,
		.ComIPduGwRoutingReq		= FALSE,
		.Com_Arc_EOL				= 0
	},
	{
    
       //停止信号
        //void Com_Init(const Com_ConfigType *config )中for (uint16 i = 0; 0 == ComConfig->ComIPdu[i].Com_Arc_EOL; i++){}
		.Com_Arc_EOL				= 1
	}
};
SECTION_POSTBUILD_DATA const ComSignal_type * const ComIPduSignalRefs_DoorStatusPdu[] = {
    
    
	&ComSignal[ComConf_ComSignal_DoorStatus],
	NULL
};

// @ /examples/CanCtrlPwm/CanCtrlPwm/config/stm32_stm3211.c/Com_PbCfg.c
SECTION_POSTBUILD_DATA const ComSignal_type ComSignal[] = {
    
    
    {
    
     // DoorStatus
        .ComHandleId                = ComConf_ComSignal_DoorStatus,
        .ComIPduHandleId            = ComConf_ComIPdu_DoorStatusPdu,
        // @req COM292          
        .ComFirstTimeoutFactor      = 0,
        .ComNotification            = COM_NO_FUNCTION_CALLOUT,        
        .ComTimeoutFactor           = 0,
        .ComTimeoutNotification     = COM_NO_FUNCTION_CALLOUT,
        .ComErrorNotification       = COM_NO_FUNCTION_CALLOUT,
        .ComTransferProperty        = COM_TRIGGERED_ON_CHANGE,
        .ComUpdateBitPosition       = 0,
        .ComSignalArcUseUpdateBit   = FALSE,
        .ComSignalInitValue         = &Com_SignalInitValue_DoorStatus,
        .ComBitPosition             = 0,
        .ComBitSize                 = 32,
        .ComSignalEndianess         = COM_LITTLE_ENDIAN,
        .ComSignalType              = COM_UINT32,
        .Com_Arc_IsSignalGroup      = FALSE,
        .ComGroupSignal             = NULL,
        .ComRxDataTimeoutAction     = COM_TIMEOUT_DATA_ACTION_NONE,
        .ComSigGwRoutingReq         = FALSE,
        .ComOsekNmNetId             = COM_OSEKNM_INVALID_NET_ID,
        .ComOsekNmNodeId            = 0,
        .Com_Arc_EOL                = 0
    },

    {
    
     // LightStatus
        .ComHandleId                = ComConf_ComSignal_LightStatus,
        .ComIPduHandleId            = ComConf_ComIPdu_LightStatusPdu,
        .ComFirstTimeoutFactor      = 0,      
        .ComNotification            = COM_NO_FUNCTION_CALLOUT,        
        .ComTimeoutFactor           = 0,
        .ComTimeoutNotification     = COM_NO_FUNCTION_CALLOUT,
        .ComErrorNotification       = COM_NO_FUNCTION_CALLOUT,
        .ComTransferProperty        = COM_PENDING,
        .ComUpdateBitPosition       = 0,
        .ComSignalArcUseUpdateBit   = FALSE,
        .ComSignalInitValue         = &Com_SignalInitValue_LightStatus,
        .ComBitPosition             = 0,
        .ComBitSize                 = 32,
        .ComSignalEndianess         = COM_LITTLE_ENDIAN,
        .ComSignalType              = COM_UINT32,
        .Com_Arc_IsSignalGroup      = FALSE,
        .ComGroupSignal             = NULL,
        .ComRxDataTimeoutAction     = COM_TIMEOUT_DATA_ACTION_NONE,
        .ComSigGwRoutingReq         = FALSE,
        .ComOsekNmNetId             = COM_OSEKNM_INVALID_NET_ID,
        .ComOsekNmNodeId            = 0,
        .Com_Arc_EOL                = 0
    },
	{
    
    
		.Com_Arc_EOL				= 1
	}
};
// GET_ArcIPdu(IPduId)

Arc_IPdu = &(Com_Arc_Config.ComIPdu[0]);  //Com_Arc_Config见下方
    
const Com_Arc_Config_type Com_Arc_Config = {
    
      // @/core/communication/Com/src/Com_Internal.h
    .ComIPdu            = Com_Arc_IPdu,     // 见下方
    .ComSignal          = Com_Arc_Signal,
#if (COM_MAX_N_GROUP_SIGNALS != 0)
    .ComGroupSignal     = Com_Arc_GroupSignal,
#else
    .ComGroupSignal     = NULL,
#endif
#if (COM_SIG_GATEWAY_ENABLE == STD_ON)
    .ComGwSrcDescSignal = Com_Arc_GwSrcDescSignals
#else
    .ComGwSrcDescSignal = NULL
#endif
};
//@/core/communication/Com/src/Com.c
static Com_Arc_IPdu_type Com_Arc_IPdu[COM_MAX_N_IPDUS]; //COM_MAX_N_IPDUS=2

typedef struct {
    
      // @/core/communication/Com/src/Com_Arc_Types.h
    /** Reference to the actual pdu data storage */
    void *ComIPduDataPtr;  //指向Com_Arc_Buffer[0],见下方
    void *ComIPduDeferredDataPtr;  //如果没有shadow_buffer,指向Com_Arc_Buffer[64]

    Com_Arc_TxIPduTimer_type Com_Arc_TxIPduTimers;
    uint32 Com_Arc_TxDeadlineCounter; 
    uint16 Com_Arc_DynSignalLength;
    boolean Com_Arc_IpduStarted;
    boolean Com_Arc_IpduRxDMControl;
    boolean Com_Arc_IpduTxMode;
} Com_Arc_IPdu_type;

//@/core/communication/Com/src/Com.c
static uint8 Com_Arc_Buffer[COM_MAX_BUFFER_SIZE]; // COM_MAX_BUFFER_SIZE = 192
  • CAN数据接收

    1.CAN接收中断初始化、触发、数据写入到IPdu过程

_can_name ## _Rx0Isr -> Can_1_Rx0Isr作为中断入口
中断触发
OsStartupTask()
@/examples/CanCtrlPwm/CanCtrlPwm/src/BSWMainFunctionTask.c
EcuM_StartupTwo()
@/core/system/EcuM/src/EcuM_Fixed.c
EcuM_AL_DriverInitTwo(EcuM_World.config)
@/core/system/EcuM/src/EcuM_Callout_Stubs.c
Can_Init(ConfigPtr->PostBuildConfig->CanConfigPtr)
(CanConfigPtr参数在Can_PBcfg.c->CanConfigData)
CanConfigPtr而后赋值给Can_Global.config
@/core/mcal/Can/src/Can_stm32.c
!!!重点:从此处开始往下需要移植到TC26x
INSTALL_HANDLERS(Can_1, CAN1_SCE_IRQn,
USB_LP_CAN1_RX0_IRQn, CAN1_RX1_IRQn, USB_HP_CAN1_TX_IRQn)
@Can_stm32.c
ISR_INSTALL_ISR2(名称, _can_name ## _Rx0Isr, _rx0, 2, 0)
@/core/include/isr.h
__ISR_INSTALL_ISR2(...)添加到中断向量表
@/core/include/isr.h
Can_1_Rx0Isr()@Can_stm32.c
Can_RxIsr((int)CAN_CTRL_1,CAN_FIFO0)
@Can_stm32.c
CAN_Receive(canHw,fifo, &RxMessage):
读取fifo中数据到RxMessage
@stm32f11.x_can.c
CanIf_RxIndication(...,(uint8 *)&RxMessage.Data[0])
@/core/communication/CanIf/src/CanIf.c
CanIfUserRxIndications[3]()
@/examples/CanCtrlPwm/CanCtrlPwm/config/stm32_stm3211.c/CanIf_Cfg.c
PduR_CanIfRxIndication()@/core/communication/PduR/src/PduR_CanIf.c
PduR_LoIfRxIndication(pduId(0), pduInfoPtr, 0x01)
@/core/communication/PduR/src/PduR_Logic.c
PduR_ARC_RxIndication(pduId, pduInfoPtr, serviceId)
@PduR_Logic.c
PduR_ARC_RouteRxIndication(destination, PduInfo)
@/core/communication/PduR/src/PduR_Routing.c
Com_RxIndication(destination->DestPduId, PduInfo)@Com_Com.c
destination->DestPduId即为存放数据的下标0
memcpy(Arc_IPdu->ComIPduDataPtr,
PduInfoPtr->SduDataPtr, IPdu->ComIPduSize)
ComIPduDataPtr指向接收到的数据
Com_Misc_RxProcessSignals():
会抛出Event(放在一个列表里)

下图是CAN中断后发生的第一步:CanIf实现从L-PDUI-PDUSWS_CANInterface.pdf P139 Receive indication (interrupt mode))

CanIf实现从LPDU到IPDU

下图是CAN中断后发生的第2步:IPDU从Canif到PDUR,再到COM(SWS_PDURouter.pdf P88 CanIf module I-PDU reception):

IPDU从CanIf到COM

Com_RxIndication实现了将中断中的数据(通过结构体pduInfo指向了中断中的数据、数据长度等内容,pduInfo交给Com_RxIndication处理,而不是直接处理中断中的数据)拷贝到Buffer中。此时,可以通过抛出信号更新的事件(要使用到ComSignal@Com_PbCfg.c),供其他的任务来进行处理。

2.OsBswTask将IPDU数据拷贝到DEFERRED_IPDU中

OsBswTask()
Com_MainFunctionRx()
memcpy(Arc_IPdu->ComIPduDeferredDataPtr,Arc_IPdu->ComIPduDataPtr,IPdu->ComIPduSize)

3.OsRteTask从DEFERRED_IPDU获取数据

Event
PRE
OsRteTask
Rte_lightManager_InteriorLightManagerMain()
@/Rte/Config/Rte_InteriorLightManager.c
Rte_Read_InteriorLightManager_lightManager_
RearDoorStatus_message(...)
@/Rte/Config/Rte_Internal_InteriorLightManager.c
Com_ReceiveSignal(...)@
/core/communication/Com/src/Com_Com.c
Com_Misc_ReadSignalDataFromPdu()@
/core/communication/Com/src/Com_misc.c

相关数据结构

1.PRE

// @/Rte/Contract/Rte_InteriorLightManager.h
typedef struct {
    
    
    Rte_DE_DoorStatusImpl * const InteriorLightManagerMain_LeftDoorStatus_status;
    Rte_DE_IntImpl * const InteriorLightManagerMain_RearDoorStatus_message;
    Rte_DE_DoorStatusImpl * const InteriorLightManagerMain_RightDoorStatus_status;
    Rte_DE_LightStatusImpl * const InteriorLightManagerMain_FrontLightStatus_status;
    Rte_DE_IntImpl * const InteriorLightManagerMain_LightStatusOnCommMedia_message;
} Rte_CDS_InteriorLightManager;
typedef Rte_CDS_InteriorLightManager const * const Rte_Instance;
#define self (Rte_Inst_InteriorLightManager)  // 在.c中创建Rte_Inst_InteriorLightManager

// @ /Rte/Config/Rte_InteriorLightManager.c
Rte_DE_IntImpl ImplDE_lightManager_InteriorLightManagerMain_RearDoorStatus_message;//存放CAN发送过来的数据
const Rte_CDS_InteriorLightManager InteriorLightManager_lightManager = {
    
    
    .InteriorLightManagerMain_RearDoorStatus_message = &ImplDE_lightManager_InteriorLightManagerMain_RearDoorStatus_message,  //1.PRE指向存放CAN发送过来的空间
    .InteriorLightManagerMain_RightDoorStatus_status = &ImplDE_lightManager_InteriorLightManagerMain_RightDoorStatus_status,
    .InteriorLightManagerMain_LeftDoorStatus_status = &ImplDE_lightManager_InteriorLightManagerMain_LeftDoorStatus_status,
    .InteriorLightManagerMain_FrontLightStatus_status = &ImplDE_lightManager_InteriorLightManagerMain_FrontLightStatus_status, //2.MAIN处理1数据,结果写入到此处;3.POST写入到Rte_Buffer_frontLightActuator_InteriorLightStatus_status @ Rte_Buffers.c
    .InteriorLightManagerMain_LightStatusOnCommMedia_message = &ImplDE_lightManager_InteriorLightManagerMain_LightStatusOnCommMedia_message
};
const Rte_Instance Rte_Inst_InteriorLightManager = &InteriorLightManager_lightManager;

2.MAIN

获取/Rte/Config/Rte_InteriorLightManager.c中的数值,并进行处理,最终写入到Rte_Inst_InteriorLightManager ->InteriorLightManagerMain_FrontLightStatus_status

3.POST

// /Rte/Config/Rte_Buffers.c
LightStatusImpl Rte_Buffer_frontLightActuator_InteriorLightStatus_status; // 3.将MAIN处理结果拷贝到此处

执行器根据上述结果执行相应操作

数据结构

// \Rte\Config\Rte_LightActuator.c
Rte_DE_LightStatusImpl ImplDE_frontLightActuator_LightActuatorMain_InteriorLightStatus_status;
const Rte_CDS_LightActuator LightActuator_frontLightActuator = {
    
    
    .LightActuatorMain_InteriorLightStatus_status = &ImplDE_frontLightActuator_LightActuatorMain_InteriorLightStatus_status,  // 将Rte_Buffer_frontLightActuator_InteriorLightStatus_status内保存的处理结果拷贝到此处
    .DigitalLight = {
    
    
        .Call_Write = Rte_Call_LightActuator_frontLightActuator_DigitalLight_Write  //需要执行的操作
    }
};
  • CAN数据发送

    对于CanCtrlPwm工程而言,在OsRteTask任务中将数据放到IPDU中,在从OsBswTask通过CAN周期性发送出去

    1.OsRteTask任务中将数据放到IPDU中

    EcuMWorld.config = EcuMConfig@EcuM_PBcfg.c在函数EcuM_Init()@EcuM.c中配置,具体语句是EcuM_World.config = EcuM_DeterminePbConfiguration();

    EcuM_AL_DriverInitTwo(EcuM_World.config)被调用,用于Com初始化Com_Init,也就是给Com_ConfigType * ComConfig赋值为ComConfiguration。

Event
PRE
MAIN
POST
OsRteTask
Rte_lightManager_InteriorLightManagerMain()
@/Rte/Config/Rte_InteriorLightManager.c
Rte_Read_InteriorLightManager_lightManager_
RearDoorStatus_message(...)
@/Rte/Config/Rte_Internal_InteriorLightManager.c
Com_ReceiveSignal(...)@
/core/communication/Com/src/Com_Com.c
Com_Misc_ReadSignalDataFromPdu()@
/core/communication/Com/src/Com_misc.c
..
Rte_Write_InteriorLightManager_lightManager_
LightStatusOnCommMedia_message(...)
@/Rte/Config/Rte_Internal_InteriorLightManager.c
Com_SendSignal(ComConf_ComSignal_
LightStatus, &value)
@/core/communication/Com/src/Com_Com.c
Com_Misc_WriteSignalDataToPdu(...)
@/core/communication/Com/src/Com_misc.c
在这里将数据放到了PDU存储空间
ComConfiguration@Com_PBcfg.h
Com_Misc_TriggerTxOnConditions(...)
@Com_misc.c
对于CanCtrlPwm工程不从此步直接发送数据
而是从OsBswTask任务Com_MainFunctionTx发送数据
下面程序(虚线)未执行
Com_Misc_TriggerIPDUSend(pduHandleId)
@Com_misc.c
PduR_ComTransmit(IPdu->ArcIPduOutgoingId,
&PduInfoPackage)
@/core/communication/PduR/src/PduR_Com.c
PduR_UpTransmit(pduId, pduInfoPtr, 0x89)
@/core/communication/PduR/src/PduR_Logic.c
PduR_ARC_Transmit(pduId, pduInfoPtr, serviceId)
@PduR_Logic.c
PduR_ARC_Transmit(destination, PduInfo)
@/core/communication/PduR/src/PduR_Routing.c
CanIf_Transmit(destination->DestPduId, pduInfo)
@/core/communication/CanIf/src/CanIf.c
Can_Write(txPduPtr->CanIfTxPduBufferRef->
CanIfBufferHthRef->CanIfHthIdSymRef, &canPdu)
@/core/mcal/Can/src/Can_stm32.c
CAN_Transmit(canHw,&TxMessage)
@stm32f11.x_can.c

2.周期性从IPdu发送CAN数据

OsBswTask
Com_MainFunctionTx()
@/core/communication/Com/src/Com_Sched.c
Com_ProcessMixedOrPeriodicTxMode()
@Com_Sched.c
Com_TriggerIPDUSend(IPduId)
@/core/communication/Com/src/Com_Com.c
Com_Misc_TriggerIPDUSend(PduId)
@/core/communication/Com/src/Com_misc.c
PduR_ComTransmit(IPdu->ArcIPduOutgoingId,
&PduInfoPackage)
@/core/communication/PduR/src/PduR_Com.c
PduR_UpTransmit(pduId, pduInfoPtr, 0x89)
@/core/communication/PduR/src/PduR_Logic.c
PduR_ARC_Transmit(pduId, pduInfoPtr, serviceId)
@PduR_Logic.c
PduR_ARC_Transmit(destination, PduInfo)
@/core/communication/PduR/src/PduR_Routing.c
CanIf_Transmit(destination->DestPduId, pduInfo)
@/core/communication/CanIf/src/CanIf.c
Can_Write(txPduPtr->CanIfTxPduBufferRef->
CanIfBufferHthRef->CanIfHthIdSymRef, &canPdu)
@/core/mcal/Can/src/Can_stm32.c
CAN_Transmit(canHw,&TxMessage)
@stm32f11.x_can.c

9.3.5 运行OsBswTask

未完待续

OsBswTask()
ComM_MainFunction_ComMChannel()
映射到某种通信方式的状态管理
ComM_MainFunction(ComMConf_ComMChannel_ComMChannel)
Com_MainFunctionRx()
Com_MainFunctionTx()
EcuM_MainFunction()
timeout倒计时,而后不再进行有意义的工作
EcuM_AL_DriverRestart(EcuM_World.config)
EcuM_AL_DriverInitOne(config)
第一步初始化,包括端口配置
EcuM_AL_DriverInitTwo(config)
第二步初始化,包括CAN
Can_MainFunction_Mode()

9.3.6 RTE设置PWM

OsRteTask()
Rte_SwcReader_SwcReaderRunnable()
swcReaderRunnable()

9.3.7 OsRteTask读取数据和设置灯PWM

  • 流程图
RTE读取数据设置灯Pwm

说明:接收和发送的IPdu类型(下标分别为0和1)指向Com_Arc_Buffer[COM_MAX_BUFFER_SIZE]空间(见上图,接收和发送缓存连续),按照配置文件的配置顺序进行指向(Can_Init函数中一个for循环),每次初始化指向一块空间,bufferIndex就会+空间大小,为后面的指向做准备。其他地方关于IPDU指向不同Buffer的说明不正确,以此说明为准,如有和本说明违背的地方,请联系我

  • 变量声明和定义
RTE变量
  • Runnable调用的函数和定义

    (1)获取数据和处理数据

    实际系统处理数据后,会通过CAN发送函数Rte_Write_InteriorPwmSetManager_pwmSetManager_PwmSetDutyOnCommMedia_message@Rte\Config\Rte_Internal_InteriorPwmSetManager.c(在Rte\Config\Rte_Internal.h声明)发送至外部。

    获取数据和处理

    (2)执行Pwm占空比设置

    执行设置占空比

    函数定义总结

    Rte_Internal_xxxx.c/Rte_Internal.hRte/Config):定义/声明内容包括从IPDU读取(通信接收到的)数据、将数据写入RteBuff、读取RteBuff、设置IO(Pwm)占空比、写入IPDU(准备发送),总结下来就是,所有连接BSW的接口和缓存区的操作(不包括和SWC任务绑定的缓存区)

    Rte_xxxx.cRte/Config):定义Runnables,在要使用的地方extern引入

    Rte_xxxx.hRte/Contract):声明xxxx.c中定义的执行器函数;定义内联函数,直接和Runnables绑定的操作变量。

    Rte_xxxx_Type.hRte/Config):结构体声明

    xxxx.cRte/src):定义执行器函数,具体进行Pwm占空比设置、Bsw主任务、IO操作等。

9.3.8 OsStartUp任务

  • 流程
1.current_state ==
ECUM_STATE_STARTUP_ONE
2.current_state ==
ECUM_STATE_STARTUP_TWO
3.current_state ==
ECUM_STATE_STARTUP_TWO
4.current_state ==
ECUM_STATE_STARTUP_TWO
OsStartUp()@BSWMainFunctionTask.c
EcuM_StartupTwo()@\system\EcuM\src\EcuM_Fixed.c
SetCurrentState
(ECUM_STATE_STARTUP_TWO)
@EcuM_Main.c
EcuM_World.current_state
= ECUM_STATE_STARTUP_TWO
EcuM_AL_DriverInitTwo(EcuM_World.config)
@EcuM_Callout_Stubs.c
Can_Init(ConfigPtr->
PostBuildConfig->CanConfigPtr)
@Can_stm32.c
检查接收和发送个数(硬件)是否支持
STM32只有2个接收FIFO,1个发送FIFO
INSTALL_HANDLERS:
CAN中断服务配置
Can_ChangeBaudrate:
配置波特率、FIFOx、滤波器组等
配置发送HTH映射
CanIf_Init(ConfigPtr->
PostBuildConfig->CanIfConfigPtr)
CanSM_Init(ConfigPtr->
CanSMConfigPtr)
Rte_Start()
EcuM_AL_DriverInitThree(EcuM_World.config)
  • 数据结构

    // EcuM_World
    typedef struct
    {
          
          
        boolean                           initiated;
        EcuM_ConfigType                   *config;  // 在EcuM_Init()指向EcuMConfig @ EcuM_PBcfg.c,见下方
        EcuM_StateType                    shutdown_target;
        AppModeType                       app_mode;
        EcuM_StateType                    current_state;
        uint32                            run_requests;
        uint32                            postrun_requests;
        boolean                           killAllRequest;
        boolean                           killAllPostrunRequest;
    #if (defined(USE_ECUM_FLEXIBLE))
        EcuM_ShutdownCauseType            shutdown_cause;
    #endif
        uint8                             sleep_mode;
        /* Events set by EcuM_SetWakeupEvent */
        uint32                            wakeupEvents;
        uint32                            wakeupTimer;
        /* Events set by EcuM_ValidateWakeupEvent */
        uint32                            validEvents;
        uint32                            validationTimer;
    #if (ECUM_ARC_SAFETY_PLATFORM == STD_ON)
        EcuM_SP_SyncStatus                syncStatusMaster;
    #endif
    } EcuM_GlobalType;
    
    /*@req SWS_EcuM_00793*/ /*@req SWS_EcuMf_00046*/
    const EcuM_ConfigType EcuMConfig = {
          
          
        .EcuMPostBuildVariant = 1,
        .EcuMConfigConsistencyHashLow = PRE_COMPILED_DATA_HASH_LOW, 
        .EcuMConfigConsistencyHashHigh = PRE_COMPILED_DATA_HASH_HIGH,  /* @req SWS_EcuM_02795 */
        .EcuMDefaultShutdownTarget = ECUM_STATE_OFF,
        .EcuMDefaultSleepMode = ECUM_SLEEP_MODE_FIRST,
        .EcuMDefaultAppMode = OSDEFAULTAPPMODE,
        .EcuMNvramReadAllTimeout = ECUM_NVRAM_READALL_TIMEOUT,
        .EcuMNvramWriteAllTimeout = ECUM_NVRAM_WRITEALL_TIMEOUT,
        .EcuMRunMinimumDuration = ECUM_NVRAM_MIN_RUN_DURATION,
        .EcuMNormalMcuMode = McuConf_McuModeSettingConf_NORMAL,
        .EcuMSleepModeConfig = EcuM_SleepModeConfig,
        .EcuMWakeupSourceConfig = EcuM_WakeupSourceConfig,
    #if defined(USE_COMM)
        .EcuMComMConfig = EcuM_ComMConfig,
    #endif
    #if defined(USE_MCU)
        .McuConfigPtr = McuConfigData,
    #endif
    #if defined(USE_PORT)
        .PortConfigPtr = &PortConfigData,
    #endif
    #if defined(USE_DIO)
        .DioConfigPtr = &DioConfigData,
    #endif
    #if defined(USE_CANSM)
        .CanSMConfigPtr = &CanSM_Config,
    #endif
    #if defined(USE_ADC)
        #if defined(CFG_ZYNQ)
            .AdcConfigPtr = NULL,
        #else
            .AdcConfigPtr = AdcConfig,
        #endif
    #endif
    #if defined(USE_PWM)
        .PwmConfigPtr = &PwmConfig,
    #endif
    #if defined(USE_BSWM) || defined(USE_PDUR) || defined(USE_COM) || defined(USE_CANIF) ||\
        defined(USE_CANTP) || defined(USE_CANNM) || defined(USE_COMM) || defined (USE_CANTRCV) || defined (USE_CAN) || (defined(USE_FIM) && (FIM_POSTBUILD_MODE == STD_ON))
        .PostBuildConfig = &Postbuild_Config,  //指向一个包含以上模块配置信息的数据结构,见下方
    #endif
    // ...未完全展示
    };
    
    // \core\system\EcuM\src\EcuM_PBHeader.c
    SECTION_POSTBUILD_HEADER const PostbuildConfigType Postbuild_Config = {
          
          
        .startPattern = 0x5A5A5A5A,
        .postBuildVariant = 1,
        .preCompileHashLow = PRE_COMPILED_DATA_HASH_LOW,
        .preCompileHashHigh = PRE_COMPILED_DATA_HASH_HIGH,
    #if defined (USE_BSWM)
        .BswMConfigPtr = &BswM_Config,
    #endif
    #if defined (USE_CAN)
        .CanConfigPtr = &CanConfigData,  //指向CanConfigData@Can_PBcfg.c,见下方
    #endif
    #if defined(USE_CANIF)
        .CanIfConfigPtr = &CanIf_Config,
    #endif
    #if defined (USE_CANNM)
        .CanNmConfigPtr = &CanNm_Config,
    #endif
    #if defined(USE_COM)
        .ComConfigPtr = &ComConfiguration,
    #endif
    #if defined (USE_COMM)
        .ComMConfigPtr = &ComM_Config,
    #endif
    #if defined(USE_PDUR)
        .PduRConfigPtr = &PduR_Config,
    #endif
    // ...未完全展示
    };
    //@Can_PBcfg.c
    SECTION_POSTBUILD_DATA  const  Can_ConfigType CanConfigData = {
          
          
    	.CanConfigSetPtr =	&CanConfigSetData
    };
    
  • HTH MAP的探究

    问题

    Can_Cfg.h中定义了

    #define CanConf_CanHardwareObject_CanHardwareObjectTx (Can_HwHandleType)0
    #define Can_CanHardwareObjectTx CanConf_CanHardwareObject_CanHardwareObjectTx
    #define NUM_OF_HTHS (Can_HwHandleType)1  // HTH(Transmit Handle) 个数 
    
    #define CanConf_CanHardwareObject_CanHardwareObjectRx (Can_HwHandleType)1
    #define Can_CanHardwareObjectRx CanConf_CanHardwareObject_CanHardwareObjectRx
    

    但是EB中必须定义Tx比Rx高。所以这里设置成Tx 1Rx 0,但是单片机无法通过CAN发送和接收数据(CAN中断服务函数也无法进入)。

    解释(我们的项目工程代码带注释)

    Can_Init()初始化的时候,需要给Can_Global.CanHTHMap(Transmit Handle)设置

    // @Can_Init()
    //     {  // Can_Arc_Hoh ptr to ...
    //         .CanObjectId	=	CanConf_CanHardwareObject_CanHardwareObjectTx,//CanHardwareObjectTx,
    //         .CanHandleType	=	CAN_ARC_HANDLE_TYPE_BASIC,
    //         .CanIdType		=	CAN_ID_TYPE_STANDARD,
    //         .CanObjectType	=	CAN_OBJECT_TYPE_TRANSMIT,
    //         .CanHwFilterMask =	0, // Not applicable for Transmit object
    //         .Can_Arc_EOL	= 	1  // [Chaochao Song] Stop while in Can_Init func , set the last enum's  Can_Arc_EOL = 1
    //     },
    hoh = canHwConfig->Can_Arc_Hoh;
    hoh--;
    do
    {
          
          
        hoh++;
    
        if (hoh->CanObjectType == CAN_OBJECT_TYPE_TRANSMIT)  // hoh ptr to 上面的元素
        {
          
          
            //设置哪个CAN,对于CanCtrlPwm Proj是CAN_CTRL_1
            // !!!!! 这里hoh->CanObjectId就是上面所说的发送和接收的数值(Tx比Rx高)
            // CanHTHMap只定义了1个(NUM_OF_HTHS),下标为0,Tx数值CanObjectId为1而造成溢出
            // 但是这里不能单纯的 -1,因为还会在发送的时候检查是否存在HTH,造成ID不对应,具体见下面的解释
            Can_Global.CanHTHMap[hoh->CanObjectId].CanControllerRef = canHwConfig->CanControllerId;
            // 设置HOH(Hardware Object Handle)
            Can_Global.CanHTHMap[hoh->CanObjectId].CanHOHRef = hoh;
        }
    } while (!hoh->Can_Arc_EOL);
    

    另外在发送的时候,也会检测(接上面的问题——“但是这里不能单纯的 -1,因为还会在发送的时候检查是否存在HTH,造成ID不对应,具体见下面的解释”)是否符合。见下图调用过程:

    CanIf_Transmit()
    txPduPtr = &CanIf_ConfigPtr->InitConfig->CanIfTxPduConfigPtr
    定义见下方补充
    Can_Write(txPduPtr->CanIfTxPduBufferRef->CanIfBufferHthRef->CanIfHthIdSymRef, &canPdu)
    CanIfHthIdSymRef = CanConf_CanHardwareObject_CanHardwareObjectTx
    定义见下方补充
    Can_FindHoh(Hth, &controller)
    判断输入的hth是否和HTHmap中的一样
    SECTION_POSTBUILD_DATA const CanIf_TxPduConfigType CanIfTxPduConfigData[] = {
          
          //下面的hth指向
        {
          
          
            .CanIfTxPduId               = PDUR_REVERSE_PDU_ID_PDUTX,
            .CanIfCanTxPduIdCanId       = 2,
            .CanIfCanTxPduIdDlc         = 8,
            .CanIfCanTxPduType          = CANIF_PDU_TYPE_STATIC,
            .CanIfTxPduPnFilterEnable   = STD_OFF,
    #if ( CANIF_PUBLIC_READTXPDU_NOTIFY_STATUS_API == STD_ON )
            .CanIfReadTxPduNotifyStatus = FALSE,
    #endif
            .CanIfTxPduIdCanIdType      = CANIF_CAN_ID_TYPE_11,
            .CanIfUserTxConfirmation    = PDUR_CALLOUT,
            /* [CanIfBufferCfg] */
            .CanIfTxPduBufferRef        = &CanIfBufferCfgData[0],//指向下面的CanIfBufferCfgData
        },
    };
    SECTION_POSTBUILD_DATA const CanIf_TxBufferConfigType CanIfBufferCfgData[] = {
          
          
    	{
          
          
    		.CanIfBufferSize = 0,
    		.CanIfBufferHthRef = &CanIfHthConfigData_CanIfInitHohCfg[0],//指向下面的CanIfHthConfigData_CanIfInitHohCfg
    		.CanIf_Arc_BufferId = 0
    	},
    };
    SECTION_POSTBUILD_DATA const CanIf_HthConfigType CanIfHthConfigData_CanIfInitHohCfg[] =
    {
          
          
    	{
          
           
        	.CanIfHthType 				= CANIF_HANDLE_TYPE_BASIC,
        	.CanIfCanControllerIdRef 	= CanIfConf_CanIfCtrlCfg_CanIfCtrlCfg,
        	.CanIfHthIdSymRef 			= CanConf_CanHardwareObject_CanHardwareObjectTx,//出现了!!!!!即为上面所定义的CanConf_CanHardwareObject_CanHardwareObjectTx
    	},
    };
    

    解决方案

    #define NUM_OF_HTHS 2 //定义两个HTH,只用第二个
    

    不能在Can_Init的时候,单纯将CanConf_CanHardwareObject_CanHardwareObjectTx -1 输入到HTHmap下标,解释是发送的时候会检测是否对应,具体见上面解释

9.3.9 RTE任务和BSW任务的通信模式如何联系

1.Rte任务初始化通信模式

EVENT_MASK_OsInitEvent抛出
OsRteTask()
Rte_modeManager_ModeManagerInit()
modeManagerInit()
@Rte_ModeManager.c
Rte_Write_ModeManager_modeManager
_ComMControl_requestedMode(requestedMode)
@Rte_ModeManager.c
改变Rte_Buffer_bswM_modeRequestPort_SwcStartCommunication_requestedMode

2.Bsw获取开始的通信模式

BswM_Internal_UpdateNotificationMirrors()
@BswM_Cfg.c
获取requestedMode(通过Rte任务修改),并修改
BswM_ModeReqMirrors[BSWM_SWCSTARTCOMMUNICATION_REQ_IDX]
BswM_Internal_Read_SwcModeReq_SwcStartCommunication()
@BswM_Cfg.c
Rte_Read_modeRequestPort_SwcStartCommunication_requestedMode()
@Rte_BswM.c
Rte_Read_BswM_bswM_modeRequestPort_SwcStartCommunication_requestedMode()
@Rte_Internal_BswM.c
获取Rte_Buffer_bswM_modeRequestPort_SwcStartCommunication_requestedMode

3.BswM修改通信模式

根据BswM_ModeReqMirrors[BSWM_SWCSTARTCOMMUNICATION_REQ_IDX]修改BswM_ModeReqMirrors[BSWM_BSWCOMMINDICATION_REQ_IDX]

4.Com发送数据

通信模块根据Com_Arc_IpduStarted是否是TRUE来发送数据

BswM_PduGroupSwitchActionPerformed==TRUE
BswM_MainFunction()
BswM_Internal_IterateActionList()
@BswM.c
修改BswM_PduGroupSwitchActionPerformed
Com_IpduGroupControl()
修改Com_Arc_IpduStarted
是否通过IPDU发送数据
BswM_PduGroupSwitchActionPerformed = FALSE

9.3.11 如何注册中断向量表

封装
1
1.1
2 1.1的id == VECTOR_ILL
2.1
2.2
Can_Init()@Can_stm32.c
INSTALL_HANDLERS(Can_1, CAN1_SCE_IRQn, CAN1_RX0_IRQn, CAN1_RX1_IRQn, CAN1_TX_IRQn)
@Can_stm32.c
参数CAN1_SCE_IRQn, CAN1_RX0_IRQn, CAN1_RX1_IRQn, CAN1_TX_IRQn指定了向量表下标偏移量
该偏移量需要再加上OFFSET,比如STM32的内部中断有16个,所以OFFSET此时为16
ISR_INSTALL_ISR2( Can_name, _can_name ## _Rx0Isr, _rx0, 2, 0 )
@isr.h
__ISR_INSTALL_ISR2(_name, _entry, _unique, _vector,_priority,_app)@isr.h
Os_IsrAdd( & _entry ## _unique)
@os_isr.h
Os_CheckISRinstalled( isrPtr )
@os_isr.c
id = (ISRType)Os_VectorToIsr[isrPtr->vector +
IRQ_INTERRUPT_OFFSET
如果不是默认安装的中断ID,则会返回VECTOR_ILL
一般都是初始化为VECTOR_ILL
Os_IsrAddWithId(isrPtr,id)
@os_isr.c
通过id安装中断
注意:STM32的0-15中断默认开启,配置
外部中断external interrupt(>15)
Os_VectorToIsr[isrPtr->vector +
IRQ_INTERRUPT_OFFSET ] = (uint8)id
通过中断向量ID表赋值,表示已经安装
Irq_EnableVector2
( isrPtr->entry, isrPtr->vector,
isrPtr->type, isrPtr->priority,
Os_ApplGetCore(isrPtr->appOwner) )
@irq.c
中断初始化使能

9.3.11 OS任务调度

  • Os_AlarmCheck函数

    Os_AlarmCheck函数按照OS的系统时钟运行,周期由变量OsTickFreq决定,在CanCtrlPwm中,定义为11.00(us),即Os_AlarmCheck每1ms运行一次。在Os_AlarmCheck函数中,会进行每个ALARM时间是否到达,到达则运行相关动作。

    如何判断ALARM时间是否到达?

    每个ALARM都会有一个expire_val,每次运行后,就增加到下一次运行的时间。在下一次判断到系统时间和该时间相同,则运行。

9.4 顶层移植、配置和应用

路径:\examples\CanCtrlPwm\CanCtrlPwm\config\stm32_stm3211.c

9.4.1 Os

配置文件主要包括:Os_Cfg.cOs_Cfg.h

  • Os_Cfg.c

    (1)外部参考——EXTERNAL REFERENCES

    Application externals

    Interrupt externals:设置Os Tick频率

    (2)DEBUG输出——DEBUG OUTPUT

    变量os_dbg_mask来控制OS_DEBUG的日志输出等级。

    (3)应用——APPLICATIONS

    每个应用下由多个任务TASK组成。

    需要修改的内容.appId.name

    const OsAppConstType Os_AppConst[OS_APPLICATION_CNT]  = {
          
          			
    	{
          
          
        .appId = APPLICATION_ID_OsApplicationInteriorLight,  // 应用ID:0,1,2...
        .name = "OsApplicationInteriorLight",  //应用名称
        .core = 0,     //运行该应用的内核
        .trusted = true,    //是否信任
    	}
    };
    

    (4)计数器——COUNTERS

    主要用于定时给任务ALARM,周期性运行

    需要修改的内容:无

    隐含变量OS_COUNTER_CNT@Os_Cfg.h)计数器个数,对应GEN_COUNTER_HEAD元素个数;

    GEN_COUNTER_HEAD = {
          
          
    	GEN_COUNTER(
            /* id          */		COUNTER_ID_OsRteCounter,  //唯一ID标识,指示计数器下标,从0开始,GEN_COUNTER函数中未用到_id变量,主要给下面Os_Arc_OsTickCounter指示
            /* name        */		"OsRteCounter",           //counter名称
            /* counterType */		COUNTER_TYPE_HARD,        // @/Os/rtos/src/os_counter_i.h
            /* counterUnit */		COUNTER_UNIT_NANO,        // @/Os/rtos/src/os_counter_i.h
            /* maxAllowed  */		OSMAXALLOWEDVALUE,        // 计数器最大值
            /*             */		1,
            /* minCycle    */		1,                       //最小周期
            /*             */		0,
            /* owningApp   */		APPLICATION_ID_OsApplicationInteriorLight,  //使用该计数器的应用,上方Os_AppConst定义应用
            /* accAppMask..*/       ((1u << APPLICATION_ID_OsApplicationInteriorLight))
        ) 
    };
    //定义Os的tick下标,所以上面的定义中id必须从0
    CounterType Os_Arc_OsTickCounter = COUNTER_ID_OsRteCounter;
    

    (5)ALARMS

    两个功能:1、周期性唤醒任务执行(通过GEN_ALARM_AUTOSTART设置);2、周期性抛出事件ALARM(通过GEN_ALARM_HEAD设置),在任务内判断是否有相应事件出现。

    需要修改的内容:无

    GEN_ALARM_AUTOSTART(  // 命名方式  Os_AlarmAutoStart_ ## _id,可以通过GEN_ALARM_AUTOSTART_NAME获取
    				ALARM_ID_OsRteAlarm11.0ms,  // _id,0,1,...
    				ALARM_AUTOSTART_RELATIVE,
    				11.0,                      //ALARM周期
    				11.0,                      //循环周期
    				OSDEFAULTAPPMODE );
    
    GEN_ALARM_AUTOSTART(
    				ALARM_ID_OsAlarmBswServices,
    				ALARM_AUTOSTART_RELATIVE,
    				5,
    				5,
    				OSDEFAULTAPPMODE );
    
    GEN_ALARM_HEAD = {
          
          
    	GEN_ALARM(	ALARM_ID_OsRteAlarm11.0ms,  //对应于上面生成的ALARM
    				"OsRteAlarm11.0ms",
    				COUNTER_ID_OsRteCounter,  //对应生成的计数器的id
    				GEN_ALARM_AUTOSTART_NAME(ALARM_ID_OsRteAlarm11.0ms), //指向ALARM_AUTOSTART变量
    				ALARM_ACTION_SETEVENT,    //设置事件
    				TASK_ID_OsRteTask,     //任务ID @ Os_Cfg.h
    				EVENT_MASK_OsMainEvent,
    				0,
    				APPLICATION_ID_OsApplicationInteriorLight, /* Application owner */
    				(( 1u << APPLICATION_ID_OsApplicationInteriorLight ) 
    				) /* Accessing application mask */
    			)
    ,
    	GEN_ALARM(	ALARM_ID_OsAlarmBswServices,
    				"OsAlarmBswServic",
    				COUNTER_ID_OsRteCounter,
    				GEN_ALARM_AUTOSTART_NAME(ALARM_ID_OsAlarmBswServices),
    				ALARM_ACTION_ACTIVATETASK,//激活任务
    				TASK_ID_OsBswTask,
    				0,
    				0,
    				APPLICATION_ID_OsApplicationInteriorLight, /* Application owner */
    				(( 1u << APPLICATION_ID_OsApplicationInteriorLight ) 
    				) /* Accessing application mask */
    			)
    };
    

    (6)资源——RESOURSES

    (7)任务栈空间——STACKS(TASKS)

    需要修改的内容:无

    //DECLARE_STACK(_name,_size)  ->  stack__
    DECLARE_STACK(OsIdle, OS_OSIDLE_STACK_SIZE);  //定义空闲任务的栈空间,栈空间名称stack_OsIdle
    DECLARE_STACK(OsBswTask,  2048);             // 栈空间stack_OsBswTask
    DECLARE_STACK(OsRteTask,  2048);
    DECLARE_STACK(OsStartupTask,  2048);
    

    (8)任务——TASKS

    需要修改的内容:无

    EVENT_MASK_OsInitEvent说明SetCurrentState() -> Rte_Switch_currentMode_currentMode(currentMode) -> Rte_Switch_EcuM_ecuM_currentMode_currentMode(currentMode) ->SYS_CALL_SetEvent(TASK_ID_OsRteTask, EVENT_MASK_OsInitEvent),将事件EVENT_MASK_OsInitEvent产生,由OsRteTask任务(ID为TASK_ID_OsRteTask)处理。

    GEN_TASK_HEAD = {
          
          
    	{
          
          
    	.pid = TASK_ID_OsIdle,
    	.name = "OsIdle",
    	.entry = OsIdle,     //任务函数入口
    	.prio = 0,           //优先级
    	.scheduling = FULL,   // ??
    	.autostart = TRUE,    // ??
    	.proc_type = PROC_BASIC,
    	.stack = {
          
          
    		.size = sizeof stack_OsIdle,   //由上方的栈空间定义 
    		.top = stack_OsIdle,
    	},
    	.resourceIntPtr = NULL_PTR, 
    	.resourceAccess = 0,
    	.activationLimit = 1,
        .applOwnerId = OS_CORE_0_MAIN_APPLICATION,  //使用该任务的应用  定义为APPLICATION_ID_OsApplicationInteriorLight
        .accessingApplMask = (1u << OS_CORE_0_MAIN_APPLICATION),
    	},
    	
    {
          
          
    	.pid = TASK_ID_OsBswTask,
    	.name = "OsBswTask",
    	.entry = OsBswTask,
    	.prio = 2,
    	.scheduling = FULL,
    	.proc_type = PROC_BASIC,
    	.stack = {
          
          
    		.size = sizeof stack_OsBswTask,
    		.top = stack_OsBswTask,
    	},
    	.autostart = TRUE,
    	.resourceIntPtr = NULL_PTR, 
    	.resourceAccess = 0 , 
    	.activationLimit = 1,
    	.eventMask = 0 ,
    	.applOwnerId = APPLICATION_ID_OsApplicationInteriorLight,
    	.accessingApplMask = (1u <<APPLICATION_ID_OsApplicationInteriorLight)
    ,
    },
    {
          
          
    	.pid = TASK_ID_OsRteTask,
    	.name = "OsRteTask",
    	.entry = OsRteTask,
    	.prio = 1,
    	.scheduling = FULL,
    	.proc_type = PROC_EXTENDED,
    	.stack = {
          
          
    		.size = sizeof stack_OsRteTask,
    		.top = stack_OsRteTask,
    	},
    	.autostart = TRUE,
    	.resourceIntPtr = NULL_PTR, 
    	.resourceAccess = 0 , 
    	.activationLimit = 1,
    	.eventMask = 0 | EVENT_MASK_OsMainEvent | EVENT_MASK_OsInitEvent ,  //两个时间在OsRteTask任务中判断。EVENT_MASK_OsInitEvent在Rte_Internal_EcuM.c中(每次模式转换都会)生成该事件
    	
    	.applOwnerId = APPLICATION_ID_OsApplicationInteriorLight,
    	.accessingApplMask = (1u <<APPLICATION_ID_OsApplicationInteriorLight)
    ,
    },
    {
          
          
    	.pid = TASK_ID_OsStartupTask,
    	.name = "OsStartupTask",
    	.entry = OsStartupTask,
    	.prio = 1,
    	.scheduling = FULL,
    	.proc_type = PROC_BASIC,
    	.stack = {
          
          
    		.size = sizeof stack_OsStartupTask,
    		.top = stack_OsStartupTask,
    	},
    	.autostart = TRUE,
    	.resourceIntPtr = NULL_PTR, 
    	.resourceAccess = 0 , 
    	.activationLimit = 1,
    	.eventMask = 0 ,
    	.applOwnerId = APPLICATION_ID_OsApplicationInteriorLight,
    	.accessingApplMask = (1u <<APPLICATION_ID_OsApplicationInteriorLight)
    ,
    },
    };
    

    (9)勾子函数——HOOKS

    GEN_HOOKS( 
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
    );
    

    (11.)中断——ISRS

    #if (!defined(CFG_TC2XX) && !defined(CFG_TC3XX)) // Table Os_VectorToIsr is not used for Aurix architecture.
    GEN_ISR_MAP = {
          
          
      0
    };
    #endif
    

    (11)调度表——SCHEDULE TABLES

    (12)自旋锁——SPINLOCKS

  • Os_Cfg.h

    OS_NUM_CORES:核个数

    具体见代码注释。

9.4.2 Com

  • Com_Cfg.c

    需要修改的内容:无

    定义callout函数,例子:

    const ComNotificationCalloutType ComNotificationCallouts [] = {
          
           // Notifications
    	NULL};
    const ComRxIPduCalloutType ComRxIPduCallouts[] = {
          
          // Rx callouts
    	NULL};
    const ComTxIPduCalloutType ComTxIPduCallouts[] = {
          
          // Tx callouts
    	NULL};
    const ComTxIPduCalloutType ComTriggerTransmitIPduCallouts[] = {
          
          // Trigger transmit callouts
    	NULL};
    
  • Com_Cfg.h

    需要修改的内容信号ID,及关联文件

    通信相关配置,例如:

    COM_MAX_BUFFER_SIZE:通信缓存192字节,见7.3.7 OsRteTask读取数据和设置灯PWM

    COM_MAX_N_IPDUS:通信IPdu(类型为Com_Arc_IPdu_type)最多个数2个——发送和接收,结合上面COM_MAX_BUFFER_SIZE,共2个192字节通信缓存。

    COM_MAX_N_SIGNALS:信号Signal(类型为Com_Arc_Signal_type)个数,如果大于0,则会开启IPDU计数

    COM_MAX_N_GROUP_SIGNALS:信号组(类型为Com_Arc_GroupSignal_type)个数

    一些通错误编号COM_INVALID_PDU_ID(11.4)COM_INVALID_SIGNAL_ID(11.9)

    信号ID:如

    // COM SIGNAL GROUPS and SIGNAL IDs
    #define ComConf_ComSignal_DoorStatus          0
    #define ComConf_ComSignal_LightStatus          1
    

    关联文件

    1.Com_PbCfg.cComSignal[n]->ComHandleId

    2.调用Com_ReceiveSignal或者Com_SendSignal时候,如Rte_Internal_PwmSetManager.cretVal |= Com_ReceiveSignal(信号ID, value),函数体内通过GET_Signal(信号ID)获取ComSignal_type * Signal,通过GET_ArcIPdu(Signal->ComIPduHandleId)获取Com_Arc_IPdu_type *Arc_IPdu

    3.Com_PbCfg.cIPdu signal,如

    SECTION_POSTBUILD_DATA const ComSignal_type * const ComIPduSignalRefs_DoorStatusPdu[] = {
          
          
    	&ComSignal[信号ID],
    	NULL
    };
    
  • Com_PbCfg.c

    需要修改的内容ComSignal[n].ComIPduHandleId对应Com_PbCfg.h中的某一个ID、信号初始数值如 Com_SignalInitValue_DoorStatus、Post Build的一些结构体如SignalRef:ComIPduSignalRefs_DoorStatusPdu、GroupRef:ComIpduGroupRefs_DoorStatusPdu

    定义通信需要的数据结构——ComConfiguration,即Signal(非Arc_IPdu)。

    Arc_IPud/core/communication/Com/src/Com_Internal.h中定义,不需要自行定义。见7.3.4 CAN调用过程中的IPDU说明。

  • Com_PbCfg.h

    需要修改的内容IPDU ID号,并修改关联文件

    定义IPDU ID号,指定数据对应对应不同IPDU。如

    // COM IPDU IDs 
    #define ComConf_ComIPdu_DoorStatusPdu               0
    #define ComConf_ComIPdu_LightStatusPdu               1
    

    定义了门状态信息为IPDU[0]

    关联文件

    1.Com_PbCfg.cComSignal[n].ComIPduHandleId

    2.PduR_PbCfg.cPduRDestination_PduRRoutingPathRx_PduRDestPdu->DestPduId(接收)或者PduRRoutingPath_PduRRoutingPathTx->SrcPduId(发送)

关于信号ID的总结Com_Cfg.hCom_PbCfg.h中定义了SignalId(命名为ComConf_ComSignal_xxx)和IPduId(命名为ComConf_ComIPdu_xxx),分别制定了IPduArc_IPduSignal获取地址——通过GET_Signal(SignalId)获取ComSignal_type * Signal,通过GET_ArcIPdu(Signal->ComIPduHandleId)获取Com_Arc_IPdu_type *Arc_IPdu,通过GET_IPdu(Signal->ComIPduHandleId)获取ComIPdu_type *IPdu ;信号ID需要修改三个地方:PDUR(Arc_IPdu)Signal(IPdu)和具体函数调用的信号ID参数。

IPduArc_IPduSignal的区别:见7.3.4 CAN调用过程

9.4.3 ComM

  • ComM_Cfg.c

    需要修改的内容:无

    定义了ComM_MainFunction_ComMChannel()调用ComM_MainFunction(ComMConf_ComMChannel_ComMChannel)ComMConf_ComMChannel_ComMChannel定义了ComMChannelId每个通信端口对应一个channel?

  • ComM_Cfg.h

    需要修改的内容:无

    宏定义,如ComMConf_ComMChannel_ComMChannelCOMM_NETWORK_HANDLE_ComMChannelCOMM_CHANNEL_COUNTCOMM_USER_COUNT

  • ComM_PbCfg.c

    需要修改的内容:无

    定义ComM的通道和使用者信息。

  • ComM_PbCfg.h

    ComM_Configextern声明。

9.4.4 BswM

  • BswM_Cfg.c

    需要修改的内容:无

    根据USE来包含特定.h文件。

  • BswM_Cfg.h

    需要修改的内容:针对使用了不同的模块,使能相应内容

    使能BswM具体功能,如BSWM_CANSM_ENABLED STD_ONBSWM_COMM_ENABLED STD_ON

    宏定义数值

  • BswM_PBcfg.c

    需要修改的内容:无

    定义BswM需要用到的数据结构。

9.4.5 EcuM

  • EcuM_PBcfg.c

    需要修改的内容:无

    定义EcuM需要用到的数据结构。

  • EcuM_GeneratedCallouts.c

    需要修改的内容:无

  • EcuM_Cfg.h

    需要修改的内容:无

    宏定义唤醒源对应数值、复位方式对应数值

9.4.6 RTE

9.4.6.1 Config
  • Rte

    需要修改的内容:RTE任务具体执行的运行实体Runnable

    (1)Rte.c

    Rte任务,等待事件(包括系统满足条件后抛出的事件和定时抛出的事件);调用EcuM状态机转化函数;调用运行实体(RE, Runnable Entity)。

    (2)Rte.h

    定义RTE相关的宏定义,如错误码、RTE版本等。

  • Rte_BswM

    需要修改的内容:无

    (1)Rte_BswM.c

    BswM运行实体Rte_bswM_BswMRunnable,实际未被调用。思考:BswM是否可作为RTE任务的一个运行实体,循环调用?实际上Bsw任务直接调用了Rte_bswM_BswMRunnable中的MAIN功能函数BswM_MainFunction

    (2)Rte_BswM_Type.h

    定义ComM三种模式,NO、SILENT和FULL,为什么在这里定义??Rte_ComM_Type.h中也有定义。

  • Rte_ComM

    (1)Rte_ComM.c

    定义函数:获取当前通信模式(状态)、获取请求的通信模式(模式)、请求通信模式(状态)等。

    (2)Rte_ComM_Type.h

    定义ComM三种模式,NO、SILENT和FULL。

    问题:为什么RTE_MODE_ComMMode_COMM_FULL_COMMUNICATIONCOMM_FULL_COMMUNICATION定义的值不同?用处是?

  • Rte_EcuM

    (1)Rte_EcuM.c

    Rte_ecuM_GetBootTarget()Rte_ecuM_GetLastShutdownTarget()等。

    (2)Rte_EcuM_Type.h

    定义ECUM状态,如ECUM_STATE_OFFECUM_STATE_SLEEP等,ECUM_BOOT_TARGET_APPECUM_BOOT_TARGET_OEM_BOOTLOADER等。

  • Rte_ModeManager

    (1)Rte_ModeManager.c

    模式管理初始化

    (2)Rte_ModeManager_Type.h

    如果Rte_EcuM_Type.hRte_ComM_Type.h未定义,在此处定义。

  • Rte_Internal

    需要修改的内容:函数声明

    (1)Rte_Internal.c

    RteInitialized:初始化为FALSE,并include <Rte_MemMap.h>

    (2)Rte_Internal.h

    状态机定义:EcuM和ComM状态机类型声明。

    状态机取值定义:EcuM的运行、停止、睡眠、启动等;ComM的FULL_COMMUNICATION(接收发送均使能)、NO_COMMUNICATION(不使能通信)和SILENT_COMMUNICATION(只接受,不发送)。

    函数声明:BswM、ComM、EcuM、PwmSetManager(主要通过RTE进行数据的读写,见7.3.7 OsRteTask读取数据和设置灯PWM-函数定义总结)、IO操作

  • Rte_Internal_xxx.c

    Rte_Internal_BswM.c:获取请求的模式(状态)

    Rte_Internal_ComM.c:获取当前通信模式(状态)、获取请求的通信模式(模式)、请求通信模式(状态)等,封装了Rte_ComM.c中的函数。

    Rte_Internal_EcuM.c:封装了Rte_EcuM.c中的函数。

    Rte_Internal_IoHwAb.c:操作IO端口的接口。

    Rte_Internal_ModeManager.c:模式管理——通信控制、运行控制等,其实是获取状态。

    Rte_Internal_PwmSetActuator.c:面向执行器(运行实体),将RteBuff中数据拷贝到执行器数据结构或者给出BSW的接口。

    Rte_Internal_PwmSetManager.c:面向数据管理/获取(运行实体),将数据拷贝到数据获取实体的数据结构,或者拷贝到RteBuff,或者给出BSW的接口。

  • Rte_Main

    RTE启动、关闭,一块初始化 Ioc。

  • Rte_Buffers

    需要修改的内容:RTE缓存区

    (1)Rte_Buffers.c

    定义RTE缓存区,作为Runnable之间信息交换的桥梁。

    (2)Rte_Buffers.h

    EcuM、ComM函数、RTE缓存区(桥梁)变量的extern声明。

  • Rte_Type.h

    需要修改的内容:用到的基础变量

    声明基础变量,如

    /* Redefinition type DigitalLevel */
    typedef uint8 DigitalLevel;
    /* Redefinition type DutyCycle */
    typedef uint32 DutyCycle;
    /* Redefinition type Frequency */
    typedef uint32 Frequency;
    //.....
    
  • Rte_DataHandleType.h

    需要修改的内容:用到的数据结构

    Rte_Type.h内基础变量进一步封装成数据结构(结构体),如

    typedef struct {
          
          
        PwmSetDutyImpl value;
    } Rte_DE_PwmSetDutyImpl;
    
  • Rte_Cbk

    需要修改的内容:添加需要使用的回调函数。

    定义回调函数,如,

    void Rte_COMCbk_PwmSetDuty(void) {
          
          
        //unused
    }
    

    补充:什么是回调函数?回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。

  • Rte_Hook.h

    钩子函数。

    补充:什么是钩子函数? 钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。

  • Rte_Utils.h

    需要修改的内容:无

    封装工具,如memcpy

  • Rte_Fifo

    需要修改的内容:无

    初始化和使用Fifo。很好用的一个数据结构。

  • Ioc(Inter OsApplication Communication)

    需要修改的内容:无

    核间通信。

  • Rte_Assert.h

    需要修改的内容:无

    DET错误追踪分类,见《AUTOSAR_SWS_RTE》的DET Error Classification。

  • Rte_Calprms

    需要修改的内容:无

    校验功能。

  • RTE_Types_Workarounds.h

    宏定义,可以使得ArcCore RTE 集成Simulink SWC更加 简单,此文件不是AUTOSAR标准。

9.4.6.2 Contract
  • Rte_xxxx.h

    声明xxxx.c内的函数

9.4.6.3 MemMap
  • xxxx_MemMap.h

    实现内存映射

10.Artop

10.1 介绍

Artop基于Eclipse的EMF建模框架而构建,提供一些公共的基础功能,如所有工具均要用到的元模型实现,而将具体的应用功能留给工具开发商实现。(来自硕士论文《基于Artop的汽车电子软件架构工具的设计与实现》)

ARTOP针对AUTOSAR标准的XML文档,提供了专门的处理方法。其实现了AUTOSAR标准元模型的定义和一些列相关服务:符合AUTOSAR标准的XSD序列化和XML文件验证、AUTOSAR XML文件编辑、基于模板的目标代码、文件和报告生成。(来自硕士论文《基于AUTOSAR标准的VFB仿真工具》)

Artop架构层次

Artop架构层次

**Artop 1.0 内部结构 **

Artop架构内部

10.1.1 子项目

Name Description Status
Core The core of Artop that mainly includes the Meta Models, Serialization and Workspace Management. ACTIVE
Validation Sub-Project A framework which provides mechanisms for validating loaded AUTOSAR and non-AUTOSAR models. ACTIVE
ECU Configuration Sub-Project ECU Configuration editors, generators, wizards and further ECU configuration related functionalities. ACTIVE
AUTOSAR Textual Language Sub-Project (ARText) A textual modeling environment for AUTOSAR. ACTIVE
AUTOSAR Test Environment Sub-Project (ARUnit) Artop based test environment for AUTOSAR. ACTIVE

2019-8-15 Artop Demonstrator使用过程

10.1.2 三个文件

Artop Technology DemonstratorArtop SDKARText SDK

  • Artop Technology Demonstrator

    The Artop Technology Demonstrator is a collection of examples that demonstrate the features and capabilities of the underlying platform, i. e. Artop. It is a standalone application that can be used to conveniently explore Artop, it does not require an existing Eclipse installation.

  • Artop SDK

    The Artop SDK contains all Artop plug-ins needed to develop own plug-ins based on Artop. The SDK also contains source code and documentation, examples are provided as separate download. To get started with the SDK please refer to the Getting Started Guide in the Artop Wiki.

  • ARText SDK

    文本建模语言框架,基于XText(语言开发框架)开发。

    The ARText SDK contains all the ARText plug-ins needed to develop your own AUTOSAR textual language based on Artop, as well as the current textual language plug-ins. The SDK contains source code and documentation. For more information please refer to the ARText section.

    什么是XText:XText帮助程序员创建一套基于文本的小型领域特定语言(DSL),抑或是实现一门成熟的通用计算机程序设计语言。

10.2 代码生成技术

(1)面向属性编程(AOP)

AOP通过在代码中添加元数据的方式来自动生成代码.

优秀软件xDoclet,是一个经过拓展的Javadoc Doclet引擎,允许用户使用类似于JavaDoc标记之类的东西来向诸如类、方法和字段之类的语言特征中添加元数据,然后利用这些额外的元数据来生成相关文件。

(2)模板技术

代码生成要有一定文本结构的文件,使用文本模板工具。Velocity开源项目(Apache),基于Java的模板引擎,用户可以使用Velocity Template Language(VTL)的脚本语言来引用Java代码定义的对象,将对象的信息和模板的内容相结合生成代码文件。

10.3 Artop例程

10.3.1 org.artop.aal.examples.actions

10.3.1.1 文件夹结构
org.artop.aal.examples.actions
|-- META-INF
|   `-- MANIFEST.MF
|-- build.properties
|-- icons
|   `-- sample.gif
|-- plugin.properties
|-- plugin.xml
|-- pom.xml
`-- src
    `-- org
        `-- artop
            `-- aal
                `-- examples
                    `-- actions
                        |-- AdvancedAddShortNamePrefixAction.java
                        |-- CountIdentifiablesAction.java
                        |-- CreateSaveNewAutosarAction.java
                        |-- ShowProxyReferencesAction.java
                        |-- SimpleAddShortNamePrefixAction.java
                        |-- internal
                        |   |-- Activator.java
                        |   |-- dialogs
                        |   |   |-- SelectTargetTypesDialog.java
                        |   |   `-- ShortNamePrefixDialog.java
                        |   `-- messages
                        |       |-- Messages.java
                        |       `-- messages.properties
                        `-- providers
                            |-- AbstractArtopExampleActionProvider.java
                            `-- AutosarExampleActionProvider.java

11.Matlab Simulink

11.1 AUTOSAR Blockset

AUTOSAR Blockset 提供了用于 AUTOSAR 库例程和基础软件 (BSW) 服务(包括 NVRAM 和诊断)的模块和结构。通过将 BSW 服务与应用程序软件模型一起进行仿真,可以在不离开 Simulink 的情况下验证 AUTOSAR ECU 软件。

11.1.1 对 AUTOSAR Classic 软件组件进行建模

在 Simulink 中,使用默认 AUTOSAR 端口、接口和其他配置自动创建 AUTOSAR Classic 软件组件。

X.一些笔记

X.文件命名

General Specification of Basic Software Modules文件中有定义,包括配置文件的c文件、h文件等。

猜你喜欢

转载自blog.csdn.net/lovely_yoshino/article/details/133345749