LiquidCrystal_I2C Using LCD from Interrupt Function

Hello,
i have a litle Problem with my I2C LCD Display.

When i try to use the LCD from a function called by an Interrupt the Arduino freezes.
I use the following Code to test it:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27,16,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display

 long debouncing_time = 300; //Debouncing Time in Milliseconds
 volatile unsigned long last_micros;
 boolean SettingsScreen = true;
 

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
  
  //Interrupt
  attachInterrupt(5, debounceInterrupt, CHANGE);
}

void loop() {

}


void DisplaySettings()
{
  Serial.println("Settings");
  lcd.print("Settings");
}


 void debounceInterrupt() {
   if((long)(micros() - last_micros) >= debouncing_time * 1000) {
     DisplaySettings();
     last_micros = micros();
   }
 }

I can use the LCD in normal functions like:

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27,16,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display



void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  lcd.init();                      // initialize the lcd 
  lcd.backlight();
}

void loop() {
    Test();
 
}


void Test()
{
  lcd.print("Test");
}

I'have tried the followign I2C Libary´s
http://www.exp-tech.de/service/library/2004%20LiquidCrystal_I2C.rar
http://www.exp-tech.de/service/library/LiquidCrystal_I2C2004V1.rar
Hope anybody has an Idea...

Hope anybody has an Idea...

In general you want to do as little as possible, as quickly as possible, within an Interrupt Service Routine. That rules out displaying messages on an LCD.

Don

floresta:

Hope anybody has an Idea...

In general you want to do as little as possible, as quickly as possible, within an Interrupt Service Routine. That rules out displaying messages on an LCD.

Don

Hello Don,
yes i want to display messages on an LCD from an Interrupt.
Kind Regards
Dafes

Edit:
I found a similiar Problem with an Timer Interrupt. I think i will try the "Lock" Variable thing tonight and report back :slight_smile:

yes i want to display messages on an LCD from an Interrupt.

You have to use the technique shown in the link you posted in reply #2.

You use the interrupt handler to set a 'flag' and you test the flag (and write to the LCD when required) outside of the interrupt handler.

Don

floresta:

yes i want to display messages on an LCD from an Interrupt.

You have to use the technique shown in the link you posted in reply #2.

You use the interrupt handler to set a 'flag' and you test the flag (and write to the LCD when required) outside of the interrupt handler.

Don

Hello Don,

yes thats the dirty solution. But why to use an interrupt to set an flag which i verify each run in the loop, i can also verify the state of the digital pin in the loop.
In my opinion it should be possible to write to the LCD within the interrupt and it is an advantage of an interrupt to resolve the need of verify the pin state in the "main loop".
Another question is it possible to write to an "normal connected" LCD within an Interrupt?

Dafes

If speed is of the essence then it might be worth using I2C via the New LiquidCrystal Library, as that is faster than other libraries and will mean the interrupt routine exiting faster.

yes thats the dirty solution.

No, it is the standard almost universally accepted solution.

In my opinion it should be possible to write to the LCD within the interrupt ...

It is possible to write to the LCD within the interrupt, just as it is also possible to drive 75 mph in a 55 mph speed zone.

It is just something that you should not do since it 'interrupts' everything else that the processor is trying to do, such as keeping track of time. When you interrupt the processor just long enough to set a flag it does not upset the timekeeping mechanism. When you write to the LCD outside of the interrupt routine, a process that takes an enormous amount of time in the microprocessor world, the timekeeping mechanism can still function properly.

Don

If speed is of the essence then it might be worth using I2C via the New LiquidCrystal Library, as that is faster than other libraries and will mean the interrupt routine exiting faster.

That won't help in this situation. The new library may be faster than others but it is still glacially slow in terms of just about everything else the microprocessor does.

Don

floresta:

In my opinion it should be possible to write to the LCD within the interrupt ...

It is possible to write to the LCD within the interrupt, just as it is also possible to drive 75 mph in a 55 mph speed zone.

It is just something that you should not do since it 'interrupts' everything else that the processor is trying to do, such as keeping track of time. When you interrupt the processor just long enough to set a flag it does not upset the timekeeping mechanism. When you write to the LCD outside of the interrupt routine, a process that takes an enormous amount of time in the microprocessor world, the timekeeping mechanism can still function properly.

Don

Hello Don,

ok that makes sense to me. :slight_smile: So in my opinion setting a flag is a sulotion for my problem.

dafes

The advice of doing very little or as little as possible in an ISR is
generally good advice but it is recommending a solution based on an
assumed set of circumstances and limitations.

Doing time consuming things from an Interrupt function is definitely possible
and there are situations where it makes sense and is useful.

That said there are some reasons for the "do as little as possible" advice particularly
in an AVR Arduino environment since time consuming processing simply
can't be done with the same ease as doing it from non interrupt level.

There are 2 main reasons to keep an ISR short and sweet.

  • Blocking/delaying other interrupts
  • Reentrancy issues.

Both of these issues can be solved and often are in other environments.
Often times they can even be worked around on the AVR as well, but they are impossible
to fully solve on the AVR in a generic way particularly when using Arduino.
And solving these is not something
that could be done from an Arduino sketch and would come with limitations.

The first issue is a quite obvious: Blocking other interrupts
It relates to the simplistic interrupt handling inside the AVR.
The AVR does not have multiple interrupts levels so while the handling of
simultaneous interrupt sources are prioritized,
there is only a single interrupt level. What that means is that by default, once an interrupt happens,
no other interrupt can happen until that ISR runs and leaves.
If your ISR takes longer than the time until something else wants to interrupt like say the timer interrupt,
you delay the processing of that interrupt.
With Arduino, the timer interrupt wants to run every 1ms.
Writing to the LCD is VERY slow. Even a single character can take more than 1ms
particularly on something like i2c.
A clear, or HOME takes 20+ ms.
This problem can be easily solved by simply having the ISR
re-enable interrupts in the ISR routine. Sounds easy enough.....
However, doing that causes the 2nd problem to rear it's ugly head.

The second issue is not always as obvious: Reentrancy
ISR Reentrancy has 2 basic problems.

  • RAM/stack usage.
  • code/resource re-entrancy

Reentrancy problem: RAM/Stack usage
Yes an AVR ISR can re-enable interrupts to allow other ISRs to run,
but depending on the type of interrupt, its frequency and the duration of the ISR code
that re-enables the interrupts (i.e. a long/slow ISR routine)
that may not work since the that same ISR itself might be re-entered and if it occurs enough times,
eventually causes a stack over flow.
At a minimum it can cause things to get processed out of order.

Consider a case were an interrupt occurs every 5ms. If that ISR triggers something that writes
to the LCD (which takes longer than 5ms) then the LCD processing can't complete before another interrupt
happens (which interrupts the previous LCD write operation).
In that case, "re-ordering" would occur, since the second ISR LCD update would happen before the
first completed.
If this continues to happen, you could end up with a stack overflow,
since each ISR and call to code that writes to the LCD uses up stack space, eventually the stack
is overflowed and the processor will crash.

On the other hand if you know that ISR is a slow ISR or never nests (can't occur again until the ISR leaves)
then sure you can re-enable interrupts and take as long as you want inside the ISR and it would
not have a problem with stack overflow. (it still migh have code/resource re-entrancy issues)

Reentrancy problem: code/resource reentrancy
This is the not so obvious part of reentrancy.
Most code is not written to be re-entrant. That means that the code cannot
properly handle more than a single instance running at one time.
While not truly possible on a single processor like the AVR, it logically happens
when the code is called from an ISR.
Since the ISR interrupts whatever is running it can interrupt anything.
Suppose a lcd.write() operation is in the middle of running (say from inside your loop() code)
and then an ISR interrupts it and then the ISR processing code calls lcd.write().
From a logical point of view the second lcd.write() is now running concurrently with the first lcd.write().
This has the potential to screw things up since the first lcd.write() was talking
to the LCD hardware and now some other piece of code is going talk to that same
piece of hardware, perhaps before it is ready.
This is a problem all over in the Arduino code because the core code and libraries
are simply not designed for re-entrance.
So even if your code is capable of re-entrancy, it may call an Arduino core code
function that is not.


Now if there is the need to write to the LCD an ISR as part of some "last resort" debugging
mechanism and you can closely control how it is done (i.e. like perhaps never call the LCD code anywhere
but inside the ISR) It is definitely possible and might be useful.

--- bill

At first thank you for your and detailed response :slight_smile:
This clears up some Questions.

My first Idea was to create an Interrupt to call an Settings Dialog. This were an huge time consuming operation within an Interrupt. As i´m programming a Terrarium Controller i have no "space" for time consuming Operations blocking other Operations.

So i think the solution over an Flag is good for this Problem.
But now i´m a litle bit worry about my other Idea: I wanted to use Timer Interrupts to verify against a Table if something exist to switch.
Like:

M D H Mi Command
* * 08 00 Switch Light 1 on

or should i use a waiting function in the "Main Loop" aswell?
Like:

void loop()
{
VerifyIfSomethingToDo(currentTime);
delay(1000);
}

Regard
Dafes