系列: AUTOSAR通信栈深度解析 - 一条CAN消息的奇幻旅程 项目: S32K148 AUTOSAR官方工程 开源地址: https://gitee.com/shaosyg/autosr_s32k144_project_test.git
🎬 故事的开始
想象一下,你是一条刚刚在应用层诞生的CAN消息。你的使命是将车速信息从ECU传递到仪表盘。这是一段充满冒险的旅程,你将穿越AUTOSAR通信栈的多个层次,最终通过CAN总线到达目的地。
而你旅程的第一站,就是Com(Communication)模块 - AUTOSAR通信栈中最重要的应用层接口。
📦 Com模块:通信世界的邮局
Com模块是什么?
Com模块就像一个智能的邮局,它是应用软件和底层通信栈之间的桥梁:
┌─────────────────────────────────────────┐ │ 应用层 (Application) │ │ - 车速控制 │ │ - 发动机管理 │ │ - 车身控制 │ │ - ... │ └─────────────────┬───────────────────────┘ │ Com_SendSignal() │ Com_ReceiveSignal() ↓ ┌─────────────────────────────────────────┐ │ Com模块 (Communication) │ ← 我们在这里! │ ┌─────────────────────────────────┐ │ │ │ 信号管理 (Signal Management) │ │ │ │ - 信号打包/解包 │ │ │ │ - 字节序转换 │ │ │ │ - 数据类型转换 │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ 传输模式 (Transmission Mode) │ │ │ │ - 周期传输 │ │ │ │ - 事件触发 │ │ │ │ │ - 混合模式 │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ 信号网关 (Signal Gateway) │ │ │ │ - 信号路由 │ │ │ │ - 信号转换 │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ 超时监控 (Timeout Monitor) │ │ │ │ - 接收超时检测 │ │ │ │ - 默认值处理 │ │ │ └─────────────────────────────────┘ │ └─────────────────┬───────────────────────┘ │ PduR_ComTransmit() │ Com_RxIndication() ↓ ┌─────────────────────────────────────────┐ │ PduR (PDU Router) │ └─────────────────────────────────────────┘
Com模块的核心职责
-
信号抽象 📊
-
将应用层的信号(Signal)打包成PDU
-
将接收的PDU解包成信号
-
处理不同的数据类型和字节序
-
-
传输控制 🚦
-
管理信号的发送时机(周期/事件)
-
控制传输模式切换
-
处理最小延迟时间(MDT)
-
-
信号网关 🌉
-
在不同总线间转发信号
-
信号格式转换
-
信号过滤和映射
-
-
可靠性保障 🛡️
-
接收超时监控
-
更新位(Update Bit)管理
-
信号有效性检查
-
🏗️ Com模块的架构设计
分层架构
Com模块内部采用分层设计,每层负责不同的功能:
┌─────────────────────────────────────────┐ │ API层 (Application Interface) │ │ Com_SendSignal() │ │ Com_ReceiveSignal() │ │ Com_SendSignalGroup() │ │ Com_ReceiveSignalGroup() │ └─────────────────┬───────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 信号处理层 (Signal Processing) │ │ - 信号打包 (TxSignal) │ │ - 信号解包 (RxSignal) │ │ - 信号组处理 (SignalGroup) │ │ - 信号网关 (Gateway) │ └─────────────────┬───────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ PDU管理层 (PDU Management) │ │ - PDU缓冲区管理 │ │ - 传输模式处理 │ │ - 传输确认处理 │ └─────────────────┬───────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 调度层 (Scheduling) │ │ - Com_MainFunctionTx() │ │ - Com_MainFunctionRx() │ │ - 周期任务调度 │ └─────────────────────────────────────────┘
核心数据结构
Com模块使用多个关键数据结构来管理信号和PDU:
/**
* @brief 信号配置结构
*
* 每个信号都有自己的配置信息,由DaVinci Configurator生成
*/
typedef struct {
Com_BitPositionOfTxSigInfoType BitPosition; // 信号在PDU中的位位置
Com_BitLengthOfTxSigInfoType BitLength; // 信号的位长度
Com_ApplTypeOfTxSigInfoType ApplType; // 应用数据类型
Com_BusAccOfTxSigInfoType BusAcc; // 总线访问类型
Com_ByteLengthOfTxSigInfoType ByteLength; // 字节长度
Com_BytePositionOfTxSigInfoType BytePosition; // 字节位置
Com_StartByteInPduPositionOfTxSigInfoType StartByteInPduPosition; // PDU中起始字节
Com_TxBufferEndIdxOfTxSigInfoType TxBufferEndIdx; // 缓冲区结束索引
Com_TxBufferStartIdxOfTxSigInfoType TxBufferStartIdx; // 缓冲区起始索引
Com_TxPduInfoIdxOfTxSigInfoType TxPduInfoIdx; // 所属PDU索引
Com_TransferPropertyOfTxSigInfoType TransferProperty; // 传输属性
// ... 更多配置项
} Com_TxSigInfoType;
/**
* @brief PDU配置结构
*
* 每个PDU包含多个信号
*/
typedef struct {
Com_TxPduInitValueEndIdxOfTxPduInfoType TxPduInitValueEndIdx; // 初始值结束索引
Com_TxPduInitValueStartIdxOfTxPduInfoType TxPduInitValueStartIdx; // 初始值起始索引
Com_TxBufferEndIdxOfTxPduInfoType TxBufferEndIdx; // 缓冲区结束索引
Com_TxBufferStartIdxOfTxPduInfoType TxBufferStartIdx; // 缓冲区起始索引
Com_TxPduCalloutFuncPtrIdxOfTxPduInfoType TxPduCalloutFuncPtrIdx;// 回调函数索引
Com_TxSigInfoEndIdxOfTxPduInfoType TxSigInfoEndIdx; // 信号结束索引
Com_TxSigInfoStartIdxOfTxPduInfoType TxSigInfoStartIdx; // 信号起始索引
PduLengthType TxPduLength; // PDU长度
Com_MetaDataLengthOfTxPduInfoType MetaDataLength; // 元数据长度
// ... 更多配置项
} Com_TxPduInfoType;
/**
* @brief 传输模式配置
*
* 定义PDU的传输行为
*/
typedef struct {
Com_TxModeTrueIdxOfTxModeInfoType TxModeTrueIdx; // TRUE模式索引
Com_TxModeFalseIdxOfTxModeInfoType TxModeFalseIdx; // FALSE模式索引
Com_InitModeOfTxModeInfoType InitMode; // 初始模式
Com_MinimumDelayOfTxModeInfoType MinimumDelay; // 最小延迟时间
} Com_TxModeInfoType;
/**
* @brief 传输模式详细配置
*/
typedef struct {
Com_TimePeriodOfTxModeTrueType TimePeriod; // 周期时间
Com_TimeOffsetOfTxModeTrueType TimeOffset; // 时间偏移
Com_RepCntOfTxModeTrueType RepCnt; // 重复次数
Com_RepPeriodOfTxModeTrueType RepPeriod; // 重复周期
boolean Periodic; // 是否周期传输
boolean Direct; // 是否立即传输
} Com_TxModeTrueType;
🔍 信号的生命周期
让我们跟随一个信号,从诞生到发送的完整过程:
阶段1:信号的诞生(应用层)
/**
* @brief 应用层代码示例
*
* 车速控制模块更新车速信号
*/
void VehicleSpeedControl_Task_10ms(void)
{
// 从传感器读取车速(单位:km/h)
uint16 vehicleSpeed = Sensor_GetVehicleSpeed();
// 限制范围(0-250 km/h)
if (vehicleSpeed > 250) {
vehicleSpeed = 250;
}
// 通过Com模块发送信号
// 这是信号旅程的起点!
uint8 result = Com_SendSignal(ComConf_ComSignal_VehicleSpeed,
&vehicleSpeed);
if (result != COM_SERVICE_NOT_AVAILABLE) {
// 信号发送成功或已加入队列
}
}
阶段2:Com_SendSignal() - 信号的入口
/**
* @brief Com_SendSignal实现
*
* 这是应用层发送信号的主要接口
*/
uint8 Com_SendSignal(Com_SignalIdType SignalId,
const void* SignalDataPtr)
{
uint8 retVal = COM_SERVICE_NOT_AVAILABLE;
/* ===== 第一步:参数检查 ===== */
#if (COM_DEV_ERROR_DETECT == STD_ON)
// 检查Com模块是否已初始化
if (Com_GetInitialized() != COM_INIT) {
Com_Det_ReportError(COM_SENDSIGNAL_ID, COM_E_UNINIT);
return COM_SERVICE_NOT_AVAILABLE;
}
// 检查SignalId是否有效
if (SignalId >= Com_GetSizeOfTxSigInfo()) {
Com_Det_ReportError(COM_SENDSIGNAL_ID, COM_E_PARAM);
return COM_SERVICE_NOT_AVAILABLE;
}
// 检查指针是否为空
if (SignalDataPtr == NULL_PTR) {
Com_Det_ReportError(COM_SENDSIGNAL_ID, COM_E_PARAM_POINTER);
return COM_SERVICE_NOT_AVAILABLE;
}
#endif
/* ===== 第二步:获取信号配置 ===== */
Com_TxSigInfoIdxType idxTxSigInfo = SignalId;
// 获取信号所属的PDU
Com_TxPduInfoIdxOfTxSigInfoType txPduId =
Com_GetTxPduInfoIdxOfTxSigInfo(idxTxSigInfo);
// 检查PDU是否处于活动状态
if (Com_IsTxPduGrpActive(txPduId)) {
/* ===== 第三步:进入临界区 ===== */
SchM_Enter_Com_COM_EXCLUSIVE_AREA_TX();
/* ===== 第四步:信号数据处理 ===== */
// 根据信号类型调用相应的处理函数
Com_ApplTypeOfTxSigInfoType applType =
Com_GetApplTypeOfTxSigInfo(idxTxSigInfo);
switch (applType) {
case COM_UINT8_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_UInt8(idxTxSigInfo, SignalDataPtr);
break;
case COM_UINT16_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_UInt16(idxTxSigInfo, SignalDataPtr);
break;
case COM_UINT32_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_UInt32(idxTxSigInfo, SignalDataPtr);
break;
case COM_SINT8_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_SInt8(idxTxSigInfo, SignalDataPtr);
break;
case COM_SINT16_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_SInt16(idxTxSigInfo, SignalDataPtr);
break;
case COM_SINT32_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_SInt32(idxTxSigInfo, SignalDataPtr);
break;
case COM_FLOAT32_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_Float32(idxTxSigInfo, SignalDataPtr);
break;
case COM_FLOAT64_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_Float64(idxTxSigInfo, SignalDataPtr);
break;
case COM_UINT8_N_APPLTYPEOFTXSIGINFO:
Com_Signal_WriteSignal_UInt8_N(idxTxSigInfo, SignalDataPtr);
break;
default:
// 不支持的类型
break;
}
/* ===== 第五步:处理传输属性 ===== */
Com_TransferPropertyOfTxSigInfoType transferProperty =
Com_GetTransferPropertyOfTxSigInfo(idxTxSigInfo);
// 根据传输属性决定是否触发传输
if (transferProperty == COM_TRIGGERED_TRANSFERPROPERTYOFTXSIGINFO) {
// 立即触发传输
Com_TxModeHdlr_TriggerIpduSend(txPduId);
}
else if (transferProperty == COM_TRIGGERED_ON_CHANGE_TRANSFERPROPERTYOFTXSIGINFO) {
// 只有值改变时才触发
if (Com_Signal_CheckSignalValueChanged(idxTxSigInfo, SignalDataPtr)) {
Com_TxModeHdlr_TriggerIpduSend(txPduId);
}
}
else if (transferProperty == COM_TRIGGERED_ON_CHANGE_WITHOUT_REPETITION_TRANSFERPROPERTYOFTXSIGINFO) {
// 值改变时触发,但不重复发送
if (Com_Signal_CheckSignalValueChanged(idxTxSigInfo, SignalDataPtr)) {
Com_TxModeHdlr_TriggerIpduSendWithoutRepetition(txPduId);
}
}
// PENDING类型:不立即触发,等待周期发送
/* ===== 第六步:退出临界区 ===== */
SchM_Exit_Com_COM_EXCLUSIVE_AREA_TX();
retVal = E_OK;
}
return retVal;
}
阶段3:信号打包 - 位操作的艺术
Com模块最核心的功能之一就是将信号数据打包到PDU缓冲区中。由于AUTOSAR支持任意位位置的信号,这需要精巧的位操作:
/**
* @brief 写入UINT16信号到PDU缓冲区
*
* 这个函数展示了Com模块如何处理跨字节边界的信号
*
* 示例:将16位信号写入从bit 12开始的位置
* PDU布局:
* Byte0: [bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0]
* Byte1: [bit15 bit14 bit13 bit12 bit11 bit10 bit9 bit8]
* Byte2: [bit23 bit22 bit21 bit20 bit19 bit18 bit17 bit16]
* Byte3: [bit31 bit30 bit29 bit28 bit27 bit26 bit25 bit24]
*
* 信号位置:bit12 ~ bit27 (16位)
*/
LOCAL_INLINE void Com_Signal_WriteSignal_UInt16(
Com_TxSigInfoIdxType idxTxSigInfo,
const void* SignalDataPtr)
{
// 获取信号值
uint16 signalValue = *((const uint16*)SignalDataPtr);
// 获取信号配置
Com_BitPositionOfTxSigInfoType bitPosition =
Com_GetBitPositionOfTxSigInfo(idxTxSigInfo);
Com_BitLengthOfTxSigInfoType bitLength =
Com_GetBitLengthOfTxSigInfo(idxTxSigInfo);
// 获取PDU缓冲区
Com_TxBufferStartIdxOfTxSigInfoType bufferStartIdx =
Com_GetTxBufferStartIdxOfTxSigInfo(idxTxSigInfo);
// 计算字节位置和位偏移
Com_TxBufferIterType byteIndex = (Com_TxBufferIterType)(bitPosition >> 3u); // 除以8
uint8 bitOffset = (uint8)(bitPosition & 0x07u); // 模8
// 处理字节序(大端/小端)
Com_BusAccOfTxSigInfoType busAcc = Com_GetBusAccOfTxSigInfo(idxTxSigInfo);
if (busAcc == COM_NBIT_BUSACCOFTXSIGINFO) {
// Intel格式(小端)
Com_Signal_WriteSignal_LittleEndian(
&Com_GetTxBuffer(bufferStartIdx + byteIndex),
signalValue,
bitOffset,
bitLength
);
}
else if (busAcc == COM_NBITNBYTE_BUSACCOFTXSIGINFO) {
// Motorola格式(大端)
Com_Signal_WriteSignal_BigEndian(
&Com_GetTxBuffer(bufferStartIdx + byteIndex),
signalValue,
bitOffset,
bitLength
);
}
else if (busAcc == COM_NBYTE_BUSACCOFTXSIGINFO) {
// 字节对齐
Com_Signal_WriteSignal_ByteAligned(
&Com_GetTxBuffer(bufferStartIdx + byteIndex),
signalValue,
bitLength
);
}
}
/**
* @brief 小端格式信号写入
*
* Intel格式:低字节在前,高字节在后
*/
LOCAL_INLINE void Com_Signal_WriteSignal_LittleEndian(
uint8* destPtr,
uint32 signalValue,
uint8 bitOffset,
uint8 bitLength)
{
// 创建掩码
uint32 mask = (bitLength == 32u) ? 0xFFFFFFFFu : ((1u << bitLength) - 1u);
// 应用掩码到信号值
signalValue &= mask;
// 左移到正确的位位置
uint32 shiftedValue = signalValue << bitOffset;
uint32 shiftedMask = mask << bitOffset;
// 计算需要写入的字节数
uint8 bytesToWrite = (uint8)((bitOffset + bitLength + 7u) >> 3u); // 向上取整
// 逐字节写入
for (uint8 i = 0; i < bytesToWrite; i++) {
uint8 byteMask = (uint8)(shiftedMask >> (i * 8u));
uint8 byteValue = (uint8)(shiftedValue >> (i * 8u));
// 清除旧值,写入新值
destPtr[i] = (destPtr[i] & ~byteMask) | (byteValue & byteMask);
}
}
/**
* @brief 大端格式信号写入
*
* Motorola格式:高字节在前,低字节在后
*/
LOCAL_INLINE void Com_Signal_WriteSignal_BigEndian(
uint8* destPtr,
uint32 signalValue,
uint8 bitOffset,
uint8 bitLength)
{
// 创建掩码
uint32 mask = (bitLength == 32u) ? 0xFFFFFFFFu : ((1u << bitLength) - 1u);
// 应用掩码
signalValue &= mask;
// 计算需要写入的字节数
uint8 bytesToWrite = (uint8)((bitOffset + bitLength + 7u) >> 3u);
// 大端格式需要反向处理
uint8 startBitInByte = 7u - bitOffset;
// 逐字节写入(从高位到低位)
for (uint8 i = 0; i < bytesToWrite; i++) {
uint8 bitsInThisByte;
if (i == 0) {
// 第一个字节
bitsInThisByte = (startBitInByte + 1u < bitLength) ?
(startBitInByte + 1u) : bitLength;
}
else if (i == (bytesToWrite - 1u)) {
// 最后一个字节
bitsInThisByte = bitLength - (i * 8u - (7u - startBitInByte));
}
else {
// 中间字节
bitsInThisByte = 8u;
}
// 计算掩码和值
uint8 byteMask = (uint8)((1u << bitsInThisByte) - 1u);
uint8 byteValue = (uint8)((signalValue >> (bitLength - (i + 1u) * 8u + (7u - startBitInByte))) & byteMask);
// 写入
destPtr[i] = (destPtr[i] & ~byteMask) | byteValue;
}
}
位操作示例详解
让我们通过一个具体例子来理解位操作:
场景:将16位车速信号(值=120)写入PDU 信号配置: - 位位置:12 - 位长度:16 - 字节序:小端(Intel) PDU初始状态(4字节): Byte0: 0x00 [00000000] Byte1: 0x00 [00000000] Byte2: 0x00 [00000000] Byte3: 0x00 [00000000] 信号值:120 (0x0078) 二进制:0000 0000 0111 1000 步骤1:计算位置 - 字节索引 = 12 / 8 = 1 - 位偏移 = 12 % 8 = 4 步骤2:创建掩码 - mask = (1 << 16) - 1 = 0xFFFF 步骤3:左移信号值 - shiftedValue = 0x0078 << 4 = 0x0780 - 二进制:0000 0111 1000 0000 步骤4:写入字节 - Byte1: 写入低8位 = 0x80 - Byte2: 写入高8位 = 0x07 PDU最终状态: Byte0: 0x00 [00000000] Byte1: 0x80 [10000000] ← 信号低8位 Byte2: 0x07 [00000111] ← 信号高8位 Byte3: 0x00 [00000000] 位视图(bit 0-31): bit 0-11: 0000 0000 0000 (未使用) bit 12-27: 0000 0000 0111 1000 (车速=120) bit 28-31: 0000 (未使用)
🚦 传输模式管理
Com模块支持多种传输模式,决定PDU何时被发送。这是Com模块最复杂也最强大的功能之一。
传输模式类型
/**
* @brief 传输模式类型枚举
*/
typedef enum {
COM_NONE_TRANSMISSIONMODEOFTXMODETRUE, // 无传输
COM_PERIODIC_TRANSMISSIONMODEOFTXMODETRUE, // 周期传输
COM_DIRECT_TRANSMISSIONMODEOFTXMODETRUE, // 直接传输(事件触发)
COM_MIXED_TRANSMISSIONMODEOFTXMODETRUE // 混合模式(周期+事件)
} Com_TransmissionModeOfTxModeTrueType;
传输模式详解
1. 周期传输模式(Periodic)
周期传输模式下,PDU按照固定的时间间隔发送:
/**
* @brief 周期传输模式配置示例
*/
const Com_TxModeTrueType Com_TxModeTrue_VehicleInfo = {
.TransmissionMode = COM_PERIODIC_TRANSMISSIONMODEOFTXMODETRUE,
.TimePeriod = 10, // 10ms周期
.TimeOffset = 0, // 无偏移
.RepCnt = 0, // 无重复
.RepPeriod = 0, // 无重复周期
.Periodic = TRUE,
.Direct = FALSE
};
/**
* @brief 周期传输处理
*
* 在Com_MainFunctionTx()中被调用
*/
LOCAL_INLINE void Com_TxModeHdlr_MainFunctionTx_Cyclic(
Com_TxPduInfoIterType txPduId)
{
// 检查是否是周期模式
if (Com_TxModeHdlr_IsPeriodicTxMode(txPduId)) {
// 获取当前计数器值
Com_TxCyclicPendingType cycleCnt =
Com_GetCycleTimeCnt(txPduId);
if (cycleCnt > 0u) {
// 递减计数器
cycleCnt--;
Com_SetCycleTimeCnt(txPduId, cycleCnt);
if (cycleCnt == 0u) {
// 计数器到0,触发发送
Com_TxModeHdlr_TriggerIpduSendOnceDeferred(txPduId);
// 重新加载周期
Com_TxCyclicPendingType timePeriod =
Com_TxModeHdlr_GetCurrentTimePeriod(txPduId);
Com_SetCycleTimeCnt(txPduId, timePeriod);
}
}
}
}
2. 事件触发模式(Direct)
事件触发模式下,只有当信号值改变或被显式触发时才发送:
/**
* @brief 事件触发模式配置示例
*/
const Com_TxModeTrueType Com_TxModeTrue_EngineStatus = {
.TransmissionMode = COM_DIRECT_TRANSMISSIONMODEOFTXMODETRUE,
.TimePeriod = 0, // 无周期
.TimeOffset = 0,
.RepCnt = 3, // 重复3次
.RepPeriod = 5, // 重复周期5ms
.Periodic = FALSE,
.Direct = TRUE
};
/**
* @brief 事件触发处理
*/
LOCAL_INLINE void Com_TxModeHdlr_TriggerIpduSend(
Com_TxPduInfoIterType txPduId)
{
// 检查是否允许发送
if (Com_IsTxPduGrpActive(txPduId)) {
// 检查最小延迟时间(MDT)
if (Com_TxModeHdlr_CheckMinimumDelay(txPduId)) {
// 设置传输请求标志
Com_SetTransmitRequest(txPduId, TRUE);
// 如果配置了重复发送
if (Com_IsRepCntOfTxModeTrue(txPduId)) {
Com_RepCntType repCnt =
Com_GetRepCntOfTxModeTrue(txPduId);
Com_SetRepCnt(txPduId, repCnt);
Com_RepPeriodType repPeriod =
Com_GetRepPeriodOfTxModeTrue(txPduId);
Com_SetRepCycleCnt(txPduId, repPeriod);
}
}
else {
// MDT未满足,延迟发送
Com_SetDelayTimeCnt(txPduId,
Com_GetMinimumDelayOfTxModeInfo(txPduId));
}
}
}
3. 混合模式(Mixed)
混合模式结合了周期和事件触发:
/**
* @brief 混合模式配置示例
*/
const Com_TxModeTrueType Com_TxModeTrue_VehicleStatus = {
.TransmissionMode = COM_MIXED_TRANSMISSIONMODEOFTXMODETRUE,
.TimePeriod = 100, // 100ms周期
.TimeOffset = 0,
.RepCnt = 2, // 事件触发时重复2次
.RepPeriod = 10, // 重复周期10ms
.Periodic = TRUE,
.Direct = TRUE
};
/**
* @brief 混合模式处理
*
* 同时处理周期和事件触发
*/
LOCAL_INLINE void Com_TxModeHdlr_MainFunctionTx_Mixed(
Com_TxPduInfoIterType txPduId)
{
// 处理周期发送
Com_TxModeHdlr_MainFunctionTx_Cyclic(txPduId);
// 处理事件触发的重复发送
if (Com_GetRepCnt(txPduId) > 0u) {
Com_RepCycleCntType repCycleCnt =
Com_GetRepCycleCnt(txPduId);
if (repCycleCnt > 0u) {
repCycleCnt--;
Com_SetRepCycleCnt(txPduId, repCycleCnt);
if (repCycleCnt == 0u) {
// 触发重复发送
Com_TxModeHdlr_TriggerIpduSendOnceDeferred(txPduId);
// 递减重复计数
Com_DecRepCnt(txPduId);
// 重新加载重复周期
if (Com_GetRepCnt(txPduId) > 0u) {
Com_RepPeriodType repPeriod =
Com_GetRepPeriodOfTxModeTrue(txPduId);
Com_SetRepCycleCnt(txPduId, repPeriod);
}
}
}
}
}
传输模式切换(TMS)
Com模块支持传输模式切换(Transmission Mode Selection),根据信号值动态切换传输模式:
/**
* @brief 传输模式切换
*
* 根据TMS条件在TRUE模式和FALSE模式之间切换
*/
LOCAL_INLINE void Com_TxModeHdlr_EvaluateTMS(
Com_TxPduInfoIterType txPduId)
{
boolean currentTxMode = Com_IsCurrentTxMode(txPduId);
boolean newTxMode = currentTxMode;
// 评估TMS条件
if (Com_IsTxFilterInitValueUsedOfTxSigInfo(txPduId)) {
// 有TMS过滤器
boolean tmsResult = Com_TxModeHdlr_EvaluateTMSFilter(txPduId);
if (tmsResult != currentTxMode) {
// 模式需要切换
newTxMode = tmsResult;
Com_SetCurrentTxMode(txPduId, newTxMode);
// 重新初始化传输模式
if (newTxMode == TRUE) {
// 切换到TRUE模式
Com_TxModeHdlr_InitTxMode_True(txPduId);
}
else {
// 切换到FALSE模式
Com_TxModeHdlr_InitTxMode_False(txPduId);
}
}
}
}
/**
* @brief 评估TMS过滤器
*
* 示例:当车速 > 0时使用TRUE模式(快速更新)
* 当车速 = 0时使用FALSE模式(慢速更新)
*/
LOCAL_INLINE boolean Com_TxModeHdlr_EvaluateTMSFilter(
Com_TxPduInfoIterType txPduId)
{
boolean result = FALSE;
// 获取TMS信号
Com_TxSigInfoIdxOfTxPduInfoType sigStartIdx =
Com_GetTxSigInfoStartIdxOfTxPduInfo(txPduId);
Com_TxSigInfoIdxOfTxPduInfoType sigEndIdx =
Com_GetTxSigInfoEndIdxOfTxPduInfo(txPduId);
// 遍历所有信号,评估过滤条件
for (Com_TxSigInfoIdxOfTxPduInfoType sigIdx = sigStartIdx;
sigIdx < sigEndIdx;
sigIdx++) {
if (Com_IsTxFilterInitValueUsedOfTxSigInfo(sigIdx)) {
// 获取信号当前值
uint32 signalValue = Com_Signal_ReadSignalValue(sigIdx);
// 获取过滤器配置
Com_FilterInfoIdxOfTxSigInfoType filterIdx =
Com_GetFilterInfoIdxOfTxSigInfo(sigIdx);
// 评估过滤条件
result = Com_Signal_EvaluateFilter(filterIdx, signalValue);
if (result == TRUE) {
break; // 任一信号满足条件即可
}
}
}
return result;
}
📝 本章小结(上篇)
在这一章的上篇中,我们深入学习了:
-
✅ Com模块架构: 分层设计和核心职责
-
✅ 数据结构: 信号配置、PDU配置、传输模式配置
-
✅ 信号生命周期: 从应用层到Com模块的完整流程
-
✅ Com_SendSignal(): 详细的实现和参数检查
-
✅ 位操作: 小端和大端格式的信号打包
-
✅ 传输模式: 周期、事件触发、混合模式
-
✅ TMS: 传输模式动态切换机制
关键技术点回顾
Com模块核心功能(上篇): 信号处理 ├── 信号打包(位操作) │ ├── 小端格式(Intel) │ ├── 大端格式(Motorola) │ └── 字节对齐 ├── 数据类型支持 │ ├── UINT8/16/32 │ ├── SINT8/16/32 │ ├── FLOAT32/64 │ └── UINT8_N(字节数组) └── 字节序转换 传输模式 ├── 周期传输(Periodic) ├── 事件触发(Direct) ├── 混合模式(Mixed) └── 传输模式切换(TMS)
下篇预告: 在下一篇中,我们将继续深入学习:
-
📬 Com_MainFunctionTx/Rx的调度机制
-
🔄 信号组(Signal Group)的处理
-
🌉 信号网关(Signal Gateway)的实现
-
⏱️ 超时监控和默认值处理
-
🎯 更新位(Update Bit)管理
-
🔧 实际调试技巧和性能优化
继续阅读: 第一章:启程 - Com模块深度解析(下篇) →
&spm=1001.2101.3001.5002&articleId=157591777&d=1&t=3&u=83222e32d108476c814ae82f7cab8961)
94

被折叠的 条评论
为什么被折叠?



