Skip to main content
Graduate
May 19, 2025
Solved

HAL_UART_Transmit() works; HAL_UART_Transmit_IT() does nothing and breaks printf

  • May 19, 2025
  • 7 replies
  • 2222 views

I've got the NUCLEO-G491RE board and have it connected to my PC over the only USB port which is of course also used for the debugger.

Both printf() and HAL_UART_Transmit() work to send data to a terminal program on the PC, but I can't use polling for my communication (there's too much else going on).  If I call HAL_UART_Transmit_IT() it returns HAL_OK but doesn't actually transmit anything, and any subsequent calls to printf() or HAL_UART_Transmit() no longer do anything.

I'm assuming this is because this isn't a "real" UART but some kind of virtual UART that gets converted into USB commands that cooperate with the debugger.

Is there a way to use interrupt-driven communication over this (virtual) serial port?

Thanks,

Chris

 

    This topic has been closed for replies.
    Best answer by cbcooper

    I think I found the solution, I'll write it up here for the next poor person trying to figure it out:

    1) Look at "Virtual Com Port" in the "BSP" section of the IOC and write down the parameters (especially baud rate)

    2) Disable "Virtual Com Port"

    3) Under "Connectivity" select LPUART1 and under Parameter Settings set Mode to Asynchronous and the other parameters as needed (e.g. baud rate)

    4) Change the output pins of the LPUART1 from the default (PC0/PC1) to the alternate (PA2/PA3).  Notice that the LPUART1 section has a "GPIO Settings" that shows you the current pin selection but doesn't let you change it.  You have to look in the "Pinout view" and click on the desired pins (PA2 and then PA3) and it will give you a box to select the alternate use

    5) Enable the "LPUART1 global interrupt" in the NVIC Settings of the LPUART1

     

    7 replies

    Super User
    May 19, 2025

    If HAL_UART_Transmit_IT is not working, probably an issue with interrupts or incorrect code. Here is an example that uses it correctly:

    STM32CubeG4/Projects/NUCLEO-G431RB/Examples/UART/UART_HyperTerminal_IT/readme.txt at 0dc930516bf5522ff1d2b3c85622cc30de986aad · STMicroelectronics/STM32CubeG4

     

    It's a real UART, sending out and receiving UART data on TX and RX pins, it's not a USB-based anything. The ST-link chip translates between UART and USB.

    Graduate II
    May 19, 2025

    HAL_UART_Transmit_IT() returns immediately, the data transfer occurs later and in the background.

    You need the UARTx_IRQHandler() handing the transfer off to the HAL layer.

    You can't have multiple transfers in-flight, and HAL_UART_Transmit() likely knows the state caused by the pending interrupt transfer, in-progress.

    If you want buffering and queuing, you need to implement that.

    >>Is there a way to use interrupt-driven communication over this (virtual) serial port?

    Yes, if you want an unblocked approach you'll need to feed your data into a larger ring-buffer, and start a HAL_UART_Transmit_IT() on a portion of it, updating that, and subsequent transmissions via the call-back.

    cbcooperAuthor
    Graduate
    May 19, 2025

    I've got code like this:

    sprintf(tx_buffer, "Button pressed\r\n");
    HAL_UART_Transmit(&hcom_uart [0], (const uint8_t*)tx_buffer, strlen(tx_buffer), COM_POLL_TIMEOUT);

    that works, and when I inspect hcom_uart[0].Instance with the debugger it's set to 0x40008000 which is LPUART1 and not one of the USARTs (right?)

    When I look at LPUART1 in the IOC file, it's disabled and grayed out so I can't make any changes to it.  It does have a tooltip that doesn't like to appear, but when it does it says "Status: Not available" and "IP under HMI BSP driver control".   Because this is happening, I can't view any of the details for the LPUART1 (like whether interrupts are enabled).

    The example code you linked is for a USART, not a UART, and definitely not for a UART that is under HMI BSP driver control, so I'm not sure how to take that code and apply it to my situation.

    Graduate II
    May 19, 2025

    If you can get polling to work but not interrupt, then as @TDK mentions, you have issues with your code. 

    The VCP is no different than using a external USB<>Serial adapter. They both do the same thing, except with the Nucleo, you can debug and have COM port using one USB cable.

    Post your code so it can be dissected

    cbcooperAuthor
    Graduate
    May 19, 2025

    Here is all the user code I have:

    #define TX_BUF_SIZE 1000
    uint8_t tx_buffer[TX_BUF_SIZE];
    
    /* USER CODE END 0 */
    
    /**
     * @brief The application entry point.
     * @retval int
     */
    int main(void)
    {
     /* USER CODE BEGIN 1 */
    	int numPresses = 0;
     /* USER CODE END 1 */
    
     /* MCU Configuration--------------------------------------------------------*/
    
     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
     HAL_Init();
    
     /* USER CODE BEGIN Init */
    
     /* USER CODE END Init */
    
     /* Configure the system clock */
     SystemClock_Config();
    
     /* USER CODE BEGIN SysInit */
     /* USER CODE END SysInit */
    
     /* Initialize all configured peripherals */
     MX_GPIO_Init();
     MX_USB_Device_Init();
     /* USER CODE BEGIN 2 */
    
     /* USER CODE END 2 */
    
     /* Initialize led */
     BSP_LED_Init(LED_GREEN);
    
     /* Initialize USER push-button, will be used to trigger an interrupt each time it's pressed.*/
     BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
    
     /* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
     BspCOMInit.BaudRate = 115200;
     BspCOMInit.WordLength = COM_WORDLENGTH_8B;
     BspCOMInit.StopBits = COM_STOPBITS_1;
     BspCOMInit.Parity = COM_PARITY_NONE;
     BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
     if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
     {
     Error_Handler();
     }
    
     /* USER CODE BEGIN BSP */
    
     /* -- Sample board code to send message over COM1 port ---- */
     printf("Welcome to STM32 world !\r\n");
     // Transmit();
    
     /* -- Sample board code to switch on led ---- */
     BSP_LED_On(LED_GREEN);
    
     /* USER CODE END BSP */
    
     /* Infinite loop */
     /* USER CODE BEGIN WHILE */
     while (1)
     {
    
     /* -- Sample board code for User push-button in interrupt mode ---- */
     if (BspButtonState == BUTTON_PRESSED)
     {
     	numPresses++;
     /* Update button state */
     BspButtonState = BUTTON_RELEASED;
     /* -- Sample board code to toggle led ---- */
     BSP_LED_Toggle(LED_GREEN);
    
     if (numPresses == 3)
     {
     sprintf(tx_buffer, "Button pressed, numPresses=%d\r\n", numPresses);
     HAL_UART_Transmit_IT(&hcom_uart[0], (const uint8_t*)tx_buffer, strlen(tx_buffer));
     }
     else
     {
     sprintf(tx_buffer, "Button pressed, numPresses=%d\r\n", numPresses);
     HAL_UART_Transmit(&hcom_uart[0], (const uint8_t*)tx_buffer, strlen(tx_buffer), 1000);
     }
     }
     }
     /* USER CODE END WHILE */
    
    }

    The first two times I press the button it calls HAL_UART_Transmit() and I see those messages.  The third time it calls HAL_UART_Transmit_IT() and I see nothing.  The fourth time also calls HAL_UART_Transmit() but I never see those messages either.

    In my IOC I have "Virtual Com Port" checked under Bsp which means LPUART1 under Connectivity is disabled.

     

     

    cbcooperAuthor
    Graduate
    May 19, 2025

    BTW, the automatically-generated code:

     /* Initialize COM1 port (115200, 8 bits (7-bit data + 1 stop bit), no parity */
     BspCOMInit.BaudRate = 115200;
     BspCOMInit.WordLength = COM_WORDLENGTH_8B;
     BspCOMInit.StopBits = COM_STOPBITS_1;
     BspCOMInit.Parity = COM_PARITY_NONE;
     BspCOMInit.HwFlowCtl = COM_HWCONTROL_NONE;
     if (BSP_COM_Init(COM1, &BspCOMInit) != BSP_ERROR_NONE)
     {
     Error_Handler();
     }

    has an incorrect comment, right?  An 8-bit word length means 8-bit data + 1 stop bit for a total of 9 bits, yes?

    cbcooperAuthor
    Graduate
    May 19, 2025

    And if I turn off the Virtual Com Port and instead enable the LPUART1 directly, I never get anything showing up in my terminal:

    int main(void)
    {
    
     /* USER CODE BEGIN 1 */
    	int numPresses = 0;
     /* USER CODE END 1 */
    
     /* MCU Configuration--------------------------------------------------------*/
    
     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
     HAL_Init();
    
     /* USER CODE BEGIN Init */
    
     /* USER CODE END Init */
    
     /* Configure the system clock */
     SystemClock_Config();
    
     /* USER CODE BEGIN SysInit */
     /* USER CODE END SysInit */
    
     /* Initialize all configured peripherals */
     MX_GPIO_Init();
     MX_USB_Device_Init();
     MX_LPUART1_UART_Init();
     /* USER CODE BEGIN 2 */
    
     /* USER CODE END 2 */
    
     /* Initialize led */
     BSP_LED_Init(LED_GREEN);
    
     /* Initialize USER push-button, will be used to trigger an interrupt each time it's pressed.*/
     BSP_PB_Init(BUTTON_USER, BUTTON_MODE_EXTI);
    
     /* USER CODE BEGIN BSP */
    
     /* -- Sample board code to send message over COM1 port ---- */
     // printf("Welcome to STM32 world !\r\n");
     // Transmit();
    
     /* -- Sample board code to switch on led ---- */
     BSP_LED_On(LED_GREEN);
    
     /* USER CODE END BSP */
    
     /* Infinite loop */
     /* USER CODE BEGIN WHILE */
     while (1)
     {
    
     /* -- Sample board code for User push-button in interrupt mode ---- */
     if (BspButtonState == BUTTON_PRESSED)
     {
     	numPresses++;
     /* Update button state */
     BspButtonState = BUTTON_RELEASED;
     /* -- Sample board code to toggle led ---- */
     BSP_LED_Toggle(LED_GREEN);
    
     if (numPresses == 3)
     {
     sprintf(tx_buffer, "Button pressed, numPresses=%d\r\n", numPresses);
     HAL_UART_Transmit_IT(&hlpuart1, (const uint8_t*)tx_buffer, strlen(tx_buffer));
     }
     else
     {
     sprintf(tx_buffer, "Button pressed, numPresses=%d\r\n", numPresses);
     HAL_UART_Transmit(&hlpuart1, (const uint8_t*)tx_buffer, strlen(tx_buffer), 1000);
     }
     }
     }
     /* USER CODE END WHILE */
    
     /* USER CODE BEGIN 3 */
    
     /* USER CODE END 3 */
    }
    
    static void MX_LPUART1_UART_Init(void)
    {
    
     /* USER CODE BEGIN LPUART1_Init 0 */
    
     /* USER CODE END LPUART1_Init 0 */
    
     /* USER CODE BEGIN LPUART1_Init 1 */
    
     /* USER CODE END LPUART1_Init 1 */
     hlpuart1.Instance = LPUART1;
     hlpuart1.Init.BaudRate = 115200;
     hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
     hlpuart1.Init.StopBits = UART_STOPBITS_1;
     hlpuart1.Init.Parity = UART_PARITY_NONE;
     hlpuart1.Init.Mode = UART_MODE_TX_RX;
     hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
     hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
     hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
     hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
     if (HAL_UART_Init(&hlpuart1) != HAL_OK)
     {
     Error_Handler();
     }
     if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
     {
     Error_Handler();
     }
     if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
     {
     Error_Handler();
     }
     if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK)
     {
     Error_Handler();
     }
     /* USER CODE BEGIN LPUART1_Init 2 */
    
     /* USER CODE END LPUART1_Init 2 */
    
    }

     

     

    cbcooperAuthor
    Graduate
    May 19, 2025

    Interesting - when configured as a virtual com port, LPUART1 talks on PA2 and PA3 but when configured as a regular LPUART1 it talks on PC0 and PC1.

    Graduate II
    May 19, 2025

    After seeing your code and I was about to reply, but you ended up beating me to it. But yeah, I never use any of ST's default BSP configuration on the Nucleo.