news 2026/10/5 13:16:09

12. 利用PY32Studio+HAL库开发UART+DMA通讯

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
12. 利用PY32Studio+HAL库开发UART+DMA通讯

前言

在第七章中,我们采用查询和中断的方式进行UART的发送和接收,查询和纯中断方式的弊端如下:

1.查询方式:

CPU不断轮询UART的状态标志位(如TXE, RXNE),效率极低,CPU完全被阻塞,无法执行其他任务。

2.中断方式:

发送: 每发送完一个字节,产生一个中断,CPU需要响应中断并写入下一个字节。对于大量数据发送,中断频率非常高,CPU频繁进出中断,额外开销很大。

接收: 每接收到一个字节,产生一个中断,CPU需要立刻读取数据寄存器,否则下一个数据可能会覆盖当前数据(Overrun Error)。如果CPU正在处理高优先级中断任务,可能导致数据丢失。

为了解决上面这两种方式的缺点,本章采用DMA方式来进行数据发送和接收:

DMA发送:CPU只需一次性将要发送的数据数组地址、长度等信息配置给DMA,DMA就会自动地将内存中的数据逐个搬运到UART的发送数据寄存器中,直到发送完成才通知CPU。整个过程CPU几乎可以完全不管。

DMA接收:CPU只需提前开辟一个接收缓冲区(数组),并将其地址和长度配置给DMA。此后,UART收到的每一个字节都会由DMA自动搬运到指定的内存数组中。只有当整个缓冲区接收满(或一半时,利用半传输中断)时,DMA才通知CPU来处理数据。极大降低了中断频率, 避免了数据丢失。

DMA通讯的优点:

  • 高效: 极大减少了CPU开销,让CPU可以专注于数据处理、算法等核心任务。
  • 低功耗: CPU在数据传输期间可以进入睡眠模式,降低系统功耗。
  • 高可靠性: 尤其对于高速数据流,DMA方式能更可靠地处理数据,避免因中断延迟导致的数据丢失。

一、功能

使用UART1模块与USB转TTL模块进行串口通讯,UART1发送和接收采用DMA。

二、硬件

通过USB转TTL模块连接PC与Startkit板, Startkit板与USB转TTL模块的连线方式如下,通过串口将信息发送到串口助手。

Startkit 开发板

USB转TTL模块

Tx(PA0)

Rx

Rx(PA1)

Tx

GND

GND

三、PY32Studio配置

3.1 创建新的工程F032_UART_DMA

3.2 配置时钟参数

UART通讯需要高精度时钟,我们采用外部HSE,系统时钟通过PLL倍频为72MHz.

3.3 配置UART1的参数

第一步:激活UART1, 配置参数:

第二步:配置UART的发送和接收的DMA通道:

第三步:使能中断:

第四步:点击生成工程

四、编写应用代码

4.1 应用代码

在PY32Studio生成的代码的基础上,添加以下代码:

在main.c文件定义一个接收长度宏,放在Private define, USER CODE BEGIN PD后面:

/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define RX_FIXED_LEN 10U // 接收固定为10字节 /* USER CODE END PD */

增加四个变量rx_buf、tx_buf、dma_tx_complete_flag和dma_rx_complete_flag,放在Private variables,USER CODE BEGIN PV的后面

/* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ uint8_t rx_buf[RX_FIXED_LEN] = {0};//接收数组 uint8_t tx_buf[] = "\r\nPlease input 10 characters: \r\n"; //发送的提示语 uint8_t dma_tx_complete_flag = 0; // DMA发送完成标志 uint8_t dma_rx_complete_flag = 0; // DMA接收完成标志 /* USER CODE END PV */

在main.c文件中,重写发送和接收完成的回调函数,放在USER CODE BEGIN 4后面:

/* USER CODE BEGIN 4 */ /** @brief DMA发送完成回调 */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == UART1) { dma_tx_complete_flag = 1; //发送完成标志位置位 } } /** @brief DMA接收完成回调:收满RX_FIXED_LEN字节触发 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == UART1) { // 收到固定长度10字节,置标记 // 注意:回调里面不要做发送,只标记,业务放到while(1) dma_rx_complete_flag = 1; //接收完成标志位置位 // 重启DMA接收,等待下一帧10字节 HAL_UART_Receive_DMA(&huart1, rx_buf, RX_FIXED_LEN); } } /* USER CODE END 4 */

在main()函数中

  • 在USER CODE BEGIN 1段中,初始化发送和接收标志位;
  • 在USER CODE BEGIN 2段中,启动DMA接收数据;
  • 在while(1)循环中,加入以下代码:
    • 发送提示语(发送之前,判断发送标志位是否空闲);
    • 判断是否接收到10个字符数据,如果接收到,把接收到的数据发回去。
int main(void) { /* USER CODE BEGIN 1 */ dma_tx_complete_flag = 1; // 初始状态发送空闲 dma_rx_complete_flag = 0; //初始接收状态空闲 /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ Studio_RCC_Init(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ Studio_GPIO_Init(); Studio_DMA_Init(); Studio_UART1_Init(); /* USER CODE BEGIN 2 */ // 启动固定长度DMA接收 HAL_UART_Receive_DMA(&huart1, rx_buf, RX_FIXED_LEN); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while(1) { if(dma_tx_complete_flag == 1) { dma_tx_complete_flag = 0; // 发送tx_buf提示语 HAL_UART_Transmit_DMA(&huart1, tx_buf, sizeof(tx_buf)-1); } HAL_Delay(3000); // 如果检测收到固定10字节帧,把接收到的字符发送回去 if(dma_rx_complete_flag == 1) { dma_rx_complete_flag = 0; if(dma_tx_complete_flag == 1) //上一次发送任务是否完成 { dma_tx_complete_flag = 0; HAL_UART_Transmit_DMA(&huart1, rx_buf, RX_FIXED_LEN); //把接收到的数据发送回去 } } HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }

4.2 设置

在选项卡中,选择安装的编译器版本和Debug类型。

4.3 编译,下载

  1. 点击编译图标,开始编译程序;
  2. 编译成功后,开始下载程序;
  3. 下载成功后,开始调试程序;

4.4调试

点击调试图标,进入调试界面,运行程序,把rx_buf加到Watch1窗口中。在View菜单中,选择Periodic Window Update命令,全速运行代码,观察变量的值(接收到的数据)。

打开串口助手,选择正确的端口名,波特率,数据位,校验位和停止位,打开串口,开始接收数据。右面的窗口可以显示出从MCU发送过来的数据,然后输入10个字符,点击发送。发送完成后,可以看到接收到返回的字符。

五、总结

本文用UART+DMA实现报文发送和接收,整个过程主要步骤如下:

  1. 通过PY32Studio配置时钟,UART, DMA;
  2. 重写发送和接收的回调函数;
  3. 在while循环中,判断是否发送完成,如果发送完成,调用HAL_UART_Transmit_DMA(&huart1, tx_buf, sizeof(tx_buf)-1);发送提示语;如果接收完成,调用HAL_UART_Transmit_DMA(&huart1, tx_buf, sizeof(tx_buf)-1);发送接收到的数据;
  4. 在接收完成的回调函数中,再次启用接收函数HAL_UART_Receive_DMA(&huart1, rx_buf, RX_FIXED_LEN)。

注意:

连续快速发送前,一定要判断dma_tx_complete_flag,防止HAL_BUSY。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 13:16:06

Ubuntu安装Anaconda并配置环境

Ubuntu安装Anaconda并配置环境 在 Ubuntu 上进行 Python 开发,尤其是涉及数据科学、机器学习或科学计算时,Anaconda 是一个值得优先考虑的环境管理工具。它将 Python 解释器、conda 包管理器以及大量常用的科学计算库整合在一起,安装后即可直…

作者头像 李华
网站建设 2026/10/5 13:15:14

HuggingFace镜像配置教程:hf-mirror设置HF_ENDPOINT,ComfyUI模型下载不再超时

你是不是 pip 源配好了,装插件不卡了,结果一加载工作流,报错说 Cannot connect to huggingface.co? 别慌,我也踩过——pip 只管 Python 包,模型下载它管不了。HuggingFace 才是第二道坎。 这篇给你最省事的方案:设一个环境变量,把 HuggingFace 的地址永久指向国内镜像 …

作者头像 李华
网站建设 2026/10/5 13:15:13

HTML笔记——1Day

一、背景知识1. 两位前驱:图灵和冯诺依曼 2. 计算机基础:3. 应用软件:(1)C/S 结构:需要安装,偶尔更新,不跨平台(2)B/S 结构:无需安装,…

作者头像 李华
网站建设 2026/10/5 13:07:51

canal做mysql的异步传输工具

文章目录一、前言二、思路三、canal使用前,mysql配置四、详细介绍Kafka模式(1)启动kafka模式(2)持久化点位(重要)五、简单介绍TCP模式(1)启动tcp模式(2&#…

作者头像 李华
网站建设 2026/10/5 13:06:37

Wand-Enhancer 完整指南:三步本地解锁 Wand 免费版 2 小时限制

Wand-Enhancer 完整指南:三步本地解锁 Wand 免费版 2 小时限制 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/GitHub_Trending/we/Wand-Enhancer 每天免费使用 Wand 满两…

作者头像 李华