当前位置: 首页 > news >正文

STM32F103_LL库+寄存器学习笔记08 - DMA串口发送,开启DMA传输完成中断

导言


DMA辅助USART发送与接收能够显著降低CPU负担,尤其在大数据量传输时优势明显。这样可以让CPU腾出更多资源去处理其他任务,同时利用DMA的并行传输能力提高数据传输效率。
大概可以这样理解:STM32F1的CPU是一个核,DMA1是第二个核,DMA2是第三个核。掌握DMA的使用后,相当于你的STM32F1芯片从单核变成三核。虽然,DMA只能用于数据的搬运,但是一个计算机系统做的绝大部分的活不就是数据搬运吗?所以,DMA必须学会!必须学会!必须学会!

效果如下:
在这里插入图片描述
如上所示,电脑串口助手发送字符串"Send_Msg\r\n"到单片机,单片机将字符串"Send_Mag\0"返回来。这一次将字符串返回来的不是CPU,而是DMA!
项目地址:https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library08_usart_dma_send

一、CubeMX


在这里插入图片描述
如上所示,在CubeMX上配置了DMA协助USART1_RX、USART1_TX。通常,RX的优先级要比TX高,先保证不丢失接收的数据。
在这里插入图片描述

1.1、usart.c

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上所示,跟上一章节相比,在函数MX_USART1_UART_Init()多些DMA相关的代码。

1.2、stm32f1xx_it.c

在这里插入图片描述
如上所示,在stm32f1xx_it.c里多了两个DMA1的全局中断函数。
在这里插入图片描述
如上所示,《STM32F1参考手册》的章节9.1.2的中断向量表看到,DMA1的通道4~通道5都有各自的全局中断地址,所以中断回调函数都不一样。

1.3、dma.c

在这里插入图片描述

二、代码(LL库)


开始使用LL库,实现USART1的DMA发送程序。

2.1、main.c

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2、stm32f1xx_it.c

在这里插入图片描述
如上所示,在DMA1通道4的全局中断里主要都是清除标志与关闭DMA通道。
在这里插入图片描述
如上所示,USART1全局中断的内容没有变化,跟上一章节一样。

2.3、编译、下载

在这里插入图片描述
如上所示,编译通过。
在这里插入图片描述
如上所示,电脑串口助手发送字符串"Send_Msg\r\n"到单片机,单片机将字符串"Send_Mag\0"返回来。这一次将字符串返回来的不是CPU,而是DMA!

三、寄存器梳理


3.1、DMA知识划重点

在这里插入图片描述
在这里插入图片描述
如上所示,DMA经过总线矩阵,可以访问APB1的所有外设、APB2的所有外设、SRAM、Flash。

在这里插入图片描述
如上所示,函数USART1_SendString_DMA()的代码是按照这个流程配置的。
在这里插入图片描述
如上所示,三个中断都很重要,都会用得上。
在这里插入图片描述
如上所示,根据《STM32F1参考手册》的章节10.3.7 - DMA请求映像看到,USART1_TX对应DMA1的通道4。

3.2、开启DMA1的时钟

在这里插入图片描述
如上所示,寄存器RCC_AHBENR的位0-DMA1EN置1,开启DMA1时钟。

RCC->AHBENR |= (1UL << 0UL); // 开启DMA1时钟

3.3、配置USART1_TX的DMA1通道4

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如上所示,配置DMA1协助USART1发送字符串,基本上将每一位都设置一遍(除了bit0)。

// 配置DMA1的通道4:普通模式,内存到外设(flash->USART1_TX),优先级高,存储器地址递增、数据大小都是8bit
__STATIC_INLINE void DMA1_Channel4_Configure(void) {// 开启时钟RCC->AHBENR |= (1UL << 0UL); // 开启DMA1时钟// 设置并开启全局中断NVIC_SetPriority(DMA1_Channel4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));NVIC_EnableIRQ(DMA1_Channel4_IRQn);// 数据传输方向DMA1_Channel4->CCR &= ~(1UL << 14UL); // 外设到存储器模式DMA1_Channel4->CCR |= (1UL << 4UL); // DIR位设置1(内存到外设)// 通道优先级DMA1_Channel4->CCR &= ~(3UL << 12UL); // 清除PL位DMA1_Channel4->CCR |= (2UL << 12UL);  // PL位设置10,优先级高// 循环模式DMA1_Channel4->CCR &= ~(1UL << 5UL);  // 清除CIRC位,关闭循环模式// 增量模式DMA1_Channel4->CCR &= ~(1UL << 6UL);  // 外设存储地址不递增DMA1_Channel4->CCR |= (1UL << 7UL);   // 存储器地址递增// 数据大小DMA1_Channel4->CCR &= ~(3UL << 8UL);  // 外设数据宽度8位,清除PSIZE位,相当于00DMA1_Channel4->CCR &= ~(3UL << 10UL); // 存储器数据宽度8位,清除MSIZE位,相当于00// 中断DMA1_Channel4->CCR |= (1UL << 1UL);   // 开启发送完成中断
}

在这里插入图片描述
如上所示,函数DMA1_Channel4_Configure()运行完之后,从debug模式的寄存器列表看到只有DMA1的寄存器CCR4有值。
在这里插入图片描述
如上所示,代码跟debug模式下看到的寄存器状态一一对应上。

3.4、串口DMA发送

void USART1_SendString_DMA(const char *data, uint16_t len)
{// 等待上一次DMA传输完成(也可以添加超时机制)while(tx_dma_busy);tx_dma_busy = 1; // 标记DMA正在发送// 如果DMA通道4正在使能,则先禁用以便重新配置if(DMA1_Channel4->CCR & 1UL) { // 检查EN位(bit0)是否置位DMA1_Channel4->CCR &= ~1UL;  // 禁用DMA通道4(清除EN位)while(DMA1_Channel4->CCR & 1UL); // 等待DMA通道4完全关闭}// 配置DMA通道4:内存地址、外设地址及数据传输长度DMA1_Channel4->CMAR  = (uint32_t)data;         // 配置内存地址DMA1_Channel4->CPAR  = (uint32_t)&USART1->DR;  // 配置外设地址DMA1_Channel4->CNDTR = len;                    // 配置传输数据长度// 开启USART1的DMA发送请求:CR3中DMAT(第7位)置1USART1->CR3 |= (1UL << 7UL);// 启动DMA通道4:设置EN位启动DMA传输DMA1_Channel4->CCR |= 1UL;
}

3.4.1、配置DMA1通道4的内存地址

在这里插入图片描述

void USART1_SendString_DMA(const char *data, uint16_t len) {//DMA1_Channel4->CMAR  = (uint32_t)data;   // 配置内存地址//
}

如上所示,寄存器DMA_CMAR就是告诉DMA从哪里(32位地址)开始搬运数据。

3.4.2、配置DMA1通道4的外设地址

在这里插入图片描述
如上所示,寄存器DMA_CPAR就是告诉DMA搬运数据的目的地(32位地址)。

DMA1_Channel4->CPAR  = (uint32_t)&USART1->DR;  // 配置外设地址串口1的DR寄存器,把字节数据发送出去

3.4.3、配置DMA1通道4的搬运数量

在这里插入图片描述

void USART1_SendString_DMA(const char *data, uint16_t len) {//DMA1_Channel4->CNDTR = len;  // 配置传输数据长度//
}

3.4.4、开启USART1的DMA发送请求

在这里插入图片描述

USART1->CR3 |= (1UL << 7UL); // 开启USART1的DMA发送请求:CR3中DMAT(第7位)置1

3.4.5、启动DMA1通道4传输数据

在这里插入图片描述

DMA1_Channel4->CCR |= 1UL;

3.5、DMA1通道4全局中断

void DMA1_Channel4_IRQHandler(void)
{/* USER CODE BEGIN DMA1_Channel4_IRQn 0 *//* USER CODE END DMA1_Channel4_IRQn 0 *//* USER CODE BEGIN DMA1_Channel4_IRQn 1 */if(DMA1->ISR & (1UL << 13)) {// 清除DMA传输完成标志:在IFCR寄存器中写1清除对应标志DMA1->IFCR |= (1UL << 13);// 禁用DMA通道4(清除CCR寄存器的EN位,位0)DMA1_Channel4->CCR &= ~(1UL << 0);// 清除USART1中DMAT位,关闭DMA发送请求(CR3寄存器的位7)USART1->CR3 &= ~(1UL << 7);// 标记DMA发送完成tx_dma_busy = 0;}/* USER CODE END DMA1_Channel4_IRQn 1 */
}

3.5.1、判断是不是DMA1通道4传输完成中断

在这里插入图片描述
如上所示,寄存器DMA_ISR的bit13-TCIF4代表的是DMA1通道4的传输完成中断。

if (DMA1->ISR & (1UL << 13UL)) {// DMA1通道4传输完成
}

3.5.2、清除DMA1通道4传输完成标志

在这里插入图片描述
如上所示,通过寄存器DMA_IFCR的位13-CTCIF置1来清除传输完成中断。

DMA1->IFCR |= (1UL << 13); // 清除DMA传输完成标志:在IFCR寄存器中写1清除对应标志

四、代码(寄存器方式)


4.1、main.c

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2、stm32f1xx_it.c

在这里插入图片描述

4.3、编译、debug调试

在这里插入图片描述
如上所示,成功编译。
在这里插入图片描述
如上所示,效果跟LL库一样。


http://www.mrgr.cn/news/96167.html

相关文章:

  • java程序员实用英语学习总结
  • STM32F103_LL库+寄存器学习笔记07 - 串口接收缓冲区非空中断
  • 网络安全法律法规简介
  • 锐捷EWEB路由器 timeout.php任意文件上传漏洞代码审计(DVB-2025-9003)
  • webpack配置详解+项目实战
  • pytorch+maskRcnn框架训练自己的模型以及模型导出ONXX格式供C++部署推理
  • 我的创作纪念日——三周年
  • unity 做一个圆形分比图
  • #C8# UVM中的factory机制 #S8.5# 对factory机制的重载进一步思考
  • 第十四届蓝桥杯省赛电子类单片机学习记录(客观题)
  • UDP视频传输中的丢包和播放花屏处理方法
  • Linux一步部署主DNS服务器
  • RGB-T综述
  • FALL靶场通关攻略
  • 【目标检测】【深度学习】【Pytorch版本】YOLOV1模型算法详解
  • Springbean(二)@Component及其派生注解自动注入(2)使用注意和加载问题
  • HarmonyOS之深入解析跳转支付宝小程序完成操作后如何自动返回App
  • 设计秒杀系统(高并发的分布式系统)
  • node-ddk,electron,主进程通讯,窗口间通讯
  • 为AI聊天工具添加一个知识系统 之152 当今AI模型和AI工具应用中的核心矛盾