commit 83fd7cc826c235a1b2a287af82f2e4a7e9b80ce5 Author: Markus Birth Date: Sat Nov 14 23:29:59 2015 +0100 Initial import diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..21436e2 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Markus Birth + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..07833e1 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +upcd8544 +======== + +MicroPython module for the PCD8544 LCD, originally used for the Nokia 5110. + +[Datasheet](https://www.sparkfun.com/datasheets/LCD/Monochrome/Nokia5110.pdf) + +![](doc/wipy-lcd.jpg) diff --git a/doc/wipy-lcd.jpg b/doc/wipy-lcd.jpg new file mode 100644 index 0000000..03645aa Binary files /dev/null and b/doc/wipy-lcd.jpg differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..349143d --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +# main.py -- put your code here! + +from machine import SPI, Pin + +# WiPy (on Exp board, SD and User-LED jumper have to be removed!) +SPI = machine.SPI(0) # GP14 (CLK) + GP16 (MOSI->DIN), User-LED jumper removed! +RST = machine.Pin('GP24') +CE = machine.Pin('GP12') +DC = machine.Pin('GP22') +LIGHT = machine.Pin('GP23') +# PWR = directly from 3V3 pin of the WiPy + +import upcd8544 + +lcd = upcd8544.PCD8544(SPI, RST, CE, DC, LIGHT) + +lcd.data([0xff]) +lcd.data([0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa]) diff --git a/upcd8544.py b/upcd8544.py new file mode 100644 index 0000000..d404d27 --- /dev/null +++ b/upcd8544.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +""" +MicroPython PCD8544 driver +(for Nokia 5110 displays) +""" + +__author__ = "Markus Birth" +__copyright__ = "Copyright 2015, Markus Birth" +__credits__ = ["Markus Birth"] +__license__ = "MIT" +__version__ = "1.0" +__maintainer__ ="Markus Birth" +__email__ = "markus@birth-online.de" +__status__ = "Production" + +# Datasheet: https://www.sparkfun.com/datasheets/LCD/Monochrome/Nokia5110.pdf +# Inspiration from: +# - https://github.com/inaugurator/upyd5110 +# - https://github.com/rm-hull/pcd8544/blob/master/src/lcd.py +# +# PINOUT +# WiPy/pyBoard display function +# +# 3V3 or any Pin => VCC 3.3V logic voltage (0=off, 1=on) +# MOSI => DIN data flow (Master out, Slave in) +# SCK => CLK SPI clock +# any Pin => RST Reset pin (0=reset, 1=normal) +# any Pin => CE Chip Enable (0=listen for input, 1=ignore input) +# any Pin => DC Data/Command (0=commands, 1=data) +# any Pin => LIGHT Light (0=on, 1=off) +# GND => GND +# +# pyBoard "Y" side +# SPI = pyb.SPI(1) +# RST = pyb.Pin('Y4') +# CE = pyb.Pin('Y5') +# DC = pyb.Pin('Y3') +# LIGHT = pyb.Pin('Y2') +# PWR = pyb.Pin('Y1') +# +# pyBoard "X" side +# SPI = pyb.SPI(2) +# RST = pyb.Pin('X4') +# CE = pyb.Pin('X5') +# DC = pyb.Pin('X3') +# LIGHT = pyb.Pin('X2') +# PWR = pyb.Pin('X1') +# +# WiPy (on Exp board, SD and User-LED jumper have to be removed!) +# SPI = machine.SPI(0) # GP14 (CLK) + GP16 (MOSI->DIN), User-LED jumper removed! +# RST = machine.Pin('GP24') +# CE = machine.Pin('GP12') +# DC = machine.Pin('GP22') +# LIGHT = machine.Pin('GP23') +# PWR = directly from 3V3 pin of the WiPy + +try: + import pyb as machine +except: + # WiPy + import machine + +import struct +import time + +class PCD8544: + def __init__(self, spi, rst, ce, dc, light, pwr=None): + self.width = 84 + self.height = 48 + + # init the SPI bus and pins + spi.init(spi.MASTER, baudrate=328125, bits=8, polarity=0, phase=1, firstbit=spi.MSB) + if "OUT_PP" in dir(rst): + # pyBoard style + rst.init(rst.OUT_PP, rst.PULL_NONE) # Reset line + ce.init(ce.OUT_PP, ce.PULL_NONE) # Chip Enable + dc.init(dc.OUT_PP, dc.PULL_NONE) # Data(1) / Command(0) mode + light.init(light.OUT_PP, light.PULL_NONE) + if pwr: + pwr.init(pwr.OUT_PP, pwr.PULL_NONE) + else: + # WiPy style + rst.init(rst.OUT, None) + ce.init(ce.OUT, None) + dc.init(dc.OUT, None) + light.init(light.OUT, None) + if pwr: + pwr.init(pwr.OUT, None) + + self.spi = spi + self.rst = rst + self.ce = ce + self.dc = dc + self.light = light + self.pwr = pwr + + self.light_off() + self.power_on() + self.ce.value(1) # set chip to disable (don't listen to input) + self.reset() + self.set_contrast(0xba) + self.clear() + + def set_contrast(self, value): + """ set LCD voltage, i.e. contrast """ + assert 0x80 <= value <= 0xff, "contrast value must be between 0x80 and 0xff" + self.command([0x21, 0x06, 0x14, value, 0x20, 0x0c]) + # 0x21 - enter extended instruction set (H=1) + # 0x06 - set temperature coefficient 2 + # 0x14 - set BIAS system to n=3 (recomm. mux rate 1:40/1:34) + # value - (80-ff) - set Vop (80 = 3.00V, ff = 10.68V), 8b seems to work (0x3b/d70: 3.00+(70*0.06)=7.2V) + # 0x20 - back to basic instruction set + # 0x0c - normal display mode + + def position(self, x, y): + """ goto to column x in seg y """ + assert 0 <= x <= 83, "x must be between 0 and 83" + assert 0 <= y <= 5, "y must be between 0 and 5" + self.command([x + 0x80, y + 0x40]) + + def clear(self): + """ clear screen """ + self.position(0, 0) + self.data([0] * (self.height * self.width // 8)) + self.position(0, 0) + + def sleep_ms(self, mseconds): + try: + time.sleep_ms(mseconds) + except AttributeError: + machine.delay(mseconds) + + def sleep_us(self, useconds): + try: + time.sleep_us(useconds) + except AttributeError: + machine.udelay(useconds) + + def power_on(self): + if self.pwr: + self.pwr.value(1) + self.reset() + + def reset(self): + """ issue reset impulse to reset the display """ + self.rst.value(0) # RST on + self.sleep_us(100) # reset impulse has to be >100 ns and <100 ms + self.rst.value(1) # RST off + + def power_off(self): + self.clear() + self.command([0x20, 0x08]) + # 0x20 - basic instruction set + # 0x08 - set display to blank (doesn't delete contents) + self.sleep_ms(10) + if self.pwr: + self.pwr.value(0) # turn off power + + def command(self, arr): + """ send bytes in command mode """ + self.bitmap(arr, 0) + + def data(self, arr): + """ send bytes in data mode """ + self.bitmap(arr, 1) + + def bitmap(self, arr, dc): + self.dc.value(dc) + buf = struct.pack('B'*len(arr), *arr) + self.ce.value(0) # set chip to listening/enable + try: + self.spi.send(buf) + except AttributeError: + self.spi.write(buf) + self.ce.value(1) # set chip to disable + + def light_on(self): + self.light.value(0) # pull to GND + + def light_off(self): + self.light.value(1) # set to HIGH