电子知识

STM32H743 解决串口同时收发遇到的问题

王燕 来源:互联网2022-07-07 14:23  浏览:150

在使用1.2版本的HAL库开发STM32H743的串口7设备的时候,遇到了如下问题:

数据发送使用HAL_UART_Transmit进行发送,单独测试发送的时候,发送正常。


接收则是HAL_UART_Receive_IT,逐字节进行接收并存放至数组,配合定时器进行不定长数据接收,单独测试接收的时候,接收也正常。


然后博主这里就把TX和RX短接,按理说在发送完成后的50ms以内就会打印接收到的数据(定时器设置的50ms溢出,即不定长数据间隔为50ms),但是这里并没有看到输出。


研究了下源码,可以发现,发送的时候,会把串口状态huart->gState标记为发送忙HAL_UART_STATE_BUSY_TX并上锁。


HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

{

  uint16_t* tmp;

  uint32_t tICkstart = 0U;


 

  if(huart->gState == HAL_UART_STATE_READY)

  {

    if((pData == NULL ) || (Size == 0U))

    {

      return  HAL_ERROR;

    }


   

    __HAL_LOCK(huart);


    huart->ErrorCode = HAL_UART_ERROR_NONE;

    huart->gState = HAL_UART_STATE_BUSY_TX;


   

    tickstart = HAL_GetTick();


    huart->TxXferSize = Size;

    huart->TxXferCount = Size;

    while(huart->TxXferCount > 0U)

    {

      huart->TxXferCount--;

      if(UART_WaitonFlagUntilTimeout(huart, UART_FLAG_TXE, RESET, tickstart, Timeout) != HAL_OK)

      {

        return HAL_TIMEOUT;

      }

      if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))

      {

        tmp = (uint16_t*) pData;

        huart->Instance->TDR = (*tmp & (uint16_t)0x01FFU);

        pData += 2U;

      }

      else

      {

        huart->Instance->TDR = (*pData++ & (uint8_t)0xFFU);

      }

    }

    if(UART_WaitonFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)

    {

      return HAL_TIMEOUT;

    }


   

    huart->gState = HAL_UART_STATE_READY;


   

    __HAL_UNLOCK(huart);


    return HAL_OK;

  }

  else

  {

    return HAL_BUSY;

  }

}


而接收中断触发后,中断向量入口UART7_IRQHandler会直接调用HAL_UART_IRQHandler(&huart7)分析中断请求,根据接收数据完成请求函数调用UART_Receive_IT(huart),注意这里会把huart->RxState设置为准备接收HAL_UART_STATE_READY并上锁。


HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

{

 

  if(huart->RxState == HAL_UART_STATE_READY)

  {

    if((pData == NULL ) || (Size == 0U))

    {

      return HAL_ERROR;

    }


   

    __HAL_LOCK(huart);


    huart->pRxBuffPtr = pData;

    huart->RxXferSize = Size;

    huart->RxXferCount = Size;


   

    UART_MASK_COMPUTATION(huart);


    huart->ErrorCode = HAL_UART_ERROR_NONE;

    huart->RxState = HAL_UART_STATE_BUSY_RX;


   

    __HAL_UNLOCK(huart);


   

    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);


   

    if (READ_BIT(huart->Instance->CR1, USART_CR1_FIFOEN) != RESET)

    {

      SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);

      SET_BIT(huart->Instance->CR3, USART_CR3_RXFTIE);

    }

    else

    {

      SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE);

    }


    return HAL_OK;

  }

  else

  {

    return HAL_BUSY;

  }

}


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


typedef struct

{

  USART_TypeDef            *Instance;       


  UART_InitTypeDef         Init;             


  UART_AdvFeatureInitTypeDef AdvancedInit;   


  uint8_t                  *pTxBuffPtr;     


  uint16_t                 TxXferSize;       


  __IO uint16_t            TxXferCount;     


  uint8_t                  *pRxBuffPtr;     


  uint16_t                 RxXferSize;       


  __IO uint16_t            RxXferCount;     


  uint16_t                 Mask;             


  DMA_HandleTypeDef        *hdmatx;         


  DMA_HandleTypeDef        *hdmarx;         


  HAL_LockTypeDef           Lock;           


  __IO HAL_UART_StateTypeDef    gState;     


  __IO HAL_UART_StateTypeDef    RxState;     


  __IO uint32_t             ErrorCode;       


}UART_HandleTypeDef;


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


首先是串口初始化,没有使用HAL_UART_Receive_IT设置接收buffer,而是直接使能接收中断。


void MX_UART7_Init(void)

{


    huart7.Instance = UART7;

    huart7.Init.BaudRate = 115200;

    huart7.Init.WordLength = UART_WORDLENGTH_8B;

    huart7.Init.StopBits = UART_STOPBITS_1;

    huart7.Init.Parity = UART_PARITY_NONE;

    huart7.Init.Mode = UART_MODE_TX_RX;

    huart7.Init.HwFlowCtl = UART_HWCONTROL_NONE;

    huart7.Init.OverSampling = UART_OVERSAMPLING_16;

    huart7.Init.oneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;

    huart7.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;

    if (HAL_UART_Init(&huart7) != HAL_OK)

    {

        Error_Handler();

    }

    __HAL_UART_ENABLE_IT(&huart7, UART_IT_RXNE); //使能接收中断


中断服务函数这里,因为我只使能了接收中断,所以只对接收中断进行处理,其他中断源直接清除。


void UART7_IRQHandler(void)

{

    char ch;

    if ((__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET) &&

            (__HAL_UART_GET_IT_SOURCE(&huart7, UART_IT_RXNE) != RESET))

    {

        ch = huart7.Instance->RDR;

__HAL_TIM_SET_COUNTER(&htim6, 0);

        if (USART7_RX_STA & 0x8000) return;         //上次接收完成的未处理,直接退出

        if (USART7_RX_STA == 0)                  //长度为0,接收到的是第一个字节,启动定时器

        {

            __HAL_TIM_CLEAR_FLAG(&htim6, TIM_FLAG_UPDATE);

            HAL_TIM_base_Start_IT(&htim6);

        }

        USART7_RX_BUF[USART7_RX_STA & 0x3FFF] = ch;

        USART7_RX_STA++;

        if (USART7_RX_STA > (USART_REC_LEN - 1))USART7_RX_STA = 0;

    }

    else

    {

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_ORE) != RESET)

        {

   __HAL_UART_CLEAR_OREFLAG(&huart7);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_NE) != RESET)

        {

            __HAL_UART_CLEAR_NEFLAG(&huart7);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_FE) != RESET)

        {

            __HAL_UART_CLEAR_FEFLAG(&huart7);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_PE) != RESET)

        {

            __HAL_UART_CLEAR_PEFLAG(&huart7);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_CTS) != RESET)

        {

            __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_CTS);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TXE) != RESET)

        {

            __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TXE);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) != RESET)

        {

            __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_TC);

        }

        if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_RXNE) != RESET)

        {

            __HAL_UART_CLEAR_IT(&huart7, UART_FLAG_RXNE);

        }

    }

}


发送过程就比较简单了,等待发送完成继续发送就行了。


void u7_printf(char* s)

{

int i=0;

while(s[i])

{

huart7.Instance->TDR = s[i];

while (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_TC) == RESET);

i++;

}

}


热门资讯

更多资讯