1.1 差分信號(hào)的本質(zhì)
markdown
CAN_H ────── ? ? ? /───────── ?
? ? ? ? ? ? ? ? / ?
? ? ? ? ? ? ___/ ?
CAN_L ──────/ ? ? ───────── ?
-
顯性狀態(tài)(Dominant):CAN_H電壓 ≥ 2.5V,CAN_L ≤ 1.5V → 差值≥1V
-
隱性狀態(tài)(Recessive):CAN_H/CAN_L均為2.5V → 差值≈0V
物理層參數(shù)對(duì)照表:
參數(shù) | 標(biāo)準(zhǔn)值 | 測(cè)試方法 |
---|---|---|
終端電阻 | 120Ω ±1% | 萬(wàn)用表直接測(cè)量 |
最大傳輸距離 | 10km @ ≤5Kbps | 示波器+時(shí)延測(cè)試儀 |
波特率容差 | ±1% | 專用CAN分析儀 |
共模電壓抑制 | ±2V | 隔離示波器測(cè)量 |
1.2 波特率計(jì)算公式
markdown
位時(shí)間 = 同步段 + 傳播時(shí)間段 + 相位緩沖段1 + 相位緩沖段2 ?
總位數(shù) = 同步段(SJW) + 時(shí)間段1(TS1) + 時(shí)間段2(TS2)
STM32配置示例(500Kbps):
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; ? ?// 同步跳轉(zhuǎn)寬度=1TQ
hcan1.Init.TimeSeg1 = CAN_BS1_9TQ; ? ? ? ? // 時(shí)間段1=9TQ
hcan1.Init.TimeSeg2 = CAN_BS2_4TQ; ? ? ? ? // 時(shí)間段2=4TQ
// 總位時(shí)間=1+9+4=14TQ → 時(shí)鐘頻率=8MHz → TQ=0.125μs → 波特率=1/(14 * 0.125μs)=500Kbps
1.3 終端電阻調(diào)試技巧
-
檢測(cè)方法:
-
斷電測(cè)量總線兩端電阻(應(yīng)為120Ω±5%)
-
上電后用示波器觀察終端反射波形
-
-
解決方案:
# 終端電阻計(jì)算公式(單位Ω) def calc_termination_resistance(length): ? ?# 每米電纜約60Ω特性阻抗 ? ?return 120 - (length * 60) / 1000 ? # 示例:總線長(zhǎng)度40m → 120 - 24 = 96Ω → 需補(bǔ)48Ω電阻
二、數(shù)據(jù)鏈路層全解析(幀結(jié)構(gòu)+仲裁機(jī)制)
2.1 CAN幀類型對(duì)比表
幀類型 | 標(biāo)識(shí)符長(zhǎng)度 | 用途 | DLC最大值 |
---|---|---|---|
標(biāo)準(zhǔn)幀 | 11位 | 普通數(shù)據(jù)傳輸 | 8字節(jié) |
擴(kuò)展幀 | 29位 | 復(fù)雜設(shè)備通信 | 8字節(jié) |
遠(yuǎn)程幀 | 11/29位 | 請(qǐng)求數(shù)據(jù) | - |
錯(cuò)誤幀 | - | 錯(cuò)誤通知 | - |
2.2 經(jīng)典仲裁過(guò)程演示
場(chǎng)景:三個(gè)節(jié)點(diǎn)同時(shí)發(fā)送數(shù)據(jù)
markdown
節(jié)點(diǎn)A: ID=0x100 (0b000100000000)
節(jié)點(diǎn)B: ID=0x200 (0b001000000000)
節(jié)點(diǎn)C: ID=0x080 (0b000010000000)
仲裁過(guò)程:
-
第一位:全顯性 → 繼續(xù)比較
-
第二位:A=0, B=0, C=1 → C失去仲裁權(quán)
-
后續(xù)位比較后,A勝出總線使用權(quán)
STM32仲裁配置要點(diǎn):
// 使能自動(dòng)重傳功能(默認(rèn)開(kāi)啟)
hcan1.Init.AutoRetransmission = ENABLE;
// 設(shè)置重試次數(shù)(最大16次)
hcan1.Init.RetryCount = 3;
2.3 錯(cuò)誤檢測(cè)機(jī)制詳解
五級(jí)錯(cuò)誤防護(hù)體系:
-
CRC校驗(yàn):15位循環(huán)冗余校驗(yàn)
-
位填充:每5個(gè)相同電平插入相反電平
-
ACK校驗(yàn):接收節(jié)點(diǎn)必須發(fā)送顯性確認(rèn)
-
幀格式校驗(yàn):7個(gè)保留位必須為隱性
-
總線監(jiān)控:持續(xù)檢測(cè)總線邏輯電平
錯(cuò)誤計(jì)數(shù)器動(dòng)態(tài)調(diào)整算法:
markdown
當(dāng)檢測(cè)到錯(cuò)誤時(shí):
TEC += 8(發(fā)送錯(cuò)誤)或 REC += 1(接收錯(cuò)誤)
當(dāng)TEC > 127時(shí):進(jìn)入總線關(guān)閉狀態(tài)
2.4 位時(shí)間同步技術(shù)
同步機(jī)制:
-
硬同步:在幀起始位強(qiáng)制對(duì)齊
-
重新同步:通過(guò)調(diào)整時(shí)間段2補(bǔ)償時(shí)鐘偏差
STM32時(shí)間參數(shù)配置示例:
// 配置同步跳轉(zhuǎn)寬度為1個(gè)時(shí)間量子
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
?
// 時(shí)間段分配(假設(shè)系統(tǒng)時(shí)鐘16MHz)
CAN_BtrTypeDef sCanBtr;
sCanBtr.SyncJumpWidth = CAN_SJW_1TQ;
sCanBtr.TimeSeg1 = CAN_BS1_9TQ; ?// 傳播延遲補(bǔ)償
sCanBtr.TimeSeg2 = CAN_BS2_4TQ; ?// 相位緩沖
三、數(shù)據(jù)鏈路層核心機(jī)制
3.1 CAN協(xié)議棧全景圖
應(yīng)用層(CANopen/J1939)
? ↓
網(wǎng)絡(luò)層(路由/錯(cuò)誤處理)
? ↓
數(shù)據(jù)鏈路層(幀結(jié)構(gòu)/仲裁)
? ↓
物理層(差分信號(hào)/終端電阻)
3.2 幀結(jié)構(gòu)深度拆解
標(biāo)準(zhǔn)幀格式(11位ID):
| 仲裁場(chǎng)(11b) | 控制場(chǎng)(6b) | 數(shù)據(jù)場(chǎng)(0-8B) | CRC場(chǎng)(15b) | ACK場(chǎng)(1b) | 幀結(jié)束(7b) |
-
仲裁場(chǎng):包含節(jié)點(diǎn)ID和幀類型標(biāo)識(shí)
-
控制場(chǎng):DLC(數(shù)據(jù)長(zhǎng)度碼) + IDE(擴(kuò)展標(biāo)識(shí)符)
-
CRC場(chǎng):15位循環(huán)冗余校驗(yàn)(生成多項(xiàng)式:x1?+x1?+...+1)
STM32 CRC配置示例:
// CAN1 CRC初始化
hcan1.Instance->CRCD = 0xFFFF; ? ?// 初始值
hcan1.Instance->CRCSA = 0x0000; ? // 起始地址
3.3 仲裁機(jī)制詳解
29位擴(kuò)展幀仲裁過(guò)程:
優(yōu)先級(jí)位 → 源地址 → 參數(shù)組號(hào)(PGN)
-
優(yōu)先級(jí)計(jì)算:ID31-ID26位決定(數(shù)值越小優(yōu)先級(jí)越高)
-
源地址沖突檢測(cè):同一網(wǎng)絡(luò)內(nèi)節(jié)點(diǎn)地址必須唯一
仲裁時(shí)序仿真:
def can_arbitration(id_list):
? ?sorted_ids = sorted(id_list, key=lambda x: bin(x).count('1'))
? ?return sorted_ids[0]
?
# 示例:三個(gè)節(jié)點(diǎn)同時(shí)發(fā)送
nodes = [0x18FEF100, 0x18FEF200, 0x18FEF300]
winner = can_arbitration(nodes) ?# 輸出0x18FEF100
四、CANopen協(xié)議深度實(shí)戰(zhàn)
4.1 對(duì)象字典(Object Dictionary)
OD結(jié)構(gòu)示例:
索引 ? ? ? 類型 ? ? ? 描述
0x2000 ? ? ARRAY ? ? ? 電機(jī)控制參數(shù)
0x2000[0] UINT16 ? ? 目標(biāo)轉(zhuǎn)速(rpm)
0x2000[1] FLOAT ? ? ? 加速度(m/s2)
0x2001 ? ? RECORD ? ? 故障代碼
0x2001[0] BITFIELD ? 故障標(biāo)志位
STM32 SDO傳輸實(shí)現(xiàn):
// SDO客戶端上傳數(shù)據(jù)
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網(wǎng)絡(luò)管理
狀態(tài)遷移圖:
INIT → PRE-OPERATIONAL → OPERATIONAL → STOPPED
? ↑ ? ? ? ↑ ? ? ? ? ? ? ? ? ? ↓
? └──RESET←───────────────────┘
心跳報(bào)文配置:
// 心跳生產(chǎn)者配置
CO_NMT_HeartbeatConfig(0x01, 0x00, 500); ?// 節(jié)點(diǎn)ID=1,周期500ms
五、J1939協(xié)議核心要點(diǎn)
5.1 參數(shù)組號(hào)(PGN)編碼規(guī)則
PGN = PF(8b) << 8 | PS(8b)
PF: 參數(shù)組功能(0-255)
PS: 參數(shù)組子功能(0-255)
典型PGN解析:
PGN | PF | PS | 描述 |
---|---|---|---|
0xFEFC | 0xFE | 0xFC | 發(fā)動(dòng)機(jī)轉(zhuǎn)速請(qǐng)求 |
0xFEF0 | 0xFE | 0xF0 | 冷卻液溫度 |
0xFECA | 0xFE | 0xCA | 車輛位置報(bào)告 |
5.2 多包數(shù)據(jù)傳輸
傳輸流程:
請(qǐng)求 → 確認(rèn) → 數(shù)據(jù)包1 → 數(shù)據(jù)包2 → ... → 結(jié)束符
STM32多包發(fā)送實(shí)現(xiàn):
// 多包數(shù)據(jù)發(fā)送(最大12字節(jié)/包)
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庫(kù)實(shí)戰(zhàn)進(jìn)階
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初始化(含過(guò)濾器配置)
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 數(shù)據(jù)收發(fā)實(shí)戰(zhàn)
// 數(shù)據(jù)發(fā)送(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);
}
?
// 接收回調(diào)(帶錯(cuò)誤檢測(cè))
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) {
? ? ?// 數(shù)據(jù)長(zhǎng)度異常處理
? ? ?return;
? }
? ?uint16_t value = (RxData[0] << 8) | RxData[1];
? ?Process_Sensor_Data(value);
}
}
七、工業(yè)級(jí)應(yīng)用案例解析
7.1 電動(dòng)汽車三電系統(tǒng)
-
BMS電池管理:通過(guò)CAN總線監(jiān)控單體電壓/溫度
-
電機(jī)控制器:接收扭矩指令并反饋轉(zhuǎn)速
-
OBC車載充電機(jī):與BMS通信實(shí)現(xiàn)充電保護(hù)
通信拓?fù)?/span>:
BMS → CAN → MCU → CAN → 電機(jī)控制器
? ? ? ? ? ↑↓
? ? ? ? 充電樁
7.2 智能倉(cāng)儲(chǔ)機(jī)器人
-
多機(jī)協(xié)同:50+臺(tái)AGV通過(guò)CAN總線同步路徑規(guī)劃
-
實(shí)時(shí)監(jiān)控:電量/故障狀態(tài)實(shí)時(shí)上報(bào)
-
抗干擾方案:
-
雙絞線屏蔽層接地
-
隔離收發(fā)器(如ADuM1201)
-
冗余幀重傳機(jī)制
-
八、調(diào)試與優(yōu)化技巧
1.示波器觀察:
-
檢查CAN_H/CAN_L差分波形(正常應(yīng)為方波)
-
波特率驗(yàn)證(500Kbps對(duì)應(yīng)周期2μs)
2.錯(cuò)誤分析:
-
錯(cuò)誤幀計(jì)數(shù):HAL_CAN_GetError(&hcan1)
-
總線負(fù)載率:CAN總線分析儀檢測(cè)
3.性能優(yōu)化:
九、擴(kuò)展學(xué)習(xí)資源
-
經(jīng)典CAN vs CAN FD:帶寬從1Mbps提升至5Mbps
-
AUTOSAR架構(gòu):標(biāo)準(zhǔn)化汽車軟件架構(gòu)
-
TSN時(shí)間敏感網(wǎng)絡(luò)