最近一直在研究如何实高性能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的架构。
现在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比较详细的介绍。
在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个字节。
下面是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。