CAN 是控制器局域网络 Controller Area Network 的缩写,是 ISO 国际标准化的串行通信协议,支持 CAN 协议 2.0A 和 2.0B。在 CAN 协议中,发送者以广播形式把报文发送给所有接收者,节点在接收报文时,会经过过滤器组根据标识符决定是否需要该报文,这种设计节省了 CPU 的开销。
APM32F072的CAN控制器特点:
- 支持 CAN 协议 2.0A 和 2.0B
- 通信波特率最大为 1Mbit/s
- 发送功能
- 有 3 个发送邮箱
- 发送报文优先级可配置
- 可记录发送时间
- 接收功能
- 有 2 个 3 级深度的接收 FIFO
- 有 14 个过滤器组.
- 可记录接收时间
如何配置CAN?
- 配置单片机的IO引脚为CAN复用模式
- CAN控制器配置
- 配置过滤寄存器(如果需要)
- 配置中断优先级
- 使能中断
void CAN_Init(void)
{
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_CAN);
RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA);
GPIO_Config_T configStruct;
CAN_Config_T canConfig;
CAN_FilterConfig_T FilterStruct;
/* Connect PA12 to CANx_Tx */
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_12, GPIO_AF_PIN4);//AF4
/* Connect PA11 to CANx_Rx */
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_11, GPIO_AF_PIN4);//AF4
configStruct.pin = GPIO_PIN_11|GPIO_PIN_12;
configStruct.mode = GPIO_MODE_AF;
configStruct.speed = GPIO_SPEED_50MHz;
GPIO_Config(GPIOA, &configStruct);
/* CAN init */
CAN_Reset();//复位
CAN_ConfigStructInit(&canConfig);
canConfig.autoBusOffManage = DISABLE;
canConfig.autoWakeUpMode = DISABLE;
canConfig.nonAutoRetran = DISABLE;
canConfig.rxFIFOLockMode = DISABLE;
canConfig.txFIFOPriority = ENABLE;
canConfig.mode = CAN_MODE_NORMAL;
canConfig.syncJumpWidth = CAN_SJW_1;
//配置波特率 250kbps
canConfig.timeSegment1 = CAN_TIME_SEGMENT1_4;
canConfig.timeSegment2 = CAN_TIME_SEGMENT2_3;
canConfig.prescaler = 24;
CAN_Config(&canConfig);
//配置过滤器
FilterStruct.filterNumber = CAN_FILTER_NUMBER_1;//选择过滤器1(一共有14个)
FilterStruct.filterMode = CAN_FILTER_MODE_IDMASK;
//配置过滤器32位宽(我们要用29位的扩展帧)
FilterStruct.filterScale = CAN_FILTER_SCALE_32BIT;
FilterStruct.filterIdHigh = (uint16_t)((ID_0>>13)&0xffff);
FilterStruct.filterIdLow = ((uint16_t)ID_0<<3&0xffff);
FilterStruct.filterMaskIdHigh = 0xffff;
FilterStruct.filterMaskIdLow = 0xfff8;
FilterStruct.filterFIFO = CAN_FIFO_0;//过滤器与 FIFO0 关联
FilterStruct.filterActivation = ENABLE;//激活过滤器1
CAN_ConfigFilter(&FilterStruct);
FilterStruct.filterNumber = CAN_FILTER_NUMBER_0;//选择过滤器0(一共有14个)
FilterStruct.filterMode = CAN_FILTER_MODE_IDMASK;
FilterStruct.filterScale = CAN_FILTER_SCALE_32BIT;
FilterStruct.filterIdHigh = (uint16_t)((ID_1>>13)&0xffff);
FilterStruct.filterIdLow = ((uint16_t)ID_1<<3&0xffff);
FilterStruct.filterMaskIdHigh = 0xffff;
FilterStruct.filterMaskIdLow = 0xfff8;
FilterStruct.filterFIFO = CAN_FIFO_0;//过滤器与 FIFO0 关联
FilterStruct.filterActivation = ENABLE;//激活过滤器 0
CAN_ConfigFilter(&FilterStruct);
NVIC_EnableIRQRequest(CEC_CAN_IRQn,0);//配置中断优先级
CAN_EnableInterrupt(CAN_INT_F0MP);//使能中断
}
波特率配置
datasheet中波特率计算公式:
上述代码中
//配置波特率 250kbps
canConfig.timeSegment1 = CAN_TIME_SEGMENT1_4;
canConfig.timeSegment2 = CAN_TIME_SEGMENT2_3;
canConfig.prescaler = 24;
单片机的pclk为48Mhz ,Tpclk就是(1/(48x10^6)),BRPSC选择23(canConfig.prescaler =24,寄存器赋值时会-1,这样便于计算和理解)。
那么:
Tq=24x(1/(48x10^6)) = 500ns
BS1时间段:Ts1 = 4xTq
BS2时间段:Ts2 = 3xTq
T1bit = Tq +Ts1+Ts2 = 8xTq = 4us
波特率 = 1/T1bit = 250 kbps
过滤器配置
过滤器的作用:在接收节点根据报文标识符决定是否需要此报文,过滤后只接收
需要的报文。CAN 控制器有 14 个过滤器组。
这句话的意思就是,当你设置了过滤器之后,只有通过过滤器的报文才会触发你的CAN接收中断,试想一下,如果没有过滤器,任何报文都会进入你的中断程序中,占用了CPU的时间。
CAN 的每个过滤器有两个寄存器,在程序中定义为
FilterStruct.filterIdHigh = (uint16_t)((ID_1>>13)&0xffff);//过滤器CAN_FiBANK1高字节
FilterStruct.filterIdLow = ((uint16_t)ID_1<<3&0xffff);//过滤器CAN_FiBANK1高字节
FilterStruct.filterMaskIdHigh = 0xffff;//过滤器CAN_FiBANK2高字节
FilterStruct.filterMaskIdLow = 0xfff8;//过滤器CAN_FiBANK2低字节
当FilterStruct.filterMode = CAN_FILTER_MODE_IDLIST列表模式,过滤器寄存器CAN_FiBANK2的ID就是你需要的CANID,
以上代码FilterStruct.filterMode = CAN_FILTER_MODE_IDMASK屏蔽位模式,CAN_FiBANK1是需要进行屏蔽的标识符位,CAN_FiBANK2是配置需要匹配的位。
FilterStruct.filterMaskIdHigh = 0xffff;//过滤器CAN_FiBANK2高字节
FilterStruct.filterMaskIdLow = 0xfff8;//过滤器CAN_FiBANK2低字节
标识符的每一位都设置为需要匹配,CAN_FiBANK1直接写成我的ID,也就成了列表模式了(手动狗头)。