stm32-hal库(5)--usart串口通信三种模式(主从通信)(关于通信失败和串口不断发送数据问题的解决)

news2024/11/25 14:58:06

问题:

最近发现,stm32cubemx最新版本f1系列的hal库(1.85版本)生成的hal库,其中stm32f1xx_hal_uart.c的库文件中,其串口发送接收存在一些问题:

1.没有使用 __HAL_LOCK__HAL_UNLOCK 宏,锁机制。

2.pdata8bitspdata16bits 未初始化为 NULL,可能会导致未定义行为。

3.在遇到超时错误时,没有恢复 huart->RxState 状态。

这样子会出现什么问题呢?

1.串口中断进不去,主机发送消息,从机无回应

2.轮询模式下,打开串口,其会不断给电脑串口发信息,直到几千几万条后溢出。

(也有可能是我没理解到位,如果知道原因的大佬,可以在评论区给点意见)

解决方法:

我将stm32cubemx的hal库版本进行了修改,改成了1.81版本,进行通信,实验成功。

步骤如下:

1.点击库管理

2.找到自己芯片库

如果你是1.85版本,如果前面框为绿色,选中然后下面点击移除就行。

点击1.81版本,进行安装。下图2框取消勾选,3框选择1.81。

其他配置和上篇文章一致:

STM32-hal库学习(4)--usart/uart通信 (单向通信)(同时显示在oled)-CSDN博客

1.轮询模式

什么是轮询模式?

轮询模式利用阻塞模式收发数据

        HAL_UART_Transmit():串口发送数据,使用超时管理机制
        HAL_UART_Receive(): 串口接收数据,使用超时管理机制

        其用于在没有中断机制或DMA机制的情况下,主动等待并处理外设的状态变化。在轮询模式下,CPU不断地检查外设的状态寄存器,以确定是否有数据可供处理。这种方式简单易用,但效率较低,因为CPU在等待期间不能处理其他任务。

程序:

main.c中加入

#include "stdio.h"
uint8_t RxDate[256];

因为oled函数里面没有显示hex类型函数,所以编写oled.c的hex显示函数:

void OLED_ShowHexArray(uint8_t x, uint8_t y, uint8_t *numArray, uint8_t Length, uint8_t size2, uint8_t Color_Turn)
{
    uint8_t i;
    uint8_t highNibble, lowNibble;
    for (i = 0; i < Length; i++)
    {
        highNibble = (numArray[i] >> 4) & 0x0F;
        lowNibble = numArray[i] & 0x0F;

        // 显示高半字节
        if (highNibble < 10)
        {
            OLED_ShowChar(x + (size2 / 2) * (2 * i), y, highNibble + '0', size2, Color_Turn);
        }
        else
        {
            OLED_ShowChar(x + (size2 / 2) * (2 * i), y, highNibble - 10 + 'A', size2, Color_Turn);
        }

        // 显示低半字节
        if (lowNibble < 10)
        {
            OLED_ShowChar(x + (size2 / 2) * (2 * i + 1), y, lowNibble + '0', size2, Color_Turn);
        }
        else
        {
            OLED_ShowChar(x + (size2 / 2) * (2 * i + 1), y, lowNibble - 10 + 'A', size2, Color_Turn);
        }
    }
}

在oled.h加入:

void OLED_ShowHexArray(uint8_t x, uint8_t y, uint8_t *numArray, uint8_t Length, uint8_t size2, uint8_t Color_Turn);

在main.c实现轮询,

#include "string.h"
#include "stdio.h"
uint8_t RxDate[256];
unsigned int num = 0;

while(1)加入

switch(HAL_UART_Receive(&huart1, RxDate, 200, 1))
    {
        case HAL_OK:
            HAL_Delay(1);
            HAL_UART_Transmit(&huart1, RxDate, 200, 1);
				
		
            break;
        
        case HAL_TIMEOUT:
            if (huart1.RxXferCount != 200-1)
            {
                HAL_UART_Transmit(&huart1, RxDate, 200-1 - huart1.RxXferCount, 1);
            }
            else
            {
                HAL_Delay(1);
            }
            break;
        
        case HAL_ERROR:
            // 错误处理逻辑,可以根据需要添加
            // 例如,重置 UART 或重新初始化
            huart1.RxState = HAL_UART_STATE_READY;
            __HAL_UNLOCK(&huart1);
            break;
        
        case HAL_BUSY:
            // 处理 UART 忙碌状态的逻辑
            // 可以选择等待一段时间再重试
            HAL_Delay(1);
            break;
        
        default:
            break;
    }
	
            
            OLED_ShowHexArray(48, 4, RxDate,1, 16, 0); // len 设置为 8,具体根据显示需求调整

  }

代码解释:HAL_UART_Receive接收huart1句柄的数据,RxDate,长度为200,等待时间为0xffff(也就是1)。若接收完毕,返回HAL_OK,则发送数据回电脑。

测试:

2.中断模式:

        USART的中断模式是一种数据传输方式,在这种模式下,当特定事件(如接收到一个字符或发送完一个字符)发生时,会触发中断请求,中断服务程序(ISR)负责处理这些事件。使用中断模式可以提高系统效率,因为在等待数据的过程中,CPU可以执行其他任务,而不需要不断轮询USART状态。

.中断模式收发数据
        HAL_UART_Transmit_IT():串口中断模式发送
        HAL_UART_Receive_IT(): 串口中断模式接收

HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

这个函数的目的是启动UART传输并以非阻塞的方式发送一定数量的数据。

参数说明:中断方式的收发函数只有三个参数
      第一个参数是要使用的串口句柄地址
      第二个参数是发送缓冲区的首地址,用于存放要发送的数据
      第三个参数是发送缓冲区长度

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

参数说明与发送函数类似,只是把第二个和第三个参数变为了接收缓冲区

程序:

main.c加入

uint8_t  TxDate[64], RxDate[64];
uint8_t  rxstate;

int main加入

HAL_UART_Receive_IT(&huart1,RxDate,1);

接收中断函数,我这里将长度设为了1,表示接收到一个字节,便接收完毕,返回一个字节,比如0x11 是一个十六进制数,表示的是一个字节(8位)数据。在计算机系统中,十六进制数 0x11 对应的二进制数是 00010001,占用一个字节的存储空间。因此,0x11 占用1个字节。

while(1)加入

if(rxstate == 1)
{
			rxstate = 0;
			HAL_UART_Transmit_IT(&huart1,TxDate,1);
}			

当接收标志位为1,则表示接收完成,如果标志位为1,便发送数据

最下面加上

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){
		memcpy(TxDate,RxDate,1);
		rxstate = 1;
		HAL_UART_Receive_IT(&huart1,RxDate,1);
	}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){

	}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance == USART1){

	}
  • memcpy(TxDate, RxDate, 1);:将接收到的一个字节的数据从 RxDate 复制到 TxDate。这里假设 RxDate 是接收缓冲区,TxDate 是发送缓冲区。
  • memcpy 函数用于内存拷贝,将 RxDate 中的一个字节数据复制到 TxDate 中。
  • 拷贝完成,标志位变成1

测试:

3.DMA模式:

什么是dma模式?

        DMA(Direct Memory Access,直接内存访问)是一种计算机系统中用于数据传输的机制。它允许数据在外设和内存之间直接传输,而不需要CPU的介入,从而减轻了CPU的负担,提高了数据传输的效率。

举个例子:

        想象一下我们搬家的场景:你要把家里的一些东西从旧房子搬到新房子。在传统的情况下,你可能要亲自搬每一箱东西,把它们从旧房子搬到新房子。这就相当于CPU传统地处理数据传输的方式。

      现在,有一支搬家队,他们专门负责搬家。你只需要告诉他们从哪里搬,搬到哪里,然后他们就会自己完成这项任务。而你可以利用这段时间去做其他事情,不需要亲自动手。这就有点类似于DMA的工作原理。

        在计算机中,CPU通常会处理数据的传输工作,就像你亲自搬家一样。但有了DMA,就好比有了一支专门负责数据传输的队伍。CPU只需要告诉DMA从哪里搬,搬到哪里,然后就可以去处理其他任务了。DMA负责在外设和内存之间直接传输数据,而不需要CPU一直参与。

        简而言之,DMA就像是一支搬家队伍,负责在不需要CPU亲自操劳的情况下完成数据传输任务,从而提高了系统的效率。

DMA模式的优势

  1. 效率高:DMA能够以较高的效率传输数据,因为传输过程不需要经过CPU。
  2. 释放CPU资源:在数据传输过程中,CPU可以执行其他任务,避免了CPU因数据传输而被阻塞。
  3. 传输速度快:由于DMA控制器专门用于数据传输,其速度通常比通过CPU进行传输要快。

DMA模式的工作原理

  1. 配置DMA控制器:在使用DMA模式之前,需要配置DMA控制器,包括源地址、目的地址、传输数据的大小等。
  2. 启动DMA传输:配置完成后,启动DMA传输。DMA控制器将接管数据传输任务。
  3. 传输完成中断:在传输完成后,DMA控制器会生成一个中断,通知CPU传输已经完成。

DMA发送函数

HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

参数类型和中断模式发送函数相同

DMA接收函数

HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

参数类型和中断模式接收函数相同 

HAL_UART_Transmit_DMA();串口DMA模式发送

HAL_UART_Transmit_DMA();串口DMA模式接收

HAL_UART_DMAPause() 暂停串口DMA

HAL_UART_DMAResume();恢复串口DMA

HAL_UART_DMAStop(); 结束串口DMA

因为比较多,关于代码部分,在日后文章中将具体写一下

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

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

相关文章

LinkedIn被封原因和解封方法

对于初识领英和对领英生态规则不熟悉的人来说&#xff0c;很容易造成领英账号被封号(被限制登录)的情况&#xff0c;那么如何才能避免和解决领英帐号被封号(被限制登录)的难题呢&#xff1f; 领英帐号被封号或被限制登录主要会有两类情况。 首先要搞清楚&#xff0c; Linkedi…

谷歌邮箱被停用,开发者账号也要废了?还能申诉回来吗?怎么申诉?

相信不少开发者都遭遇过开发者账号的邮箱被暂停的情况&#xff0c;有时候明明什么也没做&#xff0c;就被突然被停用了&#xff1f; 面对这种情况&#xff0c;开发者们最担心的莫过于这是否会波及到他们使用该邮箱注册的开发者账号&#xff1f;APP还会正常审核吗&#xff1f;毕…

接口自动化测试关联token的方法?

引言&#xff1a; 在接口自动化测试中&#xff0c;有时候我们需要关联token来进行身份验证或权限管理。本文将从零开始&#xff0c;介绍如何详细且规范地实现接口自动化测试中token的关联。 步骤一&#xff1a;准备工作 在开始之前&#xff0c;我们需要确保以下准备工作已完成…

一文说明白,香港优才计划到底适合哪些人申请?

香港优才计划的热度仍然在持续&#xff0c;自取消配额限制以来&#xff0c;仅2023年一年时间&#xff0c;优才计划申请人数就超过8万&#xff0c;比历年累积的申请人数还要多。 之所以有如此高的热度&#xff0c;完全是因为优才的灵活性&#xff0c;为许多中产精英拿香港身份甚…

Ruoyi-前后端分离部署

目录 一. 环境准备 二. 安装Nginx 三. 安装Java 四. 安装MySQL、Redis 五. 配置打包环境 1. 配置前端打包环境 2. 配置后端打包环境 3. 获取代码 4. 前端代码打包 5. 后端项目打包 六. 项目上线 1.前端项目上线 2. 后端项目上线 一. 环境准备 项目官网&#xff1a…

LED封装技术中SMD、COB和GOB的优缺点

在小间距LED显示屏的封装技术中&#xff0c;SMD、COB和GOB各有其优缺点&#xff0c;以下是对这些技术的详细分析&#xff1a; SMD&#xff08;Surface Mounted Devices&#xff09;表贴工艺技术 SMD技术是将LED灯珠焊接在电路板上的一种成熟技术&#xff0c;广泛应用于LED显示屏…

如何高效安全的开展HPC数据传输,保护数据安全?

高性能计算&#xff08;HPC&#xff09;在多个行业和领域中都有广泛的应用&#xff0c;像科学研究机构、芯片IC设计企业、金融、生物制药、能源、航天航空等。HPC&#xff08;高性能计算&#xff09;环境中的数据传输是一个关键环节&#xff0c;它涉及到将数据快速、安全地在不…

Studying-代码随想录训练营day23| 39.组合总和、40.组合总和II、131.分割回文串

第23天&#xff0c;回溯part02&#xff0c;回溯两个题型组合&#xff0c;切割(ง •_•)ง&#x1f4aa; 目录 39.组合总和 40.组合总和II 131.分割回文串 总结 39.组合总和 文档讲解&#xff1a;代码随想录组合总和 视频讲解&#xff1a;手撕组合总和 题目&#xff1a;…

启智畅想:AI集装箱箱号识别系统,解决方案提供商

AI集装箱箱号识别系统 当前,智能卡口管理行业正处于快速发展的阶段。随着物联网、大数据、人工智能等技术的不断进步,智能卡口管理系统已经能够实现对集装箱运输的全程跟踪、监控和管理,大大提高了管理效率和安全性。然而,市场上现有的智能卡口管理系统仍然存在一些痛点问题,如…

【文档智能】DLAFormer:端到端的解决版式分析、阅读顺序方法

前言 前面文章介绍到&#xff0c;文档智能中版式分析(DLA)&#xff08;《【文档智能 & RAG】RAG增强之路&#xff1a;增强PDF解析并结构化技术路线方案及思路》&#xff09;、阅读顺序&#xff08;《【文档智能】符合人类阅读顺序的文档模型-LayoutReader及非官方权重开源…

Windows怎么实现虚拟IP

在做高可用架构时&#xff0c;往往需要用到虚拟IP&#xff0c;在linux上面有keepalived来实现虚拟ip的设置。在windows上面该怎么弄&#xff0c;keepalived好像也没有windows版本&#xff0c;我推荐一款浮动IP软件PanguVip&#xff0c;它可以实现windows上面虚拟ip的漂移。设置…

Feign 配置全局日志存入mongo

1、开启feign日志 在application.yml 添加配置 feign:client:config:default:loggerLevel: FULL2、日志实体类 Document(collection "feignLogs") Data public class FeignLog {Idprivate String id;private String method;private String url;private LocalDate…

智慧园区综合平台解决方案PPT(75页)

## 智慧园区的理解 ### 从园区1.0到园区4.0的演进 1. 园区1.0&#xff1a;以土地经营为主&#xff0c;成本驱动&#xff0c;提供基本服务。 2. 园区2.0&#xff1a;服务驱动&#xff0c;关注企业成长&#xff0c;提供增值服务。 3. 园区3.0&#xff1a;智慧型园区&#xff…

Spring Boot结合FFmpeg实现视频会议系统视频流处理与优化

在构建高效稳定的视频会议系统时,实时视频流的处理和优化是开发者面临的核心挑战之一。这不仅仅是简单的视频数据传输,更涉及到一系列复杂的技术问题,需要我们深入分析和有效解决。 高并发与实时性要求: 视频会议系统通常需要支持多人同时进行视频通话,这就意味着系统需要…

IP白名单及其作用解析

在网络安全领域&#xff0c;IP白名单是一项至关重要的策略&#xff0c;它允许特定的IP地址或地址范围访问网络资源&#xff0c;从而确保只有受信任的终端能够连接。下面&#xff0c;我们将深入探讨IP白名单的定义、作用以及实施时的关键考虑因素。 一、IP白名单的定义 IP白名单…

django admin添加自己的页面

建立模型 如果要单独建一个页面&#xff0c;用于展示model的数据&#xff0c;可以新建一个model&#xff0c;继承自要展示的那个类 class ViewsByDayModel(ViewsByDay): # 父类为要展示的model类class Meta:proxy True # 使用代理verbose_name 每日浏览次数统计verbose_nam…

当客户想要实现监控视频AI识别

客户说&#xff0c;“我想要实现视频AI识别&#xff0c;你给我出个方案” “好好好&#xff0c;没问题” “现在有安装的监控设备吗” “现在已经安装了多少监控设备&#xff1f;需要加装吗&#xff1f;” “已经安装的监控设备是什么配置&#xff0c;有AI算法吗&#xff1…

GOROOT GOPATH GOPROXY GO111MODULE

GOROOT GOROOT代表Go的安装目录。可执行程序go(或go.exe)和gofmt(或gofmt.exe)位于 GOROOT/bin目录中。 配置GOROOT环境变量&#xff0c;其值为Go的安装目录&#xff1b;然后在环境变量PATH中添加GOROOT/bin路径。 注意&#xff1a;GOROOT变量只是代表了安装目录&#xff0c;不…

微服务 | Springboot整合GateWay+Nacos实现动态路由

1、简介 路由转发 执行过滤器链。 ​ 网关&#xff0c;旨在为微服务架构提供一种简单有效的统一的API路由管理方式。同时&#xff0c;基于Filter链的方式提供了网关的基本功能&#xff0c;比如&#xff1a;鉴权、流量控制、熔断、路径重写、黑白名单、日志监控等。 基本功能…

Spring容器的启动过程及留给开发者的可拓展点

一、Spring容器启动经过了哪些过程&#xff1f; 1、首先需要加载读取到应用环境中的配置&#xff0c;比如要加载的bean的class的包路径等信息。 【读取配置】 2、再就需要找到哪些类是需要spring进行类实例创建并管理的&#xff0c;扫描到具体的Class及Class元信息上的一些注…