第一章:启程 - Com模块深度解析(下篇)

系列: AUTOSAR通信栈深度解析 - 一条CAN消息的奇幻旅程 项目: S32K148 AUTOSAR官方工程 开源地址: https://gitee.com/shaosyg/autosr_s32k144_project_test.git


🎬 继续深入Com模块

在上篇中,我们学习了Com模块的基础架构、信号打包和传输模式。现在让我们继续探索Com模块更高级的功能。


📬 Com主函数:调度的心脏

Com模块的主函数是整个通信系统的心脏,负责周期性地处理发送和接收任务。

Com_MainFunctionTx - 发送调度

/**
 * @brief Com发送主函数
 * 
 * 这个函数通常在1ms或更短的周期任务中被调用
 * 负责处理所有的周期发送、重复发送和延迟发送
 */
void Com_MainFunctionTx(void)
{
    // 检查Com模块是否已初始化
    if (Com_GetInitialized() == COM_INIT) {
        
        /* ===== 第一步:处理周期计数器 ===== */
        Com_TxModeHdlr_MainFunctionTx_CycleCounter();
        
        /* ===== 第二步:处理最小延迟时间 ===== */
        #if (COM_MINIMUMDELAYOFTXMODEINFO == STD_ON)
        Com_TxModeHdlr_MainFunctionTx_MinimumDelay();
        #endif
        
        /* ===== 第三步:处理实际的PDU发送 ===== */
        Com_TxModeHdlr_MainFunctionTx_Transmit();
        
        /* ===== 第四步:处理信号网关 ===== */
        #if (COM_GWINFO == STD_ON)
        Com_MainFunction_SignalRoutings();
        #endif
    }
}
​
/**
 * @brief 处理周期计数器
 * 
 * 遍历所有Tx PDU,递减周期计数器
 */
LOCAL_INLINE void Com_TxModeHdlr_MainFunctionTx_CycleCounter(void)
{
    Com_TxPduInfoIterType txPduId;
    
    // 进入临界区
    Com_TxModeHdlr_EnterExclusiveArea_CycleCounter();
    
    // 遍历所有Tx PDU
    for (txPduId = 0; txPduId < Com_GetSizeOfTxPduInfo(); txPduId++) {
        
        // 检查PDU是否激活
        if (Com_IsTxPduGrpActive(txPduId)) {
            
            /* 处理周期传输 */
            if (Com_TxModeHdlr_IsPeriodicTxMode(txPduId)) {
                Com_TxCyclicPendingType cycleCnt = 
                    Com_GetCycleTimeCnt(txPduId);
                
                if (cycleCnt > 0u) {
                    cycleCnt--;
                    Com_SetCycleTimeCnt(txPduId, cycleCnt);
                    
                    if (cycleCnt == 0u) {
                        // 触发发送
                        Com_SetTransmitRequest(txPduId, TRUE);
                        
                        // 重新加载周期
                        Com_TxCyclicPendingType timePeriod = 
                            Com_TxModeHdlr_GetCurrentTimePeriod(txPduId);
                        Com_SetCycleTimeCnt(txPduId, timePeriod);
                    }
                }
            }
            
            /* 处理重复发送 */
            if (Com_GetRepCnt(txPduId) > 0u) {
                Com_RepCycleCntType repCycleCnt = 
                    Com_GetRepCycleCnt(txPduId);
                
                if (repCycleCnt > 0u) {
                    repCycleCnt--;
                    Com_SetRepCycleCnt(txPduId, repCycleCnt);
                    
                    if (repCycleCnt == 0u) {
                        // 触发重复发送
                        Com_SetTransmitRequest(txPduId, TRUE);
                        Com_DecRepCnt(txPduId);
                        
                        // 重新加载重复周期
                        if (Com_GetRepCnt(txPduId) > 0u) {
                            Com_RepPeriodType repPeriod = 
                                Com_TxModeHdlr_GetCurrentRepPeriod(txPduId);
                            Com_SetRepCycleCnt(txPduId, repPeriod);
                        }
                    }
                }
            }
        }
    }
    
    // 退出临界区
    Com_TxModeHdlr_ExitExclusiveArea_CycleCounter();
}
​
/**
 * @brief 处理最小延迟时间(MDT)
 * 
 * MDT确保两次发送之间有最小的时间间隔
 */
LOCAL_INLINE void Com_TxModeHdlr_MainFunctionTx_MinimumDelay(void)
{
    Com_TxPduInfoIterType txPduId;
    
    for (txPduId = 0; txPduId < Com_GetSizeOfTxPduInfo(); txPduId++) {
        
        if (Com_IsTxPduGrpActive(txPduId)) {
            
            Com_DelayTimeCntType delayTimeCnt = 
                Com_GetDelayTimeCnt(txPduId);
            
            if (delayTimeCnt > 0u) {
                delayTimeCnt--;
                Com_SetDelayTimeCnt(txPduId, delayTimeCnt);
                
                if (delayTimeCnt == 0u) {
                    // MDT已满足,可以发送
                    if (Com_IsWaitingForConfirmation(txPduId) == FALSE) {
                        Com_SetTransmitRequest(txPduId, TRUE);
                    }
                }
            }
        }
    }
}
​
/**
 * @brief 执行实际的PDU发送
 * 
 * 将标记为需要发送的PDU发送出去
 */
LOCAL_INLINE void Com_TxModeHdlr_MainFunctionTx_Transmit(void)
{
    Com_TxPduInfoIterType txPduId;
    
    for (txPduId = 0; txPduId < Com_GetSizeOfTxPduInfo(); txPduId++) {
        
        // 检查是否有发送请求
        if (Com_IsTransmitRequest(txPduId)) {
            
            // 清除发送请求标志
            Com_SetTransmitRequest(txPduId, FALSE);
            
            // 准备PDU数据
            PduInfoType pduInfo;
            pduInfo.SduDataPtr = Com_GetAddrTxBuffer(
                Com_GetTxBufferStartIdxOfTxPduInfo(txPduId));
            pduInfo.SduLength = Com_GetTxPduLength(txPduId);
            
            // 调用PDU回调(如果配置)
            #if (COM_TXPDUCALLOUTFUNCPTRUSEDOFTXPDUINFO == STD_ON)
            if (Com_IsTxPduCalloutFuncPtrUsedOfTxPduInfo(txPduId)) {
                boolean calloutResult = Com_GetTxPduCalloutFuncPtr(
                    Com_GetTxPduCalloutFuncPtrIdxOfTxPduInfo(txPduId)
                )(txPduId, &pduInfo);
                
                if (calloutResult == FALSE) {
                    // 回调返回FALSE,取消发送
                    continue;
                }
            }
            #endif
            
            // 调用PduR发送
            Std_ReturnType result = PduR_ComTransmit(txPduId, &pduInfo);
            
            if (result == E_OK) {
                // 发送成功,设置等待确认标志
                Com_SetWaitingForConfirmation(txPduId, TRUE);
                
                // 启动MDT计时器
                #if (COM_MINIMUMDELAYOFTXMODEINFO == STD_ON)
                Com_MinimumDelayOfTxModeInfoType mdt = 
                    Com_GetMinimumDelayOfTxModeInfo(txPduId);
                Com_SetDelayTimeCnt(txPduId, mdt);
                #endif
            }
        }
    }
}

Com_MainFunctionRx - 接收调度

/**
 * @brief Com接收主函数
 * 
 * 处理接收超时监控和默认值设置
 */
void Com_MainFunctionRx(void)
{
    if (Com_GetInitialized() == COM_INIT) {
        
        /* ===== 处理接收超时 ===== */
        #if (COM_RXTOUTINFO == STD_ON)
        Com_RxDlMon_MainFunctionRx();
        #endif
        
        /* ===== 处理信号网关 ===== */
        #if (COM_GWINFO == STD_ON)
        Com_MainFunction_GwDescription();
        #endif
    }
}
​
/**
 * @brief 接收超时监控
 * 
 * 监控所有配置了超时的Rx PDU
 */
LOCAL_INLINE void Com_RxDlMon_MainFunctionRx(void)
{
    Com_RxTOutInfoIterType rxTOutInfoIdx;
    
    // 遍历所有超时配置
    for (rxTOutInfoIdx = 0; 
         rxTOutInfoIdx < Com_GetSizeOfRxTOutInfo(); 
         rxTOutInfoIdx++) {
        
        // 检查超时监控是否激活
        if (Com_LMgt_RxIpduDmActive(rxTOutInfoIdx)) {
            
            Com_RxTOutCntType timeoutCnt = 
                Com_GetRxTOutCnt(rxTOutInfoIdx);
            
            if (timeoutCnt > 0u) {
                timeoutCnt--;
                Com_SetRxTOutCnt(rxTOutInfoIdx, timeoutCnt);
                
                if (timeoutCnt == 0u) {
                    // 超时发生
                    Com_RxDlMon_TimeoutOccurred(rxTOutInfoIdx);
                }
            }
        }
    }
}
​
/**
 * @brief 处理超时事件
 * 
 * 当接收超时发生时的处理
 */
LOCAL_INLINE void Com_RxDlMon_TimeoutOccurred(
    Com_RxTOutInfoIterType rxTOutInfoIdx)
{
    // 设置超时标志
    Com_LMgt_TimeoutOccurred(rxTOutInfoIdx);
    
    // 获取关联的Rx PDU
    Com_RxPduInfoIdxOfRxTOutInfoType rxPduId = 
        Com_GetRxPduInfoIdxOfRxTOutInfo(rxTOutInfoIdx);
    
    /* 设置默认值 */
    #if (COM_RXDEFPDUBUFFERUSEDOFRXPDUINFO == STD_ON)
    if (Com_IsRxDefPduBufferUsedOfRxPduInfo(rxPduId)) {
        // 将默认值拷贝到接收缓冲区
        Com_RxDefPduBufferEndIdxOfRxPduInfoType defBufEndIdx = 
            Com_GetRxDefPduBufferEndIdxOfRxPduInfo(rxPduId);
        Com_RxDefPduBufferStartIdxOfRxPduInfoType defBufStartIdx = 
            Com_GetRxDefPduBufferStartIdxOfRxPduInfo(rxPduId);
        
        Com_RxPduBufferIterType bufIdx = 
            Com_GetRxPduBufferStartIdxOfRxPduInfo(rxPduId);
        
        for (Com_RxDefPduBufferIterType defIdx = defBufStartIdx; 
             defIdx < defBufEndIdx; 
             defIdx++, bufIdx++) {
            Com_SetRxPduBuffer(bufIdx, Com_GetRxDefPduBuffer(defIdx));
        }
    }
    #endif
    
    /* 调用超时通知回调 */
    #if (COM_CBKRXTOUTFUNCPTRINDUSEDOFRXTOUTINFO == STD_ON)
    if (Com_IsCbkRxTOutFuncPtrIndUsedOfRxTOutInfo(rxTOutInfoIdx)) {
        Com_CbkRxTOutFuncPtrIndEndIdxOfRxTOutInfoType endIdx = 
            Com_GetCbkRxTOutFuncPtrIndEndIdxOfRxTOutInfo(rxTOutInfoIdx);
        Com_CbkRxTOutFuncPtrIndStartIdxOfRxTOutInfoType startIdx = 
            Com_GetCbkRxTOutFuncPtrIndStartIdxOfRxTOutInfo(rxTOutInfoIdx);
        
        for (Com_CbkRxTOutFuncPtrIndIterType idx = startIdx; 
             idx < endIdx; 
             idx++) {
            Com_GetCbkRxTOutFuncPtr(
                Com_GetCbkRxTOutFuncPtrInd(idx)
            )();
        }
    }
    #endif
}

🔄 信号组(Signal Group)

信号组允许将多个相关信号作为一个原子单元进行发送和接收。

信号组的概念

信号组示例:发动机状态
​
SignalGroup: EngineStatus
├── Signal: EngineSpeed (转速)
├── Signal: EngineTemp (温度)
├── Signal: EnginePressure (压力)
└── Signal: EngineLoad (负载)
​
特点:
- 所有信号一起更新
- 保证数据一致性
- 使用影子缓冲区

信号组的数据结构

/**
 * @brief 信号组配置
 */
typedef struct {
    Com_TxSigGrpInfoIdxOfTxSigGrpInfoType TxSigGrpInfoIdx;  // 信号组索引
    Com_TxPduInfoIdxOfTxSigGrpInfoType    TxPduInfoIdx;     // 所属PDU
    Com_TransferPropertyOfTxSigGrpInfoType TransferProperty; // 传输属性
    Com_TxBufferEndIdxOfTxSigGrpInfoType  TxBufferEndIdx;   // 缓冲区结束
    Com_TxBufferStartIdxOfTxSigGrpInfoType TxBufferStartIdx;// 缓冲区起始
} Com_TxSigGrpInfoType;
​
/**
 * @brief 组信号配置(信号组中的信号)
 */
typedef struct {
    Com_TxSigGrpInfoIdxOfTxGrpSigInfoType TxSigGrpInfoIdx;  // 所属信号组
    Com_BitPositionOfTxGrpSigInfoType     BitPosition;      // 位位置
    Com_BitLengthOfTxGrpSigInfoType       BitLength;        // 位长度
    Com_ApplTypeOfTxGrpSigInfoType        ApplType;         // 应用类型
    // ... 更多配置
} Com_TxGrpSigInfoType;

发送信号组

/**
 * @brief 发送信号组
 * 
 * 使用影子缓冲区确保数据一致性
 */
uint8 Com_SendSignalGroup(Com_SignalGroupIdType SignalGroupId)
{
    uint8 retVal = COM_SERVICE_NOT_AVAILABLE;
    
    // 参数检查
    #if (COM_DEV_ERROR_DETECT == STD_ON)
    if (Com_GetInitialized() != COM_INIT) {
        Com_Det_ReportError(COM_SENDSIGNALGROUP_ID, COM_E_UNINIT);
        return COM_SERVICE_NOT_AVAILABLE;
    }
    
    if (SignalGroupId >= Com_GetSizeOfTxSigGrpInfo()) {
        Com_Det_ReportError(COM_SENDSIGNALGROUP_ID, COM_E_PARAM);
        return COM_SERVICE_NOT_AVAILABLE;
    }
    #endif
    
    // 获取信号组配置
    Com_TxSigGrpInfoIdxType idxTxSigGrpInfo = SignalGroupId;
    Com_TxPduInfoIdxOfTxSigGrpInfoType txPduId = 
        Com_GetTxPduInfoIdxOfTxSigGrpInfo(idxTxSigGrpInfo);
    
    if (Com_IsTxPduGrpActive(txPduId)) {
        
        SchM_Enter_Com_COM_EXCLUSIVE_AREA_TX();
        
        /* 步骤1:将影子缓冲区的数据拷贝到PDU缓冲区 */
        Com_TxBufferEndIdxOfTxSigGrpInfoType bufEndIdx = 
            Com_GetTxBufferEndIdxOfTxSigGrpInfo(idxTxSigGrpInfo);
        Com_TxBufferStartIdxOfTxSigGrpInfoType bufStartIdx = 
            Com_GetTxBufferStartIdxOfTxSigGrpInfo(idxTxSigGrpInfo);
        
        for (Com_TxBufferIterType bufIdx = bufStartIdx; 
             bufIdx < bufEndIdx; 
             bufIdx++) {
            // 从影子缓冲区拷贝到PDU缓冲区
            Com_SetTxBuffer(bufIdx, Com_GetTxSigGrpBuffer(bufIdx));
        }
        
        /* 步骤2:处理传输属性 */
        Com_TransferPropertyOfTxSigGrpInfoType transferProperty = 
            Com_GetTransferPropertyOfTxSigGrpInfo(idxTxSigGrpInfo);
        
        if (transferProperty == COM_TRIGGERED_TRANSFERPROPERTYOFTXSIGGRPINFO) {
            Com_TxModeHdlr_TriggerIpduSend(txPduId);
        }
        else if (transferProperty == COM_TRIGGERED_ON_CHANGE_TRANSFERPROPERTYOFTXSIGGRPINFO) {
            if (Com_SignalGroup_CheckValueChanged(idxTxSigGrpInfo)) {
                Com_TxModeHdlr_TriggerIpduSend(txPduId);
            }
        }
        
        SchM_Exit_Com_COM_EXCLUSIVE_AREA_TX();
        
        retVal = E_OK;
    }
    
    return retVal;
}
​
/**
 * @brief 更新影子信号
 * 
 * 在调用Com_SendSignalGroup()之前,先更新影子缓冲区中的信号
 */
void Com_UpdateShadowSignal(
    Com_SignalIdType SignalId,
    const void* SignalDataPtr)
{
    // 参数检查省略...
    
    // 获取组信号配置
    Com_TxGrpSigInfoIdxType idxTxGrpSigInfo = SignalId;
    
    // 写入影子缓冲区(而不是PDU缓冲区)
    Com_Signal_WriteSignalToShadowBuffer(idxTxGrpSigInfo, SignalDataPtr);
}

信号组使用示例

/**
 * @brief 应用层使用信号组的示例
 */
void App_SendEngineStatus(void)
{
    // 读取发动机参数
    uint16 engineSpeed = Engine_GetSpeed();
    uint8 engineTemp = Engine_GetTemperature();
    uint8 enginePressure = Engine_GetPressure();
    uint8 engineLoad = Engine_GetLoad();
    
    // 更新影子信号(不会立即发送)
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_EngineSpeed, 
                          &engineSpeed);
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_EngineTemp, 
                          &engineTemp);
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_EnginePressure, 
                          &enginePressure);
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_EngineLoad, 
                          &engineLoad);
    
    // 一次性发送整个信号组(原子操作)
    Com_SendSignalGroup(ComConf_ComSignalGroup_EngineStatus);
}

🌉 信号网关(Signal Gateway)

信号网关允许Com模块在不同的PDU之间转发信号,实现跨总线通信。

网关的应用场景

场景:车速信号从CAN1转发到CAN2
​
CAN1 (动力总线)          网关ECU          CAN2 (车身总线)
     ↓                      ↓                    ↓
[VehicleSpeed]  →  Com Gateway  →  [VehicleSpeed]
  (源信号)           (信号转换)        (目标信号)

网关配置结构

/**
 * @brief 网关信号映射配置
 */
typedef struct {
    Com_RxAccessInfoIdxOfGwSigMappingType  RxAccessInfoIdx;  // 源信号
    Com_TxSigInfoIdxOfGwSigMappingType     TxSigInfoIdx;     // 目标信号
    Com_GwDescriptionAccessInfoIdxOfGwSigMappingType GwDescAccessInfoIdx; // 网关描述
} Com_GwSigMappingType;
​
/**
 * @brief 网关描述访问信息
 */
typedef struct {
    Com_TxPduInfoIdxOfGwDescriptionAccessInfoType TxPduInfoIdx; // 目标PDU
    Com_TypeOfGwDescriptionAccessInfoType         Type;         // 网关类型
    boolean                                       Immediate;    // 立即转发
    boolean                                       OnChange;     // 值改变时转发
    boolean                                       Triggered;    // 触发转发
} Com_GwDescriptionAccessInfoType;

信号网关处理

/**
 * @brief 信号网关主函数
 * 
 * 在Com_MainFunctionTx()中被调用
 */
LOCAL_INLINE void Com_MainFunction_SignalRoutings(void)
{
    Com_GwInfoIterType gwInfoIdx;
    
    // 遍历所有网关配置
    for (gwInfoIdx = 0; gwInfoIdx < Com_GetSizeOfGwInfo(); gwInfoIdx++) {
        
        // 检查是否有待处理的网关事件
        if (Com_IsGwEventPending(gwInfoIdx)) {
            
            // 清除事件标志
            Com_SetGwEventPending(gwInfoIdx, FALSE);
            
            // 处理信号映射
            Com_GwSigMappingEndIdxOfGwInfoType mappingEndIdx = 
                Com_GetGwSigMappingEndIdxOfGwInfo(gwInfoIdx);
            Com_GwSigMappingStartIdxOfGwInfoType mappingStartIdx = 
                Com_GetGwSigMappingStartIdxOfGwInfo(gwInfoIdx);
            
            for (Com_GwSigMappingIterType mappingIdx = mappingStartIdx; 
                 mappingIdx < mappingEndIdx; 
                 mappingIdx++) {
                
                // 执行信号转发
                Com_SignalGateway_ForwardSignal(mappingIdx);
            }
        }
    }
}

/**
 * @brief 转发单个信号
 */
LOCAL_INLINE void Com_SignalGateway_ForwardSignal(
    Com_GwSigMappingIterType mappingIdx)
{
    // 获取源信号
    Com_RxAccessInfoIdxOfGwSigMappingType rxAccessInfoIdx = 
        Com_GetRxAccessInfoIdxOfGwSigMapping(mappingIdx);
    
    // 获取目标信号
    Com_TxSigInfoIdxOfGwSigMappingType txSigInfoIdx = 
        Com_GetTxSigInfoIdxOfGwSigMapping(mappingIdx);
    
    // 读取源信号值
    uint32 signalValue = Com_Signal_ReadRxSignalValue(rxAccessInfoIdx);
    
    // 可选:信号转换(缩放、偏移等)
    #if (COM_GWSIGCONVERSIONUSEDOFGWSIGMAPPING == STD_ON)
    if (Com_IsGwSigConversionUsedOfGwSigMapping(mappingIdx)) {
        signalValue = Com_SignalGateway_ConvertSignalValue(
            mappingIdx, signalValue);
    }
    #endif
    
    // 写入目标信号
    Com_Signal_WriteTxSignalValue(txSigInfoIdx, signalValue);
    
    // 获取网关描述
    Com_GwDescriptionAccessInfoIdxOfGwSigMappingType gwDescIdx = 
        Com_GetGwDescriptionAccessInfoIdxOfGwSigMapping(mappingIdx);
    
    // 根据网关类型决定是否触发发送
    if (Com_IsImmediateOfGwDescriptionAccessInfo(gwDescIdx)) {
        // 立即发送
        Com_TxPduInfoIdxOfGwDescriptionAccessInfoType txPduId = 
            Com_GetTxPduInfoIdxOfGwDescriptionAccessInfo(gwDescIdx);
        Com_TxModeHdlr_TriggerIpduSend(txPduId);
    }
}

/**
 * @brief 信号值转换
 * 
 * 支持线性转换:y = k * x + b
 */
LOCAL_INLINE uint32 Com_SignalGateway_ConvertSignalValue(
    Com_GwSigMappingIterType mappingIdx,
    uint32 inputValue)
{
    // 获取转换参数
    Com_GwSigConversionIdxOfGwSigMappingType convIdx = 
        Com_GetGwSigConversionIdxOfGwSigMapping(mappingIdx);
    
    // 线性转换:output = (input * factor) + offset
    sint32 factor = Com_GetFactorOfGwSigConversion(convIdx);
    sint32 offset = Com_GetOffsetOfGwSigConversion(convIdx);
    
    sint32 result = ((sint32)inputValue * factor) + offset;
    
    // 限制范围
    if (result < 0) {
        result = 0;
    }
    
    return (uint32)result;
}

网关使用示例

/**
 * @brief 网关配置示例
 * 
 * 将CAN1的车速信号转发到CAN2,并进行单位转换
 * CAN1: km/h (0-250)
 * CAN2: mph (0-155)
 * 转换公式: mph = km/h * 0.621371
 */

// 源信号配置(CAN1)
const Com_RxAccessInfoType Com_RxAccessInfo_VehicleSpeed_CAN1 = {
    .RxPduInfoIdx = 0,
    .BitPosition = 0,
    .BitLength = 16,
    .ApplType = COM_UINT16_APPLTYPEOFRXACCESSINFO
};

// 目标信号配置(CAN2)
const Com_TxSigInfoType Com_TxSigInfo_VehicleSpeed_CAN2 = {
    .TxPduInfoIdx = 1,
    .BitPosition = 0,
    .BitLength = 16,
    .ApplType = COM_UINT16_APPLTYPEOFTXSIGINFO
};

// 网关映射配置
const Com_GwSigMappingType Com_GwSigMapping_VehicleSpeed = {
    .RxAccessInfoIdx = 0,  // VehicleSpeed_CAN1
    .TxSigInfoIdx = 0,     // VehicleSpeed_CAN2
    .GwDescAccessInfoIdx = 0,
    .GwSigConversionUsed = TRUE,
    .GwSigConversionIdx = 0
};

// 信号转换配置
const Com_GwSigConversionType Com_GwSigConversion_KmhToMph = {
    .Factor = 621371,      // 0.621371 * 1000000 (定点数)
    .Offset = 0,
    .Divisor = 1000000     // 除数用于定点数转换
};

🎯 更新位(Update Bit)管理

更新位用于指示信号是否已更新,提高接收方的处理效率。

更新位的概念

PDU布局示例:

Byte0: [Signal1 Data]
Byte1: [Signal2 Data]
Byte2: [Signal3 Data]
Byte3: [UpdateBit3][UpdateBit2][UpdateBit1][Reserved]
       ↑           ↑           ↑
       Signal3更新  Signal2更新  Signal1更新

更新位处理

/**
 * @brief 设置更新位
 * 
 * 在信号发送时自动设置更新位
 */
LOCAL_INLINE void Com_Signal_SetUpdateBit(
    Com_TxSigInfoIdxType idxTxSigInfo)
{
    #if (COM_TXSIGINFOUPDATEBITUSEDOFTXSIGINFO == STD_ON)
    if (Com_IsTxSigInfoUpdateBitUsedOfTxSigInfo(idxTxSigInfo)) {
        
        // 获取更新位配置
        Com_UpdateBitPositionOfTxSigInfoType updateBitPos = 
            Com_GetUpdateBitPositionOfTxSigInfo(idxTxSigInfo);
        
        // 获取PDU缓冲区
        Com_TxPduInfoIdxOfTxSigInfoType txPduId = 
            Com_GetTxPduInfoIdxOfTxSigInfo(idxTxSigInfo);
        Com_TxBufferStartIdxOfTxPduInfoType bufStartIdx = 
            Com_GetTxBufferStartIdxOfTxPduInfo(txPduId);
        
        // 计算更新位的字节和位位置
        Com_TxBufferIterType byteIdx = bufStartIdx + (updateBitPos >> 3u);
        uint8 bitMask = (uint8)(1u << (updateBitPos & 0x07u));
        
        // 设置更新位
        Com_SetTxBuffer(byteIdx, Com_GetTxBuffer(byteIdx) | bitMask);
    }
    #endif
}

/**
 * @brief 清除更新位
 * 
 * 在发送确认后清除更新位
 */
LOCAL_INLINE void Com_Signal_ClearUpdateBit(
    Com_TxSigInfoIdxType idxTxSigInfo)
{
    #if (COM_TXSIGINFOUPDATEBITUSEDOFTXSIGINFO == STD_ON)
    if (Com_IsTxSigInfoUpdateBitUsedOfTxSigInfo(idxTxSigInfo)) {
        
        Com_UpdateBitPositionOfTxSigInfoType updateBitPos = 
            Com_GetUpdateBitPositionOfTxSigInfo(idxTxSigInfo);
        
        Com_TxPduInfoIdxOfTxSigInfoType txPduId = 
            Com_GetTxPduInfoIdxOfTxSigInfo(idxTxSigInfo);
        Com_TxBufferStartIdxOfTxPduInfoType bufStartIdx = 
            Com_GetTxBufferStartIdxOfTxPduInfo(txPduId);
        
        Com_TxBufferIterType byteIdx = bufStartIdx + (updateBitPos >> 3u);
        uint8 bitMask = (uint8)(1u << (updateBitPos & 0x07u));
        
        // 清除更新位
        Com_SetTxBuffer(byteIdx, Com_GetTxBuffer(byteIdx) & ~bitMask);
    }
    #endif
}

/**
 * @brief 检查更新位
 * 
 * 接收方检查信号是否已更新
 */
LOCAL_INLINE boolean Com_Signal_CheckUpdateBit(
    Com_RxAccessInfoIdxType idxRxAccessInfo)
{
    boolean result = TRUE;  // 默认认为已更新
    
    #if (COM_RXSIGINFOUPDATEBITUSEDOFRXACCESSINFO == STD_ON)
    if (Com_IsRxSigInfoUpdateBitUsedOfRxAccessInfo(idxRxAccessInfo)) {
        
        Com_UpdateBitPositionOfRxAccessInfoType updateBitPos = 
            Com_GetUpdateBitPositionOfRxAccessInfo(idxRxAccessInfo);
        
        Com_RxPduInfoIdxOfRxAccessInfoType rxPduId = 
            Com_GetRxPduInfoIdxOfRxAccessInfo(idxRxAccessInfo);
        Com_RxPduBufferStartIdxOfRxPduInfoType bufStartIdx = 
            Com_GetRxPduBufferStartIdxOfRxPduInfo(rxPduId);
        
        Com_RxPduBufferIterType byteIdx = bufStartIdx + (updateBitPos >> 3u);
        uint8 bitMask = (uint8)(1u << (updateBitPos & 0x07u));
        
        // 检查更新位
        if ((Com_GetRxPduBuffer(byteIdx) & bitMask) == 0u) {
            result = FALSE;  // 更新位未设置
        }
    }
    #endif
    
    return result;
}

🎯 完整实战案例:车辆状态监控系统

让我们通过一个完整的案例来综合运用Com模块的各种功能:

场景描述

车辆状态监控系统需要:
1. 周期发送车速信号(10ms)
2. 事件触发发送发动机状态(值改变时)
3. 使用信号组发送车身状态
4. 监控接收超时
5. 使用更新位优化处理

配置代码

/**
 * @brief 信号配置
 */

// 1. 车速信号(周期发送)
const Com_TxSigInfoType Com_TxSigInfo_VehicleSpeed = {
    .TxPduInfoIdx = 0,
    .BitPosition = 0,
    .BitLength = 16,
    .ApplType = COM_UINT16_APPLTYPEOFTXSIGINFO,
    .TransferProperty = COM_PENDING_TRANSFERPROPERTYOFTXSIGINFO,
    .UpdateBitUsed = TRUE,
    .UpdateBitPosition = 16
};

// 2. 发动机状态信号(事件触发)
const Com_TxSigInfoType Com_TxSigInfo_EngineStatus = {
    .TxPduInfoIdx = 1,
    .BitPosition = 0,
    .BitLength = 8,
    .ApplType = COM_UINT8_APPLTYPEOFTXSIGINFO,
    .TransferProperty = COM_TRIGGERED_ON_CHANGE_TRANSFERPROPERTYOFTXSIGINFO,
    .UpdateBitUsed = TRUE,
    .UpdateBitPosition = 8
};

// 3. 车身状态信号组
const Com_TxSigGrpInfoType Com_TxSigGrpInfo_BodyStatus = {
    .TxPduInfoIdx = 2,
    .TransferProperty = COM_TRIGGERED_TRANSFERPROPERTYOFTXSIGGRPINFO,
    .TxBufferStartIdx = 0,
    .TxBufferEndIdx = 8
};

/**
 * @brief PDU配置
 */

// PDU 0: 车速PDU(周期10ms)
const Com_TxPduInfoType Com_TxPduInfo_VehicleSpeed = {
    .TxPduLength = 3,  // 2字节数据 + 1字节更新位
    .TxModeInfoIdx = 0
};

const Com_TxModeInfoType Com_TxModeInfo_VehicleSpeed = {
    .TxModeTrueIdx = 0,
    .InitMode = TRUE,
    .MinimumDelay = 0
};

const Com_TxModeTrueType Com_TxModeTrue_VehicleSpeed = {
    .TransmissionMode = COM_PERIODIC_TRANSMISSIONMODEOFTXMODETRUE,
    .TimePeriod = 10,  // 10ms
    .TimeOffset = 0,
    .RepCnt = 0,
    .Periodic = TRUE,
    .Direct = FALSE
};

// PDU 1: 发动机状态PDU(事件触发)
const Com_TxPduInfoType Com_TxPduInfo_EngineStatus = {
    .TxPduLength = 2,
    .TxModeInfoIdx = 1
};

const Com_TxModeInfoType Com_TxModeInfo_EngineStatus = {
    .TxModeTrueIdx = 1,
    .InitMode = TRUE,
    .MinimumDelay = 5  // 5ms最小延迟
};

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 10ms周期任务
 */
void Task_VehicleMonitor_10ms(void)
{
    /* 1. 发送车速信号(周期) */
    uint16 vehicleSpeed = Sensor_GetVehicleSpeed();
    Com_SendSignal(ComConf_ComSignal_VehicleSpeed, &vehicleSpeed);
    
    /* 2. 发送发动机状态(事件触发) */
    static uint8 lastEngineStatus = 0;
    uint8 currentEngineStatus = Engine_GetStatus();
    
    if (currentEngineStatus != lastEngineStatus) {
        // 状态改变,触发发送
        Com_SendSignal(ComConf_ComSignal_EngineStatus, &currentEngineStatus);
        lastEngineStatus = currentEngineStatus;
    }
}

/**
 * @brief 20ms周期任务
 */
void Task_BodyMonitor_20ms(void)
{
    /* 3. 发送车身状态信号组 */
    uint8 doorStatus = Body_GetDoorStatus();
    uint8 windowStatus = Body_GetWindowStatus();
    uint8 lightStatus = Body_GetLightStatus();
    
    // 更新影子信号
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_DoorStatus, &doorStatus);
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_WindowStatus, &windowStatus);
    Com_UpdateShadowSignal(ComConf_ComGroupSignal_LightStatus, &lightStatus);
    
    // 发送信号组
    Com_SendSignalGroup(ComConf_ComSignalGroup_BodyStatus);
}

/**
 * @brief 接收超时回调
 */
void Com_CbkRxTOut_RemoteVehicleSpeed(void)
{
    // 远程车速信号超时
    // 使用默认值或进入安全状态
    App_HandleRemoteSpeedTimeout();
}

📝 本章总结

通过上下两篇,我们全面深入地学习了Com模块:

上篇回顾

  1. ✅ Com模块架构和核心职责

  2. ✅ 信号生命周期和Com_SendSignal()

  3. ✅ 位操作和信号打包(小端/大端)

  4. ✅ 传输模式(周期/事件/混合)

  5. ✅ 传输模式切换(TMS)

下篇内容

  1. ✅ Com主函数调度机制

  2. ✅ 信号组处理和影子缓冲区

  3. ✅ 信号网关和信号转换

  4. ✅ 更新位管理

  5. ✅ 完整实战案例

Com模块核心价值

Com模块作为AUTOSAR通信栈的应用层接口,提供了:

  • 📊 信号抽象: 将复杂的位操作封装成简单的API

  • 🚦 传输控制: 灵活的传输模式满足不同需求

  • 🔄 信号网关: 实现跨总线通信

  • 🛡️ 可靠性: 超时监控和默认值处理

  • 性能优化: 更新位、最小延迟时间等机制

关键技术点总结

Com模块完整功能图:

信号处理
├── 信号打包/解包
│   ├── 位操作(任意位位置)
│   ├── 字节序转换(大端/小端)
│   └── 数据类型转换
├── 信号组
│   ├── 影子缓冲区
│   └── 原子操作
└── 信号网关
    ├── 信号转发
    └── 信号转换

传输管理
├── 传输模式
│   ├── 周期传输
│   ├── 事件触发
│   └── 混合模式
├── 传输模式切换(TMS)
├── 最小延迟时间(MDT)
└── 重复发送

可靠性
├── 接收超时监控
├── 默认值处理
├── 更新位管理
└── 发送确认

调度
├── Com_MainFunctionTx()
├── Com_MainFunctionRx()
└── 周期任务管理

下一站预告: 在下一章中,我们的消息将进入PduR(PDU Router)模块,开始它的路由之旅!


继续阅读: 第二章:中转站 - PduR的智慧路由(上篇)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VehSwHwDeveloper

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值