Serial DMA Analysis in RT-THREAD

Here is an analysis of the implementation of serial port DMA in RT-Thread for new development Reference when the processor serial port supports.

Background

With today’s chip performance and powerful peripheral functions, the serial port does not implement DMA/interrupt operation. I think it is basically It is unacceptable, but unfortunately, there is basically no support for serial port DMA in the current implementation of rt-thread support, and the document does not have any description about serial port DMA support. Here, taking the STM32 implementation as the background, I will sort out the serial port DMA. The implementation process of the new processor is used as a reference when the new processor is implemented.

DMA reception preparation

Enable DMA reception, you need to do some processing when opening the device, the entry function is rt_device_open(). The main implementation is:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) { ...... result = device_init(dev); ...... result = device_open(dev, oflag); ...... }   span>

device_init() is the rt_serial_init() function, which mainly calls the configure() function ,

static rt_err_t rt_serial_init(struct rt_device *dev< span class="p">) { ...... if (< span class="n">serial->ops->configure) result = serial-> ops->configure(serial, &serial->config); ...... } < /span> 

div>

Under stm32, its configure() function is stm32_configure(), which configures the registers of the STM32 peripherals according to the device opening parameters. Including serial port operating parameters such as baud rate and parity.

The device_open() function is the rt_serial_open() function, and its main implementation is:

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) { ...... #ifdef RT_SERIAL_USING_DMA else if (oflag &  RT_DEVICE_FLAG_DMA_RX) { if (serial->config.bufsz == 0) { struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(< span class="k">struct rt_serial_rx_dma)); RT_ASSERT(rx_dma != RT_NULL); rx_dma->activated = RT_FALSE; serial->serial_rx = rx_dma; } else { struct rt_serial_rx_fifo* rx_fifo; rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(< span class="n">rx_fifo != RT_NULL); rx_fifo->buffer = (rt_uint8_t *) (rx_fifo + 1); rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); rx_fifo->< span class="n">put_index = 0; rx_fifo->get_index = 0; rx_fifo< span class="o">->is_full = RT_FALSE; serial->serial_rx = rx_fifo; /* configure fifo address and length to low level device */ serial->ops< span class="o">->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) < span class="n">RT_DEVICE_FLAG_DMA_RX); } dev->open_flag |= RT_DEVICE_FLAG_ DMA_RX; } #endif /* RT_SERIAL_USING_DMA */ ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_TX) { struct rt_serial_tx_dma* tx_dma;  tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = RT_FALSE; rt_data_queue_init(&(tx_dma->data_queue), 8< span class="p">, 4, RT_NULL); serial->serial_tx = tx_dma; dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; /* configure low level device */ serial->ops ->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_TX); } #endif /* RT_SERIAL_USING_DMA */ ...... } < /span>< /span>< /span>< /span>< /span>< /span>< /span>< /span>< /span>< /span>< /span>< /span>

Visible, its main job is to prepare FIFO buffer for DMA reception ; Prepare the sending data buffer queue for DMA transmission, but it seems that the STM32 interrupt does not use the sending data buffer.

The source of the DMA configuration data is the rt_hw_usart_init() function. The default configuration parameters are determined by the macro RT_SERIAL_CONFIG_DEFAULT. Here it is determined that the default receive buffer parameter is 64 bytes, and the communication default parameter is: 115200, 8N1.

#define RT_SERIAL_RB_BUFSZ 64 < /pre> 

DMA reception

DMA reception starts from the analysis of DMA interrupt. The DMA reception interrupt service function is UARTn_DMA_RX_IRQHandler(), which is called The DMA processing function HAL_DMA_IRQHandler() of the HAL library, this function calls the callback function HAL_UART_RxCpltCallback() or HAL_UART_RxHalfCpltCallback(), these two functions enter the real interrupt service processing function dma_isr(struct rt_serial_device *), the main code is as follows:

< div class="language-c highlighter-rouge">
static void dma_isr(struct rt_serial_device *serial ) { ...... /* If it is a DMA-RX interrupt*/ if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx .handle), DMA_IT_TC) != RESET) || (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) { level = rt_hw_interrupt_disable(); /* Get the amount of data received this time*/ < span class="n">recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&< span class="p">(uart->dma_rx.handle)); if (recv_total_index == 0) { /* What does this code mean? */ recv_len = serial->config.bufsz - uart->dma_rx.last_index; } else { /* Subtract the amount of data received before to get the amount of data received this time*/ recv_len = recv_total_index - uart->dma_rx .last_index; } /* Update receiving historical data Quantity*/ uart->dma_rx.last_index < span class="o">= recv_total_index; rt_hw_interrupt_enable(level); if (recv_len ) { /* If there is new data, call the general processing of the serial device module*/ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } } } < /span>< /span>< /span>< /span>< /span>< /span>

In the function rt_hw_serial_isr() of the serial module, the main code is:

void rt_hw_serial_isr(struct < span class="n">rt_serial_device *serial, int event) { ...... case RT_SERIAL_EVENT_RX_DMADONE: { int length; rt_base_t level; /* get DMA rx length */ length  = (event & (~0xff)) >> 8; if (serial->config.bufsz == 0) { /* I don’t know how to apply the processing logic of this case. It seems that the STM32 implementation does not handle this case */ struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) serial ->serial_rx; RT_ASSERT(rx_dma != RT_NULL); RT_ASSERT(serial ->parent.rx_indicate != RT_NULL); serial->parent.rx_indicate(&(serial->parent), length); rx_dma->activated = RT_FALSE ; } else { /* disable interrupt */  level = rt_hw_interrupt_disable(); /* update fifo put index, put the data into the receiving Buffer*/ rt_dma_recv_update_put_index(serial< span class="p">, length); /* calculate received total length, update buffer information*/ length = rt_dma_calc_recved_len(serial); /* enable interrupt */ rt_hw_interrupt_enable(level); /* invoke callback, notify the upper layer that new data arrives*/ if (serial->parent.rx_indicate != RT_NULL) { serial->parent.rx_indicate(&( serial->parent), length); } } br eak; } ...... }   span>  span>  span>  span>  span>   span>

After the upper layer receives the notification, the reading function finally calls the driver reading function rt_serial_read() function, and then the DMA Under conditions, call _serial_dma_rx() to read data from the buffer. The code is:

static rt_size_t rt_serial_read(struct rt_device * dev, rt_off_t pos, void *buffer, rt_size_t size< span class="p">) { ...... else if < span class="p">(dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { return _serial_dma_rx(< span class="n">serial, (rt_uint8_t *)buffer, size); } ...... } < /span>< /span>

< h3 id="dma sending">DMA sending

DMA sending starts from the drive write function rt_serial_write(), and under DMA conditions, call _serial_dma_tx(), _serial_dma_tx() and then call the DMA sending function of the operation Send data, the code is:

static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { ...... else if ( dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { return _serial_dma_tx(serial , (const rt_uint8_t *)buffer, size); } ...... } < /span>

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) { ...... /* make a DMA transfer */ serial->ops- >dma_transmit(serial, (rt_uint8_t *)data, length , RT_SERIAL_DMA_TX ); ...... } < /span>< /span>

STM32's dma_transmit() implementation function is stm32_dma_transmit(), its implementation is simply to call HAL_UART_Transmit_DMA(), the code is:

< pre class="highlight">static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size , int direction) { . ..... if (HAL_UART_Transmit_DMA(& uart->handle, buf, size) == HAL_OK ) ...... } span> span>

The implementation is very simple.

Here we analyze the implementation of serial port DMA mode in RT-Thread for reference when new processor serial port supports.

Background

With today’s chip performance and powerful peripheral functions, the serial port does not implement DMA/interrupt operation. I think it is basically It is unacceptable, but unfortunately, there is basically no support for serial port DMA in the current implementation of rt-thread support, and the document does not have any description about serial port DMA support. Here, taking the STM32 implementation as the background, I will sort out the serial port DMA. The implementation process of the new processor is used as a reference when the new processor is implemented.

DMA reception preparation

Enable DMA reception, you need to do some processing when opening the device, the entry function is rt_device_open(). The main implementation is:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) { ...... result = device_init(dev); ...... result = device_open(dev, oflag); ...... }   span>

device_init() is the rt_serial_init() function, which mainly calls the configure() function ,

static rt_err_t  rt_serial_init(struct rt_device *dev) { ...... if (serial->ops->configure) result = serial->ops->configure(serial, &serial->config); . ..... }   span>

Under stm32, its configure() function is stm32_configure(), which configures the registers of the STM32 peripherals according to the device opening parameters.包括波特率、校验等串口工作参数。

device_open()函数就是rt_serial_open()函数,其主要实现是:

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) { ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_RX) { if (serial->config.bufsz == 0) { struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); RT_ASSERT(rx_dma != RT_NULL); rx_dma->activated = RT_FALSE; serial->serial_rx = rx_dma; } else { struct rt_serial_rx_fifo* rx_fifo; rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(rx_fifo != RT_NULL); rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); rx_fifo->put_index = 0; rx_fifo->get_index = 0; rx_fifo->is_full = RT_FALSE; serial->serial_rx = rx_fifo; /* configure fifo address and length to low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); } dev->open_flag |= RT_DEVICE_FLAG_DMA_ RX; } #endif /* RT_SERIAL_USING_DMA */ ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_TX) { struct rt_serial_tx_dma* tx_dma; tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = RT_FALSE; rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL); serial->serial_tx = tx_dma; dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; /* configure low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX); }  #endif /* RT_SERIAL_USING_DMA */ ...... } 

可见,其主要工作是为DMA接收准备FIFO缓冲区;为DMA发送准备发送数据缓冲队列,但是好像STM32中断并没有用到发送数据缓冲。

DMA配置数据来源是rt_hw_usart_init()函数,缺省的配置参数由宏RT_SERIAL_CONFIG_DEFAULT决定, 这里决定了缺省的接收缓冲区参数是64字节,通讯缺省参数是:115200,8N1。

#define RT_SERIAL_RB_BUFSZ 64 

DMA接收

DMA接收我们从DMA中断开始分析,DMA接收中断服务函数为UARTn_DMA_RX_IRQHandler(),其调用HAL库的DMA处理函数HAL_DMA_IRQHandler(),该函数调用回调函数HAL_UART_RxCpltCallback()或HAL_UART_RxHalfCpltCallback(),这两个函数进入真正的中断服务处理函数dma_isr(struct rt_serial_device *),主体代码如下:

static void dma_isr(struct rt_serial_device *serial) { ...... /* 如果是DMA-RX中断 */ if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) || (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) { level = rt_hw_interrupt_disable(); /* 得到本次接收到的数据量 */ recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.ha ndle)); if (recv_total_index == 0) { /* 这一句代码,是什么意思? */ recv_len = serial->config.bufsz - uart->dma_rx.last_index; } else { /* 减去以前接收到的数据量,得到本次接收到的数据数量 */ recv_len = recv_total_index - uart->dma_rx.last_index; } /* 更新接收历史数据量 */ uart->dma_rx.last_index = recv_total_index; rt_hw_interrupt_enable(level); if (recv_len ) { /* 如果有新数据,调用serial设备模块的通用处理 */ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } } } 

在serial模块的函数rt_hw_serial_isr()中,主体代码是:

void rt_hw_serial_isr(struct rt_serial_device *serial, int event) { ...... case RT_SERIAL_EVENT_RX_DMADONE: { int length; rt_base_t level; /* get DMA rx length */ length = (event & (~0xff)) >> 8; if (serial->config.bufsz == 0) { /* 这个case的处理逻辑不知道怎么应用,看起来STM32实现并没有处理这个case */ struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx; RT_ASSERT(rx_dma != < span class="n">RT_NULL); RT_ASSERT(serial->parent.rx_indicate != RT_NULL); serial->parent.rx_indicate(&(serial->parent), length); rx_dma->activated = RT_FALSE; } else { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* update fifo put index, 将数据放入接收缓冲区 */ rt_dma_recv_update_put_index(serial, length); /* calculate received total length, 更新缓冲区信息 */ length = rt_dma_calc_recved_len(serial); /* enable interrupt */ rt_hw_interrupt_enable(level); /* invoke callback, 通知上层,有新数据到达 */ if (serial->parent.rx_indicate != RT_NULL) { serial->parent.rx_indicate(&(serial->parent), length); } } break; } ...... } 

上层接到通知后,读取函数最终调用驱动读函数rt_serial_read()函数,在DMA的条件下,调用_serial_dma_rx()从缓冲区读取数据。其代码为:

static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size); } ...... } 

DMA发送

DMA发送从驱动写函数rt_serial_write()开始,在DMA的条件下,调用_serial_dma_tx(),_serial_dma_tx()再调用操作的DMA发送函数发送数据,代码为:

static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size); } ...... }  

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) { ...... /* make a DMA transfer */ serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); ...... } 

STM32的dma_transmit()实现函数是stm32_dma_transmit(),其实现就是简单调用HAL_UART_Transmit_DMA(),代码为:

static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) { ...... if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK) ...... } 

实现非常简单。

 

这里分析一下RT-Thread中串口DMA方式的实现,以供做新处理器串口支持时的参考。

背景

在如今的芯片性能和外设强大功能的情况下,串口不实现DMA/中断方式操作,我认为在实际项目中基本是不可接受的,但遗憾的是,rt-thread现有支持的实现中,基本上没有支持串口的DMA,文档也没有关于串口DMA支持相关的说明,这里以STM32实现为背景,梳理一下串口DMA的实现流程,以供新处理器实现时以作参考。

DMA接收准备

启用DMA接收,需要在打开设备的时候做一些处理,入口函数为rt_device_open()。主体实现是:

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) { ...... result = device_init(dev); ...... result = device_open(dev, oflag); ...... } 

device_init()就是rt_serial_init()函数,其主要是调用configure()函数,

static rt_err_t rt_serial_init(struct rt_device *dev) { ...... if (serial->ops->configure) result = serial->ops->configure(serial, &serial->config); ...... } 

在stm32下,其configure()函数是stm32_configure(),其根据设备打开参数,配置STM32外设的寄存器。包括波特率、校验等串口工作参数。

device_open()函数就是rt_serial_open()函数,其主要实现是:

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) { ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_RX) { if (serial->config.bufsz == 0) { struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); RT_ASSERT(rx_dma != RT_NULL); rx_dma->activated = RT_FALSE; serial->serial_rx = rx_dma; } else { struct rt_serial_rx_fifo* rx_fifo; rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(rx_fifo != RT_NULL); rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); rx_fifo->put_index = 0; rx_fifo->get_index = 0; rx_fifo->is_full = RT_FALSE; serial->serial_rx = rx_fifo; /* configure fifo address and length to low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); } dev->open_flag |= RT_DEVICE_FLAG_DMA_RX; } #endif /* RT_SERIAL_USING_DMA */ ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_TX) { struct rt_serial_tx_dma* tx_dma; tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = R T_FALSE; rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL); serial->serial_tx = tx_dma; dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; /* configure low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX); } #end if /* RT_SERIAL_USING_DMA */ ...... }  

可见,其主要工作是为DMA接收准备FIFO缓冲区;为DMA发送准备发送数据缓冲队列,但是好像STM32中断并没有用到发送数据缓冲。

DMA配置数据来源是rt_hw_usart_init()函数,缺省的配置参数由宏RT_SERIAL_CONFIG_DEFAULT决定, 这里决定了缺省的接收缓冲区参数是64字节,通讯缺省参数是:115200,8N1。

#define RT_SERIAL_RB_BUFSZ 64 

DMA接收

DMA接收我们从DMA中断开始分析,DMA接收中断服务函数为UARTn_DMA_RX_IRQHandler(),其调用HAL库的DMA处理函数HAL_DMA_IRQHandler(),该函数调用回调函数HAL_UART_RxCpltCallback()或HAL_UART_RxHalfCpltCallback(),这两个函数进入真正的中断服务处理函数dma_isr(struct rt_serial_device *),主体代码如下:

static void dma_isr(struct rt_serial_device *serial) { ...... /* 如果是DMA-RX中断 */ if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) || (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) { level = rt_hw_interrupt_disable(); /* 得到本次接收到的数据量 */ recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); if (recv_total_index == 0) { /* 这一句代码,是什么意思? */ recv_len = serial->config.bufsz - uart->dma_rx.last_index; } else { /* 减去以前接收到的数据量,得到本次接收到的数据数量 */ recv_len = recv_total_index - uart->dma_rx.last_index; } /* 更新接收历史数据量 */ uart->dma_rx.last_index = recv_total_index; rt_hw_interrupt_enable(level); if (recv_len ) { /* 如果有新数据,调用serial设备模块的通用处理 */ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } } } 

在serial模块的函数rt_hw_serial_isr()中,主体代码是:

void rt_hw_serial_isr(struct rt_serial_device *serial, int event) { ...... case RT_SERIAL_EVENT_RX_DMADONE: { int length; rt_base_t  level; /* get DMA rx length */ length = (event & (~0xff)) >> 8; if (serial->config.bufsz == 0) { /* 这个case的处理逻辑不知道怎么应用,看起来STM32实现并没有处理这个case */ struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx; RT_ASSERT(rx_dma != RT_NULL); RT_ASSERT(serial->parent.rx_indicate != RT_NULL); serial->parent.rx_indicate(&(serial->parent), length); rx_dma->activated = RT_FALSE; } else { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* update fifo put index, 将数据放入接收缓冲区 */ rt_dma_recv_update_put_index(serial, length); /* calculate received total length, 更新缓冲区信息 */ length = rt_dma_calc_recved_len(serial); /* enable interrupt */ rt_hw_interrupt_enable(level); /* invoke callback, 通知上层,有新数据到达 */ if (serial->parent.rx_indicate != RT_NULL) { serial->parent.rx_indicate(&(serial->parent), length); } } break; } ...... } < /span>

上层接到通知后,读取函数最终调用驱动读函数rt_serial_read()函数,在DMA的条件下,调用_serial_dma_rx()从缓冲区读取数据。其代码为:

static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size); } ...... } 

DMA发送

DMA发送从驱动写函数rt_serial_write()开始,在DMA的条件下,调用_serial_dma_tx(),_serial_dma_tx()再调用操作的DMA发送函数发送数据,代码为:

static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size); } ...... } 

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) { ...... /* make a DMA transfer */ serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); ...... } 

STM32的dma_transmit()实现函数是stm32_dma_transmit(),其实现就是简单调用HAL_UART_Transmit_DMA(),代码为:

static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) { ...... if ( HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK) ...... } 

实现非常简单。

 

rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) { ...... result = device_init(dev); ...... result = device_open(dev, oflag); ...... } 

rt_err_t rt_device_open(rt_device_ t dev, rt_uint16_t oflag) { ...... result = device_init(dev); ...... result = device_open(dev, oflag); ...... } 

static rt_err_t rt_serial_init(struct rt_device *dev) { ...... if (serial->ops->configure) result = serial->ops->configure(serial, &serial->config); ...... } 

static rt_err_t rt_serial_init(struct rt_device *dev) { ...... if (serial->ops->configure) result = serial->ops->configure(serial, &serial->config); ...... } 

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t oflag) { ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_RX) { if (serial->config.bufsz == 0) { struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); RT_A SSERT(rx_dma != RT_NULL); rx_dma->activated = RT_FALSE; serial->serial_rx = rx_dma; } else { struct rt_serial_rx_fifo* rx_fifo; rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(rx_fifo != RT_NULL); rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); rx_fifo->put_index = 0; rx_fifo->get_index = 0; rx_fifo->is_full = RT_FALSE; < span class="n">serial->serial_rx = rx_fifo; /* configure fifo address and length to low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); } dev->open_flag |= RT_DEVICE_FLAG_DMA_RX; } #endif /* RT_SERIAL_USING_DMA */ ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_T X) { struct rt_serial_tx_dma* tx_dma; tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = RT_FALSE; rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL); serial->serial_tx = tx_dma; dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; /* configure low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX); } #endif /* RT_SERIAL_USING_DMA */ ...... } 

static rt_err_t rt_serial_open(struct rt_device *dev, rt_uint16_t ofl ag) { ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_RX) { if (serial->config.bufsz == 0) { struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) rt_malloc (sizeof(struct rt_serial_rx_dma)); RT_ASSERT(rx_dma != RT_NULL); rx_dma->activated = RT_FALSE; serial->serial_rx = rx_dma; } else { struct rt_serial_rx_fifo* rx_fifo; rx_fifo = (struct rt_serial_rx_fifo*) rt_malloc (sizeof(struct rt_serial_rx_fifo) + serial->config.bufsz); RT_ASSERT(rx_fifo != RT_NU LL); rx_fifo->buffer = (rt_uint8_t*) (rx_fifo + 1); rt_memset(rx_fifo->buffer, 0, serial->config.bufsz); rx_fifo->put_index = 0; rx_fifo->get_index = 0; rx_fifo->is_full = RT_FALSE; serial->serial_rx = rx_fifo; /* configure fifo address and length to low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *) RT_DEVICE_FLAG_DMA_RX); } dev->open_flag |= RT_DEVICE_FLAG_DMA_RX; } #endif /* RT_SERIAL_USING_DMA */ ...... #ifdef RT_SERIAL_USING_DMA else if (oflag & RT_DEVICE_FLAG_DMA_TX) { struct r t_serial_tx_dma* tx_dma; tx_dma = (struct rt_serial_tx_dma*) rt_malloc (sizeof(struct rt_serial_tx_dma)); RT_ASSERT(tx_dma != RT_NULL); tx_dma->activated = RT_FALSE; rt_data_queue_init(&(tx_dma->data_queue), 8, 4, RT_NULL); serial->serial_tx = tx_dma; dev->open_flag |= RT_DEVICE_FLAG_DMA_TX; /* configure low level device */ serial->ops->control(serial, RT_DEVICE_CTRL_CONFIG, (void *)RT_DEVICE_FLAG_DMA_TX); } #endif /* RT_SERIAL_USING_DMA */ ...... } 

#define RT_SERIAL_RB_BUFSZ 64 

#define RT_SERIAL_RB_BUFSZ 64 

static void dma_isr(struct rt_serial_device *serial) { ...... /* 如果是DMA-RX中断 */ if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) || (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) { level = rt_hw_interrupt_disable(); /* 得到本次接收到的数据量 */ recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); if (recv_total_index == 0) { /* 这一句代码,是什么意思? */ recv_len = serial->config.bufsz - uart->dma_rx.last_index; } else { /* 减去以前接收到的数据量,得到本次接收到的数据数量 */ recv_len = recv_total_index - uart->dma_rx.last_index; } /* 更新接收历史数据量 */ uart->dma_rx.last_index = recv_total_index; rt_hw_interrupt_enable(level); if (recv_len ) { /* 如果有新数据,调用serial设备模块的通用处理 */ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } } } 

static void dma_isr(struct rt_serial_device *serial) { ...... /* 如果是DMA-RX中断 */ if ((__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_TC) != RESET) || (__HAL_DMA_GET_IT_SOURCE(&(uart->dma_rx.handle), DMA_IT_HT) != RESET)) { level = rt_hw_interrupt_disable(); /* 得到本次接收到的数据量 */ recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle)); if (recv_total_index == 0) { /* 这一句代码,是什么意思? */ recv_len = serial->config.bufsz - uart->dma_rx.last_index; } else { /* 减去以前接收到的数据量,得到本次接收到的数据数量 */ recv_len = recv_total_index - uart->dma_rx.last_index; } /* 更新接收历史数据量 */ uart->dma_rx.last_index = recv_total_index; rt_hw_interrupt_enable(level); if (recv_len ) { /* 如果有新数据,调用serial设备模块的通用处理 */ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8)); } } } 

void rt_hw_serial_isr(struct rt_serial_device *serial, int event) { ...... case RT_SERIAL_EVENT_RX_DMADONE: { int length; rt_base_t level; /* get DMA rx length */ length = (event & (~0xff)) >> 8; if (serial->config.bufsz == 0) { /* 这个case的处理逻辑不知道怎么应用,看起来STM32实现并没有处理这个case */ struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx; RT_ASSERT(rx_dma != RT_NULL); RT_ASSERT(serial< span class="o">->parent.rx_indicate != RT_NULL); serial->parent.rx_indicate(&(serial->parent), length); rx_dma->activated = RT_FALSE; } else { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* update fifo put index, 将数据放入接收缓冲区 */ rt_dma_recv_update_put_index(serial, length); /* calculate received total length, 更新缓冲区信息 */ length = rt_dma_calc_recved_len(serial); /* enable interrupt */ rt_hw_interrupt_enable(level); /* invoke callback, 通知上层,有新数据到达 */ if (serial->parent.rx_indicate != RT_NULL) { serial->parent.rx_indicate(&(serial->parent), length); } } break; } ...... } 

void rt_hw_serial_isr(struct rt_serial_device *serial, int event) { ...... case RT_SERIAL_EVENT_RX_DMADONE: { int length; rt_base_t level; /* get DMA rx length */ length = (event & (~0xff)) >> 8; if (serial->config.bufsz == 0) { /* 这个case的处理逻辑不知道怎么应用,看起来STM32实现并没有处理这个case */ struct rt_serial_rx_dma* rx_dma; rx_dma = (struct rt_serial_rx_dma*) serial->serial_rx; RT_ASSERT(rx_dma != RT_NULL); RT_ASSERT(serial->parent.rx_indicate != RT_NULL); serial->parent.rx_indicate(&(serial->parent), length); rx_dma->activated = RT_FALSE; } else { /* disable interrupt */ level = rt_hw_interrupt_disable(); /* update fifo put index, 将数据放入接收缓冲区 */ rt_dma_recv_update_put_index(serial, length); /* calculate received total length, 更新缓冲区信息 */ length = rt_dma_calc_recved_len(serial); /* enable interrupt */ rt_hw_interrupt_enable(level); /* invoke callback, 通知上层,有新数据到达 */ if (serial->parent.rx_indicate != RT_NULL) { serial->parent.rx_indicate(&(serial->parent), length); } } break; } ...... } 

static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size); } ...... } 

static rt_size_t rt_serial_read(struct rt_device *dev, rt_off_t pos, void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_RX) { return _serial_dma_rx(serial, (rt_uint8_t *)buffer, size); } ...... } 

static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size); } ...... } 

static rt_size_t rt_serial_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size) { ...... else if (dev->open_flag & RT_DEVICE_FLAG_DMA_TX) { return _serial_dma_tx(serial, (const rt_uint8_t *)buffer, size); } ...... } 

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) { ...... /* make a DMA transfer */ serial->ops->dma_tran smit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); ...... } 

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length) { ...... /* make a DMA transfer */ serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); ...... } 

static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) { ...... if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK) ...... } 

stat ic rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction) { ...... if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK) ...... }  

Leave a Comment

Your email address will not be published.