• 单片机开发过程中遇到的关于串口的若干问题


      最近做了一个项目,涉及到了串口,本来以为像串口这种经常使用的通讯方式,开发起来应该是很简单的,不说易如反掌,至少也不应该在一个问题上卡壳太久。说到底还是自己经验不足,还得多多学习才是!

      该项目是使用CubeMX生成的初始化代码,在配置串口的时候我格外小心,该配置的都配置了,但是生成代码后烧到单片机中,却发现串口接收数据出现问题,只能接收到一次数据,后面无论如何都接收不到了。但是我已经在串口初始化的时候增加了一个函数HAL_UART_Receive_IT(&huart1,RevBuff,5);开启串口接收中断,按理说开启了中断,就应该可以正常接受了,但实际上只接收到一次。后面经过上网查找资料,看到有相同经历的人说除了在串口初始化函数后面增加HAL_UART_Receive_IT()函数,还应该在串口接收回调函数后面增加这个函数,也就是说每一次接收到数据之后都应该重新开启中断。经过测试,果然这个做法可以解决我的这个问题。

      敢情这个函数只能起作用一次,每一次接收处理完数据,这个中断又关闭了?看了很多网上的资料,很少有说为什么需要重复开启,但是都会重复开启。那么我们的猜想应该就是成立的了。这里附上代码:

     1 void MX_USART1_UART_Init(void)
     2 {
     3 
     4   huart1.Instance = USART1;
     5   huart1.Init.BaudRate = 115200;
     6   huart1.Init.WordLength = UART_WORDLENGTH_8B;
     7   huart1.Init.StopBits = UART_STOPBITS_1;
     8   huart1.Init.Parity = UART_PARITY_NONE;
     9   huart1.Init.Mode = UART_MODE_TX_RX;
    10   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    11   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    12   if (HAL_UART_Init(&huart1) != HAL_OK)
    13   {
    14     _Error_Handler(__FILE__, __LINE__);
    15   }
    16     while(HAL_UART_Receive_IT(&huart1,RevBuff,5) != HAL_OK);
    17 }
    1 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    2 {
    3     ...
    4     ...
    5     while(HAL_UART_Receive_IT(&huart1,RevBuff,5) != HAL_OK);    //如果要正确接收数据,串口初始化函数和回调函数之后都要加上这个函数
    6 }

      然后我还遇到一个问题,就是每次当我上位机发送某一条指令的时候,总会引起单片机出现问题,具体表现为上位机发了该指令后单片机没有做出相对应的动作,甚至后面再发其他指令都没有任何对应的动作了。而经过测试,其他指令正常情况下都是可以被正确解读并执行的,那么问题应该就是出在这条特殊的指令了,这个问题现在说得轻巧,但是之前在排查的时候并没有考虑到指令的问题,绕了不少弯路,最后才找到问题所在的。问题出在这条特殊的指令不像其他指令一样是5字节的,它只有3字节,所以就导致单片机一直进入不了串口接收的回调函数。后来我查阅了资料,并且看了HAL库函数,知道了原因,这里跟大家说明一下。首先看一张图,是串口接收中断的流程图:

       由上图可知,RxXferCount必须等于0的时候,才能调用HAL_UART_RxCpltCallback(huart);函数。RxXferCount是接收到一个字节递减一次的,而它原始的值使我们调用HAL_UART_Receive_IT(&huart1,RevBuff,5)的时候确定的,就是我定了5。所以上位机只发了3个字节,它就只递减了3次,还没达到0,就没办法调用接收回调函数,也就不会执行里面的内容。解决的办法就是让上位机再填充2个没有含义的数据,或者把HAL_UART_Receive_IT(&huart1,RevBuff,5)里的5改为1,一次接收一个,通过修改回调函数里的逻辑去判断。但是我更倾向于第一种方法,所有的指令统一格式,看起来比较整洁有条理,且下位机一次性接收多个数据,效率更高。

      接下来,我们看看HAL_UART_Receive_IT()函数的源代码:

     1 HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
     2 {
     3   /* Check that a Rx process is not already ongoing */
     4   if(huart->RxState == HAL_UART_STATE_READY)
     5   {
     6     if((pData == NULL) || (Size == 0U))
     7     {
     8       return HAL_ERROR;
     9     }
    10 
    11     /* Process Locked */
    12     __HAL_LOCK(huart);
    13 
    14     huart->pRxBuffPtr = pData;
    15     huart->RxXferSize = Size;
    16     huart->RxXferCount = Size;
    17 
    18     huart->ErrorCode = HAL_UART_ERROR_NONE;
    19     huart->RxState = HAL_UART_STATE_BUSY_RX;
    20     
    21     /* Process Unlocked */
    22     __HAL_UNLOCK(huart);
    23 
    24     /* Enable the UART Parity Error Interrupt */
    25     __HAL_UART_ENABLE_IT(huart, UART_IT_PE);
    26 
    27     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    28     __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
    29 
    30     /* Enable the UART Data Register not empty Interrupt */
    31     __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
    32 
    33     return HAL_OK;
    34   }
    35   else
    36   {
    37     return HAL_BUSY;
    38   }
    39 }

       从第15行和16行我们可以看到,我们把Size赋值给了 huart->RxXferSize 和 huart->RxXferCount这两个参数,所以当我们把Size设置为5的时候,huart->RxXferCount必须递减5次才会变成0,从而进入回调函数执行其中的内容。

    
    
    
  • 相关阅读:
    27. 为什么线程执行要调用start而不是直接run
    25. ThreadLocal的使用场景
    23. 线程如何退出结束
    20. Java字符串格式化方法
    21. 时间的格式化方法
    19. 用过spring的线程池还是java的线程池?
    17. zookeeper的实现机制,有缓存,如何存储注册服务的
    面试-spring 那些事
    Apache服务器和tomcat服务器有什么区别?
    JNDI 和JDBC的区别
  • 原文地址:https://www.cnblogs.com/young-dalong/p/13716860.html
Copyright © 2020-2023  润新知