web-dev-qa-db-fra.com

STM32 HAL USART reçoit par interruption

J'ai du mal à recevoir des données via l'USART. Ce que je veux réellement réaliser, c'est que je puisse recevoir une commande sur USART sans longueur spécifique (seulement une longueur maximale possible). J'utilise donc la routine d'interruption pour vérifier chaque caractère reçu, mais je ne parviens toujours pas à obtenir ce que je veux. La routine est appelée à chaque fois que je reçois un nouveau caractère, mais en quelque sorte HAL_UART_Receive_IT (& huart1, rx_data, buff_size_rx) ne se met pas à jour en temps réel, alors je ne vois pas le caractère reçu lorsque je vérifie rx_data [pointeur], mais quelques temps plus tard il se trouve dans le tampon rx_data.

Ce que j'ai jusqu'à présent:

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
HansPeterLoft

HAL_UART_Receive_IT() n'est pas censé être appelé à partir d'un gestionnaire d'interruption de cette façon, mais pour initier la réception d'un nombre fixe d'octets via interruption.

Une solution de contournement possible consiste à vérifier votre tampon d'entrée une fois HAL_UART_IRQHandler() terminée, c'est-à-dire dans la section /* USER CODE BEGIN USART1_IRQn 1 */. Lorsqu'une commande est traitée, vous pouvez réinitialiser pRxBuffPtr et RxXferCount dans la structure du descripteur à leurs valeurs d'origine pour recommencer depuis le début du tampon.

Un autre horrible solution de contournement possible serait d'appeler HAL_UART_Receive_IT() avec une taille de tampon de 1, et de configurer un gestionnaire HAL_UART_RxCpltCallback() qui vérifie l'octet reçu à chaque fois, et appelle à nouveau HAL_UART_Receive_IT() quand c'est nécessaire.

Bien sûr, vous pouvez le faire sans HAL, comme PeterJ et d'autres le suggèrent (toujours).

  • Vous avez déjà implémenté la configuration des broches et des interruptions, laissez-les inchangées au début.
  • Calculez la valeur UART->BRR Selon le manuel de référence ou copiez le code correspondant à partir de hal.
  • set UART->CR1=USART_CR1_RE|USART_CR1_TE|USART_CR1_UE|USART_CR1_RXNEIE; Maintenant, vous obtenez des interruptions.
  • Dans la fonction d'interruption, lisez UART->SR Dans une variable temporaire et examinez-la.
  • Lisez UART->DR Quand il y a un octet reçu en attente, faites le traitement d'erreur sinon (plus tard).
  • Débarrassez-vous du reste des appels HAL lorsque ce qui précède fonctionne.

La réponse aux interruptions et le temps de traitement sont souvent critiques dans les applications intégrées, et la couche HAL gaspille juste beaucoup de cela.

5

La bibliothèque HAL normale n'est pas utile pour une réception continue ou des commandes de longueur différente.

Si le package HAL complet est installé, vous pouvez consulter les exemples de L ow L interface evel.

Projects\STM32F411RE-Nucleo\Examples_LL\USART\USART_Communication_Rx_IT_Continuous

L'essentiel est de vous mettre en route vers une réception continue:

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);
}

Le gestionnaire informatique USART devrait ressembler à

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();
  }
}

La dernière chose à configurer est le rappel

void USART_CharReception_Callback(void);

Où vous pouvez mettre les octets dans un tampon et les gérer dans la boucle principale ou où vous le souhaitez.

4
theSealion

Étant donné que je suis tombé sur le problème aujourd'hui et que je n'ai pas trouvé de bonne solution, j'aime en présenter une très simple, en utilisant la plupart des HAL mais en évitant les problèmes décrits ...

Voici une courte version de mon approche:

Dans la dernière section de code utilisateur (pour l'instance USART appropriée, si vous en utilisez plusieurs) de void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle), activez l'IRQ avec:

__HAL_UART_ENABLE_IT(&huartx, UART_IT_RXNE);

Ensuite, mettez le code souhaité dans votre fonction d'interruption:

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 */ 
}

N'utilisez HAL_UART_Receive_IT nulle part, cela désactivera l'IRQ et vous devrez le réactiver si vous souhaitez être appelé à chaque réception.

La version longue peut être trouvée dans mon article ici ...

1
themole

Vous pouvez le faire fonctionner en utilisant HAL! Ce n'est peut-être pas aussi élégant que d'autres implémentations, mais c'est faisable.

Vous devez créer une fonction de gestion des erreurs, puis la fonction qui appelle HAL_UART_RCV_IT doit vider le RX UART chaque fois qu'il y a une erreur de dépassement.

De plus, je travaille avec deux tampons. Pendant que l'interruption remplit un tampon, la boucle principale vide l'autre.

Voici comment cela fonctionne bien pour moi:

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;
    }
}
0
Gilson