你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

解决基于STM32H743问题——串口收发

[复制链接]
STMCU小助手 发布时间:2023-3-11 22:40
在使用1.2版本的HAL库开发STM32H743的串口7设备的时候,遇到了如下问题:
数据发送使用HAL_UART_Transmit进行发送,单独测试发送的时候,发送正常。
接收则是HAL_UART_Receive_IT,逐字节进行接收并存放至数组,配合定时器进行不定长数据接收,单独测试接收的时候,接收也正常。
然后博主这里就把TX和RX短接,按理说在发送完成后的50ms以内就会打印接收到的数据(定时器设置的50ms溢出,即不定长数据间隔为50ms),但是这里并没有看到输出。

研究了下源码,可以发现,发送的时候,会把串口状态huart->gState标记为发送忙HAL_UART_STATE_BUSY_TX并上锁。
  1. HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
  2. {
  3.   uint16_t* tmp;
  4.   uint32_t tickstart = 0U;

  5.   /* Check that a Tx process is not already ongoing */
  6.   if(huart->gState == HAL_UART_STATE_READY)
  7.   {
  8.     if((pData == NULL ) || (Size == 0U))
  9.     {
  10.       return  HAL_ERROR;
  11.     }

  12.     /* Process Locked */
  13.     __HAL_LOCK(huart);

  14.     huart->ErrorCode = HAL_UART_ERROR_NONE;
  15.     huart->gState = HAL_UART_STATE_BUSY_TX;

  16.     /* Init tickstart for timeout managment*/
  17.     tickstart = HAL_GetTick();

  18.     huart->TxXferSize = Size;
  19.     huart->TxXferCount = Size;
  20.     while(huart->TxXferCount > 0U)
  21.     {
  22.       huart->TxXferCount--;
  23.       if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)
  24.       {
  25.         return HAL_TIMEOUT;
  26.       }
  27.       if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
  28.       {
  29.         tmp = (uint16_t*) pData;
  30.         huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);
  31.         pData += 2U;
  32.       }
  33.       else
  34.       {
  35.         huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);
  36.       }
  37.     }
  38.     if(UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
  39.     {
  40.       return HAL_TIMEOUT;
  41.     }

  42.     /* At end of Tx process, restore huart->gState to Ready */
  43.     huart->gState = HAL_UART_STATE_READY;

  44.     /* Process Unlocked */
  45.     __HAL_UNLOCK(huart);

  46.     return HAL_OK;
  47.   }
  48.   else
  49.   {
  50.     return HAL_BUSY;
  51.   }
  52. }
复制代码

而接收中断触发后,中断向量入口UART7_IRQHandler会直接调用HAL_UART_IRQHandler(&huart7)分析中断请求,根据接收数据完成请求函数调用UART_Receive_IT(huart),注意这里会把huart->RxState设置为准备接收HAL_UART_STATE_READY并上锁。
  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.     /* Process Locked */
  11.     __HAL_LOCK(huart);

  12.     huart->pRxBuffPtr = pData;
  13.     huart->RxXferSize = Size;
  14.     huart->RxXferCount = Size;

  15.     /* Computation of UART mask to apply to RDR register */
  16.     UART_MASK_COMPUTATION(huart);

  17.     huart->ErrorCode = HAL_UART_ERROR_NONE;
  18.     huart->RxState = HAL_UART_STATE_BUSY_RX;

  19.     /* Process Unlocked */
  20.     __HAL_UNLOCK(huart);

  21.     /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  22.     SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

  23.     /* Enable the UART Parity Error interupt and RX FIFO Threshold interrupt
  24.        (if FIFO mode is enabled) or Data Register Not Empty interrupt
  25.        (if FIFO mode is disabled).
  26.     */
  27.     if (READ_BIT(huart->Instance->CR1, USART_CR1_FIFOEN) != RESET)
  28.     {
  29.       SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
  30.       SET_BIT(huart->Instance->CR3, USART_CR3_RXFTIE);
  31.     }
  32.     else
  33.     {
  34.       SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);
  35.     }

  36.     return HAL_OK;
  37.   }
  38.   else
  39.   {
  40.     return HAL_BUSY;
  41.   }
  42. }
复制代码

查看一下定义,可以发现这里的一个设计缺陷,gState代表发送状态,RxState代表接收状态,通过这个状态机来保证接收和发送过程的完整性,但是,只设计了一把锁Lock,而锁是用来保护发送或者接收单个字节的完整性的,这样就导致了在连续发送的过程中,处理接收来的数据发现串口设备被上锁了,返回busy,从而丢失了发送的数据。
  1. typedef struct
  2. {
  3.   USART_TypeDef            *Instance;        /*!< UART registers base address        */

  4.   UART_InitTypeDef         Init;             /*!< UART communication parameters      */

  5.   UART_AdvFeatureInitTypeDef AdvancedInit;   /*!< UART Advanced Features initialization parameters */

  6.   uint8_t                  *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */

  7.   uint16_t                 TxXferSize;       /*!< UART Tx Transfer size              */

  8.   __IO uint16_t            TxXferCount;      /*!< UART Tx Transfer Counter           */

  9.   uint8_t                  *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */

  10.   uint16_t                 RxXferSize;       /*!< UART Rx Transfer size              */

  11.   __IO uint16_t            RxXferCount;      /*!< UART Rx Transfer Counter           */

  12.   uint16_t                 Mask;             /*!< UART Rx RDR register mask          */

  13.   DMA_HandleTypeDef        *hdmatx;          /*!< UART Tx DMA Handle parameters      */

  14.   DMA_HandleTypeDef        *hdmarx;          /*!< UART Rx DMA Handle parameters      */

  15.   HAL_LockTypeDef           Lock;            /*!< Locking object                     */

  16.   __IO HAL_UART_StateTypeDef    gState;      /*!< UART state information related to global Handle management
  17.                                                   and also related to Tx operations.
  18.                                                   This parameter can be a value of @ref HAL_UART_StateTypeDef */

  19.   __IO HAL_UART_StateTypeDef    RxState;     /*!< UART state information related to Rx operations.
  20.                                                   This parameter can be a value of @ref HAL_UART_StateTypeDef */

  21.   __IO uint32_t             ErrorCode;       /*!< UART Error code                    */

  22. }UART_HandleTypeDef;
复制代码

因为串口本身是全双工,一个比较合理的设计方法就是再设计一把锁进行保护,而网上的大部分解决方案是注释掉调用锁这个过程,个人是不想改动HAL库的,于是自己写发送函数和接收函数。


首先是串口初始化,没有使用HAL_UART_Receive_IT设置接收buffer,而是直接使能接收中断。
  1. void MX_UART7_Init(void)
  2. {

  3.     huart7.Instance = UART7;
  4.     huart7.Init.BaudRate = 115200;
  5.     huart7.Init.WordLength = UART_WORDLENGTH_8B;
  6.     huart7.Init.StopBits = UART_STOPBITS_1;
  7.     huart7.Init.Parity = UART_PARITY_NONE;
  8.     huart7.Init.Mode = UART_MODE_TX_RX;
  9.     huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  10.     huart7.Init.OverSampling = UART_OVERSAMPLING_16;
  11.     huart7.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  12.     huart7.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  13.     if (HAL_UART_Init(&huart7) != HAL_OK)
  14.     {
  15.         Error_Handler();
  16.     }
  17.     __HAL_UART_ENABLE_IT(&huart7, UART_IT_RXNE);        //使能接收中断
复制代码

中断服务函数这里,因为我只使能了接收中断,所以只对接收中断进行处理,其他中断源直接清除。
  1. void UART7_IRQHandler(void)
  2. {
  3.     char ch;
  4.     if ((__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET) &&
  5.             (__HAL_UART_GET_IT_SOURCE(&huart7, UART_IT_RXNE) != RESET))
  6.     {
  7.         ch = huart7.Instance->RDR;
  8.                 __HAL_TIM_SET_COUNTER(&htim6, 0);
  9.         if (USART7_RX_STA & 0x8000) return;         //上次接收完成的未处理,直接退出
  10.         if (USART7_RX_STA == 0)                         //长度为0,接收到的是第一个字节,启动定时器
  11.         {
  12.             __HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);
  13.             HAL_TIM_Base_Start_IT(&htim6);
  14.         }
  15.         USART7_RX_BUF[USART7_RX_STA & 0x3FFF] = ch;
  16.         USART7_RX_STA++;
  17.         if (USART7_RX_STA > (USART_REC_LEN - 1))USART7_RX_STA = 0;
  18.     }
  19.     else
  20.     {
  21.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_ORE) != RESET)
  22.         {
  23.             __HAL_UART_CLEAR_OREFLAG(&huart7);
  24.         }
  25.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_NE) != RESET)
  26.         {
  27.             __HAL_UART_CLEAR_NEFLAG(&huart7);
  28.         }
  29.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_FE) != RESET)
  30.         {
  31.             __HAL_UART_CLEAR_FEFLAG(&huart7);
  32.         }
  33.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_PE) != RESET)
  34.         {
  35.             __HAL_UART_CLEAR_PEFLAG(&huart7);
  36.         }
  37.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_CTS) != RESET)
  38.         {
  39.             __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_CTS);
  40.         }
  41.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TXE) != RESET)
  42.         {
  43.             __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TXE);
  44.         }
  45.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) != RESET)
  46.         {
  47.             __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TC);
  48.         }
  49.         if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET)
  50.         {
  51.             __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_RXNE);
  52.         }
  53.     }
  54. }
复制代码

发送过程就比较简单了,等待发送完成继续发送就行了。
  1. void u7_printf(char* s)
  2. {
  3.         int i=0;
  4.         while(s[i])
  5.         {
  6.                 huart7.Instance->TDR = s[i];
  7.                 while (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) == RESET);
  8.                 i++;
  9.         }
  10. }
复制代码
————————————————
版权声明:小盼你最萌哒如有侵权请联系删除



收藏 评论0 发布时间:2023-3-11 22:40

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版