Skip to content

Commit 87ca81a

Browse files
TarjanPeterdhylands
authored andcommitted
Circuitpython port (#26)
* Made it work in circuitpython. * Made it work in circuitpython. * Switched to UNIX EOL. * Converted to Unix EOL. * Made it work in circuitpython. * Implemented hal_sleep_us * Made it work in circuitpython. Changed to more descriptive variable name. * Tabs to spaces.
1 parent 92e5d02 commit 87ca81a

File tree

3 files changed

+172
-2
lines changed

3 files changed

+172
-2
lines changed

lcd/circuitpython_i2c_lcd.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Implements a HD44780 character LCD connected via PCF8574 on I2C.
2+
This was tested with: https://www.wemos.cc/product/d1-mini.html"""
3+
4+
from lcd_api import LcdApi
5+
import busio
6+
from time import sleep
7+
8+
# The PCF8574 has a jumper selectable address: 0x20 - 0x27
9+
DEFAULT_I2C_ADDR = 0x27
10+
11+
# Defines shifts or masks for the various LCD line attached to the PCF8574
12+
13+
MASK_RS = 0x01
14+
MASK_RW = 0x02
15+
MASK_E = 0x04
16+
SHIFT_BACKLIGHT = 3
17+
SHIFT_DATA = 4
18+
19+
20+
class I2cLcd(LcdApi):
21+
"""Implements a HD44780 character LCD connected via PCF8574 on I2C."""
22+
def __init__(self, i2c, i2c_addr, num_lines, num_columns):
23+
self.i2c = i2c
24+
self.i2c_addr = i2c_addr
25+
self.i2c.writeto(self.i2c_addr, bytearray([0]))
26+
sleep(0.02) # Allow LCD time to powerup
27+
# Send reset 3 times
28+
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
29+
sleep(0.005) # need to delay at least 4.1 msec
30+
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
31+
sleep(0.001)
32+
self.hal_write_init_nibble(self.LCD_FUNCTION_RESET)
33+
sleep(0.001)
34+
# Put LCD into 4 bit mode
35+
self.hal_write_init_nibble(self.LCD_FUNCTION)
36+
sleep(0.001)
37+
LcdApi.__init__(self, num_lines, num_columns)
38+
cmd = self.LCD_FUNCTION
39+
if num_lines > 1:
40+
cmd |= self.LCD_FUNCTION_2LINES
41+
self.hal_write_command(cmd)
42+
43+
def hal_write_init_nibble(self, nibble):
44+
"""Writes an initialization nibble to the LCD.
45+
46+
This particular function is only used during initialization.
47+
"""
48+
byte = ((nibble >> 4) & 0x0f) << SHIFT_DATA
49+
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
50+
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
51+
52+
def hal_backlight_on(self):
53+
"""Allows the hal layer to turn the backlight on."""
54+
self.i2c.writeto(self.i2c_addr, bytearray([1 << SHIFT_BACKLIGHT]))
55+
56+
def hal_backlight_off(self):
57+
"""Allows the hal layer to turn the backlight off."""
58+
self.i2c.writeto(self.i2c_addr, bytearray([0]))
59+
60+
def hal_write_command(self, cmd):
61+
"""Writes a command to the LCD.
62+
63+
Data is latched on the falling edge of E.
64+
"""
65+
byte = ((self.backlight << SHIFT_BACKLIGHT) | (((cmd >> 4) & 0x0f) << SHIFT_DATA))
66+
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
67+
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
68+
byte = ((self.backlight << SHIFT_BACKLIGHT) | ((cmd & 0x0f) << SHIFT_DATA))
69+
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
70+
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
71+
if cmd <= 3:
72+
# The home and clear commands require a worst case delay of 4.1 msec
73+
sleep(0.005)
74+
75+
def hal_write_data(self, data):
76+
"""Write data to the LCD."""
77+
byte = (MASK_RS | (self.backlight << SHIFT_BACKLIGHT) | (((data >> 4) & 0x0f) << SHIFT_DATA))
78+
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
79+
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
80+
byte = (MASK_RS | (self.backlight << SHIFT_BACKLIGHT) | ((data & 0x0f) << SHIFT_DATA))
81+
self.i2c.writeto(self.i2c_addr, bytearray([byte | MASK_E]))
82+
self.i2c.writeto(self.i2c_addr, bytearray([byte]))
83+
84+
def hal_sleep_us(self, usecs):
85+
"""Sleep for some time (given in microseconds)."""
86+
sleep(float(usecs)/1e6)

lcd/circuitpython_i2c_lcd_test.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Implements a HD44780 character LCD connected via PCF8574 on I2C in CircuitPython.
2+
This was tested with the Adafruit Trinket M0
3+
https://www.adafruit.com/product/3500"""
4+
5+
import board
6+
import busio
7+
from time import sleep, monotonic
8+
from circuitpython_i2c_lcd import I2cLcd
9+
10+
# The PCF8574 has a jumper selectable address: 0x20 - 0x27
11+
DEFAULT_I2C_ADDR = 0x27
12+
13+
def test_main():
14+
"""Test function for verifying basic functionality."""
15+
print("Running test_main")
16+
i2c = busio.I2C(board.SCL, board.SDA)
17+
18+
# circuitpython seems to require locking the i2c bus
19+
while i2c.try_lock():
20+
pass
21+
22+
# 2 lines, 16 characters per line
23+
lcd = I2cLcd(i2c, DEFAULT_I2C_ADDR, 2, 16)
24+
25+
# smiley faces as custom characters
26+
happy = bytearray([0x00,0x0A,0x00,0x04,0x00,0x11,0x0E,0x00])
27+
sad = bytearray([0x00,0x0A,0x00,0x04,0x00,0x0E,0x11,0x00])
28+
grin = bytearray([0x00,0x00,0x0A,0x00,0x1F,0x11,0x0E,0x00])
29+
shock = bytearray([0x0A,0x00,0x04,0x00,0x0E,0x11,0x11,0x0E])
30+
meh = bytearray([0x00,0x0A,0x00,0x04,0x00,0x1F,0x00,0x00])
31+
angry = bytearray([0x11,0x0A,0x11,0x04,0x00,0x0E,0x11,0x00])
32+
tongue = bytearray([0x00,0x0A,0x00,0x04,0x00,0x1F,0x05,0x02])
33+
lcd.custom_char(0, happy)
34+
lcd.custom_char(1, sad)
35+
lcd.custom_char(2, grin)
36+
lcd.custom_char(3, shock)
37+
lcd.custom_char(4, meh)
38+
lcd.custom_char(5, angry)
39+
lcd.custom_char(6, tongue)
40+
41+
lcd.putstr("It works!\nSecond line")
42+
sleep(3)
43+
lcd.clear()
44+
lcd.putstr("Custom chars:\n")
45+
for i in range(7):
46+
lcd.putchar(" ")
47+
lcd.putchar(chr(i))
48+
sleep(1)
49+
50+
sleep(3)
51+
lcd.clear()
52+
count = 0
53+
54+
while True:
55+
lcd.move_to(0, 0)
56+
lcd.putstr("%7d" % monotonic())
57+
sleep(1)
58+
count += 1
59+
if count % 10 == 3:
60+
print("Turning backlight off")
61+
lcd.backlight_off()
62+
if count % 10 == 4:
63+
print("Turning backlight on")
64+
lcd.backlight_on()
65+
if count % 10 == 5:
66+
print("Turning display off")
67+
lcd.display_off()
68+
if count % 10 == 6:
69+
print("Turning display on")
70+
if count % 10 == 7:
71+
print("Turning display & backlight off")
72+
lcd.backlight_off()
73+
lcd.display_off()
74+
if count % 10 == 8:
75+
print("Turning display & backlight back on")
76+
lcd.backlight_on()
77+
lcd.display_on()
78+
79+
#if __name__ == "__main__":
80+
test_main()

lcd/lcd_api.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ def custom_char(self, location, charmap):
158158
"""
159159
location &= 0x7
160160
self.hal_write_command(self.LCD_CGRAM | (location << 3))
161-
time.sleep_us(40)
161+
self.hal_sleep_us(40)
162162
for i in range(8):
163163
self.hal_write_data(charmap[i])
164-
time.sleep_us(40)
164+
self.hal_sleep_us(40)
165165
self.move_to(self.cursor_x, self.cursor_y)
166166

167167
def hal_backlight_on(self):
@@ -193,3 +193,7 @@ def hal_write_data(self, data):
193193
function.
194194
"""
195195
raise NotImplementedError
196+
197+
def hal_sleep_us(self, usecs):
198+
"""Sleep for some time (given in microseconds)."""
199+
time.sleep_us(usecs)

0 commit comments

Comments
 (0)