RT-Link

RT-Link is an open link layer transmission protocol. It is designed to achieve stable, secure and efficient point-to-point data transmission between devices, and has a simple interface and is easy to use.

Directory structure:

rtthread
└───components
    └───utilities
        └───rt-link
            ├───inc        // 头文件
            ├───src        // rtlink源码文件
            ├───Kconfig
            └───SConscriptcopymistakeCopy Success

Features:

  • Stability : RT-Link has a series of capabilities such as data retransmission, frame sequence number check, and state synchronization to ensure stable transmission;

  • Security : Supports CRC verification and uses Ethernet verification protocol;

  • Efficient : The protocol header is very concise, only 4 bytes, and every bit has practical significance;

  • Open : It has a unified operation API and supports multiple underlying hardware interfaces, such as UART, SPI, and USB.

  • Easy to use : The API is simple and can easily integrate RT-Link into existing applications, with good portability and compatibility.

The overall framework of RT-Link is as follows:

  • The top layer is the service layer , which supports multiple services running on one device at the same time, and the bottom layer uses the same data communication port;

  • The second layer is the rt-link transport layer , which implements the core functions of rt-link and is used to ensure the reliability and stability of data transmission functions;

  • The third layer is the rt-link_hw port docking layer , which is used to connect to the underlying data transmission port;

  • The lowest layer is the transmission port layer of the device , which is the communication port that actually transmits data, such as UART, SPI, Network, etc.

There can be multiple services in rt-link. For upper layer applications, each service represents a transmission channel, and there can be no association between services. Each service has an independent structure object. For ease of understanding, only the service structure object and related parameter definitions are listed here. Other contents can be rt-link.hviewed in the header file. The relevant attributes are as follows:

/* service 结构体对象 */
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;             /* 传输质量标志:是否使用 CRC 和 ACK */ 
    rt_link_service_e service;     /* service 类型标识 */ 
    rt_link_linkstate_e state;   /* 通道链接状态 */ 
    rt_link_err_e err;             /* 错误码 */
};copymistakeCopy Success

timeout_tx blocking/non-blocking send

rt-link provides two data transmission modes: blocking transmission and non-blocking transmission . timeout_tx can be configured using RT_WAITING_FOREVER(blocking) or (non-blocking) during initialization .RT_WAITING_NO

In blocking mode, if you need to configure a certain timeout, you can also configure a specific timeout for timeout_tx. The unit of this value is tick (system clock). In order to prevent a service from occupying the data transmission channel for a long time, there will be a maximum timeout in rt-link. Therefore, in blocking transmission mode, the smaller timeout will be used as the final transmission timeout.

send_cb data sending callback

In non-blocking mode, send_cb will send a callback notification after the sending is completed, regardless of whether the sending result is "successful" or "failed". The specific sending result can be viewed in the err error code in the structure object.

It should be noted that after calling the send interface in non-blocking mode , the data address space to be sent will be temporarily used by rt-link. The service application should not release or modify the data in this space until it receives the notification from send_cb and then operates the data address space.

recv_cb data receiving callback

The service receives data by registering a callback. The data space for receiving data is dynamically requested by rt-link. The service application gets the data space address and size in the callback interface. The subsequent use and release of this space needs to be managed by the service application .

flag Transmission quality flag

In rt-link, you can configure the data transmission quality of the service channel. There are two main configurable items: RT_LINK_FLAG_ACKand RT_LINK_FLAG_CRC.

When the ACK function is enabled, the data sent by the service channel will have an ACK response to confirm that the other end has successfully received it, and the retransmission function will also be enabled .

If you disable the ACK function, the ACK response and timeout retransmission functions will also be disabled. Enabling or disabling only affects this service channel.

When the CRC function is enabled, the sender will calculate the CRC before sending data and fill it at the end of a data frame. After receiving the data, the receiver will calculate and verify the other parts except CRC.

Disabling the ACK and CRC functions can improve transmission efficiency to a certain extent, but the corresponding data transmission quality needs to be guaranteed by the actual data transmission channel.

service type flag

Each service object has an independent service channel identifier, and the service type is defined in rt_link_service_e. During initialization, you need to rt_link_service_eselect a type from and configure it to the service structure object.

state Connection status

state marks the connection status of the service channel, which is divided into the following three connection states:

err error code

In the service structure object, err marks the error type of the last operation. In the current version, it mainly refers to errors that occur during data transmission. The meanings of the error codes used are as follows:

Here we take studio as an example, find Components -> Tools -> RT-Link on the configuration page, select Enable and configure, as shown below.

  • Configure the CRC calculation method. The software CRC function is already included in RT-Link. The hardware CRC needs to be connected to the relevant interface according to different platforms.

  • Save the settings and you can add RT-Link to the current project. The DEBUG option can be enabled according to debugging needs and is disabled by default.

  • Find the rt-link_hw software package in the software package , software package -> iot -> rt-link_hw. For a detailed introduction to the software package, see README;

  • UART is selected here, and you need to configure the device name and the hardware interface used. If you need to add other ports, you can view the introduction of the underlying link docking interface ;

  • One more thing, don't forget to open the hardware interface you want to use, here I use UART2;

The upper-layer application interface list is shown below, and these interfaces will be introduced in the subsections.

/* rtlink init and deinit */
int rt_link_init(void);
rt_err_t rt_link_deinit(void);
/* rtlink send data interface */
rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size);
/* rtlink service attach and detach */
rt_err_t rt_link_service_attach(struct rt_link_service *service);
rt_err_t rt_link_service_detach(struct rt_link_service *service);copymistakeCopy Success

int rt_link_init(void);copymistakeCopy Success

The default is automatic initialization. You can decide whether to enable automatic initialization based on the specific application. If you need to disable automatic initialization, you can rtlink.hcomment out the macro definition inRT_LINK_AUTO_INIT

rt_err_t rt_link_deinit(void);copymistakeCopy Success

When RT-Link is not needed, you can execute rt_link_deinit()to release system resources.

rt_err_t rt_link_service_attach(struct rt_link_service *service);copymistakeCopy Success

struct rt_link_serviceThe meaning of each member variable in the structure has been explained in the [Introduction to the service concept](#Introduction to the service concept) section .

Example:

static void send_cb(struct rt_link_service *service, void *buffer)
{
    LOG_I("send_cb: service (%d) buffer (0x%p) err(%d)", service->service, buffer, service->err);
}

static void recv_cb(struct rt_link_service *service, void *data, rt_size_t size)
{
    LOG_I("service (%d) size (%d) data(0x%p)", service->service, size, data);

    if (size)
    {
        LOG_HEX("example",8,data,size);
        rt_free(data);
    }
}

static struct rt_link_service serv_socket;
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);
}copymistakeCopy Success

rt_err_t rt_link_service_detach(struct rt_link_service *service);copymistakeCopy Success

rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size);copymistakeCopy Success

The underlying link docking interface is defined in rtlink_port.hand needs to be implemented when docking the underlying transmission port.

/* 需要在传输端口中实现的功能 */
rt_err_t rt_link_port_init(void);
rt_err_t rt_link_port_deinit(void);
rt_err_t rt_link_port_reconnect(void);
rt_size_t rt_link_port_send(void *data, rt_size_t length);
/* 当接收到数据并将数据传输到RTLink时调用 */
rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length);copymistakeCopy Success

int rt_link_port_init(void);
int rt_link_port_deinit(void);copymistakeCopy Success

It is mainly used to initialize the resources of the underlying port. It will be called when RT-Link is initialized and deinitialized. This part needs to be implemented by yourself during porting. For UART, SPI, etc., the relevant interfaces provided by the device framework can be used in RT-Thread.

rt_size_t rt_link_port_send(void *data, rt_size_t length);copymistakeCopy Success

This function will be called by the core logic of RT-Link to send data through the actual underlying port. For simple interfaces such as UART , data can be sent directly without affecting the operation of the system. For more complex communication interfaces such as SPI, USB , etc., the sending logic can be implemented through mechanisms such as events and mailboxes.

rt_err_t rt_link_port_reconnect(void);copymistakeCopy Success

The function of the reconnection interface is that when rt_link_port_send() fails to send, rt-link will call this interface to try to reconnect the underlying link, and call rt_link_port_send() again to try to send data. For example, if the underlying data link uses a TCP network, if the TCP connection is abnormally disconnected, TCP reconnection can be performed in this interface.

rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length);copymistakeCopy Success

When the underlying interface receives data, this function can be directly called to send data to RT-Link and provide the received data to the upper layer service. This function can be directly called in an interrupt.

#include <rtthread.h>
#include <rtlink.h>

#define DBG_ENABLE
#define DBG_TAG "rtlink_exam"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#define TEST_CONTEXT    "This message is sent by RT-Link"

static struct rt_link_service serv_socket;

static void send_cb(struct rt_link_service *service, void *buffer)
{
    LOG_I("send_cb: service (%d) buffer (0x%p) err(%d)", service->service, buffer, service->err);
}

static void recv_cb(struct rt_link_service *service, void *data, rt_size_t size)
{
    LOG_I("service (%d) size (%d) data(0x%p)", service->service, size, data);

    if (size)
    {
        LOG_HEX("example",8,data,size); /* 使用此接口打印 16 进制数据需要开启 ulog 组件*/
        rt_free(data);/* data 指向的空间由 rtlink 动态申请,应用层使用完毕后自行释放 */
    }
}

static int rtlink_exsend(int argc, char **argv)
{
    char *data = RT_NULL;
    rt_size_t length = 0;

    if (argc == 1)
    {
        data = rt_malloc(sizeof(TEST_CONTEXT));
        rt_memcpy(data, TEST_CONTEXT, sizeof(TEST_CONTEXT) - 1);
        length = rt_link_send(&serv_socket, data, sizeof(TEST_CONTEXT) - 1);
        LOG_I("send data length: %d.", length);
        rt_free(data);
    }
    return 0;
}
MSH_CMD_EXPORT(rtlink_exsend, rt link layer send test);

int rtlink_exinit(void)
{
    /* service 结构体对象初始化 */
    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);
    return RT_EOK;
}
MSH_CMD_EXPORT(rtlink_exinit, rt link example init);copymistakeCopy Success

Last updated

Assoc. Prof. Wiroon Sriborrirux, Founder of Advance Innovation Center (AIC) and Bangsaen Design House (BDH), Electrical Engineering Department, Faculty of Engineering, Burapha University