Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve timing to make LESS blocking #14

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 92 additions & 52 deletions src/LiquidCrystal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
#include <inttypes.h>
#include "Arduino.h"

// Minimum time between falling edges of E (enable pin to do writes)
#define _LCD_COMMAND_DELAY 40
#define _MAX_COMMAND_DELAY 200

// When the display powers up, it is configured as follows:
//
// 1. Display clear
// 2. Function set:
// DL = 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
// 2. Function set:
// DL = 1; 8-bit interface data
// N = 0; 1-line display
// F = 0; 5x8 dot character font
// 3. Display on/off control:
// D = 0; Display off
// C = 0; Cursor off
// B = 0; Blinking off
// 4. Entry mode set:
// I/D = 1; Increment by 1
// S = 0; No shift
//
// Note, however, that resetting the Arduino doesn't reset the LCD, so we
// can't assume that its in that state when a sketch starts (and the
Expand Down Expand Up @@ -57,22 +61,22 @@ void LiquidCrystal::init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t en
_rs_pin = rs;
_rw_pin = rw;
_enable_pin = enable;

_data_pins[0] = d0;
_data_pins[1] = d1;
_data_pins[2] = d2;
_data_pins[3] = d3;
_data_pins[3] = d3;
_data_pins[4] = d4;
_data_pins[5] = d5;
_data_pins[6] = d6;
_data_pins[7] = d7;
_data_pins[7] = d7;

if (fourbitmode)
_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
else
else
_displayfunction = LCD_8BITMODE | LCD_1LINE | LCD_5x8DOTS;

begin(16, 1);
_comm_delay = _LCD_COMMAND_DELAY; // Default command delay
begin(16, 1);
}

void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
Expand All @@ -81,7 +85,7 @@ void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
}
_numlines = lines;

setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);
setRowOffsets(0x00, 0x40, 0x00 + cols, 0x40 + cols);

// for some 1 line displays you can select a 10 pixel high font
if ((dotsize != LCD_5x8DOTS) && (lines == 1)) {
Expand All @@ -90,47 +94,47 @@ void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {

pinMode(_rs_pin, OUTPUT);
// we can save 1 pin by not using RW. Indicate by passing 255 instead of pin#
if (_rw_pin != 255) {
if (_rw_pin != 255) {
pinMode(_rw_pin, OUTPUT);
}
pinMode(_enable_pin, OUTPUT);

// Do these once, instead of every time a character is drawn for speed reasons.
for (int i=0; i<((_displayfunction & LCD_8BITMODE) ? 8 : 4); ++i)
{
pinMode(_data_pins[i], OUTPUT);
}
}

// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
// according to datasheet, we need at least 40ms after power rises above 2.7V
// before sending commands. Arduino can turn on way before 4.5V so we'll wait 50
delayMicroseconds(50000);
delayMicroseconds(50000);
// Now we pull both RS and R/W low to begin commands
digitalWrite(_rs_pin, LOW);
digitalWrite(_enable_pin, LOW);
if (_rw_pin != 255) {
if (_rw_pin != 255) {
digitalWrite(_rw_pin, LOW);
}

//put the LCD into 4 bit or 8 bit mode
if (! (_displayfunction & LCD_8BITMODE)) {
// this is according to the hitachi HD44780 datasheet
// figure 24, pg 46

// we start in 8bit mode, try to set 4 bit mode
write4bits(0x03);
writebits(0x03, 4, 1);
delayMicroseconds(4500); // wait min 4.1ms

// second try
write4bits(0x03);
writebits(0x03, 4, 1);
delayMicroseconds(4500); // wait min 4.1ms

// third go!
write4bits(0x03);
writebits(0x03, 4, 1);
delayMicroseconds(150);

// finally, set to 4-bit interface
write4bits(0x02);
writebits(0x02, 4, 1);
} else {
// this is according to the hitachi HD44780 datasheet
// page 45 figure 23
Expand All @@ -148,10 +152,10 @@ void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
}

// finally, set # lines, font size, etc.
command(LCD_FUNCTIONSET | _displayfunction);
command(LCD_FUNCTIONSET | _displayfunction);

// turn the display on with no cursor or blinking default
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
display();

// clear it off
Expand Down Expand Up @@ -194,7 +198,7 @@ void LiquidCrystal::setCursor(uint8_t col, uint8_t row)
if ( row >= _numlines ) {
row = _numlines - 1; // we count rows starting w/0
}

command(LCD_SETDDRAMADDR | (col + _row_offsets[row]));
}

Expand Down Expand Up @@ -260,6 +264,7 @@ void LiquidCrystal::noAutoscroll(void) {
command(LCD_ENTRYMODESET | _displaymode);
}


// Allows us to fill the first 8 CGRAM locations
// with custom characters
void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
Expand All @@ -270,6 +275,24 @@ void LiquidCrystal::createChar(uint8_t location, uint8_t charmap[]) {
}
}


/* New method that if default command delay is not long enough
to set a new value between
_LCD_COMMAND_DELAY (default 40 us)
_MAX_COMMAND_DELAY (default 200 us)

Should be called before you call begin method

returns 0 for success
-1 for error (invalid parameter)
*/
int LiquidCrystal::setCommandDelay( uint8_t delay ) {
if( delay >= _LCD_COMMAND_DELAY && delay <= _MAX_COMMAND_DELAY ) {
_comm_delay = delay;
return 0;
}
return -1;
}
/*********** mid level commands, for sending data/cmds */

inline void LiquidCrystal::command(uint8_t value) {
Expand All @@ -288,39 +311,56 @@ void LiquidCrystal::send(uint8_t value, uint8_t mode) {
digitalWrite(_rs_pin, mode);

// if there is a RW pin indicated, set it low to Write
if (_rw_pin != 255) {
if (_rw_pin != 255) {
digitalWrite(_rw_pin, LOW);
}

if (_displayfunction & LCD_8BITMODE) {
write8bits(value);
writebits(value, 8, 0);
} else {
write4bits(value>>4);
write4bits(value);
writebits(value>>4, 4, 0);
writebits(value, 4, 1);
}
}

void LiquidCrystal::pulseEnable(void) {
digitalWrite(_enable_pin, LOW);
delayMicroseconds(1);

/* pulseEnable once data and RS set pulse enable line to send data in 4/8 bit mode

Checks when last falling edge of enable was and if sending a NEW command
delays for time to get up to _comm_delay to ensure controller ready

When in 4 bit mode 2nd nibble can be sent at minimum delay for Enable high
time delay 1us

Parameter type - 0 = new command start
1 = 2nd nibble for 4 bit mode (needs 1us delay
NOT _comm_delay)
*/
void LiquidCrystal::pulseEnable(uint8_t type) {
unsigned long _delaymicros;

digitalWrite(_enable_pin, HIGH);
delayMicroseconds(1); // enable pulse must be >450ns
_delaymicros = micros( ) - _oldmicros; // PREVIOUS commands need > 37us to settle
if( _delaymicros < _comm_delay && type == 0 )
delayMicroseconds( _comm_delay - _delaymicros ); // wait time for delay
else
delayMicroseconds(1); // enable pulse HIGH must be >450ns
digitalWrite(_enable_pin, LOW);
delayMicroseconds(100); // commands need > 37us to settle
_oldmicros = micros( ); // save time to check next time
}

void LiquidCrystal::write4bits(uint8_t value) {
for (int i = 0; i < 4; i++) {
digitalWrite(_data_pins[i], (value >> i) & 0x01);
}

pulseEnable();
}
/* writebits transfer correct number of bits to output and then call pulse enable

void LiquidCrystal::write8bits(uint8_t value) {
for (int i = 0; i < 8; i++) {
Parameters value - 4 or 8 bits to send
in 4 bit mode shift top 4 bits right before calling
bits - value 4 or 8 for bits to send
type - 0 = new command start
1 = 2nd nibble for 4 bit mode (needs 1us delay NOT _MIN_WRITE_DELAY
*/
void LiquidCrystal::writebits(uint8_t value, uint8_t bits, uint8_t type) {
for (int i = 0; i < bits; i++) {
digitalWrite(_data_pins[i], (value >> i) & 0x01);
}

pulseEnable();
pulseEnable( type );
}
14 changes: 8 additions & 6 deletions src/LiquidCrystal.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class LiquidCrystal : public Print {
void init(uint8_t fourbitmode, uint8_t rs, uint8_t rw, uint8_t enable,
uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3,
uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);

void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

void clear();
Expand All @@ -76,24 +76,25 @@ class LiquidCrystal : public Print {
void rightToLeft();
void autoscroll();
void noAutoscroll();
int setCommandDelay( uint8_t );

void setRowOffsets(int row1, int row2, int row3, int row4);
void createChar(uint8_t, uint8_t[]);
void setCursor(uint8_t, uint8_t);
void setCursor(uint8_t, uint8_t);
virtual size_t write(uint8_t);
void command(uint8_t);

using Print::write;
private:
void send(uint8_t, uint8_t);
void write4bits(uint8_t);
void write8bits(uint8_t);
void pulseEnable();
void writebits(uint8_t, uint8_t, uint8_t);
void pulseEnable( uint8_t );

uint8_t _rs_pin; // LOW: command. HIGH: character.
uint8_t _rw_pin; // LOW: write to LCD. HIGH: read from LCD.
uint8_t _enable_pin; // activated by a HIGH pulse.
uint8_t _data_pins[8];
uint8_t _comm_delay; // Delay between commands by default _LCD_COMMAND_DELAY

uint8_t _displayfunction;
uint8_t _displaycontrol;
Expand All @@ -103,6 +104,7 @@ class LiquidCrystal : public Print {

uint8_t _numlines;
uint8_t _row_offsets[4];
unsigned long _oldmicros;
};

#endif