Additional material for lesson 8

LCD and Interrupt Class Activity Codes

We have LCD circuit already built from the previous examples. We also attached a potentiometer to pin A0. We would like to read the analog value from this potentiometer and display in on LCD immediately.

In the working principle of the LCD, you have to print the characters and clear the LCD afterwards. Otherwise, you will have a blended display. You can try it out by yourself only with lcd.print() and removing the lcd.clear() in your code.

There is one important point in printing/clearing the LCD. If you clear the display so quickly, then you have a flicker problem. That’s why, it is better to wait a little before clearing the screen for a better display quality. The following code is the simplest way to do it.

#include <Arduino.h>
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
const uint8_t rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() 
{
  lcd.begin(16, 2);
}

void loop() 
{
  int sensorValue = analogRead(A0);
  lcd.print(sensorValue);
  delay(100);
  lcd.clear();
}

This code works perfectly in such a simple program. However, delays are always enemies of bigger programs. delay(ms) stops all other executions, such as pin read/write, calculations, communications with the other sensors etc. Basically it halts the whole process in the specified period of time. That’s why delays are one of the biggest problems in efficiency.

Insted of using a delay(ms) function, we can use millis() function. We can set a time interval and execute the routine in everr interval of time. Here is the same code without using any delay function.

#include <Arduino.h>
#include <LiquidCrystal.h>

unsigned long prevTime = 0;

// initialize the library with the numbers of the interface pins
const uint8_t rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() 
{
  lcd.begin(16, 2);
}

void loop() 
{
  int sensorValue = analogRead(A0);
  unsigned long currTime = millis();
  if (currTime - prevTime >= 100) 
  {
    prevTime = currTime;
    lcd.clear();
    lcd.print(sensorValue);
}
}

Now, you can execute any other processes in your main function without adding an extra waiting time.

However, this is still not the efficient way. There is another process that takes a lot of time in the main process. We can do in in another way since there is a hidden “delay” or a “wait” in this process. Yes, I am talking about analogRead(A0).

ADC is a pretty slow process compated to many other processes that you execute on a microcontroller. Actually, you don’t have to wait the whole time of the conversion. You can instead set an ISR (Interrupt Service Routine) and pull the data in a specified period of time. The next code shows how it is done.

#include <Arduino.h>
#include "TimerOne.h"
#include <LiquidCrystal.h>

unsigned long prevTime = 0;
volatile int sensorValue = 0;

// initialize the library with the numbers of the interface pins
const uint8_t rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() 
{
  lcd.begin(16, 2);
  Timer1.initialize(100000); // in microseconds
  Timer1.attachInterrupt(soft_interrupt); // since it is an irs, no input or output
}

void loop() 
{
  unsigned long currTime = millis();
  if (currTime - prevTime >= 100) 
  {
    prevTime = currTime;
    lcd.clear();
    lcd.print(sensorValue);
}
}

void soft_interrupt()
{
  sensorValue = analogRead(A0); // that's the slowest process
}

As you can see, we moved the ADC process in an ISR function. We call this ISR function in every 100000 microseconds (as we specified in the setup() function.

Yet, there is one more step to increase efficiency here. We can merge the wait time for LCD to clear and the conversion time for the analogRead. If we get rid of the waiting setup with the millis() function and move this process intp the ISR, we can achive this objective. Here is the code for that.

#include <Arduino.h>
#include "TimerOne.h"
#include <LiquidCrystal.h>

unsigned long prevTime = 0;
volatile int sensorValue = 0;

// initialize the library with the numbers of the interface pins
const uint8_t rs = 2, en = 3, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() 
{
  lcd.begin(16, 2);
  Timer1.initialize(100000); // in microseconds
  Timer1.attachInterrupt(soft_interrupt); // since it is an irs, no input or output
}

void loop() 
{
  delay(1000000); // this is just for showing any other long processes you need to execute.
}

void soft_interrupt()
{
  sensorValue = analogRead(A0); // that's the slowest process
  lcd.clear();
  lcd.print(sensorValue);
}

Here you see a long long delay. This is only to demonstrate other processes that you may do inside your main function. It wouldn’t be nice to leave the main function completely empty but it doesn’t have any purpose at all. Also, see that the delay(ms) function is only be cut off with an interrupt routine.