STM32 BluePill USART Interrupt Arduino Framework

I’m trying to detect when the USART IDLE interrupt fires.
I don’t know how to declare the interrupt, and direct the code to an irq_handler routine.

I’m thinking there should be a statement similar to “attachInterrupt()” but instead of for GPIO pins, for the USART peripheral.

I had this function working on a project in the Arduino IDE, but when I imported that project into VSCode / PlatformIO, it didn’t work. Maybe it didn’t copy some needed librarys. Unfortunately, in the ArduinoIDE you cannot click on a function to take you to the library file to find where that function is buried in the Arduino files.

I don’t necessarily want to import and use the same library from that previous project, but would like to do it in a manner consistent with the ststm32 platform / arduino framework. Maybe there is already an interrupt handler, and I just need to modify it to check for the IDLE flag. If that is the case, then I would need a way to create a copy of this library which would only be used for this project.

I confess that I get pretty lost when it comes to understanding the structure of the librarys, etc.

Any clues/help would be appreciated. I seem to have exhausted my attempts to find an answer using internet searches.

PlatformIO should be able to perform the same as the Arduino IDE. Can you post the exact project files that worked in the Arduino IDE, plus the info which STM32 core version that used? (Tools → Boards → Board Manager → STM32)

Here are what I think are the relevant pieces of code.

This project received a long serial message from a GPS receiver, and transferred it to memory using DMA. (Don’t ask how long it took to get that working).
I use the USART IDLE interrupt to tell me when the GPS receiver was done sending the message, so that I could execute the parsing code.

The core used in the Arduino v1.8 IDE was Arduino SAM Boards (32-bits ARM Cortex-M3) version 1.6.12

#include <usart_private.h>                 //required for usart dma functions
#include "dma_private.h"                   //required for usart dma functions
#include <EEPROM.h>                        //used to store calibration parameters in EEPROM
#include <Wire.h>                          //Include the Wire.h library so we can communicate with the gyro over I2C bus
TwoWire HWire (1, I2C_FAST_MODE);          //Initiate I2C port 1 at 400kHz.
extern "C" void __irq_usart2(void);

  nvic_globalirq_enable();                                          //Required for any interrupts to function                                           
  nvic_irq_enable(NVIC_USART2);                                     //Enable interrupts from USART2.  An interrupt calls __irq_usart2() function

  if(USART2_BASE->SR & USART_SR_IDLE){                                //Just prior to enabling interrupt, reset flags in status register
    temp_var=  USART2_BASE->SR;                                       //Read the Status Register followed by Data register to reset NE (Noise Error)
    temp_var=  USART2_BASE->DR;                                       //This should be done if an error interrupt occurs. EIE
  USART2_BASE->CR1 |= USART_CR1_IDLEIE;                             //Enable the USART Line IDEL interrupt.  

//---------------------------  USART2 ISR HANDLER  -----------------------------------
extern "C" void __irq_usart2(void){
uint32_t temp_var;
// This interrupt could be trigger by several causes, so we want to check to make sure it was the IDLE
// state interrupt that caused it.
// The usart receive line goes to IDLE when the GPS is done transmitting its block of information.
// Because we're using DMA, the whole data block should now be in RAM and ready for us to parse.

    USART2_BASE->CR1 &= ~USART_CR1_IDLEIE;                           //Temporarily disable the Idle interrupt
    gps_data_available = true;
    gps_exists = true;
    gps_data_len = GPS_PACKET_SIZE - DMA1_BASE->CNDTR6;                //Bytes received = Max bytes - bytes remaining
    temp_var =  USART2_BASE->SR; //reset flags by reading the SR and DR registers
    temp_var =  USART2_BASE->DR;
    dma_disable(DMA1, DMA_CH6);             //disable the re-enable DMA channel to reset for the next burst of data
    DMA1_BASE->CNDTR6 = GPS_PACKET_SIZE;    //RESET the data count-down counter because it is not resetting itself automatically.
    dma_enable(DMA1, DMA_CH6);
    USART2_BASE->CR1 |= USART_CR1_IDLEIE;                           //Re-enable the interrupt


  //If receive or transmit data for conventional serial communication (not DMA)
    usart_irq(USART2->rb, USART2->wb, USART2_BASE);                 //This routine is in file usart_private.h

This seems like a lot of hacking, overwriting the USART IRQ handler from the one in the STM32HAL / Arduino framework for what should already implemented in the STM32HAL as HAL_UARTEx_ReceiveToIdle_DMA().

Still, if it worked in the Arduino IDE before, it should still work here. But,

cannot be right since the bluepill is an STM32 chip, not an Atmel SAM one. I was expecting a version from

I’ve gotten a new computer since I developed that project, and tried to get everything installed the way my old PC was setup, but may have messed up. But the BluePill is an Cortex M3 processor according to its reference manual (Arm® Cortex®-M3 core, refer to the STM32F10xxx Cortex®-M3). I’m pretty sure that’s what I used.

I’ve never used any direct references to HAL in my application code. Always seems to get too deep, too fast for my feeble brain. That’s why I started in the ArduinoIDE. But Arduino doesn’t really help you understand what’s going on under the hood.

Now I’m in VSCode with PlatformIO and here is my .ini file:

platform = ststm32
board = bluepill_f103c8
framework = arduino

What is that HAL_UARTEx_ReceiveToIdle_DMA() you mentioned? How would I apply that.
(FYI, I’m not using DMA in my new project so maybe there is a different reference for just detecting the IDLE interrupt. I tried typing that into my code and saw HAL_UARTEx_ReceiveToIdle_IT in the dropdown, but I don’t know what to do with it).

The Bluepill indeed has a Cortex-M3, but “Arduino SAM Boards” is, which is essentially for the Arduino Due. For the bluepill and the core above, it is “STM32 MCU based boards”.

If it’s just about parsing GPS data, you can look into Arduino-friendly libraries like or They will parse either each character interrupt based or polling based.

If the problem is just that the code worked, but maybe on a previous STM32Duino core version, you can downgrade the core version per release notes.

For example, to use v2.6.0, set

platform = ststm32@17.0.0 

etc. Of course, here it would be best to know which exact STM32Duino core version was used.

There might also be a possibility that was used instead, i.e., the Maple core? In that case, you can configure PlatformIO the same way per docs.

See stm32f1xx_hal_driver/Src/stm32f1xx_hal_uart.c at master · STMicroelectronics/stm32f1xx_hal_driver · GitHub