【CP AUTOSAR】Can(CANDriver)分析和使用


前言

本文介绍CP AUTOSAR 架构下的Can组件,基于S32K312芯片、Vector提供的CBD包,使用DaVinci Configurator工具进行配置的经验。
Can组件位于Communication Drivers层,是AUTOSAR CAN STACK架构里位于最底层的驱动代码,被上层组件CanIf所调用。
Can组件实现MCU的CAN数据收发、模式控制。
本文不具体介绍MCU的CAN寄存器和CAN总线技术。

在这里插入图片描述在这里插入图片描述

上图为CP AUTOSAR CAN网络架构。


一、概念

(一)、CAN网络架构

根据OSI参考模型,即定义了网络的七层架构,包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,AUTOSAR的CAN网络也参考了这个模型。
其中CAN网络的物理层、数据链路层遵循ISO11898或ISO11519规范,Can组件就是用来实现其功能之一。

(二)、L-SDU、L-PDU、L-PDU Handle

在这里插入图片描述
L-SDU:CAN数据链路层的Service Data Unit,是L-PDU中的一个字段。
L-PDU:CAN数据链路层的Protocol Data Unit,由Identifier、DLC、L-SDU组成。
L-PDU Handle:L-PDU句柄,由CanIf定义并提供。
L-PDU的数据类型定义如下:
在这里插入图片描述

(三)、Mailbox

CAN邮箱,用来发送接收CAN消息的数据结构,也就是MCU里CAN控制器有一段专门接收发送CAN消息的RAM区,其数据结构是可配置的,数据结构包含CAN的ID、DLC等。
CAN的HardwareObject属性就包含Mailbox。
下图是S32K3MCU的CAN邮箱数据结构图:
在这里插入图片描述

(四)、CanHardwareUnit、CanController、CanTransceiver、PhysicalChannel

PhysicalChannel:
CAN物理通道,表示CAN网络的接口,MCU的某一组CAN TX和RX引脚和一片CanTransceiver相连,CanTransceiver再和总线上的CANH和CANL相连,那这一段便是CAN驱动的物理通道。
CAN硬件单元的不同物理通道可以访问不同的CAN网络。

CanTransceiver:
CAN收发器,通常为一个芯片,MCU的CAN TX和RX输入输出信号为数字信号,需要CAN收发器经过电平转换传输到CAN总线上。

CanController:
CAN控制器,一个CAN控制器连接一个CAN物理通道,一般MCU里有若干个CAN控制器,每个控制器有一组CANTX和RX,实现数据的收发、波特率、波特率、采样点设置等。

CanHardwareUnit:
CAN硬件单元,硬件单元由一个或多个CAN控制器和一个或多个CAN邮箱组成,可以是片上的也可以是外部设备,由一个Can驱动程序表示,比如车上某一段CAN网络里某一个连接的ECU便是一个CAN硬件单元。

示例架构如下:
在这里插入图片描述控制器通过TX引脚把数据发给收发器,收发器再通过电平转换发到CAN总线,其他节点如果有数据发过来先经过收发器电平转换通过Rx发给控制器,控制器再回复ACK。

(五)、Remote Frames

Can驱动不支持远程帧。

二、HardwareObject、HOH、HTH、HRH的关系

HardwareObject:
Can驱动程序的硬件对象,定义为CAN硬件单元内的CAN RAM的消息缓冲区,比如一个硬件对象里属性包括了该对象属于哪个控制器、有哪个CAN邮箱、帧类型、过滤器、是接收还是发送、是BASIC还是FULL等。

HOH:
Hardware Object Handle,硬件对象句柄由Can驱动程序定义和提供,一个HOH代表一个硬件对象。

HTH:
Hardware Transmit Handle,硬件传输句柄由Can驱动程序定义和提供,一个HTH能代表一个或多个发送类别的硬件对象。

HRH:
Hardware Receive Handle,硬件接收句柄由Can驱动程序定义和提供,一个HRH能代表一个或多个接收类别的硬件对象。

可以理解为CanDriver里定义了若干个HOH,HOH分为HTH或HRH,也就是这个HOH属于发送类型的硬件对象还是接收类型的硬件对象。一个HOH拥有一个CAN邮箱或多个CAN邮箱,这个怎么理解呢,根据AUTOSAR文档上的描述:
在这里插入图片描述
HOH只有一个硬件缓冲区情况下,数据的发送接收可能都会丢帧,那就需要再加几个硬件缓冲区也就是邮箱,根据CanHwObjectCount来定义大小,有些Can控制器支持多个邮箱下用FIFO的形式来接收数据,但有些Can控制器没有怎么办,比如S32K3用CANFD的情况下只有FLEXCAN0通道才支持RX FIFO,那这时候MCU不支持配置FIFO,就需要手动添加额外的邮箱,比如CanDriver配置了一路HOH,为这一路分配了两个邮箱,软件里判断当第一个邮箱缓冲区被占用时,使用其他邮箱来工作。

HOH、HRH、HTH的映射关系假设如下:

在这里插入图片描述

CanDriver里定义了四个HOH,有三个是HTH,一个是HRH,CanIf里定义四个TX PDU,两个RX PDU。
当CanIf层调用CanIf_Transmit()时,入参CanTxPduId便是1-4的TX PDU,那么CanIf_Transmit()便会根据自己的TX PDU表选择对应的HTH使能Can_Write()来发送。
当CAN收到数据时,中断程序或者轮询程序知道当前是哪个邮箱收到数据后比如是图上第四个HOH便调用并且输入给CanIf_RxIndication(),CanIf层便知道是第四个HOH收来的数据并且根据邮箱里的ID与PDU配置表里哪个RX PDU符合。
从图上看出第三个和第四个HOH承载了两个PDU,假如只有一个CAN邮箱的情况下很容易出现丢帧,所以需要配置FIFO或者使用多个邮箱。
根据AUTOSAR文档上的描述,使用多个邮箱的情况下,另一个邮箱被称为shadow buffer,影子邮箱需要跟主邮箱配置的一样。

三、接收数据的一致性

当某个HRH没使用RX FIFO并且主邮箱里已经接收到数据没读出时,可以使用其他邮箱来支持这个HRH进行接收,其他的邮箱称为shadow buffer。影子邮箱相当于多了几个数据接收缓冲区。
如果某个HRH有影子邮箱,当第一个邮箱正在忙被锁定,也就是上层组件还在处理第一个邮箱的数据,那么这时候该HRH接收到数据应该把L-SDU复制到影子邮箱里。
如果CAN控制器不支持在邮箱里收到数据时进入保护锁定(硬件特性)并且只有一个邮箱,那么这时候马上来一帧新的数据就会把当前邮箱里的数据覆盖,如果硬件支持的话这时候就会触发overwrite事件,反之,如果支持进行保护锁定,那这时候马上来一帧新的数据就会不能存进邮箱里,如果硬件支持的话这时候就会触发overrun,有overwrite或overrun事件后Can组件应该返回CAN_E_DATALOST错误,所以软件里在邮箱有数据后应该马上读出来。

四、优先级反转

如果CAN软件里发送邮箱只有一个的话,就会容易出现优先级反转的现象,即优先级低的CAN报文比优先级高的CAN报文先发出去了。CAN总线根据CANID来区分优先级,即两个CAN通道的报文同一时刻需要发送时,CANID小的报文竞争出发到总线上。

(一)、内部优先级反转:

在这里插入图片描述
由于只有一个发送缓冲区并且总线上优先级高的报文较高,当MCU里发送优先级较低的报文时,因为总线在忙,优先级低的报文就在等总线空闲下来,那MCU后面想发的优先级高的报文由于只有一个邮箱的原因便不能发了,只能等前面优先级低的报文先发出去。

(二)、外部优先级反转:

在这里插入图片描述
某个CAN节点要连续发送两个高优先级时,因为帧间隔大于CAN标准定义的最小间隔,在第一个优先级高的帧发出后第二个优先级高的帧晚于另一个节点优先级低的帧发出去,总线上就出现优先级反转帧的现象了。

(三)、多路复用传输:

通过给一个HTH添加两个或以上邮箱也就是影子邮箱就能解决优先级反转的问题了,当CanIf层的两个PDU使用的都是同一路HTH用来发送时,第一个低优先级的PDU因为总线忙的缘故,还在邮箱里待发出,这时候第二个高优先级的PDU也需要发送,因为第一个邮箱已满便使用第二个邮箱装入数据,等总线空闲后,第二个邮箱的PDU因为优先级高的缘故就先发到总线上。
注意TX FIFO不能防止优先级反转的问题,FIFO是先入先出的顺序数据结构,必须要第一个数据发完后面的数据才能发出去,在配置时需要注意。

五、BasicCan、FullCan

Can驱动支持把HOH配成BasicCan或FullCan。

BasicCan:
能接收一段范围的L-PDU。

FullCan:
能接收特定的一个L-PDU。

也就是说通过CAN控制器里的过滤器为每个邮箱进行配置报文ID过滤,如果该HOH有影子邮箱的话,过滤器和主邮箱是一样的。
另外只有BasicCan才能使用多个邮箱或FIFO。

当HOH的CanHwObjectCount大于1时,那么这个HOH的数据结构要么是FIFO,要么是Multiple CAN,所以HOH基本有以下几种类型数据结构:
Tx FiFo Basic Can、Tx Multiplexed Basic Can、Tx Full Can、Rx FiFo Basic Can、Rx Multiplexed Basic Can、Rx Full Can,每个数据结构使用的邮箱数量都不一样,具体要参考Vector的技术手册。

因为带CAN的ECU基本有应用报文、诊断报文、网络报文、标定报文。应用报文因为是特定的ID并且如果数量不多的情况下使用FullCan来配置,标定报文也是特定的ID一般使用FullCan来配置,网络报文因为是一段范围内的报文一般使用BasicCan来配置,诊断报文如果在邮箱够用的情况下使用FullCan否则用BasicCan,还要诊断报文因为是事件帧也比较重要,所以还需要增加FIFO或MultiplexedCan以防丢失。

邮箱对CANID的处理顺序,根据VECTOR里的文档描述如下:
在这里插入图片描述
在这里插入图片描述

六、Can控制器状态机

(一)、CAN驱动状态机:

在这里插入图片描述
Can驱动没初始化前状态为UNINT状态,初始化后进入READY状态,只有在READY状态才能收发。

(二)、CAN控制器状态机:

在这里插入图片描述
UNINT:
CAN控制器未初始化,CAN寄存器也处于初始状态,CAN控制器不能参与总线的数据收发。

STOPPED:
CAN控制器已经初始化,但不参与总线,也就是说不会数据收发也不会发错误帧和ACK。

STARTED:
CAN控制器已经有完整功能,参与总线的数据收发。

SLEEP:
CAN控制器处于睡眠状态,睡眠状态下不参与总线的数据收发,允许被事件唤醒,唤醒后功能正常。
一般MCU的CAN控制器支持进入睡眠模式,如果CAN控制器不支持睡眠模式,那么软件应该模拟CAN控制器进入睡眠模式。

1、由Can_Init()触发的状态转换
如果程序还没调用过Can_Init(),那么CAN驱动处于UNINT状态,调用Can_Init()后转换为STOPPED,如果再调用Can_Init()便返回错误。

2、由Can_DeInit()触发的状态转换
程序要先在CAN_READY状态才能调用Can_DeInit()否则返回错误,调用后CAN控制器状态切为UNINT。

3、由Can_SetBaudrate()触发的状态转换
调用Can_SetBaudrate()不会状态进行切换。

4、由Can_SetControllerMode()触发的状态转换

Can_SetControllerMode(CAN_CS_STARTED):
当前状态机需要处于STOPPED状态,否则返回无效,当执行后设置硬件寄存器,CAN控制器参与总线数据收发,状态机切换为STARTED状态,如果控制器处于SLEEP状态则需要先WAKEUP再STARTED。

Can_SetControllerMode(CAN_CS_STOPPED):
当前状态机在STARTED或SLEEP时切换为STOPPED,CAN控制器不参与总线数据收发并且停止,如果有有处于缓冲区还未发出的消息被删除不发。

Can_SetControllerMode(CAN_CS_SLEEP):
如果当前CAN控制器在工作中,需要先使控制器处于STOPPED状态,再使处于SLEEP模式。

5、由硬件唤醒触发的状态转换
当控制器处于SLEEP并且CAN寄存器支持休眠唤醒,这时CAN总线传入报文,CAN控制器状态机应由SLEEP切换为STOPPED,并且CAN驱动程序应在中断或轮询里的调用EcuM_CheckWakeup()通知EcuM组件,EcuM再调用Can_CheckWakeup(),如果有唤醒事件,Can_CheckWakeup()里再调用EcuM_SetWakeupEvent()通知EcuM组件有CAN的唤醒事件。

6、由BUSOFF触发的状态转换
当CAN控制器触发BUSOFF时,如果有需要的话应将控制器从STARTED切换为STOPPED,并调用CanIf_ControllerBusOff()通知CanIf层。

一般Can组件的Can_SetControllerMode()的入参还支持WAKEUP,即CanIf调用时发现当前控制器处于SLEEP模式,但是想要STARTED或者STOPPED,那这时候需要先让控制器进行唤醒操作再切换为STARTED或者STOPPED,当入参为WAKEUP时强制将CAN控制器从睡眠中唤醒。

CAN控制器在切换时可能不会立即切换,需要在Can_MainFunction_Mode()里继续执行模式切换,那这个最大超时时间CanTimeOutDuration决定。

七、轮询模式、中断模式

Can驱动里邮箱的数据发送或者接收完成后要调用CanIf_TxConfirmation()或者CanIf_RxIndication()去通知CanIf组件,中断模式的优点是能快速读取邮箱里的数据并通知上层,缺点是如果接收的报文太多,中断的上下文切换占用CPU太多时间,轮询模式的优点是周期性处理邮箱的数据不会因频繁中断占用CPU,缺点是因为周期性处理如果频率太低比如10MS、5MS容易对引发丢帧。

八、标准帧、扩展帧、CANFD

Can驱动支持接收标准帧和扩展帧,但是通过CanIf_RxIndication()传给CanIf的时候CanIf不知道当前的PDU是标准帧还是扩展帧,那么Can驱动会将CANID的最高位置为1代表是扩展帧,例如0x500是扩展帧的话变为0x80000500。
Can_Write()里会判断传进去的CANID是否有FD_MASK标志,有的话发送的报文为CANFD否则为常规CAN然后把HTH设置成对应的格式,代码里对CANID的判断如下:

	#if defined(C_ENABLE_MIXED_ID) || defined(C_ENABLE_EXTENDED_ID)
	# define CAN_ID_IDE_MASK            0x80000000UL
	# define CAN_ID_FD_MASK             0x40000000UL 
	# define CAN_ID_MASK                0x1FFFFFFFUL
	# define CAN_ID_MASK_STD            0x000007FFUL
	# define CAN_ID_UNUSED_MASK         0x20000000UL
	#else
	# define CAN_ID_IDE_MASK            0x0000U
	# define CAN_ID_FD_MASK             0x4000U 
	# define CAN_ID_MASK                0x07FFU
	# define CAN_ID_MASK_STD            0x07FFU
	# define CAN_ID_UNUSED_MASK         0x3800U
	#endif
	
	#define CanHL_IsFdMessage(id) \
	(((id) & (Can_IdType)CAN_ID_FD_MASK) == (Can_IdType)CAN_ID_FD_MASK)

常规CAN和CANFD的DLC区别如下:
在这里插入图片描述

九、主要代码描述

(一)、void Can_Init( Can_ConfigPtrType ConfigPtr )
初始化CAN驱动程序,初始化成功后控制器状态机变为STOPPED。

(二)、Std_ReturnType Can_SetBaudrate(uint8 Controller, uint16 BaudRateConfigID)
配置CAN控制器的波特率,配置完后需要重新调用Can_Init()进行初始化。

(三)、Std_ReturnType Can_SetControllerMode(uint8 Controller, Can_ControllerStateType Transition)
对CAN控制器进行模式切换,每次成功完成状态切换,则调用CanIf_ControllerModeIndication()通知CanIf。

(四)、Std_ReturnType Can_Write(Can_HwHandleType Hth, const Can_PduType* PduInfo)
发送CAN数据。
Hth:HTH的索引。

(五)、void Can_MainFunction_Write(void)、void Can_MainFunction_Read(void)、void Can_MainFunction_BusOff(void)、void Can_MainFunction_Wakeup(void)、void Can_MainFunction_Mode(void)
如果CAN驱动设置为轮询模式,则这些轮询函数需要周期调用处理CAN的发送、接收、BUSOFF处理、唤醒事件、模式切换,如果为中断模式则不需要调用,CAN的发送、接收、BUSOFF处理、唤醒事件则在中断里通知CanIf层。

CAN的数据读取需要在CanIf组件里调用,Can驱动里知道邮箱里有数据后通知CanIf组件去读取。

十、DaVinci Configurator主要配置

(一)、CAN的常规设置

在这里插入图片描述在这里插入图片描述
在这里插入图片描述FD Support:
NONE:CANFD失效。
BRS:激活比特率切换但最大DLC只有8个。
FULL:激活比特率切换但最大DLC只有64个。

Hardware Loop Check:
允许等待CAN寄存器执行完成有个超时时间,而不是死循环等寄存器跑完。

Hardware transmit FIFO support:
使能HTH的硬件FIFO功能,如果CAN控制器支持发送FIFO功能的话。

Interrupt Category:
如果有用到AutoSar OS或OSEK OS必须用Category2。

LPdu Receive Callout Function:
添加后当接收到L-PDU时调用用户自定义的Callout,用户可在里面添加自己需要的程序。

Multiple Basic CAN Objects:
允许使用多个邮箱和过滤器支持HRH的接收功能。

Multiple Basic CAN Tx Objects:
允许使用多个邮箱和过滤器支持HTH的发送功能。

Multiplexed Transmission:
指定多路复用传输功能是否打开。

Rx Basic CAN Support:
定义Rx Basic CAN是否使用。

Rx Full CAN Support:
定义Rx Full CAN是否使用。

Rx Queue:
定义Rx是否开启队列,处于队列的数据将在Can_MainFunction_Read()里执行调用CanIf_RxIndication()。

Rx Queue Size:
定义Rx队列的大小。

Tx Full CAN Support:
定义是否支持Tx Full CAN。

Timeout Duration:
等待CAN寄存器执行完成的超时时间,比如执行Can_SetControllerMode()后等到CAN控制器模式切换完成最大的超时时间。

(二)、CAN的硬件对象设置

在这里插入图片描述Controller Ref:定于该硬件对象属于哪个CAN控制器。
Fd Padding Value:用于填充CANFD中未指定数据的值。
Handle Type:用于指定该HOH是Basic CAN还是Full CAN。
Hardware FIFO:使能该HOH支持硬件传输FIFO,如果CAN控制器支持的话。
Id Type:CAN ID类型,是标准帧还是扩展帧。
Id Value:CAN ID的值,如果该HOH是Full Can,需要填写,如果是Basic Can则不需要填写。
Multiplexed Transmission:使能该HOH为多路复用传输。
Object Hw Handle:该HOH第一个硬件对象的索引,也就是邮箱的索引。
Object Hw Size:该HOH拥有的硬件对象数量,也就是邮箱数量,当数量大于1,那么该HOH就是FIFO或者Multiplexed类型的。
Object Id:该HOH的索引。
Object Type:该HOH是HTH还是HRH。
Filter Mask Ref:该HOH的过滤器拥有哪个,如果该HOH是Multiplexed类型并且数量大于1,那么用的过滤器都是一样的。如果该HOH是Full Can或者HTH,那么过滤器不需要添加。

(三)、过滤器配置

在这里插入图片描述该项是CAN邮箱的过滤器配置,公式是接收ID = Filter Code Value & Filter Mask Value,Mask的1表示关心,0表示不关心。
假设某个HRH需要接收0x638、0x666报文,0x638的二进制是0000 0110 0011 1000b,0x666的二进制是0000 0110 0110 0110b,两个相同的bit,Mask写1否则为0,那么Mask值应该为1111 1111 1010 0001,那么预期值写0000 0110 0010 0000,所以Filter Code Value的值为0x620,Filter Mask Value的值为0xFFA1。

(四)、控制器、时钟、采样点配置

在这里插入图片描述Cpu Clock Ref:CAN控制器的时钟源,来自Mcu组件里设置的时钟参考点。
Rx Processing、Tx Processing、Wakeup Processing、Busoff Processing:选择中断模式还是轮询模式。
在这里插入图片描述Baudrate Clock:时钟源频率,来自之前Mcu组件给的时钟参考点Cpu Clock Ref。
Baudrate Prescaler:时钟分频点。
Controller Baud Rate[kbps]:该CAN控制器的波特率。
Controller Prop Seg:位时间里的Propagation Segment字段的值。
Controller Seg1:位时间里的Phase Buffer Segment1字段的值。
Controller Seg2:位时间里的Phase Buffer Segment2字段的值。
Controller Sync Jump Width:SJW的值,SJW是用来实现位时间同步功能的,一般是主机厂提供该值。

1、波特率的计算
该组件波特率的计算如下:
CAN控制器波特率 = Baudrate Clock / Baudrate Prescaler / (Controller Prop Seg + Controller Seg1 + Controller Seg2 + 1) 。
如上图,500 = 30000 / 2 / (17 + 6 + 6 + 1)。

2、采样率的计算
一个位时间的组成如下:
在这里插入图片描述
由图上可知采样点 = (SYNC_SEG + PROP_SEG + PHASE_SEG1)/ (SYNC_SEG + PROP_SEG + PHASE_SEG1 + PHASE_SEG2)。
SYNC_SEG等于1,所以从配置来看(1 + 17 + 6)/ (1 + 17 + 6 + 6)= 80%采用率

十一、使用范例

(一)、初始化完后执行写操作

	/**忽略其他配置**/
	Can_PduType Pdu;
	uint8 sdu[8u] = {1,2,3,4,5,6,7,8};
	
	Pdu.id = 0x10u;
	Pdu.swPduHandle = 0x00u;/*要与Can_Write()入参的HTH相同*/
	Pdu.length = 0x08u;
	Pdu.sdu = sdu;
	
	Can_Init(Can_Config_Ptr);
	Can_SetControllerMode(0u,CAN_T_START);
	Can_Write(0u, &Pdu);

十二、工程经验

(一)、接收TP帧时帧顺序错乱
某次使用CAN接收某一个节点发的多帧时,那个节点使用五个不同ID的报文组成TP帧,帧间隔为1MS,比如ID1、ID2、ID3。ID4、ID5依次每隔1MS顺序发给我的ECU,CAN驱动都是有接收到,但是传到COM层时,从COM层读出来的顺序确是ID1、ID3、ID2、ID4、ID5,猜测是因为该HRH用了多个邮箱接收,当收到ID1时放到第一个邮箱传到COM层,再通过RTE通知该ID1更新了,但ID2确放到了第二个邮箱,让ID3又放到了第一个邮箱,但永远是第一个邮箱的数据先读出来通知到COM层所以顺序反过来了,但这还没真正去查原因只是猜测,所以应用层不能判断多帧的帧顺序不对后就抛弃多帧,要等全部收到多帧后再自己根据帧顺序进行组包。

十三、参考资料

AUTOSAR_SWS_CANDriver.pdf
AUTOSAR_SRS_CAN.pdf
AUTOSAR_EXP_LayeredSoftwareArchitecture.pdf
TechnicalReference_Asr_Can_Arm32_FLEXCAN3
RTD_CAN_IM.pdf
RTD_CAN_UM.pdf
ISO7498
ISO11898
ISO11519
ISO15765
ISO14229
can2.0.pdf
can_fd_spec.pdf


总结

本文没有描述CAN驱动的时间戳功能,另外本文文字描述多点,更像是本人的使用笔记,仅供参考,如有不对地方欢迎指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值