-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
124 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) ,里面有一份儿移植说明文档,以及两个测试程序。我们可以从测试程序里看到所有的用法。 | ||
|
||
目前已经实现了几种芯片底层驱动,想移植到其它芯片上,可以参考。 | ||
|