STM32L4使用DMA发送串口数据

∥☆過路亽.° 提交于 2020-01-25 21:19:15

在上一个工程的基础上添加使用DMA传输串口数据
(上次工程见STM32使用LL库建立工程

一、CubeMX的配置

依次点击Configuration->DMA即可进入DMA口详细配置界面。
在这里插入图片描述
点击【Add】添加DMA传输请求,然后选择USART1_TX请求;
在这里插入图片描述
其他设置如下即可
在这里插入图片描述
然后依旧是工程设置里,将HAL改成HAL
在这里插入图片描述
然后便可以生成代码
在这里插入图片描述

二、用户代码修改

代码生成后打开工程,我们可以看到,在static void MX_USART1_UART_Init(void)函数中多了这些和USART_TX的DMA相关配置代码
在这里插入图片描述
然后我们将其复制,在usart.c文件中添加static void USART1_DMA_Init(void)函数并粘贴,另外将static void MX_DMA_Init(void) 函数中的DMA时钟使能代码复制到USART1_DMA_Init()函数头部;
在这里插入图片描述
另外添加外设和内存地址设置函数到USART1_DMA_Init()函数中,得到

//USART1_TX DMA Init
static void USART1_DMA_Init(void)
{
	//使能DMA1时钟
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
	//DMA1通道4选择为USART1_TX请求
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_4, LL_DMA_REQUEST_2);
	//数据传输方向为存储器到外设
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
	//通道优先级
	LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_LOW);
	//不执行循环操作
	LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL);
	//不执行外设地址增量操作
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT);
	//执行存储器地址增量操作
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT);
	//外设数据宽度
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE);
	//存储器数据宽度
	LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE);
	//DMA内存基地址
	LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4,(u32)USART1_TX_BUF);
	//DMA外设基地址
	LL_DMA_SetPeriphAddress(DMA1, LL_DMA_CHANNEL_4,(u32)&USART1->TDR);
}

然后将其添加到uart_init()函数中,并添加串口1的DMA发送使能函数,如下图
在这里插入图片描述
可见,我们需要声明USART1_DMA_EN,我们在usart.c头部添加一下代码:
在这里插入图片描述
然后重写一个串口1打印函数:

//串口1,printf 函数
//确保一次发送数据不超过USART1_TX_LEN字节
void u1_printf(char* fmt,...)  
{  
	static u8 first = 1;
	u16 i;
	va_list ap; 
	va_start(ap,fmt);
	
	if(first == 0)
		while(LL_DMA_IsActiveFlag_TC4(DMA1) == RESET);	//等待通道4传输完成
	else
		first = 0;
	
	vsprintf((char*)USART1_TX_BUF,fmt,ap);
	va_end(ap);
	USART1_TX_BUF[USART1_TX_LEN-1] = '\0';
	i=strlen((const char*)USART1_TX_BUF);		//此次发送数据的长度

	LL_DMA_ClearFlag_TC4(DMA1);	//清除通道4传输完成标志
	LL_DMA_DisableChannel(DMA1,LL_DMA_CHANNEL_4 );  //关闭USART1 TX DMA1 所指示的通道      
 	LL_DMA_SetDataLength(DMA1,LL_DMA_CHANNEL_4,i);	//DMA通道的DMA缓存的大小
 	LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);  //使能USART1 TX DMA1 所指示的通道 
}

然后将主函数中的printf()换成u1_printf()
在这里插入图片描述
编译下载到小熊派开发板,通过串口调试助手可以看到
ff

三、代码的优化修改

stm32l4xx_ll_dma.c文件中我发现了LL_DMA_DeInit()LL_DMA_Init()两个函数,和标识外设库的及其相似,所以我想着使用这两个函数重写USART1_DMA_Init()函数,重写后如下

//USART1_TX DMA Init
static void USART1_DMA_Init(void)
{
	LL_DMA_InitTypeDef DMA_InitStructure;
						
	LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);	//使能DMA1时钟
	LL_DMA_DeInit(DMA1,LL_DMA_CHANNEL_4);//将DMA1的通道4寄存器重设为缺省值
	
	DMA_InitStructure.PeriphRequest=LL_DMA_REQUEST_2;	//选择为USART1_TX请求
	DMA_InitStructure.Priority=LL_DMA_PRIORITY_LOW;		//通道优先级
	DMA_InitStructure.Direction=LL_DMA_DIRECTION_MEMORY_TO_PERIPH;//数据传输方向为存储器到外设
	DMA_InitStructure.MemoryOrM2MDstAddress=(u32)USART1_TX_BUF;		//DMA内存基地址
	DMA_InitStructure.MemoryOrM2MDstDataSize=LL_DMA_MDATAALIGN_BYTE;//存储器数据宽度
	DMA_InitStructure.MemoryOrM2MDstIncMode=LL_DMA_MEMORY_INCREMENT;//执行存储器地址增量操作
	DMA_InitStructure.Mode=LL_DMA_MODE_NORMAL;	//正常模式,不执行循环操作
	DMA_InitStructure.NbData=USART1_TX_LEN;		//数据传输量 
	DMA_InitStructure.PeriphOrM2MSrcAddress=(u32)&USART1->TDR;			//DMA外设基地址
	DMA_InitStructure.PeriphOrM2MSrcDataSize=LL_DMA_PDATAALIGN_BYTE;	//外设数据宽度
	DMA_InitStructure.PeriphOrM2MSrcIncMode=LL_DMA_PERIPH_NOINCREMENT;	//不执行外设地址增量操作
	LL_DMA_Init(DMA1, LL_DMA_CHANNEL_4,&DMA_InitStructure);
}

是不是有一种莫名的熟悉感。
同样,串口助手验证成功
在这里插入图片描述

四、源码下载

完整代码已上传https://download.csdn.net/download/qq_38113006/12080773

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!