texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Nokia Pi LCD

Sat Jun 30, 2012 12:03 pm

Hi,
with the help of these links :
http://www.picaxeforum.co.uk/showthread ... cs-display
https://bitbucket.org/webhamster

I have been able to connect up a Nokia 3310 type display to my pi. Whilst webhamsters code works very well and can do so much more than my own, I wanted to 'dumb it down' somewhat in order for a python newbie such as myself help understand how the code works.
The main disadvantage with this approach is that it it very slow. Whether this is because of python, or the way the gpio driver works, I,m not sure.

Code :

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-

import RPi.GPIO as GPIO
import time
import sys
from grafix import *

#gpio's :
SCLK = 24
DIN = 23
DC = 22
RST = 18

font =[
0x7E, 0x11, 0x11, 0x11, 0x7E, # A
0x7F, 0x49, 0x49, 0x49, 0x36, # B
0x3E, 0x41, 0x41, 0x41, 0x22, # C
0x7F, 0x41, 0x41, 0x22, 0x1C, # D
0x7F, 0x49, 0x49, 0x49, 0x41, # E
0x7F, 0x09, 0x09, 0x09, 0x01, # F
0x3E, 0x41, 0x49, 0x49, 0x7A, # G
0x7F, 0x08, 0x08, 0x08, 0x7F, # H
0x00, 0x41, 0x7F, 0x41, 0x00, # I
0x20, 0x40, 0x41, 0x3F, 0x01, # J
0x7F, 0x08, 0x14, 0x22, 0x41, # K
0x7F, 0x40, 0x40, 0x40, 0x40, # L
0x7F, 0x02, 0x0C, 0x02, 0x7F, # M
0x7F, 0x04, 0x08, 0x10, 0x7F, # N
0x3E, 0x41, 0x41, 0x41, 0x3E, # O
0x7F, 0x09, 0x09, 0x09, 0x06, # P
0x3E, 0x41, 0x51, 0x21, 0x5E, # Q
0x7F, 0x09, 0x19, 0x29, 0x46, # R
0x46, 0x49, 0x49, 0x49, 0x31, # S
0x01, 0x01, 0x7F, 0x01, 0x01, # T
0x3F, 0x40, 0x40, 0x40, 0x3F, # U
0x1F, 0x20, 0x40, 0x20, 0x1F, # V
0x3F, 0x40, 0x38, 0x40, 0x3F, # W
0x63, 0x14, 0x08, 0x14, 0x63, # X
0x07, 0x08, 0x70, 0x08, 0x07, # Y
0x61, 0x51, 0x49, 0x45, 0x43, # Z
]

def main():
  begin(0xbc) # contrast - may need tweaking for each display
  gotoxy(28,0)
  text("HELLO")
  gotoxy(8,2)
  text("RASPBERRY PI")
  gotoxy(6,4)
  text("FORUM MEMBERS")

def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
#    print (words[i])
    display_char(words[i])

def display_char(char):
  index=(ord(char)-65)*5
  if ord(char) >=65 and ord(char) <=90:
    for i in range(5):
#      print (index+i)
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters
  elif ord(char)==32:
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)

def setup():
  # set pin directions
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(DIN, GPIO.OUT)
  GPIO.setup(SCLK, GPIO.OUT)
  GPIO.setup(DC, GPIO.OUT)
  GPIO.setup(RST, GPIO.OUT)

def begin(contrast):
  setup()
  # toggle RST low to reset
  GPIO.output(RST, False)
  time.sleep(0.100)
  GPIO.output(RST, True)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()


def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    GPIO.output(DIN, (c & (1 << (7-i))) > 0)
    GPIO.output(SCLK, True)
    GPIO.output(SCLK, False)

def lcd_cmd(c):
#  print ("lcd_cmd sent :",hex(c))
  GPIO.output(DC, False)
  SPI(c)

def lcd_data(c):
#  print ("data sent :",hex(c))
  GPIO.output(DC, True)
  SPI(c)

if __name__ == "__main__":
  main()
nokia_lcd.jpg
nokia_lcd.jpg (49.7 KiB) Viewed 37061 times
You'll need to install the rpi.gpio library and run the code as root to run. You may also need to play around with the contrast setting as these seem to change from display to display.

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Sat Jul 07, 2012 8:30 am

Update.
With the additional GPIO_OUT class developed by Kevin Casabon, the setup and initial message screen time has gone down from 14.2 seconds to 1.25 seconds!

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# using the additional RPi.GPIO class developed by Kevin Casabon

import RPi.GPIO as GPIO

from RPi.GPIO import *  # ideally this should just be tacked at the bottom of the GPIO module itself
from RPi.GPIO import _GetValidId, _ExportedIds

import time
import sys
from grafix import *

#gpio's :
SCLK = 24 # gpio 24, pin 18
DIN =  23 # gpio 23, pin 16
DC =  22 # gpio 22, pin 15
RST =  18 # gpio 18, pin 12
LED = 17 # gpio 17, pin 11

font =[
0x7E, 0x11, 0x11, 0x11, 0x7E, # A
0x7F, 0x49, 0x49, 0x49, 0x36, # B
0x3E, 0x41, 0x41, 0x41, 0x22, # C
0x7F, 0x41, 0x41, 0x22, 0x1C, # D
0x7F, 0x49, 0x49, 0x49, 0x41, # E
0x7F, 0x09, 0x09, 0x09, 0x01, # F
0x3E, 0x41, 0x49, 0x49, 0x7A, # G
0x7F, 0x08, 0x08, 0x08, 0x7F, # H
0x00, 0x41, 0x7F, 0x41, 0x00, # I
0x20, 0x40, 0x41, 0x3F, 0x01, # J
0x7F, 0x08, 0x14, 0x22, 0x41, # K
0x7F, 0x40, 0x40, 0x40, 0x40, # L
0x7F, 0x02, 0x0C, 0x02, 0x7F, # M
0x7F, 0x04, 0x08, 0x10, 0x7F, # N
0x3E, 0x41, 0x41, 0x41, 0x3E, # O
0x7F, 0x09, 0x09, 0x09, 0x06, # P
0x3E, 0x41, 0x51, 0x21, 0x5E, # Q
0x7F, 0x09, 0x19, 0x29, 0x46, # R
0x46, 0x49, 0x49, 0x49, 0x31, # S
0x01, 0x01, 0x7F, 0x01, 0x01, # T
0x3F, 0x40, 0x40, 0x40, 0x3F, # U
0x1F, 0x20, 0x40, 0x20, 0x1F, # V
0x3F, 0x40, 0x38, 0x40, 0x3F, # W
0x63, 0x14, 0x08, 0x14, 0x63, # X
0x07, 0x08, 0x70, 0x08, 0x07, # Y
0x61, 0x51, 0x49, 0x45, 0x43, # Z
]

class GPIO_OUT:
    """To attain higher write performance on the RPi GPIO (up to about 5x), keep the device open as a file
instead of opening/closing it each time you write to it.  Contributed by Kevin Cazabon"""

    def __init__(self, channel):
        self.channel = channel
        setup(self.channel, OUT)

        self.id = _GetValidId(self.channel)

        if self.id not in _ExportedIds or _ExportedIds[self.id] != OUT:
            raise WrongDirectionException

        self.f = open('/sys/class/gpio/gpio%s/value'%self.id, 'w')

        # try to guarantee that we don't leave the file open by mistake
        atexit.register(self.__del__)

    def __del__(self):
        if not self.f.closed:
            self.f.flush()
            self.f.close()

    def output(self, value):
        self.f.write('1' if value else '0')
        self.f.flush()

    def close(self):
        self.__del__()

def main():
  start = time.time()
  begin(0xbc) # contrast - may need tweaking for each display
  gotoxy(28,0)
  text("HELLO")
  gotoxy(8,2)
  text("RASPBERRY PI")
  gotoxy(6,4)
  text("FORUM MEMBERS")
  finish = time.time()
  print ("Total time : ",finish - start)
  print

def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
#    print (words[i])
    display_char(words[i])

def display_char(char):
  index=(ord(char)-65)*5
  if ord(char) >=65 and ord(char) <=90:
    for i in range(5):
#      print (index+i)
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters
  elif ord(char)==32:
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)
  print"cls OK"

def lcdsetup():
  # set pin directions
  GPIO.setmode(GPIO.BCM)
  GPIO.setup(DIN, GPIO.OUT)
  GPIO.setup(SCLK, GPIO.OUT)
  GPIO.setup(DC, GPIO.OUT)
  GPIO.setup(RST, GPIO.OUT)
  global sclkGPIO
  sclkGPIO = GPIO_OUT(SCLK)
  global dinGPIO
  dinGPIO = GPIO_OUT(DIN)
  global rstGPIO
  rstGPIO = GPIO_OUT(RST)
  global dcGPIO
  dcGPIO = GPIO_OUT(DC)
 
def begin(contrast):
  lcdsetup()
  # toggle RST low to reset
  rstGPIO.output(False)
  time.sleep(0.100)
  rstGPIO.output(True)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()


def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    dinGPIO.output((c & (1 << (7-i))) > 0)
    sclkGPIO.output(True)
    sclkGPIO.output(False)

def lcd_cmd(c):
#  print ("lcd_cmd sent :",hex(c))
  dcGPIO.output(False)
  SPI(c)

def lcd_data(c):
#  print ("data sent :",hex(c))
  dcGPIO.output(True)
  SPI(c)

if __name__ == "__main__":
  main()
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Sat Jul 07, 2012 3:05 pm

Now using wiringpi - the time has gone down to 0.322 seconds :D

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# using wiringPI GPIO method

import wiringpi

import time
import sys
from grafix import *

#gpio's :
SCLK = 24 # gpio pin 18
DIN =  23 # gpio pin 16
DC =  22 # gpio pin 15
RST =  18 # gpio pin 12
LED = 17 # gpio pin 11

font =[
0x7E, 0x11, 0x11, 0x11, 0x7E, # A
0x7F, 0x49, 0x49, 0x49, 0x36, # B
0x3E, 0x41, 0x41, 0x41, 0x22, # C
0x7F, 0x41, 0x41, 0x22, 0x1C, # D
0x7F, 0x49, 0x49, 0x49, 0x41, # E
0x7F, 0x09, 0x09, 0x09, 0x01, # F
0x3E, 0x41, 0x49, 0x49, 0x7A, # G
0x7F, 0x08, 0x08, 0x08, 0x7F, # H
0x00, 0x41, 0x7F, 0x41, 0x00, # I
0x20, 0x40, 0x41, 0x3F, 0x01, # J
0x7F, 0x08, 0x14, 0x22, 0x41, # K
0x7F, 0x40, 0x40, 0x40, 0x40, # L
0x7F, 0x02, 0x0C, 0x02, 0x7F, # M
0x7F, 0x04, 0x08, 0x10, 0x7F, # N
0x3E, 0x41, 0x41, 0x41, 0x3E, # O
0x7F, 0x09, 0x09, 0x09, 0x06, # P
0x3E, 0x41, 0x51, 0x21, 0x5E, # Q
0x7F, 0x09, 0x19, 0x29, 0x46, # R
0x46, 0x49, 0x49, 0x49, 0x31, # S
0x01, 0x01, 0x7F, 0x01, 0x01, # T
0x3F, 0x40, 0x40, 0x40, 0x3F, # U
0x1F, 0x20, 0x40, 0x20, 0x1F, # V
0x3F, 0x40, 0x38, 0x40, 0x3F, # W
0x63, 0x14, 0x08, 0x14, 0x63, # X
0x07, 0x08, 0x70, 0x08, 0x07, # Y
0x61, 0x51, 0x49, 0x45, 0x43, # Z
]

def main():
  start = time.time()
  begin(0xbc) # contrast - may need tweaking for each display
  gotoxy(28,0)
  text("HELLO")
  gotoxy(8,2)
  text("RASPBERRY PI")
  gotoxy(6,4)
  text("FORUM MEMBERS")
  finish = time.time()
  print ("Total time : ",finish - start)
  print


def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
#    print (words[i])
    display_char(words[i])

def display_char(char):
  index=(ord(char)-65)*5
  if ord(char) >=65 and ord(char) <=90:
    for i in range(5):
#      print (index+i)
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters
  elif ord(char)==32:
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)

def setup():
  # set pin directions
  wiringpi.wiringPiSetup()
  wiringpi.wiringPiGpioMode(1)
#  GPIO.setmode(GPIO.BCM)
  wiringpi.pinMode(DIN, 1)
  wiringpi.pinMode(SCLK, 1)
  wiringpi.pinMode(DC, 1)
  wiringpi.pinMode(RST, 1)
  wiringpi.pinMode(LED, 1)

def begin(contrast):
  setup()
  # toggle RST low to reset
  wiringpi.digitalWrite(RST, 0)
  wiringpi.digitalWrite(LED, 0)
  time.sleep(0.100)
  wiringpi.digitalWrite(RST, 1)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()


def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    wiringpi.digitalWrite(DIN,((c & (1 << (7-i))) > 0))
    wiringpi.digitalWrite(SCLK, 1)
    wiringpi.digitalWrite(SCLK, 0)

def lcd_cmd(c):
#  print ("lcd_cmd sent :",hex(c))
  wiringpi.digitalWrite(DC, 0)
  SPI(c)

def lcd_data(c):
#  print ("data sent :",hex(c))
  wiringpi.digitalWrite(DC, 1)
  SPI(c)

if __name__ == "__main__":
  main()
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

MadCow42
Posts: 106
Joined: Sun Jul 01, 2012 12:48 am

Re: Nokia Pi LCD

Sat Jul 07, 2012 3:23 pm

texy wrote:Now using wiringpi - the time has gone down to 0.322 seconds :D
Awesome... I guess that'll have to be the next step for me too :). I'm doing 600 steps/second with GPIO_OUT_class which is more than I need, but lower overhead time will give me more accuracy, and possibly the speed to drive everything off one 8-bit shift register (instead of 2).

Kevin.

User avatar
jacksonliam
Posts: 181
Joined: Tue Feb 07, 2012 10:09 pm

Re: Nokia Pi LCD

Sat Jul 07, 2012 3:24 pm

Great work! Might try something like this myself If I can find the right display!

Fanjita
Posts: 17
Joined: Thu Jun 07, 2012 9:31 am
Location: Edinburgh, UK

Re: Nokia Pi LCD

Mon Jul 09, 2012 9:25 am

Nice work.

These displays are really easy to pick up cheaply, too - e.g. £7 here. I picked one up a while back, with a vague plan to do something with it : it might make a nice little status display for my Pi.

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Mon Jul 09, 2012 10:45 am

If you are prepared to wait, they can be purchased from china via ebay even cheaper. If there is enough interest, I will design a mounting PCB and produce an LCD 'shield', maybe.

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

gadgetoid
Posts: 168
Joined: Wed Mar 07, 2012 9:58 pm

Re: Nokia Pi LCD

Mon Jul 09, 2012 1:11 pm

I just bought one from Hobbytronics. I've given up waiting for the negative ST graphics LCD that I wanted from Adafruit. £7 isn't a bad pop for a bit of LCD driving fun.

Will be interesting trying to get this to work in Ruby with WiringPiGem!

I should probably learn Python, though, as it seems to garner far more interest and it's clear I need to write a better wrapper for WiringPi to handle all the constants.

POSitality
Posts: 23
Joined: Thu Aug 02, 2012 8:31 pm
Location: London

Re: Nokia Pi LCD

Fri Aug 10, 2012 12:20 am

I just got one of these LCDs today so I have a few tips, i.e. these things tripped me up! Noob answers probably but you all seem friendly :)

First, do the usual apt-get update etc. and make sure you have python-dev and git.

Get the wiringPi and wiringPi-Python from Gadgetoid's git has I think Gordon as tweaked the wiringPi on his site enough to break wiringPi-Python :( The associated instructions are simple enough although I needed to move the compiled wiringPi into the wiringPi-Python directory.

Although no one mentioned it the device select / device address / SCE / whatever it is on your board needs to be jumpered to ground. You could set up an extra GPIO line I guess, maybe if you wanted two or three of these running (dunno how many lines we have left!)

BTW... we are emulating SPI via the GPIO lines on the RPi. Is this stuff easier or harder using the RPi's own SPI outputs?

Onto the fun times! I erm... couldn't be bothered to walk from one side of the room to the other where my RPi is plugged directly into my Internet router so I... um... hooked a web cam up to the RPi and used guvcview to watch the LCD from my main machine (running PuTTY + Xming.) Total abuse of technology :D

So I tried the new wiringPi code above and I'm still wondering what the "from grafix" is about otherwise it worked. Time for some mods! I extended the font definitions to do full ASCII (32 to 127) and added some convenience functions:

gotorc( row, column) - as this display is geared towards 6 rows by 14 columns I added this.
centre_word(row, word) - if the word(s) are under 14 characters, a quick and dirty centred message.
init() - as an alternative to begin(0xBC) as I will forget that value by tomorrow.
define_custom(byte0,byte1,byte2,byte3,byte4) - overwrite ASCII code 127 with your own character.
show_custom() - shortcut to display_char(chr(127)) as I have no idea if this 127 is on my keyboard.
restore_custom() - to get back the "undefined" box character.
alt_custom() - I have this set to '£' but predefine this to your favourite currency.
pi_custom() - munchkin sized raspberry :D

and now the bad boy...

load_bitmap(file) - load a standard (almost!) Windoze bitmap. Some slight caveats of course!

To keep it simple on the RPi you do need to help out a little. As the display is 84x48 that's a horrible width for a bitmap (needs to be on a byte boundary like 92.) Also the LCD memory is stored as vertical bytes, a bit like a dot matrix printer; left to right bands of vertical bytes. So, I suggest you create your 84x48 bitmap, save it as monochrome bitmap and then rotate it 90 degrees to the left to get a 48x84 portrait bitmap. I then fired up a hex editor to find the data offset (0x3E, YMMV) which is probably in the header somewhere anyway but I didn't get round to checking. Anyway, 48 is on a byte boundary and the piccy is now almost stored like the LCD's memory. Two problems solved for the price of one :)

Well, it couldn't be that simple now we have to jump though the LSB to MSB swapping hoop :( I thought about an algorithm to do this and felt that thinking by this time was a no-no. I copied over a code snippet from some 'C' website which just used a lookup table and formatted it for Python. Bada-bing!

And while I was on a roll I created an evil twin, muhahahaha!

load_bitmap_reverse(file) - simple XOR as I never looked up the LCD command for reverse display.

Drum roll for the code:

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# using wiringPI GPIO method

import wiringpi

import time
import sys

ROWS = 6
COLUMNS = 14
ON = 1
OFF = 0

#gpio's :
SCLK = 24 # gpio pin 18
DIN =  23 # gpio pin 16
DC =  22 # gpio pin 15
RST =  18 # gpio pin 12
LED = 17 # gpio pin 11

font =[
0x00, 0x00, 0x00, 0x00, 0x00, # 20  
0x00, 0x00, 0x5f, 0x00, 0x00, # 21 !
0x00, 0x07, 0x00, 0x07, 0x00, # 22 "
0x14, 0x7f, 0x14, 0x7f, 0x14, # 23 #
0x24, 0x2a, 0x7f, 0x2a, 0x12, # 24 $
0x23, 0x13, 0x08, 0x64, 0x62, # 25 %
0x36, 0x49, 0x55, 0x22, 0x50, # 26 &
0x00, 0x05, 0x03, 0x00, 0x00, # 27 '
0x00, 0x1c, 0x22, 0x41, 0x00, # 28 (
0x00, 0x41, 0x22, 0x1c, 0x00, # 29 )
0x14, 0x08, 0x3e, 0x08, 0x14, # 2a *
0x08, 0x08, 0x3e, 0x08, 0x08, # 2b +
0x00, 0x50, 0x30, 0x00, 0x00, # 2c ,
0x08, 0x08, 0x08, 0x08, 0x08, # 2d -
0x00, 0x60, 0x60, 0x00, 0x00, # 2e .
0x20, 0x10, 0x08, 0x04, 0x02, # 2f /
0x3e, 0x51, 0x49, 0x45, 0x3e, # 30 0
0x00, 0x42, 0x7f, 0x40, 0x00, # 31 1
0x42, 0x61, 0x51, 0x49, 0x46, # 32 2
0x21, 0x41, 0x45, 0x4b, 0x31, # 33 3
0x18, 0x14, 0x12, 0x7f, 0x10, # 34 4
0x27, 0x45, 0x45, 0x45, 0x39, # 35 5
0x3c, 0x4a, 0x49, 0x49, 0x30, # 36 6
0x01, 0x71, 0x09, 0x05, 0x03, # 37 7
0x36, 0x49, 0x49, 0x49, 0x36, # 38 8
0x06, 0x49, 0x49, 0x29, 0x1e, # 39 9
0x00, 0x36, 0x36, 0x00, 0x00, # 3a :
0x00, 0x56, 0x36, 0x00, 0x00, # 3b ;
0x08, 0x14, 0x22, 0x41, 0x00, # 3c <
0x14, 0x14, 0x14, 0x14, 0x14, # 3d =
0x00, 0x41, 0x22, 0x14, 0x08, # 3e >
0x02, 0x01, 0x51, 0x09, 0x06, # 3f ?
0x32, 0x49, 0x79, 0x41, 0x3e, # 40 @
0x7e, 0x11, 0x11, 0x11, 0x7e, # 41 A
0x7f, 0x49, 0x49, 0x49, 0x36, # 42 B
0x3e, 0x41, 0x41, 0x41, 0x22, # 43 C
0x7f, 0x41, 0x41, 0x22, 0x1c, # 44 D
0x7f, 0x49, 0x49, 0x49, 0x41, # 45 E
0x7f, 0x09, 0x09, 0x09, 0x01, # 46 F
0x3e, 0x41, 0x49, 0x49, 0x7a, # 47 G
0x7f, 0x08, 0x08, 0x08, 0x7f, # 48 H
0x00, 0x41, 0x7f, 0x41, 0x00, # 49 I
0x20, 0x40, 0x41, 0x3f, 0x01, # 4a J
0x7f, 0x08, 0x14, 0x22, 0x41, # 4b K
0x7f, 0x40, 0x40, 0x40, 0x40, # 4c L
0x7f, 0x02, 0x0c, 0x02, 0x7f, # 4d M
0x7f, 0x04, 0x08, 0x10, 0x7f, # 4e N
0x3e, 0x41, 0x41, 0x41, 0x3e, # 4f O
0x7f, 0x09, 0x09, 0x09, 0x06, # 50 P
0x3e, 0x41, 0x51, 0x21, 0x5e, # 51 Q
0x7f, 0x09, 0x19, 0x29, 0x46, # 52 R
0x46, 0x49, 0x49, 0x49, 0x31, # 53 S
0x01, 0x01, 0x7f, 0x01, 0x01, # 54 T
0x3f, 0x40, 0x40, 0x40, 0x3f, # 55 U
0x1f, 0x20, 0x40, 0x20, 0x1f, # 56 V
0x3f, 0x40, 0x38, 0x40, 0x3f, # 57 W
0x63, 0x14, 0x08, 0x14, 0x63, # 58 X
0x07, 0x08, 0x70, 0x08, 0x07, # 59 Y
0x61, 0x51, 0x49, 0x45, 0x43, # 5a Z
0x00, 0x7f, 0x41, 0x41, 0x00, # 5b [
0x02, 0x04, 0x08, 0x10, 0x20, # 5c \
0x00, 0x41, 0x41, 0x7f, 0x00, # 5d ]
0x04, 0x02, 0x01, 0x02, 0x04, # 5e ^
0x40, 0x40, 0x40, 0x40, 0x40, # 5f _
0x00, 0x01, 0x02, 0x04, 0x00, # 60 `
0x20, 0x54, 0x54, 0x54, 0x78, # 61 a
0x7f, 0x48, 0x44, 0x44, 0x38, # 62 b
0x38, 0x44, 0x44, 0x44, 0x20, # 63 c
0x38, 0x44, 0x44, 0x48, 0x7f, # 64 d
0x38, 0x54, 0x54, 0x54, 0x18, # 65 e
0x08, 0x7e, 0x09, 0x01, 0x02, # 66 f
0x0c, 0x52, 0x52, 0x52, 0x3e, # 67 g
0x7f, 0x08, 0x04, 0x04, 0x78, # 68 h
0x00, 0x44, 0x7d, 0x40, 0x00, # 69 i
0x20, 0x40, 0x44, 0x3d, 0x00, # 6a j 
0x7f, 0x10, 0x28, 0x44, 0x00, # 6b k
0x00, 0x41, 0x7f, 0x40, 0x00, # 6c l
0x7c, 0x04, 0x18, 0x04, 0x78, # 6d m
0x7c, 0x08, 0x04, 0x04, 0x78, # 6e n
0x38, 0x44, 0x44, 0x44, 0x38, # 6f o
0x7c, 0x14, 0x14, 0x14, 0x08, # 70 p
0x08, 0x14, 0x14, 0x18, 0x7c, # 71 q
0x7c, 0x08, 0x04, 0x04, 0x08, # 72 r
0x48, 0x54, 0x54, 0x54, 0x20, # 73 s
0x04, 0x3f, 0x44, 0x40, 0x20, # 74 t
0x3c, 0x40, 0x40, 0x20, 0x7c, # 75 u
0x1c, 0x20, 0x40, 0x20, 0x1c, # 76 v
0x3c, 0x40, 0x30, 0x40, 0x3c, # 77 w
0x44, 0x28, 0x10, 0x28, 0x44, # 78 x
0x0c, 0x50, 0x50, 0x50, 0x3c, # 79 y
0x44, 0x64, 0x54, 0x4c, 0x44, # 7a z
0x00, 0x08, 0x36, 0x41, 0x00, # 7b {
0x00, 0x00, 0x7f, 0x00, 0x00, # 7c |
0x00, 0x41, 0x36, 0x08, 0x00, # 7d }
0x10, 0x08, 0x08, 0x10, 0x08, # 7e ~
0x00, 0x7E, 0x42, 0x42, 0x7E, # 7f CUSTOM
]

bitreverse =[
  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
]

def main():
  print ("LCD Display Test: ASCII 32 to 116")
  start = time.time()
  init()
  led(1)
  for i in range(32,116):
    display_char(chr(i))
  finish = time.time()
  print ("Init, LED on, 84 chars, total time = ", finish - start)
  print

def init():
  begin(0xbc)

def load_bitmap(file):
  gotoxy(0,0)
  f = open(file,'r')
  for x in range(6):
    for y in range(84):
      f.seek(0x3e + y*8 + x)
      b = ord(f.read(1))
      rev = bitreverse[b]
      lcd_data(rev)
  f.close()

def load_bitmap_reverse(file):
  gotoxy(0,0)
  f = open(file,'r')
  for x in range(6):
    for y in range(84):
      f.seek(0x3e + y*8 + x)
      b = ord(f.read(1))
      rev = bitreverse[b]
      rev = rev ^ 0xFF
      lcd_data(rev)
  f.close()

def show_custom():
  display_char(chr(127))

def define_custom(b0,b1,b2,b3,b4):
  font[475] = b0
  font[476] = b1
  font[477] = b2
  font[478] = b3
  font[479] = b4

def restore_custom():
  font[475] = 0x00
  font[476] = 0x7E
  font[477] = 0x42
  font[478] = 0x42
  font[479] = 0x7E

def alt_custom():
  font[475] = 0x00
  font[476] = 0x50
  font[477] = 0x3C
  font[478] = 0x52
  font[479] = 0x44

def pi_custom():
  font[475] = 0x19
  font[476] = 0x25
  font[477] = 0x5A
  font[478] = 0x25
  font[479] = 0x19

def centre_word(r, word):
  l = len(word)
  c = (COLUMNS - l) / 2
  if c <= 0:
    c = 0
  gotorc(r,c)
  text(word)

def gotorc(r,c):
  lcd_cmd(c*6+128)
  lcd_cmd(r+64)

def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
    display_char(words[i])

def display_char(char):
  index=(ord(char)-32)*5
  if ord(char) >=32 and ord(char) <=127:
    for i in range(5):
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)

def setup():
  # set pin directions
  wiringpi.wiringPiSetup()
  wiringpi.wiringPiGpioMode(ON)
#  GPIO.setmode(GPIO.BCM)
  wiringpi.pinMode(DIN, ON)
  wiringpi.pinMode(SCLK, ON)
  wiringpi.pinMode(DC, ON)
  wiringpi.pinMode(RST, ON)
  wiringpi.pinMode(LED, ON)

def begin(contrast):
  setup()
  # toggle RST low to reset
  wiringpi.digitalWrite(RST, OFF)
  wiringpi.digitalWrite(LED, OFF)
  time.sleep(0.100)
  wiringpi.digitalWrite(RST, ON)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()

def led(led_value):
  wiringpi.digitalWrite(LED, led_value)

def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    wiringpi.digitalWrite(DIN,((c & (1 << (7-i))) > 0))
    wiringpi.digitalWrite(SCLK, ON)
    wiringpi.digitalWrite(SCLK, OFF)

def lcd_cmd(c):
  wiringpi.digitalWrite(DC, OFF)
  SPI(c)

def lcd_data(c):
  wiringpi.digitalWrite(DC, ON)
  SPI(c)

if __name__ == "__main__":
  main()
Huh... I would attach the bitmap too but whatever I call it "The extension is not allowed" comes up :( Any ideas?

Have fun guys!

Regards,

Andy

kb9jlo
Posts: 1
Joined: Thu Aug 02, 2012 5:56 pm

Re: Nokia Pi LCD

Fri Aug 10, 2012 2:29 am

I just got one off the watery auction site for < $4 US.

I'm going to give it a go. I'm having a blast with the Pi. No, it can't do everything in the world but then it only costs $35 too.

--Dan

recantha
Posts: 209
Joined: Mon Jun 25, 2012 10:41 am

Re: Nokia Pi LCD

Fri Aug 10, 2012 5:39 am

Texy - you're my hero! ;-)
I have *ahem* quite a few old phones. I just don't seem to be able to throw things away!
My Raspberry Pi blog with all my latest projects and links to articles
http://raspberrypipod.blogspot.com. +++ Current project: PiPodTricorder - lots of sensors, lots of mini-displays, breadboarding, bit of programming.

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Fri Aug 10, 2012 6:15 am

recantha wrote:Texy - you're my hero! ;-)
I have *ahem* quite a few old phones. I just don't seem to be able to throw things away!
The problem with using the display designed to be used in (or taken out) of a mobile phone is the way they are connected to the rest of the electronics of the phone - they don't lend themselves to soldering wires usually, but push fit onto the main PCB. However you can get pcb mounted screens from various sources that are designed to be wired to, which makes things much easier for homebrew electronic tinkerers like ourselves.

T.
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Fri Aug 10, 2012 6:20 am

POSitality wrote:I just got one of these LCDs today so I have a few tips, i.e. these things tripped me up! Noob answers probably but you all seem friendly :)

First, do the usual apt-get update etc. and make sure you have python-dev and git.

Get the wiringPi and wiringPi-Python from Gadgetoid's git has I think Gordon as tweaked the wiringPi on his site enough to break wiringPi-Python :( The associated instructions are simple enough although I needed to move the compiled wiringPi into the wiringPi-Python directory.

Although no one mentioned it the device select / device address / SCE / whatever it is on your board needs to be jumpered to ground. You could set up an extra GPIO line I guess, maybe if you wanted two or three of these running (dunno how many lines we have left!)

BTW... we are emulating SPI via the GPIO lines on the RPi. Is this stuff easier or harder using the RPi's own SPI outputs?

Onto the fun times! I erm... couldn't be bothered to walk from one side of the room to the other where my RPi is plugged directly into my Internet router so I... um... hooked a web cam up to the RPi and used guvcview to watch the LCD from my main machine (running PuTTY + Xming.) Total abuse of technology :D

So I tried the new wiringPi code above and I'm still wondering what the "from grafix" is about otherwise it worked. Time for some mods! I extended the font definitions to do full ASCII (32 to 127) and added some convenience functions:

gotorc( row, column) - as this display is geared towards 6 rows by 14 columns I added this.
centre_word(row, word) - if the word(s) are under 14 characters, a quick and dirty centred message.
init() - as an alternative to begin(0xBC) as I will forget that value by tomorrow.
define_custom(byte0,byte1,byte2,byte3,byte4) - overwrite ASCII code 127 with your own character.
show_custom() - shortcut to display_char(chr(127)) as I have no idea if this 127 is on my keyboard.
restore_custom() - to get back the "undefined" box character.
alt_custom() - I have this set to '£' but predefine this to your favourite currency.
pi_custom() - munchkin sized raspberry :D

and now the bad boy...

load_bitmap(file) - load a standard (almost!) Windoze bitmap. Some slight caveats of course!

To keep it simple on the RPi you do need to help out a little. As the display is 84x48 that's a horrible width for a bitmap (needs to be on a byte boundary like 92.) Also the LCD memory is stored as vertical bytes, a bit like a dot matrix printer; left to right bands of vertical bytes. So, I suggest you create your 84x48 bitmap, save it as monochrome bitmap and then rotate it 90 degrees to the left to get a 48x84 portrait bitmap. I then fired up a hex editor to find the data offset (0x3E, YMMV) which is probably in the header somewhere anyway but I didn't get round to checking. Anyway, 48 is on a byte boundary and the piccy is now almost stored like the LCD's memory. Two problems solved for the price of one :)

Well, it couldn't be that simple now we have to jump though the LSB to MSB swapping hoop :( I thought about an algorithm to do this and felt that thinking by this time was a no-no. I copied over a code snippet from some 'C' website which just used a lookup table and formatted it for Python. Bada-bing!

And while I was on a roll I created an evil twin, muhahahaha!

load_bitmap_reverse(file) - simple XOR as I never looked up the LCD command for reverse display.

Drum roll for the code:

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# using wiringPI GPIO method

import wiringpi

import time
import sys

ROWS = 6
COLUMNS = 14
ON = 1
OFF = 0

#gpio's :
SCLK = 24 # gpio pin 18
DIN =  23 # gpio pin 16
DC =  22 # gpio pin 15
RST =  18 # gpio pin 12
LED = 17 # gpio pin 11

font =[
0x00, 0x00, 0x00, 0x00, 0x00, # 20  
0x00, 0x00, 0x5f, 0x00, 0x00, # 21 !
0x00, 0x07, 0x00, 0x07, 0x00, # 22 "
0x14, 0x7f, 0x14, 0x7f, 0x14, # 23 #
0x24, 0x2a, 0x7f, 0x2a, 0x12, # 24 $
0x23, 0x13, 0x08, 0x64, 0x62, # 25 %
0x36, 0x49, 0x55, 0x22, 0x50, # 26 &
0x00, 0x05, 0x03, 0x00, 0x00, # 27 '
0x00, 0x1c, 0x22, 0x41, 0x00, # 28 (
0x00, 0x41, 0x22, 0x1c, 0x00, # 29 )
0x14, 0x08, 0x3e, 0x08, 0x14, # 2a *
0x08, 0x08, 0x3e, 0x08, 0x08, # 2b +
0x00, 0x50, 0x30, 0x00, 0x00, # 2c ,
0x08, 0x08, 0x08, 0x08, 0x08, # 2d -
0x00, 0x60, 0x60, 0x00, 0x00, # 2e .
0x20, 0x10, 0x08, 0x04, 0x02, # 2f /
0x3e, 0x51, 0x49, 0x45, 0x3e, # 30 0
0x00, 0x42, 0x7f, 0x40, 0x00, # 31 1
0x42, 0x61, 0x51, 0x49, 0x46, # 32 2
0x21, 0x41, 0x45, 0x4b, 0x31, # 33 3
0x18, 0x14, 0x12, 0x7f, 0x10, # 34 4
0x27, 0x45, 0x45, 0x45, 0x39, # 35 5
0x3c, 0x4a, 0x49, 0x49, 0x30, # 36 6
0x01, 0x71, 0x09, 0x05, 0x03, # 37 7
0x36, 0x49, 0x49, 0x49, 0x36, # 38 8
0x06, 0x49, 0x49, 0x29, 0x1e, # 39 9
0x00, 0x36, 0x36, 0x00, 0x00, # 3a :
0x00, 0x56, 0x36, 0x00, 0x00, # 3b ;
0x08, 0x14, 0x22, 0x41, 0x00, # 3c <
0x14, 0x14, 0x14, 0x14, 0x14, # 3d =
0x00, 0x41, 0x22, 0x14, 0x08, # 3e >
0x02, 0x01, 0x51, 0x09, 0x06, # 3f ?
0x32, 0x49, 0x79, 0x41, 0x3e, # 40 @
0x7e, 0x11, 0x11, 0x11, 0x7e, # 41 A
0x7f, 0x49, 0x49, 0x49, 0x36, # 42 B
0x3e, 0x41, 0x41, 0x41, 0x22, # 43 C
0x7f, 0x41, 0x41, 0x22, 0x1c, # 44 D
0x7f, 0x49, 0x49, 0x49, 0x41, # 45 E
0x7f, 0x09, 0x09, 0x09, 0x01, # 46 F
0x3e, 0x41, 0x49, 0x49, 0x7a, # 47 G
0x7f, 0x08, 0x08, 0x08, 0x7f, # 48 H
0x00, 0x41, 0x7f, 0x41, 0x00, # 49 I
0x20, 0x40, 0x41, 0x3f, 0x01, # 4a J
0x7f, 0x08, 0x14, 0x22, 0x41, # 4b K
0x7f, 0x40, 0x40, 0x40, 0x40, # 4c L
0x7f, 0x02, 0x0c, 0x02, 0x7f, # 4d M
0x7f, 0x04, 0x08, 0x10, 0x7f, # 4e N
0x3e, 0x41, 0x41, 0x41, 0x3e, # 4f O
0x7f, 0x09, 0x09, 0x09, 0x06, # 50 P
0x3e, 0x41, 0x51, 0x21, 0x5e, # 51 Q
0x7f, 0x09, 0x19, 0x29, 0x46, # 52 R
0x46, 0x49, 0x49, 0x49, 0x31, # 53 S
0x01, 0x01, 0x7f, 0x01, 0x01, # 54 T
0x3f, 0x40, 0x40, 0x40, 0x3f, # 55 U
0x1f, 0x20, 0x40, 0x20, 0x1f, # 56 V
0x3f, 0x40, 0x38, 0x40, 0x3f, # 57 W
0x63, 0x14, 0x08, 0x14, 0x63, # 58 X
0x07, 0x08, 0x70, 0x08, 0x07, # 59 Y
0x61, 0x51, 0x49, 0x45, 0x43, # 5a Z
0x00, 0x7f, 0x41, 0x41, 0x00, # 5b [
0x02, 0x04, 0x08, 0x10, 0x20, # 5c \
0x00, 0x41, 0x41, 0x7f, 0x00, # 5d ]
0x04, 0x02, 0x01, 0x02, 0x04, # 5e ^
0x40, 0x40, 0x40, 0x40, 0x40, # 5f _
0x00, 0x01, 0x02, 0x04, 0x00, # 60 `
0x20, 0x54, 0x54, 0x54, 0x78, # 61 a
0x7f, 0x48, 0x44, 0x44, 0x38, # 62 b
0x38, 0x44, 0x44, 0x44, 0x20, # 63 c
0x38, 0x44, 0x44, 0x48, 0x7f, # 64 d
0x38, 0x54, 0x54, 0x54, 0x18, # 65 e
0x08, 0x7e, 0x09, 0x01, 0x02, # 66 f
0x0c, 0x52, 0x52, 0x52, 0x3e, # 67 g
0x7f, 0x08, 0x04, 0x04, 0x78, # 68 h
0x00, 0x44, 0x7d, 0x40, 0x00, # 69 i
0x20, 0x40, 0x44, 0x3d, 0x00, # 6a j 
0x7f, 0x10, 0x28, 0x44, 0x00, # 6b k
0x00, 0x41, 0x7f, 0x40, 0x00, # 6c l
0x7c, 0x04, 0x18, 0x04, 0x78, # 6d m
0x7c, 0x08, 0x04, 0x04, 0x78, # 6e n
0x38, 0x44, 0x44, 0x44, 0x38, # 6f o
0x7c, 0x14, 0x14, 0x14, 0x08, # 70 p
0x08, 0x14, 0x14, 0x18, 0x7c, # 71 q
0x7c, 0x08, 0x04, 0x04, 0x08, # 72 r
0x48, 0x54, 0x54, 0x54, 0x20, # 73 s
0x04, 0x3f, 0x44, 0x40, 0x20, # 74 t
0x3c, 0x40, 0x40, 0x20, 0x7c, # 75 u
0x1c, 0x20, 0x40, 0x20, 0x1c, # 76 v
0x3c, 0x40, 0x30, 0x40, 0x3c, # 77 w
0x44, 0x28, 0x10, 0x28, 0x44, # 78 x
0x0c, 0x50, 0x50, 0x50, 0x3c, # 79 y
0x44, 0x64, 0x54, 0x4c, 0x44, # 7a z
0x00, 0x08, 0x36, 0x41, 0x00, # 7b {
0x00, 0x00, 0x7f, 0x00, 0x00, # 7c |
0x00, 0x41, 0x36, 0x08, 0x00, # 7d }
0x10, 0x08, 0x08, 0x10, 0x08, # 7e ~
0x00, 0x7E, 0x42, 0x42, 0x7E, # 7f CUSTOM
]

bitreverse =[
  0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 
  0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 
  0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 
  0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 
  0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 
  0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
  0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 
  0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
  0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
  0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 
  0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
  0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
  0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 
  0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
  0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 
  0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
]

def main():
  print ("LCD Display Test: ASCII 32 to 116")
  start = time.time()
  init()
  led(1)
  for i in range(32,116):
    display_char(chr(i))
  finish = time.time()
  print ("Init, LED on, 84 chars, total time = ", finish - start)
  print

def init():
  begin(0xbc)

def load_bitmap(file):
  gotoxy(0,0)
  f = open(file,'r')
  for x in range(6):
    for y in range(84):
      f.seek(0x3e + y*8 + x)
      b = ord(f.read(1))
      rev = bitreverse[b]
      lcd_data(rev)
  f.close()

def load_bitmap_reverse(file):
  gotoxy(0,0)
  f = open(file,'r')
  for x in range(6):
    for y in range(84):
      f.seek(0x3e + y*8 + x)
      b = ord(f.read(1))
      rev = bitreverse[b]
      rev = rev ^ 0xFF
      lcd_data(rev)
  f.close()

def show_custom():
  display_char(chr(127))

def define_custom(b0,b1,b2,b3,b4):
  font[475] = b0
  font[476] = b1
  font[477] = b2
  font[478] = b3
  font[479] = b4

def restore_custom():
  font[475] = 0x00
  font[476] = 0x7E
  font[477] = 0x42
  font[478] = 0x42
  font[479] = 0x7E

def alt_custom():
  font[475] = 0x00
  font[476] = 0x50
  font[477] = 0x3C
  font[478] = 0x52
  font[479] = 0x44

def pi_custom():
  font[475] = 0x19
  font[476] = 0x25
  font[477] = 0x5A
  font[478] = 0x25
  font[479] = 0x19

def centre_word(r, word):
  l = len(word)
  c = (COLUMNS - l) / 2
  if c <= 0:
    c = 0
  gotorc(r,c)
  text(word)

def gotorc(r,c):
  lcd_cmd(c*6+128)
  lcd_cmd(r+64)

def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
    display_char(words[i])

def display_char(char):
  index=(ord(char)-32)*5
  if ord(char) >=32 and ord(char) <=127:
    for i in range(5):
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)

def setup():
  # set pin directions
  wiringpi.wiringPiSetup()
  wiringpi.wiringPiGpioMode(ON)
#  GPIO.setmode(GPIO.BCM)
  wiringpi.pinMode(DIN, ON)
  wiringpi.pinMode(SCLK, ON)
  wiringpi.pinMode(DC, ON)
  wiringpi.pinMode(RST, ON)
  wiringpi.pinMode(LED, ON)

def begin(contrast):
  setup()
  # toggle RST low to reset
  wiringpi.digitalWrite(RST, OFF)
  wiringpi.digitalWrite(LED, OFF)
  time.sleep(0.100)
  wiringpi.digitalWrite(RST, ON)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()

def led(led_value):
  wiringpi.digitalWrite(LED, led_value)

def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    wiringpi.digitalWrite(DIN,((c & (1 << (7-i))) > 0))
    wiringpi.digitalWrite(SCLK, ON)
    wiringpi.digitalWrite(SCLK, OFF)

def lcd_cmd(c):
  wiringpi.digitalWrite(DC, OFF)
  SPI(c)

def lcd_data(c):
  wiringpi.digitalWrite(DC, ON)
  SPI(c)

if __name__ == "__main__":
  main()
Huh... I would attach the bitmap too but whatever I call it "The extension is not allowed" comes up :( Any ideas?

Have fun guys!

Regards,

Andy
Hi Andy, thanks for all the additional code - very useful ;)
The import grafix line was meant to be removed from my code - it is not used in this implementation, but it was used together with some graphic routines in Webhamster's original code.

For the bitmap file, you could try changing it to a txt or pdf file maybe. Anyone downloading it would then need to rename it to a bmp file aferwards.

T.
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am

Re: Nokia Pi LCD

Fri Aug 10, 2012 11:09 am

Some notes about the code:

Instead of having `font` as a flat list of byte values it would make the code simpler to use a dictionary that maps the character in the comment after each line to the five bytes defining that character:

Code: Select all

FONT = {
    ' ': [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],
    '0': [0x3e, 0x51, 0x49, 0x45, 0x3e],
    '1': [0x00, 0x42, 0x7f, 0x40, 0x00],
    '2': [0x42, 0x61, 0x51, 0x49, 0x46],
    '3': [0x21, 0x41, 0x45, 0x4b, 0x31],
    '4': [0x18, 0x14, 0x12, 0x7f, 0x10],
    '5': [0x27, 0x45, 0x45, 0x45, 0x39],
    '6': [0x3c, 0x4a, 0x49, 0x49, 0x30],
    '7': [0x01, 0x71, 0x09, 0x05, 0x03],
    '8': [0x36, 0x49, 0x49, 0x49, 0x36],
    '9': [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],
    'A': [0x7e, 0x11, 0x11, 0x11, 0x7e],
    'B': [0x7f, 0x49, 0x49, 0x49, 0x36],
    'C': [0x3e, 0x41, 0x41, 0x41, 0x22],
    'D': [0x7f, 0x41, 0x41, 0x22, 0x1c],
    'E': [0x7f, 0x49, 0x49, 0x49, 0x41],
    'F': [0x7f, 0x09, 0x09, 0x09, 0x01],
    'G': [0x3e, 0x41, 0x49, 0x49, 0x7a],
    'H': [0x7f, 0x08, 0x08, 0x08, 0x7f],
    'I': [0x00, 0x41, 0x7f, 0x41, 0x00],
    'J': [0x20, 0x40, 0x41, 0x3f, 0x01],
    'K': [0x7f, 0x08, 0x14, 0x22, 0x41],
    'L': [0x7f, 0x40, 0x40, 0x40, 0x40],
    'M': [0x7f, 0x02, 0x0c, 0x02, 0x7f],
    'N': [0x7f, 0x04, 0x08, 0x10, 0x7f],
    'O': [0x3e, 0x41, 0x41, 0x41, 0x3e],
    'P': [0x7f, 0x09, 0x09, 0x09, 0x06],
    'Q': [0x3e, 0x41, 0x51, 0x21, 0x5e],
    'R': [0x7f, 0x09, 0x19, 0x29, 0x46],
    'S': [0x46, 0x49, 0x49, 0x49, 0x31],
    'T': [0x01, 0x01, 0x7f, 0x01, 0x01],
    'U': [0x3f, 0x40, 0x40, 0x40, 0x3f],
    'V': [0x1f, 0x20, 0x40, 0x20, 0x1f],
    'W': [0x3f, 0x40, 0x38, 0x40, 0x3f],
    'X': [0x63, 0x14, 0x08, 0x14, 0x63],
    'Y': [0x07, 0x08, 0x70, 0x08, 0x07],
    'Z': [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],
    'a': [0x20, 0x54, 0x54, 0x54, 0x78],
    'b': [0x7f, 0x48, 0x44, 0x44, 0x38],
    'c': [0x38, 0x44, 0x44, 0x44, 0x20],
    'd': [0x38, 0x44, 0x44, 0x48, 0x7f],
    'e': [0x38, 0x54, 0x54, 0x54, 0x18],
    'f': [0x08, 0x7e, 0x09, 0x01, 0x02],
    'g': [0x0c, 0x52, 0x52, 0x52, 0x3e],
    'h': [0x7f, 0x08, 0x04, 0x04, 0x78],
    'i': [0x00, 0x44, 0x7d, 0x40, 0x00],
    'j': [0x20, 0x40, 0x44, 0x3d, 0x00],
    'k': [0x7f, 0x10, 0x28, 0x44, 0x00],
    'l': [0x00, 0x41, 0x7f, 0x40, 0x00],
    'm': [0x7c, 0x04, 0x18, 0x04, 0x78],
    'n': [0x7c, 0x08, 0x04, 0x04, 0x78],
    'o': [0x38, 0x44, 0x44, 0x44, 0x38],
    'p': [0x7c, 0x14, 0x14, 0x14, 0x08],
    'q': [0x08, 0x14, 0x14, 0x18, 0x7c],
    'r': [0x7c, 0x08, 0x04, 0x04, 0x08],
    's': [0x48, 0x54, 0x54, 0x54, 0x20],
    't': [0x04, 0x3f, 0x44, 0x40, 0x20],
    'u': [0x3c, 0x40, 0x40, 0x20, 0x7c],
    'v': [0x1c, 0x20, 0x40, 0x20, 0x1c],
    'w': [0x3c, 0x40, 0x30, 0x40, 0x3c],
    'x': [0x44, 0x28, 0x10, 0x28, 0x44],
    'y': [0x0c, 0x50, 0x50, 0x50, 0x3c],
    'z': [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],
    '\x7f': [0x00, 0x7e, 0x42, 0x42, 0x7e],
}
Displaying a character can be simpler now, without „magic numbers”:

Code: Select all

def display_char(char, font=FONT):
    try:
        for value in font[char]:
            lcd_data(value)
        lcd_data(0)     # Space inbetween characters.
    except KeyError:
        pass    # Ignore undefined characters.
As the code uses `xrange()` it has to be Python 2.x and there is no `__future__` import to make ``print`` a function. So it is irritating to have braces around it's ”arguments”. The first set is useless and the second actually alters the meaning of that line. Instead of printing the string followed by the time you are printing a tuple with a string and a number. I guess that is not what was intendet.

Instead of writing two almost identical functions for loading bitmaps there could be just one function with a `reverse` argument.

The functions does not get a `file` as the argument name suggests but a file *name*.

Files should be opened with the ``with`` statement if possible. This way it is guaranteed the file gets closed when the block is left — for whatever reasons.

The loop in `text()` contains a typical „anti pattern” in Python. Instead of creating a sequence of numbers of the length of another sequence and then iterating over the number sequence just to index the other sequence, it would be much simpler to iterate over the items of the other sequence directly — without the unneccesary indirection via an index. If an index is needed in addition to the items there is the `enumerate()` function. And if two or more sequences should be iterated in lock step there is `zip()` or `itertools.izip()`.

The `sys` module is imported but not used anywhere.

Untested:

Code: Select all

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import wiringpi

ROWS = 6
COLUMNS = 14
PIXELS_PER_ROW = 6
ON = 1
OFF = 0

#gpio's :
SCLK = 24       # gpio pin 18
DIN =  23       # gpio pin 16
DC =  22        # gpio pin 15
RST =  18       # gpio pin 12
LED = 17        # gpio pin 11

FONT = {
    ' ': [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],
    '0': [0x3e, 0x51, 0x49, 0x45, 0x3e],
    '1': [0x00, 0x42, 0x7f, 0x40, 0x00],
    '2': [0x42, 0x61, 0x51, 0x49, 0x46],
    '3': [0x21, 0x41, 0x45, 0x4b, 0x31],
    '4': [0x18, 0x14, 0x12, 0x7f, 0x10],
    '5': [0x27, 0x45, 0x45, 0x45, 0x39],
    '6': [0x3c, 0x4a, 0x49, 0x49, 0x30],
    '7': [0x01, 0x71, 0x09, 0x05, 0x03],
    '8': [0x36, 0x49, 0x49, 0x49, 0x36],
    '9': [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],
    'A': [0x7e, 0x11, 0x11, 0x11, 0x7e],
    'B': [0x7f, 0x49, 0x49, 0x49, 0x36],
    'C': [0x3e, 0x41, 0x41, 0x41, 0x22],
    'D': [0x7f, 0x41, 0x41, 0x22, 0x1c],
    'E': [0x7f, 0x49, 0x49, 0x49, 0x41],
    'F': [0x7f, 0x09, 0x09, 0x09, 0x01],
    'G': [0x3e, 0x41, 0x49, 0x49, 0x7a],
    'H': [0x7f, 0x08, 0x08, 0x08, 0x7f],
    'I': [0x00, 0x41, 0x7f, 0x41, 0x00],
    'J': [0x20, 0x40, 0x41, 0x3f, 0x01],
    'K': [0x7f, 0x08, 0x14, 0x22, 0x41],
    'L': [0x7f, 0x40, 0x40, 0x40, 0x40],
    'M': [0x7f, 0x02, 0x0c, 0x02, 0x7f],
    'N': [0x7f, 0x04, 0x08, 0x10, 0x7f],
    'O': [0x3e, 0x41, 0x41, 0x41, 0x3e],
    'P': [0x7f, 0x09, 0x09, 0x09, 0x06],
    'Q': [0x3e, 0x41, 0x51, 0x21, 0x5e],
    'R': [0x7f, 0x09, 0x19, 0x29, 0x46],
    'S': [0x46, 0x49, 0x49, 0x49, 0x31],
    'T': [0x01, 0x01, 0x7f, 0x01, 0x01],
    'U': [0x3f, 0x40, 0x40, 0x40, 0x3f],
    'V': [0x1f, 0x20, 0x40, 0x20, 0x1f],
    'W': [0x3f, 0x40, 0x38, 0x40, 0x3f],
    'X': [0x63, 0x14, 0x08, 0x14, 0x63],
    'Y': [0x07, 0x08, 0x70, 0x08, 0x07],
    'Z': [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],
    'a': [0x20, 0x54, 0x54, 0x54, 0x78],
    'b': [0x7f, 0x48, 0x44, 0x44, 0x38],
    'c': [0x38, 0x44, 0x44, 0x44, 0x20],
    'd': [0x38, 0x44, 0x44, 0x48, 0x7f],
    'e': [0x38, 0x54, 0x54, 0x54, 0x18],
    'f': [0x08, 0x7e, 0x09, 0x01, 0x02],
    'g': [0x0c, 0x52, 0x52, 0x52, 0x3e],
    'h': [0x7f, 0x08, 0x04, 0x04, 0x78],
    'i': [0x00, 0x44, 0x7d, 0x40, 0x00],
    'j': [0x20, 0x40, 0x44, 0x3d, 0x00],
    'k': [0x7f, 0x10, 0x28, 0x44, 0x00],
    'l': [0x00, 0x41, 0x7f, 0x40, 0x00],
    'm': [0x7c, 0x04, 0x18, 0x04, 0x78],
    'n': [0x7c, 0x08, 0x04, 0x04, 0x78],
    'o': [0x38, 0x44, 0x44, 0x44, 0x38],
    'p': [0x7c, 0x14, 0x14, 0x14, 0x08],
    'q': [0x08, 0x14, 0x14, 0x18, 0x7c],
    'r': [0x7c, 0x08, 0x04, 0x04, 0x08],
    's': [0x48, 0x54, 0x54, 0x54, 0x20],
    't': [0x04, 0x3f, 0x44, 0x40, 0x20],
    'u': [0x3c, 0x40, 0x40, 0x20, 0x7c],
    'v': [0x1c, 0x20, 0x40, 0x20, 0x1c],
    'w': [0x3c, 0x40, 0x30, 0x40, 0x3c],
    'x': [0x44, 0x28, 0x10, 0x28, 0x44],
    'y': [0x0c, 0x50, 0x50, 0x50, 0x3c],
    'z': [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],
    '\x7f': [0x00, 0x7e, 0x42, 0x42, 0x7e],
}

ORIGINAL_CUSTOM = FONT['\x7f']


def bit_reverse(value, width=8):
    result = 0
    for _ in xrange(width):
        result = (result << 1) | (value & 1)
        value >>= 1
    return result


BITREVERSE = map(bit_reverse, xrange(256))


def setup():
    # Set pin directions.
    wiringpi.wiringPiSetup()
    wiringpi.wiringPiGpioMode(ON)
    for pin in [DIN, SCLK, DC, RST, LED]:
        wiringpi.pinMode(pin, ON)


def SPI(value):
    # data = DIN
    # clock = SCLK
    # MSB first
    for i in reversed(xrange(8)):
        wiringpi.digitalWrite(DIN, (value >> i) & 1)
        wiringpi.digitalWrite(SCLK, ON)
        wiringpi.digitalWrite(SCLK, OFF)


def lcd_cmd(value):
    wiringpi.digitalWrite(DC, OFF)
    SPI(value)


def lcd_data(value):
    wiringpi.digitalWrite(DC, ON)
    SPI(value)


def gotoxy(x, y):
    lcd_cmd(x + 128)
    lcd_cmd(y + 64)


def cls():
    gotoxy(0, 0)
    for _ in xrange(ROWS * COLUMNS * PIXELS_PER_ROW):
        lcd_data(0)


def begin(contrast):
    setup()
    # Toggle RST low to reset.
    wiringpi.digitalWrite(RST, OFF)
    wiringpi.digitalWrite(LED, OFF)
    time.sleep(0.100)
    wiringpi.digitalWrite(RST, ON)
    # Extended mode, bias, vop, basic mode, non-inverted display.
    for value in [0x21, 0x14, contrast, 0x20, 0x0c]:
        lcd_cmd(value) # extended mode
    cls()


def init():
    begin(0xbc)


def led(led_value):
    wiringpi.digitalWrite(LED, led_value)


def load_bitmap(filename, reverse=False):
    mask = 0xff if reverse else 0x00
    gotoxy(0, 0)
    with open(filename, 'rb') as bitmap_file:
        for x in xrange(6):
            for y in xrange(84):
                bitmap_file.seek(0x3e + y * 8 + x)
                lcd_data(BITREVERSE[ord(bitmap_file.read(1))] ^ mask)


def show_custom(font=FONT):
    display_char('\x7f', font)


def define_custom(values):
    FONT['\x7f'] = values


def restore_custom():
    define_custom(ORIGINAL_CUSTOM)


def alt_custom():
    define_custom([0x00, 0x50, 0x3C, 0x52, 0x44])


def pi_custom():
    define_custom([0x19, 0x25, 0x5A, 0x25, 0x19])


def display_char(char, font=FONT):
    try:
        for value in font[char]:
            lcd_data(value)
        lcd_data(0)     # Space inbetween characters.
    except KeyError:
        pass    # Ignore undefined characters.


def text(string, font=FONT):
    for char in string:
        display_char(char, font)


def gotorc(r, c):
    lcd_cmd(c * 6 + 128)
    lcd_cmd(r + 64)


def centre_word(r, word):
    gotorc(r, max(0, (COLUMNS - len(word)) // 2))
    text(word)


def main():
    start, end = 32, 116
    print 'LCD Display Test: ASCII %d to %d' % (start, end)
    start_time = time.time()
    init()
    led(1)
    for i in xrange(start, end + 1):
        display_char(chr(i))
    finish_time = time.time()
    print 'Init, LED on, %d chars, total time = %.2f' % (
        end - start, finish_time - start_time
    )
    print


if __name__ == '__main__':
    main()

Code: Select all

while not self.asleep():
    sheep += 1

POSitality
Posts: 23
Joined: Thu Aug 02, 2012 8:31 pm
Location: London

Re: Nokia Pi LCD

Sat Aug 11, 2012 8:29 pm

Nice optimisations, BlackJack, takes me back to my C programming days :) Don't think I did too bad considering I've never used Python before.

About adding a parameter to load_bitmap for the reverse: as this is an interpreted language and more than likely people will be importing and firing off functions by hand I thought words were easier than a numbered parameter. For compiled (static) code, code duplication is obviously a no-no. Just an opinion :)

I like the little 'trick' with the font lookup table. I got taught about ASCII and the reasoning behind many moons ago at college so you could probably shoot a number at me cold and I'd tell you what character it represents :D Considering the education angle of the RPi it's probably best not to assume everyone knows what ASCII is... "Hey grandad... we're onto unicode now!"

Anyhoo, I did try uploading my test bitmap with various extension renames, same answer each time :(

Regards,

Andy

POSitality
Posts: 23
Joined: Thu Aug 02, 2012 8:31 pm
Location: London

Re: Nokia Pi LCD

Sat Aug 11, 2012 8:38 pm

Oooh! Forgot to mention, I'm thinking I can use this little display for feedback in programs when you can't use SSH/HTTP/VNC/Composite/HDMI for visual output. I'm also thinking if there are any GPIO lines left I could add some buttons to the side of the display to get user responses. Bit like the buttons at the side of hole-in-the wall machines.

This is why I asked if the RPi's 'real' SPI output is easy to program as they'll be more GPIO lines left. Ofc... I need to sober up a bit first! Maybe tomorrow :)

Regards,

Andy

recantha
Posts: 209
Joined: Mon Jun 25, 2012 10:41 am

Re: Nokia Pi LCD

Sat Aug 11, 2012 8:44 pm

Oh, I love the ATM button idea. Works really well on Printer control panels that kind of thing.
You could even build in a "Connect to which WiFi network?" interface!
My Raspberry Pi blog with all my latest projects and links to articles
http://raspberrypipod.blogspot.com. +++ Current project: PiPodTricorder - lots of sensors, lots of mini-displays, breadboarding, bit of programming.

BlackJack
Posts: 288
Joined: Sat Aug 04, 2012 8:28 am

Re: Nokia Pi LCD

Sat Aug 11, 2012 8:47 pm

@POSitality: If you want you can use keyword arguments when calling a function like ``load_bitmap('foo.bmp', reverse=True)`` to make it more readable. Code duplication is a no go in any language IMHO. It does not matter if it is a static or a dynamic language — if you change or fix something in duplicated code it is more work to find and change all copies and there is always the danger of introducing subtle differences.

Code: Select all

while not self.asleep():
    sheep += 1

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Sat Aug 18, 2012 4:22 pm

I have designed a PCB mounted version, together with GPIO connecting ribbon cable with PWM controllable blue backlite :
http://www.raspberrypi.org/phpBB3/viewt ... 59&t=14913

This code includes PWM control :

Code: Select all

#!/usr/bin/python
# -*- coding: utf-8 -*-
# using wiringPi GPIO method
# also using PWM on gpio pin 12
# note that the contrast may need adjustment between displays
# also note that I took out wiringPi's built in shiftOut routine as it caused corruption, possibly
# because it is too fast for the display
# Texy 18/8/12

import wiringpi
import time

#gpio's :
SCLK = 24 # gpio pin 18 = wiringpi no. 5
DIN =  23 # gpio pin 16 = wiringpi no. 4
DC =  22 # gpio pin 15 = wiringpi no. 3
RST =  17 # gpio pin 11 = wiringpi no. 0
LED = 18 # gpio pin 12 = wiringpi no. 1

font =[
0x7E, 0x11, 0x11, 0x11, 0x7E, # A
0x7F, 0x49, 0x49, 0x49, 0x36, # B
0x3E, 0x41, 0x41, 0x41, 0x22, # C
0x7F, 0x41, 0x41, 0x22, 0x1C, # D
0x7F, 0x49, 0x49, 0x49, 0x41, # E
0x7F, 0x09, 0x09, 0x09, 0x01, # F
0x3E, 0x41, 0x49, 0x49, 0x7A, # G
0x7F, 0x08, 0x08, 0x08, 0x7F, # H
0x00, 0x41, 0x7F, 0x41, 0x00, # I
0x20, 0x40, 0x41, 0x3F, 0x01, # J
0x7F, 0x08, 0x14, 0x22, 0x41, # K
0x7F, 0x40, 0x40, 0x40, 0x40, # L
0x7F, 0x02, 0x0C, 0x02, 0x7F, # M
0x7F, 0x04, 0x08, 0x10, 0x7F, # N
0x3E, 0x41, 0x41, 0x41, 0x3E, # O
0x7F, 0x09, 0x09, 0x09, 0x06, # P
0x3E, 0x41, 0x51, 0x21, 0x5E, # Q
0x7F, 0x09, 0x19, 0x29, 0x46, # R
0x46, 0x49, 0x49, 0x49, 0x31, # S
0x01, 0x01, 0x7F, 0x01, 0x01, # T
0x3F, 0x40, 0x40, 0x40, 0x3F, # U
0x1F, 0x20, 0x40, 0x20, 0x1F, # V
0x3F, 0x40, 0x38, 0x40, 0x3F, # W
0x63, 0x14, 0x08, 0x14, 0x63, # X
0x07, 0x08, 0x70, 0x08, 0x07, # Y
0x61, 0x51, 0x49, 0x45, 0x43, # Z
]

def main():
  start = time.time()
  begin(0xb3) # contrast - may need tweaking for each display
  gotoxy(28,0)
  text("HI")
  gotoxy(18,2)
  text("THERE")
  gotoxy(26,4)
  text("PEEPS")
  finish = time.time()
  print ("Total time : ",finish - start)
  print

def gotoxy(x,y):
  lcd_cmd(x+128)
  lcd_cmd(y+64)

def text(words):
  for i in range(len(words)):
    display_char(words[i])

def display_char(char):
  index=(ord(char)-65)*5
  if ord(char) >=65 and ord(char) <=90:
    for i in range(5):
      lcd_data(font[index+i])
    lcd_data(0) # space inbetween characters
  elif ord(char)==32:
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)
      lcd_data(0)

def cls():
  gotoxy(0,0)
  for i in range(84):
    for j in range(6):
      lcd_data(0)

def setup():
  # set pin directions
  wiringpi.wiringPiSetupGpio()
  wiringpi.pinMode(DIN, 1)
  wiringpi.pinMode(SCLK, 1)
  wiringpi.pinMode(DC, 1)
  wiringpi.pinMode(RST, 1)
  wiringpi.pinMode(LED, 2) # LED set up as PWM
  print ("setup ok")
  wiringpi.pwmWrite(LED, 255)

def begin(contrast):
  setup()
  # toggle RST low to reset
  wiringpi.digitalWrite(RST, 0)
  wiringpi.digitalWrite(LED, 0)
  time.sleep(0.100)
  wiringpi.digitalWrite(RST, 1)
  lcd_cmd(0x21) # extended mode
  lcd_cmd(0x14) # bias
  lcd_cmd(contrast) # vop
  lcd_cmd(0x20) # basic mode
  lcd_cmd(0xc) # non-inverted display
  cls()

def SPI(c):
  # data = DIN
  # clock = SCLK
  # MSB first
  # value = c
  for i in xrange(8):
    wiringpi.digitalWrite(DIN,((c & (1 << (7-i))) > 0))
    wiringpi.digitalWrite(SCLK, 1)
    wiringpi.digitalWrite(SCLK, 0)
#  wiringpi.shiftOut(DIN, SCLK, 1, c)

def lcd_cmd(c):
  wiringpi.digitalWrite(DC, 0)
  SPI(c)

def lcd_data(c):
  wiringpi.digitalWrite(DC, 1)
  SPI(c)

if __name__ == "__main__":
  main()
Thanks for the above posters who have suggested code improvements - I will take a look at them later ;)

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

dan3008
Posts: 1172
Joined: Wed Aug 15, 2012 1:05 pm

Re: Nokia Pi LCD

Sat Aug 18, 2012 7:43 pm

ooo wow, I have an old Razor V3 screen somewhere from when i broke my old mobile. If the screen still works i'll have to give this a spin :D
would make an epic addition to one of my projects
dan3008 wrote:Pays your money, takes your choice

POSitality
Posts: 23
Joined: Thu Aug 02, 2012 8:31 pm
Location: London

Re: Nokia Pi LCD

Mon Aug 20, 2012 6:05 pm

@dan3008:

Um... you can't just lump any old screen on the RPi: you need to know the wiring, style of communication, etc. What it is about the old Nokia 3110/5110 screens is that they are a known factor from the Arduino guys and also some kind ppl in China have mounted them on a handy PCB for pennies! Also, these things want 3.3v and RPi spits that out by default, an Arduino needs something to drop the level down from 5v. We have it so easy!

I would imagine many moons ago someone miss-ordered on the 5110 screens and there's a warehouse somewhere with thousands of the things just waiting for the PIC and Arduino guys to think of a way of (re)using them :D

Anyhoo, take advantage of these cheap things till you understand what they're about. Then move onto more fantastical displays later. I mean take the way the data is organised: vertical stripes of 8 bytes which is completely daft compared to the way, for example, an old ZX Spectrum's bitmaps were constructed. If it wasn't for working with old dot matrix printers I'd have been completely thrown.

Regards,

Andy

dan3008
Posts: 1172
Joined: Wed Aug 15, 2012 1:05 pm

Re: Nokia Pi LCD

Mon Aug 20, 2012 10:04 pm

POSitality wrote:@dan3008:

Um... you can't just lump any old screen on the RPi: you need to know the wiring, style of communication, etc. What it is about the old Nokia 3110/5110 screens is that they are a known factor from the Arduino guys and also some kind ppl in China have mounted them on a handy PCB for pennies! Also, these things want 3.3v and RPi spits that out by default, an Arduino needs something to drop the level down from 5v. We have it so easy!

I would imagine many moons ago someone miss-ordered on the 5110 screens and there's a warehouse somewhere with thousands of the things just waiting for the PIC and Arduino guys to think of a way of (re)using them :D

Anyhoo, take advantage of these cheap things till you understand what they're about. Then move onto more fantastical displays later. I mean take the way the data is organised: vertical stripes of 8 bytes which is completely daft compared to the way, for example, an old ZX Spectrum's bitmaps were constructed. If it wasn't for working with old dot matrix printers I'd have been completely thrown.

Regards,

Andy
I know I cant just jerry rig any old screen on and this will just work lol. I've played with outputting to mobile screens a lot in VB.net, and used to have a job testing screens at a mobile repair shop back home, so I know the wiring. I just didnt know it would be possible to put a mobile screen onto the pi, didnt think the power output would be good enough, and i'm new to python, so seeing an old hobby of mine is doable though the pi :D even better
dan3008 wrote:Pays your money, takes your choice

POSitality
Posts: 23
Joined: Thu Aug 02, 2012 8:31 pm
Location: London

Re: Nokia Pi LCD

Tue Aug 21, 2012 12:42 pm

Yeah, the more I fiddle with these things the more I go "O..M..G!" :) I've even gone backwards slightly and bought an Arduino Nano. I'd done some work on the old PICs but somehow work got in the way and I missed how these developed.

I've come to realise that the HDMI socket is just to keep the kids amused, the low level interfaces are where it's really at!

Another small contribution, not everyone can convert binary to hex in their head so if you want a custom character here's a handy page:

http://www.carlos-rodrigues.com/projects/pcd8544/

sneakervdv
Posts: 2
Joined: Thu Aug 23, 2012 8:11 pm

Re: Nokia Pi LCD

Thu Aug 23, 2012 8:18 pm

I'am having some difficulties to get my LCD working.

Today I finally got my LCD connected to Raspberry and started messing around by installing wiringpi-python from github and running setup.py. So far no problems. (This was fresh install, just with python and python-dev)
After that I tried PWM code that Texy provided.

All I get is LEDs and one really fast flick. No text or characters.

I connected pins as instructed in start of the code + vcc to GPIO 5v and GND + CE to GPIO GND.

Any ideas?

texy
Forum Moderator
Forum Moderator
Posts: 5174
Joined: Sat Mar 03, 2012 10:59 am
Location: Berkshire, England

Re: Nokia Pi LCD

Fri Aug 24, 2012 6:31 am

I did have somthing similair, if not the same scenario at one point. It seemed to drive the LCD correctly whilst the program was running, but the screen went blank as soon as it finished. Can you put a long delay into the program after the last 'print' statement to see if it behaves in the same way.

The LCD's are a 3v3 device, not 5v. Some people have stated that they 'just work', but I would suggest rewiring it to the 3v3 pin.
Also, I,ve used a mosfet to drive the LED line as the current consumption could exceed the Pi's rating.
The other thing is that you may need to adjust the contrast value.
Is your Pi over clocked?

Texy
Various male/female 40- and 26-way GPIO header for sale here ( IDEAL FOR YOUR PiZero ):
https://www.raspberrypi.org/forums/viewtopic.php?f=93&t=147682#p971555

Return to “Python”