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;
}