找回密码
 立即注册
首页 业界区 安全 CAN通信协议

CAN通信协议

碣滥 2025-6-10 09:38:55

 ​一、基础概念​

  • 物理连接​:

    • TX/RX无需交叉连接(高速CAN默认)
    • 采用差分信号传输(抗干扰性强)

  • 通信方式​:

    • 广播式通信:发送方主动广播数据(数据帧)
    • 请求式通信:接收方主动请求数据(遥控帧)
    • 接收方通过拉低总线电平(显性电平0)确认接收(类似停止位)

  • 关键术语​:

    • DLC​:数据长度码(1~8字节)
    • IDE​:标识符扩展位(0=标准帧11位ID,1=扩展帧29位ID)
    • RTR​:远程传输请求位(0=数据帧,1=遥控帧)
    • r0/r1​:保留位(为协议升级预留)

​二、帧类型与结构​
帧类型
用途
数据帧
主动广播数据(发送设备→所有设备)
遥控帧
请求数据(接收设备→发送设备,无数据段)
错误帧
设备检测错误时通知总线(破坏当前通信)
过载帧
接收设备未准备好时延缓发送方
帧间隔
分隔数据帧/遥控帧与其他帧
​数据帧结构​(标准格式):
字段
功能
SOF (帧起始)
显性电平0,标志传输开始
ID (标识符)
11位,区分功能并决定优先级(值越小优先级越高)
RTR
0=数据帧
IDE
0=标准帧
DLC
4位,指定数据段长度(0~8字节)
Data
实际数据(0~8字节)
CRC
15位循环冗余校验 + 1位界定符(隐性1)
ACK
发送方发隐性1,接收方拉低为显性0确认
EOF (帧结束)
7位隐性1,标志传输结束
扩展帧差异​:

  • 29位ID(11位基础ID + 18位扩展ID)
  • 新增SRR位(固定隐性1,保证标准帧优先)
​三、核心机制​

  • 非破坏性仲裁​:

    • 线与特性​:总线显性0覆盖隐性1(0 & x = 0,1 & 1 = 1)
    • 回读机制​:设备发送后回读总线电平,确认是否发送成功
    • 仲裁规则​:

      • 逐位比较ID,显性0优先级高
      • ID相同时:数据帧 > 遥控帧,标准帧 > 扩展帧


  • 位填充规则​:

    • 发送方:连续5个相同电平后插入一个反向电平(填充位)
    • 接收方:移除填充位恢复原始数据
    • 作用​:提供定时同步、区分错误帧/过载帧、保持总线活跃

  • 再同步机制​:

    • 位时间划分:同步段(SS) + 传播段(PTS) + 相位缓冲段1(PBS1) + 相位缓冲段2(PBS2)
    • 接收方通过调整PBS1/PBS2(±SJW值)对齐采样点

​四、错误处理​

  • 错误类型​:
错误类型
触发条件
位错误
发送电平与总线电平不一致
填充错误
连续检测到6个相同电平(违反填充规则)
CRC错误
接收方计算的CRC与帧内CRC不匹配
格式错误
固定格式字段(如界定符、EOF)电平错误
ACK错误
发送方未检测到ACK显性0(无设备接收)

  • 错误状态转换​:
状态
行为
主动错误
可正常通信,检测错误时发送主动错误帧(破坏总线)
被动错误
可正常通信,检测错误时发送被动错误帧(不破坏总线)
总线关闭
禁止通信(需检测128次连续11位隐性1恢复)

  • 错误计数器​:

    • TEC​(发送错误计数器):错误发送时增加,成功发送时减少
    • REC​(接收错误计数器):接收错误时增加,成功接收时减少
    • 状态转换阈值:

      • TEC/REC < 128 → 主动错误
      • TEC/REC ≥ 128 → 被动错误
      • TEC > 255 → 总线关闭


​五、物理层特性​

  • 无时钟线​:设备约定波特率,通过位时序同步
  • 采样点​:位于数据位中心附近(通过再同步调整)
  • 总线空闲​:连续11位隐性1(设备仅此时可发起发送)
​六、STM32 CAN外设简介​

  • 特性​:

    • 支持CAN 2.0A/B
    • 最高波特率1 Mbps
    • 3个发送邮箱(可配置优先级)
    • 2个接收FIFO(3级深度)
    • 14个过滤器组(互联型28个)

  • 关键功能​:

    • 自动重传控制
    • 时间触发通信模式
    • 自动离线恢复
    • 接收FIFO溢出处理可配置

​关键总结表​
​机制​
​核心规则​
优先级仲裁
ID值小者优先 → 数据帧 > 遥控帧 → 标准帧 > 扩展帧
位填充
连续5相同电平 → 插入1反向电平
总线空闲判定
连续11位隐性1
错误状态切换
TEC/REC ≥ 128 → 被动错误;TEC > 255 → 总线关闭
ACK机制
发送方发1,接收方拉低为0确认
CAN总线仲裁机制示例
下面是一个用C语言实现的简化版CAN总线仲裁模拟程序,配合波形图解释CAN总线的核心机制。
[code]  1 #include   2   3 #include   4   5 #include   6   7 #include   8   9 #include  10  11 // CAN节点结构体 12  13 typedef struct { 14  15 int id; // 节点ID 16  17 bool active; // 节点是否活跃 18  19 int state; // 0:发送显性位, 1:发送隐性位 20  21 int bit_index; // 当前发送的位索引 22  23 char frame[14]; // 要发送的帧(SOF+ID+RTR+IDE+控制段) 24  25 } CAN_Node; 26  27 // 总线状态枚举 28  29 typedef enum { 30  31 RECESSIVE, // 隐性 (逻辑1) 32  33 DOMINANT // 显性 (逻辑0) 34  35 } BusState; 36  37 // 模拟CAN总线 38  39 void simulate_can_bus() { 40  41 // 创建三个节点 42  43 CAN_Node nodes[3]; 44  45  46  47 // 初始化节点 (ID越小优先级越高) 48  49 nodes[0] = (CAN_Node){0x123, true, -1, 0, {0,0,0,1,0,0,1,0,0,0,1,1,0,0}}; // ID: 0x123 50  51 nodes[1] = (CAN_Node){0x124, true, -1, 0, {0,0,0,1,0,0,1,0,0,1,0,0,0,0}}; // ID: 0x124 52  53 nodes[2] = (CAN_Node){0x122, true, -1, 0, {0,0,0,1,0,0,1,0,0,0,1,0,0,0}}; // ID: 0x122 (最高优先级) 54  55  56  57 printf("CAN总线仲裁模拟 - 节点ID: 0x%X, 0x%X, 0x%X\n\n", 58  59 nodes[0].id, nodes[1].id, nodes[2].id); 60  61  62  63 BusState bus = RECESSIVE; 64  65 bool arbitration_complete = false; 66  67 int winner_id = -1; 68  69  70  71 // 逐位模拟总线传输 72  73 for (int bit_pos = 0; bit_pos < 14; bit_pos++) { 74  75 // 重置总线为隐性 76  77 bus = RECESSIVE; 78  79  80  81 // 每个节点发送当前位 82  83 for (int i = 0; i < 3; i++) { 84  85 if (nodes.active && bit_pos < strlen(nodes.frame)) { 86  87 // 发送显性位会覆盖隐性位 88  89 if (nodes.frame[bit_pos] == '0') { 90  91 bus = DOMINANT; 92  93 } 94  95 } 96  97 } 98  99 100 101 // 节点检测总线状态102 103 for (int i = 0; i < 3; i++) {104 105 if (nodes.active && bit_pos < strlen(nodes.frame)) {106 107 // 回读机制:比较发送值与总线实际值108 109 if (nodes.frame[bit_pos] == '1' && bus == DOMINANT) {110 111 // 仲裁失败:发送隐性位但读到显性位112 113 nodes.active = false;114 115 printf("位 %2d: 节点 0x%X 仲裁失败\n", bit_pos, nodes.id);116 117 }118 119 }120 121 }122 123 124 125 // 检查仲裁是否完成126 127 int active_count = 0;128 129 for (int i = 0; i < 3; i++) {130 131 if (nodes.active) {132 133 active_count++;134 135 winner_id = nodes.id;136 137 }138 139 }140 141 142 143 // 打印当前总线状态144 145 printf("位 %2d: 总线状态: %s | 发送节点: ",146 147 bit_pos, bus == DOMINANT ? "显性(0)" : "隐性(1)");148 149 150 151 for (int i = 0; i < 3; i++) {152 153 if (nodes.active && bit_pos < strlen(nodes.frame)) {154 155 printf("0x%X:%c ", nodes.id, nodes.frame[bit_pos]);156 157 }158 159 }160 161 printf("\n");162 163 164 165 // 仲裁完成判断166 167 if (!arbitration_complete && active_count == 1) {168 169 printf("\n>>> 仲裁完成! 胜出节点: 0x%X
您需要登录后才可以回帖 登录 | 立即注册