一、CAN总线拓扑结构深度解析

1. CAN节点核心组成

CAN节点的核心硬件单元由MCU内核、CAN控制器、CAN收发器三部分构成:

  • MCU内核:作为节点的控制核心,负责CAN控制器的初始化配置、报文处理逻辑及上层业务调度;
  • CAN控制器:集成于MCU内部,是CAN通信的逻辑核心,负责报文的封装、解包、错误检测及位时序控制;
  • CAN收发器:独立于MCU的板级组件,负责CAN控制器数字信号与CAN总线差分电平的转换,是物理层的核心载体。

2. 通信流程分层实现

CAN通信的数据流遵循“内核→控制器→收发器→总线”的分层传递逻辑:

  1. 初始化阶段:MCU内核向CAN控制器加载波特率、采样点、工作模式等配置参数;
  2. 报文发送:上层调用Can_Write接口,CAN控制器将数据封装为标准CAN帧,通过CAN_Tx引脚发送至收发器;
  3. 信号转换:收发器将0/1数字信号转换为CAN_HCAN_L的差分电平(显性/隐性电平);
  4. 总线传输:差分信号通过双绞线传输至总线,实现多节点共享通信介质。

3. AUTOSAR架构映射

在AUTOSAR标准中,CAN节点的硬件组件对应明确的软件分层:

  • 数据链路层:由CAN控制器(CanIf模块)实现,负责帧格式、仲裁逻辑及错误处理;
  • 物理层:由CAN收发器(CanTrcv模块)实现,负责电平转换、差分驱动及总线保护。

4. 网络拓扑与典型应用

CAN总线采用线性拓扑结构,多个ECU(电子控制单元)通过CAN_HCAN_L双绞线并联,总线两端需接入120Ω终端电阻以匹配阻抗,减少信号反射。典型车载CAN网络包含:

  • 动力域:发动机ECU、变速箱TCU;
  • 底盘域:ABS防抱死ECU、ESP车身稳定ECU;
  • 车身域:BCM车身控制模块、车门控制单元;
  • 信息娱乐域:中控主机、仪表ECU。

二、代码示例:基于STM32 LL库的CAN通信实现

1. CAN控制器初始化

#include "stm32f4xx_ll_can.h"
#include "stm32f4xx_ll_rcc.h"
#include "stm32f4xx_ll_gpio.h"

// CAN1初始化函数
void CAN1_Init(void) {
  // 使能CAN1和GPIOA时钟
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_CAN1);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  
  // 配置CAN引脚:PA11(CAN_RX)、PA12(CAN_TX)
  LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_11, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_11, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_11, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_11, LL_GPIO_PULL_UP);
  LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_11, LL_GPIO_AF_9);
  
  LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_12, LL_GPIO_MODE_ALTERNATE);
  LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_12, LL_GPIO_SPEED_FREQ_HIGH);
  LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_12, LL_GPIO_OUTPUT_PUSHPULL);
  LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_12, LL_GPIO_PULL_UP);
  LL_GPIO_SetAFPin_8_15(GPIOA, LL_GPIO_PIN_12, LL_GPIO_AF_9);
  
  // 配置CAN控制器
  LL_CAN_SetMode(CAN1, LL_CAN_MODE_INITIALIZATION);
  LL_CAN_SetPrescaler(CAN1, 6); // 波特率:APB1时钟42MHz / (6*(1+12+2)) = 500Kbps
  LL_CAN_SetTimeSeg1(CAN1, LL_CAN_TIME_SEG1_12TQ);
  LL_CAN_SetTimeSeg2(CAN1, LL_CAN_TIME_SEG2_2TQ);
  LL_CAN_SetSyncJumpWidth(CAN1, LL_CAN_SYNC_JUMP_WIDTH_1TQ);
  LL_CAN_EnableAutoBusOff(CAN1);
  LL_CAN_EnableAutoRetransmission(CAN1);
  
  // 配置滤波器:接收所有标准帧
  LL_CAN_SetFilterMode(CAN1, LL_CAN_FILTER_MODE_MASK);
  LL_CAN_SetFilterScale(CAN1, LL_CAN_FILTER_SCALE_32BIT);
  LL_CAN_SetFilterBank(CAN1, 0);
  LL_CAN_SetFilterFIFOAssignment(CAN1, LL_CAN_FILTER_FIFO0);
  LL_CAN_EnableFilter(CAN1, 0);
  
  // 进入正常工作模式
  LL_CAN_SetMode(CAN1, LL_CAN_MODE_NORMAL);
}

2. CAN报文发送

// 发送标准CAN报文
void CAN1_SendMessage(uint32_t id, uint8_t *data, uint8_t len) {
  // 等待邮箱空闲
  while(LL_CAN_IsTransmitMailboxPending(CAN1, LL_CAN_TX_MAILBOX0));
  
  // 配置报文头
  LL_CAN_SetTxIdentifier(CAN1, LL_CAN_TX_MAILBOX0, id);
  LL_CAN_SetTxIde(CAN1, LL_CAN_TX_MAILBOX0, LL_CAN_STANDARD_ID);
  LL_CAN_SetTxRtr(CAN1, LL_CAN_TX_MAILBOX0, LL_CAN_DATA_FRAME);
  LL_CAN_SetTxDlc(CAN1, LL_CAN_TX_MAILBOX0, len);
  
  // 加载数据
  for(uint8_t i=0; i<len; i++) {
    LL_CAN_SetTxData(CAN1, LL_CAN_TX_MAILBOX0, i, data[i]);
  }
  
  // 发送报文
  LL_CAN_TransmitMessage(CAN1, LL_CAN_TX_MAILBOX0);
}

3. AUTOSAR风格的报文发送

#include "CanIf.h"
#include "PduR.h"

// 应用层发送信号示例
Std_ReturnType App_SendEngineSpeed(uint16_t speed) {
  PduInfoType pduInfo;
  uint8_t data[2];
  
  // 封装信号到PDU
  data[0] = (uint8_t)(speed >> 8);
  data[1] = (uint8_t)(speed & 0xFF);
  
  pduInfo.SduDataPtr = data;
  pduInfo.SduLength = 2;
  
  // 通过CanIf发送PDU
  return CanIf_Transmit(PDUR_PDUID_ENGINE_SPEED, &pduInfo);
}

// CanIf模块的发送实现
Std_ReturnType CanIf_Transmit(PduIdType TxPduId, const PduInfoType *PduInfoPtr) {
  Can_HwHandleType hth = CanIf_GetHth(TxPduId);
  Can_PduType pdu;
  
  pdu.id = CanIf_GetCanId(TxPduId);
  pdu.dlc = PduInfoPtr->SduLength;
  pdu.sdu = PduInfoPtr->SduDataPtr;
  
  // 调用底层Can驱动发送
  return Can_Write(hth, &pdu);
}

 

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐