系列: 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, ¤tEngineStatus);
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模块:
上篇回顾
-
✅ Com模块架构和核心职责
-
✅ 信号生命周期和Com_SendSignal()
-
✅ 位操作和信号打包(小端/大端)
-
✅ 传输模式(周期/事件/混合)
-
✅ 传输模式切换(TMS)
下篇内容
-
✅ Com主函数调度机制
-
✅ 信号组处理和影子缓冲区
-
✅ 信号网关和信号转换
-
✅ 更新位管理
-
✅ 完整实战案例
Com模块核心价值
Com模块作为AUTOSAR通信栈的应用层接口,提供了:
-
📊 信号抽象: 将复杂的位操作封装成简单的API
-
🚦 传输控制: 灵活的传输模式满足不同需求
-
🔄 信号网关: 实现跨总线通信
-
🛡️ 可靠性: 超时监控和默认值处理
-
⚡ 性能优化: 更新位、最小延迟时间等机制
关键技术点总结
Com模块完整功能图:
信号处理
├── 信号打包/解包
│ ├── 位操作(任意位位置)
│ ├── 字节序转换(大端/小端)
│ └── 数据类型转换
├── 信号组
│ ├── 影子缓冲区
│ └── 原子操作
└── 信号网关
├── 信号转发
└── 信号转换
传输管理
├── 传输模式
│ ├── 周期传输
│ ├── 事件触发
│ └── 混合模式
├── 传输模式切换(TMS)
├── 最小延迟时间(MDT)
└── 重复发送
可靠性
├── 接收超时监控
├── 默认值处理
├── 更新位管理
└── 发送确认
调度
├── Com_MainFunctionTx()
├── Com_MainFunctionRx()
└── 周期任务管理
下一站预告: 在下一章中,我们的消息将进入PduR(PDU Router)模块,开始它的路由之旅!
继续阅读: 第二章:中转站 - PduR的智慧路由(上篇) →
&spm=1001.2101.3001.5002&articleId=157592069&d=1&t=3&u=34a0b4589e2e4d4b9107d75108ee05cd)
513

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



