1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > tx2串口与can通信控制c620电机(使用usb转can模块)

tx2串口与can通信控制c620电机(使用usb转can模块)

时间:2022-03-04 04:15:37

相关推荐

tx2串口与can通信控制c620电机(使用usb转can模块)

简介

出于各种各样的原因,我们没有使用常见的stm32来进行can通信,也没有使用tx2自带的can口,而是采用串口来发送和接收can数据报文。事实证明,这是可行的,但现在网络上关于这方面的资料较少,故博主将探索过程和所得记下,希望可以帮助到其他有同样任务的人,虽然我所使用的开发板为NVIDIA的tx2,但基本想法是适用于几乎所有的开发板的(只要有串口USB)。本文仅供参考,如有不足或错误,欢迎讨论!

1.硬件

我所使用的USB-can模块是维特智能的USB-CAN适配器

资料详情见链接资料详情-维特智能

使用手册很详细,有问题可以直接找客服和技术支持

实物与图片不一样,有五个接头,只需要找到TX和RX就行。

我所使用的电机和电调为大疆的m3508和c620,电调can两根信号线与USB-can模块连接即可。

电机的连线由于不同电机不同,请自行查询使用手册。重点在于can总线id的设置。

2.基本思路

usb-can的使用范例都是基于pc机的串口助手,不过既然用串口助手可以work,用开发板的串口同样可以。我们将用板子的串口代替串口助手,发送和接收报文,但这样做的话我们需要解析can的报文。所幸并不复杂。

我们使用的是AT指令。根据上面的USB-can模块使用手册以及大疆c620电调的can发送、接收报文格式,(见下)我们可以看出:

c620接收报文格式要求

想要成功控制电机,我们通过串口应该发送的格式如下(举个例子)

41 54 40 00 00 00 08 00 FF 00 FF 00 FF 00 FF 0D 0A

其中,41 54为帧头,40 00 00 00为ID(这个值是根据c620电机的标识符0x200得到的,由于id为ID域的前11位,因此要移位,值也就变了),08为数据长度,00 FF 00 FF 00 FF 00 FF为数据域,也就是四个轮子的电流值,0D 0A为帧尾。

!!注意,以上指令是以十六进制发送的,也就是用串口助手的话,要勾选上hex

接收报文格式类似,见c620使用手册

因此,我们的串口收到的报文应该长这样(类似的格式,这里以电调1为例)

41 54 40 20 00 00 08 00 FF 00 FF 00 FF 00 FF 0D 0A

还是要注意id域的值是要移位的!!

3.具体细节

3.1串口助手使用细节

如果使用pc的串口助手,详情见上面模块的指导书,这里简述。

AT+CG进入配置模式,AT+USART_PARAM查询/设置串口参数,由于c620电机can总线的频率为1M,为了不丢包,串口的波特率也要接近1M,所以我们选择921600或1M。但这样在串口助手上会发生恐怖的事,因为太快了电脑反应不过来,或者需要很长时间反应,建议不要轻易尝试。

配置完成后,AT+AT进入AT指令模式,此时我们可以发送报文了,格式见上述发送报文的样例。成功的话轮子会转动(但可能转一下很快就停了)。在进入AT指令后,我们也会不断的收到电调的返回报文。

3.2开发板串口使用细节

但是,真正要使用的话,大多数情况还是要用开发板的。所以下面重点介绍板子串口的代码。

3.2.1 开发板串口配置

tx2串口配置网上有很多相关blog,此处不再赘述。贴个链接

tx2串口配置

注意修改波特率(由于奇怪的原因,tx2波特率最高只能设置到460800,但是也能用)

cfsetispeed(&opt, B460800);

cfsetospeed(&opt, B460800);

3.2.2 发送

其次就是发送报文的生成。我采用的方法是进行一系列的字符串操作,把int类型的控制值(电流)转换成字符串嵌入发送报文,重点和难点在于要实现十六进制发送,但一般我们的控制量都是十进制的,所以要做个转换。这个网上也有blog。

还有就是正反转的问题,正数和负数的补码不同,需要分情况。

具体代码见文件

(此处的代码只贴了一个轮子的)(避免过长)

char HexChar(char c){if((c>='0')&&(c<='9'))return c-'0';else if((c>='A')&&(c<='F'))return c-'A'+10;else if((c>='a')&&(c<='f'))return c-'a'+10;elsereturn 0x10;}int Str2Hex(string str, char *data)//send in hex{int t,t1;int rlen=0;int len=str.size();if(len==1){char h=str[0];t=HexChar(h);data[0]=t;rlen++;}for(int i=0;i<len;){char l,h=str[i];if(h==' '){i++;continue;}i++;if(i>=len){break;}l=str[i];t=HexChar(h);t1=HexChar(l);if((t==16)||(t1==16))break;elset=t*16+t1;i++;data[rlen]=t;rlen++;}return rlen;}void tohex(char spd[],char spd_hex[])//special function to fit the format of AT command, like "1234" to " 12 34"{int j=6;for(int i=5;i>=0;i--){if(!spd[i])j--;}int k=5;for(j--;j>=0;j--){if(spd_hex[k]==' ')k=k-1;spd_hex[k]=spd[j];k--;}}int wspd1=2000;void atsend(int fd){char writebuffer[60]="";char head[ ]="41 54 40 00 00 00 08";char tail[ ]=" 0D 0A";char writedata[33]="";//write speed to hexchar spd1[6]=""//write speed formatchar new1[7]=" 00 00";//negative data formatstring _strnew1="0000";//write speed to hexchar mm1[ ]="";//write speed formatchar _charnew1[7];if(wspd1<0){sprintf(mm1,"%X",wspd1);//transfer to hexfor(int i=4;i<8;i++)_strnew1[i-4]=mm1[i];//keep four letters of hex(take -100 for example, mm1 is FFFFFF9C, we just need FF9C) _strnew1=_strnew1.insert(0," ");_strnew1=_strnew1.insert(3," ");//_strnew1 is now " FF 9C", to fit the format of AT commandstrcpy(_charnew1,_strnew1.c_str());//turn string to char[]strcat(writedata,_charnew1);//construct writedata}else{sprintf(spd1,"%X",wspd1);//turn to standard formattohex(spd1,new1);//construct writedatastrcat(writedata,new1);}//construct writebufferstrcat(writebuffer,head);strcat(writebuffer,writedata);strcat(writebuffer,tail);printf("writerbuffer=%s\n",writebuffer);//writerbuffer is like 41 54 40 00 00 00 08 00 FF 00 FF 00 FF 00 FF 0D 0Achar data[18]={};Str2Hex(writebuffer,data);//data actually what we send(send in hex)int w;printf("%s\n", data);w = write(fd, data, 17);printf("write %d \n", w);}

我的方法很笨,大佬勿喷。

附上头文件

#include<termios.h>#include<fcntl.h>#include<unistd.h>#include <stdio.h>#include <stdlib.h>#include<string.h>#include<time.h>#include<string>#include<iostream>#include<math.h>

此时,我们只需要修改外部定义的int wspd1=2000;,即可改变AT报文,即可控制给电机的电流值。

3.2.3 接收

接收函数如下

//retrun speedint speed1=0;int speed2=0;int speed3=0;int speed4=0;int cread(int fd, char buff[], int lenth){int len;memset(buff, 0, lenth);len = read(fd, buff, lenth);printf("LEN:%d\n", len);if(len!=17)cout<<"wrong len"<<endl;else if(buff[0]!=0x41)cout<<"wrong AT command"<<endl;else//buff is like 41 54 40 20 00 00 08 00 FF 00 FF 00 FF 00 FF 0D 0Aint id=buff[3]>>4;int _speed=(buff[9]*256+buff[10]);//unsigned from 0 to 65535signed short int speed=0;speed=_speed;//signed, + and - means positive or negaive rotationswitch(id){case 2:speed1=speed;break;case 4:speed2=speed;break;case 6:speed3=speed;break;case 8:speed4=speed;break;}cout<<speed1<<endl;//for debugcout<<speed2<<endl;cout<<speed3<<endl;cout<<speed4<<endl;return len;}

将接收报文中的数据读出、保存在外部变量speed中

3.2.4 主函数

下面为主函数(只有读取数据),用开发板很明显要两个进程

int main(){int fd;char str[32];char begin_CG[ ]="AT+CG\r\n";char begin_AT[ ]="AT+AT\r\n";fd = open_port();set_opt(fd);//上面两个函数为串口配置函数,,自己写一下,我就不贴了csend(fd, begin_CG, 7);while(cread(fd, str, 32));//读取之前可能中途截断残留的返回报文csend(fd, begin_AT, 7);sleep(1);cread(fd, str, 4);//read head(useless message)while(1){cread(fd, str, 17);}//main loop.17 is the length of AT messageclose(fd);return 0;}void csend(int fd, char buff[], int length){int w;printf("%s\n", buff);w = write(fd, buff, length);printf("write %d \n", w);}

4.结语

由于网上有关can的教程都是基于stm32的,所以特意写下这个特殊的方法,以及我在整个过程中遇到的困难和解决办法。文章很冗长,是因为我想把每一个细节都讲清楚,希望可以帮助到有需要的人!

这只是tx2与电调的通讯模块,真正要使用can控制轮子的话还需要加一个pid控制,之后有可能再补充pid的代码。

具体代码见我的资源。(csdn上传文件好像只能这样)如果愿意下载谢谢你的打赏!

不过,我知道大家都是白嫖人,所以我贴个网盘。

链接:/s/1lZP_gV3xwxQyBOv3VlYewg

提取码:pgkw

复制这段内容后打开百度网盘手机App,操作更方便哦

(其中的demo.zip为stm32的样例代码)

(代码里串口的配置要根据自己的情况做修改啊!)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。