• 正文
  • 相关推荐
申请入驻 产业图谱

手把手教你玩转CAN总线 从原理到STM32实战

04/11 11:32
2493
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、物理层深度剖析

1.1 差分信号的本质

CAN总线采用双线差分传输,核心原理图解:

markdown

CAN_H ────── ? ? ? /───────── ?
 ? ? ? ? ? ? ? ? / ?
 ? ? ? ? ? ? ___/ ?
CAN_L ──────/ ? ? ───────── ?
  • 显性状态(Dominant):CAN_H电压 ≥ 2.5V,CAN_L ≤ 1.5V → 差值≥1V

  • 隐性状态(Recessive):CAN_H/CAN_L均为2.5V → 差值≈0V

物理层参数对照表

参数 标准值 测试方法
终端电阻 120Ω ±1% 万用表直接测量
最大传输距离 10km @ ≤5Kbps 示波器+时延测试仪
波特率容差 ±1% 专用CAN分析仪
共模电压抑制 ±2V 隔离示波器测量

1.2 波特率计算公式

markdown

位时间 = 同步段 + 传播时间段 + 相位缓冲段1 + 相位缓冲段2 ?
总位数 = 同步段(SJW) + 时间段1(TS1) + 时间段2(TS2)

STM32配置示例(500Kbps):

hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; ? ?// 同步跳转宽度=1TQ
hcan1.Init.TimeSeg1 = CAN_BS1_9TQ; ? ? ? ? // 时间段1=9TQ
hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; ? ? ? ? // 时间段2=4TQ
// 总位时间=1+9+4=14TQ → 时钟频率=8MHz → TQ=0.125μs → 波特率=1/(14 * 0.125μs)=500Kbps

1.3 终端电阻调试技巧

  • 错误现象:总线波形畸变通信不稳定

  • 检测方法:

    1. 断电测量总线两端电阻(应为120Ω±5%)

    2. 上电后用示波器观察终端反射波形

  • 解决方案:

    # 终端电阻计算公式(单位Ω)
    def calc_termination_resistance(length):
     ? ?# 每米电缆约60Ω特性阻抗
     ? ?return 120 - (length * 60) / 1000 ?
    # 示例:总线长度40m → 120 - 24 = 96Ω → 需补48Ω电阻

二、数据链路层全解析(帧结构+仲裁机制)

2.1 CAN帧类型对比表

帧类型 标识符长度 用途 DLC最大值
标准帧 11位 普通数据传输 8字节
扩展帧 29位 复杂设备通信 8字节
远程帧 11/29位 请求数据 -
错误帧 - 错误通知 -

2.2 经典仲裁过程演示

场景:三个节点同时发送数据

markdown

节点A: ID=0x100 (0b000100000000)
节点B: ID=0x200 (0b001000000000)
节点C: ID=0x080 (0b000010000000)

仲裁过程

  1. 第一位:全显性 → 继续比较

  2. 第二位:A=0, B=0, C=1 → C失去仲裁权

  3. 后续位比较后,A胜出总线使用权

STM32仲裁配置要点

// 使能自动重传功能(默认开启)
hcan1.Init.AutoRetransmission = ENABLE;
// 设置重试次数(最大16次)
hcan1.Init.RetryCount = 3;

2.3 错误检测机制详解

五级错误防护体系

  1. CRC校验:15位循环冗余校验

  2. 位填充:每5个相同电平插入相反电平

  3. ACK校验:接收节点必须发送显性确认

  4. 帧格式校验:7个保留位必须为隐性

  5. 总线监控:持续检测总线逻辑电平

错误计数器动态调整算法

markdown

当检测到错误时:
TEC += 8(发送错误)或 REC += 1(接收错误)
当TEC > 127时:进入总线关闭状态

2.4 位时间同步技术

同步机制

  • 硬同步:在帧起始位强制对齐

  • 重新同步:通过调整时间段2补偿时钟偏差

STM32时间参数配置示例

// 配置同步跳转宽度为1个时间量子
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
?
// 时间段分配(假设系统时钟16MHz)
CAN_BtrTypeDef sCanBtr;
sCanBtr.SyncJumpWidth = CAN_SJW_1TQ;
sCanBtr.TimeSeg1 = CAN_BS1_9TQ; ?// 传播延迟补偿
sCanBtr.TimeSeg2 = CAN_BS2_4TQ; ?// 相位缓冲

三、数据链路层核心机制

3.1 CAN协议栈全景图

应用层CANopen/J1939)
 ? ↓
网络层(路由/错误处理)
 ? ↓
数据链路层(帧结构/仲裁)
 ? ↓
物理层(差分信号/终端电阻)

3.2 帧结构深度拆解

标准帧格式(11位ID)

| 仲裁场(11b) | 控制场(6b) | 数据场(0-8B) | CRC场(15b) | ACK场(1b) | 帧结束(7b) |
  • 仲裁场:包含节点ID和帧类型标识

  • 控制场:DLC(数据长度码) + IDE(扩展标识符)

  • CRC场:15位循环冗余校验(生成多项式:x??+x??+...+1)

STM32 CRC配置示例

// CAN1 CRC初始化
hcan1.Instance->CRCD = 0xFFFF; ? ?// 初始值
hcan1.Instance->CRCSA = 0x0000; ? // 起始地址

3.3 仲裁机制详解

29位扩展帧仲裁过程

优先级位 → 源地址 → 参数组号(PGN)
  • 优先级计算:ID31-ID26位决定(数值越小优先级越高)

  • 源地址冲突检测:同一网络内节点地址必须唯一

仲裁时序仿真

def can_arbitration(id_list):
 ? ?sorted_ids = sorted(id_list, key=lambda x: bin(x).count('1'))
 ? ?return sorted_ids[0]
?
# 示例:三个节点同时发送
nodes = [0x18FEF100, 0x18FEF200, 0x18FEF300]
winner = can_arbitration(nodes) ?# 输出0x18FEF100

四、CANopen协议深度实战

4.1 对象字典(Object Dictionary)

OD结构示例

索引 ? ? ?  类型 ? ? ?  描述
0x2000 ? ? ARRAY ? ? ? 电机控制参数
0x2000[0]  UINT16 ? ?  目标转速(rpm)
0x2000[1]  FLOAT ? ? ? 加速度(m/s?)
0x2001 ? ? RECORD ? ?  故障代码
0x2001[0]  BITFIELD ?  故障标志位

STM32 SDO传输实现

// SDO客户端上传数据
void SDO_Upload(uint16_t index, uint8_t subindex) {
 ? ?CO_SDO_Req req;
 ? ?CO_SDO_ReqInit(&req);
 ? ?req.Cmd = CO_SDO_CMD_UPLOAD_REQ;
 ? ?req.Index = index;
 ? ?req.SubIndex = subindex;
 ? ?
 ? ?if (CO_SDO_Transmit(&req) == CO_SDO_OK) {
 ? ? ? ?Process_SDO_Response(req.Data);
 ?  }
}

4.2 NMT网络管理

状态迁移图

INIT → PRE-OPERATIONAL → OPERATIONAL → STOPPED
 ? ↑ ? ? ?  ↑ ? ? ? ? ? ? ? ? ?  ↓
 ? └──RESET←───────────────────┘

心跳报文配置

// 心跳生产者配置
CO_NMT_HeartbeatConfig(0x01, 0x00, 500); ?// 节点ID=1,周期500ms

五、J1939协议核心要点

5.1 参数组号(PGN)编码规则

PGN = PF(8b) << 8 | PS(8b)
PF: 参数组功能(0-255)
PS: 参数组子功能(0-255)

典型PGN解析

PGN PF PS 描述
0xFEFC 0xFE 0xFC 发动机转速请求
0xFEF0 0xFE 0xF0 冷却液温度
0xFECA 0xFE 0xCA 车辆位置报告

5.2 多包数据传输

传输流程

请求 → 确认 → 数据包1 → 数据包2 → ... → 结束符

STM32多包发送实现

// 多包数据发送(最大12字节/包)
void CAN_Send_MultiPacket(uint8_t *data, uint16_t length) {
 ? ?uint8_t packets[6][8] = {0};
 ? ?uint8_t packet_count = (length + 7) / 8;
 ? ?
 ? ?for (int i=0; i<packet_count; i++) {
 ? ? ? ?packets[i][0] = 0x00; ?// 流控制字段
 ? ? ? ?memcpy(&packets[i][1], &data[i*8], 8);
 ? ? ? ?CAN_TransmitPacket(packets[i]);
 ?  }
}

六、STM32HAL库实战进阶

6.1 完整初始化流程

// 1. GPIO配置(CubeMX生成)
void MX_GPIO_Init(void)
{
 ?GPIO_InitTypeDef GPIO_InitStruct = {0};
 ?__HAL_RCC_GPIOB_CLK_ENABLE();
 ?
 ?// CAN_RX/TX引脚配置
 ?GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
 ?GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
 ?GPIO_InitStruct.Pull = GPIO_PULLUP;
 ?GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
 ?GPIO_InitStruct.Alternate = GPIO_AF9_CAN1;
 ?HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
?
// 2. CAN初始化(含过滤器配置)
void MX_CAN1_Init(void)
{
 ?CAN_HandleTypeDef hcan1;
 ?
 ?hcan1.Instance = CAN1;
 ?hcan1.Init.Prescaler = 5; ? ? ? ? // 500Kbps
 ?hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
 ?hcan1.Init.TimeSeg1 = CAN_BS1_9TQ;
 ?hcan1.Init.TimeSeg2 = CAN_BS2_4TQ;
 ?hcan1.Init.Mode = CAN_MODE_NORMAL;
 ?
 ?if (HAL_CAN_Init(&hcan1) != HAL_OK) {
 ? ?Error_Handler();
  }
 ?
 ?// 滤波器配置(接收ID=0x100-0x1FF)
 ?CAN_FilterTypeDef sFilterConfig = {0};
 ?sFilterConfig.FilterBank = 0;
 ?sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
 ?sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
 ?sFilterConfig.FilterIdHigh = 0x100 << 13;
 ?sFilterConfig.FilterIdLow = 0x1FF << 13 | 0xFFFF;
 ?HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig);
}

6.2 数据收发实战

// 数据发送(PDO模拟)
void CAN_Send_PDO(uint8_t node_id, uint16_t position) {
 ?CAN_TxHeaderTypeDef TxHeader = {0};
 ?uint8_t TxData[8] = {0};
 ?
 ?TxHeader.StdId = 0x200 + node_id; ?// PDO ID
 ?TxHeader.IDE = CAN_ID_STD;
 ?TxHeader.DLC = 2;
 ?
 ?TxData[0] = (position >> 8) & 0xFF;
 ?TxData[1] = position & 0xFF;
 ?
 ?HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
}
?
// 接收回调(带错误检测)
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
 ?CAN_RxHeaderTypeDef RxHeader;
 ?uint8_t RxData[8] = {0};
 ?
 ?if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK) {
 ? ?if (RxHeader.DLC != 2) {
 ? ? ?// 数据长度异常处理
 ? ? ?return;
 ?  }
 ? ?uint16_t value = (RxData[0] << 8) | RxData[1];
 ? ?Process_Sensor_Data(value);
  }
}

七、工业级应用案例解析

7.1 电动汽车三电系统

通信拓扑

BMS → CAN → MCU → CAN → 电机控制器
 ? ? ? ? ? ↑↓
 ? ? ? ? 充电桩

7.2 智能仓储机器人

  • 多机协同:50+台AGV通过CAN总线同步路径规划

  • 实时监控:电量/故障状态实时上报

  • 抗干扰方案:

    • 双绞线屏蔽层接地

    • 隔离收发器(如ADuM1201)

    • 冗余帧重传机制

八、调试与优化技巧

1.示波器观察

  • 检查CAN_H/CAN_L差分波形(正常应为方波

  • 波特率验证(500Kbps对应周期2μs)

2.错误分析

  • 错误帧计数:HAL_CAN_GetError(&hcan1)

  • 总线负载率:CAN总线分析仪检测

3.性能优化

  • 使用CAN FD(Flexible Data Rate)提升带宽

  • 优化过滤器配置减少CPU开销

  • 采用环形缓冲区处理高频率数据

九、扩展学习资源

  • 经典CAN vs CAN FD:带宽从1Mbps提升至5Mbps

  • AUTOSAR架构:标准化汽车软件架构

  • TSN时间敏感网络工业4.0通信新标准

相关推荐