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

坤仔N 2019-07-18 11:44:27  2811  收藏 9
版权
原本我写上位机的,最近工作需要写了一下下位机的代码。

使用的是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
————————————————
版权声明:本文为CSDN博主「坤仔N」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chenjk10/article/details/95939369

STM32F412 串口接收不到数据的问题相关推荐

  1. openmv串口数据 串口助手_STM32 串口接收不定长数据 STM32 USART空闲检测中断

    编者注: 单片机串口接收不定长数据时,必须面对的一个问题为:怎么判断这一包数据接收完成了呢?常见的方法主要有以下两种: 1.在接收数据时启动一个定时器,在指定时间间隔内没有接收到新数据,认为数据接收完 ...

  2. C# 串口接收1次数据会进入2次串口接收事件serialPort1_DataReceived,第2次进入时串口缓冲区为空

    在C#中使用串口接收数据时发现,在完整的接收完一次数据后,还会再次进入串口接收事件. 在网上搜索资料发现其他开发者也有遇到该问题: [1]  c#串口事件接受一次数据莫名其妙会触发两次   原文链接: ...

  3. 串口接收不定长数据的几种方式

    在阅读本文前,你需要先做到串口成功接收一个数据(相信这一点是很简单的)  这几天简单总结了一下用串口怎么接收一帧数据的办法,个人使用的有三种,下面逐一介绍: 第一种:使用中断的方式: 这种在数据接收不 ...

  4. ZYNQ进阶之路14--PS端uart串口接收不定长数据

    ZYNQ进阶之路14--PS端uart串口接收不定长数据 导语 ZYNQ串口简介 实现步骤 导语 繁忙的博主又来了,本节我们实现一个比较简单的东西:串口.之前的章节中我们也有使用PS端的串口进行收发数 ...

  5. STM32串口接收不定长数据原理与源程序

    **STM32串口接收不定长数据原理与源程序**CSDN上有很多关于STM32串口接收不定长数据的文章,但实际使用后发现照搬他们的代码,程序根本就不能正确接收数据,其中最关键的一句有问题.其余内容完全 ...

  6. Arduino 从串口接收到的数据中提取整型数据(parseInt)

    Arduino 从串口接收到的数据中提取整型数据(parseInt) 函数说明 parseInt函数可用于从设备接收到的数据中寻找整数数值. 该函数属于Stream类.该函数可被Stream类的子类所 ...

  7. 第三课--EFM32GG11系列--串口接收不定长度数据的几种方式

    一.硬件串口引脚定义 PE10-->USART0-TX PE11-->USART0-RX 上面表格中,红色方框画出的内容用程序实现如下,在串口初始化时需要实现,不过经本人测试过,把这段代码 ...

  8. stm32串口接收不定长数据_基于STM32之UART串口通信协议--接收

    一.前言 1.简介 回顾上一篇UART发送当中,已经讲解了如何实现UART的发送操作了,接下来这一篇将会继续讲解如何实现UART的接收操作. 2.UART简介 嵌入式开发中,UART串口通信协议是我们 ...

  9. HAL库的串口基础学习(包含串口接收不定长数据的实现)

    HAL库的串口基础学习(1) HAL库有一个特点就是对于许多外设的初始化以及功能操作,都提供有一个weak版本的函数,这是充分的展现出库名字的含义(Hardware Abstraction Layer ...

  10. HAL库实践记录之串口接收不定长数据

    串口1接收不定长数据 实验板是原子mini板 一开始使用官方库,只能接受定长数据.把数据长度设置为1时,发送多字节数据时又会丢数.所以自己重写串口中断处理函数. 首先搞一下Cube配置用法:Mode选 ...

最新文章

  1. 四月青少年编程组队学习(图形化四级)Task03
  2. 阿里专家讲中台:技术中台-分布式架构在蚂蚁金服的实践
  3. 除了芯片 我们还应关注哪些核心技术
  4. jodd-servlet工具集锦
  5. Android 常用的函数记录
  6. 我是大富豪php源码,最全大富豪3.4源码【自用可运营】含23款子游戏+可控制输赢工具...
  7. 语言模型(N-Gram)
  8. C语言中未显示声明函数返回值的类型
  9. 关于游戏网络设计的问题
  10. 如何优化cocos2d/x程序的内存使用和程序大小
  11. 通过日志分析mysql访问量,Mysql 慢查询和慢查询日志分析
  12. 固高板卡mct2008调试轴回零_MCT2008-for-ges 固高所有GE运动控制卡的DEMO软件,用来测试板卡是否正常工作 matlab 238万源代码下载- www.pudn.com...
  13. [异步图书].Python机器学习:预测分析核心算法.pdf
  14. 常见几种USB接口引脚定义,Type A、Type B、Micro USB、Mini USB、Type C
  15. 冷门节日借势营销策略分析
  16. ElasticSearch 技术分析与实战 读后感
  17. 双系统不显示系统选择界面
  18. 解决网页中文字无法选中的问题
  19. vue登录页面实现记住密码的操作
  20. Windows下后台运行Python程序,并终止特定程序

热门文章

  1. Techme INC:这5类人要警惕癌症!
  2. 当安装完Windows系统后出现“系统保留”分区该怎么办?
  3. shell 脚本实例--持续更新
  4. 基于stm32f401的双按键可视化多模式选择模块
  5. 【考研词汇训练营】Day 6 —— eventually,state,create,productivity,stimulate
  6. matlab排序excel数据库,matlab对excel数据进行排序求和
  7. IRQL的理解和认识
  8. python画正态分布_用python的matplotlib画标准正态曲线
  9. 使用数字滤波器处理音频噪声(附Matlab程序)
  10. matlab设置保存图像分辨率_matlab saveas 分辨率