Skip to content

Commit 15037c2

Browse files
committed
LCD with PFC8547 I2C expander/backpack
1 parent ef2c32d commit 15037c2

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed

Adafruit_PFC8547LCD/Adafruit_I2C.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../Adafruit_I2C/Adafruit_I2C.py
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
from time import sleep
2+
from Adafruit_I2C import Adafruit_I2C
3+
4+
# Code based on https://gist.github.com/ufux/6094977
5+
# Massaged to use Adafruit_I2C library and commands.
6+
# Communication from expander to display: high nibble first, then low nibble
7+
# Communication via i2c to the PCF 8547: bits are processed from highest to lowest (send P7 bit first)
8+
9+
class Adafruit_PCF8547LCD:
10+
#initializes objects and lcd
11+
12+
# LCD Commands
13+
LCD_CLEARDISPLAY = 0x01
14+
LCD_RETURNHOME = 0x02
15+
LCD_ENTRYMODESET = 0x04
16+
LCD_DISPLAYCONTROL = 0x08
17+
LCD_CURSORSHIFT = 0x10
18+
LCD_FUNCTIONSET = 0x20
19+
LCD_SETCGRAMADDR = 0x40
20+
LCD_SETDDRAMADDR = 0x80
21+
22+
# Flags for display on/off control
23+
LCD_DISPLAYON = 0x04
24+
LCD_DISPLAYOFF = 0x00
25+
LCD_CURSORON = 0x02
26+
LCD_CURSOROFF = 0x00
27+
LCD_BLINKON = 0x01
28+
LCD_BLINKOFF = 0x00
29+
30+
# Flags for display entry mode
31+
LCD_ENTRYRIGHT = 0x00
32+
LCD_ENTRYLEFT = 0x02
33+
LCD_ENTRYSHIFTINCREMENT = 0x01
34+
LCD_ENTRYSHIFTDECREMENT = 0x00
35+
36+
# Flags for display/cursor shift
37+
LCD_DISPLAYMOVE = 0x08
38+
LCD_CURSORMOVE = 0x00
39+
LCD_MOVERIGHT = 0x04
40+
LCD_MOVELEFT = 0x00
41+
42+
# flags for function set
43+
LCD_8BITMODE = 0x10
44+
LCD_4BITMODE = 0x00
45+
LCD_2LINE = 0x08
46+
LCD_1LINE = 0x00
47+
LCD_5x10DOTS = 0x04
48+
LCD_5x8DOTS = 0x00
49+
50+
# flags for backlight control
51+
LCD_BACKLIGHT = 0x08
52+
LCD_NOBACKLIGHT = 0x00
53+
54+
EN = 0b00000100 # Enable bit
55+
RW = 0b00000010 # Read/Write bit
56+
RS = 0b00000001 # Register select bit
57+
58+
'''
59+
new pinout:
60+
----------- -----------
61+
0x80 P7 - - D7
62+
0x40 P6 - - D6
63+
0x20 P5 - - D5
64+
0x10 P4 - - D4
65+
----------- -----------
66+
0x08 P3 - - BL Backlight ???
67+
0x04 P2 - - EN Starts Data read/write
68+
0x02 P1 - - RW low: write, high: read
69+
0x01 P0 - - RS Register Select: 0: Instruction Register (IR) (AC when read), 1: data register (DR)
70+
'''
71+
72+
def __init__(self, addr=0x3f, busnum=-1, withBacklight=True, withOneTimeInit=False):
73+
'''
74+
device writes!
75+
crosscheck also http://www.monkeyboard.org/tutorials/81-display/70-usb-serial-to-hd44780-lcd
76+
here a sequence is listed
77+
'''
78+
self.addr = addr
79+
self.busnum = busnum
80+
81+
self.bus = Adafruit_I2C(self.addr, self.busnum)
82+
83+
self.displayshift = (self.LCD_CURSORMOVE |
84+
self.LCD_MOVERIGHT)
85+
self.displaymode = (self.LCD_ENTRYLEFT |
86+
self.LCD_ENTRYSHIFTDECREMENT)
87+
self.displaycontrol = (self.LCD_DISPLAYON |
88+
self.LCD_CURSOROFF |
89+
self.LCD_BLINKOFF)
90+
91+
self.displayfunction = self.LCD_4BITMODE | self.LCD_1LINE | self.LCD_5x8DOTS
92+
self.displayfunction |= self.LCD_2LINE
93+
94+
if withBacklight:
95+
self.blFlag=self.LCD_BACKLIGHT
96+
else:
97+
self.blFlag=self.LCD_NOBACKLIGHT
98+
99+
# we can initialize the display only once after it had been powered on
100+
if(withOneTimeInit):
101+
self.bus.writeRaw8(0x3f)
102+
self.pulseEnable()
103+
sleep(0.0100) # TODO: Not clear if we have to wait that long
104+
self.write(self.displayfunction) # 0x28
105+
106+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol) # 0x08 + 0x4 = 0x0C
107+
self.write(self.LCD_ENTRYMODESET | self.displaymode) # 0x06
108+
self.clear() # 0x01
109+
self.home()
110+
111+
def begin(self, cols, lines):
112+
if (lines > 1):
113+
self.numlines = lines
114+
self.displayfunction |= self.LCD_2LINE
115+
116+
def home(self):
117+
self.write(self.LCD_RETURNHOME) # set cursor position to zero
118+
self.delayMicroseconds(3000) # this command takes a long time!
119+
120+
def clear(self):
121+
self.write(self.LCD_CLEARDISPLAY) # command to clear display
122+
self.delayMicroseconds(3000) # 3000 microsecond sleep, clearing the display takes a long time
123+
124+
def setCursor(self, col, row):
125+
self.row_offsets = [0x00, 0x40, 0x14, 0x54]
126+
if row > self.numlines:
127+
row = self.numlines
128+
row -= 1 # row_offsets is zero indexed
129+
self.write(self.LCD_SETDDRAMADDR | (col - 1 + self.row_offsets[row]))
130+
131+
def noDisplay(self):
132+
""" Turn the display off (quickly) """
133+
self.displaycontrol &= ~self.LCD_DISPLAYON
134+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
135+
136+
def display(self):
137+
""" Turn the display on (quickly) """
138+
self.displaycontrol |= self.LCD_DISPLAYON
139+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
140+
141+
def noCursor(self):
142+
""" Turns the underline cursor off """
143+
self.displaycontrol &= ~self.LCD_CURSORON
144+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
145+
146+
def cursor(self):
147+
""" Turns the underline cursor on """
148+
self.displaycontrol |= self.LCD_CURSORON
149+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
150+
151+
def noBlink(self):
152+
""" Turn the blinking cursor off """
153+
self.displaycontrol &= ~self.LCD_BLINKON
154+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
155+
156+
def blink(self):
157+
""" Turn the blinking cursor on """
158+
self.displaycontrol |= self.LCD_BLINKON
159+
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
160+
161+
def DisplayLeft(self):
162+
""" These commands scroll the display without changing the RAM """
163+
self.write(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT)
164+
165+
def scrollDisplayRight(self):
166+
""" These commands scroll the display without changing the RAM """
167+
self.write(self.LCD_CURSORSHIFT | self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT)
168+
169+
def leftToRight(self):
170+
""" This is for text that flows Left to Right """
171+
self.displaymode |= self.LCD_ENTRYLEFT
172+
self.write(self.LCD_ENTRYMODESET | self.displaymode)
173+
174+
def rightToLeft(self):
175+
""" This is for text that flows Right to Left """
176+
self.displaymode &= ~self.LCD_ENTRYLEFT
177+
self.write(self.LCD_ENTRYMODESET | self.displaymode)
178+
179+
def autoscroll(self):
180+
""" This will 'right justify' text from the cursor """
181+
self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
182+
self.write(self.LCD_ENTRYMODESET | self.displaymode)
183+
184+
def noAutoscroll(self):
185+
""" This will 'left justify' text from the cursor """
186+
self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
187+
self.write(self.LCD_ENTRYMODESET | self.displaymode)
188+
189+
def delayMicroseconds(self, microseconds):
190+
seconds = microseconds / float(1000000) # divide microseconds by 1 million for seconds
191+
sleep(seconds)
192+
193+
# clocks EN to latch command
194+
def pulseEnable(self):
195+
# uses underlying
196+
self.bus.writeRaw8((self.bus.bus.read_byte(self.addr) | self.EN | self.blFlag)) # | 0b0000 0100 # set "EN" high
197+
self.bus.writeRaw8(((self.bus.bus.read_byte(self.addr) | self.blFlag) & 0xFB)) # & 0b1111 1011 # set "EN" low
198+
199+
# write data to lcd in 4 bit mode, 2 nibbles
200+
# high nibble is sent first
201+
def write(self, cmd):
202+
#write high nibble first
203+
self.bus.writeRaw8( (cmd & 0xF0) | self.blFlag )
204+
hi= self.bus.bus.read_byte(self.addr)
205+
self.pulseEnable()
206+
207+
# write low nibble second ...
208+
self.bus.writeRaw8( (cmd << 4) | self.blFlag )
209+
lo= self.bus.bus.read_byte(self.addr)
210+
self.pulseEnable()
211+
self.bus.writeRaw8(self.blFlag)
212+
213+
# write a character to lcd (or character rom) 0x09: backlight | RS=DR
214+
def write_char(self, charvalue):
215+
controlFlag = self.blFlag | self.RS
216+
217+
# write high nibble
218+
self.bus.writeRaw8((controlFlag | (charvalue & 0xF0)))
219+
self.pulseEnable()
220+
221+
# write low nibble
222+
self.bus.writeRaw8((controlFlag | (charvalue << 4)))
223+
self.pulseEnable()
224+
self.bus.writeRaw8(self.blFlag)
225+
226+
# put char function
227+
def putc(self, char):
228+
self.write_char(ord(char))
229+
230+
def _setDDRAMAdress(self, line, col):
231+
# we write to the Data Display RAM (DDRAM)
232+
# TODO: Factor line offsets for other display organizations; this is for 20x4 only
233+
if line == 1:
234+
self.write(self.LCD_SETDDRAMADDR | (0x00 + col) )
235+
if line == 2:
236+
self.write(self.LCD_SETDDRAMADDR | (0x40 + col) )
237+
if line == 3:
238+
self.write(self.LCD_SETDDRAMADDR | (0x14 + col) )
239+
if line == 4:
240+
self.write(self.LCD_SETDDRAMADDR | (0x54 + col) )
241+
242+
# put string function
243+
def message(self, string, line=1):
244+
""" Send string to LCD. Newline wraps to next line.
245+
Starts at line 1 unless passed starting line """
246+
self._setDDRAMAdress(line, 0)
247+
for char in string:
248+
if char == '\n':
249+
line = 1 if line > 4 else line + 1
250+
self._setDDRAMAdress(line, 0)
251+
else:
252+
self.putc(char)
253+
254+
def putString(self, string):
255+
""" Sends a string to LCD starting at current cursor position
256+
Doesn't handle newline character
257+
"""
258+
for char in string:
259+
self.putc(char)
260+
261+
# add custom characters (0 - 7)
262+
def lcd_load_custon_chars(self, fontdata):
263+
self.lcd_device.bus.write(0x40);
264+
for char in fontdata:
265+
for line in char:
266+
self.write_char(line)
267+
268+
if __name__ == '__main__':
269+
from time import localtime, strftime
270+
271+
initFlag=False
272+
debug=False
273+
backlight=True
274+
275+
lcd = Adafruit_PCF8547LCD(0x3f,1,backlight, initFlag)
276+
lcd.begin(20,4)
277+
msg = "+" + "=" * 18 + "+\n"
278+
msg += "| 20x4 LCD |\n"
279+
msg += "| w/ PCF8547 |\n"
280+
msg += "+" + "=" * 18 + "+"
281+
282+
lcd.message(msg)
283+
sleep(3)
284+
285+
lcd.clear()
286+
lcd.cursor()
287+
lcd.message("Cursor Positioning")
288+
lcd.setCursor(2,4)
289+
lcd.putString(". <- (2,4)")
290+
lcd.setCursor(6,2)
291+
lcd.putString(". <- (6,2)")
292+
lcd.setCursor(11,3)
293+
lcd.putString(".")
294+
lcd.setCursor(1,3)
295+
lcd.putString("(11,3) -> ")
296+
297+
sleep(5)
298+
299+
lcd.clear()
300+
lcd.noCursor()
301+
lcd.message(" Simple Clock ",1)
302+
lcd.message("CTRL-C to quit",4)
303+
while True:
304+
lcd.message(strftime("%Y-%m-%d %H:%M:%S ", localtime()),3)
305+
sleep(1)

0 commit comments

Comments
 (0)