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

系列: 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模块的核心职责

  1. 信号抽象 📊

    • 将应用层的信号(Signal)打包成PDU

    • 将接收的PDU解包成信号

    • 处理不同的数据类型和字节序

  2. 传输控制 🚦

    • 管理信号的发送时机(周期/事件)

    • 控制传输模式切换

    • 处理最小延迟时间(MDT)

  3. 信号网关 🌉

    • 在不同总线间转发信号

    • 信号格式转换

    • 信号过滤和映射

  4. 可靠性保障 🛡️

    • 接收超时监控

    • 更新位(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;
}

📝 本章小结(上篇)

在这一章的上篇中,我们深入学习了:

  1. Com模块架构: 分层设计和核心职责

  2. 数据结构: 信号配置、PDU配置、传输模式配置

  3. 信号生命周期: 从应用层到Com模块的完整流程

  4. Com_SendSignal(): 详细的实现和参数检查

  5. 位操作: 小端和大端格式的信号打包

  6. 传输模式: 周期、事件触发、混合模式

  7. 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模块深度解析(下篇)

随着人类对生命健康需求的不断增长,新药研发面临着前所未有的挑战。传统的药物研发流程通常耗时长达十年以上,耗资数十亿美元,且最终成功率极低,这在制药界被称为“反摩尔定律”困境。近年来,人工智能技术的飞速发展,特别是深度学习和大数据分析的广泛应用,为新药发现带来了革命性的契机。人工智能能够从海量的化学和生物数据中挖掘潜在规律,显著加速药物靶点发现、先导化合物优化等关键环节。在此背景下,本研究旨在设计并实现一个基于人工智能的新药发现辅助系统,以期为传统药物研发流程提供高效的智能化辅助工具,从而有效缩短研发周期并大幅降低研发成本。本研究以Python作为主要开发语言,深度结合PyTorch和TensorFlow两大主流深度学习框架,并集成RDKit化学信息学工具包,构建了一个功能完善的新药发现辅助系统。系统的核心目标是利用先进的人工智能技术辅助新药分子的设计与活性评估。在研究方法上,本文创新性地提出了一种融合多模态数据的新药发现算法。该算法综合处理分子的多种表示形式,包括一维的SMILES序列、二维的分子图结构以及三维的空间构象数据。通过构建多通道神经网络,系统能够有效提取并融合不同模态的特征,从而全面捕捉分子的理化性质与生物学活性之间的复杂非线性关系。 【课程报告内容】 摘要 第1章 绪论 第2章 相关技术与理论 第3章 系统需求分析 第4章 系统总体设计 第5章 系统详细设计与实现 第6章 系统测试与分析 第7章 总结与展望 参考文献 附件-实现指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

VehSwHwDeveloper

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

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

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

打赏作者

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

抵扣说明:

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

余额充值