电子说
CAN总线采用****双线差分传输 ,核心原理图解:
markdown
CAN_H ────── /─────────
/
___/
CAN_L ──────/ ─────────
物理层参数对照表 :
| 参数 | 标准值 | 测试方法 |
|---|---|---|
| 终端电阻 | 120Ω ±1% | 万用表直接测量 |
| 最大传输距离 | 10km @ ≤5Kbps | 示波器+时延测试仪 |
| 波特率容差 | ±1% | 专用CAN分析仪 |
| 共模电压抑制 | ±2V | 隔离示波器测量 |
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
# 终端电阻计算公式(单位Ω)
def calc_termination_resistance(length):
# 每米电缆约60Ω特性阻抗
return 120 - (length * 60) / 1000
# 示例:总线长度40m → 120 - 24 = 96Ω → 需补48Ω电阻
| 帧类型 | 标识符长度 | 用途 | DLC最大值 |
|---|---|---|---|
| 标准帧 | 11位 | 普通数据传输 | 8字节 |
| 扩展帧 | 29位 | 复杂设备通信 | 8字节 |
| 远程帧 | 11/29位 | 请求数据 | - |
| 错误帧 | - | 错误通知 | - |
场景 :三个节点同时发送数据
markdown
节点A: ID=0x100 (0b000100000000)
节点B: ID=0x200 (0b001000000000)
节点C: ID=0x080 (0b000010000000)
仲裁过程 :
STM32仲裁配置要点 :
// 使能自动重传功能(默认开启)
hcan1.Init.AutoRetransmission = ENABLE;
// 设置重试次数(最大16次)
hcan1.Init.RetryCount = 3;
五级错误防护体系 :
错误计数器动态调整算法 :
markdown
当检测到错误时:
TEC += 8(发送错误)或 REC += 1(接收错误)
当TEC > 127时:进入总线关闭状态
同步机制 :
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; // 相位缓冲
应用层(CANopen/J1939)
↓
网络层(路由/错误处理)
↓
数据链路层(帧结构/仲裁)
↓
物理层(差分信号/终端电阻)
标准帧格式(11位ID) :
| 仲裁场(11b) | 控制场(6b) | 数据场(0-8B) | CRC场(15b) | ACK场(1b) | 帧结束(7b) |
STM32 CRC配置示例 :
// CAN1 CRC初始化
hcan1.Instance- >CRCD = 0xFFFF; // 初始值
hcan1.Instance- >CRCSA = 0x0000; // 起始地址
29位扩展帧仲裁过程 :
优先级位 → 源地址 → 参数组号(PGN)
仲裁时序仿真 :
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
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);
}
}
状态迁移图 :
INIT → PRE-OPERATIONAL → OPERATIONAL → STOPPED
↑ ↑ ↓
└──RESET←───────────────────┘
心跳报文配置 :
// 心跳生产者配置
CO_NMT_HeartbeatConfig(0x01, 0x00, 500); // 节点ID=1,周期500ms
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 | 车辆位置报告 |
传输流程 :
请求 → 确认 → 数据包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]);
}
}
// 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);
}
// 数据发送(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);
}
}
通信拓扑 :
BMS → CAN → MCU → CAN → 电机控制器
↑↓
充电桩
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !