Linux 上串口PRC 协议的实现

      最近一直在研究如何实高性能Cortex-A 处理器在工业控制,物联网领域的应用。Cortex-A 内核的SOC 芯片通常运行linux OS ,直接支持的硬件接口不多,而且不够灵活。我们倾向采用cortex-M 单片机作为Cortex-A的扩展IO来使用。为了提高软件研发的效率cortex-M 运行Arm 公司的Mbed OS。

那么问题来了,Cortex-M 和Cortex-A 之间如何通信和控制呢?硬件方面我们尝试果Ethernet,SPI 和UART。软件方面采用各种自定义的交换协议。本文介绍如何在USB 串口上实现RPC 协议。

    RPC 的全称是远程过程调用(Remote procedure call),它是一个比较古老的协议。伴随着TCP/IP 协议的出现就出现了。 RPC 是request/response的架构。

   

Linux 上串口PRC 协议的实现_第1张图片

        现在RPC 协议可以在许多协议上实现,例如HTTP。当然也可以在串口上实现。

信息格式更加倾向于XML和json格式。例如在json rpc 协议中:

--> {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}

<-- {"jsonrpc": "2.0", "result": 19, "id": 1}

    我在另一篇博文《关于jsonrpc》一文中有关于jsonrpc比较详细的介绍。

RPC 在串口上的实现

    在cortex-M 的串口上实现json 格式的PRC 开销比较大,需要进行json 的解析。涉及数据格式转换,传输的时间也占有比较大,所以我们并不倾向使用jsonrpc 来实现cotex-M上的RPC协议,而是采纳了类似MODBUS RTU帧结构的形式来实现

请求(Request)

Address(1 Byte)

method(1 Byte)

length(2 Byte)

parameters(N Byte)

LRC(2 Byte)

响应(Response)

 

Address(1 Byte)

method(1 Byte)

length(2 Byte)

results(N Byte)

LRC(2 Byte)

 address,method 是1 个字节,length 为两个字节,高位在前,低位在后。

 results和parameters 是长度为length 的字节。而LRC是2个字节。

serialRPC 类

 下面是serial PRC类的实现。

linux 端(serialRPC.hpp)

#include "serialPort.hpp"
#include 
class serialRPC 
{
private:
   
	uint8_t _method;
	uint8_t _address;
	uint8_t sbuf[32];
    uint8_t rbuf[32];
	unsigned int calculateCRC16( uint8_t *puchMsg,unsigned int  usDataLen);
public:
    serialRPC();
    bool init(const char * dev);	
    bool callMethod(uint8_t address,uint8_t Method,uint8_t *data,int length );
	bool waitResult(uint8_t * data);
};

serialRPC.cpp

#include "serialRPC.hpp"
#include 
#define    DEFAULT_BAUD_RATE   9600
 serialPort serial;
      const  uint8_t auchCRCHi[256]={
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40
};
const  uint8_t auchCRCLo[256]={
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
    0x40
};
 serialRPC::serialRPC() 
			{
			_method=-1;
			}
 bool serialRPC::init(const char * dev)
 {            cout<<"seerial init:"<< endl;
	         serial.OpenPort(dev);
             cout<<"serial Setup"<< endl;			 
	   return  serial.setup(19200,0,8,1,'N');  
 
			 
 } 
 bool serialRPC::callMethod(uint8_t address,uint8_t method,uint8_t *data,int length )
 {
	  int index,i;
    index=0;
	 _address=address;
     _method=method;
    sbuf[index++]=address;
    sbuf[index++]=method;
    sbuf[index++]=(length>>8)&0xff;
    sbuf[index++]=length&0xff;
	for (i=0;i>8)&0xff;
    sbuf[index++]=resultCRC&0xff; 
	serial.writeBuffer(sbuf, index);
    return true;
 }
 bool serialRPC::waitResult(uint8_t * data)
 {
	   unsigned short resultCRC ,recCRC;
    
    int state,index,i;
	uint16_t len,cnt;
    uint8_t ch;
    bool flag;
     flag=true;
     state=0;
     index=0;
     while(flag)
     { 
         ch=serial.getchar();
        
         switch(state)
         {
             case 0:{
                 if (ch==_address)
                   {state=1;
                   rbuf[index++]=ch;}
                 break;
                 }
            case 1:{
               
              if (ch==_method)
                   {state=2;rbuf[index++]=ch;}
                   else if(ch==(_method+0x80))
                     state=10;
                     else 
                     return 0;
                 break;
                 }
             case 2:{
                    len=ch;
                    rbuf[index++]=ch;
                    state=3;
                    break;
                    } 
			case 3:{
				   len=(len<<8)&0xff00+ch;cnt=len;
				    rbuf[index++]=ch;
					if (cnt>0)
                    state=4;
				else 
					 state=5;
                    break;
			}		
             case 4:{
                    rbuf[index++]=ch;
                    cnt--;
                    if (cnt==0)
                      { 
                      state=5;
                     
                      }
                    break;
                    }
              case 5:{
                     recCRC=ch;
                     state=6;
                     break;
                     } 
              case 6:{
                     recCRC=(recCRC<<8)|ch;
                      resultCRC =  calculateCRC16(rbuf,index);  
                   //  printf("CRC=%02x,%02x\n",recCRC,resultCRC);
                     if (recCRC==resultCRC) {
                         for (i=0;i

mbed 端

serialRPC.h

#include "mbed.h"
#include "USBSerial.h"
class serialRPC
{
    public :
    serialRPC();
    bool connect();
    uint8_t waitCall(uint8_t *data);
     bool sendResult(uint8_t *data,int length);
    private:
      USBSerial serial;
      int _method;
      int _address;
      uint8_t sbuf[32];
      uint8_t rbuf[32];
      void readBuffer(uint8_t * buf,int length);
      void writeBuffer(uint8_t * buf,int length);
      unsigned int  calculateCRC16( uint8_t *puchMsg,unsigned int  usDataLen);
    };
    

serialRPC.cpp(mbed OS)

#include "serialRPC.h"
#define   DEFAULT_BAUD_RATE   115200
    const  char auchCRCHi[256]={
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40
};
const  char auchCRCLo[256]={
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
    0x40
};

serialRPC::serialRPC() 
:serial(false,0x1f00,0x2012,0x0001) 
{   
   
    serial.init();
    _address=0x00;
}
bool serialRPC::connect()
{
   serial.connect();
   serial. wait_ready();
    return true;
    }
uint8_t  serialRPC::waitCall(uint8_t *data)
{  
      unsigned short resultCRC ,recCRC,len,cnt;    
    int state,index,i;
    uint8_t ch;
    bool flag;
     flag=true;
     state=0;
     index=0;
     while(flag)
     { 
     while(! serial.readable()){};
      ch=serial.getc();
        printf("0x%2.2X ",ch);
         switch(state)
         {
             case 0:{
                    _address=ch;
                if (_address==0x00)
                 {  
                  state=1;
                   rbuf[index++]=ch; 
                   }
                 break;
                 }
            case 1:{                            
                   _method=ch;
                   state=2;
                   rbuf[index++]=ch;                  
                 break;
                 }
             case 2:{
                    len=ch;
                    rbuf[index++]=ch;
                    state=3;
                    break;
                    }
             case 3:{
                    len=(len<<8)&0xff00|ch;
                    rbuf[index++]=ch;
                    if (len>0)
                    {
                        cnt=len;
                        state=4;
                      } else state=5;
                     break; 
                    }        
             case 4:{
                    rbuf[index++]=ch;
                    cnt--;
                    if (cnt==0)
                      { 
                      state=5;
                     
                      }
                    break;
                    }
              case 5:{
                     recCRC=ch;
                     state=6;
                     break;
                     } 
              case 6:{
                     recCRC=(recCRC<<8)|ch;
                      resultCRC =  calculateCRC16(rbuf,index);  
                     printf("CRC=0x%2.2X 0x%2.2X\n",recCRC,resultCRC);
                     if (recCRC==resultCRC) {
                         for (i=0;i>8)&0xff;
      sbuf[index++]= length&0xff;
    for (i=0;i>8)&0xff;
     sbuf[index++]=resultCRC &0xff;
     writeBuffer(sbuf,index);
      return true;
    }
 void serialRPC::readBuffer(uint8_t * buf,int length)
{
    int i;
    for (i=0;i

测试程序

该测试软件由linux 程序调用mbed OS 上的flipflop 程序,控制LED1 灯翻转。

Mbed  端

#include "mbed.h"
#include "serialRPC.h"
#include "method.h"
DigitalOut led1(LED1);
DigitalOut led2(LED2);
uint8_t buf[32];
serialRPC rpc;
int main()
{ 
uint8_t Method;
uint8_t val;
 led1=1;
 led2=1;
  printf("RPC over Serial Test\n");
  rpc.connect();
  printf("connected\n");
    while (true) {
     Method=rpc.waitCall(buf);
     switch (Method)
     {
       case DegitalOut_Write:{
           switch(buf[0])
           {
            case 1:{led1=buf[1];break;}
            case 2:{led2=buf[1];break;}
            }
            buf[0]=OK;
            rpc.sendResult(buf,1);
            break;
            }
       case DegitalOut_Read:{
               switch(buf[0])
           {
            case 1:{val=led1;break;}
            case 2:{val=led2;break;}
            }
            buf[0]=val;
            rpc.sendResult(buf,1);
            break;
            } 
       case DegitalOut_Filpflop:{
             switch(buf[0])
           {
            case 1:{led1=!led1;break;}
            case 2:{led2=!led2;break;}
            }
            buf[0]=OK;
          rpc.sendResult(buf,1);
            break;
            }  
        }
     }   
}

linux 端(serialRPCTest)

#include "serialRPC.hpp"
#include 
#include 
#define DigitalOut_Write  	   0
#define DigitalOut_Read    	   1
#define DigitalOut_Filpflop    2  
  serialRPC interface;
class DigitalOut
{   private:
    
	  int _gpio;
	  bool _value;
	  uint8_t buf[32];
	public:
	   DigitalOut(int gpio)
	   {
		  _gpio= gpio;
		  
	   }

	   void write(bool value)	
	   {   
	       buf[0]=_gpio;
		   buf[1]=value;
		   interface.callMethod(0,DigitalOut_Write,buf,2);
		   interface.waitResult(buf);
		   _value=value;
	   }
	   bool read ()
	   {
		    buf[0]=_gpio;
		   interface.callMethod(0,DigitalOut_Read,buf,1);
		    interface.waitResult(buf);
		   return _value;
	   }
	 char  flipflops()
	  {    cout<<"Send Flipflop"<< endl;
		   buf[0]=_gpio;
		   interface.callMethod(0,DigitalOut_Filpflop,buf,1);		   
		   interface.waitResult(buf);
		   cout <<"result:0x"<< std::setfill('0') << std::setw(2);
		   cout<

实现 LED 闪烁,也可以写成:

  

bool val;

  val=!LED.read();
   LED.write(val);

但是交互的数据多了一点。所以我设计了一个Flipflop。

 

你可能感兴趣的:(mbed,linux,C++)