进入汽车电子有两年了,一直在做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