進入汽車電子有兩年了,一直在做BSP的工作,竟然沒有接觸過CAN調試,這次有機會給上層做一個CAN接口,調試過程中遇到了一些問題,記錄如下。
1、CAN總線波特率的計算
以前接觸的的通信協議,波特率都可以直接賦值,但是CAN的一次採樣分成4個時間段。
它的波特率計算公式爲:
BAUD_RATE_CLOCK/(BAUD_RATE_BRP+1)/(1 +(BAUD_RATE_TSEG1+1)+(BAUD_RATE_TSEG2+1))
爲了給上層提供方便的接口,在時鐘源選擇後,將波特率的配置寫死,並且選擇一個較爲通用的採樣點,同時將同步跳轉寬度用宏定義的方式留出來。
if(baudrate == CAN_BAUD_500K)
{
//BAUD_RATE_CLOCK/(BAUD_RATE_BRP+1)/(1 +(BAUD_RATE_TSEG1+1)+(BAUD_RATE_TSEG2+1))
sMSCANConfig.sBaudRateSetting.SJW = USR_DEF_SJW; //同步跳轉寬度,爲保持同步,一位中最多可被縮短或延長的時間量子時鐘週期的數目
sMSCANConfig.sBaudRateSetting.BRP = 1; //波特率預分頻器
sMSCANConfig.sBaudRateSetting.SAMP = BAUD_RATE_SAMP; // 0
sMSCANConfig.sBaudRateSetting.TSEG1= 15; //總線定時寄存器時段1
sMSCANConfig.sBaudRateSetting.TSEG2= 2; //總線定時寄存器時段2
}
if(baudrate == CAN_BAUD_250K)
{
sMSCANConfig.sBaudRateSetting.SJW = USR_DEF_SJW;
sMSCANConfig.sBaudRateSetting.BRP = 3;
sMSCANConfig.sBaudRateSetting.SAMP = BAUD_RATE_SAMP;
sMSCANConfig.sBaudRateSetting.TSEG1= 15;
sMSCANConfig.sBaudRateSetting.TSEG2= 2;
}
else
{
sMSCANConfig.sBaudRateSetting.SJW = USR_DEF_SJW;
sMSCANConfig.sBaudRateSetting.BRP = 7;
sMSCANConfig.sBaudRateSetting.SAMP = BAUD_RATE_SAMP;
sMSCANConfig.sBaudRateSetting.TSEG1= 15;
sMSCANConfig.sBaudRateSetting.TSEG2= 2;
}
2、CAN的接收報文過濾
CAN的消息ID可以簡單理解爲一個報文過濾標示位,所以在接收的時候要提供報文過濾接口。一般的報文過濾都是ID+MASK的形式構成。通過閱讀芯片手冊,發現KEA128的ID和MSK寄存器有點複雜。
KEA128提供兩組32位的寄存器用來設置ID和MSK,每組都是標準幀(11bit)和擴展幀(29bit)共用的(比較特殊的是這款芯片的MRn的位設置爲0表示去匹配掩碼,設置爲1表示不匹配。這一點我在底層接口做了轉換)
從上面寄存器的位分佈可以看到,標準幀和擴展幀的ID位置分佈。所以必須爲各種幀格式提供配置接口。所以做了下面一個函數,用來計算IDAR和IDMR寄存器
//參數1是幀類型,參數2是任意需要過濾的ID組中之一,參數3爲掩碼,參數4是寄存器組ID(分別是0、1)
void can_fliter_interface(can_frame_type_t type,uint32_t id_base,uint32_t mask,uint8_t flt_group)
{
uint32_t code = id_base;
uint32_t code_high;
uint32_t code_low;
uint32_t mask_high;
uint32_t mask_low;
if(CAN_STD_FRAME == type)
{
//標準幀 11位code
switch(flt_group)
{
case 0:
{
code = code<<21;
uIDAR0 = code;
mask = mask<<21;
uIDMR0 = ~mask;//底層做反碼轉換
}
break;
case 1:
{
code = code<<21;
uIDAR1 = code;
mask = mask<<21;
uIDMR1 = ~mask;
}
break;
default:
break;
}
}
if(CAN_EXT_FRAME == type)
{
//擴展幀 29位code
switch(flt_group)
{
case 0:
{
//取code的高11位並移動到32位的高11位
code_high= (code&0x1FFC0000)<<3;
//取code的低18位並移動到32位的1~19位
code_low = (code&0x3FFFF)<<1;
uIDAR0 = (code_high|((uint32_t)0x18<<16)|code_low);
mask_high= (mask&0x01FFC0000)<<3;
mask_low = (mask&0x3FFFF)<<1;
uIDMR0 = ~(mask_high|(0x01|(uint32_t)0x10<<16)|mask_low);
}
break;
case 1:
{
code_high= (code&0x1FFC0000)<<3;
code_low = (code&0x3FFFF)<<1;
uIDAR1 = (code_high|((uint32_t)0x18<<16)|code_low);
mask_high= (mask&0x01FFC0000)<<3;
mask_low = (mask&0x3FFFF)<<1;
uIDMR1 = ~(mask_high|(0x01|(uint32_t)0x10<<16)|mask_low);
}
break;
default:
break;
}
}
}
最終將計算出來的IDAR和IDMR賦值給初始化數組對象
sMSCANConfig.u32IDAR0 = uIDAR0;
sMSCANConfig.u32IDAR1 = uIDAR1;
//標識符屏蔽寄存器指定標識符驗收寄存器中哪些對應的位與驗收濾波相關
sMSCANConfig.u32IDMR0 = uIDMR0;
sMSCANConfig.u32IDMR1 = uIDMR1;
3、過濾測試,在初始化CAN接口之前配置過濾
#define NODE_ID2 0x801
can_fliter_interface(CAN_EXT_FRAME,NODE_ID2,0x1FFFFFFC,0); //設置過濾擴擴展幀 29位,掩碼放開後兩位
//可以過兩次接收ID爲0x800,0x801,0x802和0x803的擴展幀
mscan_hal_init(CAN_BAUD_250K); //初始化波特率爲250K