在上一个工程的基础上添加使用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()
编译下载到小熊派开发板,通过串口调试助手可以看到
三、代码的优化修改
在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
来源:CSDN
作者:Willliam_william
链接:https://blog.csdn.net/qq_38113006/article/details/103836558