Timer interrupt using global variables issue

Hi,
I am trying to make a simple countdown using timer interrupt and show remaining time on 4x20 LCD display via I2C interface. The code is shown bellow. I had no issue when using LCD without I2C interface but now the program just freezes inside the interrupt. If I comment the code in void TimerInterrupt() and replace it with just Serial.print(“something”) the program does not freeze
so my guess is that the issue has something to do with global variables being used in void TimerInterrupt() but I really do not have a clue why.

Thank you for help

#include <LiquidCrystal_PCF8574.h>
#include <TimerOne.h>

#define LCD_ADDRESS 0x27

#define LCD_ROWS 4
#define LCD_COLUMNS 20
#define BACKLIGHT 255

LiquidCrystal_PCF8574 lcd(LCD_ADDRESS);

volatile int hour = 0;
volatile int minute = 1;
volatile int sec = 10;

void setup() {
    lcd.begin(LCD_COLUMNS, LCD_ROWS, LCD_ADDRESS, BACKLIGHT); 
    Serial.begin(9600);
    while(!Serial);
    Serial.println("start");
    PrintTimeRemaining();
    delay(2000);
    Timer1.initialize(1000000); // 1sec
    Timer1.attachInterrupt(TimerInterrupt);
  
}

void loop() {
  // put your main code here, to run repeatedly:

}




void TimerInterrupt(){
   if(sec == 0){
      sec = 59;
      if(minute == 0){
        minute = 59;
        if(hour != 0)
          --hour;
      }
      else
        --minute;
    }
    else
      --sec;
    PrintTimeRemaining();
}

void PrintTimeRemaining(){
  lcd.home();
  if(hour < 10)
    lcd.print("0");
  lcd.print(hour);
  lcd.print(":");
  if(minute < 10)
    lcd.print("0");
  lcd.print(minute);
  lcd.print(":");
  if(sec < 10)
    lcd.print("0");
  lcd.print(sec);
}

I think the problem is that the LCD requires interrupts to function and interrupts are disabled in an ISR.

The ISR should contain the minimum code necessary. In your case it would be sufficient to keep the time in seconds and decrement the time if it isn't zero. Then move all the display to loop().

Thank you, it worked. Although this code is only a small part of the project I am working on and I would prefer to print on LCD in the timer's ISR if possible. Can I just set higher interrupt priority for I2C?

I would prefer to print on LCD in the timer's ISR if possible.

Why ?

Eventually I moved the LCD print to loop and it works just fine. Thank you :slight_smile:

This is how I would do it (using a different LCD library):

// #include <LiquidCrystal_PCF8574.h>
#include <LiquidCrystal_I2C.h>

#include <TimerOne.h>

#define LCD_ADDRESS 0x27

#define LCD_ROWS 4
#define LCD_COLUMNS 20
#define BACKLIGHT 255

//LiquidCrystal_PCF8574 lcd(LCD_ADDRESS);
LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLUMNS, LCD_ROWS);

const unsigned TimerHours = 0;
const unsigned TimerMinutes = 1;
const unsigned TimerSeconds = 10;

volatile unsigned long SecondsRemaining = ((TimerHours * 60UL) + TimerMinutes) * 60UL + TimerSeconds;

void setup()
{
//  lcd.begin(LCD_COLUMNS, LCD_ROWS, LCD_ADDRESS, BACKLIGHT);
  lcd.init();
  Serial.begin(9600);
  while (!Serial);
  Serial.println("start");
  PrintTimeRemaining(SecondsRemaining);
  delay(2000);
  Timer1.initialize(1000000); // 1sec
  Timer1.attachInterrupt(TimerInterrupt);

}

void loop()
{
  // With interrupts disabled, make local copies of any volatile variables larger than one byte.
  unsigned long localSecondsRemaining;
  noInterrupts();
  localSecondsRemaining = SecondsRemaining;
  interrupts();
  static unsigned long previousSecondsRemaining = 0;

  if (localSecondsRemaining != previousSecondsRemaining)
  {
    PrintTimeRemaining(localSecondsRemaining);
    previousSecondsRemaining = localSecondsRemaining;
  }
}

void TimerInterrupt()
{
  if (SecondsRemaining > 0)
    SecondsRemaining--;
}

void PrintTimeRemaining(unsigned long remaining)
{
  int seconds = remaining % 60;
  int minutes = (remaining / 60) % 60;
  int hours = (remaining / 60) / 60;
  
  lcd.home();
  if (hours < 10)
    lcd.print("0");
  lcd.print(hours);
  lcd.print(":");
  if (minutes < 10)
    lcd.print("0");
  lcd.print(minutes);
  lcd.print(":");
  if (seconds < 10)
    lcd.print("0");
  lcd.print(seconds);
}