总代码在文末,需要完整的工程文件可以私聊(收一点点辛苦费)
首先创建一个功能包,用于发送和接收数据。
注意:
1.功能包依赖: roscpp std_msgs rosserial
2.当有两个c++文件进行编译时可以在功能包下的CMakeLists.txt文件中
add_executable(publish_node src/publish_node.cpp
src/mbot_linux_serial.cpp)
第一部分是ROS上位机给stm32发送数据。第二部分是stm32给ROS上位机发送数据。下面已经给出每个部分要用到的函数,直接调用即可。第三部分给出完整的stm32与ros上位机的文件代码。
要注意的是共用体的运用
union sendData
{
short d;
unsigned char data[2];
}leftVelNow,rightVelNow,angleNow;
//左右轮速控制速度共用体
union receiveData
{
short d;
unsigned char data[2];
}leftVelSet,rightVelSet;
用了这个结构就可以让发送的数据扩充到2字节,如果有需要可以更大。
short d 和 unsigned char data[2] 共用一段内存,发送的时候short类型的数据分两次发送出去,接收到后两段数据通过共用体结构体来读取出来。
1.1 ROS发送数据
发送的数据结构
帧数 | 数据 |
---|---|
第1帧 | 0x55 |
第2帧 | 0xaa |
第3帧 | 发送的有效数据的长度(5字节) |
第4帧 | 数据1 |
第5帧 | 数据1 |
第6帧 | 数据2 |
第7帧 | 数据2 |
第8帧 | 数据3 |
第9帧 | 0x0d |
第10帧 | 0x0a |
/********************************************************
函数功能:将对机器人要控制的参数,打包发送给下位机
入口参数:机器人线速度、角速度
出口参数:
********************************************************/
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[i] = header[i]; //buf[0] buf[1]
// 设置机器人左右轮速度
length = 5;
buf[2] = length; //buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelSet.data[i]; //buf[3] buf[4]
buf[i + 5] = rightVelSet.data[i]; //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));
}
1.2 stm32接收数据
利用单片机的串口中断进行接收
//======================串口中断服务程序===========================
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
//首先清除中断标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
//从ROS接收到的数据,存放到下面三个变量中
usartReceiveOneData(&testRece1,&testRece2,&testRece3);
}
}
八位循环冗余校验函数,用于检验数据是否正确
/**************************************************************************
函数功能:计算八位循环冗余校验,被usartSendData和usartReceiveOneData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
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;
}
单片机的串口一次只能接收一个字节(8位)的数据。而如果需要接收一个完整数据帧,就要对接收函数进行处理。
/**************************************************************************
函数功能:通过串口中断服务函数,获取上位机发送的左右轮控制速度、预留控制标志位,分别存入参数中
入口参数:左轮轮速控制地址、右轮轮速控制地址、预留控制标志位
返回 值:无特殊意义
**************************************************************************/
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); //@@@@@#####如果你使用不是USART1更改成相应的,比如USART3
//接收消息头
if(Start_Flag == START)
{
//(1)进来的第一个数据 为0x55
//(2)第二个数据是 0xaa
if(USART_Receiver == 0xaa) //buf[1]
{
if(USARTReceiverFront == 0x55) //数据头两位 //buf[0]
{
Start_Flag = !START; //收到数据头,开始接收数据
//printf("header ok\n");
receiveBuff[0]=header[0]; //buf[0]
receiveBuff[1]=header[1]; //buf[1]
USARTBufferIndex = 0; //缓冲区初始化
checkSum = 0x00; //校验和初始化
}
}
else
{
USARTReceiverFront = USART_Receiver; // (1)0x55 保存起来
}
}
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)
{
//数据0d buf[9] 无需判断
k++;
}
else if (k==1)
{
//数据0a buf[10] 无需判断
//进行速度赋值操作
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;
}
2.1 stm32发送数据
/**************************************************************************
函数功能:将左右轮速和角度数据、控制信号进行打包,通过串口发送给Linux
入口参数:实时左轮轮速、实时右轮轮速、实时角度、控制信号(如果没有角度也可以不发)
返回 值:无
**************************************************************************/
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[i] = header[i]; // buf[0] buf[1]
// 设置机器人左右轮速度、角度
length = 7;
buf[2] = length; // buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelNow.data[i]; // buf[3] buf[4]
buf[i + 5] = rightVelNow.data[i]; // buf[5] buf[6]
buf[i + 7] = angleNow.data[i]; // 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));
}
/**************************************************************************
函数功能:发送指定大小的字符数组,被usartSendData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
void USART_Send_String(u8 *p,u16 sendSize)
{
static int length =0;
while(length<sendSize)
{
//@@@@@#####如果你使用不是USART1更改成相应的,比如USART3,这里有两处修改
while( !(USART1->SR&(0x01<<7)) );//发送缓冲区为空
USART1->DR=*p;
p++;
length++;
}
length =0;
}
2.2 ROS接收数据
/********************************************************
函数功能:从下位机读取数据
入口参数:机器人左轮轮速、右轮轮速、角度,预留控制位
出口参数: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, "\r\n",err);
copy(istream_iterator<unsigned char>(istream(&response)>>noskipws),
istream_iterator<unsigned char>(),
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[i] = buf[i + 3]; //buf[3] buf[4]
rightVelNow.data[i] = buf[i + 5]; //buf[5] buf[6]
angleNow.data[i] = buf[i + 7]; //buf[7] buf[8]
}
// 读取控制标志位
ctrlFlag = buf[9];
Left_v =leftVelNow.d;
Right_v =rightVelNow.d;
Angle =angleNow.d;
return true;
}
上位机串口初始化文件代码
mbot_linux_serial.cpp文件
#include "mbot_linux_serial.h"
using namespace std;
using namespace boost::asio;
//串口相关对象
boost::asio::io_service 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[i] = header[i]; //buf[0] buf[1]
// 设置机器人左右轮速度
length = 5;
buf[2] = length; //buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelSet.data[i]; //buf[3] buf[4]
buf[i + 5] = rightVelSet.data[i]; //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, "\r\n",err);
copy(istream_iterator<unsigned char>(istream(&response)>>noskipws),
istream_iterator<unsigned char>(),
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[i] = buf[i + 3]; //buf[3] buf[4]
rightVelNow.data[i] = buf[i + 5]; //buf[5] buf[6]
angleNow.data[i] = 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文件
#ifndef MBOT_LINUX_SERIAL_H
#define MBOT_LINUX_SERIAL_H
#include <ros/ros.h>
#include <ros/time.h>
#include <geometry_msgs/TransformStamped.h>
#include <tf/transform_broadcaster.h>
#include <nav_msgs/Odometry.h>
#include <boost/asio.hpp>
#include <geometry_msgs/Twist.h>
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
下位机stm32的串口配置代码
mbotLinuxUsart.c 文件
#include "mbotLinuxUsart.h"
#include "usart.h" //包含printf
/*--------------------------------发送协议-----------------------------------
//----------------55 aa size 00 00 00 00 00 crc8 0d 0a----------------------
//数据头55aa + 数据字节数size + 数据(利用共用体) + 校验crc8 + 数据尾0d0a
//注意:这里数据中预留了一个字节的控制位,其他的可以自行扩展,更改size和数据
--------------------------------------------------------------------------*/
/*--------------------------------接收协议-----------------------------------
//----------------55 aa size 00 00 00 00 00 crc8 0d 0a----------------------
//数据头55aa + 数据字节数size + 数据(利用共用体) + 校验crc8 + 数据尾0d0a
//注意:这里数据中预留了一个字节的控制位,其他的可以自行扩展,更改size和数据
--------------------------------------------------------------------------*/
/**************************************************************************
通信的发送函数和接收函数必须的一些常量、变量、共用体对象
**************************************************************************/
//数据接收暂存区
unsigned char receiveBuff[16] = {0};
//通信协议常量
const unsigned char header[2] = {0x55, 0xaa};
const unsigned char ender[2] = {0x0d, 0x0a};
//发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
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); //@@@@@#####如果你使用不是USART1更改成相应的,比如USART3
//接收消息头
if(Start_Flag == START)
{
//(1)进来的第一个数据 为0x55
//(2)第二个数据是 0xaa
if(USART_Receiver == 0xaa) //buf[1]
{
if(USARTReceiverFront == 0x55) //数据头两位 //buf[0]
{
Start_Flag = !START; //收到数据头,开始接收数据
//printf("header ok\n");
receiveBuff[0]=header[0]; //buf[0]
receiveBuff[1]=header[1]; //buf[1]
USARTBufferIndex = 0; //缓冲区初始化
checkSum = 0x00; //校验和初始化
}
}
else
{
USARTReceiverFront = USART_Receiver; // (1)0x55 保存起来
}
}
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)
{
//数据0d buf[9] 无需判断
k++;
}
else if (k==1)
{
//数据0a buf[10] 无需判断
//进行速度赋值操作
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;
}
/**************************************************************************
函数功能:将左右轮速和角度数据、控制信号进行打包,通过串口发送给Linux
入口参数:实时左轮轮速、实时右轮轮速、实时角度、控制信号(如果没有角度也可以不发)
返回 值:无
**************************************************************************/
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[i] = header[i]; // buf[0] buf[1]
// 设置机器人左右轮速度、角度
length = 7;
buf[2] = length; // buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelNow.data[i]; // buf[3] buf[4]
buf[i + 5] = rightVelNow.data[i]; // buf[5] buf[6]
buf[i + 7] = angleNow.data[i]; // 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));
}
/**************************************************************************
函数功能:发送指定大小的字符数组,被usartSendData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
void USART_Send_String(u8 *p,u16 sendSize)
{
static int length =0;
while(length<sendSize)
{
//@@@@@#####如果你使用不是USART1更改成相应的,比如USART3,这里有两处修改
while( !(USART1->SR&(0x01<<7)) );//发送缓冲区为空
USART1->DR=*p;
p++;
length++;
}
length =0;
}
/**************************************************************************
函数功能:计算八位循环冗余校验,被usartSendData和usartReceiveOneData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
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;
}
/**********************************END***************************************/
mbotLinuxUsart.h 文件
定义了一些发送和接收需要的数据
#include "mbotLinuxUsart.h"
#include "usart.h" //包含printf
/*--------------------------------发送协议-----------------------------------
//----------------55 aa size 00 00 00 00 00 crc8 0d 0a----------------------
//数据头55aa + 数据字节数size + 数据(利用共用体) + 校验crc8 + 数据尾0d0a
//注意:这里数据中预留了一个字节的控制位,其他的可以自行扩展,更改size和数据
--------------------------------------------------------------------------*/
/*--------------------------------接收协议-----------------------------------
//----------------55 aa size 00 00 00 00 00 crc8 0d 0a----------------------
//数据头55aa + 数据字节数size + 数据(利用共用体) + 校验crc8 + 数据尾0d0a
//注意:这里数据中预留了一个字节的控制位,其他的可以自行扩展,更改size和数据
--------------------------------------------------------------------------*/
/**************************************************************************
通信的发送函数和接收函数必须的一些常量、变量、共用体对象
**************************************************************************/
//数据接收暂存区
unsigned char receiveBuff[16] = {0};
//通信协议常量
const unsigned char header[2] = {0x55, 0xaa};
const unsigned char ender[2] = {0x0d, 0x0a};
//发送数据(左轮速、右轮速、角度)共用体(-32767 - +32768)
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); //@@@@@#####如果你使用不是USART1更改成相应的,比如USART3
//接收消息头
if(Start_Flag == START)
{
//(1)进来的第一个数据 为0x55
//(2)第二个数据是 0xaa
if(USART_Receiver == 0xaa) //buf[1]
{
if(USARTReceiverFront == 0x55) //数据头两位 //buf[0]
{
Start_Flag = !START; //收到数据头,开始接收数据
//printf("header ok\n");
receiveBuff[0]=header[0]; //buf[0]
receiveBuff[1]=header[1]; //buf[1]
USARTBufferIndex = 0; //缓冲区初始化
checkSum = 0x00; //校验和初始化
}
}
else
{
USARTReceiverFront = USART_Receiver; // (1)0x55 保存起来
}
}
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)
{
//数据0d buf[9] 无需判断
k++;
}
else if (k==1)
{
//数据0a buf[10] 无需判断
//进行速度赋值操作
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;
}
/**************************************************************************
函数功能:将左右轮速和角度数据、控制信号进行打包,通过串口发送给Linux
入口参数:实时左轮轮速、实时右轮轮速、实时角度、控制信号(如果没有角度也可以不发)
返回 值:无
**************************************************************************/
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[i] = header[i]; // buf[0] buf[1]
// 设置机器人左右轮速度、角度
length = 7;
buf[2] = length; // buf[2]
for(i = 0; i < 2; i++)
{
buf[i + 3] = leftVelNow.data[i]; // buf[3] buf[4]
buf[i + 5] = rightVelNow.data[i]; // buf[5] buf[6]
buf[i + 7] = angleNow.data[i]; // 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));
}
/**************************************************************************
函数功能:发送指定大小的字符数组,被usartSendData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
void USART_Send_String(u8 *p,u16 sendSize)
{
static int length =0;
while(length<sendSize)
{
//@@@@@#####如果你使用不是USART1更改成相应的,比如USART3,这里有两处修改
while( !(USART1->SR&(0x01<<7)) );//发送缓冲区为空
USART1->DR=*p;
p++;
length++;
}
length =0;
}
/**************************************************************************
函数功能:计算八位循环冗余校验,被usartSendData和usartReceiveOneData函数调用
入口参数:数组地址、数组大小
返回 值:无
**************************************************************************/
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;
}
/**********************************END***************************************/
uart.c文件
进行串口的初始化文章来源:https://www.toymoban.com/news/detail-558022.html
void uart_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
可以加企鹅群 658476482 交流
全部工程代码也放在群文件文章来源地址https://www.toymoban.com/news/detail-558022.html
到了这里,关于ROS机器人制作(三)—— ROS上位机与stm32进行串口通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!