The program transmit a message using interrupts, this time the function HAL_UART_Transmit_IT is called, and when the function returns the message is not send yet, it only send the first data, enable interrupts and backup the data using an internal pointer that later will be used by the HAL_UART_IRQHandler function in the interrupt vector. It is the actual ISR the one that sends the entire string and is necessary to indicate to application the message has been entirely transmitted

#include "bsp.h"

/*string to transmit*/
uint8_t TxBuffer[] = "Welcome everyone to Modular MX website\r\n";
__IO ITStatus UartReady = SET; /*flag to indicate transmitter is available*/
UART_HandleTypeDef UartHandle; /*usrt handler*/

int main( void )
{
    HAL_Init(); /*init cube library*/
    
    /*uart configuration options for module USART2, 9600 baudrate,
    8bits, 1 stop bit, no parity, no flow control, and 8 bit lenght */
    UartHandle.Instance         = USART2;
    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;
    /*init uart2 with previous paramters*/
    HAL_UART_Init( &UartHandle );

    while(1)
    {
        /*Before call the send transmit function we need to verify if uart module is not busy
        with some other transmition*/
        if( UartReady == SET )
        { 
            /*send the message usin interrupts*/
            HAL_UART_Transmit_IT( &UartHandle, TxBuffer, sizeof( TxBuffer ) );
            UartReady = RESET; /*put the flag indicating uart is busy*/
        }
    }
}

/*When the entire message had been transmitted by the ISR this fucntion will be called
by the HAL_UART_IRQHandler which in turn is called by USART2_LPUART2_IRQHandler
interrupt vector*/
void HAL_UART_TxCpltCallback( UART_HandleTypeDef *huart )
{ 
    /*set the flag to indicate the uart is ready*/
    UartReady = SET;
}

msps.c

/*The function is called from HAL_UART_Init at the very beginning
and allows us to set the pins to be used as serial port*/
void HAL_UART_MspInit( UART_HandleTypeDef *huart )
{
    GPIO_InitTypeDef GPIO_InitStruct; /*gpios init structure*/
    
    __HAL_RCC_USART2_CLK_ENABLE();    /*enable usart2 clock*/
    __HAL_RCC_GPIOA_CLK_ENABLE();     /*eneable port A clock */
    
    /*set pin 2(tx) and pin 3(rx) from port A in altern mode usart2
    the altern mode info can be found in device datasheet*/
    GPIO_InitStruct.Pin   = GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode  = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF1_USART2;
    /*apply configuration configuracion*/
    HAL_GPIO_Init( GPIOA, &GPIO_InitStruct );
}

ints.c

extern UART_HandleTypeDef UartHandle;

/*Declare interrupt service rutine as it is declare in startup_stm32g0b1xx.s file,
the program will jump here every time a byte has been transmitted*/
void USART2_LPUART2_IRQHandler( void )/*vector de interrupcion usart2*/
{
    /*HAL library fucntion that attend interrupts on UART module*/
    HAL_UART_IRQHandler( &UartHandle );
}