百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

STM32F412 串口接收不到数据的问题

toyiye 2024-06-06 22:11 12 浏览 0 评论

原本我写上位机的,最近工作需要写了一下下位机的代码。

使用的是STM32F412RETx的芯片,板子是电子工程师做的

使用STM32CubeMX V5.2.1、Keil uVision5做开发,使用HAL库

使用过程中多次出现串口接收的问题,最后都解决了,这里记录一下

串口的HAL有3类API

// 同步堵塞收发
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
// 异步中断传输
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
// 异步DMA传输(Direct Memory Access,DMA),不经过CPU,外设直接读写内存
HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

我使用的是异步中断HAL_UART_Receive_IT的进行串口数据接收,这个需要用到

接收完成中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);

在中断回调中只进行数据的解析,不做过多其他处理,避免中断时间过长,中断中也不要使用延时函数,尽量不在中断中进行IO输出操作。

第一种串口接收问题:现象为开机后串口可以接收数据,一会儿后就一直没有数据了。

查看错误为:HAL_UART_Receive_IT返回HAL_BUSY

先说一下我的使用方法:使用HAL_UART_Receive_IT接收数据,需要在每次接收完成后,再次调用HAL_UART_Receive_IT函数,一般是在HAL_UART_RxCpltCallback函数的末尾再次HAL_UART_Receive_IT。

HAL_UART_Receive_IT有个状态返回值,可以自己看一下这个函数的实现,代码也就几十行

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
 /* Check that a Rx process is not already ongoing */
 if (huart->RxState == HAL_UART_STATE_READY)
 {
 if ((pData == NULL) || (Size == 0U))
 {
 return HAL_ERROR;
 }
 /* Process Locked */
 __HAL_LOCK(huart);
 huart->pRxBuffPtr = pData;
 huart->RxXferSize = Size;
 huart->RxXferCount = Size;
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 huart->RxState = HAL_UART_STATE_BUSY_RX;
 /* Process Unlocked */
 __HAL_UNLOCK(huart);
 /* Enable the UART Parity Error Interrupt */
 __HAL_UART_ENABLE_IT(huart, UART_IT_PE);
 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
 __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
 /* Enable the UART Data Register not empty Interrupt */
 __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
 return HAL_OK;
 }
 else
 {
 return HAL_BUSY;
 }
}

一般这个函数失败都是返回HAL_BUSY居多,这里有两种情况会返回HAL_BUSY

1. huart->RxState != HAL_UART_STATE_READY,串口没准备好或者串口正在接收数据中,如果时正在接收数据,意味着其他地方已经调用过HAL_UART_Receive_IT,直接忽略等待接收完成就好,而我知道只有在HAL_UART_RxCpltCallback有再次调用,所以其他地方调用不存在,而且失败时查看过状态huart->RxState 是等于 HAL_UART_STATE_READY

2. __HAL_LOCK(huart);加锁失败,锁被占用。

看一下__HAL_LOCK(__HANDLE__)的定义

#define __HAL_LOCK(__HANDLE__) \
 do{ \
 if((__HANDLE__)->Lock == HAL_LOCKED) \
 { \
 return HAL_BUSY; \
 } \
 else \
 { \
 (__HANDLE__)->Lock = HAL_LOCKED; \
 } \
 }while (0U)

这里就判断了一次,如果锁被占用,直接返回,从我这边测试可以看到,一般都是因为锁被暂用然后返回了HAL_BUSY,那么就要看一下哪里占用了锁。

接收数据使用的是异步中断的函数HAL_UART_Receive_IT(),然而发送数据我使用的是同步堵塞的函数HAL_UART_Transmit(),可以自己看一下HAL_UART_Transmit()的实现,这里不列代码了,HAL_UART_Transmit()函数内从开始发送开始加锁,等待全部数据发送完成后才解锁,所以占用锁的时间是比较长的。而数据发送也比较多,所以基本判定是发送造成的加锁。

有两种方法可以解决发送造成的加锁问题:

1. 使用异步函数发送,这样占用锁时间就短,不过也有概率锁占用,可以选择重试几次可能就可以了

2. huart->RxState=HAL_UART_STATE_READY,且保证不存在多处代码同时发送,那么可以选择暴力解锁,我使用这种方案

下面确认一下被加锁的代码

加锁一般是因为要操作一下公用的数据。

下面我们分析一下串口UART_HandleTypeDef结构体

typedef struct __UART_HandleTypeDef
{
 USART_TypeDef *Instance; /*!< UART registers base address */
 UART_InitTypeDef Init; /*!< UART communication parameters */
 uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
 uint16_t TxXferSize; /*!< UART Tx Transfer size */
 __IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
 uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
 uint16_t RxXferSize; /*!< UART Rx Transfer size */
 __IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
 DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
 DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
 HAL_LockTypeDef Lock; /*!< Locking object */
 __IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
 and also related to Tx operations.
 This parameter can be a value of @ref HAL_UART_StateTypeDef */
 __IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.
 This parameter can be a value of @ref HAL_UART_StateTypeDef */
 __IO uint32_t ErrorCode; /*!< UART Error code */
} UART_HandleTypeDef;

结合HAL_UART_Receive_IT()、HAL_UART_Transmit()两个函数,发现其实收发数据时使用的成员变量基本是分开的,

发送使用pTxBuffPtr、TxXferSize、TxXferCount、hdmatx、gState

接收使用pRxBuffPtr、RxXferSize、RxXferCount、hdmarx、RxState

共用部分:ErrorCode

gState也可能是公用的,不过暂时在函数HAL_UART_Receive_IT中没发现有使用gState

检查自己的代码,发现确实是HAL_UART_Transmit()造成的锁,既然基本上主要成员没有共用,那就暴力解锁

在判断接收状态为HAL_UART_STATE_READY时,且被加锁,直接暴力解锁
if(HAL_UART_STATE_READY == huart->RxState && HAL_LOCKED == huart->Lock)
{ 
 __HAL_UNLOCK(huart); // 暴力解锁
}

至此由于加锁问题而造成的串口突然接收不到数据的问题暂时解决了,很暴力的方式


第一种串口接收问题:同样是上电后串口可以接收数据,接收一段时间后没数据了,

而且HAL_UART_Receive_IT()函数返回的是HAL_OK

检查了HAL_UART_GetError(),获取到错误HAL_UART_ERROR_ORE,应该是串口溢出的意思。

只是为什么溢出后就直接停止接收了?就算是丢包也不要给我直接停止工作了呀!!!删库后跑路了???

解决方法如下

既然知道报错,那就考虑清楚错误标志,特地也看了一下函数HAL_UART_IRQHandler(UART_HandleTypeDef *huart);内,的确是有错误标志的时候,不会调用接收完成回调

看了多篇文章,试了几种方法后终于找到一个清除错误标志有效的

使用__HAL_UART_CLEAR_OREFLAG(__HANDLE__)可以清空错误标志

使用__HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__)是没用的

最终完整版的修改如下:

HAL_StatusTypeDef K_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
 HAL_StatusTypeDef status = HAL_OK;
 for(int32_t i = 1; i < 1000; ++i)
 {
#if 1
 // 清除错误
 uint32_t isrflags = READ_REG(huart->Instance->SR);
 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_PE))!=RESET)
 {
 __HAL_UART_CLEAR_PEFLAG(huart);
 }
 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_FE))!=RESET)
 {
 __HAL_UART_CLEAR_FEFLAG(huart);
 }
 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_NE))!=RESET)
 {
 __HAL_UART_CLEAR_NEFLAG(huart);
 }
 if((__HAL_UART_GET_FLAG(huart, UART_FLAG_ORE))!=RESET)
 {
 //READ_REG(huart->Instance->CR1);//ORE清标志,第二步读CR
 //__HAL_UART_CLEAR_FLAG(huart, UART_FLAG_ORE);
 __HAL_UART_CLEAR_OREFLAG(huart);
 }
 if(HAL_UART_ERROR_NONE != huart->ErrorCode)
 {
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 }
#endif
 // 请求接收
 status = HAL_UART_Receive_IT(huart, pData, Size);
 if(HAL_OK == status)
 { // 成功
 return status;
 }
 else if(HAL_BUSY == status)
 {
 //printf("HAL_UART_Receive_IT failed. status:%d, RxState:0X%x, Lock:%d\r\n", status, huart->RxState, huart->Lock);
 if(HAL_UART_STATE_READY == huart->RxState && HAL_LOCKED == huart->Lock && i % 500 == 0)
 { // 接收是已经ready的,只是修改数据的锁被lock了,应该是堵塞发送那边一直在lock中, 稍微重试多次后(即等待一下解锁)就直接暴力解锁
 __HAL_UNLOCK(huart);
 continue;
 }
 }
 else if(HAL_ERROR == status)
 { // 直接返回错误
 //printf("HAL_UART_Receive_IT HAL_ERROR\r\n");
 return status;
 }
 else if(HAL_TIMEOUT == status)
 { // HAL_UART_Receive_IT 不存在timeout返回
 //printf("HAL_UART_Receive_IT HAL_TIMEOUT\r\n");
 }
 }
 if(HAL_OK != status)
 {
 //printf("HAL_UART_Receive_IT Err status:%d\r\n", status);
 }
 // 重试了N次
 return status;
}

发送也稍微处理一下

HAL_StatusTypeDef K_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
 HAL_StatusTypeDef status = HAL_OK;
 for(int32_t i = 1; i < 100000; ++i)
 {
 status = HAL_UART_Transmit(huart, pData, Size, Timeout);
 if(HAL_OK == status)
 { // 成功
 return status;
 }
 else if(HAL_BUSY == status)
 {
 //printf("HAL_UART_Transmit failed. status:%d, gState:0X%x, Lock:%d\r\n", status, huart->gState, huart->Lock);
 if(HAL_UART_STATE_READY == huart->gState && HAL_LOCKED == huart->Lock && i % 50 == 0)
 { // 发送状态是已经ready的,只是修改数据的锁被lock了,应该是其他发送或者接收锁了, 重试几次后还是一样则暴力解锁
 __HAL_UNLOCK(huart);
 continue;
 }
 }
 else if(HAL_ERROR == status)
 { // 直接返回错误
 return status;
 }
 else if(HAL_TIMEOUT == status)
 { // 超时则增加时间重试
 Timeout += 200;
 continue;
 }
 }
 // 重试了N次
 return status;
}

这样处理后,接收是可以了,虽然有丢包,至少能一直接收,之后发现之所以那么不稳定,因为STM32F412RETx需要在输入电源增加一个电容进行滤波,增减滤波后就正常了,至少没发现丢包,不加清除标志也没问题

参考了很多篇文章,仅列举有用的:

https://blog.csdn.net/mickey35/article/details/78529637

http://bbs.21ic.com/icview-2514912-1-1.html?ordertype=1

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码