diff --git a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rt-link_hw.jpg b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rt-link_hw.jpg new file mode 100644 index 0000000000000000000000000000000000000000..37dfac44008917a8193261b0679658193a1cc5bb Binary files /dev/null and b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rt-link_hw.jpg differ diff --git "a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rtlink\345\272\224\347\224\250\346\241\206\346\236\266.jpg" "b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rtlink\345\272\224\347\224\250\346\241\206\346\236\266.jpg" index 29ef2ef2bb28551dc372ac9cfe946996f5f4f194..bd3702d617290aeb64d9cfd72d7b2fb410c4510f 100644 Binary files "a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rtlink\345\272\224\347\224\250\346\241\206\346\236\266.jpg" and "b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/rtlink\345\272\224\347\224\250\346\241\206\346\236\266.jpg" differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/settings.jpg b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/settings.jpg index fa9f490bdf346682c7de2f3c4dbfe073436fe48e..7faf0fb421068e43469421c7e3ec8a8f13f68cd3 100644 Binary files a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/settings.jpg and b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/settings.jpg differ diff --git "a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/\346\224\266\345\217\221\346\265\213\350\257\225.jpg" "b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/\346\224\266\345\217\221\346\265\213\350\257\225.jpg" index e060f2fafa996cb93c18d2a8e0074565d5699e34..5c8876e9433212a9275f47fbb716e80923803c81 100644 Binary files "a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/\346\224\266\345\217\221\346\265\213\350\257\225.jpg" and "b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/figures/\346\224\266\345\217\221\346\265\213\350\257\225.jpg" differ diff --git a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/rtlink.md b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/rtlink.md index 93d779f676249a09baadeb3ad8408a993c793377..8db5e8b6eac0df646379d79a13f0c16f22d141a3 100644 --- a/rt-thread-version/rt-thread-standard/programming-manual/rtlink/rtlink.md +++ b/rt-thread-version/rt-thread-standard/programming-manual/rtlink/rtlink.md @@ -9,7 +9,6 @@ rtthread └───components └───utilities └───rt-link - ├───hw_port // 对接硬件端口 ├───inc // 头文件 ├───src // rtlink源码文件 ├───Kconfig @@ -22,38 +21,119 @@ rtthread - **安全**:支持 CRC 校验,采用以太网检验协议; - **高效**:协议头十分精简,只有 4 Byte,并且每一个 bit 位都有实际意义; - **开放**:对下具有统一的操作API,可支持多种底层硬件接口,如UART、SPI、USB; -- **使用便捷**:对上只有 发送 和 注册接收回调 两个API,可以很方便的将 RT-Link 接入现有的上层应用中。具有很好的可移植性和兼容性。 +- **使用便捷**:API 简洁,可以很方便的将 RT-Link 接入现有的应用中,具有很好的可移植性和兼容性。 # 工作原理 -**整体框架:** +## 整体框架: ![rtlink整体框架](./figures/rtlink应用框架.jpg) -- 最上层为 APP 应用服务层,提供发送和注册接收回调两个操作接口,使用非常便捷; -- 第二层为 RT-Link 传输层,是 RT-Link 核心功能的实现,用来保证数据传输功能的可靠稳定。 -- 第三层为 hardware 链路层,是用来对接具体的硬件端口,例如 UART、SPI、USB 等; -- 最下层为设备的 BSP 驱动层。 +- 最上层是 **service 服务层**,支持多种服务同时运行在一个设备,且底层使用的是同一个数据通信端口; +- 第二层是 **rt-link 传输层**,是 rt-link 核心功能的实现,用来保证数据传输功能的可靠稳定; +- 第三层是 **rt-link_hw 端口对接层**,是用来对接底层的数据传输端口; +- 最下层是设备的**传输端口层**,是实际传输数据的通信端口,例如 UART、SPI、Network 等。 + +## service 概念介绍 + +在 rt-link 中可以有多种 service(服务) 同时存在,对于上层应用来说每种 service 代表了一个传输通道,service 之间可以没有任何关联。每个 service 都有一个独立的结构体对象,为了便于理解这里仅仅列出 service 结构体对象及相关的参数定义,其他内容可在 `rt-link.h` 头文件中查看。相关属性如下: + +```c +/* 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; /* 错误码 */ +}; +``` + +**timeout_tx 阻塞/非阻塞发送** + +rt-link 提供**阻塞发送**和**非阻塞发送**两种数据发送方式,timeout_tx 可在初始化时使用 `RT_WAITING_FOREVER` (阻塞)或 `RT_WAITING_NO` (非阻塞)进行配置。 + +在阻塞模式下,如果需要配置一个确定的超时时间,也可以对 timeout_tx 配置具体的超时时长,此数值的单位是 tick(系统时钟)。为了防止某一个 service 长时间的占用数据发送通道,在 rt-link 中会有最大的超时时长,所以在阻塞发送模式下,会以时长较小的那个做为最终的发送超时时间。 + +**send_cb 数据发送回调** + +在非阻塞模式下,发送完成后会由 send_cb 发送回调通知,不论发送结果是“成功”还是“失败”。具体的发送结果可查看结构体对象中的 **err** 错误码。 + +需要注意一点,在**非阻塞模式**下调用了发送接口后,发送的**数据地址空间**会暂时由 rt-link 使用,service 应用**不应该释放或修改**此空间的数据,直到收到 send_cb 的通知后再操作数据地址空间。 + +**recv_cb 数据接收回调** + +service 的数据接收使用注册接收回调的方式,接收数据的数据空间由 rt-link 动态申请, service 应用在回调接口中会拿到数据空间地址和空间大小,**此空间后续的使用和释放需要由 service 应用管理**。 + +**flag 传输质量标志** + +在 rt-link 中可以配置 service 通道的数据传输质量,主要有两个可配置项 `RT_LINK_FLAG_ACK` 和 `RT_LINK_FLAG_CRC`。 + +开启 ACK 功能,service 通道发送的数据会有 **ACK 应答**确认对端接收成功,同时也会开启**重传**功能。 + +关闭 ACK 功能,ACK 应答和超时重传功能也会关闭。开启和关闭都只影响这一个 service 通道。 + +开启 CRC 功能,发送端在数据发送前会进行 CRC 计算,并填充到一个数据帧的尾部。接收端收到数据后会对除 CRC 外的其他部分进行计算校验。 + +关闭 ACK 和 CRC 功能可以在一定程度上提高传输的效率,但是相应的数据传输质量就需要依靠实际的数据传输通道来保证。 + +**service 类型标志** + +每个 service 对象都有一个独立的服务通道标识,service 类型定义在 `rt_link_service_e` 中。初始化时需要从 `rt_link_service_e` 中选择一个类型配置到 service 结构体对象。 + +**state 连接状态** + +state 标记了 service 通道的连接状态,分为以下三种连接状态: + +| 类型 | 意义 | 说明 | +| --------------- | -------- | ------------------------------------------- | +| RT_LINK_INIT | 初始化 | service 初始化时的状态 | +| RT_LINK_DISCONN | 连接断开 | 对端 detach 后的状态,代表对端 service 下线 | +| RT_LINK_CONNECT | 连接成功 | 对端 attach 后的状态,代表对端 service 上线 | + +**err 错误码** + +在 service 结构体对象中,err 标记的是最后一个操作的错误类型,目前的版本中主要是数据发送过程中出现的错误。已用到的错误码意义如下: + +| 类型 | 说明 | +| ---------------- | ------------------------------------ | +| RT_LINK_EOK | 成功 | +| RT_LINK_ERR | 通用错误,一般为接口参数问题 | +| RT_LINK_ETIMEOUT | 数据发送超时 | +| RT_LINK_ENOMEM | 内存不足,发送数据超长或内存空间不足 | +| RT_LINK_EIO | 底层 IO 错误,底层端口发送失败 | + # 使用指南 ## 如何添加、使用 RT-Link: -这里以 studio 为例,在配置页找到 组件-->工具-->RT-Link,选择开启并进行配置。如下图所示。 +这里以 studio 为例,在配置页找到 组件 —>工具 —>RT-Link,选择开启并进行配置。如下图所示。 -![rtlink整体框架](./figures/settings.jpg) +![settings](./figures/settings.jpg) - 配置 CRC 计算方式,软件 CRC 功能已包含在 RT-Link 中,硬件 CRC 需要根据不同平台自行对接相关接口; -- 配置设备名称及所使用的硬件接口,这里选择了 UART,如果需要添加其他端口可查看下方对应接口的介绍进行对接; -- 保存设置就可以将 RT-Link 添加到现在的工程中了,DEBUG 选项可根据调试需求选择开启,默认关闭。 +- 保存设置就可以将 RT-Link 添加到现在的工程中了,DEBUG 选项可根据调试需求选择开启,默认关闭; +- 在软件包中找到 [rt-link_hw](http://packages.rt-thread.org/detail.html?package=rt-link_hw) 软件包,软件包 —> iot —> rt-link_hw,软件包详细介绍可查看 README; -- 还有一件事,别忘了打开要使用的硬件接口 +![rt-link_hw](./figures/rt-link_hw.jpg) -![rtlink整体框架](./figures/uart.jpg) +- 这里选择了 UART,需要配置设备名称及所使用的硬件接口,如果需要添加其他端口可查看[底层链路对接接口介绍](#底层链路对接接口介绍); +- 还有一件事,别忘了打开要使用的硬件接口,这里使用了 UART2; -- 这里选择了两块潘多拉开发板来测试运行效果,[示例代码](#示例代码) 在文档尾部↓↓↓。运行效果如下: +![uart](./figures/uart.jpg) + +- 这里选择了两块潘多拉开发板来测试运行效果,在文档尾部有[示例代码](#示例代码)。运行效果如下: +![收发测试](./figures/收发测试.jpg) -![rtlink整体框架](./figures/收发测试.jpg) ## 上层应用接口介绍 @@ -62,10 +142,10 @@ rtthread int rt_link_init(void); rt_err_t rt_link_deinit(void); /* rtlink send data interface */ -rt_size_t rt_link_send(rt_link_service_t service, void *data, rt_size_t size); +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(rt_link_service_t service, rt_err_t (*function)(void *data, rt_size_t size)); -rt_err_t rt_link_service_detach(rt_link_service_t service); +rt_err_t rt_link_service_attach(struct rt_link_service *service); +rt_err_t rt_link_service_detach(struct rt_link_service *service); ``` ### 初始化 @@ -86,7 +166,7 @@ int rt_link_init(void); ```c rt_err_t rt_link_deinit(void); ``` -当不需要使用 RT-Link 时,可执行 deinit 删除处理线程,释放系统资源。 +当不需要使用 RT-Link 时,可执行 `rt_link_deinit()` 释放系统资源。 | 返回值 | 描述 | | ------ | ---------- | @@ -95,75 +175,90 @@ rt_err_t rt_link_deinit(void); ### 注册服务接收回调 ```c -rt_err_t rt_link_service_attach(rt_link_service_t service, rt_err_t (*function)(void *data, rt_size_t size)); +rt_err_t rt_link_service_attach(struct rt_link_service *service); ``` +| 参数 | 描述 | +| ------ | ---------- | +| service | 要注册的 service 对象, 参数类型 `struct rt_link_service*` | +| **返回值** | -- | +| RT_EOK | 注册成功 | +| -RT_EINVAL | 参数错误 | + +在 [service 概念介绍](#service 概念介绍)部分已经对`struct rt_link_service` 结构体中的每个成员变量的意义做了说明。 + +**示例:** + ```c -typedef enum +static void send_cb(struct rt_link_service *service, void *buffer) { - RT_LINK_SERVICE_RTLINK = 0, - RT_LINK_SERVICE_LINK_SOCKET = 1, - RT_LINK_SERVICE_LINK_WIFI = 2, - RT_LINK_SERVICE_LINK_MNGT = 3, - RT_LINK_SERVICE_LINK_MSHTOOLS = 4, - RT_LINK_SERVICE_MAX -}rt_link_service_t; -``` + LOG_I("send_cb: service (%d) buffer (0x%p) err(%d)", service->service, buffer, service->err); +} -| 参数 | 描述 | -| ------ | ---------- | -| service | 要注册的服务通道,可在`rt_link_service_t`中选择,或添加自定义服务类型 | -| function | 服务对应的接收回调函数,接收回调的参数为 `void *data, rt_size_t size`,返回值为 `rt_err_t` 类型,
需要注意的是 **data 指向的空间由 RT-link 动态申请,应用层使用完毕后自行释放** | +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); -| 返回值 | 描述 | -| ------ | ---------- | -| RT_EOK | 注册成功 | + 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); +} +``` ### 解除服务接收通道 ```c -rt_err_t rt_link_service_detach(rt_link_service_t service); +rt_err_t rt_link_service_detach(struct rt_link_service *service); ``` | 参数 | 描述 | | ------ | ---------- | -| service | 要解除的服务通道 | - -| 返回值 | 描述 | -| ------ | ---------- | -| RT_EOK | 解除成功 | +| service | 要移除的 service 对象,参数类型 `struct rt_link_service*` | +| **返回值** | -- | +| RT_EOK | 移除成功 | +| -RT_EINVAL | 参数错误 | ### 发送数据 ```c -rt_size_t rt_link_send(rt_link_service_t service, void *data, rt_size_t size); +rt_size_t rt_link_send(struct rt_link_service *service, const void *data, rt_size_t size); ``` | 参数 | 描述 | | ------ | ---------- | -| service | 数据所属的服务通道 | +| service | service 结构体对象 | | data | 发送的数据 | | size | 数据的长度 | - -| 返回值 | 描述 | -| ------ | ---------- | +| **返回值** | -- | | 0 | 发送失败 | | size | 发送的数据长度 | ## 底层链路对接接口介绍 -底层链路对接接口定义在 `rtlink_port.h` 中,需要在对接移植是实现。 +底层链路对接接口定义在 `rtlink_port.h` 中,需要在对接底层传输端口时实现。 ```c -/* Functions that need to be implemented at the hardware */ -int rt_link_port_init(void); -int rt_link_port_deinit(void); +/* 需要在传输端口中实现的功能 */ +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); - -/* Called when the hardware receives data and the data is transferred to RTLink */ +/* 当接收到数据并将数据传输到RTLink时调用 */ rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length); ``` @@ -174,7 +269,7 @@ int rt_link_port_init(void); int rt_link_port_deinit(void); ``` -主要用于初始化底层端口的资源,在 RT-Link 初始化和去初始化时会调用,这部分是需要在移植对接时自行实现。对于 UART、SPI 等在 RT-Thread 中则可使用 device 框架提供的相关接口。 +主要用于初始化底层端口的资源,在 RT-Link 初始化和去初始化时会调用,这部分是需要在移植对接时自行实现。对于 UART、SPI 等在 RT-Thread 中可使用 device 框架提供的相关接口。 ### 数据发送 @@ -182,18 +277,23 @@ int rt_link_port_deinit(void); rt_size_t rt_link_port_send(void *data, rt_size_t length); ``` -此函数将会由 RT-Link 的核心逻辑调用,用于通过实际的端口发送数据。对于 UART 这类比较 **简单的** 接口,可以直接发送,不会影响系统的运行。对于比较 **复杂的** 通信接口,例如 **SPI, USB** 等可以通过事件、邮箱等机制来实现发送逻辑。 +此函数将会由 RT-Link 的核心逻辑调用,用于通过实际的底层端口发送数据。对于 UART 这类比较 **简单的** 接口,可以直接发送,不会影响系统的运行。对于比较 **复杂的** 通信接口,例如 **SPI, USB** 等可以通过事件、邮箱等机制来实现发送逻辑。 | 参数 | 描述 | | ------ | ---------- | | data | 发送的数据 | | length | 数据的长度 | - -| 返回值 | 描述 | -| ------ | ---------- | +| **返回值** | -- | | 0 | 发送失败 | | length | 发送的数据长度 | +### 重连 + +```c +rt_err_t rt_link_port_reconnect(void); +``` +重连接口的作用是当 rt_link_port_send() 发送失败后,在 rt-link 中会调用此接口尝试底层链路重连,并再次调用 rt_link_port_send() 尝试发送数据。 +例如底层数据链路使用的是 TCP 网络,如果出现 TCP 连接异常断开,可以在此接口中进行 TCP 重连。 ### 数据写入 @@ -207,9 +307,7 @@ rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length); | ------ | ---------- | | data | 写入的数据 | | length | 数据的长度 | - -| 返回值 | 描述 | -| ------ | ---------- | +| **返回值** | -- | | 0 | 写入失败 | | > 0 | 实际写入的数据长度 | @@ -220,43 +318,57 @@ rt_size_t rt_link_hw_write_cb(void *data, rt_size_t length); #include #define DBG_ENABLE -#define DBG_TAG "rtlink" +#define DBG_TAG "rtlink_exam" #define DBG_LVL DBG_LOG #include #define TEST_CONTEXT "This message is sent by RT-Link" -rt_err_t rt_link_receive_example_callback(void *data, rt_size_t length) +static struct rt_link_service serv_socket; + +static void send_cb(struct rt_link_service *service, void *buffer) { - LOG_I("recv data %d",length); - LOG_HEX("example",8,data,length); /* 使用此接口打印 16 进制数据需要开启 ulog 组件*/ - rt_free(data); /* data 指向的空间由 rtlink 动态申请,应用层使用完毕后自行释放 */ - return RT_EOK; + LOG_I("send_cb: service (%d) buffer (0x%p) err(%d)", service->service, buffer, service->err); } -int rt_link_example_send(int argc, char **argv) +static void recv_cb(struct rt_link_service *service, void *data, rt_size_t size) { - char *receive = RT_NULL; + 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) + if (argc == 1) { - receive = rt_malloc(sizeof(TEST_CONTEXT)); - rt_memcpy(receive, TEST_CONTEXT, sizeof(TEST_CONTEXT) - 1); - length = rt_link_send(RT_LINK_SERVICE_RTLINK, receive, sizeof(TEST_CONTEXT) - 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(receive); + rt_free(data); } return 0; } -MSH_CMD_EXPORT(rt_link_example_send, rt link layer send test); +MSH_CMD_EXPORT(rtlink_exsend, rt link layer send test); -int rt_link_example_init(void) +int rtlink_exinit(void) { - /* 注册应用服务与接收回调 */ - rt_link_service_attach(RT_LINK_SERVICE_RTLINK, rt_link_receive_example_callback); + /* 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(rt_link_example_init, rt link layer example init); -``` - +MSH_CMD_EXPORT(rtlink_exinit, rt link example init); +``` \ No newline at end of file