UART串口需要使用DMA发送吗?

IoT 2年前 (2021) admin
890 0 0

关注+星标公众,不错过精彩内容

UART串口需要使用DMA发送吗?

作者 | strongerHuang

微信公众号 | 嵌入式专栏


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发送就会带来很大方便。使用裸机运行的相同,尤为突出。

实例代码: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); //初始化禁止}

3.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发送完成中断)。

———— END ————


后台回复嵌入式软件设计与开发STM32阅读更多相关文章。


欢迎关注我的公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。

欢迎关注我的视频号:

UART串口需要使用DMA发送吗?


点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

原文始发于微信公众号(嵌入式专栏):UART串口需要使用DMA发送吗?

版权声明:admin 发表于 2021年12月21日 上午3:50。
转载请注明:UART串口需要使用DMA发送吗? | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...