Read serial on timer interrupt?

Hi guys!

Task: reading Serial1 (GPS data) in realtime and also doing other stuff like updating LCD while CPU is idle.
Problem: LCD updates takes too much time so data from Serial1 readings too slow…

I tried using Timer interrupts, but I getting trash data from Serial1 if I read Serial1 inside interrupt function :frowning:

ISR( TIMER1_OVF_vect )
{
     while (Serial1.available()) {
          GPS_Read( Serial1.read() );
     }
     
}

int main() {
     TIMSK1 |= (1<<TOIE1);
     sei();
     while() {
         LCD_update();
         /*...*/
     }
}

It is a very bad idea to wait within an interrupt, as other interrupts are blocked. The serial port has its own interrupts.

You are spending way too much time updating the LCD. Post ALL your code, with code tags, and perhaps someone can help.

jremington:
It is a very bad idea to wait within an interrupt, as other interrupts are blocked. The serial port has its own interrupts.

You are spending way too much time updating the LCD. Post ALL your code, with code tags, and perhaps someone can help.

Actually source is pretty big, here is issues part

NMEAGPS  __GPS__;
gps_fix  __GPSDATA__;

U8GLIB_NHD_C12864 LCD(15,16,10,9,8);

void GPS_Init(void) {
    Serial.begin(115200);
    Serial1.begin(115200);
}

const uint16_t GPS_Speed(void) {
    //Getting current speed
    return __GPSDATA__.speed_kph();
}

volatile void GPS_Update(void) { 
    //Reading GPS data
    while (__GPS__.available( Serial1 )) {
        __GPSDATA__ = __GPS__.read();
    }

}
void UI_buttons_hander(void) {
    /*
    handlers for check button states
    */
}
void UI_Update() {
    LCD.firstPage();
    do {
        GPS_Update();
        /*
        Lots of stuff to render on LCD
        */
        
        LCD.print( GPS_Speed() );


    } while( LCD.nextPage() );

    UI_buttons_hander();
}

int main() {

    init();
    USBDevice.attach();
    Wire.begin();
 
         UI_Init();
    Sensors_Init();
        GPS_Init();
    
    while(true) {
        GPS_Update();
        UI_Update();
        GPS_Update();
        Sensors_Update();
    }
    
    return 0;
}

the most important thing getting data from Serial1 when it's available.

for not LCD stuff takes too much time to run so I getting outdated data from serial1 or ever lose data :frowning:

One way to speed up the UI update is by not updating every page in one call to UI_Update.

Everytime you call UI_Update, update one page. The more pages you have, the more you gain.

Next you might be able to split your 'do lots of stuff' into smaller chunks and every call to UI_Update only does one of those smaller chunks.

I have no expetience with the library that you use nor with fancy lcd screens. And as you don't show all your code I can't say if it is going to solve your problems.

the most important thing getting data from Serial1 when it's available.

Then use the UART for the GPS, take over the serial interrupt and feed the output to the GPS interpreter.

jremington:
Then use the UART for the GPS, take over the serial interrupt and feed the output to the GPS interpreter.

That is really completely unnecessary. Serial data arrives slowly, and is buffered. The key is to make the LCD update process take less time.

        /*
        Lots of stuff to render on LCD
        */

It does NOT have to be rendered all at once.

And it can be rendered by a state machine perhaps? Then its not blocking.

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data.

As others have said, divide the LCD process into small pieces and only do one piece in each iteration of loop(). Also, don't update the LCD more often than is needed by the human eye and brain.

...R

PaulS:
That is really completely unnecessary. Serial data arrives slowly, and is buffered. The key is to make the LCD update process take less time.

        /*

Lots of stuff to render on LCD
        */



It does NOT have to be rendered all at once.

Guess I need replace display lib, I'm using u8glib, this lib redrawing all screen each page loop, even if I need update couple pixels

this lib redrawing all screen each page loop, even if I need update couple pixels

No, it doesn't. It is you that chose to do that. Even if the class tells you to draw the entire page, you can choose to only draw a portion of the page.

PaulS:
No, it doesn't. It is you that chose to do that. Even if the class tells you to draw the entire page, you can choose to only draw a portion of the page.

But how? Just tried to draw 2 squares one by one, after second square is drawing the first one is removing, how can I keep image while page loop?

void Screen_static_draw(void) {
    LCD.firstPage();
        do {    
            LCD.setColorIndex(1);
            LCD.drawBox(0, 0, 10, 10);
        } while( LCD.nextPage() );
}
void Screen_update_draw(void) {
   LCD.firstPage();
    do {
        LCD.setColorIndex(1);
        LCD.drawBox(0, 20, 10, 10);
    } while( LCD.nextPage() );
}
void Screen_draw(void) {
    Screen_static_draw();
    Screen_update_draw();
}

how can I keep image while page loop?

Step 1 would be to post all of your code.

PaulS:
Step 1 would be to post all of your code.

well, then we “redrawing all screen each page loop” that is blocking CPU pretty much. if I have complex graphics where I need fast updating only one number, heavy loops completely blocking code, so I cannot updating number on display in realtime if complex graphics present in draw loop.

You need to develop realistic expectations. If "heavy graphics" are important, then reading the GPS can't be that important. If reading the GPS is important, then "heavy graphics" can't be. If both are, then the Arduino is not the right platform.

It is possible to handle the GPS data during the RX character interrupt. This can be required if some part of your system absolutely has to block (e.g., SD write). I’m not sure updating the display really has to block like that, but…

I have modified the standard HardwareSerial class into a new class, NeoHWSerial. The NMEA_isr.ino example program shows how to use it.

Basically, you use NeoSerial everywhere instead of Serial (and NeoSerial1 instead of Serial1, etc.), set the ISR function in setup (to call the GPS parser), and change your GPS_Update routine:

volatile void GPS_Update(void) { 
    //Reading GPS data
    while (__GPS__.available()) {   // <-- NOTE!  no Serial1 argument!
        __GPSDATA__ = __GPS__.read();
    }

}

Fixes will be magically parsed in the background, and will become available at the update interval of your GPS device (usually 1Hz).

Coincidentally, this technique is more efficient overall, because the characters are not queued and dequeued. They are consumed immediately. It can also simplify the display update because no state machine is required (i.e., less code).

Like Robin2 said, I would still pace the screen updates to go no faster than 1 or 2 times per second. If that’s the GPS update rate, only draw it when a new fix is available. Either take ScreenUpdate out of loop and call it from GPSUpdate, or set a global flag in GPSUpdate and check it in ScreenUpdate (which clears the flag).

I see you have two screen update routines, but you need to make sure the “static” parts are drawn as infrequently as possible (only once, in setup?), and the dynamic parts are only drawn (1) when something changes, and (2) no faster than 1 or 2 times per second.

Cheers,
/dev

/dev:
It is possible to handle the GPS data during the RX character interrupt. This can be required if some part of your system absolutely has to block (e.g., SD write). I’m not sure updating the display really has to block like that, but…

I have modified the standard HardwareSerial class into a new class, NeoHWSerial. The NMEA_isr.ino example program shows how to use it.

Basically, you use NeoSerial everywhere instead of Serial (and NeoSerial1 instead of Serial1, etc.), set the ISR function in setup (to call the GPS parser), and change your GPS_Update routine:

volatile void GPS_Update(void) { 

//Reading GPS data
    while (GPS.available()) {  // ← NOTE!  no Serial1 argument!
        GPSDATA = GPS.read();
    }

}



Fixes will be magically parsed in the background, and will become available at the update interval of your GPS device (usually 1Hz).

Coincidentally, this technique is more efficient overall, because the characters are not queued and dequeued. They are consumed immediately. It can also simplify the display update because no state machine is required (i.e., less code).

Like Robin2 said, I would still pace the screen updates to go no faster than 1 or 2 times per second. If that's the GPS update rate, only draw it when a new fix is available. Either take ScreenUpdate out of `loop` and call it from GPSUpdate, or set a global flag in GPSUpdate and check it in ScreenUpdate (which clears the flag).

I see you have two screen update routines, but you need to make sure the "static" parts are drawn as infrequently as possible (only once, in setup?), and the dynamic parts are only drawn (1) when something changes, and (2) no faster than 1 or 2 times per second.

Cheers,
/dev

GPS is already parsed data from serial, gps actually is 10Hz update rate, I created timer with milsec() to do display updates every 500ms, and now I got blocked every 500ms thats also bad :smiley:

UARTS interrupt I think will work, but that’s terribly optimized display updates can be a problem in future.
I compiled ST7565 LCD library by Adafruit and seems like it works lots of faster and able to update display by parts because it stores all display data in memory and doesn’t needs to be calculated 8 times (like it do in u8g) before send to display, but lack of instruments in this lib causes difficulty, AND ofc 1KB for display data it’s alot for my at32u4, guess I’ll switch to cortex

jleed:
gps actually is 10Hz update rate,

How fast are you moving that you need to update your position 10 times per second. I suspect once every 10 seconds would be sufficient.

...R

Robin2:
How fast are you moving that you need to update your position 10 times per second. I suspect once every 10 seconds would be sufficient.

...R

Actually it will be 18Hz, main feature of device is calculating acceleration so gps/accel/gyro data has to have very high accuracy,

jleed:
main feature of device is calculating acceleration so gps/accel/gyro data has to have very high accuracy,

It would take more than that to convince me. There is a difference between accuracy and frequency.

But it's your project.

...R

jleed:
Actually it will be 18Hz, main feature of device is calculating acceleration so gps/accel/gyro data has to have very high accuracy,

Different sensors will have different requirements. "Accuracy" and "sampling frequency" are not the same thing. Just because you have acceleration and rotation sensors that need to be read at a high rate doesn't mean the GPS data needs to be processed at the same frequency. And that also doesn't mean the LCD has to be refreshed at the same time either.

Have you timed exactly how long it takes to draw an LCD frame? I want a number, not "too slow".