Today's little project was driving a Nokia 5110 LCD with the Black Pill board. The 5110 phone was introduced in 1998. The display is monochrome and resolution is 84 x 48 pixels. Pretty savage by today's standards. But they're cheap and easy to drive. Good for microcontroller projects.
I had some old bitbang code from the PyBoard that worked fine after changing the pin definitions. Way too easy - hardly a project at all. But it was quite slow and I wasn't happy.
So I rewired for hardware SPI, made some changes in the code and tested. No go. Look carefully and find I have SCK and MOSI reversed. Try again. Nothing. Check everything. I had forgotten to change my pin definitions. Try again. Nothing. SPI polarity is 1 - should be 0 for this device. Try again. Success! Still not terribly fast. I bump baud rate up from 400000 to 4000000. Try again. That's more like it. Still not speedy, but not too bad. Now that's a project!
Hardware SPI source code. Nothin fancy - just a simple demo. (Scroll way down for software SPI source)
# ghmicro.com
# Nokia 5110 LCD - hardware SPI
from pyb import SPI, Pin
spi = SPI(1, SPI.MASTER, baudrate=4000000, polarity=0, phase=0)
RST = Pin('A2',Pin.OUT_PP)
SCE = Pin('A1',Pin.OUT_PP)
DC = Pin('A3',Pin.OUT_PP)
#ascii font - Space 32 ($20) thru DEL 127 ($7f)
Alpha1 = [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]
buff = [0] * 505
def update():
lcd_send(0x40,0) #y address
lcd_send(0x80,0) #x address
for i in range(504):
lcd_send(buff[i],1)
def box(x1,y1,x2,y2,color):
for y in range(y1,y2):
for x in range(x1,x2):
pixel(x,y,color)
update()
def pixel(x,y,color):
mbit = 1
offset = ((y >> 3) * 84) + x
mbit = mbit << (y - ((y >> 3) << 3))
temp = buff[offset]
if color == 0:
mask = mbit ^ 0xff
buff[offset] = temp & mask
else:
buff[offset] = temp | mbit
def lcd_string(str,row): #row must be 0-5
lcd_send(0x40 + row,0) #y address
lcd_send(0x80,0) #x address
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)
def lcd_send(cmd,type):
if type:
DC.high()
else:
DC.low()
SCE.low()
spi.send(cmd)
SCE.high()
def lcd_cls(patt):
for i in range(504):
buff[i] = patt
update()
def lcd_init():
pyb.delay(15)
RST.low()
pyb.delay(20)
RST.high()
lcd_send(0x21,0) #extended instructions enabled
lcd_send(0xb0,0) #set vop (contrast) 0-127
lcd_send(0x04,0) #set temp coefficient
lcd_send(0x13,0) #lcd bias mode 1:48 (try 0x13)
lcd_send(0x20,0)
lcd_send(0x0c,0) #display control - normal mode
lcd_cls(0)
#------------------------------------------------------
# Begin here
#------------------------------------------------------
SCE.high()
RST.high()
lcd_init()
x = 0; y = 0; px = 0; py = 0
dx = 1;dy = 1
lcd_string("Nokia 5110",0)
lcd_string("Coded by futz",1)
lcd_string(" ",2)
lcd_string("Powered by ",3)
lcd_string("MicroPython ",4)
lcd_string("0123456789 ",5)
pyb.delay(5000)
while True:
update()
box(x,y,x + 4,y + 3,1)
box(px,py,px + 4,py + 3,0)
px = x; py = y
x += dx; y += dy
if x < 0 or x > 79:
dx = -dx
x = px
x += dx
if y < 0 or y > 44:
dy = -dy
y = py
y += dy
Software SPI source code. Nothin fancy - just a simple demo.
# ghmicro.com
# Nokia 5110 LCD - software SPI
from pyb import Pin
RST = Pin('A4',Pin.OUT_PP)
SCE = Pin('A3',Pin.OUT_PP)
DC = Pin('A5',Pin.OUT_PP)
DATA = Pin('A6',Pin.OUT_PP)
SCLK = Pin('A7',Pin.OUT_PP)
#ascii font - Space 32 ($20) thru DEL 127 ($7f)
Alpha1 = [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]
buff = [0] * 505
def update():
lcd_send(0x40,0) #y address
lcd_send(0x80,0) #x address
for i in range(504):
lcd_send(buff[i],1)
def box(x1,y1,x2,y2,color):
for y in range(y1,y2):
for x in range(x1,x2):
pixel(x,y,color)
update()
def pixel(x,y,color):
mbit = 1
offset = ((y >> 3) * 84) + x
mbit = mbit << (y - ((y >> 3) << 3))
temp = buff[offset]
if color == 0:
mask = mbit ^ 0xff
buff[offset] = temp & mask
else:
buff[offset] = temp | mbit
def lcd_string(str,row): #row must be 0-5
lcd_send(0x40 + row,0) #y address
lcd_send(0x80,0) #x address
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)
def lcd_send(cmd,type):
if type:
DC.high()
else:
DC.low()
SCE.low()
for x in range(8):
SCLK.low()
DATA.low()
if cmd & 0x80:
DATA.high()
SCLK.high()
cmd = cmd << 1
SCE.high()
def lcd_cls(patt):
for i in range(504):
buff[i] = patt
update()
def lcd_init():
pyb.delay(15)
RST.low()
pyb.delay(20)
RST.high()
lcd_send(0x21,0) #extended instructions enabled
lcd_send(0xb0,0) #set vop (contrast) 0-127
lcd_send(0x04,0) #set temp coefficient
lcd_send(0x13,0) #lcd bias mode 1:48 (try 0x13)
lcd_send(0x20,0)
lcd_send(0x0c,0) #display control - normal mode
lcd_cls(0)
#------------------------------------------------------
# Begin here
#------------------------------------------------------
SCE.high()
SCLK.low()
RST.high()
lcd_init()
x = 0; y = 0; px = 0; py = 0
dx = 1;dy = 1
lcd_string("Nokia 5110",0)
lcd_string("Coded by futz",1)
lcd_string(" ",2)
lcd_string("Powered by ",3)
lcd_string("MicroPython ",4)
lcd_string("0123456789 ",5)
pyb.delay(5000)
while True:
update()
# pixel(px,py,0)
pixel(x,y,1)
# box(px,py,px + 4,py + 3,0)
# box(x,y,x + 4,y + 3,1)
px = x; py = y
x += dx; y += dy
if x < 0 or x > 83:
dx = -dx
x = px
x += dx
if y < 0 or y > 47:
dy = -dy
y = py
y += dy