LCD3Wire: a 3-wire driver for HD44780-based LCDs

dhenry:
Not a problem, Bill. I have the post deleted, as well as the performance data generated by the code: I do not like including any copyright statement in my code, and don't like to publish performance data if I cannot publish the code.

In this case it is not your code.
And while the license does have some restrictions, it does not restrict you from modifying or publishing the code.
It merely prevents you from removing or modifying the original licensing agreement and attribution
that comes with the code.

When the lead in header comment was removed,
the license agreement was removed, and essentially changed the code to "free ware"
with no attribution to the original author which is not permitted under the licensing terms.

While many people often violate these types of licensing terms,
these type of restrictions exist in pretty much all open source code.

--- bill

That's why I cannot afford to use those "free" stuff, :slight_smile:

Which library did you base it on? The one on this page:

... has this license:

The text of the Arduino reference is licensed under a Creative Commons Attribution-ShareAlike 3.0 License. Code samples in the reference are released into the public domain.

Here is that license: Creative Commons — Attribution-ShareAlike 3.0 Unported — CC BY-SA 3.0

What is onerous about it? You can copy, adapt and make commercial use of it. You just have to attribute the original author, and if you redistribute, keep the terms of the license on the copies.

Nick,
The licensing comments were actually with respect to a sketch not the library code.

With respect to the LiquidCrystal library, it is one of the worst examples in the Arduino libraries
with respect to copyright/licensing/attribution.
Yes there is a small notice on the Arduino LiquidCrystal site referring to Creative Commons licensing,
But there is nothing in the actual source files, not even a reference in the source back to the
Arduino LiquidCrystal web page.

So as far as the actual LiquidCrystal library source goes, the LiquidCrystal code and examples
appear to be distributed as free-ware since there are no copyright or licensing notices.
i.e. someone receiving the library source code in its original form, would have no idea
of any sort of associated copyrights or licensing or even who the author is
from looking at the source code which all they would have.

The authors have failed to take even basic steps to preserve their copyrights
by not including the necessary information in the source code itself
or some sort of readme or license file that is distributed with the source.

Nearly all the other libraries have properly properly delt with this.
wifi and stepper are close runners up to LiquidCrystal in that they don't have
proper copyright and licensing notices.

Most of the others are LGPL 2.1 but SD may be a bit of a surprise for many people in that
it is GPL v3 which requires that any source that use it be open source.
This means that the SD library cannot be used in closed source projects/products.
While I like this, many folks do not.

--- bill

The authors have failed to ...

Or maybe they just don't care: if I were to release something into the public, I don't want to put any restrictions on anyone using it - or I wouldn't have released to the public.

If I cannot use a piece of code in a way that I see fit, I have no use for it: I am not in the business of furthering someone's claims on anything.

The stuff I release states that you can use it as you see fit. You just can't claim that you wrote it, nor apply extra restrictions on it that I didn't have.

dhenry:

The authors have failed to ...

Or maybe they just don't care:

That is my guess as well. But that would be in conflict with the Creative Commons licensing mentioned
on the Arduino LiquidCrystal page.

If I cannot use a piece of code in a way that I see fit, I have no use for it: I am not in the business of furthering someone's claims on anything.

Hmmm. I guess that means you have no use for Arduino or any open source tools like gcc and its associated tools. :smiley:

I guess that means you have no use for Arduino or any open source tools like gcc and its associated tools.

True, if I cannot use a piece of code in a way that I see fit.

Here is probably an extreme implementation of a 2-wire interface. I wrote this to really show the limitations of such a concept in a multi-pin environment.

Here is the code.

/*
Demo code to run lcd1602 with two io pins
Code runs on 1MIPS avr / gcc-avr

Parts required:
 - 1 mcu (avr for now but portable to ther chips) running at 1MIPS. Need adjustments to other frequencies
 - two pins (digital outs)
 - 4 rc filters: 2 x 1k resistors, 1 x 10k resistor, 1 x 100k resistor and 4x 110n capacitors
 - supports hd44780-compatible lcds, up to 4 lines. lcd in 4-bit mode, no reading of busy flag

Connection:
 - RC filters: from a mcu pin to a resistor and then capacitor to ground. a lcd pin is connected to the joint of the capacitor + resistor.
 - RS: connected to the ENABLE pin via a 1k/110n filter.
 - RW: tied to ground
 - ENABLE: connected to the mcu pin designated as LCD_EN. No rc filter
 - D0..3: not connected
 - D4: connected to the mcu pin designated as LCD_Ds
 - D5: connected to D4 via a 1k/110n filter
 - D6: connected to D4 via a 10k/110n filter
 - D7: connected to D4 via a 100k/110n filter

Disclaimer: no warranty of any kind made. Use at your own risk.
 */

#include <avr/io.h>						//gcc-avr

//pin configuration
#define LCD_EN			D8				//defined to D8
#define LCD_Ds			D9				//defined to D9
#define DLY_min			1				//delays to send D4/en. determined by your rc time constant
#define DLY_x			10ul			//delay multipliers. "ul" for possible overflow. 10x to match the 10x ratio for rc constants

#define LCD_RS			LCD_EN			//en / rs the same pin
#define DLY_D4			(DLY_min)		//delays to send D7. In cycles
#define DLY_D5			(DLY_D4 * DLY_x)	//delays to send D7. In cycles
#define DLY_D6			(DLY_D5 * DLY_x)	//delays to send D7. In cycles
#define DLY_D7			(DLY_D6 * DLY_x)	//delays to send D7. In cycles

typedef struct {
	unsigned char B0: 1;
	unsigned char B1: 1;
	unsigned char B2: 1;
	unsigned char B3: 1;
	unsigned char B4: 1;
	unsigned char B5: 1;
	unsigned char B6: 1;
	unsigned char B7: 1;
} BIT8_T;

//extend bit fields
#if defined (PORTA)
#define PORTAbits	(*(volatile BIT8_T *)&PORTA)
#define DDRAbits	(*(volatile BIT8_T *)&DDRA)
#define PINAbits	(*(volatile BIT8_T *)&PINA)
#endif

#if defined (PORTB)
#define PORTBbits	(*(volatile BIT8_T *)&PORTB)
#define DDRBbits	(*(volatile BIT8_T *)&DDRB)
#define PINBbits	(*(volatile BIT8_T *)&PINB)
#endif

#if defined (PORTC)
#define PORTCbits	(*(volatile BIT8_T *)&PORTC)
#define DDRCbits	(*(volatile BIT8_T *)&DDRC)
#define PINCbits	(*(volatile BIT8_T *)&PINC)
#endif

//helper functions. don't call directly
#define _DDR(id, pin)			DDR ## id ## bits.B ## pin
#define _PORT(id, pin)			PORT ## id ## bits.B ## pin

//port functions
#define DDR(pin)				_DDR(pin)
#define PORT(pin)				_PORT(pin)

//arduino compatibility
#define OUTPUT		1			//set a pin to output
#define INPUT		0			//set a pin to input
#define HIGH		1			//set a pin
#define LOW			0			//clear a pin
//pin macros
#define pinMode(pin, mode)		_DDR(pin) = (mode)
#define digitalWrite(pin, val)	_PORT(pin) = (val)
//pin definitions
#define D8	B, 0						//pin D8 on B.0
#define D9	B, 1						//pin D9 on B.1

#define NOP()		asm("NOP")

//set cursor to row/col
#define lcd_goto(row, col)	lcd_write(0, 0x80 | (row) | (col));
#define LCD_LINE0			0x00				//lcd display line0
#define LCD_LINE1			0x40				//lcd display line1
#define LCD_LINE3			0x14				//lcd display line2
#define LCD_LINE4			0x54				//lcd display line3

//delay routines
void lcd_delay(unsigned long cycles) {
	while (cycles--) NOP();
}

//write 8-bits to the lcd in 4-bit mode
void lcd_write(unsigned char rs, unsigned char dat) {
	//send the highest 4 bits. RS has the same rc filter as D6
	if (dat & 0x80) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D7);
	if (dat & 0x40) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D6);
	if (rs)         digitalWrite(LCD_RS, HIGH); else digitalWrite(LCD_EN, LOW);
	if (dat & 0x20) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D5);
	//like D4, EN has no rc filter
	if (dat & 0x10) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW);
	digitalWrite(LCD_EN, HIGH);
	//lcd_delay(DLY_D4);	in case you need it
	digitalWrite(LCD_EN, LOW);	//strobe out data

	//send the lowest 4 bits
	//send the highest 4 bits. RS has the same rc filter as D6
	if (dat & 0x08) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D7);
	if (dat & 0x04) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D6);
	if (rs)         digitalWrite(LCD_RS, HIGH); else digitalWrite(LCD_EN, LOW);
	if (dat & 0x02) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW); lcd_delay(DLY_D5);
	//like D4, EN has no rc filter
	if (dat & 0x01) digitalWrite(LCD_Ds, HIGH); else digitalWrite(LCD_Ds, LOW);
	digitalWrite(LCD_EN, HIGH);
	//lcd_delay(DLY_D4);	in case you need it
	digitalWrite(LCD_EN, LOW);	//strobe out data
}

//initialize the lcd
void lcd_init(void) {
	//set the pin modes
	pinMode(LCD_EN, OUTPUT);
	pinMode(LCD_Ds, OUTPUT);

	//initialize the pins
	digitalWrite(LCD_EN, LOW);
	digitalWrite(LCD_Ds, LOW);

	//send the initialize sequence
	lcd_write(0, 0x33);			//send 1 of 3 0b0011
	lcd_write(0, 0x32);			//send 2+3 of 3 0b0011, 4-bit mode
	lcd_write(0, 0x28);			//4bit 2 lines
	lcd_write(0, 0x01);			//clear display
	lcd_delay(1000);			//wait for 2ms - need additional work
	lcd_write(0, 0x0c);			//display on, cursor off, no blinking
}

//send string
void lcd_str(unsigned char *str) {
	while (*str) lcd_write(1, *str++);	//send display data
}

//ends lcd2wire driver

//demo code starts here
void setup(void) {
	lcd_init();
}

void loop(void) {
	//display 1st line
	lcd_goto(LCD_LINE0, 0);			//start in col 0
	lcd_str("abcdefg");

	//display 2nd line
	lcd_goto(LCD_LINE1, 2);			//start in col 2
	lcd_str("0123456");
}

//stylized main
int main(void)
{

    // Insert code

	setup();						//reset the mcu
	while(1) {
		loop();						//looping around
	}

	return 0;
}

Unlike earlier 2-wire implementation show, this one requires no shift registers. Just two IO pins + 4 rc filters (4x110n capacitors, 2x1k resistors, 1x10k resistor and 1x100k resistor). The capacitors are chosen so that parasitics is minimized for real life reliability.

The code is really written around 1 function, lcd_write(). That's where all the timing stuff is implemented.

lcd_init() calls on lcd_write() to bring the lcd out of its power-on state.

As the timing gets expotentially slower for a 2-wire system, this implementation is very slow: about 2 seconds for each line of display. You can literally see the characters showing up one at a time, :).

As is, the code is actually written in gcc-avr (the compiler under-neath arduino).

However, it implemented a digital IO subsystem whose calling convention is identical to Arduino's, but much more efficient, much faster, more portable and more flexible - it can be easily ported to any mcu while retaining the arduino calling convention.

dhenry:
Connection:
MOSI pin: from the mcu to shift register's data pin (pin1+2 on HC164 and pin 14 on HC595). LCD's RS pin tied to this line as well.
SCK pin: from the mcu to shift register's serial clock pin (pin 8 on HC164 and pin 11 on HC595)
Enable pin: from the mcu to the lcd.

What about the RCK line (pin 12) on the HC595? Anything connected to that? A schematic for your 3wire HC595 interface would be welcome.

Advantages over other 3-wire solutions:

  1. It allows 8-bit as well as 4-bit mode.
  2. It uses no parts other than the shift register.
  3. It allows HC164 as well as HC595.

Are there any example 3-wire interfaces that drive an LCD in 8-bit interface mode, please? I designed one that uses 3 pins on a 74HC164 but I was wondering if there were any others?

TIA, Mike

What about the RCK line (pin 12) on the HC595?

Connect it to the Enable line: those people who designed HD44780 are incredibly smart and with tons of foresight, :slight_smile:

dhenry:

What about the RCK line (pin 12) on the HC595?

Connect it to the Enable line: those people who designed HD44780 are incredibly smart and with tons of foresight, :slight_smile:

Ahhh! Oooo! Nice! Haven't seen that one anywhere before... So something like the second drawing below. That's much better than what I came up with a couple days ago which requires an extra clock pulse.

Thank you Sir. Cheerful regards, Mike

K8LH 3-Pin 74HC595 LCD 8-Bit Mode Backpack.jpg

3-Pin 74HC595 LCD 8-Bit Mode Backpack.jpg

Yes.

In the code I wrote earlier, I forced a particular connection (Q0 -> D0, Q1 -> D1, etc.) With a little modification, you can remap those pin connections to your own liking, providing additional flexibility for your layout guy.

BTW, what's the capture tool you used there? Pretty refreshing.

The drawings were done using the drawing tools in MS Excel.

Hey, I added an RC integrator and tested a 2-pin 8-bit design but I'm a PIC guy (sorry). Anyway, if you're interested, I've attached a shot of the board just moments after it came up (first try!) and another couple shots of the board before I added the RC parts. The host is a PIC18F14K22 (16 MHz) with an assembly language program. The low level LCD driver uses 28 words of program memory and is isochronous. With delays for the RC tau of 3.3 microseconds, it takes precisely 83.75 microseconds to send each byte to the LCD.

Cheerful regards, Mike

K8LH 2-Pin 74HC595 8-Bit 1st Try.jpg

K8LH 595 8-Bit Proto Front.JPG

K8LH 595 8-Bit Proto Back.JPG

K8LH 2-Pin 74HC595 8-Bit Mode Concept.jpg

Mike,
If you are wanting a 2 pin design, you can get significantly better throughput using an and gate instead of an RC
network to multiplex the pins. The and gate can be created using a resistor and a diode.
When doing that, I was able to get 76us per byte transfer on a 16 mhz AVR.
And that 76 us is the full overhead from an Arduino sketch going through the Print class then down through the library
sending the byte to the display and returning back to the sketch.
The library is also using 4 bit mode which requires sending 2 nibbles and because of the and gate you have
clear the SR before each nibble transfer.
The code I used the SR2W code in this library:
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home
(I am the author of the SR2W interface code)
You can see a diagram of how the SR is wired up in LiquidCrystal_SR2W.h

Since you are a PIC guys here is a link to a page that is doing essentially the same thing
for PIC:
http://www.rentron.com/Myke1.htm

From an overall design perspective I think the AND gate multiplexor is better
than the RC network multiplexor even though it requires 4 times the number of shifts to the SR
since it is faster and allows backlight control.
It also has not timing critical sections so interrupts won't ever have to be masked
while doing the transfers.
Cost wise, it should be nearly the same resistor & diode vs resistor and cap.
Then for a few additional pennies you can add backlight control.
The backlight control does require some dampening on the transistor input to remove
the flicker because of live output bits on the SR during shifting.

You can see a sample backlight circuit in the SR2W header file.

--- bill

Thanks for all the great info', Bill... I'm not really interested in 2-pin designs, per se, or simply driving an LCD, per se. I think there may be more possible applications for an 8-bit shift register + latch than just simply duplicating Myke Predko's old 74HC174 2-wire LCD interface (grin). Anyway, I'm having fun discovering different clever, creative, and elegant design solutions...

The low level LCD driver uses 28 words of program memory and is isochronous.

Excellent. The advantage of coding in assembly.

With delays for the RC tau of 3.3 microseconds, it takes precisely 83.75 microseconds to send each byte to the LCD.

As long as your refresh time for a line is within 5 - 6 ms, you are fine.

A word of caution: the r/c approach is of no use in a noisy environment. As a matter of fact, I would recommend that you perform periodic software reset of the display, in case it goes out of sync.