目录
1.内容简介
2.CanIf详细设计
2.1 CanIf功能简介
2.2 一些关键概念
2.3依赖的上下层模块
2.4 功能详细设计
2.4.1 Hardware object handles
2.4.2 Static L-PDUs
2.4.3 Dynamic L-PDUs
2.4.4 Dynamic Transmit L-PDUs
2.4.5 Dynamic receive L-PDUs
2.4.6Physical vhannel view
2.4.7 CAN Hardware Unit
2.4.8 BasicCAN and FUllCAN reception
2.4.9 Initialization
2.4.10 Transmit request
2.4.11 Transmit data flow
2.4.12 Transmit buffering
2.4.12.1 General Behavior
2.4.12.2 Buffer characteristics
2.4.12.2.1 Storage of L-PDUs in the transmit L-PDU buffer
2.4.12.2.2 Clearance of transmit L-PDU buffers
2.4.12.2.3 Initialization of transmit L-PDU buffers
2.4.12.3 Data integrity of transmit L-PDU buffers
2.4.13 Transmit confirmation
2.4.14 Receive data flow
2.4.15 Receive indication
2.4.16 Read received data
2.4.17 Read Tx/Rx notification status
2.4.18 Data integrity
2.4.19 CAN Controller Mode
2.4.19.1 General Functionality
2.4.19.2 CAN Controller Operation Modes
2.4.19.3 Controller Mode Transitions
2.4.19.4 Wake-up
2.4.19.4.1 Wake-up detection
2.4.19.4.2 Wake-up Validation
2.4.20 PDU channel mode control
2.4.20.1 PDU channel groups
2.4.20.2 PDU channel modes
2.4.21 Software receive filter
2.4.21.1 Software filtering concept
2.4.22 Data Length Check
2.4.23 L-SDU dispatcher to upper layers
3.CanIf模块配置
3.1 CanIf
3.2 CanIfCtrlDrvCfg
3.3 CanIfDispatchCfg
3.4 CanIfPrivateCfg
3.5 CanIfPublicCfg
3.6 CanIfTrcvDrvCfg
3.7 CanIfInitCfg
3.7.1 CanIfBufferCfg
3.7.2 CanIfInitHohCfg
3.7.3 CanIfRxPduCfg
3.7.4 CanIfTxPduCfg
4.CanIf模块代码分析
4.1 发送回调
4.2 接收回调
4.3 BusOff事件回调
4.4 模式切换回调
4.5 Can模块接口函数分析
1.内容简介
本问将从CanIf详细设计、CanIf模块I配置、CanIf模块关键代码分析三个大的方面来详细分析CanIf模块。
2.CanIf详细设计
2.1 CanIf功能简介
CanIf 模块位于底层的(CAN控制器,收发器)驱动和更上的通信服务层(CAN状态管理,CAN网络管理,CAN传输层)之间。CanIf模块是上层通信模块与CanDrv之间的服务接口。
CanIf模块提供统一的接口来管理CAN硬件设备(CAN控制器,CAN收发器)。因此,CanSM模块可以基于物理CAN通道来控制多个底层内部和外部CAN控制器/CAN收发器。
所有独立于硬件的CAN驱动功能接口组成了CanIf模块,这些独立于硬件的CAN驱动功能接口属于相应ECU的CAN通信设备驱动程序。这些功能在CAN接口模块中实现一次,这样底层CAN设备驱动程序只关注于相应的特定CAN硬件设备的访问和控制。
CanIf实现了PDU Router和AUTOSAR COM上层通信模块的主控制流和数据流要求:发送请求处理、发送确认/接收指示/错误通知和CAN控制器的启动/停止,从而唤醒/参与网络。它的数据处理和通知API是基于CAN L-SDU的,而用于控制和模式处理的API提供了一个与CAN Controller相关的视图。
在Transmit Requests的情况下,CanIf用相应的参数完成L-PDU传输,并通过相应的CanDrv将CAN L-PDU传递给CAN控制器。CanIf将收到的L- PDU中的L-SDU分发给上层。接收L-SDU与上层之间的分配是静态配置的。在传输确认时,CanIf负责通知上层传输成功。
CanIf模块提供CAN通信抽象访问CAN驱动程序和CAN收发驱动程序服务,以实现对CAN网络的控制和监控。CanIf向下转发CanSM的状态改变请求到下层CAN设备驱动,向上转发CanDrv / CAN Transceiver Driver事件由CanIf模块转发到相应的NM模块。
2.2 一些关键概念
CAN communication matrix:CAN通信DBC通信矩阵,描述了完整的CAN通信网络,通常包含以下基本信息。
-- 参与CAN网络的节点(nodes)
-- 定义所有的CAN PDU(ID,数据长度)
-- 每个PDU的发送节点和接收节点
CanIf Rx L-PDU:从底层接收的L-PDU并传输到上层
CanIf Tx L-PDU:上层需要通过底层发送出去的PDU
CanIfRxBuffer:CanIf模块中用于存储接收PDU的单元素RAM缓存区
CanIfTxBuffer:CanIf模块中用于存储发送PDU的单元素RAM缓存区,如果单个CanIfTxBuffer的buffersize设置为0,则CanIfTxBuffer仅用于引用HTH。
HW object:一个HW object可以理解位CAN硬件单元(Can Controller) 中一个PDU的硬件缓存(CAN RAM,MaxiBox)。
Hardware Receive Handle (HRH):HRH由CanDrv模块定义和提供,一个HRH可以理解为代表一个HW Object(一个接收邮箱的硬件缓存)。HRH通常被CanIf作为PDU接收处理中的参数使用。
Hardware Transmit Handle(HTH):HTH由CanDrv模块定义和提供,一个HRH可以理解为代表一个或多个HW Object(发送邮箱的硬件缓存)。
Integration Code:需要集成到AUTOSAR系统的代码,以适应非标准化的功能。例如ECU状态管理器的Callouts和各种其他BSW模块的Callbacks。I/O硬件抽象也被称为Integration Code。
L-PDU channel group:一组CAN L-PDU,它只属于一个底层网络。它们通常由一个上层模块处理。
Physical channel:一个物理通道表示一个从CAN控制器到CAN网络的接口。CAN硬件单元的不同物理通道可以访问不同的网络。
2.3依赖的上下层模块
依赖的上层模块有PDUR,CanNm,CanTp,CanSM,EcuM等。AUTOSAR BSW体系结构表明,应用程序数据缓冲区位于上层。直接访问这些缓冲区是非法的。在数据传输过程中缓冲区位置由CanIf模块传递到CANDriver模块(传递数据指针),在数据接收过程中,CANDriver调用用CanIf的回调函数会引用这些缓冲区。在执行这些发送任务/接收服务时缓冲区位置被传递。每次访问缓冲区时,使用锁机制来保证数据完整性。CanIf使用的API由通知服务组成,这些服务是传输与CAN相关的数据(即数据长度)到目标上层的基本代理。这些服务的调用参数指向在CanDrv中缓冲的信息,或者它们直接指向CAN硬件。
EcuM模块初始化CanIf模块。
CanSM模块负责所有CAN控制器和收发器的模式控制管理:CanSM -> CanIf_SetMode -> Can_SetMode。
底层模块:CanDrv
CanDrv仅提供对CAN控制器的硬件访问抽象,CanSM模块完成模式控制。CanDrv检测和处理CAN控制器相关的事件并把他们通知到CanIf层。CanIf将CanSM的操作模式请求传递给相应的底层CAN控制器
CanDrv提供了一个标准化的L-PDU来确保CanIf的硬件独立性。指向标准的L-PDU的指针指向的是一个临时的缓冲区或者是一个硬件独立的CAN驱动。对于CanIf模块而言,L-PDU是透明的。
CanIf模块提供通知服务给CanDrv,例如:传输确认,接收确认,传输取消确认,控制器模式改变通知。
多个CanDrv对应不同的中断向量,上面提到的这些回调函数必须是可重入的。
CanDrv中使用的回调函数声明和定义在CanIf模块。CanIf模块中调用的回调函数声明和定义在更上层模块。
底层模块:CAN Transceiver Driver
每个CanTrcv模块自己实现模式控制,CanIf没有控制CanTrcv模式的功能,CanIf仅能把所有的CanTrcv的的模式访问接口统一为一个。CanSM模块能够触发CanTrcv的模式转换。
2.4 功能详细设计
2.4.1 Hardware object handles
用于传输和接收数据的硬件对象句柄是CAN邮箱结构的抽象引用,它包含了CAN相关的CanId, DLC, Data等参数。基于CAN硬件缓存抽象,每一个在CanIf模块中引用的硬件对象独立于CAN hardware buffer layout。HOH被作为调用CanDrv模块接口函数的参数,由CanDrv模块配置提供,被CanDrv模块用作CAN邮箱通讯的缓存标识。
CanIf仅是HOH的用户,对硬件的特定信息不做解释。CanIf是独立于硬件的。
硬件接收句柄:一个HRH标识一个CAN控制器的发送邮箱(The HRH shall be a handle referencing a logical Hardware Receive Object of the CAN Controller mailbox)
HRH应使CanIf能够使用参考接收单元的BasicCAN或FullCAN接收方法,并向目标上层模块指示接收L-SDU。一个HRH可以配置为接收单个CAN报文(FullCAN),接收一组CAN报文(BasicCAN)。如果HRH配置为BasicCAN接收,CanIf层的软件滤波将会使能。
硬件传输句柄:一个HRT标识一个CAN控制器的接收邮箱(The HRH shall be a handle referencing a logical Hardware Receive Object of the CAN Controller mailbox)
HTH应使CanIf能够使用参考传输单元的BasicCAN或FullCAN传输方式,并确认向目标上层模块传输的L-SDU。
2.4.2 Static L-PDUs
CanIf需要为每一个L-PDU分配一个CAN Controller。单个L-PDU分配给多个CAN Controller是非法的。这样是为了在传输确认和接收指示事件时确保正确的L-SDU调度。这样CanIf就可以从L-PDU中识别出CAN控制器。
每个L-PDU与一个上层模块相关联,以确保在接收、传输确认和数据访问时正确的数据分配。每个上层模块可以使用L-PDU同时服务于不同的CAN控制器。
2.4.3 Dynamic L-PDUs
CanIf应该支持使用CanIfRxPduCanIdMask过滤传入消息的能力。在对两个id都应用了CanIfRxPduCanIdMask之后,过滤应该通过比较传入的CanId和存储的CanIfRxPduCanId来完成。这应该在过滤不带掩码的常规Can Id之后进行,以允许对属于掩码定义范围或基于Can Id的范围的一些Can Id进行单独处理。此外,应该支持动态 Tx和Rx L-SDU,其中CanId驻留在L-SDU的元数据(MetaData)中。
在动态l - sdu的传输过程中,当定义了一个CanIfTxPduCanIdMask时,元数据提供的CanId的可变部分必须使用该掩码与CanId合并。当没有配置CanIfTxPduCanIdMask和CanIfTxPduCanId时,元数据应使用直接作为CanId。
在接收动态L-SDU时,将接收到的CanId放入L-SDU元数据中。MetaData的内容独立于CanIfRxPduCanIdMask参数
2.4.4 Dynamic Transmit L-PDUs
动态传输L-PDU的定义:L-PDU允许在运行时重新配置CanId (CanIfTxPduType)或部分作为L-SDU的元数据。
2.4.5 Dynamic receive L-PDUs
动态接收l -PDU定义:与一组Can Id对应的l -PDU,其中实际接收到的Can Id作为PDU数据的一部分提供给上层。
2.4.6Physical vhannel view
一个物理通道与一个CAN控制器和一个CAN收发器相连,而一个或多个物理通道可以连接到一个网络。
CanIf提供控制所有CAN设备的服务,如所有支持的ECU CAN通道的CAN控制器和CAN收发器。CanSM使用这些API为ComM提供一个网络视图,用于对连接到单个网络的所有物理通道执行唤醒和休眠请求。
CanDrv和CanTrcv提供每一个物理通道的状态信息给CanIf模块,CanIf模块把这些状态信息传递给CanSM模块
CanIf模块提供的ControllerId是对CanDrv instances的抽象,范围从0开始。
CanIf模块提供的TransceiverId是对CanTrcv instances的抽象,范围从0开始。
在通知过程中,CanIf映射来自CAN Controller 或 CAN Transceiver的驱动模块参数到CanSM。
CanIf模块支持多个物理CAN通道。CanSM在网络控制中必须能区分这些物理通道。CanIf API提供了请求和读控制多个底层的物理CAN通道。
CanIf不会区分使用哪一个专用的CAN物理层,也不会决定一个或者多个CAN Controller被连接。
2.4.7 CAN Hardware Unit
CAN Hardware Unit由一个或者多个相同的CAN Controller组合而成,每一个硬件单元由相应的CanDrv提供服务。
如果不同的CAN Controllers被使用,那么不同类型的CanDrvs就会被使用,但是CanIf模块却提供了统一的API。CanIf模块在配置中收集了CAN Controller以及Hardware Objects的梳理和类型。
2.4.8 BasicCAN and FUllCAN reception
CanIf模块为了激活软件接收滤波,会区分是BasicaCAN还是FullCAN。
FullCAN:一个CAN邮箱(Hardware Object)仅能发生或者接收单个的CanIDs
BasicCAN:一个CAN邮箱(Hardware Object)能接收或者发送一定范围内的CanIds
配置成BasicCAN的Hardware Receive Object 通过软件滤波能够接收一定范围内的CanIds。这个范围内的CanIDs可能会将超出预定Rx L-PDUs范围外的Can报文也接收了。CanIf模块随后会执行软件滤波,只会将预定范围内的Rx L-PDU传递给对应的上层模块。
2.4.9 Initialization
EcuM模块调用CanIf模块的CanIf_Init()函数完成模块初始化。
2.4.10 Transmit request
CanIf模块提供CanIf_Transmit()公共接口给上层模块传递L-PDU。上层模块只能通过CanIf_Transmit()接口来启动数据传输服务。
调用CanIf_Transmit()接口的时候,CanIf模块执行下述的Action:
. 检查,初始化CanIf模块状态
. 识别CanDrv(有多个CanDrvs被使用)
. 确定访问CAN硬件传输对象的HTH
. 调用CanDrv的Can_Write()函数
传输成功返回E_OK
2.4.11 Transmit data flow
传输服务CanIf_Transmit()是基于L-PDUs的。通过L-PDU数据结构中L-SDU ID和指向L-SDU数据的指针可以访问/获取L-SDU数据。
CanIf_Transmit()或回调服务<User_RxIndication>()将L-SDU数据结构的引用作为服务中的参数。如果将L-PDU配置为触发传输,则L-SDU指针为空指针。
CanIf存储关于为发送任务配置的可用硬件对象的信息。函数CanIf_Transmit()将CanTxPduId映射到相应的HTH并调用函数Can_Write()。
2.4.12 Transmit buffering
2.4.12.1 General Behavior
在CanIf的范围内,传输过程从调用CanIf_Transmit()开始,并以调用上层模块的回调函数结束。在发送过程中,CanIf、CanDrv和CAN Mailbox在一个位置只存储一次要发送的L-PDU。根据传输方法的不同,它们是: Can硬件发送邮箱或者是CanIf模块中L-PDU发送buffer(如果发送缓存使能了)。
对于触发传输,CanIf只需要存储给定L-PDU的发送请求,而不需要存储L-PDU的数据。当HTH(再次)空闲时,通过触发器传输函数及时获取数据。要求传输的单个Tx L-PDU不能存储两次。这种行为与CAN网络上定期通信的通常方式相对应。
如果传输缓冲被启用,CanIf将在CanIf 发送(CanIfBufferCfg)中存储Tx L-PDU,如果它在发送请求时被CanDrv拒绝。
基本上,在CanIf缓冲Tx L-PDU的整体缓冲区包含一个或多个 CanIfBufferCfg(参见CanIfBufferCfg)。然而每个CanIfBufferCfg被分配到一个或多个专用的CanIfBufferHthRef(参见CanIfBufferHthRef),并可以配置缓冲一个或多个Tx L-PDU。但是正如上面提到的,在CanIfBufferCfg的总数量中,每个Tx的L-PDU只能缓存一个实例。
在相应的Tx L-PDU的配置设置中,是否使能了传输缓冲,在L-PDU传输过程中CanIf的行为是不同的。如果transmit缓冲被禁用,并且发送到CanDrv的请求失败(CAN控制器邮箱正在使用,BasicCAN), L-PDU不会被复制到CAN控制器的邮箱,CanIf_Transmit()返回值E_NOT_OK。如果传输缓冲被启用,并且发送请求到CanDrv失败,根据CanIfTxBuffer的配置,L-PDU可以存储在CanIfTxBuffer中。在这种情况下,API CanIf_Transmit()返回值E_OK,尽管传输无法执行。在这种情况下,CanIf通过CanIf_TxConfirmation()回调处理L-PDU的未完成传输,上层不必重试传输请求。
可用的传输CanIf Tx L-PDU缓冲区的数量可以完全独立于该ECU的can网络描述文件中定义的已使用的传输L-PDU的数量进行配置。
Tx L-PDU通过CanIfBufferCfg con配置容器(参见CanIfBufferCfg)引用hth。如果传输缓冲也不需要,这是有效的。在这种情况下,CanIfBufferCfg的缓冲区大小(参见CanIfBufferSize)必须设置为0。那么CanIfBufferCfg配置容器只用于引用HTH。
2.4.12.2 Buffer characteristics
CanIfTxPduBufferRef, CanIfBufferCfg, CanIfBufferHthRef 和 CanIfBufferSize 描述了CanIfBufferCfg 的可能配置。
2.4.12.2.1 Storage of L-PDUs in the transmit L-PDU buffer
如果CanDrv在调用Can_Write()时返回CAN_BUSY, CanIf尝试只在发送L-PDU缓冲区中存储一个新的发送L-PDU或它的发送请求。
2.4.12.2.2 Clearance of transmit L-PDU buffers
CanIf将在传输确认期间评估是否挂起的CanIfTx L-PDU或发送请求存储在CanIfTxBuffers中,这些缓冲区被分配给新的空闲硬件传输对象。
如果挂起的CanIfTx L-PDU或发送请求在CanIfTxBuffers中可用,那么CanIf将调用Can_Write()对挂起的CanIfTx L-PDU或发送请求(分配给新的硬件传输对象的请求)具有最高优先级。
CanIf应按照每个HTH的优先顺序发送L-PDU或存储在发送L-PDU缓冲区中的发送请求。CanIf不区分L-PDU和Transmit Requests。
当CanIf对存储在CanIfTxBuffer中的高先级为L-PDU和Transmit Requests调用Can_Write()函数,且Can_Write()的返回值为E_OK时,CanIf应在的传输确认转到之前,立即从Transmit L-PDU Buffer中删除该L-PDU或Transmit Request。
2.4.12.2.3 Initialization of transmit L-PDU buffers
CanIf_Init()函数被调用后,CanIf完成发送L-PDU Buffer的初始化。
2.4.12.3 Data integrity of transmit L-PDU buffers
CanIf应防止并发访问用于发送L-PDU和发送请求的发送L-PDU缓冲区。
2.4.13 Transmit confirmation
如果之前的传输请求成功完成,CanDrv通过调用CanIf_TxConfirmation()将其通知给CanIf。
当调用回调通知CanIf_TxConfirmation()时,CanIf需要识别与成功传输的L-PDU连接的上层通信层,并通过调用CanIf的确认服务<User_TxConfirmation>(E_OK)通知其已完成传输。
回调函数<User_TxConfirmation>()由被通知的上层模块实现。
可以设计或配置上层通信模块,使不同的L-PDU或L-PDU组可以使用单个或多个回调函数服务处理发送确认。在发送确认相应的L-PDU传输请求时,所有这些服务都由CanIf调用。Transmit L-PDU用于发送与目标上层模块相关联的不同确认业务。此分配是在配置期间静态进行的。
一个发送L-PDU只能分配给一个发送确认回调服务。
2.4.14 Receive data flow
根据AUTOSAR基本软件架构,接收到的数据将在上层通信栈(即AUTOSAR COM、CanNm、CanTp、DCM)中进行评估和处理。这意味着,上层模块既不能与CanDrv (Rx)的缓冲区一起工作(即改变),也不能访问CanIf (Tx)的缓冲区。
只有当CanIf_PUBLIC_READRXPDU_DATA_API设置为TRUE时,CanIf才能在接收路径中提供内部缓冲。
如果新接收到一个L-PDU, CanDrv调用CanIf的CanIf_RxIndication()。L-PDU特定数据的访问由以下参数实现: 硬件接收句柄(HRH),接收CAN ID,接收CAN数据长度,接收到的L-PDU数据的引用(指针)。
接收到的L-PDU依赖于硬件,并分配到通信系统的最低层—给CanDrv。HRH通过L-PDU作为CanDrv与上层模块之间的链路。HRH标识一个CAN硬件接收对象,其中接收到一个新的CAN L-PDU。
在CanDrv指示接收到的L-PDU (CanIf_RxIndication()被调用)之后,CanIf应按照7.14 Receive指示进行操作。CanIf无法识别CanDrv是使用临时缓冲还是直接硬件访问。它在调用CanIf_RxIndication().v时期望规范化的L-PDU数据。
CAN硬件接收对象被锁定,直到数据复制到临时或上层模块缓冲区的过程结束。在CanIf的CanIf_RxIndication()返回后,硬件对象将立即被释放,以避免数据丢失。
CanDrv、CanIf和上层模块,属于接收的L-PDU,访问相同的临时中间缓冲区,这些缓存区可以位于can控制器的can硬件接收对象中,也可以作为CanDrv的临时缓冲区。
2.4.15 Receive indication
对CanIf_RxIndication()的调用在其参数中引用了一个新接收到的CAN L-PDU。如果调用了CanIf_RxIndication()函数,CanIf将评估CAN L-PDU是否接受,并为上层通信层随后的访问准备L-SDU。如果配置了该CAN L-PDU,并且该CAN L-PDU被成功检测并接受进行进一步处理,则CanIf使用<User_RxIndication>()通知上层模块关于此异步事件。
如果CanTrv中调用CanIf_RxIndication函数,CanIf执行一下操作:
. 软件过滤,如果配置了
. DLC检查,如果配置了
. 缓冲区接收L-SDU,如果配置了
. 调用上层提供的接收通知回调函数,如果配置了
2.4.16 Read received data
读取接收数据的API CanIf_ReadRxPduData()是上层模块读取最近从CAN网络接收到的CAN l - SDU的通用接口。上层模块仅通过CanIf服务发起接收请求,不直接访问CanDrv。如果CanIf将接收到的L-SDU写入上层模块L-PDU缓冲区,则初始化的接收请求成功完成。
函数CanIf_ReadRxPduData()使得读取数据不依赖于接收事件(RxIndication)成为可能。当它配置为启用时,不一定要为相同的L-SDU配置一个接收指示服务。如果需要,也可以启用接收指示。
通过这种方式的类型机制获得L-SDUs (CanIf上层模块的)可以选择在配置时的参数CanIf_RXPDU_USERRXINDICATION_ULand参数CanIf_RXPDU_READ_DATA根据上层模块的需求,相应的接收L-SDU所属。
2.4.17 Read Tx/Rx notification status
除了通知回调函数,CanIf提供API服务CanIf_ReadTxNotifStatus()来读取任何发送L-SDU的发送确认状态,并提供API服务CanIf_ReadRxNotifStatus()来读取任何接收L-SDU的接收指示状态。
2.4.18 Data integrity
基本原理:尝试更新上层模块缓冲区以及CanIf内部缓冲区中的数据,必须考虑到在中断服务程序或其他抢占事件的上下文中可能发生的更改。抢占事件可能发生在抢占任务,多个CAN中断,如果多个物理通道,如网关,或在其他外围设备或工作系统中断的情况下,有需要在网络上发送和接收L-PDU。
如果在CanIf模块中调用CanIf_Transmit()、CanIf_TxConfirmation()和CanIf_ReadRxPduData()函数从CanIf controlled内存区域读取数据,CanIf应保证提供的值是最近获得的值。
CanIf_Transmit()、CanIf_TxConfirmation()和CanIf_ReadRxPduData()函数只访问CanIf控制的内存区域的数据,如果CanIf被配置为使用传输缓冲区或接收缓冲区。
发送和接收L-PDU/L-SDU缓冲区的共享处理是实现CanIf的关键问题。因此,CanIf应确保数据完整性,从而使用适当的机制访问共享资源,如传输/接收L-PDU/L-SDU缓冲区。抢占式事件,即来自其他CAN控制器的传输和接收事件,通过写入相同的L-PDU/L-SDU缓冲区,会危及数据的完整性。
CanIf可以使用CanDrv服务来启用(can_enablecontrolllerinterrupts())和禁用(Can_DisableControllerInterrupts())每个CAN控制器的临界区入口和出口的CAN中断及其通知。如果多个CAN控制器有公共资源,则必须Disable整个CAN中断。这些部分不能花很长时间,以防止严重的性能下降。因此,数据的复制、静态变量的改变、计数器和信号量的改变应该在这些关键区域内进行。使用适当的机制来保证数据完整性、中断能力和可重入性取决于实现。
发送请求API CanIf_Transmit()必须能够实现可重入,以允许由不同的L-PDU / L- SDU的不同抢占事件引起的多个发送请求调用。CanDrv的传输请求API Can_Write()也必须是可重入的。
2.4.19 CAN Controller Mode
2.4.19.1 General Functionality
CanIf模块提供服务控制CAN Controllers的模式。提供CanIf_GetControllerMode()获取CAN Controllers的模式,提供CanIf_SetControllerMode()设置CAN Controller模式。
一个CAN网络上所有CAN Controllers的一致性管理是CanSM模块的任务。CanSM负责设置一个网络上的所有CAN Controllers按顺序的进入休眠或者唤醒。
CAN Controller中发生BusOff事件后,调用CanIf模块的CanIf_ControllerBusOff()服务将CanIf模块中缓存的CAN Controller模式设置为CanIf_CS_STOPPED,在CanIf_ControllerBusOff的最后调用CanSM_ControllerBusOff服务通知CanSM模块。
请求CAN Controller模式转化的模块可以在CCMSM(CAN Controller Mode State Machine)的任何状态下请求,CanIf模块调用CanIf_SetControllerMode和CanIf_ControllerBusOff接口接受每一个模式转化请求。
CanSM实现CAN网络相关的状态机,CanIf模块仅仅存储请求模式和执行请求转变。
2.4.19.2 CAN Controller Operation Modes
CanIf将CanSM的操作模式请求翻译成CAN Controller模式转变需要的正确时序。CanIf成功完成CAN Controller模式转变后,通过CanIf_ControllerModeIndicaiton接口改变或者存储CAN Controller的新模式。
ControllerID标识的当前CAN网络如果处于CAN_CS_STOPPED状态,那么通过该网路调用CanIf_Transmit()函数将返回E_NOT_OK,不会调用Can_Write()函数。同时还会清除掉CanIf中的发送缓存。同时还会调用<User_TxConfirmation>(id, E_NOT_OK)同时上层发送数据失败。
BusOff事件发生后CanIf_ControllerBusOff(ControllerID)会被调用,CanIf会调用 CanSM_ControllerBusOff(ControllerID)回调函数通知到CanSM模块。
回调函数CanIf_ControllerModeIndication(ControllerId,ControllerMode) 被调用后, CanIf模块就会调用CanSM模块的回调函数 CanSM_ControllerModeIndication(ControllerId,
ControllerMode) 。
2.4.19.3 Controller Mode Transitions
CAN控制器的状态更改请求API以异步的方式通过回调服务发出异步通知。
根据CAN控制器硬件中转换请求的设置,真实的转换到请求模式是异步发生的,例如,请求睡眠转换CAN_CS_SLEEP。成功更改为例如CAN_CS_SLEEP模式后,CanDrv调用函数CanIf_ControllerModeIndication(), CanIf依次调用函数<User_ControllerModeIndication>()。如果CAN传输非常快,CanIf_ControllerModeIndication()可以在CanIf_SetControllerMode()期间调用。这是具体实现的。
CAN控制器不成功或没有模式转换必须由上层模块跟踪。模式转换CAN_CS_STARTED和CAN_CS_STOPPED的处理方式类似。
CanIf的上层模块可以通过CanIf_GetControllerMode()轮询当前的控制器模式。
不是所有类型的CAN控制器都支持Sleep和Wake-Up模式。这些模式随后由CanDrv封装,通过其接口提供独立于硬件的运行模式,而该接口必须由CanIf管理。
CanIf区分内部发起的CAN控制器唤醒请求(internal request)和网络唤醒请求(external request)。内部请求是通过调用CanIf的函数CanIf_SetControllerMode(ControllerId, CAN_CS_STARTED)发起的,它是一个内部异步请求。外部请求是一个CAN控制器事件,由CanDrv或CanTrcv通知给ECU Integration Code。
2.4.19.4 Wake-up
ECU支持通过CAN网络唤醒,无论使用的唤醒方法(直接关于CAN控制器或CAN收发器),只有当CAN控制器和CAN收发器被设置为某种“听唤醒”模式。这通常是一种睡眠模式,通常的通信被禁用。只有这种模式才能确保CAN控制器停止。因此,可以启用唤醒中断.
2.4.19.4.1 Wake-up detection
如果启用了唤醒支持,CanIf通过集成代码检测到CanIf_CheckWakeup()服务的CAN唤醒。
在CAN总线“wake-up”事件中,在执行EcuM_CheckWakeup(WakeupSource)时,可能会调用CanIf_CheckWakeup(WakeupSource)函数。CanIf反过来通过配置输入引用来检查CanDrvs中的EcuMWakeupSource,这是必须检查的CanDrvs。CanIf通过引用CanIfCtrlCanCtrlRef获取此信息。
被调用的通信服务属于配置期间定义的服务。通过这种方式,EcuM和CanSM能够改变CAN控制器状态,并控制与BusOff恢复或唤醒过程相关的系统行为。
2.4.19.4.2 Wake-up Validation
当CAN控制器/ CAN收发器检测到总线唤醒事件时,将直接通知ECU状态管理器。如果需要验证这样的唤醒事件,EcuM(或CDD)打开对应的 CAN Controller (CanIf_SetControllerMode())和CAN Transceiver (CanIf_SetTrcvMode())。
当“PDU Channel Mode”设置为“CanIf_ONLINE”或“CanIf_TX_OFFLINE”时,CanIf将接收到的消息通知上层模块。因此,如果需要唤醒验证,则不需要将PDU通道模式设置为CanIf_ONLINE或CanIf_TX_OFFLINE。
2.4.20 PDU channel mode control
2.4.20.1 PDU channel groups
每个L-PDU被分配到一个专用的物理CAN通道,这个物理CAN通道连接到一个CAN Controller和一个CAN网络。通过这种方式,以处理单个逻辑L-PDU通道组的方式来控制一个组里面的所有L-PDU。这些逻辑组代表了连接到一个下层网络的所有L-PDU。
2.4.20.2 PDU channel modes
CanIf模块提供CanIf_SetPduMode()和CanIf_GetPduMode()服务来设置和获取CAN通道模式。
只能在某个CAN通道处于CAN_CS_STARTED模式下才能改变通道模式。
CanIf_ONLINE 和CanIf_OFFLINE会影响PDU通道的整个通信过程(收/发),CanIf_TX_OFFLINE 和 CanIf_TX_OFFLINE_ACTIVE模式分别关闭/打开PDU的发生。
每路PDU通道可以在CAN_OFFLINE模式(没有通信),CanIf_TX_OFFLINE(passive mode 能收不能发)模式,CanIf_TX_OFFLINE_ACTIVE(模拟发送但不收),CanIf_ONLINE(完全通信模式)。
2.4.21 Software receive filter
并不是所有的L-PDU都被定义为Receive L-PDU,Receive L-PDU需要从相应的ECU接收,这些L-PDU可以通过硬件接收滤波器,因此在BasicCAN硬件对象中被成功接收。CanIf可选地过滤掉这些L-PDU并禁止进一步的软件处理。
软件过滤机制的方法是从正在处理的HRH和CanId中找出相应的L-PDU。找到L-PDU后,CanIf接受接收到的L-PDU,上层可以直接访问L-SDU信息。
2.4.21.1 Software filtering concept
配置工具会处理硬件接收过滤器设置的相关信息。最重要的设置是L-PDU硬件对象的数量及其范围。出口范围定义了接收L-PDU属于每个硬件接收对象。以下定义是可能的:
-- 接收一个L-PDU(FullCAN recption)
-- 解释一系列的L-PDU,一个或者多个范围的L-PDUs可以映射到硬件接收对象上(BasicCAN Reception)
2.4.22 Data Length Check
接收到的数据长度值与接收到的L-PDU的配置数据长度值进行比较。配置的数据长度值应该从这个L-PDU中使用的字节的大小派生出来。配置的数据长度值不一定是CAN通信矩阵中定义的数据长度值,也不一定是CAN L-PDU的发送方所使用的数据长度值。
CanIf模块应该接收数据长度大于等于配置数据长度的L-PDUs。并将接收的的数据和数据长度信息传递给上层模块。
2.4.23 L-SDU dispatcher to upper layers
在传输端,L-SDU调度器需要找出目标上层模块对应的Tx confirmation回调函数服务。在接收端,每个L-SDU属于一个单一的上层模块。这个关系在配置时被静态分配。CanIf内部L-SDU调度器的任务是找到接收L-SDU的上层模块,并通知上层模块。这些发送确认和接收指示通知服务可能存在几次,在被通知的上层模块中定义了不同的名称。这些通知服务是静态配置的,具体取决于需要服务的上层模块。
3.CanIf模块配置
3.1 CanIf
CanIf模块配置主要配置CanIfCtrlDrvCfg,CanIfDispatchCfg,CanIfInitCfg,CanIfPrivateCfg,CanIfPublicCfg,CanIfTrcvDrvCfg五个Container。
3.2 CanIfCtrlDrvCfg
配置底层CanDrv模块,每个CanDrv模块都有单独的配置。
CanIfCtrlDrvInitHohConfigRef : 引用CanIfInitHohCfg配置
CanIfCtrlDrvNameRef: 引用CanGeneral
CanIfCtrlID: 该参数从CAN驱动中抽象出特定的参数Controller。所有连接的CanDrv模块的每个控制器都应分配给CanIf的一个特定的ControllerId。CanIfCtrlWakeupSupport: 该参数定义是否可以查询被引用的CAN驱动模块的相应控制器的唤醒事件。
CanIfCtrlCanCtrlRef: 该参数引用CAN接口模块提供的CAN驱动模块的底层CAN控制器的逻辑句柄。
CanIfCtrlTrcvRef: 该参数引用底层CanTrcv模块的逻辑句柄。
3.3 CanIfDispatchCfg
配置上层模块提供的回调函数。
3.4 CanIfPrivateCfg
这个容器包含CAN接口的私有配置(参数)。
CanIfFixedBuffer:该参数定义缓冲区元素长度是否固定为8字节,也就是说当接收到的PDU数据长度小于8字节的时候,会修正为8字节长度。
CanIfPrivateDataLengthCheck : 是否支持数据长度的检查。
CanIfPrivateSoftwareFilterType: 自定义的软件滤波方法的枚举定义。
CanIfSupportTTCAN: 是否支持TTCAN。
3.5 CanIfPublicCfg
这个容器包含CAN接口的公共配置(参数)
3.6 CanIfTrcvDrvCfg
配置底层CanTrcv模块,每个CanTrcv模块都有单独的配置。
CanIfTrcvId: CanTrcv的枚举定义
CanIfTrcvWakeupSupport : 该参数定义是否可以查询被引用的CAN transceiver Driver模块的各个收发器的唤醒事件
CanIfTrcvCanTrcvRef: 该参数引用来自CAN接口模块提供的CAN收发器驱动模块的底层CAN收发器的逻辑句柄。
3.7 CanIfInitCfg
该容器包含CAN接口的初始化参数。
CanIfMaxBufferSize: Tx Buffer的最大值。
CanIfMaxRxPduCfg: 接收PDU的最大值
CanIfMaxTxPduCfg: 发送PDU的最大值
3.7.1 CanIfBufferCfg
配置TxBuffer,有多少个Tx PDU就需要配置多少个内容。
CanIfBufferSize: 一个TxBuffer能缓存多少个接收到的L_PDU,如果配置为0,就是不使用缓存。
CanIfBufferHthRef: 引用的HTH(硬件发送句柄)。
3.7.2 CanIfInitHohCfg
此容器包含对每个底层CAN驱动程序的配置设置的引用。
CanIfHrhCfg: CAN接收句柄的配置
CanIfHrhCanCtrldRed: Can Controller的引用,也就是ControllerID
CanIfHrhIdSymRef: 硬件接收邮箱的引用,在CanDrv里面定义的
CanIfHtrCfg: CAN发送句柄的配置
CanIfHthCanCtrldRed: Can Controller的引用,也就是ControllerID
CanIfHthIdSymRef: 硬件发送邮箱的引用,在CanDrv里面定义的
3.7.3 CanIfRxPduCfg
所有接收L-PDU的配置
CanIfRxPduCanID: 接收PDU信号ID
CanIfRxPduCanIdMask: 接收信号的掩码,一般不用
CanIfRxPduCanIdType: CAN FD还是普通CAN,标准帧还是扩展帧
CanIfRxPduDlc: 帧长度
CanIfRxPduReadData: 接收到的L-PDU中的L-SDU是否可以被读取
CanIfRxPduReadNotifyStatus: 接收数据后是否产生回调通知
CaIfRxPduType: Static还是Dynamic
CanIfRxPduUserRxIndicationName: 接收到数据后最后调用上层模块的回调函数
CanIfRxPduUserRxIndicationUL: 该PDU的上层接收模块
CanIfRxPduHrhIdRef: 该PDU隶属的HRH引用
CanIfRxPduRef: 全局PDU的引用,也就是DBC文件被解析后定义的全局PDU的引用
3.7.4 CanIfTxPduCfg
发送PDU的配置,配置参数基本和接收PDU类似。、
4.CanIf模块代码分析
Can Drive/Transceiver底层发生某个事件后,调用CanIf模块的回调函数通知到CanIf层,而回调函数本身又会调用上层使用CanIf模块的回调函数。
4.1 发送回调
CanIf_TxConfirmation函数是CanDrive发送完数据后调用的。
CanIf_TxConfirmation函数就会更加Can ID查找到当前ID所在的CAN通道,然后获取该通道的模式(OffLine/Online)后,调用该CAN ID配置好的上层的回调函数(CanTp_TxIndication/CanNm_TxIndication/Pdur_TxIndication)。
4.2 接收回调
CanIf_RxIndication函数是CanDrive收到完数据后调用的
CanIf_RxIndication最重要的功能就是根据接收到CAN ID查找到该报文属于哪类报文(Can_NM, Can_Tp, Can_Pdur等)调用配置好的上层回调函数。
4.3 BusOff事件回调
CanIf_ControllerBusOff函数是CanDrive发生BusOff故障后调用的。
最后调用CanSM模块的Bus Off函数,由CanSM模块同一管理Can BusOff状态。
4.4 模式切换回调
CanIf_TrcvModeIndication函数是CanTrcv模块发生模式切换后调用。
CanIf_TrcvModeIndication通知CanSM模块发送模式切换。
CanIf_ControllerModeIndication函数是Can Controller模块发生模式切换后调用。
4.5 Can模块接口函数分析
CanIf_Init接口由EcuM模块调用,Ecu上电后初始化CanIf模块(初始化用户配置项)。
模式设置相关的接口(Can Controller mode, Can Tranceiver mode)由Can SM模块调用,统一管理Can module。
CanIf_Transmit接口由需要发送Can报文的模块调用(OsekNm, CanTp, CanPdur)。