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倍即可。

### STM32串口调试卡的原因及解决方案 #### 一、可能的卡原因 1. **中断优先级设置不当** 如果多个外设共享同一个中断源,或者某些高优先级中断抢占了低优先级中断的时间片,则可能导致串口中断无法及时响应。这可能是导致 `USART1_IRQHandler` 函数中程序卡的主要原因之一[^1]。 2. **缓冲区溢出或未清标志位** 当接收数据的速度超过处理速度时,可能会发生缓冲区溢出。如果在中断服务函数中未能正确清除状态寄存器中的标志位(如 RXNE 或 TXE),则会引发重复触发中断的情况,最终导致系统卡[^3]。 3. **硬件连接问题** 不良的硬件设计也可能引起通信异常。例如,信号线过长、抗干扰能力不足或电源不稳定都可能导致数据传输错误并进一步影响软件逻辑运行[^2]。 4. **printf函数阻塞** 使用标准库中的`printf`进行调试输出时,如果没有配置好重定向到指定串口的功能,或者是该操作本身耗时较长而没有考虑到实时性需求的话,也容易造成整个系统的停滞现象[^1]。 #### 二、具体解决办法 针对以上提到的各种可能性,可以采取如下措施来预防和修复STM32串口卡问题: 1. 调整NVIC中断优先级配置,确保各个模块之间不会因为争夺资源而导致互相干扰; 2. 在每次进入ISR之前先保存当前上下文环境,在退出前恢复原状,并且务必记得清理所有相关的中断请求标记以及更新全局变量的状态信息; ```c void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){ uint8_t data = USART_ReceiveData(USART1); /* Process received byte */ // Clear interrupt flag after processing complete. USART_ClearITPendingBit(USART1, USART_IT_RXNE); } } ``` 3. 对于长时间占用CPU周期的任务考虑将其拆分成更小的部分逐步完成而不是一次性做完全部工作量从而减少单次调用时间长度进而降低被其他更高优先生效的风险; 4. 配合环形队列实现高效的数据缓存机制以便能够快速接纳来自外部设备传入的信息流而不至于丢失任何重要片段同时也方便后续应用程序层面对这些原始资料加以利用; 5. 定期检查物理连线状况排除潜在隐患比如更换质量更好的接插件缩短走线距离加装滤波电路等等手段提升整体可靠性水平。 通过上述方法应该可以在很大程度上缓解乃至彻底消除由于各种因素共同作用所造成的串口通讯失败情形的发生几率让产品更加稳定可靠满足实际应用场合下的严格要求。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值