使用libpbdata协议,使用UART进行数据传输,方便通信
uild模块
MPU和MCU之间的统一通信接口模块。它封装实现了统一的MPU和MCU之间的数据接收发送接口,所有两个芯片间的通信都是通过此模块完成,它构建在底层的高速串口上,当然也可以很方便的构建在其他物理接口之上。
Uild报文包括包含起始标识、协议ID号、报文长度、CRC校验和报文内容。起始标记用来确定一个报文的开始,协议ID是各自模块定义的,只有关心此ID的模块才能处理此报文。各个模块初始化时要到uild上注册相应的处理函数。
MCU接收方向为例,它接收MPU uild模块发送的报文,并根据报文头部的ID号调用相应模块的处理接口进行数据的处理;发送方向提供发送接口,把MCU的报文发送到MPU的uild模块,再由MPU的uild模块调用相应模块的处理接口函数。
MPU uild模块:
uild 源码路径:pkgs\uild
uild库函数路径:pkgs\libuild
源码主要文件: uild_serial_proto.c uild_serial.c uild_net_server.c uild_net_proto.c uild_net_client.c serv_ubus.c uild_net_dispatch.c
MCU uild模块:
uild源码路径:frtosdk\protocols\uild
源码主要文件:uild_faw.c
MPU-MCU板间通信协议
MPU和MCU之间的通信是关键模块,底层协议构建在Protocol Buffer这个通信协议上。Protocol Buffer是 google公司出的一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式,可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。各个模块把自己要传输的信息写在一个叫.proto的文件里面,然后使用protobuf的编译器能方便生成各自对应的数据结构和操作这个数据结构的各种接口,如把数据结构系列化成数据流消息、解系列化数据流消息为数据结构,这其实就是对各个模块要传递消息的编码和解码操作。Protobuf 序列化后所生成的二进制消息非常紧凑,这得益于 Protobuf 采用的非常巧妙的 Encoding 方法,在这里不做详细的描述,有兴趣可以阅读文末附件文档。 所以,采用protobuf的好处是我们不需要自己去设计编码格式,不需要自己去写封包解包的函数,所以它的稳定性和性能都非常好。
每个模块要传输的信息已经被protobuf封装成它自己对应的格式,接下来这个信息要在板间的物理接口上传输,就要把protobuf的信息流打包成uild格式的报文。具体报文格式下下图:
HEAD(4byte) | LEN(4byte) | ID(1byte) | CRC(1byte) | DATA(len) |
所以整个的板间消息的传递流程就是模块调用protobuf生成的自己模块的打包函数接口将要传递的数据编码城protobuf格式,即上图的data部分,接着加上uild协议的报文头部生成uild传输的报文送到板间通信接口驱动发送(板间通信接口可以是uart或spi)。对端uild模块收到后解出报文头部,CRC校验通过后根据头部ID调用相应模块来处理data部分,模块调用protobuf的解包函数将data解析成模块关心的数据结构从而完成板间数据的交互。
我们以一个简单模块uversion为例来详细描述整个流程。
uversion模块是MPU获取MCU版本信息的一个模块,主要是为了升级和调试用,信息只有两种,版本编译时间和版本号。在.proto中按照规范定义为
//version
message Version {
required uint32 btime = 1;
required bytes os_name = 2;
}
使用编译器会生成对应的数据结构和解封包函数,在data.pb-c.h和data.pb-c.c中
数据结构:
struct _Version
{
ProtobufCMessage base;
uint32_t btime;
ProtobufCBinaryData os_name;
};
#define VERSION__INIT \
{ PROTOBUF_C_MESSAGE_INIT (&version__descriptor) \
, 0, {0,NULL} }
主要的三个操作接口:
version__get_packed_size (const Version *message)
Version * version__unpack (ProtobufCAllocator *allocator, size_t len,
const uint8_t *data)
size_t version__pack(const Version *messag, uint8_t *out)
MPU的uversion模块调用这三个接口把要传递的信息编码为protobuf格式,再打上uild头部信息通过uild接口发送给MCU。MCU首先解包出uild头部信息,调用version模块处理,模块同样调用MCU端生成的protobuf解包接口将信息解析出来,得到信息是要获取MCUversion,接下来把version的数据结构编码成protobuf格式,再打上uild头部包传递回MPU,MPU再从报文中解析出相应的信息显示出来。
uversionc
从MPU->MCU
mpu端:
send_uild_version (调用version__pack编码为protobuffer格式报文)->send_uild_packet(uild_net_dispatch.c/libuild模块,此处要封uild协议包)->send_client_data(uild_net_client)->ustream_write(本地socket发送到uild server)->
net_proto_recv(uild_net_proto.c/uild模块)->net_packet_dispatch(uild_net_proto.c/uild模块)->serial_proto_send(uild_serial_proto.c,打上uild包头)->
uild_serial_send(uild_serial.c)->ustream_write(直接发送到串口)->
mcu端:
spc560_fuart_rx_handle(spc5_uart.c)->uild_recv(解出包头,uild_fw.c)->uild_dispatch(uild_fw.c)-> version_get (解protobuf内容,获取版本信息,version.c)
从MCU->MPU
MCU端:
version_get (获取版本信息)-> version_send (版本信息编码成protobuf格式version.c)->uild_send(封装城uild协议报文 uild_fw.c)->uart_write(spc5_uart.c)->
MPU端:
serial_notify_read_cb(uild_serial.c)->serial_proto_recv(uild_serial_proto.c)->net_client_dispatch(uild_net_client.c)->net_proto_send(uild_net_proto.c,此处要封包)->
net_client_read_cb(uild_net_client.c)->recv_uild_packet(解包,uild_net_dispatch.c)->uild_packet_dispatch(uild_net_dispatch.c)-> handle_uild_packet (uversion模块的uild_if.c)-> handle_ioc_data(uversion模块的uild_if.c)->打印MCU版本信息
tsp模块:
tsp连接管理模块负责连接服务器以及设备与平台通信数据的收发,该模块定义为一个通用模块,负责向下MCU提供创建连接、数据收发以及发送缓存等接口。具体什么时候连接服务器、服务器断开后怎样处理由MCU程序处理
MPU tsp模块:
源码路径:pkgs\utspd
源码主要文件:uild_if.c,misock.c,tsp_cmd.c
MCU tsp模块:
源码路径:logread\ protocols\uild
源码文件:uild_tsp.c