一、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()
编译下载到小熊派开发板,通过串口调试助手可以看到
三、代码的优化修改
在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);
- }
复制代码
是不是有一种莫名的熟悉感。
同样,串口助手验证成功
四、源码下载
————————————————
转载:Willliam_william
|