Skip to content

Commit

Permalink
add serialX user manual (#6964)
Browse files Browse the repository at this point in the history
  • Loading branch information
thewon86 committed Feb 23, 2023
1 parent e98df69 commit 862acd9
Showing 1 changed file with 124 additions and 0 deletions.
124 changes: 124 additions & 0 deletions components/drivers/serial/serialX User Manual.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
## serialX User Manual

serialX 驱动框架在尽量延续 v1 版使用方法的前提下,增加阻塞和非阻塞操作,同时对 DMA 收发缓冲机制进行了调整。

RT-Thread 论坛上有几篇介绍和讲解 serialX 的文章,使用 serialX 前请先了解它。

如果用分层理论解释 serialX ,那么它可以划分成应用层、中间驱动框架层、底层硬件层。这三层各有分工,又相互影响。

硬件是千变万化的,驱动的一个职责就是把千变万化的硬件进行包装,把共性的行为固化出来,交给应用层使用。


### 串口驱动框架和硬件底层接口功能定义详解

```
struct rt_uart_ops
{
// 配置外设寄存器设置默认波特率等;外设时钟配置、引脚功能复用(如果还没配置过);启用外设等等
int (*init)(struct rt_serial_device *serial);
// 仅仅用于配置外设波特率、数据位数、停止位等等
rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
// 用于使能禁用中断,初始配置 DMA
rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);
// 串口外设“写数据寄存器”*为空*,把数据放入“写数据寄存器”。*不为空*,死等
int (*putc)(struct rt_serial_device *serial, char c);
// 串口外设“读数据寄存器”*不为空*,读出“读数据寄存器”的值。*为空*,返回 -1
int (*getc)(struct rt_serial_device *serial);
// 启动发送,多数是使能串口外设“发送寄存器”*空*中断
void (*start_tx)(struct rt_serial_device *serial);
// 结束发送,多数是禁止串口外设“发送寄存器”*空*中断
void (*stop_tx)(struct rt_serial_device *serial);
#ifdef RT_SERIAL_USING_DMA
// 判断 DMA 是否在发送过程中,必须有效检测 DMA 是否在发送数据中,有些芯片有寄存器位标志,可以用标志位判断,如果没有,使用变量标志。
rt_bool_t (*is_dma_txing)(struct rt_serial_device *serial);
// 启动 DMA 发送,数据缓存首地址和数据长度由驱动框架提供。(最后置位 DMA tx 标志)
void (*start_dma_tx)(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size);
// 停止 DMA 发送,停用 DMA 发送,(最后复位 DMA tx 标志)
void (*stop_dma_tx)(struct rt_serial_device *serial);
#endif
// 使能串口外设中断,添加这个的初衷是减少全局中断开关操作。目前这部分代码已被注释并使用 rt_hw_interrupt_enable
void (*enable_interrupt)(struct rt_serial_device *serial);
// 禁用串口外设中断,添加这个的初衷是减少全局中断开关操作。目前这部分代码已被注释并使用 rt_hw_interrupt_disable
void (*disable_interrupt)(struct rt_serial_device *serial);
};
```

其中,`control` 是一个多功能扩展接口,目前支持的 `cmd` 包括:
- `RT_DEVICE_CTRL_OPEN`: 先清理一些可能出现的中断,最后配置并使能外设中断
- `RT_DEVICE_CTRL_CLOSE`: 禁止外设中断,清理中断,卸载外设
- `RT_DEVICE_CTRL_CLR_INT`: 定向禁止中断,包括接收中断,接收通道 DMA 中断,发送通道 DMA 中断
- `RT_DEVICE_CTRL_SET_INT`: 定向使能中断,包括接收中断,接收通道 DMA 中断,发送通道 DMA 中断
- `RT_DEVICE_CTRL_CONFIG`: 这部分主要是初始化配置接收通道 DMA 和发送通道 DMA。

`enable_interrupt` `disable_interrupt` 两个接口是一组很细腻的操作,但是有些外设如果用中断方式,有些用 DMA 方式。这时候需要区别当前外设是否使用了 DMA ,是否需要操作 DMA 中断。(某外设 “是否支持 DMA 收发” 和外设 “使用了 DMA 收发” 是两个概念)

PS: `control` 好像是无所不能的,有了它,为什么还要添加 `init` `start_tx` `stop_tx` `enable_interrupt` `disable_interrupt`

#### `rt_hw_serial_isr` 中断回调函数怎么调用?

在 serialX 里只关心五种中断,
1. “接收寄存器不空” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_TX_DONE);`
2. “发送寄存器空” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);`
3. “接收通道空闲” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));`
4. “接收通道 DMA 半/全中断” `rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (dma_cnt << 8));`
5. “发送通道 DMA 发送完成中断” `rt_hw_serial_isr(&uart->serial, RT_SERIAL_EVENT_TX_DMADONE);`

其中,dma_cnt 是 DMA 接收缓存中已接收数据字节数。

前两个用于中断收发,后三个用于 DMA 收发。

> 注:其它异常错误中断,或者清理掉,或者转入其它流程,这些不在 serialX 驱动考虑范围内。
### 重新定义 `rt_serial_device` 定义:

```
struct rt_serial_device
{
struct rt_device parent;
const struct rt_uart_ops *ops;
struct serial_configure config;
rt_uint32_t bufsz; // 驱动层收发缓存容量大小
void *serial_rx; // 串口接收缓存
void *serial_tx; // 串口发送缓存
#ifdef RT_SERIAL_USING_DMA // 串口收发缓存和 DMA 使用的二级缓存分开
rt_size_t dma_idx_rx;
rt_uint8_t serial_dma_rx[RT_SERIAL_DMA_BUFSZ]; // DMA 接收缓存
rt_uint8_t serial_dma_tx[RT_SERIAL_DMA_BUFSZ]; // DMA 发送缓存
#endif
cb_serial_tx _cb_tx; // 写过程回调函数指针
cb_serial_rx _cb_rx; // 读过程回调函数指针
struct rt_completion completion_tx; // 发送完成
struct rt_completion completion_rx; // 接收到新数据
};
typedef struct rt_serial_device rt_serial_t;
```

在最近的一次修改中,把 `struct serial_configure` 中的 `bufsz` 成员挪到了 `struct rt_serial_device` 。就是为了 open 了设备之后还能修改波特率,而且,修改缓存大小的功能可以挪到 `control` 里,达到 open 设备之后还能修改缓存大小的目地。

### 之前写过的文章汇总

#### 理论类

- [rt-thread 驱动篇 之 串口驱动框架剖析及性能提升](https://club.rt-thread.org/ask/article/0ee3da5b6a9c347d.html) 这里是 serialX 的理论基础,之后的所有工作都是这些想法的实现。
- [rt-thread 驱动篇 之 serialX 全网公测](https://club.rt-thread.org/ask/article/bfd92159ba11aef6.html) 这是第一次在 stm32 上实现并进行的测试,内含测试代码。 `struct rt_uart_ops` 结构体接口定义有很详细的注释,在新的芯片上写底层驱动时,这些注释说明很重要,一定严格按照这些接口定义的功能进行实现。

#### 实践类

- [rt-thread 驱动篇(五)serialX 小试牛刀](http:https://www.elecfans.com/d/1849301.html) 这是在控制台串口上使用 serialX 的实践。
以及,[rt-thread 驱动篇(六)serialX弊端及解决方法](https://www.elecfans.com/d/1850548.html) 会告诉你在控制台上使用**中断/DMA**收发模式时,可能遇到的尴尬问题以及怎么去避免它们。
- [测试 serialX 的 posix 支持](https://club.rt-thread.org/ask/article/e7b067264f6badfe.html) 这里是 posix 接口测试。内含测试代码
- [基于 serialX 串口驱动移植 libmodbus](https://club.rt-thread.org/ask/article/91437d9031d4ac5a.html) 这篇是使用 serialX 驱动跑的 libmodbus 。开启了 posix 之后,serialX 驱动和 libmodbus 配合的还是蛮好的。

### 仓库

最后,serialX 的[源码仓库](https://gitee.com/thewon/serialX) ,里面有一份儿移植说明文档,以及两个测试程序。我们可以从测试程序里看到所有的用法。

目前已经实现了几种芯片底层驱动,想移植到其它芯片上,可以参考。

0 comments on commit 862acd9

Please sign in to comment.