I ported my 7110 LCD C code for PIC to MicroPython on the PyBoard. It wasn't very difficult. Yet again, most of the trouble I had was due to having to learn Python as I ported the code. Not a big deal - Python isn't difficult to learn.
I wasn't sure about text formatting for my font array in Python, so I turned it into the big blob (one long line) you see below. Now that I have the code working I may tinker a bit and see if I can get it formatted to be more readable.
Video of the 7110 LCD driven by the PyBoard:
I fought with my graphic code for quite a while before finally figuring out the Python way. I could not get it to work properly for the longest time. First problem was figuring out that the way Python handles global variables is... odd. Nothin wrong with it - it's just unusual.
Then I had to figure out how to deal with negative numbers, which Python does completely differently than C does. I eventually realized that I was only using the negative numbers to do a ones complement for a mask to clear pixels, so I didn't need to do it the C way at all. Rewrote those bits of code and cured that problem. Easy, but took forever to find the problem.
Then I decided to change from using arrays to native Python lists for the frame buffers. Easier, and works just as well.
The code works well. It's not blazing fast, but good enough for what it is. If I cared there are things I could do to improve the speed some.
Here's video of the demo:
Here's the MicroPython PyBoard code to do text on the 7110 LCD. Scroll down past it to see the graphic demo code.:
from pyb import Pin
import array as arr
CS = Pin('X4',Pin.OUT_PP)
DC = Pin('X3',Pin.OUT_PP)
SCLK = Pin('X2',Pin.OUT_PP)
SDATA = Pin('X1',Pin.OUT_PP)
#ascii font - Space 32 ($20) thru DEL 127 ($7f)
Alpha1 = arr.array('B',[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x07,0x00,0x07,0x00,0x14,0x7f,0x14,0x7f,0x14,0x24,0x2a,0x7f,0x2a,0x12,0x23,0x13,0x08,0x64,0x62,0x36,0x49,0x55,0x22,0x50,0x00,0x05,0x03,0x00,0x00,0x00,0x1c,0x22,0x41,0x00,0x00,0x41,0x22,0x1c,0x00,0x14,0x08,0x3e,0x08,0x14,0x08,0x08,0x3e,0x08,0x08,0x00,0x50,0x30,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x60,0x60,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x3e,0x51,0x49,0x45,0x3e,0x00,0x42,0x7f,0x40,0x00,0x42,0x61,0x51,0x49,0x46,0x21,0x41,0x45,0x4b,0x31,0x18,0x14,0x12,0x7f,0x10,0x27,0x45,0x45,0x45,0x39,0x3c,0x4a,0x49,0x49,0x30,0x01,0x71,0x09,0x05,0x03,0x36,0x49,0x49,0x49,0x36,0x06,0x49,0x49,0x29,0x1e,0x00,0x36,0x36,0x00,0x00,0x00,0x56,0x36,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x41,0x22,0x14,0x08,0x02,0x01,0x51,0x09,0x06,0x32,0x49,0x79,0x41,0x3e,0x7e,0x11,0x11,0x11,0x7e,0x7f,0x49,0x49,0x49,0x36,0x3e,0x41,0x41,0x41,0x22,0x7f,0x41,0x41,0x22,0x1c,0x7f,0x49,0x49,0x49,0x41,0x7f,0x09,0x09,0x09,0x01,0x3e,0x41,0x49,0x49,0x7a,0x7f,0x08,0x08,0x08,0x7f,0x00,0x41,0x7f,0x41,0x00,0x20,0x40,0x41,0x3f,0x01,0x7f,0x08,0x14,0x22,0x41,0x7f,0x40,0x40,0x40,0x40,0x7f,0x02,0x0c,0x02,0x7f,0x7f,0x04,0x08,0x10,0x7f,0x3e,0x41,0x41,0x41,0x3e,0x7f,0x09,0x09,0x09,0x06,0x3e,0x41,0x51,0x21,0x5e,0x7f,0x09,0x19,0x29,0x46,0x46,0x49,0x49,0x49,0x31,0x01,0x01,0x7f,0x01,0x01,0x3f,0x40,0x40,0x40,0x3f,0x1f,0x20,0x40,0x20,0x1f,0x3f,0x40,0x38,0x40,0x3f,0x63,0x14,0x08,0x14,0x63,0x07,0x08,0x70,0x08,0x07,0x61,0x51,0x49,0x45,0x43,0x00,0x7f,0x41,0x41,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x41,0x41,0x7f,0x00,0x04,0x02,0x01,0x02,0x04,0x40,0x40,0x40,0x40,0x40,0x00,0x01,0x02,0x04,0x00,0x20,0x54,0x54,0x54,0x78,0x7f,0x48,0x44,0x44,0x38,0x38,0x44,0x44,0x44,0x20,0x38,0x44,0x44,0x48,0x7f,0x38,0x54,0x54,0x54,0x18,0x08,0x7e,0x09,0x01,0x02,0x0c,0x52,0x52,0x52,0x3e,0x7f,0x08,0x04,0x04,0x78,0x00,0x44,0x7d,0x40,0x00,0x20,0x40,0x44,0x3d,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x41,0x7f,0x40,0x00,0x7c,0x04,0x18,0x04,0x78,0x7c,0x08,0x04,0x04,0x78,0x38,0x44,0x44,0x44,0x38,0x7c,0x14,0x14,0x14,0x08,0x08,0x14,0x14,0x18,0x7c,0x7c,0x08,0x04,0x04,0x08,0x48,0x54,0x54,0x54,0x20,0x04,0x3f,0x44,0x40,0x20,0x3c,0x40,0x40,0x20,0x7c,0x1c,0x20,0x40,0x20,0x1c,0x3c,0x40,0x30,0x40,0x3c,0x44,0x28,0x10,0x28,0x44,0x0c,0x50,0x50,0x50,0x3c,0x44,0x64,0x54,0x4c,0x44,0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x41,0x36,0x08,0x00,0x10,0x08,0x08,0x10,0x08,0x78,0x46,0x41,0x46,0x78])
#outputs a string (up to 16 chars) to any of 8 rows (0 - 7)
def lcd_string(str,row):
page = 0xb0
page = page + row
lcd_send(page,0) #page address
lcd_send(0x11,0) #column address
lcd_send(0x02,0)
for x in range(len(str)):
letter = ord(str[x]) - 0x20
for col in range(5):
lcd_send(Alpha1[(letter*5)+col],1)
lcd_send(0,1)
#outputs a single byte to the display
def lcd_send(cmd,type):
if type:
DC.high()
else:
DC.low()
CS.low()
for x in range(8):
SCLK.low()
SDATA.low()
if cmd & 0x80:
SDATA.high()
SCLK.high()
cmd = cmd << 1
CS.high()
#clear screen
def lcd_cls():
line = 0xb0 #page address variable
for x in range(9):
lcd_send(line,0) #set page address
lcd_send(0x11,0) #set column address
lcd_send(0x02,0)
for i in range(0x60): #write zeros to display RAM
lcd_send(0x00,1)
line += 1
#clear single row
def lcd_row_cls(row):
line = 0xb0 + row
lcd_send(line,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for x in range(0x60):
lcd_send(0x00,1)
#initialize LCD
def lcd_init():
DC.high()
pyb.delay(2)
CS.high()
pyb.delay(2)
lcd_send(0xa6,0) #Display: Normal
lcd_send(0xa3,0) #LCD Bias Settings: 1/7
lcd_send(0xa1,0) #ADC Selection: Reverse
#lcd_send(0xc0,0) #Common Output: Upside Down
lcd_send(0x22,0) #Set the V5 output voltage
lcd_send(0x81,0) #set Electronic Volume - brightness
lcd_send(0x2f,0)
lcd_send(0x2e,0) #Power Controller Set: Booster circuit: ON
#Voltage regulator circuit: ON
#Voltage follower circuit: off
lcd_send(0x2f,0) #Power Controller Set: Voltage follower circuit: ON
#lcd_send(0xe3,0) #Non-Operation Command
#lcd_send(0x40,0) #Set the start line
lcd_send(0xaf,0) #LCD on
lcd_send(0xa4,0) #Display all points: Normal
lcd_cls()
#------------------------------------------------------
# Begin here
#------------------------------------------------------
lcd_init()
lcd_string("Coded by futz: ",0)
lcd_string("Nokia 7110 LCD ",2)
lcd_string("Powered by ",4)
lcd_string("MicroPython ",5)
for x in range(999999):
lcd_string(str(x),7)
pyb.delay(50)
Here is the MicroPython Pyboard code for the simple bouncy line graphic demo above:
from pyb import Pin
CS = Pin('X4',Pin.OUT_PP)
DC = Pin('X3',Pin.OUT_PP)
SCLK = Pin('X2',Pin.OUT_PP)
SDATA = Pin('X1',Pin.OUT_PP)
framebuff1 = [0] * 192
framebuff2 = [0] * 192
framebuff3 = [0] * 192
framebuff4 = [0] * 192
framebuff5 = [0] * 96
def update():
global framebuff1
global framebuff2
global framebuff3
global framebuff4
global framebuff5
count = 0
page = 0xb0
for x in range(2):
lcd_send(page,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for i in range(0x60):
outbyte = framebuff1[count]
count += 1
lcd_send(outbyte,1)
page += 1
count = 0
for x in range(2):
lcd_send(page,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for i in range(0x60):
outbyte = framebuff2[count]
count += 1
lcd_send(outbyte,1)
page += 1
count = 0
for x in range(2):
lcd_send(page,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for i in range(0x60):
outbyte = framebuff3[count]
count += 1
lcd_send(outbyte,1)
page += 1
count = 0
for x in range(2):
lcd_send(page,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for i in range(0x60):
outbyte = framebuff4[count]
count += 1
lcd_send(outbyte,1)
page += 1
count = 0
lcd_send(page,0)
lcd_send(0x11,0)
lcd_send(0x02,0)
for i in range(0x60):
outbyte = framebuff1[count]
count += 1
lcd_send(outbyte,1)
def pixel(x,y,color):
global framebuff1
global framebuff2
global framebuff3
global framebuff4
global framebuff5
div = y >> 3
if div < 2:
offset = div * 96 + x
mbit = y - (div << 3)
temp = 1 << mbit
readtemp = framebuff1[offset]
if color == 0:
mask = temp ^ 0xff
y = readtemp & mask
else:
y = readtemp | temp
framebuff1[offset] = y
elif div < 4:
offset = ((div * 96) - 192) + x
mbit = y - (div << 3)
temp = 1 << mbit
readtemp = framebuff2[offset]
if color == 0:
mask = temp ^ 0xff
y = readtemp & mask
else:
y = readtemp | temp
framebuff2[offset] = y
elif div < 6:
offset = ((div * 96)-384) + x
mbit = y - (div << 3)
temp = 1 << mbit
readtemp = framebuff3[offset]
if color == 0:
mask = temp ^ 0xff
y = readtemp & mask
else:
y = readtemp | temp
framebuff3[offset] = y
elif div < 8:
offset = ((div * 96) - 576) + x
mbit = y - (div << 3)
temp = 1 << mbit
readtemp = framebuff4[offset]
if color == 0:
mask = temp ^ 0xff
y = readtemp & mask
else:
y = readtemp | temp
framebuff4[offset] = y
elif div < 9:
offset = ((div * 96) - 768) + x
mbit = y - (div << 3)
temp = 1 << mbit
readtemp = framebuff5[offset]
if color == 0:
mask = temp ^ 0xff
y = readtemp & mask
else:
y = readtemp | temp
framebuff5[offset] = y
def line(x1,y1,x2,y2,color):
steep = abs(y2-y1) > abs(x2-x1)
if(steep):
temp = x1 #swap x1, y1
x1 = y1
y1 = temp
temp = x2 #swap x2, y2
x2 = y2
y2 = temp
deltax = abs(x2 - x1)
deltay = abs(y2 - y1)
error = 0
deltaerror = deltay
x = x1
y = y1
if x1 < x2:
xstep = 1
else:
xstep = -1
if y1 < y2:
ystep = 1
else:
ystep = -1
if steep:
pixel(y,x,color)
else:
pixel(x,y,color)
while x != x2:
x += xstep
error += deltaerror
if (error << 1) > deltax:
y += ystep
error = error - deltax
if steep:
pixel(y,x,color)
else:
pixel(x,y,color)
#outputs a single byte to the display
#type 1 = ascii character
#type 0 = command
def lcd_send(cmd,type):
if type:
DC.high()
else:
DC.low()
CS.low()
for x in range(8):
SCLK.low()
SDATA.low()
if cmd & 0x80:
SDATA.high()
SCLK.high()
cmd = cmd << 1
CS.high()
#clear screen
def lcd_cls():
line = 0xb0 #page address variable
for x in range(8):
lcd_send(line,0) #set page address
lcd_send(0x11,0) #set column address
lcd_send(0x02,0)
for i in range(0x60): #write zeros to display RAM
lcd_send(0x00,1)
line += 1
#initialize LCD
def lcd_init():
DC.high()
pyb.delay(2)
CS.high()
pyb.delay(2)
lcd_send(0xa6,0) #Display: Normal
lcd_send(0xa3,0) #LCD Bias Settings: 1/7
lcd_send(0xa1,0) #ADC Selection: Reverse
#lcd_send(0xc0,0) #Common Output: Upside Down
lcd_send(0x22,0) #Set the V5 output voltage
lcd_send(0x81,0) #set Electronic Volume - brightness
lcd_send(0x2f,0)
lcd_send(0x2e,0) #Power Controller Set: Booster circuit: ON
#Voltage regulator circuit: ON
#Voltage follower circuit: off
lcd_send(0x2f,0) #Power Controller Set: Voltage follower circuit: ON
#lcd_send(0xe3,0) #Non-Operation Command
#lcd_send(0x40,0) #Set the start line
lcd_send(0xaf,0) #LCD on
lcd_send(0xa4,0) #Display all points: Normal
lcd_cls()
#------------------------------------------------------
# Begin here
#------------------------------------------------------
lcd_init()
x = [3,45,90,10]
y = [2,50,30,10]
px0 = x[0]; py0 = y[0]; px1 = x[1]; py1 = y[1]
px2 = x[2]; py2 = y[2]; px3 = x[3]; py3 = y[3]
dx0 = 3; dy0 = -2; dx1 = -3; dy1 = 3; dx2 = -3; dy2 = 3; dx3 = -3; dy3 = 3
#main loop
while True:
line(px0,py0,px1,py1,0)
line(px1,py1,px2,py2,0)
line(px2,py2,px3,py3,0)
line(px3,py3,px0,py0,0)
line(x[0],y[0],x[1],y[1],1)
line(x[1],y[1],x[2],y[2],1)
line(x[2],y[2],x[3],y[3],1)
line(x[3],y[3],x[0],y[0],1)
update()
px0 = x[0]; py0 = y[0]; px1 = x[1]; py1 = y[1]
px2 = x[2]; py2 = y[2]; px3 = x[3]; py3 = y[3]
x[0] += dx0
if x[0] < 0 or x[0] > 95:
dx0 = -dx0
x[0] = px0
x[0] += dx0
x[1] += dx1
if x[1] < 0 or x[1] > 95:
dx1 = -dx1
x[1] = px1
x[1] += dx1
x[2] += dx2
if x[2] < 0 or x[2] > 95:
dx2 = -dx2
x[2] = px2
x[2] += dx2
x[3] += dx3
if x[3] < 0 or x[3] > 95:
dx3 = -dx3
x[3] = px3
x[3] += dx3
y[0] += dy0
if y[0] < 0 or y[0] > 65:
dy0 = -dy0
y[0] = py0
y[0] += dy0
y[1] += dy1
if y[1] < 0 or y[1] > 65:
dy1 = -dy1
y[1] = py1
y[1] += dy1
y[2] += dy2
if y[2] < 0 or y[2] > 65:
dy2 = -dy2
y[2] = py2
y[2] += dy2
y[3] += dy3
if y[3] < 0 or y[3] > 65:
dy3 = -dy3
y[3] = py3
y[3] += dy3