Cos'è ElectroYou | Login Iscriviti

ElectroYou - la comunità dei professionisti del mondo elettrico

[STM32F429] Utilizzare la UART

Raccolta di codici sorgenti

Moderatore: Foto UtentePaolino

0
voti

[1] [STM32F429] Utilizzare la UART

Messaggioda Foto UtenteDarwinNE » 24 lug 2022, 12:46

Ciao a tutti,
la situazione e l'ambiente di sviluppo sono simili alla mia richiesta viewtopic.php?f=17&t=86559

Per riassumere, sono un principiante degli STM32 e sto giocando con una STM32F429I-DISCO. Sono riuscito a fare diverse cosette ed è molto carina.

In questi ultimi giorni, mi sto battendo un po' con l'UART. In pratica, vorrei ricevere dei segnali MIDI (cosa che ho già fatto molte volte con i PIC). Riesco a configurare l'UART per ricevere tramite interrupt, solo che sembra che solo un byte venga ricevuto. Ecco un estratto del codice :

Codice: Seleziona tutto
int main(void)
{
    HAL_Init();
    SystemClock_Config();

    /* [...] */

    ConfigureGPIOPorts();
    /*##-1- Configure the UART peripheral ###################################*/
    /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
    /* UART1 configured as follow:
      - Word Length = 8 Bits
      - Stop Bit = One Stop bit
      - Parity = None
      - BaudRate = 31250 baud (as per MIDI standard)
      - Hardware flow control disabled (RTS and CTS signals) */
    UartHandle.Instance          = USARTx;
    UartHandle.Init.BaudRate     = 31250;
    UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits     = UART_STOPBITS_1;
    UartHandle.Init.Parity       = UART_PARITY_NONE;
    UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode         = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
   
    if(HAL_UART_Init(&UartHandle) != HAL_OK) {
        Error_Handler();
    }
   
    /* Other configuration code for timers and GPIO here */

    /*##-2- Put UART peripheral in reception process #####################*/ 
    if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, 1) != HAL_OK) {
        Error_Handler();
    }

    while (1) {
        /* [...] */
    }
}

/* [...]   */

/**
  * @brief  Rx Transfer completed callback
  * @param  handle: UART handle
  * @note
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *handle)
{
    if(handle->Instance !=USARTx)
        return;

    /* MIDI Event state machine here. */

    HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, 1);
}

/**
  * @brief  UART error callbacks
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report transfer error, and you can
  *         add your own implementation.
  * @retval None
  */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    /* Turn LED3 on: Transfer error in reception/transmission process */
    BSP_LED_On(LED3);
}



In pratica, la chiamata a HAL_UART_Receive_IT nell'interrupt non va a buon fine per una ragione che ignoro (il valore di ritorno non è HAL_OK, tra l'altro). Il sistema quindi dopo aver ricevuto correttamente un byte (il primo ricevuto) non riesce a ricevere i byte successivi, perché l'interrupt non è più richiamato.

La cosa divertente è che se nell'interrupt inserisco delle funzioni che lo rallentano troppo (come una scrittura sul display, che uso anche per debug), il sistema segnala errore tramite la HAL_UART_ErrorCallback, ma se recupero l'errore riesce a ricevere altri byte (perdendone comunque per strada un bel po').

Ho letto in giro che alcune versioni della HAL (io ne uso una piuttosto vecchia, del 2014) erano afflitte da un bug, ma non ho trovato una descrizione di questo bug abbastanza chiara perché possa capire da dove venga. Qui c'è qualcosa, ma non ci capisco molto: https://community.st.com/s/question/0D5 ... -something

Qui c'è il codice di tutto il progettino, abbastanza brutto perché sono tuttora in fase alquanto esploratoria: https://github.com/DarwinNE/MIDI2SwinSIDs

Il file main.c da cui ho estratto i pezzi di cui sopra è qui:

https://github.com/DarwinNE/MIDI2SwinSI ... src/main.c

Una risposta RTFM mi va benissimo, mi basta essere indirizzato sul buon manuale.
Follow me on Mastodon: @davbucci@mastodon.sdf.org
Avatar utente
Foto UtenteDarwinNE
31,0k 7 11 13
G.Master EY
G.Master EY
 
Messaggi: 4420
Iscritto il: 18 apr 2010, 9:32
Località: Grenoble - France

1
voti

[2] Re: [STM32F429] Utilizzare la UART

Messaggioda Foto Utentegvee » 24 lug 2022, 14:48

Ciao Foto UtenteDarwinNE,

Ho sempre usato le LL ed evitato le HAL quindi non mi esprimo molto riguardo al codice, pero ti sei rassicurato di azzerare il bit di IF (Interrupt Flag) ?

Ad ogni modo hai spulciato gli esempi che mette a disposizione ST?

Non so se è il tuo caso, ma se ti interessa eseguire l'interrupt ogni N bytes ricevuti e consecutivi, la soluzione migliore è usare il DMA in peripheral to memory mode.

O_/
Avatar utente
Foto Utentegvee
1.475 5 7
Sostenitore
Sostenitore
 
Messaggi: 526
Iscritto il: 11 feb 2018, 20:34

0
voti

[3] Re: [STM32F429] Utilizzare la UART

Messaggioda Foto UtenteDarwinNE » 24 lug 2022, 21:10

Grazie Foto Utentegvee!

Mi sono basato su questo esempio, ma non è molto esplicito, in pratica fa una comunicazione fra due carte in cui un messaggio di lunghezza nota viene trasmesso e poi ricevuto:

Codice: Seleziona tutto
/**
  ******************************************************************************
  * @file    UART/UART_TwoBoards_ComIT/Src/main.c
  * @author  MCD Application Team
  * @version V1.2.1
  * @date    13-March-2015
  * @brief   This sample code shows how to use STM32F4xx UART HAL API to transmit
  *          and receive a data buffer with a communication process based on
  *          IT transfer.
  *          The communication is done using 2 Boards.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/** @addtogroup STM32F4xx_HAL_Examples
  * @{
  */

/** @addtogroup UART_TwoBoards_ComIT
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define TRANSMITTER_BOARD

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* UART handler declaration */
UART_HandleTypeDef UartHandle;
__IO ITStatus UartReady = RESET;

/* Buffer used for transmission */
uint8_t aTxBuffer[] = " ****UART_TwoBoards_ComIT****  ****UART_TwoBoards_ComIT****  ****UART_TwoBoards_ComIT**** ";

/* Buffer used for reception */
uint8_t aRxBuffer[RXBUFFERSIZE];

/* Private function prototypes -----------------------------------------------*/
static void SystemClock_Config(void);
static void Error_Handler(void);
static uint16_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{

  /* STM32F4xx HAL library initialization:
       - Configure the Flash prefetch, instruction and Data caches
       - Configure the Systick to generate an interrupt each 1 msec
       - Set NVIC Group Priority to 4
       - Global MSP (MCU Support Package) initialization
     */
  HAL_Init();
 
  /* Configure LED3 and LED4 */
  BSP_LED_Init(LED3);
  BSP_LED_Init(LED4);

  /* Configure the system clock to 180 MHz */
  SystemClock_Config();
 
  /*##-1- Configure the UART peripheral ######################################*/
  /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
  /* UART1 configured as follow:
      - Word Length = 8 Bits
      - Stop Bit = One Stop bit
      - Parity = None
      - BaudRate = 9600 baud
      - Hardware flow control disabled (RTS and CTS signals) */
  UartHandle.Instance          = USARTx;
 
  UartHandle.Init.BaudRate     = 9600;
  UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;
  UartHandle.Init.StopBits     = UART_STOPBITS_1;
  UartHandle.Init.Parity       = UART_PARITY_NONE;
  UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
  UartHandle.Init.Mode         = UART_MODE_TX_RX;
  UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
   
  if(HAL_UART_Init(&UartHandle) != HAL_OK)
  {
    Error_Handler();
  }
 
#ifdef TRANSMITTER_BOARD

  /* Configure USER Button */
  BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);
  /* Wait for USER Button press before starting the Communication */
  while (BSP_PB_GetState(BUTTON_KEY) == RESET)
  {
  }
 
  /* The board sends the message and expects to receive it back */
 
  /*##-2- Start the transmission process #####################################*/ 
  /* While the UART in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE)!= HAL_OK)
  {
    Error_Handler();
  }
 
  /*##-3- Wait for the end of the transfer ###################################*/   
  while (UartReady != SET)
  {
  }
 
  /* Reset transmission flag */
  UartReady = RESET;
 
  /* Turn LED3 Off */
  BSP_LED_Off(LED3);
 
  /*##-4- Put UART peripheral in reception process ###########################*/ 
  if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
  {
    Error_Handler();
  }

#else
 
  /* The board receives the message and sends it back */

  /*##-2- Put UART peripheral in reception process ###########################*/ 
  if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
  {
    Error_Handler();
  }
 
  /*##-3- Wait for the end of the transfer ###################################*/   
  while (UartReady != SET)
  {
  }

  /* Reset transmission flag */
  UartReady = RESET;
   
  /* Turn LED3 Off */
  BSP_LED_Off(LED3);
 
  /*##-4- Start the transmission process #####################################*/ 
  /* While the UART in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if(HAL_UART_Transmit_IT(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE)!= HAL_OK)
  {
    Error_Handler();
  }
 
#endif /* TRANSMITTER_BOARD */
 
  /*##-5- Wait for the end of the transfer ###################################*/   
  while (UartReady != SET)
  {
  }
 
  /* Reset transmission flag */
  UartReady = RESET;

  /*##-6- Compare the sent and received buffers ##############################*/
  if(Buffercmp((uint8_t*)aTxBuffer,(uint8_t*)aRxBuffer,RXBUFFERSIZE))
  {
    Error_Handler();
  }
   
  /* Infinite loop */
  while (1)
  {
    /* Toggle LED3 */
    BSP_LED_Toggle(LED3);
   
    /* Wait for 40ms */
    HAL_Delay(40);
  }
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 180000000
  *            HCLK(Hz)                       = 180000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 4
  *            APB2 Prescaler                 = 2
  *            HSE Frequency(Hz)              = 8000000
  *            PLL_M                          = 8
  *            PLL_N                          = 360
  *            PLL_P                          = 2
  *            PLL_Q                          = 7
  *            VDD(V)                         = 3.3
  *            Main regulator output voltage  = Scale1 mode
  *            Flash Latency(WS)              = 5
  * @param  None
  * @retval None
  */
static void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_OscInitTypeDef RCC_OscInitStruct;

  /* Enable Power Control clock */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  /* The voltage scaling allows optimizing the power consumption when the device is
     clocked below the maximum system frequency, to update the voltage scaling value
     regarding system frequency refer to product datasheet.  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
  /* Enable HSE Oscillator and activate PLL with HSE as source */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 360;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /* Activate the Over-Drive mode */
  HAL_PWREx_EnableOverDrive();

  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
     clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; 
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; 
  if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief  Tx Transfer completed callback
  * @param  UartHandle: UART handle.
  * @note   This example shows a simple way to report end of IT Tx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  /* Set transmission flag: transfer complete*/
  UartReady = SET;

  /* Turn LED3 on: Transfer in transmission process is correct */
  BSP_LED_On(LED3);
}

/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of IT Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  /* Set transmission flag: transfer complete*/
  UartReady = SET;

  /* Turn LED3 on: Transfer in reception process is correct */
  BSP_LED_On(LED3);
}

/**
  * @brief  UART error callbacks
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report transfer error, and you can
  *         add your own implementation.
  * @retval None
  */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
{
  /* Turn LED3 on: Transfer error in reception/transmission process */
  BSP_LED_On(LED3);
}

/**
  * @brief  Compares two buffers.
  * @param  pBuffer1, pBuffer2: buffers to be compared.
  * @param  BufferLength: buffer's length
  * @retval 0  : pBuffer1 identical to pBuffer2
  *         >0 : pBuffer1 differs from pBuffer2
  */
static uint16_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
  while (BufferLength--)
  {
    if ((*pBuffer1) != *pBuffer2)
    {
      return BufferLength;
    }
    pBuffer1++;
    pBuffer2++;
  }

  return 0;
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
static void Error_Handler(void)
{
  /* Turn LED4 on */
  BSP_LED_On(LED4);
  while(1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


Nell'interrupt non viene fatto un granché, viene solo segnalato che qualcosa è successo al programma principale che può così continuare. Se ho capito bene, l'interrupt viene chiamato una sola volta. Ho pensato anch'io al bit IF, ma in questo esempio non viene toccato:

Codice: Seleziona tutto
/**
  * @brief  Rx Transfer completed callback
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report end of IT Rx transfer, and
  *         you can add your own implementation.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
  /* Set transmission flag: transfer complete*/
  UartReady = SET;

  /* Turn LED3 on: Transfer in reception process is correct */
  BSP_LED_On(LED3);
}
Follow me on Mastodon: @davbucci@mastodon.sdf.org
Avatar utente
Foto UtenteDarwinNE
31,0k 7 11 13
G.Master EY
G.Master EY
 
Messaggi: 4420
Iscritto il: 18 apr 2010, 9:32
Località: Grenoble - France

0
voti

[4] Re: [STM32F429] Utilizzare la UART

Messaggioda Foto Utentegvee » 24 lug 2022, 21:17

Mmmm ... hai provato con la libreria LL ?

Anche solo per vedere se il problema è con la libreria HAL.
Avatar utente
Foto Utentegvee
1.475 5 7
Sostenitore
Sostenitore
 
Messaggi: 526
Iscritto il: 11 feb 2018, 20:34

0
voti

[5] Re: [STM32F429] Utilizzare la UART

Messaggioda Foto UtenteDarwinNE » 24 lug 2022, 22:02

No, non ho provato. Tuttavia ci penserò sicuramente in futuro.
Follow me on Mastodon: @davbucci@mastodon.sdf.org
Avatar utente
Foto UtenteDarwinNE
31,0k 7 11 13
G.Master EY
G.Master EY
 
Messaggi: 4420
Iscritto il: 18 apr 2010, 9:32
Località: Grenoble - France

1
voti

[6] Re: [STM32F429] Utilizzare la UART

Messaggioda Foto UtenteDarwinNE » 26 lug 2022, 23:43

Allora, ho trovato il problema: HO BISOGNO DI UNA VACANZA!!!

In pratica, nella funzione HAL_UART_RxCpltCallback avevo introdotto alcuni test che facevano uscire dalla funzione SENZA richiamare HAL_UART_Receive_IT. Quindi l'interrupt non veniva più richiamato.

Il problema non veniva certo dalla libreria HAL né dalla complessità degli STM32, ma stava tra la sedia ed il computer :roll:

Codice: Seleziona tutto
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *handle)
{
    if(handle->Instance !=USARTx)
        return;

    uint8_t rec = aRxBuffer[0];
    int channel = rec & 0x0F;
    int event =   rec & 0xF0;

    if(channel!=current_channel)
       return;      //   <------ INCREDIBLY STUPID! INTERRUPT NEVER CALLED AGAIN!!!

    // MIDI receive state machine.
    /*  [...]   */

    HAL_UART_Receive_IT(&UartHandle, aRxBuffer, 1);
}
Follow me on Mastodon: @davbucci@mastodon.sdf.org
Avatar utente
Foto UtenteDarwinNE
31,0k 7 11 13
G.Master EY
G.Master EY
 
Messaggi: 4420
Iscritto il: 18 apr 2010, 9:32
Località: Grenoble - France


Torna a Firmware e programmazione

Chi c’è in linea

Visitano il forum: Nessuno e 16 ospiti