![]()
一、位時(shí)間的基本概念 1.1 核心時(shí)間單位
如您所述,CAN位時(shí)間由多個(gè)時(shí)間份額(Time Quantum, TQ)構(gòu)成。關(guān)鍵公式:
TQ = BRP × Tclk
其中:
Tclk:CAN控制器外設(shè)時(shí)鐘周期(通常來(lái)自系統(tǒng)時(shí)鐘分頻)BRP:波特率預(yù)分頻器值(寄存器值,通常為5~10位寬)
關(guān)于BRP+1的說(shuō)明:不同芯片廠商的實(shí)現(xiàn)有差異。例如: STM32的BTR寄存器中 BRP 字段值為0~1023,實(shí)際分頻 = BRP+1 某些老式獨(dú)立CAN控制器(如SJA1000)的 BRP 寄存器直接等于分頻值 本文后續(xù)代碼采用實(shí)際分頻值 = BRP寄存器值 + 1的方式(最通用)。1.2 位時(shí)間的四段結(jié)構(gòu)
一個(gè)標(biāo)準(zhǔn)CAN位時(shí)間分為四個(gè)連續(xù)段:
段名稱
長(zhǎng)度(TQ)
作用
同步段(SS)
1
固定,用于檢測(cè)邊沿
傳播段(PROP)
1~8
補(bǔ)償物理延遲
相位緩沖段1(PBS1)
1~8
采樣點(diǎn)前緩沖,可延長(zhǎng)
相位緩沖段2(PBS2)
1~8
采樣點(diǎn)后緩沖,可縮短
采樣點(diǎn)位置 = (SS + PROP + PBS1) / 總TQ數(shù)。推薦范圍為70%~80%。
總位時(shí)間TQ數(shù)= 1 + PROP + PBS1 + PBS2,典型值8~25。
二、同步機(jī)制深度解析 2.1 硬同步(Hard Synchronization)
觸發(fā)時(shí)機(jī):僅在幀起始(SOF)的隱性→顯性跳變沿
動(dòng)作:接收節(jié)點(diǎn)將自己的SS段對(duì)齊到這個(gè)邊沿
調(diào)整幅度:無(wú)限制(可以大幅移動(dòng))
觸發(fā)時(shí)機(jī):SOF之后的任何隱性→顯性跳變沿(需滿足跳變沿在PBS1或PBS2內(nèi))
動(dòng)作:通過(guò)延長(zhǎng)PBS1或縮短PBS2來(lái)補(bǔ)償相位誤差
限制:調(diào)整量不超過(guò)SJW(同步跳轉(zhuǎn)寬度)
重同步規(guī)則:
若邊沿發(fā)生在PBS1內(nèi)→ 相位誤差
e > 0(接收端慢),延長(zhǎng)PBS1若邊沿發(fā)生在PBS2內(nèi)→ 相位誤差
e < 0(接收端快),縮短PBS2調(diào)整量 = min(|e|, SJW)
這是CAN控制器硬件實(shí)現(xiàn)的限制,根源在于:
采樣點(diǎn)穩(wěn)定性要求:SJW過(guò)大(如≥5)會(huì)導(dǎo)致采樣點(diǎn)在位時(shí)間內(nèi)過(guò)度漂移,降低噪聲容限。
振蕩器容差計(jì)算:CAN協(xié)議標(biāo)準(zhǔn)(ISO 11898-1)根據(jù)最大允許振蕩器誤差推導(dǎo)出SJW上限為4。公式如下:
其中NBT為一個(gè)位時(shí)間的TQ總數(shù)。當(dāng)SJW=4時(shí),可支持±1.58%的晶振誤差(常見(jiàn)晶振為±0.5%~±1.5%)。振蕩器容差 ≤ SJW / (20 × NBT)主流控制器事實(shí)標(biāo)準(zhǔn):Bosch CAN 2.0規(guī)范建議SJW為1~4,所有主流芯片(STM32、SJA1000、MCP2515)均遵循此范圍。
如果您看到某些文獻(xiàn)中SJW取值可到8,那通常是指CAN FD(可變速率模式)下的擴(kuò)展配置,經(jīng)典CAN僅支持1~4。三、代碼實(shí)現(xiàn)示例 3.1 位定時(shí)參數(shù)計(jì)算器(C語(yǔ)言)
以下代碼根據(jù)目標(biāo)波特率、時(shí)鐘頻率自動(dòng)計(jì)算BRP和段長(zhǎng)度。
3.2 STM32 HAL庫(kù)配置示例#include
#include
#include
typedef struct {
uint16_t brp; // 波特率分頻器 (實(shí)際分頻 = brp+1)
uint8_t prop_seg; // 傳播段長(zhǎng)度 (1~8 TQ)
uint8_t pbs1_seg; // 相位緩沖段1 (1~8 TQ)
uint8_t pbs2_seg; // 相位緩沖段2 (1~8 TQ)
uint8_t sjw; // 同步跳轉(zhuǎn)寬度 (1~4 TQ)
uint8_t sample_point_percent; // 采樣點(diǎn)百分比
} CAN_BitTiming_TypeDef;
/**
* @brief 計(jì)算CAN位定時(shí)參數(shù)
* @param clk_khz CAN控制器時(shí)鐘頻率 (kHz)
* @param baud_kbps 目標(biāo)波特率 (kbps)
* @param target_sp 目標(biāo)采樣點(diǎn)百分比 (例如 75 表示75%)
* @param timing 輸出參數(shù)結(jié)構(gòu)體
* @return 1:成功, 0:失敗
*/
uint8_t CAN_ComputeBitTiming(uint32_t clk_khz, uint32_t baud_kbps,
uint8_t target_sp, CAN_BitTiming_TypeDef *timing)
{
uint32_t best_error = 0xFFFFFFFF;
uint8_t best_brp = 0, best_prop = 1, best_pbs1 = 2, best_pbs2 = 1;
uint32_t actual_baud;
int32_t error;
// 總TQ數(shù)范圍: 8~25
for (uint8_t tq_num = 8; tq_num <= 25; tq_num++)
{
// 計(jì)算理論TQ時(shí)間 (us)
double tq_us = 1000.0 / (baud_kbps * tq_num);
double clk_t_us = 1.0 / clk_khz;
// 計(jì)算所需BRP (取整)
uint16_t brp_calc = (uint16_t)(tq_us / clk_t_us + 0.5) - 1;
if (brp_calc < 0 || brp_calc > 1023) continue;
// 實(shí)際波特率
actual_baud = clk_khz * 1000 / ((brp_calc+1) * tq_num);
error = (int32_t)actual_baud - (int32_t)(baud_kbps * 1000);
if (error < 0) error = -error;
// 誤差小于0.5%才考慮
if (error * 1000 <= (int32_t)(baud_kbps * 1000 * 5))
{
// 分配段長(zhǎng)度: SS=1固定, PROP=1~8, PBS1=1~8, PBS2=1~8
for (uint8_t prop = 1; prop <= 8 && (1+prop+2+1) <= tq_num; prop++)
{
for (uint8_t pbs1 = 1; pbs1 <= 8; pbs1++)
{
uint8_t pbs2 = tq_num - 1 - prop - pbs1;
if (pbs2 < 1 || pbs2 > 8) continue;
if (pbs1 < pbs2) continue; // 要求PBS1 >= PBS2
uint8_t sp = (1 + prop + pbs1) * 100 / tq_num;
if (abs(sp - target_sp) > 5) continue; // 采樣點(diǎn)偏差±5%
// 選擇最優(yōu)組合 (優(yōu)先采樣點(diǎn)準(zhǔn)確,次選誤差小)
uint32_t current_error = abs(sp - target_sp) * 1000 + error;
if (current_error < best_error) {
best_error = current_error;
best_brp = brp_calc;
best_prop = prop;
best_pbs1 = pbs1;
best_pbs2 = pbs2;
}
}
}
}
}
if (best_error == 0xFFFFFFFF) return 0;
timing->brp = best_brp;
timing->prop_seg = best_prop;
timing->pbs1_seg = best_pbs1;
timing->pbs2_seg = best_pbs2;
timing->sjw = (best_pbs1 < 4) ? best_pbs1 : 4; // SJW不超過(guò)PBS1和4
timing->sample_point_percent = (1 + best_prop + best_pbs1) * 100 /
(1 + best_prop + best_pbs1 + best_pbs2);
return 1;
}// 使用示例
int main(void)
{
CAN_BitTiming_TypeDef timing;
// 假設(shè)CAN外設(shè)時(shí)鐘40MHz,目標(biāo)波特率500kbps,采樣點(diǎn)75%
if (CAN_ComputeBitTiming(40000, 500, 75, &timing)) {
printf("BRP = %d (實(shí)際分頻 %d)\n", timing.brp, timing.brp+1);
printf("PROP_SEG = %d TQ\n", timing.prop_seg);
printf("PBS1 = %d TQ\n", timing.pbs1_seg);
printf("PBS2 = %d TQ\n", timing.pbs2_seg);
printf("SJW = %d TQ\n", timing.sjw);
printf("采樣點(diǎn) = %d%%\n", timing.sample_point_percent);
printf("總TQ數(shù) = %d\n", 1+timing.prop_seg+timing.pbs1_seg+timing.pbs2_seg);
} else {
printf("未找到合適的位定時(shí)參數(shù)\n");
}
return 0;
}
3.3 同步過(guò)程模擬(偽代碼)#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;void CAN1_Config(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Mode = CAN_MODE_NORMAL;
// 手動(dòng)計(jì)算好的參數(shù) (假設(shè)APB1=42MHz, 波特率500k, 采樣點(diǎn)75%)
// 總TQ=16, BRP=5 (實(shí)際分頻6), 則TQ=6/42M≈142.86ns, 位時(shí)間=16*142.86ns≈2.2857us -> 437.5kbps
// 調(diào)整為BRP=4 (分頻5), TQ=5/42M≈119ns, 位時(shí)間=16*119ns=1.904us -> 525kbps
// 實(shí)際更精確的配置:BRP=3(分頻4), TQ=4/42M≈95.24ns, 位時(shí)間=20*95.24ns=1.9048us -> 525kbps
// 為了500kbps,需要位時(shí)間=2us,TQ=100ns,BRP分頻=42M*100ns=4.2->取4,BRP=3
// 總TQ=20,SS=1,PROP=7,PBS1=6,PBS2=6,采樣點(diǎn)=(1+7+6)/20=70%
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.AutoBusOff = DISABLE;
hcan1.Init.AutoWakeUp = DISABLE;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
// 位定時(shí)寄存器 BTR 配置
// STM32的BS1 = PROP + PBS1, BS2 = PBS2, 且BS1/BS2長(zhǎng)度=位長(zhǎng)-3
// 這里需要根據(jù)芯片手冊(cè)轉(zhuǎn)換
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ; // SJW=1
hcan1.Init.TimeSeg1 = CAN_BS1_13TQ; // 13TQ (包含SS)
hcan1.Init.TimeSeg2 = CAN_BS2_6TQ; // 6TQ
hcan1.Init.Prescaler = 4; // BRP=4 (實(shí)際分頻=4+1=5)
if (HAL_CAN_Init(&hcan1) != HAL_OK) {
Error_Handler();
}
}
以下演示接收節(jié)點(diǎn)如何根據(jù)邊沿誤差調(diào)整PBS1/PBS2:
四、總結(jié)與最佳實(shí)踐// 接收節(jié)點(diǎn)位流處理狀態(tài)機(jī)
typedef enum {
IDLE,
WAITING_SOF,
RECEIVING_BITS
} CAN_RxState;
CAN_RxState rx_state = WAITING_SOF;
int16_t tq_counter = 0; // 當(dāng)前位內(nèi)的TQ計(jì)數(shù)
int16_t phase_error = 0; // 相位誤差 (TQ單位)void CAN_Rx_Tick(void) // 每個(gè)TQ調(diào)用一次
{
if (detect_falling_edge()) { // 隱性->顯性跳變
if (rx_state == WAITING_SOF) {
// 硬同步:直接對(duì)齊到SS起點(diǎn)
tq_counter = 0;
rx_state = RECEIVING_BITS;
}
else if (rx_state == RECEIVING_BITS && tq_counter > 0) {
// 重同步:計(jì)算相位誤差
if (tq_counter < (ss_len + prop_len + pbs1_len)) {
// 邊沿在PBS1內(nèi) -> 節(jié)點(diǎn)慢,需要延長(zhǎng)PBS1
phase_error = tq_counter - (ss_len + prop_len);
if (phase_error > sjw) phase_error = sjw;
pbs1_extend = phase_error;
pbs2_shorten = 0;
}
else if (tq_counter < total_tq) {
// 邊沿在PBS2內(nèi) -> 節(jié)點(diǎn)快,需要縮短PBS2
phase_error = (total_tq - tq_counter);
if (phase_error > sjw) phase_error = sjw;
pbs2_shorten = phase_error;
pbs1_extend = 0;
}
}
}
// 采樣點(diǎn)判斷 (位于PBS1結(jié)束時(shí)刻)
if (tq_counter == (ss_len + prop_len + pbs1_len - 1 + pbs1_extend)) {
sample_bit(); // 讀取總線電平
}
// 位結(jié)束,準(zhǔn)備下一個(gè)位
if (tq_counter >= (total_tq + pbs1_extend - pbs2_shorten - 1)) {
tq_counter = 0;
pbs1_extend = 0;
pbs2_shorten = 0;
} else {
tq_counter++;
}
}
參數(shù)
推薦值/范圍
總TQ數(shù)
16~20
平衡同步精度與開銷
采樣點(diǎn)
70%~80%
經(jīng)典CAN常用75%
SJW
1~3
晶振精度高時(shí)可取1~2
PROP
根據(jù)總線長(zhǎng)度
每米總線約需5~10ns,轉(zhuǎn)換為TQ
BRP
盡可能小
提高采樣分辨率,但注意寄存器范圍
同步設(shè)計(jì)要點(diǎn):
整個(gè)網(wǎng)絡(luò)應(yīng)采用相同的位定時(shí)參數(shù)
長(zhǎng)總線(>40m)需增加PROP段
高波特率(>500k)應(yīng)減少總TQ數(shù),提高采樣點(diǎn)精度
希望這篇補(bǔ)充了代碼實(shí)例和SJW深度解析的文章能對(duì)您有所幫助。如果您需要特定MCU(如NXP S32K、Infineon AURIX)的寄存器級(jí)配置代碼,我可以進(jìn)一步提供。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.