cancel
Showing results for 
Search instead for 
Did you mean: 

Problems getting UART5 reception working with byte-level interrupts (SOLVED)

Eqqman
Associate III

This situation arose when working with the Nucleo-G0B1RE board and Cube Version: 1.18.1.

It was desired to do per-byte reception over UART channel 5 on PD2/3 with interrupts.  The VAST majority of articles discussing this process state to use the HAL_UART_RxCpltCallback() function to read the byte from the UART buffer, since this function gets called by the ISR chain on UART interrupt.  However, these same articles also frequently fail to mention the following:

1) The HAL_UART_RxCpltCallback won't get called unless it gets registered first.  This can be done with the HAL_UART_RegisterCallback() function...

2) ...BUT, even if you register functions in this manner, they won't be used unless enabled by internal configuration files. To do that, you have to pull up the IOC file and go to the Project Manager section.  Go to the "Advanced Settings" and you will see that you have to explicitly enable callbacks for each peripheral (Not known if enabling USART also triggers UART callbacks if UART is not also enabled).

3) BUT, even if you do that, the HAL_UART_RxCpltCallback still won't be used.  There are around eight places in the code that would invoke this callback, and none of them were linked to basic byte reception, they were all tied to some advanced feature.  To get this function to be triggered, it had to be stored as the RxISR function for the UART object, which didn't involve a registration function, just a simple assignment to the UART object created by Cube:

huart5.RxISR = HAL_UART_RxCpltCallback; // Note: UART5 being used

This was manually placed at the end of the UART initialization function created by Cube.

4) Even with this done, the callback wasn't getting invoked.  Why?  Because Cube didn't correctly enable all the required interrupt settings.  There's a single tickbox you can check to enable interrupts for UART5, however, this is just the "allow an interrupt on any UART sub-activity" global UART setting.  To get the RX interrupt going, this had to be added manually as well (again, also placed at the end of the Cube UART init function).

__HAL_UART_ENABLE_IT(&huart5, UART_IT_RXNE); // Receive data register not empty

There does not seem to be any configuration options available in Cube to make sure that any of these sub-interrupt enable bits are set by default in their code generation process.  Hopefully this information will be helpful to others.

 

3 REPLIES 3
TDK
Super User

Can you attach an IOC that fails to initialize things correctly? The global UART5 interrupt checkbox needs enabled.

 

HAL_UART_RxCpltCallback is called right here with default (non-user defined callbacks) right here:

stm32g0xx-hal-driver/Src/stm32g0xx_hal_uart.c at b86a4ee8d730b2c1696416048ca4b4f3f31d8c04 · STMicroelectronics/stm32g0xx-hal-driver

If you feel a post has answered your question, please click "Accept as Solution".
Eqqman
Associate III

That line you're highlighting is inside UART_RxISR_8BIT, which is one of the examples I mentioned that fall into the categories of code paths that don't normally get executed by the ISR (at least, not in the project being tested).  As stated in the OP, yes, places can be found where HAL_UART_RxCpltCallback should be used, but the code path doesn't go there.  Instead the basic HAL_UART_IRQHandler gets used.

 

Attached is an IOC.  The only interrupt options that appear to be available are for the shared USART3/4/5/6/LPUART1/EXTI28 top-level option.

static void MX_USART5_UART_Init(void)
{

  /* USER CODE BEGIN USART5_Init 0 */

  /* USER CODE END USART5_Init 0 */

  /* USER CODE BEGIN USART5_Init 1 */

  /* USER CODE END USART5_Init 1 */
  huart5.Instance = USART5;
  huart5.Init.BaudRate = 115200;
  huart5.Init.WordLength = UART_WORDLENGTH_8B;
  huart5.Init.StopBits = UART_STOPBITS_1;
  huart5.Init.Parity = UART_PARITY_NONE;
  huart5.Init.Mode = UART_MODE_TX_RX;
  huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart5.Init.OverSampling = UART_OVERSAMPLING_16;
  huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart5) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART5_Init 2 */
  huart5.RxISR = HAL_UART_RxCpltCallback; // NOT from Cube, added manually
  __HAL_UART_ENABLE_IT(&huart5, UART_IT_RXNE); // NOT from Cube added manually
  /* USER CODE END USART5_Init 2 */

}

Cube generates this code, after execution of which, no IE bits are set in the UART5 module even though the NVIC bit for UART is set.

TDK
Super User

> Cube generates this code, after execution of which, no IE bits are set in the UART5 module even though the NVIC bit for UART is set.

They won't get set until the transfer is started.

Are you starting the transfer with HAL_UART_Receive_IT?

 

RxISR gets set here:

stm32g0xx-hal-driver/Src/stm32g0xx_hal_uart.c at b86a4ee8d730b2c1696416048ca4b4f3f31d8c04 · STMicroelectronics/stm32g0xx-hal-driver

 

RXNE bit gets set here:

stm32g0xx-hal-driver/Src/stm32g0xx_hal_uart.c at b86a4ee8d730b2c1696416048ca4b4f3f31d8c04 · STMicroelectronics/stm32g0xx-hal-driver

 

These are in UART_Start_Receive_IT which gets called directly from HAL_UART_Receive_IT.

If you feel a post has answered your question, please click "Accept as Solution".