STM32接收PC端TAG信号,在STM32上打包发送数据,串口问题

tips:
这是XJIE工作学习中遇到的一些问题,以及处理方式,是个人笔记(禁止没礼貌的转载

工作描述

最近工作需要在PC端接收标签信号然后发送到我的板子上,和ADC数据信号一起打包发送到上位机。在调试UART5的是否出现一系列的问题。
一开始在直接加上UART5的时候,直接用HAL库的HAL_UART_Receive(),程序直接卡死,在论坛上看了一些资料后,尝试看看发送功能是否正常,调用HAL_UART_Transmit(),系统正常。

硬件

  1. 搭载STM32的板子
  2. win64bit的PC

通信链

  1. PC --> 板子:UART5
  2. 板子 --> 上位机:USART1

问题一:UART5

一开始在直接加上UART5的时候,直接用HAL库的HAL_UART_Receive(),程序直接卡死,在论坛上看了一些资料后,尝试看看发送功能是否正常,调用HAL_UART_Transmit,系统正常。

于是一步一步的寻找问题,试图解决问题

Debug

1、检查硬件连接:

  1. 确保UART5的RX(接收)引脚正确连接,并且没有物理损坏。
  2. 检查接收线是否正确接触到对应的引脚,没有接触不良或短路的问题。

2、检查引脚配置:

  1. 在STM32的初始化代码中,确认RX引脚是否被正确配置为UART接收功能。你可以通过查阅STM32F446的数据手册来确认引脚功能和配置。

3、查看软件设置:

  1. 检查UART初始化代码,确保波特率、数据位、停止位和奇偶校验位设置正确。
  2. 确认是否启用了UART接收中断或DMA接收,并在相应的中断服务例程中添加了处理接收数据的代码。

4、使用HAL库或LL库函数:

  1. 如果你使用的是STM32的HAL库,确保调用了HAL_UART_Receive_IT()(中断方式)或HAL_UART_Receive_DMA()(DMA方式)来启动接收。
  2. 检查是否有错误处理代码,例如检查HAL_UART_ErrorCallback()是否被调用,以及是否处理了错误状态。

5、串口助手工具:

  1. 使用串口调试工具(如串口助手)来测试UART通信,确保问题不在于外部设备或电脑的串口设置。

6、固件和库版本:

  1. 确认你的STM32CubeMX或STM32CubeIDE工具包和固件库是最新版本,有时候库的BUG也会导致功能异常。

7、电源和接地:

1、检查供电和接地是否稳定,不稳定的电源和接地问题有时也会影响微控制器的功能。

8、示波器检测:

  1. 如果可能,使用示波器检查UART5 RX引脚上的信号,确保信号幅度和波形正常。

解决

在startup_stm32f446XX.s 中发现没有USRT4、USRT5的中断向量表。
添加DCD UART4_IRQHandler 以及 DCD UART5_IRQHandler 后,使用HAL_UART_Receive_IT程序正常接收到,PC端发送来的数据。

问题二:中断接收

ps :在usart.c 以及stm32f4XX_it.c 中并未做任何的修改。

到了这一步遇到的问题是,我的PC端给MCU发送了15次标签信号,但是,MCU只接收到了5次、6次或者8次(很随机,包括但是不限于这三个数值)。

串口接收

于是我检查了PC断是否发出15次标签信号,于是我连接上了示波器使用tigger功能来捕捉串口的波形。简单的描述一下这个使用步骤:

  1. 给示波器上电,连接好示波器的探头。
  2. 把示波器恢复默认模式(师傅说因为你不知道上一个人设置了什么)。
  3. 在你的示波器上设置好你探头的X1或者X10挡位,我是设置了X10挡位,因为X10挡位的带宽高
  4. 设置好tigger功能,按下sigal按钮,等待PC端发送信号。

串口中断原理

这里补充一点:中断时一个很重要的概念,我们需要了解,中断的触发机制(中断向量表的实现)。

续接上文,我是怎么发现,我的UART4无法进入中断的,我是怎么做让他进入中断的。

点开我的 .S 我发现他居然没有UART4的中断向量。于是在文件中加入

DCD     UART4_IRQHandler                  ; UART4
DCD     UART5_IRQHandler                  ; UART5 

UART4和UART5均可以进入中断。

问题三:收发不匹配

上面的问题已经得到了很好的解决,但是结果很完美吗?不并不是 😂

这个时候就出现了上面的结果我的PC端给MCU发送了15次标签信号,但是,MCU只接收到了5次、6次或者8次。

HAL库中断函数

这里我调整了我代码,删除了不合时宜的代码,尽管现在他依然不是很完美。

UART5_IRQHandler()

这个函数我之前提到过,如果你还记得。他是中断向量表中我们添加的函数。换言之,一旦接收寄存器存满了数据,就触发中断,触发这个函数。如果你的代码模板是STM32CubeMX生成的,那么这个函数会存在stm 32f4xx_it.c文件中。

中断服务程序(Interrupt Request Handler, IRQHandler)

void UART5_IRQHandler(void)
{
  /* USER CODE BEGIN UART5_IRQn 0 */

  /* USER CODE END UART5_IRQn 0 */
  	HAL_UART_IRQHandler(&huart5);
  /* USER CODE BEGIN UART5_IRQn 1 */
	HAL_UART_Receive_IT(&huart5, (uint8_t*)RxFreq, 1);

  /* USER CODE END UART5_IRQn 1 */
}

很明显我们可以看到,在这个函数里并没有接收数据等操作。

HAL_UART_IRQHandler()

顾名思义,这个就是HAL编写的串口中断服务函数。

  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

可以看出来在这个函数里开始经行幅值了。

    if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      UART_Receive_IT(huart);
      return;
    }

更重要的是,这个函数调用了一个名为UART_Receive_IT()的函数。

UART_Receive_IT()

这个函数主要是来修改串口的状态的。在这个函数中又这样的描述(我挑重要的来讲)

      /* Disable the UART Data Register not empty Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

      /* Disable the UART Parity Error Interrupt */
      __HAL_UART_DISABLE_IT(huart, UART_IT_PE);

      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
      __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;

他在函数里关闭了串口中断这点很重要,因为,在大部分的情况下我们需要整板可以连续的工作,而不是在,收到一个TAG后,就复位或者重新上电。

所以我们指定是要在其他的地方重新的中断。这里先卖一个关子。

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx Event callback*/
        huart->RxEventCallback(huart, huart->RxXferSize);
#else
        /*Call legacy weak Rx Event callback*/
        HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
      }
      else
      {
        /* Standard reception API called */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx complete callback*/
        huart->RxCpltCallback(huart);
#else
        /*Call legacy weak Rx complete callback*/
        HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

好好好,在这个函数里提到了另一个重要的函数HAL_UART_RxCpltCallback()这就是大名鼎鼎的中断回调函数了。这个名字特别奇怪,或许要从’线程‘的角度的来解释吧。我的理解就是,如果里希望在串口中断发生的时候进行一些其他的功能,比如点个灯,就可以写在这个函数里。对了,这个函数是一个弱函数,你可以在里工程的任何位置重新定义他。我一般习惯定义在usart.c文件里。

HAL_UART_Receive_IT()

哦不!!上面说到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);

    /* Set Reception type to Standard reception */
    huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

    return (UART_Start_Receive_IT(huart, pData, Size));
  }
  else
  {
    return HAL_BUSY;
  }
}

这个函数有指向另一个函数UART_Start_Receive_IT()

UART_Start_Receive_IT()
HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  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);

  if (huart->Init.Parity != UART_PARITY_NONE)
  {
    /* 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;
}

在这个函数里不仅幅值了,更重要的是,他重新开启了串口中断。

  • 32
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值