0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

UART需要使用DMA发送吗 ?

黄工的嵌入式技术圈 来源:黄工的嵌入式技术圈 2020-03-07 16:57 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

DMA一种在嵌入式实时任务处理中常用的功能。

UART发送数据包,使用DMA方式能大量减轻CPU处理的时间,使其CPU资源不被大量浪费,尤其在UART收发大量数据包(如高频率收发指令)时具有明显优势。

Ⅰ简述DMA

DMA:Direct Memory Access,直接内存存取/访问。简单来说就是内存RAM直接和其他设备(外设)进行数据交互,而不需要CPU参与的一种控制器

DMA它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的数据复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

ⅡDMA优点

DMA在系统中的角色好比一个公司的员工,CPU好比是公司的老板。

老板想要寄送一个快递到北京,只需要一个口令安排员工即可,具体填写快递单号、物流、派送等一系列工作老板不用关心。最后快递被对方收到,通知一声老板即可。

回到UART发送数据,同样的道理,CPU只需要简单的操作(类似上面的“安排”),就可把一串数据包丢给DMA直接发送,最后发送完成,收到一个发送完成中断,通知CPU发送完成即可。

说到这里相信大部分人都明白了,老板可以亲自开车或者坐飞机送快递,完成这件事情,但会耽搁老板很多时间。

同样,如果我们使用UART自己发送,CPU就会不停仲裁发送结果,占据CPU大量资源。

RTOS中,特别是有大量任务需要处理的时候,UART使用DMA发送就会带来很大方便。使用裸机运行的相同,尤为突出。

ⅢUART使用DMA发送配置

本文使用STM32F4 MCU、标准外设库为例给大家简单讲述一下配置。


1.USART配置

USART(COM)宏定义:

/* COMM通信 */ #define COMM_COM USART2 #define COMM_COM_CLK RCC_APB1Periph_USART2 #define COMM_COM_TX_GPIO_CLK RCC_AHB1Periph_GPIOD //UART TX #define COMM_COM_TX_PIN GPIO_Pin_5 #define COMM_COM_TX_GPIO_PORT GPIOD #define COMM_COM_TX_SOURCE GPIO_PinSource5 #define COMM_COM_TX_AF GPIO_AF_USART2 #define COMM_COM_RX_GPIO_CLK RCC_AHB1Periph_GPIOD //UART RX #define COMM_COM_RX_PIN GPIO_Pin_6 #define COMM_COM_RX_GPIO_PORT GPIOD #define COMM_COM_RX_SOURCE GPIO_PinSource6 #define COMM_COM_RX_AF GPIO_AF_USART2 #define COMM_COM_IRQn USART2_IRQn #define COMM_COM_Priority 9 //优先级 #define COMM_COM_BaudRate 115200 //波特率 #define COMM_COM_IRQHandler USART2_IRQHandler //中断函数接口(见stm32f4xx_it.c)

USART配置:

/************************************************函数名称 : USART_COMM_Configuration功 能 : 通信串口配置参 数 : 无返 回 值 : 无作 者 : strongerHuang*************************************************/ void USART_COMM_Configuration(void){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 时钟配置 */ RCC_AHB1PeriphClockCmd(COMM_COM_TX_GPIO_CLK | COMM_COM_RX_GPIO_CLK, ENABLE); if((USART1 == COMM_COM) || (USART6 == COMM_COM)) RCC_APB2PeriphClockCmd(COMM_COM_CLK, ENABLE); else RCC_APB1PeriphClockCmd(COMM_COM_CLK, ENABLE); /* 复用配置 */ GPIO_PinAFConfig(COMM_COM_TX_GPIO_PORT, COMM_COM_TX_SOURCE, COMM_COM_TX_AF); GPIO_PinAFConfig(COMM_COM_RX_GPIO_PORT, COMM_COM_RX_SOURCE, COMM_COM_RX_AF); /* 引脚配置 */ GPIO_InitStructure.GPIO_Pin = COMM_COM_TX_PIN; //USART Tx GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(COMM_COM_TX_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = COMM_COM_RX_PIN; //USART Rx GPIO_Init(COMM_COM_RX_GPIO_PORT, &GPIO_InitStructure); /* NVIC配置 */ NVIC_InitStructure.NVIC_IRQChannel = COMM_COM_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = COMM_COM_Priority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* USART配置 */ USART_InitStructure.USART_BaudRate = COMM_COM_BaudRate; //波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b; //传输位数 USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位 USART_InitStructure.USART_Parity = USART_Parity_No ; //校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发功能 USART_Init(COMM_COM, &USART_InitStructure); USART_ClearFlag(COMM_COM, USART_FLAG_RXNE | USART_FLAG_TC); USART_ITConfig(COMM_COM, USART_IT_RXNE, ENABLE); //接收中断 USART_DMACmd(COMM_COM, USART_DMAReq_Tx, ENABLE); //使能DMA USART_Cmd(COMM_COM, ENABLE); //使能USART }

2.DMA配置

DMA宏定义:

/* COMM_DMA */ #define COMM_DR_ADDRESS ((uint32_t)USART2 + 0x04) #define COMM_DMA DMA1 #define COMM_DMA_CLK RCC_AHB1Periph_DMA1 #define COMM_TX_DMA_CHANNEL DMA_Channel_4 #define COMM_TX_DMA_STREAM DMA1_Stream6 #define COMM_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF6 #define COMM_TX_DMA_IRQn DMA1_Stream6_IRQn #define COMM_TX_DMA_Priority 8 //优先级 #define COMM_TX_DMA_IRQHandler DMA1_Stream6_IRQHandler //中断函数接口(见stm32f4xx_it.c) #define COMM_TX_DMA_IT_TCIF DMA_IT_TCIF6

DMA配置:

/************************************************函数名称 : USART_COMM_DMA_Configuration功 能 : 通信串口的DMA配置参 数 : 无返 回 值 : 无作 者 : strongerHuang*************************************************/ void USART_COMM_DMA_Configuration(void){ DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 使能时钟 */ RCC_AHB1PeriphClockCmd(COMM_DMA_CLK, ENABLE); /* NVIC配置 */ NVIC_InitStructure.NVIC_IRQChannel = COMM_TX_DMA_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = COMM_TX_DMA_Priority; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* DMA配置 */ DMA_DeInit(COMM_TX_DMA_STREAM); DMA_InitStructure.DMA_Channel = COMM_TX_DMA_CHANNEL; //DMA通道 DMA_InitStructure.DMA_PeripheralBaseAddr = COMM_DR_ADDRESS; //外设地址 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)0; //内存地址(待传入参数) DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //传输方向 DMA_InitStructure.DMA_BufferSize = 0; //传输长度(待传入参数) DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设递增 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //循环模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(COMM_TX_DMA_STREAM, &DMA_InitStructure); DMA_ClearFlag(COMM_TX_DMA_STREAM, COMM_TX_DMA_FLAG_TCIF); DMA_ITConfig(COMM_TX_DMA_STREAM, DMA_IT_TC, ENABLE); //使能DMA传输完成中断 DMA_Cmd(COMM_TX_DMA_STREAM, DISABLE); //初始化禁止 } ⅣDMA发送UART数据包

DMA发送函数:

/************************************************函数名称 : COMM_SendBufByDMA功 能 : 通信串口通过DMA发送数据参 数 : Buf ------ 数据(地址) Length --- 数据长度(字节)返 回 值 : 无作 者 : strongerHuang*************************************************/ void COMM_SendBufByDMA(uint8_t *Buf, uint16_t Length){ DMA_Cmd(COMM_TX_DMA_STREAM, DISABLE); //关闭DMA //内存地址 DMA_MemoryTargetConfig(COMM_TX_DMA_STREAM, (uint32_t)Buf, DMA_Memory_0); DMA_SetCurrDataCounter(COMM_TX_DMA_STREAM, Length); //设置DMA传输长度 DMA_Cmd(COMM_TX_DMA_STREAM, ENABLE); //使能DMA }

细心的朋友会发现,这个发送函数其实很简单,当然,这里是使用STM32F4芯片,其他芯片也差不多,原理类似。 HAL库同样可以完成。

关于DMA发送完成中断,可根据实际情况,如果使用RTOS,一般发送数据是一个任务,这个任务会OS等待(检测)发送完成信号(即DMA发送完成中断)。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    11327

    浏览量

    225903
  • uart
    +关注

    关注

    22

    文章

    1319

    浏览量

    106975
  • dma
    dma
    +关注

    关注

    3

    文章

    582

    浏览量

    106275
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    S32K344 RTD600上UART+DMA使用MCAL库,Uart_AsyncSend()发不出数据的原因?如何解决?

    我使用MCAL库配置两个串口,其中UART0使用DMA模式,UART1使用中断模式;发现UART0无法发出数据,UART1正常;如果把
    发表于 04-23 07:38

    是否可以通过基于UARTDMA在两个外部SRAM之间发送和接收数据?

    两个MPC5777C微控制器通过外部总线接口连接到两个外部SRAM。是否可以通过基于UARTDMA在两个外部SRAM之间发送和接收数据?
    发表于 04-06 07:38

    CW32F030的DMA问题求解

    应用很简单,通过DMAUART接收到的数据放到指定的内存缓冲区rx_buffer[]. 每次接收8个字节数据,下一次数据覆盖上一次. 这个过程本来可以无需CPU参与自动完成.无论串口发送的快慢都
    发表于 12-08 08:07

    UART无法收发/乱码的排查

    了吗?共地了吗? 3、引脚复用: 确认使用的 UART 引脚配置正确,且复用功能已开启。 4、中断/DMA: 如果使用中断或 DMA,相关配置和使能是否正确? 5、电平转换: 如果需要
    发表于 11-18 07:05

    哪些场景适合使用DMA

    。 串行通信(UART,SPI,I2C等): UART:在大量数据收发时,例如通过串口接收一帧数据或发送一帧数据,可以使用DMA来搬运数据。比如,使用
    发表于 11-12 07:13

    RVMCU课堂「18」: 手把手教你玩转RVSTAR—DMA数据传输篇

    _init()和uart_send()实现的完整代码工程,请参考文末附带链接)。 代码编写完成后,需要编译并上传到开发板,然后需要使用一个类似下图所示的TTL-USB串口转换器,将RV-STAR的
    发表于 10-30 08:29

    UART指令控制RGB灯实验

    保证开发板相关硬件连接正确,用Type-C USB线连接开发板“USB TO UART”接口跟电脑。本次实验需要使用到串口调试助手,配置好串口参数并打开串口后,在调试助手的发送区域输入任意字符并点击
    的头像 发表于 10-27 11:26 2453次阅读
    <b class='flag-5'>UART</b>指令控制RGB灯实验

    串口DMA发送有缓存吗?

    串口DMA发送有缓存吗, 我是从ringbuffer取出来,放到申请的缓存里,启动串口DMA发送,然后就释放了。暂时没发现什么问题。 用的drv_usart.c是这个版本
    发表于 10-10 06:14

    串口DMA发送数据一直发不出来是怎么回事?

    , buf, size) == HAL_OK) { return size; } else { return 0; } } return 0; } 仿真只有发送第一次的时候调用了HAL_UART_Transmit_DMA()函数,而且返回HAL_OK,但是就是没数
    发表于 10-09 07:01

    finsh 控制台dma发送如何操作?

    今天因为在高优先级线程中频繁调用rt_kprintf导致低优先级运行阻塞,所以突发奇想想试试控制台能不能dma发送,在保证对应uart tx dma的驱动打开后,rt_device_o
    发表于 09-11 07:48

    STM32H743 UART DMA接收不到数据是为什么?

    ;find %s failed!n\", \"uart3\"); return RT_ERROR; } /* 以 DMA 接收及轮询发送方式打开串口设备
    发表于 09-11 07:14

    SPI主机/从机接收发送都开启DMA通信

    发送;SPI 作为从机时,接收和发送同时开启 DMA 进行数据接收和发送。 注:本应用笔记对应的代码是基于雅特力提供的V2.x.x 板级支持包(BSP)而开发,对于其他版本BSP,
    发表于 09-10 16:56

    串口DMA发送失败的原因?怎么解决?

    我想使用DMA发送,但是出现了发送不出去,但是发送完成回调函数它是能进去的,打印出里面的内容,接收数据是没问题,这是为什么呢?有大佬指点一下吗 #include #include
    发表于 08-15 06:21

    XMC7100 F100K4160AA Uart2 dma实例程序无法运行,使用hal驱动发送丢字符并且无法接收怎么解决?

    问题1:修改SCB UART DMA Echo实例程序引脚和dma通道配置正常运行后,无法按照实例设计功能接收和显示。 问题2:使用hal驱动方式,能正常发送数据,但经常丢字符,添加
    发表于 08-13 06:23

    如何才能拿到UART通过DMA收取回来的数据呢?

    你好,我想询问一下UART使用DMA时收取数据的代码。之前我是用的是非DMA模式,使用的函数是CyU3PUartReceiveBytes,但是其处理速度不符合我的要求。因此我使用DMA
    发表于 05-22 08:31