Tutorial.
New people often ask Why is my sketch unresponsive ?
The code example below creates a 63ns pulse on pin D12 every time the loop( ) function starts.
The sketch also toggles the state of pin D13 every 500 milliseconds.
The accompanying oscilloscope trace depicts what is happening.
#include <Wire.h>
#include <hd44780.h> //main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> //I2C expander i/o class header
hd44780_I2Cexp lcd(0x27);
//macro to generate a 63ns pulse on UNO PINB4 i.e. D12
#define PULSE62_12 cli(); PINB = bit(PINB4); PINB = bit(PINB4); sei()
// G P I O & V a r i a b l e s
//********************************************^************************************************
//
const byte pulseOutput = 12;
const byte heartbeatLED = 13;
unsigned int counter;
//****************************************
//timing stuff
unsigned long heartbeatTime;
unsigned long LCDupdateTime;
//********************************************^************************************************
void setup()
{
Serial.begin(115200);
pinMode(pulseOutput, OUTPUT);
pinMode(heartbeatLED, OUTPUT);
//****************************************
//LCD stuff
lcd.begin(20, 4);
lcd.clear();
} //END of setup()
//********************************************^************************************************
void loop()
{
//for oscilloscope measuring, make a 63ns pulse on D12
PULSE62_12;
//**************************************** h e a r t b e a t T I M E R
//is it time to toggle the heartbeatLED ?
if (millis() - heartbeatTime >= 500ul)
{
//restart this TIMER
heartbeatTime = millis();
//toggle the heartbeatLED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//**************************************** L C D u p d a t e T I M E R
//is it time to update the LCD ?
// if (millis() - LCDupdateTime >= 100ul)
// {
// //restart this TIMER
// LCDupdateTime = millis();
//
// lcd.clear();
//
// //update static data
// lcd.setCursor(0, 0);
// // 1111111111
// // 01234567890123456789
// // Skeleton Sketch
// lcd.print(" Skeleton Sketch ");
//
// lcd.setCursor(0, 1);
// // 1111111111
// // 01234567890123456789
// // Counter = xxxxxx
// lcd.print(" Counter = ");
//
// //update dynamic data
// lcd.setCursor(13, 1);
// // 1111111111
// // 01234567890123456789
// // Counter = xxxxxx
// lcd.print(counter++);
// }
} //END of loop()
//********************************************^************************************************
Image A shows the 63ns pulses viewed at a slow rate; we cannot see the individual pulses.
Image B magnifies the pulses, and we now see the time between two 63ns pulses is just under 2.5us; that is loop( ) executes every 2.5us.
Image C is further magnified, and we can now see the pulse width is ~62.5ns.
We want loop( ) speed to be as fast as possible, so our sketch acts responsively.
New users are often unaware of a problem called “blocking code“.
First time users often fall into a trap by writing their code in a blocking fashion.
Blocking occurs when a portion of your sketch takes up all the controller’s time.
One of the biggest culprits of blocking is when we use delay(x);. During time interval (x), no other code can execute, i.e., during this x millisecond period.
The code example below creates the same 63ns pulses on pin D12.
We have added the new line of code, delay(1); at the top of loop( ).
The accompanying oscilloscope trace depicts what is happening.
#include <Wire.h>
#include <hd44780.h> //main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> //I2C expander i/o class header
hd44780_I2Cexp lcd(0x27);
//macro to generate a 63ns pulse on UNO PINB4 i.e. D12
#define PULSE62_12 cli(); PINB = bit(PINB4); PINB = bit(PINB4); sei()
// G P I O & V a r i a b l e s
//********************************************^************************************************
//
const byte pulseOutput = 12;
const byte heartbeatLED = 13;
unsigned int counter;
//****************************************
//timing stuff
unsigned long heartbeatTime;
unsigned long LCDupdateTime;
//********************************************^************************************************
void setup()
{
Serial.begin(115200);
pinMode(pulseOutput, OUTPUT);
pinMode(heartbeatLED, OUTPUT);
//****************************************
//LCD stuff
lcd.begin(20, 4);
lcd.clear();
} //END of setup()
//********************************************^************************************************
void loop()
{
delay(1);
//for oscilloscope measuring, make a 63ns pulse on D12
PULSE62_12;
//**************************************** h e a r t b e a t T I M E R
//is it time to toggle the heartbeatLED ?
if (millis() - heartbeatTime >= 500ul)
{
//restart this TIMER
heartbeatTime = millis();
//toggle the heartbeatLED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//**************************************** L C D u p d a t e T I M E R
//is it time to update the LCD ?
// if (millis() - LCDupdateTime >= 100ul)
// {
// //restart this TIMER
// LCDupdateTime = millis();
//
// lcd.clear();
//
// //update static data
// lcd.setCursor(0, 0);
// // 1111111111
// // 01234567890123456789
// // Skeleton Sketch
// lcd.print(" Skeleton Sketch ");
//
// lcd.setCursor(0, 1);
// // 1111111111
// // 01234567890123456789
// // Counter = xxxxxx
// lcd.print(" Counter = ");
//
// //update dynamic data
// lcd.setCursor(13, 1);
// // 1111111111
// // 01234567890123456789
// // Counter = xxxxxx
// lcd.print(counter++);
// }
} //END of loop()
//********************************************^************************************************
Image D shows a zoomed-out trace of the 63ns pulses.
Image E shows a magnified image of these pulses which are now separated by 1 millisecond.
delay(1); has slowed the loop( ) speed down from 2.5us to 1ms.
We can imagine what will happen if we change delay(1); to delay(1000); the loop( ) speed would be 1 second.
There are other examples that block program execution, but we do not often recognize their contribution to blocking and they can routinely or intermittently make our program stutter.
As before, the code example below creates a 63ns pulse on pin D12 every time the loop( ) function starts.
The sketch still toggles the state of pin D13 every 500 milliseconds.
We have removed the delay(1); line of code and have added code to update an LCD display every 100ms.
The accompanying oscilloscope trace depicts what is happening.
#include <Wire.h>
#include <hd44780.h> //main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> //I2C expander i/o class header
hd44780_I2Cexp lcd(0x27);
//macro to generate a 63ns pulse on UNO PINB4 i.e. D12
#define PULSE62_12 cli(); PINB = bit(PINB4); PINB = bit(PINB4); sei()
// G P I O & V a r i a b l e s
//********************************************^************************************************
//
const byte pulseOutput = 12;
const byte heartbeatLED = 13;
unsigned int counter;
//****************************************
//timing stuff
unsigned long heartbeatTime;
unsigned long LCDupdateTime;
//********************************************^************************************************
void setup()
{
Serial.begin(115200);
pinMode(pulseOutput, OUTPUT);
pinMode(heartbeatLED, OUTPUT);
//****************************************
//LCD stuff
lcd.begin(20, 4);
lcd.clear();
} //END of setup()
//********************************************^************************************************
void loop()
{
//for oscilloscope measuring, make a 63ns pulse on D12
PULSE62_12;
//**************************************** h e a r t b e a t T I M E R
//is it time to toggle the heartbeatLED ?
if (millis() - heartbeatTime >= 500ul)
{
//restart this TIMER
heartbeatTime = millis();
//toggle the heartbeatLED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//**************************************** L C D u p d a t e T I M E R
//is it time to update the LCD ?
if (millis() - LCDupdateTime >= 100ul)
{
//restart this TIMER
LCDupdateTime = millis();
lcd.clear();
//update static data
lcd.setCursor(0, 0);
// 1111111111
// 01234567890123456789
// Skeleton Sketch
lcd.print(" Skeleton Sketch ");
lcd.setCursor(0, 1);
// 1111111111
// 01234567890123456789
// Counter = xxxxxx
lcd.print(" Counter = ");
//update dynamic data
lcd.setCursor(13, 1);
// 1111111111
// 01234567890123456789
// Counter = xxxxxx
lcd.print(counter++);
}
} //END of loop()
//********************************************^************************************************
Image F shows our zoomed out 63ns pulses on D12.
Image G magnifies the trace, and we see that loop speed has slowed from 2.5us to 4.2us.
A loop( ) speed of 4.2us is still very fast.
But now let us slow the scope trace down to see the overall picture of what’s happening.
Image H now reveals an interval of 28ms when there are no 63ns pulses.
During this 28ms, we are updating the LCD display with a library function that is written in a blocking fashion.
Consider what would happen if we needed to monitor a switch, encoder etc. and didn’t want to miss events.
During the dead 28ms period, other device code sections do not execute, hence we will miss signal changes during this time.
We still want to update our LCD, but our job now is to shorten the 28ms period.
The code example below still creates a 63ns pulses on pin D12 every time the loop( ) function starts.
The sketch still toggles the state of pin D13 every 500 milliseconds.
We have moved the static display LCD section up into the setup( ) function.
The accompanying oscilloscope trace depicts what is happening.
#include <Wire.h>
#include <hd44780.h> //main hd44780 header
#include <hd44780ioClass/hd44780_I2Cexp.h> //I2C expander i/o class header
hd44780_I2Cexp lcd(0x27);
//macro to generate a 63ns pulse on UNO PINB4 i.e. D12
#define PULSE62_12 cli(); PINB = bit(PINB4); PINB = bit(PINB4); sei()
// G P I O & V a r i a b l e s
//********************************************^************************************************
//
const byte pulseOutput = 12;
const byte heartbeatLED = 13;
unsigned int counter;
//****************************************
//timing stuff
unsigned long heartbeatTime;
unsigned long LCDupdateTime;
//********************************************^************************************************
void setup()
{
Serial.begin(115200);
pinMode(pulseOutput, OUTPUT);
pinMode(heartbeatLED, OUTPUT);
//****************************************
//LCD stuff
lcd.begin(20, 4);
lcd.clear();
//update static data
lcd.setCursor(0, 0);
// 1111111111
// 01234567890123456789
// Skeleton Sketch
lcd.print(" Skeleton Sketch ");
lcd.setCursor(0, 1);
// 1111111111
// 01234567890123456789
// Counter = xxxxxx
lcd.print(" Counter = ");
} //END of setup()
//********************************************^************************************************
void loop()
{
//for oscilloscope measuring, make a 63ns pulse on D12
PULSE62_12;
//**************************************** h e a r t b e a t T I M E R
//is it time to toggle the heartbeatLED ?
if (millis() - heartbeatTime >= 500ul)
{
//restart this TIMER
heartbeatTime = millis();
//toggle the heartbeatLED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//**************************************** L C D u p d a t e T I M E R
//is it time to update the LCD ?
if (millis() - LCDupdateTime >= 100ul)
{
//restart this TIMER
LCDupdateTime = millis();
//update dynamic data
lcd.setCursor(13, 1);
// 1111111111
// 01234567890123456789
// Counter = xxxxxx
lcd.print(counter++);
}
} //END of loop()
//********************************************^************************************************
Image I shows the 28ms dead time is now only 1.8ms.
Our goal is to have blocking periods that are very short.
Image J shows even less magnification, we can see your LCD update interval is every 100ms with the 1.8ms blocking intervals.
Note:
If we must detect very fast events, we can temporarily disable lower priority code and/or use interrupts.
As a side note, when it comes to LCD display updating, anything less than 500ms isn’t needed as the eye/brain really cannot handle the speed.
If there are questions or comments please address them in the Software forum while linking to this thread.









