前言: 实际项目中,一个单片机经常要和另一个单片机通信,需要自定义一些通信协议,实现可靠通信,自己尝试写过一些,但没什么面向对象的精华,读RT-Thread简介看到了RT-Link,对于RT-Link个人总结如下。 每包发送数据为(4个字节的包头+4字节的帧数据信息+传输内容+4字节的crc),每包数据多12字节的传输开销,就可以实现数据重传、帧序号检查、状态同步等一系列能力,保证传输的稳定。一包数据的最大长度可配置,多个包数据可以自动组合成需要的一帧长数据。 发送结果,有回调通知,通知成功的话,能确保接收端收到了信息。 接收到数据,会处理成发送端的一帧有效传输内容,再回调通知。
RT-Link 介绍
初步看RT-Link的介绍,这个组件可以建立点对点的不同端口通信,一开始理解是能让单片机的进程和进程通信,无线射频设备可以建立不同的信道通信,但需要模组硬件跟着适配,但读了源码发现,这功能是可以建立不同硬件的点对点通信,一个主MCU/SOC可以和多个从MCU/SOC建立不同的串口通信或者无线设备通信,这就很nice,符合应用需求。这个理解误差也让我觉得读懂源码是挺好的学习,要实现对项目的具体功能扩展和使用,就需要对源码的机制了解,才会无bug。
开始记录自己读源码RT_Link V0.2.0的理解,首先是看官方使用例程,看应用层和要实现的接口是什么,然后是看最重要的头文件”rtlink.h“,理解有什么数据结构和协议。注解的包和帧说明,一帧可能是多包组合而成,也可能一包组成,每次硬件发送一次数据,称为一包。
1 应用层接口
1.1 使用方式
发送调用接口如下 rt_size_t rt_link_port_send(void *data, rt_size_t length); 接收会有一个回调函数入口设置。
1.2 应用和驱动层要实现的接口
/* 需要在传输端口中实现的功能 / rt_err_t rt_link_port_init(void);//对通信的硬件初始化,uart、iic、spi等。 rt_err_t rt_link_port_deinit(void);//去初始化,如果用了动态内存初始化,需要释放 rt_err_t rt_link_port_reconnect(void);//重连,可以建立一个握手机制或者不需要 / 当通信的硬件接收到数据要写入这个API,一般是硬件中断接收完一包写入*/ rt_size_t rt_link_hw_write_cb(void data, rt_size_t length); / service 结构体对象初始化 */ rt_err_t rt_link_service_attach(struct rt_link_service *serv)
static struct rt_link_service serv_socket;
struct rt_link_service
{
rt_int32_t timeout_tx;
void (*send_cb)(struct rt_link_service *service, void *buffer);
void (*recv_cb)(struct rt_link_service *service, void *data, rt_size_t size);
void *user_data;
rt_uint8_t flag;
rt_link_service_e service;
rt_link_linkstate_e state;
rt_link_err_e err;
};
typedef enum
{
RT_LINK_SERVICE_RTLINK = 0,
RT_LINK_SERVICE_SOCKET = 1,
RT_LINK_SERVICE_WIFI = 2,
RT_LINK_SERVICE_MNGT = 3,
RT_LINK_SERVICE_MSHTOOLS = 4,
RT_LINK_SERVICE_MAX
} rt_link_service_e;
typedef enum
{
RT_LINK_INIT = 0,
RT_LINK_DISCONN = 1,
RT_LINK_CONNECT = 2,
} rt_link_linkstate_e;
int rtlink_exinit(void)
{
serv_socket.service = RT_LINK_SERVICE_SOCKET;
serv_socket.timeout_tx = RT_WAITING_FOREVER;
serv_socket.flag = RT_LINK_FLAG_ACK | RT_LINK_FLAG_CRC;
serv_socket.recv_cb = recv_cb;
serv_socket.send_cb = send_cb;
rt_link_service_attach(&serv_socket);
}
理解实现以上接口,就能使用这套源码了,下面是对这套源码的配置使用。
2 配置使用
配置使用就在”rtlink.h“头文件,理解宏定义和源码的一些操作有利于改动配置
#ifndef __RT_LINK_H__
#define __RT_LINK_H__
#include <rtdef.h>
#define RT_LINK_VER "0.2.0"
#define RT_LINK_AUTO_INIT
#define RT_LINK_FLAG_ACK 0x01
#define RT_LINK_FLAG_CRC 0x02
#define RT_LINK_FRAME_HEAD 0x15
#define RT_LINK_FRAME_HEAD_MASK 0x1F
rt_link_frame_init这个API被3个API调用 函数原型如下,可以看到2个传的是空,rt_link_send传入的是rt_link_service_attach配置结构的标志,来确定协议是否要ack和crc标志。
static int rt_link_frame_init(struct rt_link_frame *frame, rt_uint8_t config)
{
if (frame == RT_NULL)
{
return -RT_ERROR;
}
rt_memset(&frame->head, 0, sizeof(struct rt_link_frame_head));
if (config & RT_LINK_FLAG_CRC)
{
frame->head.crc = 1;
}
if (config & RT_LINK_FLAG_ACK)
{
frame->head.ack = 1;
}
frame->head.magicid = RT_LINK_FRAME_HEAD;
rt_memset(&frame->extend, 0, sizeof(struct rt_link_extend));
frame->crc = 0;
frame->real_data = RT_NULL;
frame->data_len = 0;
frame->index = 0;
frame->total = 0;
frame->attribute = RT_LINK_RESERVE_FRAME;
frame->issent = RT_LINK_FRAME_NOSEND;
rt_slist_init(&frame->slist);
return RT_EOK;
}
数据包占用了4字节,定义用了位域,其中magicid、extend 、crc 、ack合起来用了一个字节,sequence一个字节, service+length 2个字节
struct rt_link_frame_head
{
rt_uint8_t magicid : 5;
rt_uint8_t extend : 1;
rt_uint8_t crc : 1;
rt_uint8_t ack : 1;
rt_uint8_t sequence;
rt_uint16_t service: 5;
rt_uint16_t length : 11;
};
要梳理的是位域的存储,存储需要地址,其次是对应的RMA和ROM采用的是大端还是小段存储。 假设每个bit都有一个地址,实际上最小单位是每个字节才有一个地址,因为最小的指针是1字节类型,用位域去的定义,能访问到值的大小,但是不能访问位域名的地址,不然会报错。做了以下实验,这个实验平台的执行环境判断是小端存储。 在用位域定义后,赋值时候就会以多少个bit给到位域访问名称,截图用注释说明了实际存储的bit。重点留意11015535这个数据,即打印a结构体的内容。 我们会发现magicid存在0x404032这个地址的最低的5bit,extend 存在0x404032这个地址的最低的第6bit,0x404032地址字节的二进制就是00110101就是0x35(第一个bit是ack,第二个bit是crc,程序没有赋值,但是全局变量不赋值,定义在ZI-data数据段,当这个程序执行时,对应的启动内核一般将ZI-data数据段清零了,要是将a定义成局部变量,结果就不一定了,局部变量也是一个地址,地址不变,并且对这个地址的内容操作不变,结果就一定)
sequence存在0x404033这个地址,存储的是0x55。service是5bit,所以赋值1就是00001。 最终打印a结构变量值的时候,显示11015535,我们知道第一个字节地址0x404032存储的是0x35,第二个字节地址0x404033存储的是0x55,第二个字节地址比第一个字节地址高,第3个字节存储的是0x01,第4个字节存储的是0x11,所以a结构体内容为(11015535),表示一个数,大数写在最前面,也称高位,即低地址内容存在了低位,高地址内容存在了高位,属于小端存储。 如果访问a.length会发现是0x88,但不能访问a.length的地址,具体怎么能得到0x88的,和c库的位域源码处理相关,推测是取某个地址附近的11个比特,赋值到2个字节的RAM存储空间去运行。
以上是表诉可能有些抽象,主要是为了说明下面两个宏定义 #define RT_LINK_FRAME_HEAD 0x15 #define RT_LINK_FRAME_HEAD_MASK 0x1F 0x15属于0x00-0x1F的范围,可以定义为这个头标识,掩码是0x1F(000011111)的原因就是默认magicid这5个bit是小端存储。也就是这个协议包的发送和识别默认是小端存储,如果小端的数据包到了大端运行的电子设备里,需要做字节序调整,大部分嵌入式设备都是小端存储。
#define RT_LINK_FRAMES_MAX 0x03
#define RT_LINK_MAX_FRAME_LENGTH 1024
#define RT_LINK_ACK_MAX 0x07
#define RT_LINK_CRC_LENGTH 4
#define RT_LINK_HEAD_LENGTH 4
#define RT_LINK_EXTEND_LENGTH 4
#define RT_LINK_MAX_DATA_LENGTH (RT_LINK_MAX_FRAME_LENGTH - \
RT_LINK_HEAD_LENGTH - \
RT_LINK_EXTEND_LENGTH - \
RT_LINK_CRC_LENGTH)
#define RT_LINK_RECEIVE_BUFFER_LENGTH (RT_LINK_MAX_FRAME_LENGTH * \
RT_LINK_FRAMES_MAX + \
RT_LINK_HEAD_LENGTH + \
RT_LINK_EXTEND_LENGTH)
综合上面的信息,根据实际的硬件通信,去定义发送包最大,根据单片机自身RAM考虑接收fifo的大小,要配置的宏,初步看3个就够了。 是否要用RTT的自动初始化 #define RT_LINK_AUTO_INIT 一帧数据的最大分包数 #define RT_LINK_FRAMES_MAX 0x03 一包数据的最大长度 #define RT_LINK_MAX_FRAME_LENGTH 1024
考虑实际的应用,有些硬件设备,一包的数据最大长度往往不会很大,比如无线通信模组,发送的fifo字节可能不到128字节,如果不想用模组的发送中断,直接简单的填fifo就发送出去,那么RT_LINK_MAX_FRAME_LENGTH宏应该定义为小于通信模组的发送fifo,方便硬件接口的实现,RT_LINK_FRAMES_MAX 可以定义大一些。应用层能一次发送的最大buff为 RT_LINK_FRAMES_MAX * RT_LINK_MAX_DATA_LENGTH 如果不涉及模组的发送和接收fifo大小,用串口、485等硬件通信,考虑双方发送和接收能力,再去定义发送和接收的大小。
3 RT-Link的协议处理
3.1 RT-Link的发送处理
从应用层的rt_size_t rt_link_port_send(void *data, rt_size_t length); 向下追踪经过RTT的设备和驱动框架,调用的是下面的API
3.1.1 rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size)
rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size)
{
RT_ASSERT(service != RT_NULL);
rt_uint32_t recved = 0;
rt_uint8_t total = 0;
rt_uint8_t index = 0;
rt_size_t offset = 0;
rt_size_t send_len = 0;
struct rt_link_frame *send_frame = RT_NULL;
rt_link_frame_attr_e attribute = RT_LINK_SHORT_DATA_FRAME;
if ((size == 0) || (data == RT_NULL))
{
service->err = RT_LINK_ERR;
goto __exit;
}
service->err = RT_LINK_EOK;
if (size % RT_LINK_MAX_DATA_LENGTH == 0)
{
total = size / RT_LINK_MAX_DATA_LENGTH;
}
else
{
total = size / RT_LINK_MAX_DATA_LENGTH + 1;
}
if (total > RT_LINK_FRAMES_MAX)
{
service->err = RT_LINK_ENOMEM;
goto __exit;
}
else if (total > 1)
{
attribute = RT_LINK_LONG_DATA_FRAME;
}
do
{
send_frame = rt_malloc(sizeof(struct rt_link_frame));
if (send_frame == RT_NULL)
{
service->err = RT_LINK_ENOMEM;
goto __exit;
}
rt_link_frame_init(send_frame, service->flag);
send_frame->head.sequence = ++rt_link_scb->tx_seq;
send_frame->head.service = service->service;
send_frame->real_data = (rt_uint8_t *)data + offset;
send_frame->index = index;
send_frame->total = total;
if (attribute == RT_LINK_LONG_DATA_FRAME)
{
send_frame->attribute = RT_LINK_LONG_DATA_FRAME;
if (offset + RT_LINK_MAX_DATA_LENGTH > size)
{
send_frame->data_len = size - offset;
}
else
{
send_frame->data_len = RT_LINK_MAX_DATA_LENGTH;
offset += RT_LINK_MAX_DATA_LENGTH;
}
rt_link_frame_extend_config(send_frame, RT_LINK_LONG_DATA_FRAME, size);
}
else
{
send_frame->attribute = RT_LINK_SHORT_DATA_FRAME;
send_frame->data_len = size;
}
LOG_D("append send slist, seq(%d), len(%d)", send_frame->head.sequence, send_frame->data_len);
rt_slist_append(&rt_link_scb->tx_data_slist, &send_frame->slist);
index++;
send_len += send_frame->data_len;
}while(total > index);
rt_event_send(&rt_link_scb->event, RT_LINK_SEND_READY_EVENT);
if (service->timeout_tx != RT_WAITING_NO)
{
rt_err_t ret = rt_event_recv(&rt_link_scb->sendevent, (0x01 << service->service),
RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
service->timeout_tx, &recved);
if (ret == -RT_ETIMEOUT)
{
service->err = RT_LINK_ETIMEOUT;
send_len = 0;
}
}
__exit:
return send_len;
}
rt_event_send(&rt_link_scb->event, RT_LINK_SEND_READY_EVENT);这个事件处理,最终调用的是下面的API
3.1.2 static void rt_link_send_ready(void)
static void rt_link_send_ready(void)
{
struct rt_link_frame *frame = RT_NULL;
rt_uint8_t seq = rt_link_scb->tx_seq;
if (rt_slist_next(&rt_link_scb->tx_data_slist))
{
frame = rt_container_of(rt_slist_next(&rt_link_scb->tx_data_slist), struct rt_link_frame, slist);
}
if (rt_link_scb->state != RT_LINK_CONNECT)
{
rt_link_scb->state = RT_LINK_DISCONN;
rt_link_command_frame_send(RT_LINK_SERVICE_RTLINK, seq,
RT_LINK_HANDSHAKE_FRAME, rt_link_scb->rx_record.rx_seq);
rt_int32_t timeout = 50;
rt_timer_control(&rt_link_scb->sendtimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->sendtimer);
}
else
{
if ((frame != RT_NULL) && (frame->issent == RT_LINK_FRAME_NOSEND))
{
if (RT_EOK != rt_link_frame_send(&rt_link_scb->tx_data_slist))
{
rt_link_scb->state = RT_LINK_DISCONN;
rt_link_service_send_finish(RT_LINK_EIO);
}
}
}
}
从rt_link_send_ready的调用,可以看到是调用了下面2个API /* performs data transmission */ static rt_err_t rt_link_frame_send(rt_slist_t *slist)
//这个API是发送协议ack包,协议自身机制的东西,发送帧序号检查、状态同步等,保证传输的稳定; static int rt_link_command_frame_send(rt_uint8_t serv, rt_uint8_t sequence, rt_link_frame_attr_e attribute, rt_uint16_t parameter)
3.1.3 static rt_err_t rt_link_frame_send(rt_slist_t *slist)
static rt_err_t rt_link_frame_send(rt_slist_t *slist)
{
rt_uint8_t is_finish = RT_FALSE;
struct rt_link_frame *frame = RT_NULL;
rt_uint8_t send_max = RT_LINK_ACK_MAX;
if (slist == &rt_link_scb->tx_data_slist)
{
slist = rt_slist_next(&rt_link_scb->tx_data_slist);
}
if (slist == RT_NULL)
{
LOG_W("send data list NULL");
return -RT_ERROR;
}
do
{
frame = rt_container_of(slist, struct rt_link_frame, slist);
slist = rt_slist_next(slist);
if (frame_send(frame) == 0)
{
rt_link_scb->service[frame->head.service]->err = RT_LINK_EIO;
goto __err;
}
frame->issent = RT_LINK_FRAME_SENT;
if ((slist == RT_NULL) || (frame->index + 1 >= frame->total))
{
send_max = 0;
is_finish = RT_TRUE;
}
else
{
send_max >>= 1;
}
}while (send_max);
if ((is_finish) && (frame->head.ack == 0))
{
rt_link_service_send_finish(RT_LINK_EOK);
if (slist != RT_NULL)
{
LOG_D("Continue sending");
rt_event_send(&rt_link_scb->event, RT_LINK_SEND_READY_EVENT);
}
}
else
{
rt_int32_t timeout = RT_LINK_SENT_FRAME_TIMEOUT;
rt_timer_control(&rt_link_scb->sendtimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->sendtimer);
}
return RT_EOK;
__err:
return -RT_ERROR;
}
从上面api发现还需要分析frame_send,3.1.5再分析。
下面API是发送协议ack包,协议自身机制的东西,发送帧序号检查、状态同步等,保证传输的稳定;
3.1.4 static int rt_link_command_frame_send(rt_uint8_t serv, rt_uint8_t sequence, rt_link_frame_attr_e attribute, rt_uint16_t parameter)
static int rt_link_command_frame_send(rt_uint8_t serv, rt_uint8_t sequence, rt_link_frame_attr_e attribute, rt_uint16_t parameter)
{
struct rt_link_frame command_frame = {0};
rt_uint8_t data[sizeof(command_frame.head) + sizeof(command_frame.extend)] = {0};
rt_uint8_t data_len = 0;
rt_link_frame_init(&command_frame, RT_NULL);
data_len += sizeof(command_frame.head);
rt_link_frame_extend_config(&command_frame, attribute, parameter);
rt_memcpy(data + data_len, &command_frame.extend, sizeof(command_frame.extend));
data_len += sizeof(command_frame.extend);
command_frame.head.sequence = sequence;
command_frame.head.service = serv;
rt_memcpy(data, &command_frame.head, sizeof(command_frame.head));
rt_link_hw_send(data, data_len);
return RT_EOK;
}
rt_link_command_frame_send的调用主要是要知道传入的参数,发现有很多地方调用了这个api,如下图,attribute是帧处理流程,parameter用于说明发送和接收的序列号或者是发送长帧数据的时候,说明整帧的字节长度,最大一帧65535字节。 参与了下面流程的处理,比较多就暂不展开具体说明,看完后续的接收分析,找到对应API名称展开阅读,应该比较容易理解其功能作用。
frame_send这个API就是发送一帧数据,其中的数据包,都是内存申请的,在收到对应的发送序列ack,才去释放内存。
向上追踪发现有2个函数调用了frame_send,一起分析这两个api
static rt_err_t rt_link_frame_send(rt_slist_t *slist)//前面分析过了,第一次发送内容使用
static rt_err_t rt_link_resend_handle(struct rt_link_frame *receive_frame)//重发数据用
3.1.5 static rt_size_t frame_send(struct rt_link_frame *frame)
static rt_size_t frame_send(struct rt_link_frame *frame)
{
rt_size_t length = 0;
rt_uint8_t *data = RT_NULL;
rt_memset(rt_link_scb->sendbuffer, 0, sizeof(rt_link_scb->sendbuffer));
data = rt_link_scb->sendbuffer;
length = RT_LINK_HEAD_LENGTH;
if (frame->head.crc)
{
length += RT_LINK_CRC_LENGTH;
}
if (frame->head.extend)
{
length += RT_LINK_EXTEND_LENGTH;
}
length += frame->data_len;
frame->head.length = frame->data_len;
rt_memcpy(data, &frame->head, RT_LINK_HEAD_LENGTH);
data = data + RT_LINK_HEAD_LENGTH;
if (frame->head.extend)
{
rt_memcpy(data, &frame->extend, RT_LINK_EXTEND_LENGTH);
data = data + RT_LINK_EXTEND_LENGTH;
}
if (frame->attribute == RT_LINK_SHORT_DATA_FRAME || frame->attribute == RT_LINK_LONG_DATA_FRAME)
{
rt_memcpy(data, frame->real_data, frame->data_len);
data = data + frame->data_len;
}
if (frame->head.crc)
{
frame->crc = rt_link_scb->calculate_crc(RT_FALSE, rt_link_scb->sendbuffer, length - RT_LINK_CRC_LENGTH);
rt_memcpy(data, &frame->crc, RT_LINK_CRC_LENGTH);
}
LOG_D("frame send seq(%d) len(%d) attr:(%d), crc:(0x%08x).", frame->head.sequence, length, frame->attribute, frame->crc);
return rt_link_hw_send(rt_link_scb->sendbuffer, length);
}
3.1.6 static rt_err_t rt_link_resend_handle(struct rt_link_frame *receive_frame)
static rt_err_t rt_link_resend_handle(struct rt_link_frame *receive_frame)
{
struct rt_link_frame *find_frame = RT_NULL;
rt_slist_t *tem_list = RT_NULL;
tem_list = rt_slist_first(&rt_link_scb->tx_data_slist);
while (tem_list != RT_NULL)
{
find_frame = rt_container_of(tem_list, struct rt_link_frame, slist);
if (find_frame->head.sequence == receive_frame->head.sequence)
{
LOG_D("resend frame(%d)", find_frame->head.sequence);
frame_send(find_frame);
break;
}
tem_list = tem_list->next;
}
if (tem_list == RT_NULL)
{
LOG_D("frame resent failed, can't find(%d).", receive_frame->head.sequence);
rt_link_command_frame_send(receive_frame->head.service,
receive_frame->head.sequence,
RT_LINK_SESSION_END, RT_NULL);
}
return RT_EOK;
}
发送先分析到这里,还差调用rt_link_command_frame_send相关的api没梳理完全,先分析接收,后续再看要不要分析。
3.2 RT-Link的接收处理
从应用层的rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length); 向下追踪经过ringbuff和事件通知处理,调用的是下面的API,帧检查
3.2.1 static void rt_link_frame_check(void)
static void rt_link_frame_check(void)
{
static struct rt_link_frame receive_frame = {0};
static rt_link_frame_parse_t analysis_status = FIND_FRAME_HEAD;
static rt_uint8_t *data = RT_NULL;
static rt_uint16_t buff_len = RT_LINK_HEAD_LENGTH;
struct rt_link_frame *send_frame = RT_NULL;
rt_tick_t timeout = 0;
rt_uint32_t temporary_crc = 0;
rt_uint8_t offset = 0;
rt_size_t recv_len = rt_link_hw_recv_len(rt_link_scb->rx_buffer);
while (recv_len > 0)
{
switch (analysis_status)
{
case FIND_FRAME_HEAD:
{
if ((*rt_link_scb->rx_buffer->read_point & RT_LINK_FRAME_HEAD_MASK) == RT_LINK_FRAME_HEAD)
{
analysis_status = PARSE_FRAME_HEAD;
break;
}
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
break;
}
case PARSE_FRAME_HEAD:
{
if (recv_len < buff_len)
{
LOG_D("HEAD: actual: %d, need: %d.", recv_len, buff_len);
return ;
}
data = rt_link_scb->rx_buffer->read_point;
rt_link_frame_init(&receive_frame, RT_NULL);
rt_link_hw_copy((rt_uint8_t *)&receive_frame.head, data, sizeof(struct rt_link_frame_head));
rt_link_hw_buffer_point_shift(&data, sizeof(struct rt_link_frame_head));
LOG_D("HEAD: seq(%d) serv(%d) ack(%d) crc(%d) ext(%d) len(%d) attr(%d)(0x%x)"
, receive_frame.head.sequence
, receive_frame.head.service
, receive_frame.head.ack
, receive_frame.head.crc
, receive_frame.head.extend
, receive_frame.head.length
, receive_frame.extend.attribute
, receive_frame.extend.parameter);
receive_frame.data_len = receive_frame.head.length;
if (receive_frame.head.service >= RT_LINK_SERVICE_MAX)
{
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
if (receive_frame.head.extend)
{
buff_len += RT_LINK_EXTEND_LENGTH;
analysis_status = PARSE_FRAME_EXTEND;
}
else
{
receive_frame.attribute = RT_LINK_SHORT_DATA_FRAME;
analysis_status = PARSE_FRAME_SEQ;
}
}
case PARSE_FRAME_EXTEND:
{
if (receive_frame.head.extend)
{
if (recv_len < buff_len)
{
LOG_D("EXTEND: actual: %d, need: %d.", recv_len, buff_len);
timeout = 50;
rt_timer_control(&rt_link_scb->recvtimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->recvtimer);
return;
}
rt_timer_stop(&rt_link_scb->recvtimer);
rt_link_hw_copy((rt_uint8_t *)&receive_frame.extend, data, sizeof(struct rt_link_extend));
rt_link_hw_buffer_point_shift(&data, sizeof(struct rt_link_extend));
if (receive_frame.extend.attribute < RT_LINK_RESERVE_FRAME)
{
receive_frame.attribute = receive_frame.extend.attribute;
}
else
{
LOG_D("EXTEND: attr(%d) err", receive_frame.extend.attribute);
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
}
analysis_status = PARSE_FRAME_SEQ;
}
case PARSE_FRAME_SEQ:
{
switch (receive_frame.attribute)
{
case RT_LINK_CONFIRM_FRAME:
case RT_LINK_RESEND_FRAME:
{
offset = rt_link_check_seq(receive_frame.head.sequence, rt_link_scb->tx_seq);
if (rt_slist_first(&rt_link_scb->tx_data_slist) != RT_NULL)
{
send_frame = rt_container_of(rt_link_scb->tx_data_slist.next, struct rt_link_frame, slist);
if (offset > send_frame->total)
{
LOG_D("seq (%d) failed, tx_seq (%d).offset=(%d) total= (%d)", receive_frame.head.sequence, rt_link_scb->tx_seq, offset, send_frame->total);
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
}
break;
}
case RT_LINK_LONG_DATA_FRAME:
case RT_LINK_SHORT_DATA_FRAME:
case RT_LINK_SESSION_END:
{
offset = rt_link_check_seq(receive_frame.head.sequence, rt_link_scb->rx_record.rx_seq) - 1;
if (offset > RT_LINK_FRAMES_MAX)
{
LOG_D("seq (%d) failed, rx_seq (%d) offset=(%d) attr= (%d) status (%d)", receive_frame.head.sequence, rt_link_scb->rx_record.rx_seq, offset, receive_frame.attribute, rt_link_scb->state);
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
}
case RT_LINK_HANDSHAKE_FRAME:
case RT_LINK_DETACH_FRAME:
analysis_status = HEADLE_FRAME_DATA;
break;
default:
LOG_D("quick filter error frame.");
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
buff_len += receive_frame.data_len;
if (receive_frame.head.crc)
{
buff_len += RT_LINK_CRC_LENGTH;
analysis_status = CHECK_FRAME_CRC;
}
else
{
analysis_status = HEADLE_FRAME_DATA;
}
receive_frame.real_data = data;
}
case CHECK_FRAME_CRC:
{
if (receive_frame.head.crc)
{
if (recv_len < buff_len)
{
LOG_D("CRC: actual: %d, need: %d.", recv_len, buff_len);
timeout = 50;
rt_timer_control(&rt_link_scb->recvtimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->recvtimer);
return;
}
rt_timer_stop(&rt_link_scb->recvtimer);
rt_link_hw_buffer_point_shift(&data, receive_frame.data_len);
rt_link_hw_copy((rt_uint8_t *)&receive_frame.crc, data, RT_LINK_CRC_LENGTH);
temporary_crc = rt_link_scb->calculate_crc(RT_TRUE, rt_link_scb->rx_buffer->read_point, buff_len - RT_LINK_CRC_LENGTH);
if (receive_frame.crc != temporary_crc)
{
LOG_D("CRC: calc:(0x%08x) ,recv:(0x%08x).", temporary_crc, receive_frame.crc);
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, 1);
goto __find_head;
}
}
analysis_status = HEADLE_FRAME_DATA;
}
case HEADLE_FRAME_DATA:
{
if (recv_len < buff_len)
{
LOG_D("PARSE: actual: %d, need: %d.", recv_len, buff_len);
timeout = 50;
rt_timer_control(&rt_link_scb->recvtimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->recvtimer);
return;
}
LOG_D("PARSE: buff_len (%d) r (0x%p) w (0x%p)"
, buff_len, rt_link_scb->rx_buffer->read_point
, rt_link_scb->rx_buffer->write_point);
rt_timer_stop(&rt_link_scb->recvtimer);
rt_link_hw_buffer_point_shift(&rt_link_scb->rx_buffer->read_point, buff_len);
rt_link_parse_frame(&receive_frame);
data = RT_NULL;
buff_len = RT_LINK_HEAD_LENGTH;
analysis_status = FIND_FRAME_HEAD;
break;
}
default:
__find_head:
LOG_D("to find head (%d)", analysis_status);
rt_link_frame_stop_receive(&receive_frame);
buff_len = RT_LINK_HEAD_LENGTH;
analysis_status = FIND_FRAME_HEAD;
break;
}
recv_len = rt_link_hw_recv_len(rt_link_scb->rx_buffer);
}
}
下面的API没什么好解释的,每包有4字节的帧数据信息,有个帧信息流程2个字节,去对于的句柄处理,后面文字简单一起解释下。
3.2.2 static rt_err_t rt_link_parse_frame(struct rt_link_frame *receive_frame)
static rt_err_t rt_link_parse_frame(struct rt_link_frame *receive_frame)
{
switch (receive_frame->attribute)
{
case RT_LINK_RESEND_FRAME:
rt_link_resend_handle(receive_frame);
break;
case RT_LINK_CONFIRM_FRAME:
rt_link_confirm_handle(receive_frame);
break;
case RT_LINK_HANDSHAKE_FRAME:
rt_link_handshake_handle(receive_frame);
break;
case RT_LINK_SESSION_END:
rt_link_session_end_handle(receive_frame);
break;
case RT_LINK_DETACH_FRAME:
rt_link_detach_handle(receive_frame);
break;
case RT_LINK_SHORT_DATA_FRAME:
rt_link_short_handle(receive_frame);
break;
case RT_LINK_LONG_DATA_FRAME:
rt_link_long_handle(receive_frame);
break;
default:
return -RT_ERROR;
}
return RT_EOK;
}
rt_link_resend_handle,重发句柄3.1.6具体解释过,收到的包信息里面,有个序列号,去检查发送的链表里面有没有这个序列号,有就重发一次。
rt_link_confirm_handle,确认句柄,检查收到的接收序列号,和发送的序列号是否相同,如果相同,说明收到了ack, rt_link_service_send_finish(RT_LINK_EOK);回调通知发送成功,并判断发送链表如果有数据,继续发送下一帧数据。
rt_link_handshake_handle,握手回馈句柄,发送的是收到的序列号和RT_LINK_CONFIRM_FRAME帧流程。请求握手方法参考rt_link_service_attach里面调用的rt_link_command_frame_send所用参数。
rt_link_session_end_handle,对应序列号关闭句柄,没有对方需要的序列号数据的时候,发送RT_LINK_SESSION_END帧信息,说明关闭这个序列回话。
rt_link_detach_handle,这个和握手相反,解除通信连接。
rt_link_short_handle,一包就是一帧数据句柄,申请该数据帧需要的长度内存,如果需要ack,先进行ack,帧流程是RT_LINK_CONFIRM_FRAME,序列号为此包的接收序列号。然后从fifo里面拷贝真实数据,并发送一个接收回调通知 rt_link_recv_finish(receive_frame->head.service, rt_link_scb->rx_record.dataspace, receive_frame->data_len);//回调里处理完这包数据后要主动释放
rt_link_long_handle,多包组合成一帧数据句柄,这个处理机制较复杂些和巧妙,展开分析学习一下。
static rt_err_t rt_link_long_handle(struct rt_link_frame *receive_frame)
{
if (rt_link_scb->rx_record.long_count == 0)
{
_long_handle_first(receive_frame);
}
if (rt_link_scb->rx_record.total > 0)
{
_long_handle_second(receive_frame);
}
receive_frame->real_data = RT_NULL;
return RT_EOK;
}
3.2.3 static void _long_handle_first(struct rt_link_frame *receive_frame)
static void _long_handle_first(struct rt_link_frame *receive_frame)
{
if (receive_frame->extend.parameter % RT_LINK_MAX_DATA_LENGTH == 0)
{
receive_frame->total = receive_frame->extend.parameter / RT_LINK_MAX_DATA_LENGTH;
}
else
{
receive_frame->total = receive_frame->extend.parameter / RT_LINK_MAX_DATA_LENGTH + 1;
}
rt_link_scb->rx_record.total = receive_frame->total;
rt_link_scb->rx_record.dataspace = rt_malloc(receive_frame->extend.parameter);
if (rt_link_scb->rx_record.dataspace == RT_NULL)
{
LOG_W("long data (%dB) alloc failed.", receive_frame->extend.parameter);
}
}
3.2.4 static void _long_handle_second(struct rt_link_frame *receive_frame)
static void _long_handle_second(struct rt_link_frame *receive_frame)
{
static rt_uint8_t ack_mask = RT_LINK_ACK_MAX;
rt_size_t offset = 0;
receive_frame->index = rt_link_check_seq(receive_frame->head.sequence, rt_link_scb->rx_record.rx_seq) - 1;
LOG_D("seq(%d), rxseq(%d), index(%d), total(%d), count(0x%x)"
, receive_frame->head.sequence
, rt_link_scb->rx_record.rx_seq
, receive_frame->index
, receive_frame->total
, rt_link_scb->rx_record.long_count);
if ((receive_frame->index > RT_LINK_FRAMES_MAX) || (rt_link_scb->rx_record.long_count & (0x01 << receive_frame->index)))
{
LOG_D("ERR:index %d, rx_seq %d", receive_frame->index, rt_link_scb->rx_record.rx_seq);
}
else if (rt_link_scb->rx_record.dataspace != RT_NULL)
{
rt_link_scb->rx_record.long_count |= (0x01 << receive_frame->index);
offset = RT_LINK_MAX_DATA_LENGTH * receive_frame->index;
rt_link_hw_copy(rt_link_scb->rx_record.dataspace + offset, receive_frame->real_data, receive_frame->data_len);
if (receive_frame->head.ack)
{
if (rt_link_utils_num1(rt_link_scb->rx_record.long_count) == rt_link_scb->rx_record.total)
{
rt_link_command_frame_send(receive_frame->head.service,
(rt_link_scb->rx_record.rx_seq + rt_link_scb->rx_record.total),
RT_LINK_CONFIRM_FRAME, RT_NULL);
}
else if ((rt_link_scb->rx_record.long_count & ack_mask) == ack_mask)
{
rt_link_command_frame_send(receive_frame->head.service,
(rt_link_scb->rx_record.rx_seq + rt_link_utils_num1(ack_mask)),
RT_LINK_CONFIRM_FRAME, RT_NULL);
ack_mask |= ack_mask << rt_link_utils_num1(RT_LINK_ACK_MAX);
}
}
if (rt_link_utils_num1(rt_link_scb->rx_record.long_count) == rt_link_scb->rx_record.total)
{
rt_timer_stop(&rt_link_scb->longframetimer);
rt_link_recv_finish(receive_frame->head.service, rt_link_scb->rx_record.dataspace, receive_frame->extend.parameter);
rt_enter_critical();
rt_link_scb->rx_record.rx_seq += rt_link_scb->rx_record.total;
rt_link_scb->rx_record.dataspace = RT_NULL;
rt_link_scb->rx_record.long_count = 0;
rt_link_scb->rx_record.total = 0;
ack_mask = RT_LINK_ACK_MAX;
rt_exit_critical();
}
else if (rt_link_hw_recv_len(rt_link_scb->rx_buffer) < (receive_frame->data_len % RT_LINK_MAX_DATA_LENGTH))
{
rt_int32_t timeout = RT_LINK_LONG_FRAME_TIMEOUT;
rt_timer_control(&rt_link_scb->longframetimer, RT_TIMER_CTRL_SET_TIME, &timeout);
rt_timer_start(&rt_link_scb->longframetimer);
}
}
}
至此源码的初始化和使用配置都梳理完了,解释了下发送和接收的机制。目前分析认为没有bug,传输效率和稳定性是有的,目前梳理了一段时间,具体其他细节处理,后续再更新。
4 操作系统帮助处理的内容
待更新
5 其他部分API解析
待更新
6 写在最后
希望上面的一些信息,有利于将RT-Link移植到实际的项目中,将RT-Link用起来。 个人分析难免有理解不到位的地方,如阅读有发现疑点或错误,请留言。
|