ROS机器人制作(三)—— ROS上位机与STM32串口通信详解

ROS上位机与stm32进行串口通信

  • 1.1 ROS发送数据
  • 1.2 stm32接收数据
  • 2.1 stm32发送数据
  • 2.2 ROS接收数据
  • 上位机串口初始化文件代码
  • 下位机stm32的串口配置代码
  • 总代码在文末,需要完整的工程文件可以私聊(收一点点辛苦费)

    首先创建一个功能包,用于发送和接收数据。
    注意:
    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文件
    进行串口的初始化

    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 
    
    }
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » ROS机器人制作(三)—— ROS上位机与STM32串口通信详解

    发表评论