大疆M3508、M2006必备CAN总线知识与配置方法
使用大疆M3508、M2006的CAN总线知识与配置方法
前言:
两个月前的一篇文章:PID与三环控制发布以后,有不少朋友在微信上交流大疆M3508、M2006的使用问题,其中有一个方面值得单独拿出来聊一聊:CAN通信
对于很多朋友,包括我自己,使用大疆的M3508、M2006无刷直流电机和C610、C620电调是第一次接触到CAN通信。大疆的这几款产品原本设计的非常容易上手,应该几天就能熟练使用。但是由于C610、C620电调选用了CAN总线通信,给不少人带来了困扰。然而使用这几个电机并不需要把CAN通信了解的有多么深。
困扰往往来自:
- CAN总线之前用的比较少
- 现有CAN通信资料少而且复杂
这篇文章旨在快速扫盲,给出使用M3508、M2006电机的必备CAN通信知识,希望能降低使用门槛,给各位朋友节约时间。内容均使用M3508与M2006实测过,演示视频在文章末尾。
本文仅涉及必备知识,甚至尽量避开复杂的通信原理,力求浅显易懂。
0x00 需要额外的CAN收发器!!!
这件事要放到前面去说,因为已经有几位朋友的经历指出了其必要性。
如果我们有一块STM32开发板,也有电调电机和电源。那么我们的STM32引脚与电调的CAN_H和CAN_L之间是不能直接相连的。
需要购买如下图所示的CAN收发器,作为两者连接的桥梁。一般我用的是TJA1050芯片的模块。
CAN收发器是使用电机的必备硬件条件,一定要先确定自己有这个东西。至于原因,咱们接下来说明。
0x01 硬件层面分析
为什么需要CAN收发器
通过分析硬件层面我们能知道为什么需要额外的CAN收发器。请看下图:
从图中我们能看出,STM32拥有的外设叫做:CAN控制器,它负责CAN通信的筛选、优先级、仲裁等问题。相当于咱们的邮局,帮忙盖个邮戳,分分类。通过复用GPIO以后,它延伸出两个引脚:CAN_RX
和CAN_TX
,类比串口我们知道一个负责收一个负责发。
实际上,这两个引脚上传输的数据已经是CAN报文了,该有的格式它都有,STM32的CAN回环模式就相当于直接把CAN_RX
和CAN_TX
相连,就可以收到自己发的消息。
问题是,CAN在设计的时候为了消除共模干扰,特地选用了差分信号(也叫差模信号)传输。咱们的C610、C620电调,接收发送的就是差分信号。这才是CAN收发器的作用:
把来自STM32的信号转换成差分信号让电调听得懂,把来自电调的差分信号转换成STM32听得懂的信号。
请注意:
连接CAN_RX
和CAN_TX
引脚与CAN收发器的引脚时,并不需要像串口一样交叉。直接将RX连接到标有RX的引脚,TX类似。
120Ω的终端电阻呢?
不知道朋友们有没有注意到C610、C620电调上都有一个开关,C620的在侧面,旁边写着CAN RESISTOR
,这也是CAN总线的设计要求。
根据咱们学的《电路理论》这门课,当信号波长小于电路尺寸的时候,我们就不能把电路当成集中电路来分析。对于can总线是一样的,由于电调采用1MHz的通信频率,为了防止一些不必要的干扰,我们需要在CAN总线的两端分别用120Ω电阻跨接起来。比如之前图中的两端可以认为是咱们的CAN收发器是一端,下面的电调是另一端。
对于只有一个电调的情况下,可以打开电调上的电阻,然后直接和CAN收发器连接,这样虽然少了一端的终端电阻,但实际上可以运行。如果同时控制两个及以上的电调,那就可以打开其中两个的终端电阻,构成总线的两端。当然也可以手动组成如下图的总线结构并且关闭电调上的终端电阻,图中还加入了电容进行滤波。
0x02 软件配置简析
其实,看完硬件部分就已经可以正常使用电调电机了,因为大疆官方例程中已经把CAN通信配置好了,直接可以使用。
但是如果涉及非常多的设备同时控制,比如超过四个电机的情况下,或者还有其他的外设要通过CAN总线相连,简单了解软件配置也是很有好处。
而且,我发现很多朋友是在做RoboMaster比赛,全面的了解软硬件还是十分必要的,如果出现问题也有利于排查,不至于浪费大量时间。
接下来我们还是使用咱们的老朋友:大疆官方M2006例程、M3508例程来作为样本。主要探究以下问题:
- 通信频率如何设定为1MHz
- CAN过滤器的配置
- 报文的接收与发送
两个文件:bsp_can.c
和can.c
如何把通信频率设定为1MHz?
以下是文件can.c
中截取的代码(有删改):
CAN_HandleTypeDef hcan1;
/* CAN1 init function */
void MX_CAN1_Init(void)
{
hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 3;//分频系数设置为3
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SJW = CAN_SJW_1TQ;
// =================请看这里👇
hcan1.Init.BS1 = CAN_BS1_9TQ;
hcan1.Init.BS2 = CAN_BS2_4TQ;
// =====================这里👆
//省略一些。。。。。
HAL_CAN_Init(&hcan1);
}
注意,大疆的例程是为大疆的开发板所编写,其主控芯片是STM32F429,在这里F4与F1的时钟频率差别会影响到配置的具体参数。
咱们重点来看分频系数的确定以及标出的两行。
STM32F4一般来说主频率会配置到168MHz,经过4分频之后得到了42MHz的APB1时钟,CAN控制器正是从APB1上获取时钟。代码中hcan1.Init.Prescaler = 3;
正是将42MHz的时钟进行3分频,得到了14MHz的时钟。这就是真正被使用的频率。接着我们看传输一个bit需要哪几部分:
所以,传输一位的时间可以看作T=(1+BS1+BS2)*tq,tq(time quantum)。所以在例程里就是1+9+4=14个tq。而tq刚好是CAN时钟频率14MHz的倒数,tq=(1/14MHz),T=14*tq,得T=(1/1MHz),同样f=1/T=1MHz。由此获得了我们需要的频率。
同理,如果是F1,APB1时钟一般是36MHz,那么我们设置分频系数为4,BS1=5,BS2=3,也能得到1MHz。
CAN过滤器配置
其实一般来说并不需要注意CAN过滤器得配置,因为官方例程也没怎么配置,大可直接套用。
过滤器是针对接收信息进行筛选得部件,因为CAN总线上得消息往往比较复杂,通过过滤器可以选出想要接受的消息。大致过程是过滤器会对所有收到的报文按照咱们设定的规则进行筛选,如果符合要求就会放入FIFO缓冲区保存下来。而这个规则就是报文的id信息。为了实现复杂的id筛选,引入了id寄存器和id掩码寄存器,其配合过程非常的。。精彩👍。有兴趣可以了解一下。
下面得代码摘自文件bsp_can.c
(有删改):
void my_can_filter_init_recv_all(CAN_HandleTypeDef* _hcan)
{
CAN_FilterConfTypeDef CAN_FilterConfigStructure;
static CanTxMsgTypeDef Tx1Message;
static CanRxMsgTypeDef Rx1Message;
CAN_FilterConfigStructure.FilterNumber = 0;
CAN_FilterConfigStructure.FilterMode = CAN_FILTERMODE_IDMASK;
CAN_FilterConfigStructure.FilterScale = CAN_FILTERSCALE_32BIT;
CAN_FilterConfigStructure.FilterIdHigh = 0x0000;
CAN_FilterConfigStructure.FilterIdLow = 0x0000;
CAN_FilterConfigStructure.FilterMaskIdHigh = 0x0000;
CAN_FilterConfigStructure.FilterMaskIdLow = 0x0000;
CAN_FilterConfigStructure.FilterFIFOAssignment = CAN_FilterFIFO0;
CAN_FilterConfigStructure.BankNumber = 14;
CAN_FilterConfigStructure.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(_hcan, &CAN_FilterConfigStructure) ;
}
这里我们发现不论是id还是掩码都是0,也就是接收总线上的所有报文。那么在多电机的情况下如何区分消息的发出者呢?
消息的接收与发送
我们先看看接收,以下选自bsp_can.c
(有删改):
void HAL_CAN_RxCpltCallback(CAN_HandleTypeDef* _hcan)
{
switch(_hcan->pRxMsg->StdId){
case CAN_2006Moto1_ID://0x201
case CAN_2006Moto2_ID://0x202
case CAN_2006Moto3_ID://0x203
case CAN_2006Moto4_ID://0x204
{
static u8 i;
i = _hcan->pRxMsg->StdId - CAN_2006Moto1_ID;
get_moto_measure(&moto_chassis[i], _hcan);
}
break;
}
这是CAN的接收中断回调函数,在来到这个函数之前,获得的报文已经被储存在pRxMsg
指向的结构体当中。switch语句通过判别StdId
来确定是不是电机发来的报文,并且判断是哪个电机。而这个StdId
咱们并不陌生,它是前面所说的FilterId
的一部分。
官方手册中提到在同一个CAN总线上,最多可以接入8个电调,通过电调的自动分配他们分别是地址0x201~0x208。
再看发送:
void set_moto_current(CAN_HandleTypeDef* hcan, s16 iq1, s16 iq2, s16 iq3, s16 iq4){
hcan->pTxMsg->StdId = 0x200;
hcan->pTxMsg->IDE = CAN_ID_STD;
hcan->pTxMsg->RTR = CAN_RTR_DATA;
hcan->pTxMsg->DLC = 0x08;
hcan->pTxMsg->Data[0] = (iq1 >> 8);
hcan->pTxMsg->Data[1] = iq1;
hcan->pTxMsg->Data[2] = (iq2 >> 8);
hcan->pTxMsg->Data[3] = iq2;
hcan->pTxMsg->Data[4] = iq3 >> 8;
hcan->pTxMsg->Data[5] = iq3;
hcan->pTxMsg->Data[6] = iq4 >> 8;
hcan->pTxMsg->Data[7] = iq4;
HAL_CAN_Transmit(hcan, 100);
}
按照手册,我们能看出,如果要给编号为0x2010x204的电调发信息,要把发送中的`StdId`设置为0x200,若是要给0x2050x208的四个电调发信息,则要设置为0x1FF。
后面依照手册填充数据然后发送,过程比较简单。
0x03 总结
又是一篇有关大疆电机的文章,也是结合我自己的经历和上一篇文章的反馈才有了这篇。现在还记得我刚开始接触CAN通信时候的疑惑感,感觉资料都太复杂看不下去。水平确实有限,也就只能力求个浅显易懂,希望能帮助朋友们节约一些时间吧。相关例程可以在公众号上向我索要。
这里放上前一篇文章链接:PID和三环控制-以大疆M3508、M2006为例 https://blog.csdn.net/qq_28039135/article/details/116379392
更多嵌入式,电机控制相关文章请移步公众号,来找我聊聊天吧:
技术新人,水平有限,还请各位朋友多多指教。如果对文章有任何的疑问或者发现错误请一定指出!
演示视频:
同时控制M3505、M2006双电机的演示视频在公众号原文底部:
欢迎转载,请注明作者与原文地址:
作者:胡小安