HAL库配置RS485+DMA+空闲中断收发数据

news2025/5/12 8:03:56

前言:

(1)DMA是单片机集成在芯片内部的一个数据搬运工,它可以代替单片机对数据进行传输、存储,节约CPU资源。一般应用场景,ADC多通道采集,串口收发(频繁进入接收中断),SPI和IIC通信等

(2)STM32F2系列的DMA控制器最多有2个,每个控制器有8个数据流,每个数据流可以映射到不同的通道。例如,DMA2的数据流7可能用于某个特定外设,比如USART1的TX。(每个数据流同一时间只能服务一个外设。例如,若USART1_TX占用了DMA2_Stream7,则该流不可用于其他外设)

(3)DMA配置参数:

DMA请求源与通道选择:DMA1通道1

数据传输方向:外设 ↔ 存储器,存储器 ↔ 存储器

地址递增配置:外设地址递增和存储器地址递增(如果外设是ADC数据寄存器,关闭递增,如果存储器地址是接收数组,则开启递增)

数据宽度与对齐:外设/存储器数据宽度(串口一般是8位,ADC一般是16位)

传输模式:单次模式(串口接收)和循环模式(ADC采集数据)

正文:

1、配置时钟,烧入方式,配置RS485的接收使能脚

2、配置串口,DMA

3、串口串口收发缓存结构体

#define USART_TX_MAX_LEN 100
#define USART_RX_MAX_LEN 100
typedef struct
{
	unsigned char tx_buf[USART_TX_MAX_LEN]; 
	unsigned char rx_buf[USART_RX_MAX_LEN];
	unsigned char rx_flag;
	unsigned char rx_len;
}usart_data_t;

extern usart_data_t stUsart1Data;

 4、 串口初始化代码加入  开启IDLE中断、开始DMA接收

__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);

5、串口中断函数中加入 接收数据代码

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))  
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		HAL_UART_AbortReceive(&huart1);	//已经接收完一帧数据,所以这里要停止接收,然后再重新接收	
		
		uint32_t   temp =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 ,
		stUsart1Data.rx_len =  USART_RX_MAX_LEN - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		stUsart1Data.rx_flag = 1;	// 接受完成标志位置1	
		
		HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);	//开始DMA接收
	}
  /* USER CODE END USART1_IRQn 1 */
}

6、配置串口printf打印函数

void Usart1Printf(const char *format,...)
{
	uint16_t len;
	va_list args;	
	va_start(args,format);
	len = vsnprintf((char*)stUsart1Data.tx_buf,sizeof(stUsart1Data.tx_buf)+1,(char*)format,args);
	va_end(args);
	if(HAL_UART_Transmit_DMA(&huart1, stUsart1Data.tx_buf, len)!= HAL_OK)   //判断是否发送正常,如果出现异常则进入异常中断函数
	{
		Error_Handler();
	}
	
}

7、RS485通信加入使能发送和接收

/* USER CODE BEGIN 1 */
void USART1_RS485_Send_Enable(void)
{
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
}

void USART1_RS485_Receive_Enable(void)
{
	HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}

8、测试代码

USART1_RS485_Receive_Enable(); //默认为接收使能
 while (1)
  {
    /* USER CODE END WHILE */
	if(stUsart1Data.rx_flag  == 1)  //接收完成标志
	{
		USART1_RS485_Send_Enable();
		Usart1Printf("%s\r\n",stUsart1Data.rx_buf);
		HAL_Delay(100);//等待串口DMA发送完成,如果不加入此行代码,会出现发送失败,因为还没有发送完就切换成接收模式,导致不能发送数据 
		USART1_RS485_Receive_Enable();
		stUsart1Data.rx_len = 0;//清除计数
		stUsart1Data.rx_flag = 0;//清除接收结束标志位
		memset(stUsart1Data.rx_buf,0,USART_RX_MAX_LEN);
	}
  }

9、测试结果

10、优化RS485发送

当发送数据后,我们会等待一定时间,等待发送完成后在开启串口接收,但此时资源遭到了一定的浪费

优化思路:等待触发串口发送完成中断后,使能串口接收中断

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
	if((__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET))  
	{
		__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
		HAL_UART_AbortReceive(&huart1);	//已经接收完一帧数据,所以这里要停止接收,然后再重新接收	
		
		uint32_t   temp =  __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数 ,
		stUsart1Data.rx_len =  USART_RX_MAX_LEN - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
		stUsart1Data.rx_flag = 1;	// 接受完成标志位置1	
		
		HAL_UART_Receive_DMA(&huart1,stUsart1Data.rx_buf,USART_RX_MAX_LEN);	//开始DMA接收
	}
	if (HAL_UART_GetState(&huart1) == HAL_UART_STATE_READY) 
	{
		// 发送完成 
		USART1_RS485_Receive_Enable();
	}
}


 USART1_RS485_Receive_Enable();
  while (1)
  {
    /* USER CODE END WHILE */
	if(stUsart1Data.rx_flag  == 1)  //接收完成标志
	{
		USART1_RS485_Send_Enable();
		Usart1Printf("%s\r\n",stUsart1Data.rx_buf);
//		HAL_Delay(100);//等待串口DMA发送完成 
//		USART1_RS485_Receive_Enable();
		stUsart1Data.rx_len = 0;//清除计数
		stUsart1Data.rx_flag = 0;//清除接收结束标志位
		memset(stUsart1Data.rx_buf,0,USART_RX_MAX_LEN);
	}
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338484.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【java实现+4种变体完整例子】排序算法中【计数排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是计数排序的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格: 一、计数排序基础实现 原理 通过统计每个元素的出现次数,按顺序累加得到每个元素的最终位置,并填充到结果数组中。 代码示…

嵌入式单片机开发 - Keil MDK 编译与烧录程序

Keil MDK 编译程序 1、Keil MDK 编译按钮 Build 按钮:重新编译整个工程的所有源文件,无论它们是否被修改过 Rebuild 按钮:仅编译修改过的文件及其依赖项,未修改的文件直接使用之前的编译结果 2、Keil MDK 编译结果 linking... …

裂项法、分式分解法——复杂分式的拆解

目录 一、裂项法 1. 核心思想 2. 适用场景 3. 步骤 4. 例题 二、分式分解 1. 核心思想 2. 适用场景 3. 步骤 4.例题 一、裂项法 1. 核心思想 将一项拆解为多项之差,使得在求和时中间项相互抵消,最终仅剩首尾少数项。 2. 适用场景 级数求和…

黑马点评秒杀优化

异步优化秒杀业务 回顾之前的内容黑马点评 秒杀优惠券集群下一人一单超卖问题-CSDN博客,为了处理并发情况下的线程安全和数据一致性的问题,我们已经完成了查询优惠券信息、判断秒杀是否开始和结束、检查库存、用户ID加锁、创建订单和扣减库存。 尽管之前…

JavaScript 的演变:2023-2025 年的新特性解析

随着Web技术的飞速发展,ECMAScript(简称ES)作为JavaScript的语言标准,也在不断进化。 本文将带你学习 ECMAScript 2023-2025 的新特性。 一、ECMAScript 2023 新特性 1.1 数组的扩展 Array.prototype.findLast()/Array.protot…

[Java · 初窥门径] Java 注释符

🌟 想系统化学习 Java 编程?看看这个:[编程基础] Java 学习手册 0x01:Java 注释符简介 在编写程序时,为了使代码易于理解,通常会为代码加一些注释。Java 注释就是用通俗易懂的语言对代码进行描述或解释&a…

Docker环境下SpringBoot程序内存溢出(OOM)问题深度解析与实战调优

文章目录 一、问题背景与现象还原**1. 业务背景****2. 故障特征****3. 核心痛点****4. 解决目标** 二、核心矛盾点分析**1. JVM 与容器内存协同失效****2. 非堆内存泄漏****3. 容器内存分配策略缺陷** 三、系统性解决方案**1. Docker 容器配置**2. JVM参数优化(容器…

【计算机网络】网络基础(协议,网络传输流程、Mac/IP地址 、端口号)

目录 1.协议简述2.网络分层结构2.1 软件分层2.2 网络分层为什么? 是什么?OSI七层模型TCP/IP五层(或四层)结构 3. 网络与操作系统之间的关系4.从语言角度理解协议5.网络如何传输局域网通信(同一网段) 不同网…

pgsql中使用jsonb的mybatis-plus和jps的配置

在pgsql中使用jsonb类型的数据时,实体对象要对其进行一些相关的配置,而mybatis和jpa中使用各不相同。 在项目中经常会结合 MyBatis-Plus 和 JPA 进行开发,MyBatis_plus对于操作数据更灵活,jpa可以自动建表,两者各取其…

使用MetaGPT 创建智能体(2)多智能体

先给上个文章使用MetaGPT 创建智能体(1)入门打个补丁: 补丁1: MeteGTP中Role和Action的关联和区别?这是这两天再使用MetaGPT时候心中的疑问,这里做个记录 Role(角色)和 Action&…

C# 使用.NET内置的 IObservable<T> 和 IObserver<T>-观察者模式

核心概念 IObservable<T> 表示 可观察的数据源&#xff08;如事件流、实时数据&#xff09;。 关键方法&#xff1a;Subscribe(IObserver<T> observer)&#xff0c;用于注册观察者。 IObserver<T> 表示 数据的接收者&#xff0c;响应数据变化。 三个核心…

Redis——网络模型之IO讲解

目录 前言 1.用户空间和内核空间 1.2用户空间和内核空间的切换 1.3切换过程 2.阻塞IO 3.非阻塞IO 4.IO多路复用 4.1.IO多路复用过程 4.2.IO多路复用监听方式 4.3.IO多路复用-select 4.4.IO多路复用-poll 4.5.IO多路复用-epoll 4.6.select poll epoll总结 4.7.IO多…

vue3 传参 传入变量名

背景&#xff1a; 需求是&#xff1a;在vue框架中&#xff0c;接口传参我们需要穿“变量名”&#xff0c;而不是字符串 通俗点说法是&#xff1a;在网络接口请求的时候&#xff0c;要传属性名 效果展示&#xff1a; vue2核心代码&#xff1a; this[_keyParam] vue3核心代码&…

旅游特种兵迪士尼大作战:DeepSeek高精准路径优化

DeepSeek大模型高性能核心技术与多模态融合开发 - 商品搜索 - 京东 随着假期的脚步日渐临近&#xff0c;环球影城等备受瞩目的主题游乐场&#xff0c;已然成为大人与孩子们心中不可或缺的节日狂欢圣地。然而&#xff0c;随之而来的庞大客流&#xff0c;却总让无数游客在欢乐的…

【MySQL】第一弹——MySQL数据库结构与操作

目录 一. 数据库介绍1.1 什么是数据库1.2 为什么要使用数据库1.3 主流数据库1.3.1 关系型数据库1.3.2 非关系型数据库 二. MySQL 的结构2.1 MySQL服务器和客户端2.2 MySQL服务器是如何组织数据的 三. 数据库的操作3.1 创建数据库语法格式示例 3.2 查看数据库语法格式示例 3.3 使…

Spring_MVC 快速入门指南

Spring_MVC 快速入门指南 一、Spring_MVC 简介 1. 什么是 Spring_MVC&#xff1f; Spring_MVC 是 Spring 框架的一个模块&#xff0c;用于构建 Web 应用程序。它基于 MVC&#xff08;Model-View-Controller&#xff09;设计模式&#xff0c;将应用程序分为模型&#xff08;M…

L38.【LeetCode题解】四数之和(双指针思想) 从汇编角度分析报错原因

目录 1.题目 2.分析 去重的代码 错误代码 3.完整代码 提交结果 1.题目 四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元…

字符串系列一>最长回文子串

目录 题目&#xff1a;解析&#xff1a;代码&#xff1a; 题目&#xff1a; 链接: link 解析&#xff1a; 代码&#xff1a; class Solution {public String longestPalindrome(String s) {char[] ss s.toCharArray();int n ss.length;int begin 0;//返回结果的起始字符串…

双击热备方案及不同方案的需求、方案对比

双击热备方案概述 一般实现双机热备的方案有三种,分别是共享存储双机热备方案、镜像双机热备方案、双机双柜双机热备方案,这三种方案对硬件要求不同,大家可以根据自身的业务应用特性来选择具体的双机热备方案以及对应的ServHA双机热备软件产品。 1、镜像双机热备 1.1镜像…

VSCODE插值表达式失效问题

GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_-CSDN博客 更换正确的vue域名 GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_ <script src"https://unpkg.com/vue2.6.14/dist/vue.js"></sc…