add serialX user manual (#6964)
This commit is contained in:
parent
e98df69775
commit
862acd98a1
|
@ -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) ,里面有一份儿移植说明文档,以及两个测试程序。我们可以从测试程序里看到所有的用法。
|
||||
|
||||
目前已经实现了几种芯片底层驱动,想移植到其它芯片上,可以参考。
|
||||
|
Loading…
Reference in New Issue