## RT-Thread 串口DMA發(fā)送代碼解析
說(shuō)明:該代碼為基于現(xiàn)有的stm32例程對(duì)fm33lc02單片機(jī)進(jìn)行的修改之后的代碼。
#### 初始化代碼
設(shè)備初始化,在fm33_uart_get_dma_config()函數(shù)里面對(duì)串口的dma參數(shù)進(jìn)行了初始化。
```c
int rt_hw_usart_init(void)
{
rt_size_t obj_num = sizeof(uart_obj) / sizeof(struct _uart);
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
rt_err_t result = 0;
fm33_uart_get_dma_config();
for (int i = 0; i < obj_num; i++)
{
/* init UART object */
uart_obj[i].config = &uart_config[i];
uart_obj[i].serial.ops = &_uart_ops;
uart_obj[i].serial.config = config;
/* register UART device */
result = rt_hw_serial_register(&uart_obj[i].serial, uart_obj[i].config->name,
RT_DEVICE_FLAG_RDWR
| RT_DEVICE_FLAG_INT_RX
| RT_DEVICE_FLAG_INT_TX
| uart_obj[i].uart_dma_flag
, NULL);
RT_ASSERT(result == RT_EOK);
}
return result;
}
```
```c
static void fm33_uart_get_dma_config(void)
{
#ifdef BSP_USING_UART0
uart_obj[UART0_INDEX].uart_dma_flag = 0;
#ifdef BSP_UART0_RX_USING_DMA
uart_obj[UART2_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif
#ifdef BSP_UART0_TX_USING_DMA
uart_obj[UART0_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif
#endif
#ifdef BSP_USING_UART1
uart_obj[UART1_INDEX].uart_dma_flag = 0;
#ifdef BSP_UART1_RX_USING_DMA
uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif
#ifdef BSP_UART1_TX_USING_DMA
uart_obj[UART1_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif
#endif
#ifdef BSP_USING_UART4
uart_obj[UART4_INDEX].uart_dma_flag = 0;
#ifdef BSP_UART4_RX_USING_DMA
uart_obj[UART4_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif
#ifdef BSP_UART4_TX_USING_DMA
uart_obj[UART4_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif
#endif
#ifdef BSP_USING_UART5
uart_obj[UART5_INDEX].uart_dma_flag = 0;
#ifdef BSP_UART5_RX_USING_DMA
uart_obj[UART5_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_RX;
#endif
#ifdef BSP_UART5_TX_USING_DMA
uart_obj[UART5_INDEX].uart_dma_flag |= RT_DEVICE_FLAG_DMA_TX;
#endif
#endif
}
```
#### 控制函數(shù)
該函數(shù)通過(guò)fm33_dma_config對(duì)dma外設(shè)進(jìn)行初始化。
```c
static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
{
struct _uart *uart;
#ifdef RT_SERIAL_USING_DMA
rt_ubase_t ctrl_arg = (rt_ubase_t)arg;
#endif
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct _uart, serial);
switch (cmd)
{
/* disable interrupt */
case RT_DEVICE_CTRL_CLR_INT:
/* disable rx irq */
NVIC_DisableIRQ(uart->config->irq_type);
/* disable interrupt */
FL_UART_DisableIT_RXBuffFull(uart->config->InitTypeDef);
break;
/* enable interrupt */
case RT_DEVICE_CTRL_SET_INT:
/* enable rx irq */
NVIC_SetPriority(uart->config->irq_type, 1);
NVIC_EnableIRQ(uart->config->irq_type);
/* enable interrupt */
FL_UART_EnableIT_RXBuffFull(uart->config->InitTypeDef);
break;
#ifdef RT_SERIAL_USING_DMA
case RT_DEVICE_CTRL_CONFIG:
fm33_dma_config(serial, ctrl_arg);
break;
#endif
case RT_DEVICE_CTRL_CLOSE:
if (FL_UART_DeInit(uart->config->InitTypeDef) != FL_PASS )
{
RT_ASSERT(0)
}
break;
}
return RT_EOK;
}
```
dma初始化代碼,該部分只實(shí)現(xiàn)了tx部分的dma傳輸。
```c
void fm33_dma_config(struct rt_serial_device *serial,rt_ubase_t ctrl_arg)
{
FL_DMA_InitTypeDef *DMA_Handle;
struct dma_config *dma_config;
struct _uart *uart;
RT_ASSERT(serial != RT_NULL);
uart = rt_container_of(serial, struct _uart, serial);
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
DMA_Handle = &uart->dma_rx.handle;
dma_config = &uart->config->dma_rx;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
DMA_Handle = &uart->dma_tx.handle;
dma_config = &uart->config->dma_tx;
}
LOG_D("%s dma config start", uart->config->name);
/* enable DMA clock && Delay after an RCC peripheral clock enabling*/
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
DMA_Handle->direction = FL_DMA_DIR_PERIPHERAL_TO_RAM;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
DMA_Handle->direction = FL_DMA_DIR_RAM_TO_PERIPHERAL;
}
if(uart->config->irq_type == UART0_IRQn)
{
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_1;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION1;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_2;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION2;
}
}
if(uart->config->irq_type == UART1_IRQn)
{
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_3;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION3;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_4;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION5;
}
}
if(uart->config->irq_type == UART4_IRQn)
{
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_2;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION3;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_3;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION4;
}
}
if(uart->config->irq_type == UART5_IRQn)
{
if (RT_DEVICE_FLAG_DMA_RX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_4;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION6;
}
if (RT_DEVICE_FLAG_DMA_TX == ctrl_arg)
{
dma_config->channel = FL_DMA_CHANNEL_6;
DMA_Handle->periphAddress = FL_DMA_PERIPHERAL_FUNCTION4;
}
}
DMA_Handle->memoryAddressIncMode = FL_DMA_MEMORY_INC_MODE_INCREASE;
DMA_Handle->flashAddressIncMode = FL_DMA_CH7_FLASH_INC_MODE_INCREASE;
DMA_Handle->dataSize = FL_DMA_BANDWIDTH_8B;
DMA_Handle->priority = FL_DMA_PRIORITY_HIGH;
DMA_Handle->circMode = FL_DISABLE;
FL_DMA_Init(DMA,DMA_Handle,dma_config->channel);
if (ctrl_arg == RT_DEVICE_FLAG_DMA_TX)
{
FL_DMA_DisableIT_TransferComplete(DMA,dma_config->channel);
FL_DMA_DisableIT_TransferHalfComplete(DMA,dma_config->channel);
FL_UART_SetTXIFMode(uart->config->InitTypeDef,FL_UART_TXIF_MODE_AFTER_DMA);
}
LOG_D("%s dma %s instance: %x", uart->config->name, flag == RT_DEVICE_FLAG_DMA_RX ? "RX" : "TX", DMA_Handle->Instance);
LOG_D("%s dma config done", uart->config->name);
}
```
#### 數(shù)據(jù)發(fā)送
在數(shù)據(jù)發(fā)送的時(shí)候通過(guò)調(diào)用下面函數(shù)進(jìn)行數(shù)據(jù)發(fā)送。在函數(shù)中引用了dataqueue數(shù)據(jù)隊(duì)列的函數(shù)(介紹見(jiàn):https://blog.csdn.net/whj123999/article/details/114175258),在函數(shù)中先將數(shù)據(jù)寫(xiě)入到數(shù)據(jù)隊(duì)列中,之后將數(shù)據(jù)進(jìn)行填充發(fā)送。
```c
rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{
rt_base_t level;
rt_err_t result;
struct rt_serial_tx_dma *tx_dma;
tx_dma = (struct rt_serial_tx_dma*)(serial->serial_tx);
//將數(shù)據(jù)寫(xiě)入消息隊(duì)列
result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER);
if (result == RT_EOK)
{
level = rt_hw_interrupt_disable();
//判斷串口是否正在進(jìn)行收發(fā)
if (tx_dma->activated != RT_TRUE)
{
//串口沒(méi)有進(jìn)行發(fā)送操作。
tx_dma->activated = RT_TRUE; //切換到發(fā)送操作中
rt_hw_interrupt_enable(level);
//數(shù)據(jù)發(fā)送
/* make a DMA transfer */
serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX);
}
else
{
//數(shù)據(jù)正在發(fā)送中
rt_hw_interrupt_enable(level);
}
return length;
}
else
{
rt_set_errno(result);
return 0;
}
}
```
當(dāng)一次數(shù)據(jù)發(fā)送完成之后會(huì)調(diào)用回調(diào)函數(shù),回調(diào)函數(shù)中會(huì)識(shí)別數(shù)據(jù)隊(duì)列是否還有數(shù)據(jù),有的話,會(huì)自動(dòng)進(jìn)行發(fā)送。
```c
void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
{
switch (event & 0xff)
{
case RT_SERIAL_EVENT_RX_IND:
{
int ch = -1;
rt_base_t level;
struct rt_serial_rx_fifo* rx_fifo;
/* interrupt mode receive */
rx_fifo = (struct rt_serial_rx_fifo*)serial->serial_rx;
RT_ASSERT(rx_fifo != RT_NULL);
while (1)
{
ch = serial->ops->getc(serial);
if (ch == -1) break;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rx_fifo->buffer[rx_fifo->put_index] = ch;
rx_fifo->put_index += 1;
if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0;
/* if the next position is read index, discard this 'read char' */
if (rx_fifo->put_index == rx_fifo->get_index)
{
rx_fifo->get_index += 1;
rx_fifo->is_full = RT_TRUE;
if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;
_serial_check_buffer_size();
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/* invoke callback */
if (serial->parent.rx_indicate != RT_NULL)
{
rt_size_t rx_length;
/* get rx length */
level = rt_hw_interrupt_disable();
rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index):
(serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index));
rt_hw_interrupt_enable(level);
if (rx_length)
{
serial->parent.rx_indicate(&serial->parent, rx_length);
}
}
break;
}
case RT_SERIAL_EVENT_TX_DONE:
{
struct rt_serial_tx_fifo* tx_fifo;
tx_fifo = (struct rt_serial_tx_fifo*)serial->serial_tx;
rt_completion_done(&(tx_fifo->completion));
break;
}
#ifdef RT_SERIAL_USING_DMA
//dma數(shù)據(jù)發(fā)送完成
case RT_SERIAL_EVENT_TX_DMADONE:
{
const void *data_ptr;
rt_size_t data_size;
const void *last_data_ptr;
struct rt_serial_tx_dma *tx_dma;
tx_dma = (struct rt_serial_tx_dma*) serial->serial_tx;
//取出當(dāng)前發(fā)送完成的數(shù)據(jù)
rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0);
//判斷消息隊(duì)列中是否還有未發(fā)送完成的數(shù)據(jù)
if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK)
{
//有數(shù)據(jù)未發(fā)送完成,繼續(xù)發(fā)送數(shù)據(jù)
/* transmit next data node */
tx_dma->activated = RT_TRUE;
serial->ops->dma_transmit(serial, (rt_uint8_t *)data_ptr, data_size, RT_SERIAL_DMA_TX);
}
else
{
//將外設(shè)改為空閑狀態(tài)
tx_dma->activated = RT_FALSE;
}
/* invoke callback */
if (serial->parent.tx_complete != RT_NULL)
{
serial->parent.tx_complete(&serial->parent, (void*)last_data_ptr);
}
break;
}
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)
{
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;
}
#endif /* RT_SERIAL_USING_DMA */
}
}
```