目录
一、Autosar CAN通讯
1.CAN通讯协议
提及通讯协议,往往需要理清三点内容:物理链路、时序、数据有效性
a.CAN通讯物理链路
CAN总线上传递的信号为差分信号通常为±12V,可靠性高实时性高支持多节点通讯,广泛应用于汽车各个ECU之间通讯。西区图为CAN总线物理架构,其中每个节点都可以视为一个控制器,CAN收发器作用在于将MCU输出的5V信号转换为12V的查封信号,部分收发器还支持低功耗、网络唤醒功能。120欧电阻为阻抗匹配电阻,可以保证信号质量。
b.CAN通讯时序
由于CAN通讯协议涉及内容较多,本文只对CAN通讯协议结构介绍,以下文章管can通讯协议描述较为详细,可以参考:
[CAN总线通讯详解](https://zhuanlan.zhihu.com/p/162708070)
- CAN帧类型
数据帧:
can_id:标识符(11位标准或29位扩展,通过CAN_EFF_FLAG标记扩展帧)。
can_dlc:数据长度码(0-8),实际数据存储在data数组中。
远程帧:
使用CAN_RTR_FLAG标志位指示远程请求,can_dlc指定期望返回的数据长度。
CAN FD帧:
len:数据长度(0-64字节)。
flags:标志位如CANFD_BRS(速率切换)、CANFD_ESI(错误状态指示)。
错误处理:
错误帧通过CAN_ERR_FLAG识别,具体错误类型由can_id & CAN_ERR_MASK解析
- 标准CAN通讯时序
- CANFD时序
c.数据有效性
数据有效性即在总线上读取店铺状态确定每个数据位是否有效,CAN总线中,每个位(Bit)被划分为 4个时间段,用于确保节点间的时钟同步和采样点准确性。其组成如下:
时间段 | 符号 | 作用 |
---|---|---|
同步段(Sync Seg) | SYNC | 用于节点间的时钟同步,固定为1个时间单位(Tq)。 |
传播段(Prop Seg) | PROP | 补偿总线物理延迟(信号传输时间 + 收发器延迟),可配置(1~8 Tq)。 |
相位缓冲段1 | PH1 | 调整采样点位置,补偿节点间时钟偏差(1~8 Tq)。 |
相位缓冲段2 | PH2 | 同上,用于位时序的二次调整(1~8 Tq)。 |
采样点的确定:
定义:采样点位于 PH1段结束 的位置,是节点读取总线电平的时刻。
推荐值:
低速CAN(<125kbps):75%~80%
高速CAN(≥125kbps):75%~87.5%
采样点常见问题处理
2.Autosar中CAN通讯链路
a.Autosar架构中CAN通讯数据流
- Autosar通讯模块
在Autosar架构中关于CAN通讯定义了不同功能的服务:XCP标定功能,DEM\DCM诊断功能,网络管理功能,基本信号交互功能等。本文着重与解释基本信号交互功能的完整链路
- CAN通讯矩阵
在汽车电子行业,当需要开发一款ECU用于控制汽车上的某个部件时,你所定义的ECU对于整个汽车电子架构来说就建立了一个通讯节点,往往需要接收一些信息也需要往外部传递一些信息。对于开发来说,CAN通讯矩阵表就是用于约定节点间交互数据具体内容以及实际意义的文件,对于Autosar工具链来说,交互文件格式为DBC。下图为CAN通讯矩阵表的示例:
其中,一帧标准CAN报文数据场长度为8bytes,可以定义其中某几个bit用于表示具有实际意义的信号,例如车速信号占用2bytes,并定义起始位以及数据存储格式为Motorrola_LSB格式,这样对于一帧CAN来说一个信号就完全定义了。
- 数据协议单元 PDU(Protocol Data Unit)
Autoasr架构中定义了三种PDU分别为:L-PDU、N-PDU、I-PDU,用于不同协议层间传输数据。-
L-PDU (Link Layer PDU)
层级:数据链路层(对应CAN帧)。
功能:直接与物理总线交互,包含CAN标识符、数据长度(DLC)和实际数据。
内容:标准CAN帧结构,如CAN ID + DLC + 8字节数据。 -
N-PDU (Network Layer PDU)
层级:网络层(如ISO-TP协议)。
功能:处理长数据的分段与重组(如传输超过8字节的数据)。
内容:包含协议控制信息(PCI)和部分数据,如首帧、流控制帧、连续帧等。 -
I-PDU (Interaction Layer PDU)
层级:交互层(如AUTOSAR COM模块)。
功能:将多个应用信号(如车速、温度)打包成一个PDU,或从PDU解包信号。
内容:由应用层定义的信号集合,映射到CAN数据字段。
-
上图解释了PDU在autosar架构中的具体应用,数据流转由上至下会经过以下模块:
-
应用层(Application Layer)
生成需要发送的应用信号(如车速、温度)。
通过RTE(Runtime Environment)调用COM模块接口传递信号。 -
COM模块(Communication Module)
信号组包:将多个应用信号组合成I-PDU(Interaction Layer PDU)。
信号路由:根据配置将I-PDU路由到目标总线(如CAN、LIN)。
通信管理:处理通信超时、信号过滤、模式控制(正常/静默模式)。 -
PDUR模块(PDU Router)
协议数据单元路由:将COM模块的I-PDU转发至底层通信模块(如CanIf)。
支持多总线协议(CAN、LIN、FlexRay)的路由决策。 -
CanIf模块(CAN Interface)
统一接口抽象:为上层模块(COM/PDUR)提供硬件无关的CAN通信接口。
缓冲区管理:维护发送/接收缓冲区,处理硬件中断事件。 -
CanDrv模块(CAN Driver)
硬件驱动:直接操作CAN控制器,配置邮箱(Mailbox)、波特率、中断。
数据帧传输:将CanIf传递的L-PDU(Link Layer PDU)写入CAN控制器发送邮箱。 -
CAN控制器硬件(CAN收发器)
执行物理层通信,将数据帧转换为差分信号(CAN_H/CAN_L)并发送到总线。
为了更为具体的展示不同PDU的流转过程,以下为一个示例:
C语言示例
- 定义各层级PDU结构体
// L-PDU:对应CAN帧结构
typedef struct {
uint32_t id; // CAN标识符(11或29位)
uint8_t dlc; // 数据长度码(0-8)
uint8_t data[8]; // 数据域
} L_PDU;
// N-PDU:网络层协议数据单元(ISO-TP示例)
typedef struct {
uint8_t pci; // 协议控制信息(帧类型 + 序列号)
uint8_t data[7]; // 数据部分(实际长度根据帧类型变化)
} N_PDU;
// I-PDU:交互层协议数据单元(信号集合)
typedef struct {
uint16_t speed; // 信号1:车速(单位:km/h)
uint8_t temp; // 信号2:温度(单位:℃)
} I_PDU;
- 发送端处理流程
// 应用层生成信号并打包为I-PDU
void AppLayer_SendData(uint16_t speed, uint8_t temp) {
I_PDU ipdu;
ipdu.speed = speed;
ipdu.temp = temp;
// 将I-PDU传递给通信层
ComLayer_PackIPDU(&ipdu);
}
// 通信层:将I-PDU拆分为N-PDU(假设需要分帧)
void ComLayer_PackIPDU(I_PDU *ipdu) {
N_PDU npdu[2];
// 拆分示例:假设I-PDU数据需分为两个N-PDU
npdu[0].pci = 0x10; // 首帧,PCI高4位为1,低4位为数据长度
memcpy(npdu[0].data, &ipdu->speed, 2); // 写入车速(2字节)
memcpy(npdu[0].data + 2, &ipdu->temp, 1); // 写入温度(1字节)
npdu[1].pci = 0x21; // 连续帧,序列号为1
// 填充剩余数据(此处仅为示例,实际可能更复杂)
// 传递N-PDU到网络层
NetworkLayer_Send(npdu, 2);
}
// 网络层:将N-PDU封装为L-PDU(CAN帧)并发送
void NetworkLayer_Send(N_PDU *npdu, int count) {
for (int i = 0; i < count; i++) {
L_PDU lpdu;
lpdu.id = 0x123; // 示例CAN ID
lpdu.dlc = 8; // 数据长度固定8字节
// 将N-PDU的PCI和数据拷贝到CAN数据域
lpdu.data[0] = npdu[i].pci;
memcpy(lpdu.data + 1, npdu[i].data, 7);
// 调用CAN驱动发送L-PDU
CanDriver_Send(&lpdu);
}
}
- 接收端处理流程
// CAN驱动接收L-PDU后传递给网络层
void CanDriver_ReceiveCallback(L_PDU *lpdu) {
N_PDU npdu;
npdu.pci = lpdu->data[0];
memcpy(npdu.data, lpdu->data + 1, 7); // 提取N-PDU数据
NetworkLayer_Receive(&npdu);
}
// 网络层重组N-PDU为I-PDU
static N_PDU rx_buffer[2];
static int rx_count = 0;
void NetworkLayer_Receive(N_PDU *npdu) {
rx_buffer[rx_count++] = *npdu;
if (rx_count == 2) { // 假设需要接收两个N-PDU
I_PDU ipdu;
// 从首帧提取数据
memcpy(&ipdu.speed, rx_buffer[0].data, 2);
memcpy(&ipdu.temp, rx_buffer[0].data + 2, 1);
// 传递I-PDU到应用层
AppLayer_ReceiveData(&ipdu);
rx_count = 0;
}
}
// 应用层解包I-PDU并使用信号
void AppLayer_ReceiveData(I_PDU *ipdu) {
printf("Received: Speed=%d km/h, Temp=%d ℃\n", ipdu->speed, ipdu->temp);
}
上述示例仅展示了数据量较小(单帧 不超过8bytes)的情况,这样做可以缩短单帧信号的传输链路,实时性更好。当需要传输的数据量比较大,而一帧标准can没法将信息完全发送时,则需要CANTP模块进行数据的拆包组包,并在数据段定义当前帧对于完整信号来说是哪一部分数据(连续帧 SN位)。
关于CANTP的理解这篇文章写得较为通俗易懂,可以参考:CAN-TP(暴力理解)
b.Autosar架构中CAN通讯接口交互
接收
发送
二、Davinci cfg & EB配置
1.CAN信号交互功能配置
- 配置步骤:
1.EB配置使用的CAN port
2.EB导出arxml文件,davinci cfg导入对应EB配置
3.Davinci cfg导入DBC文件
4.Davinci cfg配置CAN模块
5.Davinci cfg配置CanIf模块
6.Davinci cfg配置CanSM模块
7.Davinci cfg配置Pdur模块
8.Davinci cfg配置Com模块
9.Davinci cfg配置 Mode Mangement
10.Davinci cfg配置 os
- 1.EB配置使用的CAN port
- 2.EB导出arxml文件,davinci cfg导入对应EB配置
- 3.Davinci cfg导入DBC文件
在此之前先简单介绍一下davinci cfg界面,Davinci cfg提供了两种浏览模式:按功能浏览配置,按模块浏览配置
- 4.Davinci cfg配置CAN模块
-
can模块配置
-
波特率配置
添加当前CAN ID过滤器,不需要过滤不配参数即可
-
- 5.Davinci cfg配置CanIf模块
- 6.Davinci cfg配置CAN SM模块
- 7.Davinci cfg配置Pdur模块
- 8.Davinci cfg配置Com模块
- 9.Davinci cfg配置Mode Management模块
- 10.Davinci cfg配置Os模块
- 配置osISR
- 配置Taskmapping
- 配置osISR
三、总结
本文为博主开发过程中总结而得,如有不正之处欢迎指正。