thewon86 пре 2 година
родитељ
комит
862acd98a1
1 измењених фајлова са 124 додато и 0 уклоњено
  1. 124 0
      components/drivers/serial/serialX User Manual.md

+ 124 - 0
components/drivers/serial/serialX User Manual.md

@@ -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://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) ,里面有一份儿移植说明文档,以及两个测试程序。我们可以从测试程序里看到所有的用法。
+
+目前已经实现了几种芯片底层驱动,想移植到其它芯片上,可以参考。
+