STM32 HAL USART通过中断

时间:2017-09-13 13:24:55

标签: stm32 hal

通过USART接收数据我遇到了一些麻烦。我实际想要实现的是,我可以通过USART接收没有特定长度的命令(只有最大可能的长度)。所以我使用中断例程检查收到的每个字符,但我仍然无法实现我想要的。每次收到一个新角色时都会调用该例程,但不知何故HAL_UART_Receive_IT(& huart1,rx_data,buff_size_rx)不会实时升级,然后当我检查rx_data [指针]时我看不到收到的字符,但是几次之后,它位于rx_data缓冲区中。

到目前为止我所拥有的:

int pointer =0;

...

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    if ( USART1->ISR & UART_IT_TXE) {

    }

    if ( USART1->ISR & UART_IT_RXNE) {
        HAL_UART_Receive_IT(&huart1,rx_data,buff_size_rx);
        if(rx_data[pointer]=='\0') {
              pointer=0;
              readCommand(rx_data);
              clearBuffer(rx_data,buff_size_rx);
        } else {
          pointer++;
          if(pointer>=buff_size_rx) {
              pointer=0;
          }
        }
    }
    /* USER CODE END USART1_IRQn 0 */
    HAL_UART_IRQHandler(&huart1);
    /* USER CODE BEGIN USART1_IRQn 1 */



  /* USER CODE END USART1_IRQn 1 */
}

5 个答案:

答案 0 :(得分:4)

HAL_UART_Receive_IT()并不意味着从中断处理程序调用,而是通过中断启动接收固定字节数。

可能的解决方法是在 HAL_UART_IRQHandler()完成后检查输入缓冲区,即在/* USER CODE BEGIN USART1_IRQn 1 */部分。处理命令时,您可以将句柄结构中的pRxBuffPtrRxXferCount重置为其原始值,以便再次从缓冲区的起点开始。

另一个可怕的可能的解决方法是调用缓冲区大小为1的HAL_UART_Receive_IT(),并设置一个HAL_UART_RxCpltCallback()处理程序,每次检查收到的字节,并调用必要时再次HAL_UART_Receive_IT()

当然,你可以在没有HAL的情况下做到这一点,正如PeterJ和其他人(总是)建议的那样。

  • 您已经实现了引脚和中断设置,首先保持不变。
  • 根据参考手册计算UART->BRR值,或从hal复制相关代码。
  • 设置UART->CR1=USART_CR1_RE|USART_CR1_TE|USART_CR1_UE|USART_CR1_RXNEIE;现在,您正在接收中断。
  • 在中断函数中,将UART->SR读入临时变量,然后检查它。
  • 当收到的字节等待时,读取UART->DR,否则(稍后)进行错误处理。
  • 当上述工作正常时,摆脱其余的HAL电话。

在嵌入式应用程序中,中断响应和处理时间通常是 critical ,而HAL只是浪费了很多。

答案 1 :(得分:2)

普通的HAL库对于连续接收或不同长度的命令没有用。

如果您安装了完整的HAL包,则可以查看 L ow L evel界面的示例。

Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous

最重要的是让你接受连续接收:

void Configure_USART(void) {    
    /* (1) Enable GPIO clock and configures the USART pins *********************/

    /* Enable the peripheral clock of GPIO Port */
    USARTx_GPIO_CLK_ENABLE();

    /* Configure Tx Pin as : Alternate function, High Speed, Push pull, Pull up */
    LL_GPIO_SetPinMode(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_MODE_ALTERNATE);
    USARTx_SET_TX_GPIO_AF();
    LL_GPIO_SetPinSpeed(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
    LL_GPIO_SetPinOutputType(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
    LL_GPIO_SetPinPull(USARTx_TX_GPIO_PORT, USARTx_TX_PIN, LL_GPIO_PULL_UP);

    /* Configure Rx Pin as : Alternate function, High Speed, Push pull, Pull up */
    LL_GPIO_SetPinMode(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_MODE_ALTERNATE);
    USARTx_SET_RX_GPIO_AF();
    LL_GPIO_SetPinSpeed(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_SPEED_FREQ_HIGH);
    LL_GPIO_SetPinOutputType(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_OUTPUT_PUSHPULL);
    LL_GPIO_SetPinPull(USARTx_RX_GPIO_PORT, USARTx_RX_PIN, LL_GPIO_PULL_UP);

    /* (2) NVIC Configuration for USART interrupts */
    /*  - Set priority for USARTx_IRQn */
    /*  - Enable USARTx_IRQn */
    NVIC_SetPriority(USARTx_IRQn, 0);  
    NVIC_EnableIRQ(USARTx_IRQn);

    /* (3) Enable USART peripheral clock and clock source ***********************/
    USARTx_CLK_ENABLE();

    /* (4) Configure USART functional parameters ********************************/
    /* TX/RX direction */
    LL_USART_SetTransferDirection(USARTx_INSTANCE, LL_USART_DIRECTION_TX_RX);

    /* 8 data bit, 1 start bit, 1 stop bit, no parity */
    LL_USART_ConfigCharacter(USARTx_INSTANCE, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);

    /* No Hardware Flow control */
    /* Reset value is LL_USART_HWCONTROL_NONE */
    // LL_USART_SetHWFlowCtrl(USARTx_INSTANCE, LL_USART_HWCONTROL_NONE);

    /* Oversampling by 16 */
    /* Reset value is LL_USART_OVERSAMPLING_16 */
    // LL_USART_SetOverSampling(USARTx_INSTANCE, LL_USART_OVERSAMPLING_16);

    /* Set Baudrate to 115200 using APB frequency set to 100000000/APB_Div Hz */
    /* Frequency available for USART peripheral can also be calculated through LL RCC macro */
    /* Ex :
        Periphclk = LL_RCC_GetUSARTClockFreq(Instance); or 
        LL_RCC_GetUARTClockFreq(Instance); depending on USART/UART instance

        In this example, Peripheral Clock is expected to be equal to 
        100000000/APB_Div Hz => equal to SystemCoreClock/APB_Div
    */
    LL_USART_SetBaudRate(USARTx_INSTANCE, SystemCoreClock/APB_Div, LL_USART_OVERSAMPLING_16, 115200); 

    /* (5) Enable USART *********************************************************/
    LL_USART_Enable(USARTx_INSTANCE);
}

USART IT处理程序看起来应该是

void USARTx_IRQHandler(void)
{
  /* Check RXNE flag value in SR register */
  if(LL_USART_IsActiveFlag_RXNE(USARTx_INSTANCE) && LL_USART_IsEnabledIT_RXNE(USARTx_INSTANCE))
  {
    /* RXNE flag will be cleared by reading of DR register (done in call) */
    /* Call function in charge of handling Character reception */
    USART_CharReception_Callback();
  }
  else
  {
    /* Call Error function */
    Error_Callback();
  }
}

最后要设置的是Callback

void USART_CharReception_Callback(void);

您可以将字节放入缓冲区并在主循环中或您想要的位置处理它。

答案 2 :(得分:1)

由于我今天偶然发现了这个问题,却找不到一个好的解决方案,因此我想提出一个非常简单的解决方案,它使用了大多数HAL,但避免了所描述的问题...

我的方法的简短依据是:

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)的最后一个用户代码部分(对于相应的USART实例,如果使用多个),请使用以下命令启用IRQ:

__HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);

然后将所需的代码放入中断函数:

void USART3_IRQHandler(void)  {    
    /* USER CODE BEGIN USART3_IRQn 0 */
    CallMyCodeHere();
    return;  // To avoid calling the default HAL handler at all 
             // (in case you want to save the time)
    /* USER CODE END USART3_IRQn 0 */
    HAL_UART_IRQHandler(&huart3);  // This is the CubeMX generated HAL handler
    /* USER CODE BEGIN USART3_IRQn 1 */
    /* USER CODE END USART3_IRQn 1 */ 
}

不要在任何地方使用HAL_UART_Receive_IT,它将禁用IRQ,并且如果您希望每次接收都被呼叫,则需要重新启用它。

长版本可以在我的帖子中找到...

答案 3 :(得分:0)

您可以使用HAL使其正常运行!它可能不像其他实现那么优雅,但是是可行的。

您必须创建一个错误处理程序函数,然后在发生溢出错误时,调用HAL_UART_RCV_IT的函数必须刷新UART RX。

此外,我使用两个缓冲区。当中断正在填充一个缓冲区时,主循环将另一个缓冲区清空。

这里对我来说效果很好:

typedef enum
{
  UARTREADY = 0,
    UARTBUSY  = 1,
    UARTDATA  = 2,
    UARTERROR = 3
} enumUartStatus;

while(1){
    if(UARTREADY == isUsart3RxReady()){

        Usart3RxBuffer((char *)&SOMRxBytesBuffer[use_buffer_index], RCV_BUFFER_BANK_SIZE);   // receive bytes in the raw buffer

        if(use_buffer_index == RCV_BUFFER_BANK1_INDEX){         
            use_buffer_index = RCV_BUFFER_BANK2_INDEX;
            rxb1_stats++;
        }else{  
            use_buffer_index = RCV_BUFFER_BANK1_INDEX;
            rxb2_stats++;
        }

    }
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    if(huart == NULL){
        return;
    }

    if(huart->Instance == USART3){

        if(HAL_UART_GetError(huart) == HAL_UART_ERROR_FE){
            Usart3Ready = UARTREADY;
        }else{      
            Usart3Ready = UARTERROR;
        }
    }
}

void Usart3RxBuffer(char *buffer, unsigned short rxbuffersize){
  /* Reset transmission flag */

    if(Usart3Ready != UARTREADY)
    {
        return;
    }

    if(HAL_UART_GetState(&huart3) == HAL_UART_STATE_READY){

        /*##-3- Put UART peripheral in reception process ###########################*/
        if (HAL_UART_Receive_IT(&huart3, (uint8_t *)buffer, rxbuffersize) != HAL_OK)
        {
            // TODO: Error_Handler();
            DEBUG_TRACE(DEBUG_MSK_MAIN, "UART3 error starting receiver!\r\n");
        }else{

            // An interrupt HAL_UART_ErrorCallback hit right here !!!!
            // There is an overrun error happening here so we have to retry
            // this is because we are using the Receive_IT in a continous communication and there is no handshake or flow control
            if(Usart3Ready != UARTERROR){
                /* Busy waiting to receive bytes */
                Usart3Ready = UARTBUSY;
            }
        }
    }

    if(Usart3Ready == UARTERROR){
        HAL_UART_AbortReceive_IT(&huart3);
        Usart3Ready = UARTREADY;
    }
}

答案 4 :(得分:0)

以下是通过中断接收数据和空闲线路检测的完整示例:

  1. main.c 中启用接收中断和空闲线路检测:
  /* USER CODE BEGIN USART2_Init 2 */
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);  // enable receive intterupts 
  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);  // enable idle line detection 
  /* USER CODE END USART2_Init 2 */
  1. USARTx_IRQHandler 中的 stm32f4xx_it.c 中整理空闲线路事件:
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
  if (__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE)) { 
    __HAL_UART_CLEAR_IDLEFLAG(&huart2); // taken from https://electronics.stackexchange.com/questions/471272/setting-up-stm32-timer-for-uart-idle-detection#comment1353999_480556
    uart2_idleHandler();
  } else {
    uart2_handler();
  }
  return;
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
  /* USER CODE END USART2_IRQn 1 */
}

测试:

  1. 创建以下处理程序:
char my_uart_buffer[256];
int my_uart_buffer_index = 0;

void uart2_handler(void){
  char buff; 
  HAL_UART_Receive (&huart2, (uint8_t *)&buff, 1, 400);
  my_uart_buffer[my_uart_buffer_index++] = buff;
}

void uart2_idleHandler(){
  my_uart_buffer_index = 0; 
}
  1. 在您的 PC 上打开一个串口客户端,设置为 115200 波特,8N1。
  2. 将断点设置为 uart2_idleHandler()
  3. 发送“Hello world”。
  4. 当断点命中时,您可以通过 p/c *my_uart_buffer@20 检查您的缓冲区。

示例项目

Here 是在 STM32F407 Discovery 板上运行的完整示例。