CAN的原理与配置方法
一、通讯相关的基础知识
1. 分层思想
分层思想——如同分工合作,不同的抽象层完成不同的工作,相互通过通讯协议或其他方式进行数据交换,完成信息的传递。
例如在现实中,产业链分为上下游,各个层级分工合作,相互配合来完成最后产品的上市。其中下游在完成基础制造比如华星光电、京东方屏幕制造,台积电、三星的芯片制造,而上游有华米OV耀的配件集成、系统开发和产品上市宣发等的工作。
2. CAN通讯介绍
CAN通讯的特点是他是一个中速、高可靠、多主竞争的异步串行总线的通讯协议,由CAN_H和CAN_L两条双绞线组成。在单片机中,分层主要分为三层,分别是硬件物理层、链路传输层、应用数据层。
二、各个层的介绍
1. 硬件物理层
通过CAN_H和CAN_L两根线,在两根线中间有两个120Ω的电阻避免振铃效应导致的信号不稳定和寄生电容导致的信号相应变慢。
比特率,bit/s:每秒钟传输的二进制码元的数量;
波特率,symbol/s:每秒钟传输的有效信息码元的数量。
例如在CAN协议中,采用NRZ编码(不归零编码),1个码元携带1比特信息,因此比特率 = 波特率(均为1 Mbps),所以传输速度Mbps和mb/s是 。而在计算机中,则是由于其1 Byte = 8 bits,因而8个二进制码元为1个有效信息码元,所以 1 MB/s = 8 Mbps。
由于为异步通讯,CAN通讯分为了同步段、传播段和相位缓冲段。同步段人如其名,就是同步时许的时间片(分为硬同步和再同步),然后是传播段,内部有数据的类型、接受地址和内容,最后则是有2个的相位缓冲段,用于CAN接受时的采集电信号时间,一般 使得采集点于50%~80%间,而每完成一个时间片,则是完成1bit数据的传输。
- 硬同步:检测到帧起始(SOF)的下降沿时,节点会立即将位时序重置,使该下降沿位于同步段。
- 再同步:在后续数据中,如果边沿出现偏差,节点会通过延长相位缓冲段1或缩短相位缓冲段2来微调采样点,补偿时钟漂移。
由于双线通讯,在隐性模式下CAN_H≈CAN_L≈2.5V,显性模式下CAN_H≈3.5V,CAN_L≈1.5V,从而使得显性模式可以覆盖隐性模式。可以理解为做与运算,即线与。而在CAN传输过程中,为了防止异步通讯长时间都在显性或者隐性电平,所以如果有5个连续相同的电平会插入一个相反的电平来避免长时间同一电平而导致数据错位。
CAN线则有四种通讯模式:
| 常规模式(又收又发) | 回环模式(只发不收) | 静默模式(只收不发) | 回环静默模式(自收自发) |
|---|---|---|---|
| 向总线发送,从总线接收 | 向总线和本机发送,不从总线接收,仅从本机接收 | 不向总线发送,仅向本机发送,从总线和本机接收 | 不向总线发送,不从总线接收 |
2. 链路传输层
CAN传输的东西,包括其传输前的CAN_ID(标识符),帧类型srr(标准帧和扩展帧,扩展帧ID为18位)(数据帧和遥控帧,遥控帧要求接收方发送数据回来),帧长度(标准帧最长8Byte);传输中的数据;和最后的数据传输无错的回复ACK。
由于CAN协议是异步通讯协议,所以CAN通讯会有仲裁系统,仲裁系统即判断优先级,来决定是谁来发送数据,而仲裁的原理则是显性电平和隐性电平的显性一定覆盖来实现的。在仲裁系统中,数据类型优先级高低是: 标准帧数据帧 > 标准帧遥控帧 > 扩展帧数据帧 > 扩展帧遥控帧。
而CAN通讯在接收过程中需要配置滤波器和对应的掩码,实现对于通讯的过滤接收机制(即接收自己需要的),而滤波器以CAN_ID是否匹配来判断是否是自己需要的信息。由于CAN_ID为11位二进制(即0x000~0x7ff),所以掩码也在这个范围内。
3. 应用数据层
在CAN通讯中,软件配置首先需要使用CubeMX来配置调试、时钟树等设置,然后观察其输入的频率,公式为:
因此例如在RoboMaster A板上,CAN线接与APB1,传入为45MHz,因此可配置为922,即:
而在其他的配置项上,大部分的默认disable即可,但可以开:
- Bus-Off:如果总线出现错误,比如总线电平不稳导致掉线,会自动恢复;
- Retransmission:如果数据未成功发出去,比如被仲裁掉了,会自动重发(权衡数据丢失的代价、数据延迟的代价)。
并且一定要开中断使接收到后触发中断接收数据。
三、软件代码
1. Cube MX界面初始化
CAN时钟配置在此处为预分频器=3,相位缓冲段1为10,相位缓冲段2为3,可以得出位于76%左右的位置,位于50%~80%处。由手册可以看到,CAN1和CAN2都是挂载在APB1时钟的,APB1在此时的频率为42MHz,因而其比特率为:
和目标RM的相关电机统一的1Mbps相同。
而我们同样需要开启接收回调函数,用来处理CAN接收后触发中断的数据。

2. 初始化CAN
CAN发送和接收中断初始化
void CAN_Init(CAN_HandleTypeDef *hcan, CAN_Call_Back Callback_Function){ HAL_CAN_Start(hcan); __HAL_CAN_ENABLE_IT(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
if(hcan->Instance == CAN1) { CAN1_Manage_Object.CAN_Handler = hcan; CAN1_Manage_Object.Callback_Function = Callback_Function; } else if(hcan->Instance == CAN2) { CAN2_Manage_Object.CAN_Handler = hcan; CAN2_Manage_Object.Callback_Function = Callback_Function; }}HAL库CAN总线启动:
// HAL_StatusTypeDef用来反馈初始化状态,CAN_HandleTypeDef *hcan项传入CAN的指针HAL_StatusTypeDef HAL_CAN_Start(CAN_HandleTypeDef *hcan)↓HAL_CAN_Start(&hcan);HAL库中断开启:
// 无返回值,__HANDLE__表示需要控制开关中断的指针,__INTERRUPT__是指控制中断的功能__HAL_CAN_ENABLE_IT(__HANDLE__, __INTERRUPT__)↓__HAL_CAN_ENABLE_IT(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING);对对应中断的回调处理(不同的CAN线):
// 设置回调函数的结构体struct Struct_CAN_Manage_Object{ CAN_HandleTypeDef *CAN_Handler; // CAN地址 CAN_Call_Back Callback_Function; // CAN对应回调函数的指针};
// 设置CAN的地址CAN1_Manage_Object.CAN_Handler = hcan;
// 设置回调函数的指针(地址)CAN1_Manage_Object.Callback_Function = Callback_Function;滤波器和掩码的设置
void CAN_Feilter_Mask_Config(CAN_HandleTypeDef *hcan, uint32_t Filter_ID, uint32_t Filter_Mask, uint32_t Filter_FIFO){ CAN_FilterTypeDef can_filter_init_structure = {0}; // 归零
// 防止空值输入导致操作出界 if(hcan == NULL) {return;} if((Filter_FIFO != CAN_FILTER_FIFO0) && (Filter_FIFO != CAN_FILTER_FIFO1)) {return;}
// 标准帧: 11bit ID 放在 32bit 过滤器高 16bit 的 [15:5] can_filter_init_structure.FilterIdHigh = (uint16_t)((Filter_ID & 0x7FFU) << 5); can_filter_init_structure.FilterIdLow = 0x0000; can_filter_init_structure.FilterMaskIdHigh = (uint16_t)((Filter_Mask & 0x7FFU) << 5); can_filter_init_structure.FilterMaskIdLow = 0x0000;
can_filter_init_structure.FilterBank = (hcan->Instance == CAN1) ? 0U : 14U; can_filter_init_structure.FilterMode = CAN_FILTERMODE_IDMASK; can_filter_init_structure.FilterScale = CAN_FILTERSCALE_32BIT; can_filter_init_structure.FilterActivation = ENABLE; can_filter_init_structure.SlaveStartFilterBank = 14; can_filter_init_structure.FilterFIFOAssignment = Filter_FIFO;
(void)HAL_CAN_ConfigFilter(hcan, &can_filter_init_structure);}3. 发送数据
void CAN_Motor_Control(uint32_t Send_id, int16_t motor1, int16_t motor2, int16_t motor3, int16_t motor4){ uint32_t send_can_message; can_tx_message.StdId = Send_id; can_tx_message.IDE = CAN_ID_STD; can_tx_message.RTR = CAN_RTR_DATA; can_tx_message.DLC = 0x08;
can_send_data[0] = motor1 >> 8; can_send_data[1] = motor1; can_send_data[2] = motor2 >> 8; can_send_data[3] = motor2; can_send_data[4] = motor3 >> 8; can_send_data[5] = motor3; can_send_data[6] = motor4 >> 8; can_send_data[7] = motor4;
HAL_CAN_AddTxMessage(&hcan1, &can_tx_message, can_send_data, &send_can_message);}如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时






