AUTOSAR LIN (Local Interconnect Network) 协议详解
目录
1. 概述
LIN(Local Interconnect Network,本地互联网络)是一种低成本的串行通信总线系统,主要用于汽车电子领域的分布式系统。AUTOSAR(AUTomotive Open System ARchitecture)标准为LIN协议提供了完整的软件栈支持,实现了对LIN主节点和从节点的配置与控制。
本文基于AUTOSAR CP 4.4.0标准的LIN相关规范,详细分析LIN协议在AUTOSAR架构中的实现方式、状态转换机制、帧结构和调度表设计。
AUTOSAR LIN协议主要包含以下模块:
- LIN接口 (LinIf):提供统一的API接口
- LIN驱动 (Lin):负责与硬件交互
- LIN传输层 (LinTp):处理多帧数据传输
- LIN收发器驱动 (LinTrcv):管理物理层通信
这些模块共同遵循ISO 17987规范,并向后兼容LIN 2.2、LIN 2.1、LIN 2.0和LIN 1.3等早期版本的LIN规范。
2. AUTOSAR LIN架构
2.1 架构层次分析
AUTOSAR LIN架构图展示了LIN协议栈在AUTOSAR软件架构中的位置和各模块间的关系。从图中可以清晰地看到,LIN协议栈位于通信服务层和微控制器抽象层之间,作为连接应用软件和硬件的桥梁。
组件 应用软件组件:
- 职责: 应用软件组件是用户开发的功能模块,通过通信服务层访问LIN总线通信功能
- 功能点:
- 使用通信管理服务发起或终止LIN通信
- 通过PDU路由器发送和接收LIN数据
- 配置LIN通信参数
组件 通信管理:
- 职责: 管理所有通信介质(包括LIN)的启动和关闭
- 功能点:
- 处理网络管理请求
- 控制LIN通信状态(唤醒、睡眠)
- 协调多个网络层的通信状态
根据源文档中的[SRS_Lin_01560]要求,当在睡眠模式转换过程中接收到唤醒请求时,通信管理必须取消睡眠过程,并恢复到运行模式。
组件 LIN接口(LinIf):
- 职责: 管理LIN调度表,处理LIN帧收发,提供统一API给上层模块
- 功能点:
- 管理调度表执行
- 处理LIN主节点和从节点功能
- 支持ISO 17987规范及兼容早期LIN规范
- 提供唤醒和睡眠控制
源文档中的[SRS_Lin_01577]要求LIN接口必须兼容ISO 17987规范,包括支持不同版本的LIN协议。
组件 LIN驱动(Lin):
- 职责: 控制UART硬件,处理LIN帧的物理传输
- 功能点:
- 帧错误检测与处理
- 管理检验和类型(经典和增强)
- 支持主节点和从节点操作
- 实现唤醒和睡眠功能
源文档[SRS_Lin_01504]和[SRS_Lin_01598]指出,AUTOSAR架构适用于LIN主节点和从节点的实现,支持各版本LIN规范。
组件 LIN传输层(LinTp):
- 职责: 支持多帧数据传输,分段和重组,传输流控制
- 功能点:
- 处理诊断数据传输
- 支持分段和重组
- 实现传输流控制
- 管理连接
源文档中定义了LinTp支持LIN诊断传输,包括支持FF(首帧)、CF(连续帧)和SF(单帧)的传输。
2.2 代码示例
以下是LIN接口初始化的代码示例,展示了如何配置和初始化LIN接口:
/* LIN接口配置结构 */
typedef struct {
uint8 LinIfTimeBase; /* LIN接口时间基准(ms) */
uint8 LinIfVersionInfo; /* 版本信息启用标志 */
uint8 LinIfChannel; /* LIN通道索引 */
const LinIf_ChannelConfigType* LinIfChannelConfig; /* 通道配置 */
const LinIf_ScheduleTableType* LinIfScheduleTableConfig; /* 调度表配置 */
} LinIf_ConfigType;
/* LIN接口初始化函数 */
Std_ReturnType LinIf_Init(const LinIf_ConfigType* ConfigPtr)
{
Std_ReturnType ret = E_NOT_OK;
/* 参数检查 */
if (ConfigPtr == NULL) {
Det_ReportError(LINIF_MODULE_ID, LINIF_INSTANCE_ID,
LINIF_SID_INIT, LINIF_E_PARAM_POINTER);
return E_NOT_OK;
}
/* 配置LIN接口 */
LinIf_State = LINIF_INIT;
/* 初始化所有通道 */
for (uint8 channel = 0; channel < LINIF_CHANNEL_COUNT; channel++) {
LinIf_ChannelState[channel] = LINIF_CH_OPERATIONAL;
/* 初始化通道特定参数 */
}
/* 初始化LIN驱动 */
ret = Lin_Init(&Lin_Config);
if (ret == E_OK) {
LinIf_State = LINIF_OPERATIONAL;
}
return ret;
}
3. LIN状态转换
3.1 状态转换机制
LIN状态转换图展示了LIN驱动和接口的状态管理机制,包括初始化、运行和睡眠状态之间的转换。
状态 LIN_UNINIT:
- 描述: LIN驱动的未初始化状态
- 特性:
- 模块加载后的初始状态
- 此状态下不能执行任何LIN操作
- 必须通过Lin_Init函数转换到初始化状态
状态 LIN_INIT:
- 描述: LIN驱动的初始化状态
- 特性:
- Lin_Init函数执行后的临时状态
- 在此状态下完成硬件配置
- 初始化完成后自动转换到运行状态
状态 LIN_CH_OPERATIONAL:
- 描述: LIN通道的正常运行状态
- 特性:
- 可以执行LIN帧传输
- 调度表可以正常运行
- 可以通过Lin_GoToSleep函数请求进入睡眠状态
状态 LIN_CH_SLEEP_TRANSITION:
- 描述: LIN通道的睡眠转换状态
- 特性:
- 主节点会发送睡眠命令帧
- 从节点收到睡眠命令或总线空闲超时进入此状态
- 如果在此状态收到唤醒请求,则取消睡眠并返回运行状态
根据源文档[SRS_Lin_01560],如果在睡眠转换过程中接收到唤醒请求,必须取消睡眠过程并返回运行状态。
状态 LIN_CH_SLEEP:
- 描述: LIN通道的睡眠状态
- 特性:
- 低功耗模式
- 所有LIN通信暂停
- 只能通过唤醒信号或软件唤醒命令退出
3.2 代码示例
以下代码展示了LIN驱动的状态转换操作:
/* LIN驱动睡眠命令函数 */
Std_ReturnType Lin_GoToSleep(uint8 Channel)
{
Std_ReturnType ret = E_NOT_OK;
/* 检查模块状态 */
if (Lin_State != LIN_OPERATIONAL) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_GOTOSLEEP, LIN_E_UNINIT);
return E_NOT_OK;
}
/* 检查通道参数 */
if (Channel >= LIN_CHANNEL_COUNT) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_GOTOSLEEP, LIN_E_INVALID_CHANNEL);
return E_NOT_OK;
}
/* 检查通道状态 */
if (Lin_ChannelState[Channel] != LIN_CH_OPERATIONAL) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_GOTOSLEEP, LIN_E_INVALID_STATE);
return E_NOT_OK;
}
/* 发送睡眠命令帧 */
ret = Lin_SendSleepFrame(Channel);
if (ret == E_OK) {
/* 更新通道状态 */
Lin_ChannelState[Channel] = LIN_CH_SLEEP_TRANSITION;
}
return ret;
}
/* LIN驱动唤醒函数 */
Std_ReturnType Lin_Wakeup(uint8 Channel)
{
Std_ReturnType ret = E_NOT_OK;
/* 检查模块状态 */
if (Lin_State != LIN_OPERATIONAL) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_WAKEUP, LIN_E_UNINIT);
return E_NOT_OK;
}
/* 检查通道参数 */
if (Channel >= LIN_CHANNEL_COUNT) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_WAKEUP, LIN_E_INVALID_CHANNEL);
return E_NOT_OK;
}
/* 发送唤醒脉冲 */
ret = Lin_SendWakeupPulse(Channel);
if (ret == E_OK) {
/* 更新通道状态 */
if (Lin_ChannelState[Channel] == LIN_CH_SLEEP ||
Lin_ChannelState[Channel] == LIN_CH_SLEEP_TRANSITION) {
Lin_ChannelState[Channel] = LIN_CH_OPERATIONAL;
}
}
return ret;
}
4. LIN帧结构
4.1 帧结构分析
LIN帧结构图展示了LIN帧的完整组成部分,包括帧头和帧响应。LIN帧由主节点发送的帧头和从节点发送的帧响应两部分组成。
组件 Break字段:
- 描述: LIN帧的起始部分,用于同步
- 特性:
- 长度至少13位低电平
- 用于唤醒接收器
- 标志新帧开始
组件 同步字段:
- 描述: 用于波特率同步的固定值字段
- 特性:
- 固定值0x55 (01010101)
- 用于波特率同步
- 通过位沿变化帮助从节点确定波特率
组件 帧ID (PID):
- 描述: 标识LIN帧类型和用途的字段
- 特性:
- 6位ID + 2位校验位
- 决定帧类型和优先级
- 0x3C/0x3D为诊断帧
- 0x3E为主节点请求帧(MRF)
- 0x3F为从节点响应帧(SRF)
源文档中提到,ISO 17987规范支持两种LIN ID分配模型(“2.0"和"2.1或更高版本”),确保兼容性。
组件 数据字段:
- 描述: 包含实际传输数据的字段
- 特性:
- 长度1-8字节
- 首先传输LSB (最低有效位)
- 内容由帧ID决定
源文档[SRS_Lin_01522]强调LIN-SDU(数据部分)必须在传输和接收时保持一致性,包括有效数据和标志位。
组件 校验和:
- 描述: 用于校验数据完整性的字段
- 特性:
- LIN 1.x: 经典校验(仅数据)
- LIN 2.x/ISO 17987: 增强校验(包含PID和数据)
- 诊断帧始终使用经典校验
源文档中明确指出,除了预留的LIN ID外,每个LIN ID的校验和模型(经典和增强)都是可配置的。
4.2 代码示例
以下是处理LIN帧的代码示例:
/* LIN帧结构定义 */
typedef struct {
uint8 Pid; /* 帧ID (PID) */
uint8 Cs; /* 校验和类型 (LIN_CLASSIC_CS, LIN_ENHANCED_CS) */
uint8 Drc; /* 响应方向 (LIN_MASTER_RESPONSE, LIN_SLAVE_RESPONSE) */
uint8 Dl; /* 数据长度 (1-8) */
uint8 Data[8]; /* 数据内容 */
} Lin_PduType;
/* 计算LIN帧校验和 */
uint8 Lin_CalculateChecksum(const Lin_PduType* LinPduPtr)
{
uint8 checksum = 0;
uint8 i;
if (LinPduPtr == NULL) {
return 0;
}
/* 增强校验包含PID */
if (LinPduPtr->Cs == LIN_ENHANCED_CS) {
checksum = LinPduPtr->Pid;
}
/* 累加数据字节 */
for (i = 0; i < LinPduPtr->Dl; i++) {
checksum += LinPduPtr->Data[i];
/* 处理进位 */
if (checksum >= 0xFF) {
checksum -= 0xFF;
}
}
/* 取反 */
checksum = ~checksum;
return checksum;
}
/* 发送LIN帧 */
Std_ReturnType Lin_SendFrame(uint8 Channel, const Lin_PduType* PduInfoPtr)
{
Std_ReturnType ret = E_NOT_OK;
/* 参数检查 */
if (PduInfoPtr == NULL) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_SENDFRAME, LIN_E_PARAM_POINTER);
return E_NOT_OK;
}
if (Channel >= LIN_CHANNEL_COUNT) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_SENDFRAME, LIN_E_INVALID_CHANNEL);
return E_NOT_OK;
}
/* 数据长度检查 */
if (PduInfoPtr->Dl < 1 || PduInfoPtr->Dl > 8) {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_SENDFRAME, LIN_E_PARAM_LENGTH);
return E_NOT_OK;
}
/* 发送帧 */
if (Lin_ChannelState[Channel] == LIN_CH_OPERATIONAL) {
/* 发送LIN帧头 */
ret = Lin_SendHeader(Channel, PduInfoPtr->Pid);
/* 如果是主节点响应,发送响应部分 */
if (ret == E_OK && PduInfoPtr->Drc == LIN_MASTER_RESPONSE) {
ret = Lin_SendResponse(Channel, PduInfoPtr);
}
} else {
Det_ReportError(LIN_MODULE_ID, LIN_INSTANCE_ID,
LIN_SID_SENDFRAME, LIN_E_INVALID_STATE);
}
return ret;
}
5. LIN调度表
5.1 调度表分析
LIN调度表结构图展示了AUTOSAR中LIN调度表的配置模型,包括表项、帧触发和相关参数。
类 LinIfScheduleTable:
- 功能: 定义LIN主节点使用的调度表,控制LIN总线通信
- 关键属性:
- LinIfScheduleTableName:
- 描述: 调度表的唯一标识符名称
- 类型: String
- 来源: 系统配置工具
- LinIfScheduleTableIndex:
- 描述: 调度表索引编号
- 类型: Integer
- 取值范围: 0-255
- LinIfRunMode:
- 描述: 调度表运行模式
- 类型: RunModeType
- 取值范围: LIN_CONTINUOUS, LIN_ONCE
- LinIfResumePosition:
- 描述: 调度表恢复位置
- 类型: Integer
- 默认值: 0
- LinIfScheduleTableName:
源文档中提到,调度表用于确定LIN总线上的通信流程,一个LIN总线可以配置多个调度表。
类 LinIfScheduleTableEntry:
- 功能: 定义调度表中的单个表项,包含帧触发和延迟时间
- 关键属性:
- LinIfEntryIndex:
- 描述: 表项索引号
- 类型: Integer
- LinIfEntryDelay:
- 描述: 延迟时间(毫秒)
- 类型: Integer
- LinIfFrameRef:
- 描述: 引用的LIN帧
- 类型: Reference
- LinIfCollisionResolution:
- 描述: 碰撞解决标志
- 类型: Boolean
- 默认值: FALSE
- LinIfEntryIndex:
源文档中的ISO 17987规范第5.3节详细描述了调度表的结构和行为。
类 LinIfFrameTriggering:
- 功能: 定义LIN帧的触发配置,包括帧类型和校验和类型
- 关键属性:
- LinIfPduRef:
- 描述: 引用的PDU
- 类型: Reference
- LinIfChannelRef:
- 描述: 引用的LIN通道
- 类型: Reference
- LinIfFrameType:
- 描述: 帧类型
- 类型: FrameType
- 取值范围: UNCONDITIONAL, EVENT_TRIGGERED, SPORADIC, DIAGNOSTIC, SLEEP
- LinIfChecksumType:
- 描述: 校验和类型
- 类型: ChecksumType
- 取值范围: CLASSIC, ENHANCED
- LinIfPduRef:
源文档提到,除了预留的LIN ID外,每个LIN ID的校验和类型都是可配置的,支持经典校验和和增强校验和。
5.2 代码示例
以下是LIN调度表配置的代码示例:
/* LIN调度表配置结构 */
typedef enum {
LIN_CONTINUOUS, /* 连续执行模式 */
LIN_ONCE /* 单次执行模式 */
} LinIf_ScheduleTableRunModeType;
typedef enum {
LIN_UNCONDITIONAL, /* 无条件帧 */
LIN_EVENT_TRIGGERED, /* 事件触发帧 */
LIN_SPORADIC, /* 偶发帧 */
LIN_DIAGNOSTIC, /* 诊断帧 */
LIN_SLEEP /* 睡眠帧 */
} LinIf_FrameTypeType;
typedef enum {
LIN_CLASSIC_CS, /* 经典校验和 */
LIN_ENHANCED_CS /* 增强校验和 */
} LinIf_CSModelType;
/* 帧触发配置 */
typedef struct {
uint8 LinIfPid; /* 帧ID */
LinIf_FrameTypeType LinIfFrameType; /* 帧类型 */
LinIf_CSModelType LinIfCsModel; /* 校验和类型 */
uint8 LinIfDl; /* 数据长度 */
const PduIdType LinIfTxPduId; /* 发送PDU ID */
const PduIdType LinIfRxPduId; /* 接收PDU ID */
} LinIf_FrameTriggeringConfigType;
/* 调度表表项配置 */
typedef struct {
uint16 LinIfDelay; /* 延迟时间(ms) */
const LinIf_FrameTriggeringConfigType* LinIfFrameTriggeringRef; /* 帧触发引用 */
} LinIf_ScheduleTableEntryType;
/* 调度表配置 */
typedef struct {
uint8 LinIfScheduleTableIndex; /* 调度表索引 */
LinIf_ScheduleTableRunModeType LinIfRunMode; /* 运行模式 */
uint8 LinIfEntryCount; /* 表项数量 */
const LinIf_ScheduleTableEntryType* LinIfEntry; /* 表项数组 */
} LinIf_ScheduleTableConfigType;
/* 调度表配置示例 */
const LinIf_FrameTriggeringConfigType LinIf_FrameTriggeringConfig[] = {
{
.LinIfPid = 0x10,
.LinIfFrameType = LIN_UNCONDITIONAL,
.LinIfCsModel = LIN_ENHANCED_CS,
.LinIfDl = 8,
.LinIfTxPduId = PDU_ID_TX_ENGINE_STATUS,
.LinIfRxPduId = PDU_ID_RX_ENGINE_STATUS
},
{
.LinIfPid = 0x22,
.LinIfFrameType = LIN_UNCONDITIONAL,
.LinIfCsModel = LIN_ENHANCED_CS,
.LinIfDl = 4,
.LinIfTxPduId = PDU_ID_TX_DOOR_STATUS,
.LinIfRxPduId = PDU_ID_RX_DOOR_STATUS
},
{
.LinIfPid = 0x3C,
.LinIfFrameType = LIN_DIAGNOSTIC,
.LinIfCsModel = LIN_CLASSIC_CS, /* 诊断帧使用经典校验和 */
.LinIfDl = 8,
.LinIfTxPduId = PDU_ID_TX_DIAG,
.LinIfRxPduId = PDU_ID_RX_DIAG
}
};
const LinIf_ScheduleTableEntryType LinIf_NormalTableEntries[] = {
{
.LinIfDelay = 10, /* 10ms */
.LinIfFrameTriggeringRef = &LinIf_FrameTriggeringConfig[0]
},
{
.LinIfDelay = 20, /* 20ms */
.LinIfFrameTriggeringRef = &LinIf_FrameTriggeringConfig[1]
}
};
const LinIf_ScheduleTableConfigType LinIf_ScheduleTableConfig[] = {
{
.LinIfScheduleTableIndex = 0,
.LinIfRunMode = LIN_CONTINUOUS,
.LinIfEntryCount = 2,
.LinIfEntry = LinIf_NormalTableEntries
}
};
/* 请求切换调度表 */
Std_ReturnType LinIf_ScheduleRequest(uint8 Channel, uint8 ScheduleTableIndex)
{
Std_ReturnType ret = E_NOT_OK;
/* 参数检查 */
if (Channel >= LINIF_CHANNEL_COUNT) {
Det_ReportError(LINIF_MODULE_ID, LINIF_INSTANCE_ID,
LINIF_SID_SCHEDULEREQUEST, LINIF_E_INVALID_CHANNEL);
return E_NOT_OK;
}
if (ScheduleTableIndex >= LINIF_SCHEDULE_TABLE_COUNT) {
Det_ReportError(LINIF_MODULE_ID, LINIF_INSTANCE_ID,
LINIF_SID_SCHEDULEREQUEST, LINIF_E_NONEXISTENT_TABLE);
return E_NOT_OK;
}
/* 检查LIN接口状态 */
if (LinIf_State != LINIF_OPERATIONAL) {
Det_ReportError(LINIF_MODULE_ID, LINIF_INSTANCE_ID,
LINIF_SID_SCHEDULEREQUEST, LINIF_E_UNINIT);
return E_NOT_OK;
}
/* 检查通道状态 */
if (LinIf_ChannelState[Channel] != LINIF_CH_OPERATIONAL) {
return E_NOT_OK;
}
/* 切换调度表 */
LinIf_ActiveScheduleTable[Channel] = ScheduleTableIndex;
LinIf_ScheduleTableIndex[Channel] = 0; /* 从第一个表项开始 */
return E_OK;
}
6. 总结
AUTOSAR LIN协议规范提供了完整的软件栈支持,实现了对LIN主节点和从节点的配置与控制。通过本文的分析,我们了解了AUTOSAR LIN的架构设计、状态管理、帧结构和调度表机制。
AUTOSAR LIN协议的主要特点:
- 标准兼容性:遵循ISO 17987规范,同时向后兼容LIN 2.2、LIN 2.1、LIN 2.0和LIN 1.3等早期版本
- 模块化架构:包含LIN接口、LIN驱动、LIN传输层和LIN收发器驱动四个核心模块
- 灵活配置:支持多种校验和类型、帧类型和调度表类型
- 状态管理:完善的状态转换机制,支持唤醒和睡眠功能
- 诊断支持:通过LIN传输层支持诊断服务
AUTOSAR LIN协议的实现为汽车电子系统提供了稳定、标准化的通信方案,特别适合成本敏感的低速控制场景,如车窗控制、座椅调节、照明控制等应用。