前一章简单的对Communication Stack作了一个介绍,了解到了在AUTOSAR中通信的层级架构。这一章来看看这通信架构中的属于ECU抽象层的interface,还是以CAN为例,那对应的就是CanIf(CAN Interface)。
补充:本来是应该从处于AUTOSAR最底层的MCAL层开始进行介绍,因为MCAL是跟MCU强相关的,每个MCU原厂的MCU都会有点不一样的东西,这些异处自然也会体现在MCAL层上,再因为MCAL层其实就是底层驱动,所以在这个专栏里,就不再对MCAL中详细介绍了。感兴趣的小伙伴可以参考文章:《AUTOSAR CAN Driver(CAN驱动程序)》。
Can的Communication Stack结构图如下图所示:
在MCAL层,看到了不仅仅有CanDriver, 还有SPI和I/O Driver,其中SPI针对的是那些需要外挂CAN controller的应用,除了外部CAN controller芯片需要IO来进行输入输出,有些特殊的CAN Tranceiver也需要IO来进行输入输出,比如经典的TJA1043,就有一个EN引脚和STB引脚,所以就需要IO Driver来控制。
CanIf模块位于MCAL层(CAN Driver)和上层通信服务层(如 CAN Network Management, CAN Transport Protocol 等)的中间,其充当 CAN Driver 和上层通信服务层的接口层。所以它位于BSW层中的ECU抽象层,为了让上层软件与ECU硬件设计无关。
CAN Interface 模块主要功能如下:
1. 初始化
2. 发送请求服务
3. 发送确认服务
4. 接收指示服务
5. Controller 模式控制服务
6. Transceiver 模式控制服务
7. PDU channel mode 控制服务
首先介绍一个叫做(HOH)Hardware Object handles的这个概念,这个其实在MCAL层出现的较多,对MCAL的上层软件来说,HOH其实就是一个CAN报文的消息缓冲区,上层软件想发送报文的时候,就把报文往里面塞就好了,MCAL会负责发送出去,同理,接收报文就是从缓冲区里读取。但是在MCAL层,这个HOH的实现就五花八门了,这里就不多解释了。后文出现的HTH意思就是用来发送的HOH,而HRH意思就是用来接收的HOH。
再来介绍一下BasicCAN 和FullCAN的概念,BasicCAN 就是说一个HOH可以同时接收和发送多个CAN ID报文,而FullCAN则是一个HOH直接接收和发送单个CAN ID报文。这里不妨衍生以下,这两个的定义其实起源颇深,大家可参考文章:《Full-CAN vs. Basic-CAN》。
当 CANIF_PUBLIC_TX_BUFFERING 配置为 ON 的时候, CanIf 会为 Basic 类型(多个 PDU 共享一个 HTH)的 PDU 分配发送缓冲区。
当 CanIf 调用 Can_Write 返回 CAN_BUSY 的时候,说明当前的 HTH 正在发送上一帧数据,不能写入新的数据。此时, CanIf 将会把新的数据储存在 CanIf 的缓冲区。在 Can 的发送确认中,针对刚刚发送成功的 HTH(此时已经处于空闲状态,可以写入新的数据), CanIf 将查询分配给该 HTH 的 PDU,是否有数据在缓冲区中,如果有的话, CanIf 将自动把缓冲区中的数据写入 HTH,并发送出去。
CanIf 发送缓冲区具有如下特性:
1. Full 类型的 PDU 不会分配缓冲区;
2. 每个 Basic 类型的 PDU 有且只有一级缓存,因此,新的数据覆盖旧的数据;
3. 当 CANIF_PUBLIC_TX_BUFFERING 配置为 OFF 时,所有的 PDU 都没有发送缓冲区;
4. 如果同一 HTH 的缓冲区中有多个 PDU,将按照 PDU 的优先级(CANID 从小到大的顺序)进行发送。
CAN controller 在硬件实现上的状态抽象为以下四种基本状态: UNINIT、 STOPPED、STARTED 和 SLEEP。对每个 CAN Controller,在 Can Interface 模块上有一个对应的“软件”状态机,其有以下几种状态: CANIF_CS_UNINIT,CANIF_CS_STOPPED, CANIF_CS_STARTED 和CANIF_CS_SLEEP。上层可以通过调用 CanIf_SetControllerMode()服务来改变 CAN Controller 的状态。下表展示了用户使用CanIf_SetControllerMode()时的状态转换:
CanIf 当前模式 | 用户请求模式 | 传递给 Can 的模式 |
---|---|---|
X | CANIF_CS_STARTED | CAN_T_START |
CANIF_CS_SLEEP | CANIF_CS_STOPPED | CAN_T_WAKEUP |
CANIF_CS_STOPPED | CANIF_CS_STOPPED | CAN_T_STOP |
CANIF_CS_STARTED | CANIF_CS_STOPPED | CAN_T_STOP |
X | CANIF_CS_SLEEP | CAN_T_SLEEP |
不合法的状态转换请求,例如 SLEEP 到 START, START 到 SLEEP,将在 Can 模块被检测到,并返回 E_NOT_OK。在转换阶段, Can Interface 模块中软件状态机的状态可能会与 CAN controller 中的硬件状态不一致。
调用 CanIf_SetControllerMode 进行模式切换,如果硬件的模式能够在较短的时间内完成,那么模式转换可以是同步的,也就是说,在 CanIf_SetControllerMode 返回之前,模式转换已经完成,并且通过 User_ControllerModeIndication 通知了上层。否则,模式转换是异步完成的,用户需周期调用 Can_MainFunction_Mode 来完成模式转换并通知上层。
CanIf中,可调用 CanIf_SetPduMode 控制某个 CAN 通道上 PDU 的接收和发送,各 CAN通道可分别控制。CanIf支持的Pdu Mode 模式和特征描述如下所示:
PDU Mode | 描述 |
---|---|
CANIF_OFFLINE | 1.阻止发送请求,调用CanIf_Transmit()时,返回 E_NOT_OK。 2.清除相关通道的发送缓冲区。 3.阻止调用上层的接收指示回调函数。 4.阻止调用上层的发送确认回调函数。 |
CANIF_TX_OFFLINE_ACTIVE | 1. 调用的 CanIf_Transmit()时,返回 E_OK,但是不调用 Can 的发送接口发送报文。 2. 调用的 CanIf_Transmit()时,直接调用上层的发送确认函数。 3. 阻止调用上层的接收指示回调函数。 |
CANIF_TX_OFFLINE | 1. 阻止发送请求,调用 CanIf_Transmit()时,返回 E_NOT_OK。 2. 清除相关通道的发送缓冲区。 3. 允许调用上层的接收指示回调函数。 4. 阻止调用上层的发送确认回调函数。 |
CANIF_ONLINE | 1. 调用 CanIf_Transmit()时,正常发送报文,返回 E_OK 。 2. 报文发送成功时,调用上层的发送确认函数。 3. 接收到报文时,调用上层的接收指示回调函数。 |
当 CanIf 从 Can Drviver接收到一帧报文,可以对报文的 DLC 进行检查,如果 DLC 不合法,可以报错,并进行相应的处理。DLC 检查的方法有两种:
若 DLC 检查失败,等同于该报文没有接收到,不会调用上层的接收指示回调函数。
当相关配置参数使能后, CanIf 提供了接口,可通过这些接口查询 CanIf PDU 的的状态和数据,这包括:
当 CanIfPublicSetDynamicTxIdApi 为 ON 时,用户可以将单个或多个发送 PDU 的CanIfTxPduType 配置为 DYNAMIC。对于DYNAMIC 类型的 PDU,用户可以在运行过程中调用 CanIf_SetDynamicTxId 修改其 CANID 值,修改之后,该 PDU 将按照的新的 CANID进行发送。新的 CANID 是存储在 CanIf 模块的 Buffer 当中的,所以当控制器掉电重启后,将按照默认的 CANID 进行发送。
调用 CanIf_SetDynamicTxId 传入的 CANID 并不限制是标准帧还是扩展帧,但传入扩展帧时,传入的扩展帧的 CANID 需要将其最高位置 1。例如 0x5000 的扩展帧 ID 为, 0x80005000; 0x100 的扩展帧为 0x80000100。注:扩展帧必须为 32Bit。
补充:除了以上简单介绍的几大点,CanIf模块还有一大堆的细节,大家如果想更深入了解CanIf模块,可参考AUTOSAR官方文档《AUTOSAR_SWS_CANInterface.pdf》。