RT-Thread / rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS).
https://www.rt-thread.io
Apache License 2.0
10.46k stars 5.01k forks source link

串口DMA驱动细节的几处疑问 #628

Closed armink closed 7 years ago

armink commented 8 years ago

由于众多 BSP 中缺少最新 DMA 方式的串口设备驱动的实现,整个设备驱动的流程并不明确,自己摸索中实现了一套 DMA 方式的串口驱动,但涉及到 RTT 源码中一些细节存在冲突,便产生如下疑问:

struct rt_uart_ops
{
    rt_err_t (*configure)(struct rt_serial_device *serial, struct serial_configure *cfg);
    rt_err_t (*control)(struct rt_serial_device *serial, int cmd, void *arg);

    int (*putc)(struct rt_serial_device *serial, char c);
    int (*getc)(struct rt_serial_device *serial);

    rt_size_t (*dma_transmit)(struct rt_serial_device *serial, const rt_uint8_t *buf, rt_size_t size, int direction);
};

串口操作回调接口中的 dma_transmit 方法,我的理解应该是 DMA 传输的操作接口, direction 代表方向,那么在接收数据时,由于 const rt_uint8_t *buf 指向的是一个只读缓冲区,则无法传入上层接收缓冲区的地址。

我以往的串口设备读取数据的流程是,先调用 rt_device_read 读取串口 FIFO 中已经接收到的数据。如果数据不够时,设置接收回调,此时读数据线程被阻塞。在接收回调中会检测接收长度,如果长度够了,则唤醒被阻塞的读数据线程,设置回调为 NULL。 但是,对于 RTT 自带的 DMA 串口驱动中,在打开设备到关闭设备之间,必须得时刻存在接收回调接口(见 https://github.com/RT-Thread/rt-thread/blob/master/components/drivers/serial/serial.c#L615 )。同时,在这段代码中(https://github.com/RT-Thread/rt-thread/blob/master/components/drivers/serial/serial.c#L174-L179 ),有强制要求, DMA 是在激活状态才允许传输 一次 ,如果一次不把数据全部读走,剩下的数据只能等待下次接收传输结束时才能够读取走。所以顺着以往的思路去处理 DMA 接收驱动,发现跟 RTT 设计意图应该有所背离。虽然最后注释掉 rx_dma->activated 检测逻辑(stm32f10x 驱动在这里,还没来得及整理),DMA 驱动已经能够按照往的流程稳定工作,但还是想了解当时 RTT 的 DMA 串口驱动设计思想及实现流程。

BernardXiong commented 8 years ago
  1. const确实有些问题,这个应该是要修改的。
  2. 读取的流程是这样的,上层调用device read,并给出buffer和长度,然后底层启动DMA。DMA结束后向上层返回实际的长度。
armink commented 8 years ago

1、这个问题我接下来发起个 pr 2、恩,现在能想明白这个过程了,buffer 由上层应用来提供,设备驱动框架内部并不提供 buffer ,因为 DMA 传输需要人为启动,并不是时刻都在传输的。那这也就意味着,同样的应用程序,在采用 DMA中断 两种不同的底层驱动接收数据时,上层处理业务流程会有差异,无法兼容。所以我现在的做法是改造了 DMA 串口驱动框架,跟中断方式一样,设备驱动层提供 buffer ,专心负责数据的接收,应用层业务也得到简化和统一。 @BernardXiong 你觉得这种方式怎么样?

BernardXiong commented 8 years ago

可以考虑,不过这样必然需要做buffer复制了。

armink commented 8 years ago

是的,写 buffer 是 DMA 自动完成的, device read 需要做 buffer 复制。

armink commented 8 years ago

@BernardXiong 或者我把驱动完善为新旧模式都支持,判断 serial->config.bufsz ,等于 0 时,则使用原先的那种传递指针的非缓冲模式,否则使用我说的这种底层缓冲接收的模式。你看可以吗?

BernardXiong commented 8 years ago

用Keil MDK编译,好像会编译失败,确认下代码。

armink commented 8 years ago

@BernardXiong 熊大,新驱动刚用 stm32f10x BSP 在 Keil 里测试了下,没有问题。

BernardXiong commented 8 years ago

_serial_dma_tx => serial->ops->dma_transmit(serial, data, length, RT_SERIAL_DMA_TX); 应该会报一个const数据类型不匹配的错误,或警告吧。

armink commented 8 years ago

不会的,我在 这里data 做了强制 (rt_uint8_t *) 类型转换。