Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
DMA发送
RT-Thread 串口DMA发送代码解析
发布于 2023-12-21 16:56:11 浏览:610
订阅该版
[tocm] ## RT-Thread 串口DMA发送代码解析 说明:该代码为基于现有的stm32例程对fm33lc02单片机进行的修改之后的代码。 #### 初始化代码 设备初始化,在fm33_uart_get_dma_config()函数里面对串口的dma参数进行了初始化。 ```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 } ``` #### 控制函数 该函数通过fm33_dma_config对dma外设进行初始化。 ```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初始化代码,该部分只实现了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); } ``` #### 数据发送 在数据发送的时候通过调用下面函数进行数据发送。在函数中引用了dataqueue数据队列的函数(介绍见:https://blog.csdn.net/whj123999/article/details/114175258),在函数中先将数据写入到数据队列中,之后将数据进行填充发送。 ```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); //将数据写入消息队列 result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER); if (result == RT_EOK) { level = rt_hw_interrupt_disable(); //判断串口是否正在进行收发 if (tx_dma->activated != RT_TRUE) { //串口没有进行发送操作。 tx_dma->activated = RT_TRUE; //切换到发送操作中 rt_hw_interrupt_enable(level); //数据发送 /* make a DMA transfer */ serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX); } else { //数据正在发送中 rt_hw_interrupt_enable(level); } return length; } else { rt_set_errno(result); return 0; } } ``` 当一次数据发送完成之后会调用回调函数,回调函数中会识别数据队列是否还有数据,有的话,会自动进行发送。 ```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数据发送完成 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; //取出当前发送完成的数据 rt_data_queue_pop(&(tx_dma->data_queue), &last_data_ptr, &data_size, 0); //判断消息队列中是否还有未发送完成的数据 if (rt_data_queue_peak(&(tx_dma->data_queue), &data_ptr, &data_size) == RT_EOK) { //有数据未发送完成,继续发送数据 /* 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 { //将外设改为空闲状态 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 */ } } ```
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
不讲武德的老法师
这家伙很懒,什么也没写!
文章
6
回答
16
被采纳
0
关注TA
发私信
相关文章
1
串口DMA发送,需要把DMA的发送中断关了吗?
2
串口DMA发送一段时间后,发送的频率越来越高
3
关于串口DMA发送大量数据,数据会丢失问题
4
rs485的发送问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
a1012112796
20
个答案
3
次被采纳
张世争
11
个答案
3
次被采纳
踩姑娘的小蘑菇
7
个答案
3
次被采纳
rv666
9
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
RTT_逍遥
1
篇文章
6
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部