北理DreamChaser战队电控培训——CAN通信理论与实践

文章介绍了CAN通信的基本原理和在RoboMaster比赛中的应用,包括CAN通信的硬件层面如差分信号、帧仲裁和波特率,以及软件层面的过滤器配置。通过与UART的对比,突出了CAN通信的高速和可靠性。文章还详细讲解了CAN总线的波特率计算,并展示了如何在CUBE中配置CAN接口和过滤器,最后给出了CAN初始化、发送和接收函数的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

什么是CAN通信?

理论部分

硬件层面

信息传递方式——差分信号

帧仲裁

波特率

软件层面

过滤器

列表模式

掩码模式

标准数据帧

仲裁场

控制场

代码实现

CUBE配置

 代码部分

CAN初始化

过滤器初始化

发送函数

接收函数


前言

在RoboMaster比赛中,无论是电机驱动还是板间通信,CAN通信都发挥了极其重要的作用,但是相比于其它基本知识,CAN是一个相对而言比较复杂的通讯协议,有着更多的特性需要去记忆,也因此成为了很多RMer电控入门阶段的美好回忆,本篇文章将从理论出发,结合实践,讲解如何使用CAN通信。

什么是CAN通信?

CAN 是控制器域网 (Controller Area Network, CAN) 的简称,是由研发和生产汽车电子产 品著称的德国 BOACH 公司开发,并最终成为国际标准(ISO11898),CAN 是国际上应用 最广泛的现场总线之一。 在北美和西欧,CAN 总线协议已经成为汽车计算机控制系统和嵌 入式工业控制局域网的标准总线,并且拥有以 CAN 为底层协议专为大型货车和重工机械车 辆设计的 J1939 协议。近年来,它具有的高可靠性和良好的错误检测能力受到重视,被广泛应用于汽车计算机控制系统和环境温度恶劣、电磁辐射强及振动大的工业环境。

理论部分

硬件层面

信息传递方式——差分信号

CAN控制器通过组成总线的2根线(CAN-HCAN-L)的电位差来确定总线的电平,信号是以两线之间的“差分”电压形式出现,总线电平分为显性电平和隐性电平。

这样,通过采用两种互补的逻辑数值"显性""隐性"的方法,用"显性"数值表示逻辑"0",而"隐性"表示逻辑"1"。从而,CAN总线仅通过CAN_H与CAN_L两条线即可实现信息的传递。

 这时我们可以联想到之前学过的另一种通信方式UART

对比——CAN&UART
CANUART
比赛中应用板间通信,大多数电机驱动与操作手通信
总线结构多主机、多从机的总线结构,其中多个节点可以同时发送和接收消息点对点连接,只有一个发送器和一个接收器
通信速率支持高速传输,可以实现较高的数据速率,1 Mbps或更高低速通信,通常在几十 kbps 到几 Mbps 的范围内
数据帧格式使用帧格式来发送和接收数据,其中包括标识符、数据和检验位异步通信,使用起始位、数据位、校验位和停止位来传输数据
可靠性和冲突处理具有较高的可靠性和抗干扰能力,可以检测和处理冲突,确保数据的完整性和正确性较为简单,没有内置的错误检测和冲突处理机制

部分内容将会在下面讲解

帧仲裁

在编写控制程序时,我们往往需要处理来自多个传感器或上位机的数据,那么当我们用CAN传递信息时,如果多个模块同时向我们反馈信息,如果不对顺序进行安排,数据将会混在一起无法解算。这种情况下就需要进行仲裁,判断哪个设备可以占用总线,而其他设备要转变为接收或者等待。

CAN的仲裁机制巧妙地利用了差分信号的特性,即显性电平覆盖隐形电平的特性,如果出现多个设备同时发送的情况,则先输出隐形电平的设备会失去对总线的占有权。下图中D为显性电平,R为隐形电平,通过该图可以很容易地理解CAN的仲裁机制。

波特率

CAN有着很高的通讯速率,通过查阅手册可知,一般RM系列电调的通讯速率为1Mbps只有波特率一致的情况下,主控才能成功与电调进行通讯CAN的通讯速率的决定因素包括以下四点:

同步段 (SYNC_SEG) :位变化应该在此时间段内发生。只有一个时间片的固定长度 (1 x tq )
位段 1(BS1) :定义采样点的位置。其持续长度可以在 1 16 个时间片之间调整
位段 2(BS2) :定义发送点的位置。其持续长度可以在 1 8 个时间片之间调整
同步跳转宽度 SJW ):定义位段加长或缩短的上限。它可以在 1 4 个时间片之间调整

 CAN 总线波特率和 tq (Time Quantum),tBS1 (Time Quanta in Bit Segment 1)和 tBS2 (Time Quanta in Bit Segment 1) 的值直接相关,tq 通过总线分频后直接得到,tBS1和 tBS2 则通过 TS1 和 TS2 放大为 tq 的若干倍,需要注意的是下图中 tBS1 和 tBS2 的计 算是通过 tq*(TS1+1)和 tq*(TS2+1)得到的,而在 CubeMX 中,我们配置的 Time Quanta in Bit Segment 的值对应的就是 (TS1+1)和(TS2+1)。至于CAN究竟挂在哪个总线上就需要查阅数据手册

软件层面

过滤器

把CAN总线看作是一个兵种组,每天会为组员安排不同任务,但是队员们并不是每个人都要做全部工作的,不加区分地把数据传给每一个人只会导致工作的混乱。因此我们可以在任务前面加上标识,如电控任务,机械任务,电路任务,视觉任务让每个人只接收自己技术组的任务,而直接过滤掉与自己无关的工作

事实上,bxCAN的过滤器就是采用上述方法,你只需要设置好你感兴趣的那些CAN报文ID,那么MCU就只能收到这些CAN报文,是从硬件上过滤掉,完全不需要软件参与进来,从而节省了大大节省了MCU的时间,可以更加专注于其他事务,这个就是bxCAN过滤器的意义所在。

CAN的过滤器模式分为掩码模式列表模式。

列表模式

简单来说就是制作一张ID表,如果来的数据的ID在这张表中则接收,否则不收。

掩码模式

假设某学校的学工号是 入学年份+学生序号,那么,我想要确定学生的年级的话只需要看学工号就可以了,但是为了省事,其实我们完全可以把学工号后面的序号忽略,只看前面的入学年份部分。这就是掩码模式。

标准数据帧

CAN的一个标准数据帧包括以下几个部分

仲裁场

仲裁场包括12位标识符,作用在前面帧仲裁处已进行讲解

控制场

仲裁场后跟随的是控制场,存放数据长度DLC,数据场中要填写CAN发送的数据 

代码实现

CUBE配置

  1. 选择外部高速晶振
  2. Debug选Serial Wire
  3. 配置时钟树

接下来结合时钟树讲解一下波特率的配置

 

经查阅,我们知道STM32F407IGHX的CAN挂在APB1总线上,因此我们在时钟树上找到APB1,外设频率为42MHz。

打开CAN

此时我们设的预分频(Prescaler)为3,所以t_q=\frac{1}{f}=\frac{1}{\frac{42MHz}{3}}=\frac{1}{14}ns

因而,由理论部分的公式求得:波特率(Baud Rate)Rb=1000000bit/s

 

选取工作模式为 Normal

 关于各种工作模式,介绍如下:

工作模式

模式说明

详细说明

Normal

正常模式

可发可收

Loopback

回环模式

自发自收

Silent

静默模式

只收不发

Loopback combine with Silent

回环静默

模式

 代码部分

CAN初始化

变量含义

  • gimbal_tx_message.StdId:CAN总线消息的标准ID
  • gimbal_tx_message.IDE:指定CAN总线消息的标识符扩展位
  • gimbal_tx_message.RTR:指定CAN总线消息的远程传输请求位
  • gimbal_tx_message.DLC:指定CAN总线消息的数据长度码
  chassis_tx_message.StdId = 0x222; //标准ID
  chassis_tx_message.RTR = CAN_RTR_DATA;//设置了远程传输请求位
  chassis_tx_message.IDE = CAN_ID_STD;//使用了标准的11位标识符
  chassis_tx_message.DLC = 0x08;    //8字节长

接收初始化

    CAN_FilterTypeDef CAN_FilterType;
    CAN_FilterType.FilterBank=0;                        //使用0号过滤器                   
    CAN_FilterType.FilterIdHigh=0x0000;                 //设置验证码高低各4字节
    CAN_FilterType.FilterIdLow=0x0000;
    CAN_FilterType.FilterMaskIdHigh=0x0000;             //设置屏蔽码高低各4字节
    CAN_FilterType.FilterMaskIdLow=0x0000;
    CAN_FilterType.FilterFIFOAssignment=CAN_RX_FIFO0;   //通过CAN的信息放入0号FIFO
    CAN_FilterType.FilterMode=CAN_FILTERMODE_IDMASK;    //采用掩码模式
    CAN_FilterType.FilterScale=CAN_FILTERSCALE_32BIT;   //设置32位宽
    CAN_FilterType.FilterActivation=ENABLE;             //激活滤波器
    CAN_FilterType.SlaveStartFilterBank=14;
    HAL_CAN_ConfigFilter(hcan, &CAN_FilterType);        //配置过滤器
    HAL_CAN_Start(hcan);                                //开启CAN总线
    HAL_CAN_ActivateNotification(hcan,CAN_IT_RX_FIFO0_MSG_PENDING);//激活CAN接收

发送函数

HAL_StatusTypeDef HAL_CAN_AddTxMessage(CAN_HandleTypeDef *hcan, CAN_TxHeaderTypeDef *pHeader, uint8_t aData[], uint32_t *pTxMailbox)
函数名HAL_CAN_AddTXMessage
函数功能将一段数据通过 CAN 总线发送
返回值HAL_StatusTypeDef,HAL 库定义的几种状态,如果本次 CAN 发送成功, 则返回 HAL_OK
参数1

CAN_HandleTypeDef *hcan,即 can 的句柄指针,如果是 can1 就输入

&hcan1,can2 就输入&hcan2

参数2

CAN_TxHeaderTypeDef *pHeader,待发送的 CAN 数据帧信息的结构体指 针,包含了 CAN 的 ID,格式等重要信息
参数3uint8_t aData[],装载了待发送的数据的数组名称
参数4uint32_t *pTxMailbox,用于存储 CAN 发送所使用的邮箱号

实例:队内CAN发送函数

/**
  * @brief      向CAN总线发送信息
  * @param      phcan: 指向CAN句柄的指针
  * @param      txdata: 要发送的信息
  * @retval     无
  */
void Can_SendMessage(CAN_HandleTypeDef* phcan, CAN_TxHeaderTypeDef* pheader, uint8_t txdata[])
{
    uint32_t mailbox;
    /* Start the Transmission process */
    uint32_t ret = HAL_CAN_AddTxMessage(phcan, pheader, txdata, &mailbox);
    if (ret != HAL_OK) {
        /* Transmission request Error */
       
			Can_Error_Source = 5;
			Can_Error_Message_Header = *pheader;
			 Can_ErrorHandler(ret);
    }
}

接收函数

HAL_StatusTypeDef HAL_CAN_GetRxMessage(CAN_HandleTypeDef *hcan, uint32_t RxFifo, CAN_RxHeaderTypeDef *pHeader, uint8_t aData[]) 
函数名HAL_CAN_GetRxMessage
函数功能接收 CAN 总线上发送来的数据
返回值HAL_StatusTypeDef,HAL 库定义的几种状态,如果本次 CAN 接收成功, 则返回 HAL_OK
参数1

CAN_HandleTypeDef *hcan,即 can 的句柄指针,如果是 can1 就输入&hcan1,can2 就输入&hcan2

参数2

uint32_t RxFifo,接收时使用的 CAN 接收 FIFO 号,一般为 CAN_RX_FIFO0
参数3CAN_RxHeaderTypeDef *pHeader,存储接收到的 CAN 数据帧信息的结构 体指针,包含了 CAN 的 ID,格式等重要信息
参数4uint8_t aData[],存储接收到的数据的数组名称

实例:CAN接收电机反馈值

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) 
{
	
  HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &chassis_rx_message, rxData);
  int i = (int)chassis_rx_message.StdId - 0x204 - 1;
  wheelrxdata.rxangle[i] = ((rxData[0]<<8) | rxData[1]);
	wheelrxdata.rxspeed[i] = ((rxData[2]<<8) | rxData[3]);
	wheelrxdata.rxI[i] = ((rxData[4]<<8) | rxData[5]);
	wheelrxdata.rxT[i] = rxData[6];
  return;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值