完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
2个回答
|
|
本章节主要解决的是如何在ROS与STM32之间建立串口通信,达到相互发送和接收对方数据的目的,从而为我们自己以ros操作系统开发硬件提供相应的便利。
一、相关原理及准备工作 完成ROS与STM32之间的串口通信,需要准备的硬件有STM32串口,TTL转USB模块,Linux硬件设备,博主使用的STM32是正点原子的STM32精英板,Linux硬件设备下搭载的rROS环境是ros-kinetic,其工作原理如下图所示 在开始ROS与STM32的通信之前,需要注意以下几点 (1)确保stm32端和ros端波特率一定一致 (2)确保是stm32串口,TTL转USB模块和Linux设备之间是按照上图所示连接,TX与RX相连,RX与TX相连,同时,也要测试连接线的好坏 (3)确保linux设备系统中有CH340/CH341驱动,一般情况下都会有,如果没有CH340/CH341驱动,需要到网上查找相关资料下载驱动 (4)确保自己的串口在Linux系统中具有超级用户权限(一般都默认不具有该权限),一般插上TTL转USB模块,Linux系统中就可以检测到ttyUSB0的串口设备 (5)如果插上TTL转USB模块后,Linux系统中查询的串口设备不是ttyUSB0的话,在编写相应的程序时,要注意串口设备名称的一致性 二、STM32与ROS工程文件下载 STM32与ROS工程文件所包含的工程文件 (1)STM32端:STM32与ROS串口通信测试工程文件 (2)ROS端:ROS与STM32串口通信测试功能包文件 1、STM32测试工程文件结构 主要包括: (1)主函数main.c (2)文件mbotLinuxUsart.c STM32与ROS串口通信测试工程文件程序详解 main.c文件 #include "delay.h" #include "sys.h" #include "usart.h" #include "led.h" #include "mbotLinuxUsart.h" //测试发送变量 short testSend1 =5000; short testSend2 =2000; short testSend3 =1000; unsigned char testSend4 = 0x05; //测试接收变量 int testRece1 =0; int testRece2 =0; unsigned char testRece3 = 0x00; int main(void) { delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); uart_init(115200); while(1) { usartSendData(testSend1,testSend2,testSend3,testSend4); delay_ms(13); } } //串口中断服务函数,接收数据 void USART1_IRQHandler() { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { USART_ClearITPendingBit(USART1,USART_IT_RXNE); //从ROS接收数据,并把他们存放到以下三个变量当中 usartReceiveOneData(&testRece1,&testRece2,&testRece3); } } mbotLinuxUsart.c文件 #include "mbotLinuxUsart.h" #include "usart.h" //数据接收暂存区 unsigned char receiveBuff[16] = {0}; //通信协议常量 const unsigned char header[2] = {0x55, 0xaa}; const unsigned char ender[2] = {0x0d, 0x0a}; //发送数据共用体 union sendData { short d; unsigned char data[2]; }leftVelNow,rightVelNow,angleNow; //接收数据共用体 union receiveData { short d; unsigned char data[2]; }leftVelSet,rightVelSet; int usartReceiveOneData(int *p_leftSpeedSet,int *p_rightSpeedSet,unsigned char *p_crtlFlag) { unsigned char USART_Receiver = 0; static unsigned char checkSum = 0; static unsigned char USARTBufferIndex = 0; static short j=0,k=0; static unsigned char USARTReceiverFront = 0; static unsigned char Start_Flag = START; static short dataLength = 0; USART_Receiver = USART_ReceiveData(USART1); if(Start_Flag == START) { if(USART_Receiver == 0xaa) { if(USARTReceiverFront == 0x55) { Start_Flag = !START; printf("header okn"); receiveBuff[0]=header[0]; receiveBuff[1]=header[1]; USARTBufferIndex = 0; checkSum = 0x00; } } else { USARTReceiverFront = USART_Receiver; } } else { switch(USARTBufferIndex) { case 0: receiveBuff[2] = USART_Receiver; dataLength = receiveBuff[2]; //buf[2] USARTBufferIndex++; break; case 1: receiveBuff[j + 3] = USART_Receiver; //buf[3] - buf[7] j++; if(j >= dataLength) { j = 0; USARTBufferIndex++; } break; case 2: receiveBuff[3 + dataLength] = USART_Receiver; checkSum = getCrc8(receiveBuff, 3 + dataLength); if (checkSum != receiveBuff[3 + dataLength]) //buf[8] { printf("Received data check sum error!"); return 0; } USARTBufferIndex++; break; case 3: if(k==0) { k++; } else if (k==1) { for(k = 0; k < 2; k++) { leftVelSet.data[k] = receiveBuff[k + 3]; //buf[3] buf[4] rightVelSet.data[k] = receiveBuff[k + 5]; //buf[5] buf[6] } *p_leftSpeedSet = (int)leftVelSet.d; *p_rightSpeedSet = (int)rightVelSet.d; //ctrlFlag *p_crtlFlag = receiveBuff[7]; //buf[7] USARTBufferIndex = 0; USARTReceiverFront = 0; Start_Flag = START; checkSum = 0; dataLength = 0; j = 0; k = 0; } break; default:break; } } return 0; } void usartSendData(short leftVel, short rightVel,short angle,unsigned char ctrlFlag) { unsigned char buf[13] = {0}; int i, length = 0; leftVelNow.d = leftVel; rightVelNow.d = rightVel; angleNow.d = angle; for(i = 0; i < 2; i++) buf = header; // buf[0] buf[1] length = 7; buf[2] = length; // buf[2] for(i = 0; i < 2; i++) { buf[i + 3] = leftVelNow.data; // buf[3] buf[4] buf[i + 5] = rightVelNow.data; // buf[5] buf[6] buf[i + 7] = angleNow.data; // buf[7] buf[8] } buf[3 + length - 1] = ctrlFlag; // buf[9] buf[3 + length] = getCrc8(buf, 3 + length); // buf[10] buf[3 + length + 1] = ender[0]; // buf[11] buf[3 + length + 2] = ender[1]; // buf[12] USART_Send_String(buf,sizeof(buf)); } void USART_Send_String(u8 *p,u16 sendSize) { static int length =0; while(length while( !(USART1->SR&(0x01<<7)) ); USART1->DR=*p; p++; length++; } length =0; } unsigned char getCrc8(unsigned char *ptr, unsigned short len) { unsigned char crc; unsigned char i; crc = 0; while(len--) { crc ^= *ptr++; for(i = 0; i < 8; i++) { if(crc&0x01) crc=(crc>>1)^0x8C; else crc >>= 1; } } return crc; } 2、ROS测试功能包文件结构 主要包括: (1)通信协议以及串口配置程序文件mbot_linux_serial.cpp (2)系统结构测试主程序文件publish_node.cpp (3)一个基础ROS功能包具备的其他程序文件 |
|
|
|
ROS与STM32串口通信测试功能包程序详解
publish_node.cpp文件 / //包含ros库下的ros.h头文件 #include "ros/ros.h" //包含std_msgs库下的String.h头文件 #include "std_msgs/String.h" //包含mbot_linux_serial.h头文件 #include "mbot_linux_serial.h" //测试发送数据两 double testSend1=5555.0; double testSend2=2222.0; unsigned char testSend3=0x07; //测试接受数据变量 double testRece1=0.0; double testRece2=0.0; double testRece3=0.0; unsigned char testRece4=0x00; int main(int agrc,char **argv) { //创建一个ros节点,节点名称为public_node ros::init(agrc,argv,"public_node"); //创建句柄,用于管理节点信息 ros::NodeHandle nh; //设置频率,10HZ ros::Rate loop_rate(10); //串口初始化,相关定义在mbot_linux_serial.cpp有描述 serialInit(); /* ros::ok()在不进行任何操作时,就相当于返回True,只有在以下几种情况下会变成返回False (1)运行终端时,按下Ctrl-C时 (2)我们被一个同名同姓的节点从网络中踢出。 (3)ros::shutdown()被应用程序的另一部分调用。 (4)所有的ros::NodeHandles都被销毁了。 */ while(ros::ok()) { /* ros::spinOnce()和ros::spin()是ros消息回调处理函数 ros消息回调处理函数原理:如果你的程序写了相关的消息订阅函数,那么程序在执行过程中,除了主程序以外,ROS还会自动在后台按照你规定的格式,接受订阅的消息,但是所接到的消息并不是立刻就被处理,而是必须要等到ros::spin()或ros::spinOnce()执行的时候才被调用 他们的区别在于ros::spinOnce调用后会继续执行其后面的语句,而ros::spin()则在调用后不会继续执行其后面的语句 */ ros::spinOnce(); //向STM32端发送数据,前两个为double类型,最后一个为unsigned char类型,其函数相关定义在mbot_linux_serial.cpp有描述 writeSpeed(testSend1,testSend2,testSend3); //从STM32接收数据,输入参数依次转化为小车的线速度、角速度、航向角(角度)、预留控制位,其函数相关定义在mbot_linux_serial.cpp有描述 readSpeed(testRece1,testRece2,testRece3,testRece4); //打印数据 ROS_INFO("%f,%f,%f,%dn",testRece1,testRece2,testRece3,testRece4); //等待100ms的时间 loop_rate.sleep(); } return 0; } mbot_linux_serial.cpp文件 //包含对应头文件 #include "mbot_linux_serial.h" using namespace std;//设定工作空间的名称 using namespace boost::asio;//设定工作空间的名称 //串口相关对象 //创建一个 io_service实例 boost::asio::io_service iosev; //构造一个串口,将"/dev/ttySUB0"转移给实例iosev boost::asio::serial_port sp(iosev, "/dev/ttyUSB0"); boost::system::error_code err; /******************************************************** 串口发送接收相关常量、变量、共用体对象 ********************************************************/ const unsigned char ender[2] = {0x0d, 0x0a};//数据尾 const unsigned char header[2] = {0x55, 0xaa};//数据头 //发送左右轮速控制速度共用体 union sendData { short d; unsigned char data[2]; }leftVelSet,rightVelSet; //接收数据(左轮速、右轮速、角度)共用体(-32767 - +32768) union receiveData { short d; unsigned char data[2]; }leftVelNow,rightVelNow,angleNow; /******************************************************** 函数功能:串口参数初始化 入口参数:无 出口参数: ********************************************************/ void serialInit() { sp.set_option(serial_port::baud_rate(115200));//设置波特率 sp.set_option(serial_port::flow_control(serial_port::flow_control::none));//流量控制 sp.set_option(serial_port::parity(serial_port::parity::none));//奇偶校验 sp.set_option(serial_port::stop_bits(serial_port::stop_bits::one));//停止位 sp.set_option(serial_port::character_size(8)); //字符大小 } /******************************************************** 函数功能:将对机器人的左右轮子控制速度,打包发送给下位机 入口参数:机器人线速度、角速度 出口参数: ********************************************************/ void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag) { unsigned char buf[11] = {0};// int i, length = 0; leftVelSet.d = Left_v;//mm/s rightVelSet.d = Right_v; // 设置消息头 for(i = 0; i < 2; i++) buf = header; //buf[0] buf[1] // 设置机器人左右轮速度 length = 5; buf[2] = length; //buf[2] for(i = 0; i < 2; i++) { buf[i + 3] = leftVelSet.data; //buf[3] buf[4] buf[i + 5] = rightVelSet.data; //buf[5] buf[6] } // 预留控制指令 buf[3 + length - 1] = ctrlFlag; //buf[7] // 设置校验值、消息尾 buf[3 + length] = getCrc8(buf, 3 + length);//buf[8] buf[3 + length + 1] = ender[0]; //buf[9] buf[3 + length + 2] = ender[1]; //buf[10] // 通过串口下发数据 boost::asio::write(sp, boost::asio::buffer(buf)); } /******************************************************** 函数功能:从下位机读取数据 入口参数:机器人左轮轮速、右轮轮速、角度,预留控制位 出口参数:bool ********************************************************/ bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag) { char i, length = 0; unsigned char checkSum; unsigned char buf[150]={0}; //========================================================= //此段代码可以读数据的结尾,进而来进行读取数据的头部 try { boost::asio::streambuf response; boost::asio::read_until(sp, response, "rn",err); copy(istream_iterator istream_iterator buf); } catch(boost::system::system_error &err) { ROS_INFO("read_until error"); } //========================================================= // 检查信息头 if (buf[0]!= header[0] || buf[1] != header[1]) //buf[0] buf[1] { ROS_ERROR("Received message header error!"); return false; } // 数据长度 length = buf[2]; //buf[2] // 检查信息校验值 checkSum = getCrc8(buf, 3 + length); //buf[10] 计算得出 if (checkSum != buf[3 + length]) //buf[10] 串口接收 { ROS_ERROR("Received data check sum error!"); return false; } // 读取速度值 for(i = 0; i < 2; i++) { leftVelNow.data = buf[i + 3]; //buf[3] buf[4] rightVelNow.data = buf[i + 5]; //buf[5] buf[6] angleNow.data = buf[i + 7]; //buf[7] buf[8] } // 读取控制标志位 ctrlFlag = buf[9]; Left_v =leftVelNow.d; Right_v =rightVelNow.d; Angle =angleNow.d; return true; } /******************************************************** 函数功能:获得8位循环冗余校验值 入口参数:数组地址、长度 出口参数:校验值 ********************************************************/ unsigned char getCrc8(unsigned char *ptr, unsigned short len) { unsigned char crc; unsigned char i; crc = 0; while(len--) { crc ^= *ptr++; for(i = 0; i < 8; i++) { if(crc&0x01) crc=(crc>>1)^0x8C; else crc >>= 1; } } return crc; } mbot_linux_serial.h文件 //类似stm32程序头文件的编写规则 #ifndef MBOT_LINUX_SERIAL_H #define MBOT_LINUX_SERIAL_H #include #include #include #include #include #include #include extern void serialInit(); extern void writeSpeed(double Left_v, double Right_v,unsigned char ctrlFlag); extern bool readSpeed(double &Left_v,double &Right_v,double &Angle,unsigned char &ctrlFlag); unsigned char getCrc8(unsigned char *ptr, unsigned short len); #endif 三、ROS与STM32串口通信实验及运行效果 在STM32端,下载测试工程文件后,打开keil5软件,对程序进行编译烧录;在ROS端,打开vscode软件,在/home/ubuntu/fjy_xm路径下创建catkin_serial/src文件,对工作空间进行编译,并在/catkin_serial/src文件加下添加topic_example功能包,再进行工作空间的编译,并设置环境变量source devel/setup.bash 将TTY转USB模块按照对应的连接关系分别与STM32和ROS端相连,确保设备连接好之后,上电,首先需要查看以下串口设备,显示如下,说明设备之间连接正常 #查看串口设备 其次,为串口添加超级用户权限 #添加设备权限然后,新建一个终端 ,启动rosmaster,即在终端中输入roscore 在原终端下运行publish_node节点 rosrun topic_example publish_node ROS端正常接收到STM32端发送过来的信息,stm32查看ROS端发送过来的信息,可以在串口调试助手上进行查看,这样,我们就完成了一个简单的ROS与STM32之间的串口通信,为我们的ROS操作系统的开发提供了些许帮助 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1792 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1626 浏览 1 评论
1094 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
732 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1682 浏览 2 评论
1943浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
740浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
577浏览 3评论
600浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
562浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 01:49 , Processed in 0.814383 second(s), Total 48, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号