STM32串口卡死问题

本次遇到的问题是USART串口发送函数卡死程序,究其原因原来是串口发送函数中的发送空寄存器没有置位,且超时时间设置的太大导致程序死循环,直到发送超时退出。

在调用CUBE的串口发送函数时一定要注意写的方式。关于传送完毕,有人用如下方法等待传送完毕虽然方案可行
while(HAL_OK !=HAL_UART_Transmit(&huart2, transmit,len, timeout));     
这容易卡死,一般也不要这样写这是第一种情况,使用while有几个弊端,不仅程序写的难看 万一,HAL_UART_Transmit()返回的不是HAL_OK而是HAL_TIMEOUT,程序卡死在这里。
还有一种情况就是上边提到的超时时间太长导致程序短暂的卡死,下边简单介绍。

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint16_t *tmp;
  uint32_t tickstart = 0U;

  /* Check that a Tx process is not already ongoing */
  if (huart->gState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return  HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    huart->gState = HAL_UART_STATE_BUSY_TX;

    /* Init tickstart for timeout managment*/
    tickstart = HAL_GetTick(); //这里每次在发送前拿到systick中断中的计数值,作为计数的初值。

    huart->TxXferSize  = Size;
    huart->TxXferCount = Size;

    while (huart->TxXferCount > 0U)
    {
    //下边这个函数会去判断每一个字节是否发送 UART_FLAG_TXE,并进行超时判断。
      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);
      }
      huart->TxXferCount--;
    }

    if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, tickstart, Timeout) != HAL_OK)
    {
      return HAL_TIMEOUT;
    }

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

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}



HAL_StatusTypeDef UART_WaitOnFlagUntilTimeout(UART_HandleTypeDef *huart, uint32_t Flag, FlagStatus Status, uint32_t Tickstart, uint32_t Timeout)
{
  /* Wait until flag is set */
  while ((__HAL_UART_GET_FLAG(huart, Flag) ? SET : RESET) == Status)//在这里对发送空寄存器进行判断,如果为0,进行超时判断。
  {
    /* Check for the Timeout */
    if (Timeout != HAL_MAX_DELAY)
    {
      if ((Timeout == 0U) || ((HAL_GetTick() - Tickstart) > Timeout))//这里拿到SYSTICK的最新计数值减去传入的初值与超时时间进行比较,当大于超时时间时,退出。
      {
        /* Disable TXE, RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts for the interrupt process */
#if defined(USART_CR1_FIFOEN)
        CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE | USART_CR1_TXEIE_TXFNFIE));
#else
        CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE | USART_CR1_TXEIE));
#endif
        CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

        huart->gState = HAL_UART_STATE_READY;
        huart->RxState = HAL_UART_STATE_READY;

        /* Process Unlocked */
        __HAL_UNLOCK(huart);

        return HAL_TIMEOUT;
      }
    }
  }
  return HAL_OK;
}

USART的各个寄存器描述如下:
  WAKE:唤醒的方法 (Wakeup method)  0:被空闲总线唤醒;  1:被地址标记唤醒。
  PCE:检验控制使能 (Parity control enable)
  PS:校验选择 (Parity selection)  0:偶校验;1:奇校验。
  PEIE:PE中断使能 (PE interrupt enable)
  TXEIE:发送缓冲区空中断使能 (TXE interrupt enable)
  TCIE:发送完成中断使能 (Transmission complete interrupt enable)
  RXNEIE:接收缓冲区非空中断使能 (RXNE interrupt enable)
  IDLEIE:IDLE中断使能 (IDLE interrupt enable)  0:禁止产生中断; 1:当USART_SR中的IDLE为’1’时,产生USART中断。
  TE:发送使能 (Transmitter enable)
  RE:接收使能 (Receiver enable)
  RWU:接收唤醒 (Receiver wakeup)  0:接收器处于正常工作模式; 1:接收器处于静默模式。
  TXE:发送数据寄存器空 (Transmit data register empty)

    当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
        0:数据还没有被转移到移位寄存器;
        1:数据已经被转移到移位寄存器。
    TC:发送完成 (Transmission complete)

    当包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。在我遇到的问题中改发送空标志位没有置位,并且我的超时时间又设置的比较大,导致一直等待知道超时退出为止,所以合理的设置超时时间可以有效的保证硬件没有及时置位(或者芯片抽风了硬件未能自动置位)程序超时退出,不至于等太久。

HAL_UART_Transmit(UART_HandleTypeDef huart, uint8_t pData, uint16_t Size, uint32_t Timeout)
这个Timeout是按照毫秒级的,就是ms
因为timeout = HAL_GetTick() + Timeout;HAL_GetTick()是毫秒级的....所以当传输一个字节的数据的时候超时时间可以设定为传输时间,当你波特率是9600 bit/s的时候,那么一秒能传输9600位数据,8位数据一个字节,这样1s就传输了1200字节,即1200byte,那么一个字节传输的时间就是1000ms/1200byte=0.84ms,于是当在波特率为9600传输一个字节的数据的时候超时时间可以设定为1ms,写作:HAL_UART_Transmit (&huart6 ,(uint8_t *)&ucByte,1,0x01);,那么当你波特率是115200的时候,传输一个字节的超时时间理论是大于0.0695ms也即最小1ms,写作:HAL_UART_Transmit (&huart6 ,(uint8_t *)&ucByte,1,0x01);,同理要是传输10个字节,传输时间乘以10,就是0x0A了,那么这个时间要根据发送的数据量进行调整,个人经验设置为最大发送字节时间的2倍即可。

你可能感兴趣的:(嵌入式开发)