拓达TSDA-C21B, 通过单片机CAN通信发送命令控制电机转向
上篇链接: 拓达TSDA-C21B,通过单片机串口发命令控制电机转向.
一、串口通信的不足之处
上篇博客中,我们用单片机的串口对驱动器进行了控制。串口控制的优点是 相对简单,廉价,对于任意的单片机都提供串口,入门单片机必学串口,而且串口的操作也是比较简单,学习成本比较低(有基础甚至不用学习😁))。
至于缺点,先来看看这个驱动器提供的串口发命令的格式
串口 控制的方式 需要延时 (此处是一帧数据之间需要延时10ms,每帧数据之间又需要延时10ms, 所以一帧数据至少需要延时20ms!!),对于一些精读要求较高而且发命令较频繁的场合,这非常致命,而且一般串口的最大波特率为 115200,大概100K (bit/s)。串口本身发命令的速度也不快。
二、CAN通信控制优势
咨询过拓达的工程师这个问题之后我们,了解到 CAN通信的速度快很多。
先来分析一下CAN通信为什么要快:
1、从数据手册上我们知道 CAN通信,每帧数据之间不需要延时,基于缓存的收数据方式,每帧之间也不需要延时。
2、CAN通信本身的速度就很快最快能 1000K (bit/s)是串口通信最大传输速度的十倍左右!!
至于用 CAN通信进行控制的缺点,主要是 CAN通信控制的技术门槛相对高一些,CAN总线偏向工业控制,平常学习单片机开发中少有涉及。 而且 CAN通信需要驱动芯片支持, CAN芯片相对较贵。网上资料也较少。
三、CAN通信实现电机的转向角度控制原理分析
由上面的图和 阅读数据手册可以知道 CAN通信发送命令的格式和 串口发送数据的格式类似。(这里就不做详细的分析说明了,看不懂数据手册的可以从串口命令看是看,可以去看 上一篇 串口通信控制的软件设计部分。)
我们只需要写数据给驱动器,所以只关注下面的信息
发命令的格式:
ID + 从机组号 + 功能码+ 寄存器数据 1地址 + 数据内容1高8位 + 数据内容1低8位 + 寄存器数据 2地址 + 数据内容2高8位 + 数据内容2低8位。
电机启动与停止 发命令的格式
这部分和串口发命令格式一样,在 0x50地址处发高16位数据,在 0x05地址处发低 16位地址,只是 CAN通信没有检验位
位置模式下绝对模式和相对模式的控制命令和 速度限幅的控制命令。(由上图可知,如果不设置的话默认为绝对模式,看它写入的值 0x00 0x00可知)
接上面位置模式速度限幅的命令的16位数据对应速度的转速, 实际转速 = 写入值(16位数据的大小)/8192 x 3000 RPM。
四、CAN通信控制电机转向的程序设计
1、先用单片机测试CAN通信
(1)、硬件环境搭建
两个带CAN模块的STM32单片机、仿真器、USB线、串口调试助手、杜邦线若干
(2)、软件设计
软件设计思路:先用两个单片机进行 CAN通信,一个单片机写发送 CAN命令程序,另一个单片机写接收程序 模拟驱动器内的 CAN设备的接收。确保硬件没问题。通过串口观察收到的数据。收发数据与数据手册上的描述无误之后再到驱动器上去测试。
CAN 主机的程序设计:
根据 CAN发命令的格式可知,发CAN命令和串口命令类似,都是将32位的数据分成高16位和低16位发送,并且高16位数据也要分高8低8发送。
CAN发送的格式为:
ID + 从机组号 + 功能码 + 寄存器1地址 + 数据高8位 + 数据低8位 + 寄存器2地址 + 数据高8位 + 数据低8位 我们设置从机ID 为0x01,组号为 0, 功能码为点对点写指令 0x1a, 寄存器1位高16位地址 0x50,寄存器2为低16位地址。发送命令的格式可以简化为:
0x01 + 0 + 0x1a+ 0x50 + 32位数据高16位的高8位 + 32位数据高16位的低8位 +0x05 + 32位数据低16位的高8位 + 32位数据低16位的低8位
参考 串口通信代码的格式,所以我很快总结写出以下代码:
如果你没有CAN通信的驱动程序,可以点击此处下载
CAN F1 driver.
CAN F4 driver.
此为初期的测试代码,不了解数据的接收机制 想稳当就安全性进行延迟。
利用主函数发数据,图中GPIO操作是LED的闪烁。为观察程序是否在工作。也能指示数据的发送频率。
CAN 从机的软件设计:
这里是根据数据手册模拟 CAN的接收,使用了动态数组,为什么使用动态数组呢? 因为我们需要打印收到数据的长度,静态数组的长度定义之后就不变,默认初始化所有的元素为0。记得使用完 free掉申请的内存,不然会长时间工作会引起内存泄露😜, 程序宕机。我们以十六进制打印接收到的数组内的每一个数据,用空格隔开了。打印完收到的数据之后再打印接收到数据的长度 len。后面清零用来存储下一次接收的长度。
分别烧入 主机和从机的 程序。将从机连接串口,用串口调试助手进行调试。
观察串口输出,如下:
原本我以为不存在什么问题,因为, 数据确实如手册里所说的。
先发ID, 再发组号 然后发速度限幅命令,启动命令。也如手册里所描述的 发送-10000的例子
发送了 ID +0x00 0x1a 0x50 0xff 0xff 0x05 0xd8 0xf0, 与手册描述一模一样。
Can_SetAngel(10000); (10000对应16进制数 00 00 27 10)
也发送了 ID +0x00 0x1a 0x50 0x00 0x00 0x05 0x27 0x10。
但是拿去接上驱动器接上水晶头网线调试发现怎么也调试不通, 电机就是不转😂。
后来才知道原因:原因就是驱动器内的CAN设备接收数据的时候接收的功能命令是一次能接收8字节的。它在处理的时候也是一次处理缓冲内的数据(缓冲区为8字节)。看数据手册也知道是一次发送八字节的指令。
所以我们用 与发串口命令相同的格式发CAN命令行不通,区别在于: 串口是一位位的读数据,每读一位都会确认是否收到,而CAN通信是是基于发送和接收缓冲区的,每次最多发送0~ 8字节的数据,每次接收0~8字节数据。驱动器手册上的说明的是基于缓冲数组一次发八字节数据,而不是我们写的程序,每次发一位。
找到错误的原因之后我们就可以修改主机发送数据的程序了。其实理解了原理 在原程序的基础上修改很简单,我们只需创建一个静态的长度为8的无符号数字数组CMD, 里面的数据有 0x00, 0x1a, 0x50, 0x00, 0x00, 0x05, 0x00, 0x00 其中CMD[0]为组号,0x1a为功能码一对一写数据,0x50位高十六位地址,后面0x00 0x00 分别是其高八位和低八位,每次发数据需要改变的,我们将它默认为 0x00, 0x05为低16位地址,后面的 0x00 0x00分别是其高八位和低八位, 初始化为 0x00。后面根据Can_SetAngle函数的输入来将相应的数据填入相应的位。(不用担心效率问题, 数组的访问效率很高😘)
void Can_SetAngle(s32 val)
{
u8 ID = 0x01; // 从机的 ID号
u8 CMD[8] = {0x00, 0x1a, 0x50, 0x00, 0x00, 0x05, 0x00, 0x00};
u16 G16,D16; // val的高16位、低16位
u8 G16G8,G16D8,D16G8, D16D8; // val高16位的高8位、高16位的低8位v、低16位的高8位、低16位的低8位
G16 = (val >>16)&0xffff;
D16 = val&0xffff;
G16G8 = (G16>>8)&0xff;
G16D8 = G16&0xff;
D16G8 = (D16>>8)&0xff;
D16D8 = D16&0xff;
// 将相应的数据填入数组的相应位置
CMD[3] = G16G8;
CMD[4] = G16D8;
CMD[6] = D16G8;
CMD[7] = D16D8;
MyCan_Send_Msg(&ID, 1); // 首先发送 ID
//delay_ms(1); // 测试知此处不需要延时
MyCan_Send_Msg(CMD, 8); // 将8位缓冲数组中的8位数据命令发送
}
void Can_SpeedStart()
{
u8 Id = 0x01;
u8 Ins[8] = {0x00, 0x1a, 0x1d,0x02,0x00,0x00, 0x00,0x01};
MyCan_Send_Msg(&Id, 1);
//delay_ms();
MyCan_Send_Msg(Ins, 8);
}
main函数的格式不变,GPIO为LED的引脚。
int main()
{
//USART1_Init();
delay_init();
MyCan_Init();
LED_Init();
Can_SpeedStart(); //发送电机速度限幅和启动命令
while(1)
{
Can_SetAngle(-10000);
delay_ms(5);
Can_SetAngle(-10000);
delay_ms(5);
GPIO_SetBits(GPIOB,GPIO_Pin_13);
delay_ms(550);
GPIO_ResetBits(GPIOB,GPIO_Pin_13);
delay_ms(500);
}
}
我们通过串口观察主机发送的命令:
如我们所预期的!! 先发送 从机ID,然后一次发八位的命令。(SpeedStar函数发送的命令没有捕获到。)
2、将写的程序在驱动器上测试
首先接上上位机将模式来源设置为位置控制模式-PC数字输入,CAN的ID设置为我们程序里的 0x01即 1,组号为 0, 自动报告的内容暂时不用管, CAN通信的波特率是右边的数字对应(左边为484的波特率),这里我们的程序里CAN初始化为500K bps,所以我们的波特率设置 2。设置完之后点击下载即可。
注意厂家提供的网口转串口线只连接了网口的正、TXD、RXD即网口的 9、3、2号可以结合下下图分析,我们拆开这个串口即可发现,用万用表测出 CAN_H和 CAN_L两根线,将他们接上空的串口引脚。
将厂家提供的 网口转串口母口 线接上自己买的串口公头线,公头线的另外一端剪断。
结合下面的网口的序号和说明 通过万用表测出公头线的那一跟为 CAN-L 哪一根为CAN_L。其他的五关的线可以剪断,避免干扰。
最后将 CAN_L和 CAN_H接到单片相应的引脚。硬件搭建大工告成。
开启驱动器电源,给单片机通电,车轮上的电机开始左右循环往复的转动。 ±10000对应转向12.5度, 目测差不多。
CAN通信成功!!!