Electrical – STM32L031 Nucleo USART Problem

interruptsstm32stm32luart

I have a problem with STM32L031 Nucleo board USART communication.

I am trying to send data to PC on USART periodically and catch the spesific character ('a') with Rx interrupt and toggle the onboard LED when microcontroller receives any of it.

Here is the problems that I encountered during the test:

  • Microcontroller sent the data periodically to PC but the data was corrupted when it came to PC. The original string was "STM32L031 USART Test" but when it received from PC it was "S⸮M3⸮⸮03⸮⸮USA⸮Ԡ⸮e⸮t" .

  • When I send the 'a' character from PC to microcontroller; the interrupt handler successfully triggered and toggled the LED. After receiving the data, microcontroller stopped to send data to PC but continue receiving the character and triggering the interrupt handler successfully.

I used the ST's Low Level Drivers (LL) in my program, here is my routines.

This is a function for system clock configuration, I used HSI (16MHz – High Speed Internal Clock) as a source for PLL and used 24 MHz PLL clock as system clock.

/**
  * @brief  This function configurates the system clock as:
    *         - uses HSI as PLL source
    *         - uses PLL as system clock at 24 MHz
  * @param  none
  * @retval none
  */
void System_ClockConfiguration(void)
{
    LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE3); // regulator configuraiton in order to freq

    /* high speed internal (HSI - 16MHz) system clock avtivating
   * in order to use for PLL source clock   */
    LL_RCC_HSI_Enable(); 
    LL_RCC_HSI_DisableDivider();

    /* PLL configuration */
    LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLL_MUL_6, LL_RCC_PLL_DIV_4);
    LL_RCC_PLL_Enable();

    LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); // set system clock source as PLL

    /* peripheral clock divide value configuraiton */
    LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); 
    LL_RCC_SetAPB1Prescaler(LL_RCC_SYSCLK_DIV_1);   
    LL_RCC_SetAPB2Prescaler(LL_RCC_SYSCLK_DIV_2);   

    SystemCoreClockUpdate(); // system clock updating
}

Here is my USART2 configuration:

/**
  * @brief  This function initializes the USART2
  * @param  none
  * @retval none
  */
void USART2_Init(void)
{
    LL_USART_InitTypeDef LL_Usart_InitStruct; // USART initialization struct    
    LL_GPIO_InitTypeDef LL_GPIO_Initstruct; // GPIO initialization struct

    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); // Clock freq activated for GPIOA peripheral

    /* GPIO pin configuration */
    LL_GPIO_Initstruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Initstruct.Mode = LL_GPIO_MODE_ALTERNATE;
    LL_GPIO_Initstruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Initstruct.Pin = LL_GPIO_PIN_9; // Tx Pin
    LL_GPIO_Initstruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Initstruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;

    LL_GPIO_Init(GPIOA, &LL_GPIO_Initstruct);   // GPIO pin initialize

    LL_GPIO_Initstruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Initstruct.Mode = LL_GPIO_MODE_ALTERNATE;
    LL_GPIO_Initstruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Initstruct.Pin = LL_GPIO_PIN_10; // Rx Pin
    LL_GPIO_Initstruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Initstruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;

    LL_GPIO_Init(GPIOA, &LL_GPIO_Initstruct);   // GPIO pin initialize


    /* Important: we need to disable related USART first in order to write 
     * USART segisters properly! */
    LL_USART_Disable(USART2);
    LL_USART_DeInit(USART2);

    LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_SYSCLK); // define usart clocksource as system clock

    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2); // Clock freq activated for USART2 peripheral

    /* USART initialization struct configurations */
    LL_Usart_InitStruct.BaudRate = 9600;
    LL_Usart_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
    LL_Usart_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
    LL_Usart_InitStruct.Parity = LL_USART_PARITY_NONE;
    LL_Usart_InitStruct.StopBits = LL_USART_STOPBITS_1;
    LL_Usart_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;

    LL_USART_Init(USART2, &LL_Usart_InitStruct); // edit USART registers from related struct    

    /* NVIC Configuration */
    __NVIC_DisableIRQ(USART2_IRQn);
    __NVIC_ClearPendingIRQ(USART2_IRQn);
    __NVIC_SetPriority(USART2_IRQn, 1);
    __NVIC_SetVector(USART2_IRQn, USART2_IRQn);
    __NVIC_EnableIRQ(USART2_IRQn);  

    LL_USART_Enable(USART2); // enable USART

}

Here is my interrupt handler function:

/**
  * @brief  USART2 interrupt handler
  * @param  none
  * @retval none
  */    
void USART2_IRQHandler(void)
{

    if((USART2->ISR & USART_ISR_RXNE) == USART_ISR_RXNE)
    {
        char t = USART2->RDR;

        if(t == 'a')            
        {           
            LL_GPIO_TogglePin(GPIOB, LL_GPIO_PIN_3);
        }

    }

}

The function that I used for send the data on USART:

/**
  * @brief  This function is used for transmit a character array
    *         through USART communication
  * @param  USARTx
    * @param  txArray
  * @retval none
  */    
void USART_Puts(USART_TypeDef* USARTx, volatile char *txArray)
{

    while(*txArray)
    {
        while(!(USARTx ->ISR & 0x0040)); // wait for the USART until its available again (TC bit)
        LL_USART_TransmitData8(USART2, *txArray); // send the data
        *txArray++; // jump to next array member (next character)
    }   

}

Finally, here is my program main function:

char str[50]; // USART buffer string
int32_t i = 0; // general purpose
char t;

/**
  * @brief  Program entry point
  * @param  none
  * @retval none
  */
int main(void)
{
    System_ClockConfiguration(); // system clock configuration 

    LED3_Init(); // on board LED initialization

    USART2_Init(); // USART2 initialization


    /* main loop */
    while(1)
    {

        sprintf(str, "STM32L031 USART Test\n");
        USART_Puts(USART2, str);

        i = 150000;

        /* basic delay */
        while(i)
        {
            i--;
        }   

    }

}   

Best Answer

I found the exact reason of the problem.. It seems its all about the optimization mode of the KEIL's compiler. When I saw the optimization setting in the C/C++ section of the "options for target menu", it was set as "level 0". Then I changed the value to "default" and everything worked as expected.

Here is my working configuration function for USART2:

/**
  * @brief  This function initializes the USART2
  * @param  none
  * @retval none
  */
void USART2_Init(void)
{
    LL_USART_InitTypeDef LL_Usart_InitStruct; // USART initialization struct    
    LL_GPIO_InitTypeDef LL_GPIO_Initstruct; // GPIO initialization struct

    LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); // Clock freq activated for GPIOA peripheral

    /* GPIO pin configuration */
    LL_GPIO_Initstruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Initstruct.Mode = LL_GPIO_MODE_ALTERNATE;
    LL_GPIO_Initstruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Initstruct.Pin = LL_GPIO_PIN_9; // Tx Pin
    LL_GPIO_Initstruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Initstruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;

    LL_GPIO_Init(GPIOA, &LL_GPIO_Initstruct);   // GPIO pin initialize

    LL_GPIO_Initstruct.Alternate = LL_GPIO_AF_4;
    LL_GPIO_Initstruct.Mode = LL_GPIO_MODE_ALTERNATE;
    LL_GPIO_Initstruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
    LL_GPIO_Initstruct.Pin = LL_GPIO_PIN_10; // Rx Pin
    LL_GPIO_Initstruct.Pull = LL_GPIO_PULL_UP;
    LL_GPIO_Initstruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;

    LL_GPIO_Init(GPIOA, &LL_GPIO_Initstruct);   // GPIO pin initialize


    /* Important: we need to disable related USART first in order to write 
     * USART segisters properly! */
    LL_USART_Disable(USART2);
    LL_USART_DeInit(USART2);

    LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_SYSCLK); // define usart clocksource as system clock

    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2); // clock freq activated for USART2 peripheral

    /* USART initialization struct configurations */
    LL_Usart_InitStruct.BaudRate = 115200;
    LL_Usart_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
    LL_Usart_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
    LL_Usart_InitStruct.Parity = LL_USART_PARITY_NONE;
    LL_Usart_InitStruct.StopBits = LL_USART_STOPBITS_1;
    LL_Usart_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;

    LL_USART_Init(USART2, &LL_Usart_InitStruct); // edit USART registers from related struct    

    /* NVIC Configuration */
    __NVIC_DisableIRQ(USART2_IRQn);
    __NVIC_ClearPendingIRQ(USART2_IRQn);
    __NVIC_SetPriority(USART2_IRQn, 1);
    __NVIC_SetVector(USART2_IRQn, USART2_IRQn);
    __NVIC_EnableIRQ(USART2_IRQn);  

    LL_USART_EnableIT_RXNE(USART2); // Rx buffer not empty interrupt enable

    LL_USART_Enable(USART2); // enable USART

}