Skip to content

Commit 4dd9f8b

Browse files
committed
Merge pull request #2 from dsmall/master
EXPERIMENTAL Pytronics support for i2c and jeelabs lcd plug
2 parents 0835d20 + 18e4430 commit 4dd9f8b

File tree

3 files changed

+302
-5
lines changed

3 files changed

+302
-5
lines changed

i2c.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import os, fcntl
2+
from ctypes import *
3+
4+
RASCAL_I2C = '/dev/i2c-0'
5+
6+
# smbus_access read or write markers
7+
I2C_SMBUS_READ = 1
8+
I2C_SMBUS_WRITE = 0
9+
10+
# SMBus transaction types (size parameter in the above functions)
11+
I2C_SMBUS_QUICK = 0
12+
I2C_SMBUS_BYTE = 1
13+
I2C_SMBUS_BYTE_DATA = 2
14+
I2C_SMBUS_WORD_DATA= 3
15+
I2C_SMBUS_PROC_CALL = 4
16+
I2C_SMBUS_BLOCK_DATA = 5
17+
I2C_SMBUS_I2C_BLOCK_BROKEN = 6
18+
I2C_SMBUS_BLOCK_PROC_CALL = 7 # SMBus 2.0
19+
I2C_SMBUS_I2C_BLOCK_DATA = 8
20+
21+
I2C_SLAVE = 0x0703
22+
I2C_FUNCS = 0x0705
23+
I2C_SMBUS = 0x0720 # SMBus-level access
24+
25+
I2C_SCAN_BUSY = -2
26+
I2C_SCAN_NODEV = -1
27+
I2C_SCAN_SKIPPED = 0
28+
29+
# Data for SMBus Messages
30+
I2C_SMBUS_BLOCK_MAX = 32 # As specified in SMBus standard
31+
I2C_SMBUS_I2C_BLOCK_MAX = 32 # Not specified but we use same structure
32+
33+
debug = False
34+
35+
class i2c_smbus_data(Union):
36+
_fields_ = [('byte', c_ubyte),
37+
('word', c_uint),
38+
('block', c_ubyte * 34)]
39+
40+
class i2c_smbus_ioctl_data(Structure):
41+
_fields_ = [('read_write', c_ubyte),
42+
('command', c_ubyte),
43+
('size', c_int),
44+
('data', POINTER(i2c_smbus_data))]
45+
46+
def i2c_smbus_access(file, read_write, command, size, data):
47+
try:
48+
args = i2c_smbus_ioctl_data(read_write, command, size, data)
49+
return fcntl.ioctl(file, I2C_SMBUS, args)
50+
except IOError, e:
51+
if debug: print '## i2c_smbus_access ##: IOError {0}'.format(e)
52+
return -1
53+
except IOError, e:
54+
return -1
55+
except Exception, e:
56+
print '## i2c_smbus_access ## {0}'.format(e)
57+
return -1
58+
59+
def i2c_smbus_write_quick(file, value):
60+
data = i2c_smbus_data(0)
61+
return i2c_smbus_access(file, value, 0, I2C_SMBUS_QUICK, pointer(data))
62+
63+
def i2c_smbus_read_byte(file):
64+
if debug: print '## i2c_smbus_read_byte ##'
65+
data = i2c_smbus_data(0)
66+
if (i2c_smbus_access(file, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, pointer(data)) == 0):
67+
return 0x0FF & data.byte
68+
else:
69+
return -1
70+
71+
def i2c_smbus_write_byte(file, value):
72+
if debug: print '## i2c_smbus_write_byte ##: value 0x{0:00X}'.format(value)
73+
data = i2c_smbus_data(0)
74+
return i2c_smbus_access(file, I2C_SMBUS_WRITE, value, I2C_SMBUS_BYTE, pointer(data))
75+
76+
def i2c_smbus_read_byte_data(file, daddr):
77+
if debug: print '## i2c_smbus_read_byte_data ##: daddr {0}'.format(daddr)
78+
data = i2c_smbus_data(0)
79+
if (i2c_smbus_access(file, I2C_SMBUS_READ, daddr, I2C_SMBUS_BYTE_DATA, pointer(data)) == 0):
80+
return 0x0FF & data.byte
81+
else:
82+
return -1
83+
84+
def i2c_smbus_write_byte_data(file, daddr, value):
85+
if debug: print '## i2c_smbus_write_byte_data ##: daddr {0}, value=0x{1:00X}'.format(daddr, value)
86+
data = i2c_smbus_data(0)
87+
data.byte = value
88+
return i2c_smbus_access(file, I2C_SMBUS_WRITE, daddr, I2C_SMBUS_BYTE_DATA, pointer(data))
89+
90+
def i2c_smbus_read_word_data(file, daddr):
91+
if debug: print '## i2c_smbus_read_word_data ##: daddr {0}'.format(daddr)
92+
data = i2c_smbus_data(0)
93+
if (i2c_smbus_access(file, I2C_SMBUS_READ, daddr, I2C_SMBUS_WORD_DATA, pointer(data)) == 0):
94+
return 0x0FFFF & data.word
95+
else:
96+
return -1
97+
98+
def i2c_smbus_write_word_data(file, daddr, value):
99+
if debug: print '## i2c_smbus_write_word_data ##: daddr {0}, value=0x{1:0000X}'.format(daddr, value)
100+
data = i2c_smbus_data(0)
101+
data.word = value
102+
return i2c_smbus_access(file, I2C_SMBUS_WRITE, daddr, I2C_SMBUS_WORD_DATA, pointer(data))
103+
104+
# Main entry points
105+
def _i2cRead(addr, reg = 0, size = ''):
106+
if debug: print '## i2cRead ## addr={0}, reg={1}, size={2}'.format(addr, reg, size)
107+
try:
108+
file = os.open(RASCAL_I2C, os.O_RDWR)
109+
try:
110+
fcntl.ioctl(file, I2C_SLAVE, addr)
111+
if size.upper() == 'W':
112+
data = i2c_smbus_read_word_data(file, reg)
113+
elif size.upper() == 'B':
114+
data = i2c_smbus_read_byte_data(file, reg)
115+
else:
116+
data = i2c_smbus_read_byte(file)
117+
except Exception, e:
118+
if debug: print '## i2cRead ## Can\'t set slave addr {0}'.format(e)
119+
data = -1
120+
os.close(file)
121+
except OSError:
122+
if debug: print '## i2cRead ## Can\'t open {0}'.format(RASCAL_I2C)
123+
data = -2
124+
return data
125+
126+
def _i2cWrite(addr, reg, value, size):
127+
if debug: print '## i2cWrite ## addr=0x{0:02x}, reg=0x{1:02x}, value=0x{2:04X}, size={3}'.format(addr, reg, value, size)
128+
try:
129+
file = os.open(RASCAL_I2C, os.O_RDWR)
130+
try:
131+
fcntl.ioctl(file, I2C_SLAVE, addr)
132+
if size.upper() == 'W':
133+
data = i2c_smbus_write_word_data(file, reg, value)
134+
elif size.upper() == 'B':
135+
data = i2c_smbus_write_byte_data(file, reg, value)
136+
else:
137+
data = i2c_smbus_write_byte(file, value)
138+
except Exception, e:
139+
if debug: print '## i2cWrite ## Can\'t set slave addr {0}'.format(e)
140+
data = -1
141+
os.close(file)
142+
except OSError:
143+
if debug: print '## i2cWrite ## Can\'t open {0}'.format(RASCAL_I2C)
144+
data = -2
145+
return data
146+
147+
# Support for scanning i2C bus
148+
def probe_bus(file, addr):
149+
# Set slave address
150+
try:
151+
fcntl.ioctl(file, I2C_SLAVE, addr)
152+
if ((addr >= 0x30 and addr <= 0x37) or (addr >= 0x50 and addr <= 0x5F)):
153+
res = i2c_smbus_read_byte(file)
154+
else:
155+
res = i2c_smbus_write_quick(file, I2C_SMBUS_WRITE)
156+
if res < 0:
157+
return I2C_SCAN_NODEV
158+
else:
159+
return addr
160+
except Exception, e:
161+
return I2C_SCAN_BUSY
162+
163+
# Address status: 0=out of range, -1=not present, -2=busy, otherwise device address
164+
def scanBus(first = 0x03, last = 0x77):
165+
file = os.open(RASCAL_I2C, os.O_RDWR)
166+
scan = {}
167+
for i in range(0, 128, 16):
168+
row = ()
169+
for j in range(0, 16):
170+
addr = i + j
171+
# Skip unwanted addresses
172+
if (addr < first) or (addr > last):
173+
row += (I2C_SCAN_SKIPPED,)
174+
else:
175+
row += (probe_bus(file, addr),)
176+
scan[i] = row
177+
os.close(file)
178+
return scan
179+
180+
181+

i2c_lcd.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Support for JeeLabs i2c LCD Plug and HD44780 LCD
2+
from i2c import _i2cRead, _i2cWrite
3+
4+
# LCD Plug chip address
5+
LCD = 0x24
6+
# On power on the MCP23008 IOCON reg defaults to 0x0
7+
# We disable SEQOP (bit5 = 1) and set INT to open drain (bit2 = 1)
8+
# Our reset function checks this value to see whether the LCD needs to be initialised
9+
MCP_IOCON_INIT = 0x24
10+
11+
# MCP23008 registers
12+
MCP_IODIR = 0x00
13+
MCP_IOCON = 0x05
14+
MCP_GPIO = 0x09
15+
16+
# MCP23008 outputs P4-P7 (P0-P3 are data)
17+
MCP_BACKLIGHT = 0x80
18+
MCP_ENABLE = 0x40 # Pulse high to write to HD44780
19+
MCP_OTHER = 0x20
20+
MCP_REGSEL = 0x10 # 1=LCD data, 0=LCD command
21+
22+
# HD44780 command registers
23+
LCD_CLR = 0x01
24+
LCD_HOME = 0x02
25+
LCD_MODE = 0x04
26+
LCD_DISPLAY = 0x08
27+
LCD_SHIFT = 0x10
28+
LCD_FUNCTION = 0x20
29+
LCD_CGADDR = 0x40
30+
LCD_DDADDR = 0x80
31+
32+
33+
def _write4bits(rs, value):
34+
data = value | MCP_BACKLIGHT | rs
35+
_i2cWrite(LCD, MCP_GPIO, data, 'B')
36+
_i2cWrite(LCD, MCP_GPIO, data | MCP_ENABLE, 'B')
37+
_i2cWrite(LCD, MCP_GPIO, data, 'B')
38+
39+
def writeByte(rs, value):
40+
_write4bits(rs, (value >> 4) & 0x0f)
41+
_write4bits(rs, value & 0x0f)
42+
43+
def writeCmd(value):
44+
writeByte(0, value)
45+
46+
def writeData(value):
47+
writeByte(MCP_REGSEL, value)
48+
49+
# With gratitude to http://joshuagalloway.com/lcd.html
50+
# who explains how to do this clearly and concisely
51+
# See also Hitachi HD4487U specification, page 46
52+
def init():
53+
# Set MCP IO Control register (SREAD disabled, INT to ODR)
54+
_i2cWrite(LCD, MCP_IOCON, MCP_IOCON_INIT, 'B')
55+
# Set MCP IO Direction register to output (all pins)
56+
# A side-effect is to enable backlight on first write to MCP_GPIO
57+
_i2cWrite(LCD, MCP_IODIR, 0, 'B')
58+
59+
# Set to 4-bit mode (Hitachi magic)
60+
_write4bits(0, 0x03)
61+
_write4bits(0, 0x03)
62+
_write4bits(0, 0x03)
63+
_write4bits(0, 0x02)
64+
65+
# 001x xx-- set interface length: 4 bits, 2 lines
66+
# bit 4: 8-bit(1)/4-bit(0)
67+
# bit 3: 1 line(0)/2 lines(1)
68+
# bit 2: font 5x10(1)/5x7(0)
69+
writeCmd(LCD_FUNCTION | 0x08)
70+
71+
# 0000 1xxx enable display/cursor: display off, cursor off, blink off
72+
# bit 2: display on
73+
# bit 1: display cursor
74+
# bit 0: blinking cursor
75+
writeCmd(LCD_DISPLAY)
76+
77+
# 0000 0001 clear display, home cursor
78+
writeCmd(LCD_CLR)
79+
80+
# 0000 01xx set cursor move direction: inc cursor, do not shift data
81+
# bit 1: inc(1)/dec(0)
82+
# bit 0: shift display
83+
writeCmd(LCD_MODE | 0x02)
84+
85+
# 0000 1xxx enable display/cursor: display on, cursor off, blink off
86+
# bit 2: display on
87+
# bit 1: display cursor
88+
# bit 0: blinking cursor
89+
writeCmd(LCD_DISPLAY | 0x04)
90+
91+
# Backlight is controlled by toggling MCP23008 P7 IO status
92+
# Set P7 to output (0 = on) or input (1 = off)
93+
def backlight(state = True):
94+
if state:
95+
_i2cWrite(LCD, MCP_IODIR, 0, 'B')
96+
else:
97+
_i2cWrite(LCD, MCP_IODIR, MCP_BACKLIGHT, 'B')
98+
99+
def setCursor(row, col):
100+
rowstart = [ 0x00, 0x40, 0x14, 0x54 ]
101+
writeCmd(LCD_DDADDR | (rowstart[row] + col))
102+
103+
def reset(clear = True):
104+
# Check if LCD has been initialised
105+
if _i2cRead(LCD, MCP_IOCON, 'B') == MCP_IOCON_INIT:
106+
if clear:
107+
writeCmd(LCD_CLR)
108+
else:
109+
setCursor(0, 0)
110+
else:
111+
init()
112+
113+
def writeString(s):
114+
for i in s:
115+
writeData(ord(i))

pytronics.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,13 @@ def readPins(pinlist):
101101
print ('## readPins ## Cannot access pin {0} ({1})'.format(pin, syspin))
102102
return pins
103103

104-
def i2cRead():
105-
pass
104+
def i2cRead(addr, reg = 0, size = 'B'):
105+
from i2c import _i2cRead
106+
return _i2cRead(addr, reg, size)
106107

107-
def i2cWrite(val):
108-
cmd = 'i2cset -y 0 0x29 ' + str(val)
109-
subprocess.Popen([cmd], shell=True)
108+
def i2cWrite(addr, reg, val, size):
109+
from i2c import _i2cWrite
110+
return _i2cWrite(addr, reg, val, size)
110111

111112
def serialRead():
112113
pass

0 commit comments

Comments
 (0)