diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cd78447 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +temp/ \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.txt similarity index 97% rename from LICENSE.md rename to LICENSE.txt index 30de3b9..f1f7203 100644 --- a/LICENSE.md +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -The MIT License (MIT) +MIT License Copyright (c) 2016 Mike Causer diff --git a/README.md b/README.md index ccdee91..089976b 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,63 @@ # MicroPython PCD8544 -A MicroPython library for the PCD8544 84x48 LCD, used by the Nokia 5110 display. +A MicroPython library for the Philips PCD8544 84x48 monochrome LCD, used by the Nokia 5110 display. ![demo](docs/demo.jpg) #### Pinout -``` -+-----------------+ -| | -| +-------------+ | -| | Nokia 5110 | | -| | PCD8544 | | -| | 84x48 | | -| | | | -| +-------------+ | -+-----------------+ - | | | | | | | | - 1 2 3 4 5 6 7 8 -``` -* 1 RST -* 2 CE -* 3 DC -* 4 DIN -* 5 CLK -* 6 VCC -* 7 BL -* 8 GND +| ![demo](docs/nokia5110_pinout.jpg) | ![demo](docs/esp8266_pinout.jpg) | +|-|-| + +Pin | Name | Description +:--:|:----:|:-------------------------------- +1 | RST | External reset input, active low +2 | CE | Chip enable, active low +3 | D/C | Data high / Command low +4 | DIN | Serial data input +5 | CLK | Serial clock, up to 4 Mbits/s +6 | VCC | Supply voltage 2.7-3.3V +7 | BL | Backlight +8 | GND | Ground ## Example Copy the file to your device, using ampy, webrepl or compiling and deploying. eg. -``` +```bash $ ampy put pcd8544.py ``` -**Hello World** +**Basic Example** -``` +```python import pcd8544 from machine import Pin, SPI spi = SPI(1) -spi.init(baudrate=8000000, polarity=0, phase=0) -CE = Pin(2) -DC = Pin(15) -RST = Pin(4) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) # backlight on -BL = Pin(12, Pin.OUT, value=1) +bl = Pin(12, Pin.OUT, value=1) -lcd = pcd8544.PCD8544(spi, CE, DC, RST) +lcd = pcd8544.PCD8544(spi, cs, dc, rst) # test pattern (50% on) lcd.data(bytearray([0x55, 0xAA] * 42 * 6)) # bitmap smiley (horzontal msb) lcd.clear() -# draw 8x16 in bank 0 +# draw 8x16 in bank 0 (rows 0..7) lcd.position(0, 0) lcd.data(bytearray(b'\xE0\x38\xE4\x22\xA2\xE1\xE1\x61\xE1\x21\xA2\xE2\xE4\x38\xE0\x00')) -# draw 8x16 in bank 1 +# draw 8x16 in bank 1 (rows 8..15) lcd.position(0, 1) lcd.data(bytearray(b'\x03\x0C\x10\x21\x21\x41\x48\x48\x48\x49\x25\x21\x10\x0C\x03\x00')) -# toggle display, DDRAM persists image +# toggle display, image persists in DDRAM lcd.power_off() lcd.power_on() @@ -77,6 +69,10 @@ lcd.invert(True) lcd.invert(False) lcd.reset() +lcd.init() + +# swtich to vertical addressing +lcd.init(horizontal=False) # adjust contrast, bias and temp lcd.contrast(0x1f, pcd8544.BIAS_1_40, pcd8544.TEMP_COEFF_0) @@ -84,34 +80,9 @@ lcd.contrast(0x3f, pcd8544.BIAS_1_48, pcd8544.TEMP_COEFF_0) lcd.contrast(0x3c, pcd8544.BIAS_1_40, pcd8544.TEMP_COEFF_0) lcd.contrast(0x42, pcd8544.BIAS_1_48, pcd8544.TEMP_COEFF_0) lcd.contrast(0x3f, pcd8544.BIAS_1_40, pcd8544.TEMP_COEFF_2) - -# use the framebuf -import framebuf -width = 84 -height = 48 -pages = height // 8 -buffer = bytearray(pages * width) -framebuf = framebuf.FrameBuffer1(buffer, width, height) - -framebuf.text('Hello', 0, 0, 1) -framebuf.pixel(30, 30, 1) -framebuf.pixel(31, 31, 1) -framebuf.pixel(32, 32, 1) -framebuf.pixel(33, 33, 1) -framebuf.pixel(34, 34, 1) -framebuf.hline(0, 20, 8, 1) -framebuf.vline(30, 8, 8, 1) -framebuf.rect(40, 16, 8, 8, 1) -framebuf.fill_rect(16, 32, 8, 8, 1) - -lcd.position(0, 0) -lcd.data(buffer) - -framebuf.fill(0) -lcd.data(buffer) ``` -See [pcd8544_examples.py](pcd8544_examples.py) for more. +See [/examples](/examples) for more. ## Addressing @@ -127,6 +98,8 @@ In horizontal mode you can use framebuf with MONO_VLSB format. Vertical mode doe ![Vertical](docs/pcd8544-vertical.gif) +See [/examples/addressing](/examples/addressing) for more details. + ## Parts * [WeMos D1 Mini](https://www.aliexpress.com/store/product/D1-mini-Mini-NodeMcu-4M-bytes-Lua-WIFI-Internet-of-Things-development-board-based-ESP8266/1331105_32529101036.html) $4.00 USD @@ -136,14 +109,14 @@ In horizontal mode you can use framebuf with MONO_VLSB format. Vertical mode doe WeMos D1 Mini | PCD8544 LCD ------------- | ---------- -D2 (GPIO4) | 0 RST -D4 (GPIO2) | 1 CE -D8 (GPIO15) | 2 DC -D7 (GPIO13) | 3 Din -D5 (GPIO14) | 4 Clk -3V3 | 5 Vcc -D6 (GPIO12) | 6 BL -G | 7 Gnd +D3 (GPIO0) | 1 RST +D4 (GPIO2) | 2 CE +D8 (GPIO15) | 3 DC +D7 (GPIO13) | 4 Din +D5 (GPIO14) | 5 Clk +3V3 | 6 Vcc +D6 (GPIO12) | 7 BL +G | 8 Gnd ## Links @@ -151,3 +124,7 @@ G | 7 Gnd * [micropython.org](http://micropython.org) * [Adafruit Ampy](https://learn.adafruit.com/micropython-basics-load-files-and-run-code/install-ampy) * [PCD8544 Datasheet](docs/PCD8544.pdf) + +## License + +Licensed under the [MIT License](http://opensource.org/licenses/MIT). diff --git a/docs/esp8266_pinout.jpg b/docs/esp8266_pinout.jpg new file mode 100644 index 0000000..afe34f2 Binary files /dev/null and b/docs/esp8266_pinout.jpg differ diff --git a/docs/horizontal-addressing.png b/docs/horizontal-addressing.png new file mode 100644 index 0000000..1b6adae Binary files /dev/null and b/docs/horizontal-addressing.png differ diff --git a/docs/nokia-splash.gif b/docs/nokia-splash.gif new file mode 100644 index 0000000..3ee6a0c Binary files /dev/null and b/docs/nokia-splash.gif differ diff --git a/docs/nokia5110_pinout.jpg b/docs/nokia5110_pinout.jpg new file mode 100644 index 0000000..1cf6176 Binary files /dev/null and b/docs/nokia5110_pinout.jpg differ diff --git a/docs/vertical-addressing.png b/docs/vertical-addressing.png new file mode 100644 index 0000000..8065f68 Binary files /dev/null and b/docs/vertical-addressing.png differ diff --git a/examples/3d_cube.py b/examples/3d_cube.py new file mode 100644 index 0000000..33fe9b8 --- /dev/null +++ b/examples/3d_cube.py @@ -0,0 +1,74 @@ +# Rotating 3D cube + +# Based on MicroViewCube.ino by Jim Lindblom @ SparkFun Electronics +# https://github.com/sparkfun/SparkFun_MicroView_Arduino_Library/blob/master/examples/MicroViewCube/MicroViewCube.ino + +import math +import pcd8544_fb +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) # backlight on + +lcd = pcd8544_fb.PCD8544_FB(spi, cs, dc, rst) + +pi = math.pi + +size = 700 +width = 84 +height = 48 + +d = 3 +px = [-d, d, d, -d, -d, d, d, -d] +py = [-d, -d, d, d, -d, -d, d, d] +pz = [-d, -d, -d, -d, d, d, d, d] + +p2x = [0,0,0,0,0,0,0,0] +p2y = [0,0,0,0,0,0,0,0] +r = [0,0,0] + +def drawCube(): + r[0] = r[0] + pi / 180.0 + r[1] = r[1] + pi / 180.0 + r[2] = r[2] + pi / 180.0 + if (r[0] >= 360.0 * pi / 180.0): + r[0] = 0 + if (r[1] >= 360.0 * pi / 180.0): + r[1] = 0 + if (r[2] >= 360.0 * pi / 180.0): + r[2] = 0 + + for i in range(8): + px2 = px[i] + py2 = math.cos(r[0]) * py[i] - math.sin(r[0]) * pz[i] + pz2 = math.sin(r[0]) * py[i] + math.cos(r[0]) * pz[i] + + px3 = math.cos(r[1]) * px2 + math.sin(r[1]) * pz2 + py3 = py2 + pz3 = -math.sin(r[1]) * px2 + math.cos(r[1]) * pz2 + + ax = math.cos(r[2]) * px3 - math.sin(r[2]) * py3 + ay = math.sin(r[2]) * px3 + math.cos(r[2]) * py3 + az = pz3 - 150 + + p2x[i] = width / 2 + ax * size / az + p2y[i] = height / 2 + ay * size / az + + lcd.fill(0) + + for i in range(3): + lcd.line(int(p2x[i]), int(p2y[i]), int(p2x[i+1]), int(p2y[i+1]), 1) + lcd.line(int(p2x[i+4]), int(p2y[i+4]), int(p2x[i+5]), int(p2y[i+5]), 1) + lcd.line(int(p2x[i]), int(p2y[i]), int(p2x[i+4]), int(p2y[i+4]), 1) + + lcd.line(int(p2x[3]), int(p2y[3]), int(p2x[0]), int(p2y[0]), 1) + lcd.line(int(p2x[7]), int(p2y[7]), int(p2x[4]), int(p2y[4]), 1) + lcd.line(int(p2x[3]), int(p2y[3]), int(p2x[7]), int(p2y[7]), 1) + lcd.show() + +while(True): + drawCube() diff --git a/examples/addressing/README.md b/examples/addressing/README.md new file mode 100644 index 0000000..0a9ee8f --- /dev/null +++ b/examples/addressing/README.md @@ -0,0 +1,175 @@ +# Addressing + +The display is 84x48 pixels, 504 bytes, divided into 6 banks of 84x8 pixels, 84 bytes each. + +You can configure the display to draw consecutive bytes moving horizontally or vertically. + +In both modes, each byte is drawn to the display as a vertical column of 8 pixels. +You are unable to draw 8 horizontal pixels from a single byte. +The least significant bit in each byte is displayed at the top. + +In horizontal mode, every subsequent byte is drawn beside the current byte until the end of the bank, then moves to the start of the next bank. + +![Horizontal](../../docs/pcd8544-horizontal.gif) + +In vertical mode, every subsequent byte is drawn in the same column on the next bank until the last bank, where it wraps back to the first bank and moves across one column. + +![Vertical](../../docs/pcd8544-vertical.gif) + +Changing the addressing mode does not affect what is already displayed on the screen. It only affects the position the next bytes are drawn. + +## Example 1 + +The Nokia example image: + +![Nokia](../../docs/nokia-splash.gif) + +### Horizontal addressing mode: + +![Horizontal](../../docs/horizontal-addressing.png) + +Horizontal addressing is compatible with framebuf.MONO_VLSB + +```python +0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x40, 0x80, 0x80, 0xC0, 0xC0, 0x40, 0xC0, 0xA0, 0xE0, 0xC0, 0xE0, 0xE0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xFC, 0xFC, 0xFE, 0xEE, 0xF4, 0xF0, 0xF0, 0x70, 0x30, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x0C, 0x9C, 0x1C, 0x38, 0xB8, 0x38, 0x38, 0xB8, 0xF8, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF8, 0xF8, 0xF8, 0xF8, +0x88, 0x20, 0x8A, 0x20, 0x08, 0x22, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x02, 0x80, 0x71, 0xBA, 0xDA, 0xFD, 0xDD, 0xED, 0xDE, 0xEE, 0xF7, 0xFF, 0xFB, 0xFD, 0xFD, 0xFE, 0xFF, 0x7F, 0x3F, 0x1F, 0x9F, 0x3F, 0x7F, 0x6F, 0x0F, 0xAF, 0x1F, 0xBF, 0x3E, 0x3C, 0x7A, 0x78, 0x70, 0x22, 0x88, 0xA0, 0x2A, 0x80, 0x08, 0x62, 0xE0, 0xE0, 0xF2, 0xF0, 0x58, 0xDA, 0xF8, 0xFC, 0x92, 0xFE, 0xFF, 0xFF, 0xD3, 0xFF, 0xFD, 0xF3, 0xE1, 0xF0, 0xF9, 0x7F, 0xBF, 0x3F, 0x8F, 0x2F, 0x4F, 0xAF, 0x0F, 0x4F, 0xA7, 0x0F, 0xAF, 0x87, 0x2F, +0x82, 0x80, 0x20, 0xC0, 0x80, 0x80, 0x50, 0x40, 0xC4, 0xD0, 0xA0, 0xE8, 0xE4, 0xEA, 0xFF, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x4F, 0x27, 0x53, 0xA8, 0x54, 0x29, 0x4A, 0xB5, 0x82, 0xAC, 0xA1, 0x8A, 0xB6, 0x50, 0x4D, 0x32, 0xA4, 0x4A, 0xB4, 0xA9, 0x4A, 0x52, 0xB4, 0xAA, 0x45, 0xA8, 0xDA, 0x22, 0xAC, 0xD2, 0x2A, 0x52, 0xA8, 0x52, 0x4C, 0xB0, 0xAD, 0x43, 0x5B, 0xB3, 0x45, 0xA8, 0x5B, 0xA3, 0xAB, 0x55, 0xA8, 0x52, 0x54, 0xA9, 0x56, 0xA8, 0x45, 0xBA, 0xA4, 0x49, 0x5A, 0xA2, 0x54, 0xAA, 0x52, +0xFE, 0xFF, 0xFF, 0xFE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xBF, 0x7F, 0xBF, 0xBF, 0xFF, 0xDF, 0xBF, 0x5F, 0xDF, 0x7F, 0xDF, 0x7F, 0xDF, 0xAF, 0x7F, 0xEE, 0x8E, 0xF1, 0x6E, 0x99, 0xF7, 0x6A, 0xDD, 0xB2, 0x6E, 0xD5, 0x7A, 0xD7, 0xAC, 0x75, 0xDB, 0x6D, 0xD5, 0x7A, 0xD7, 0xAC, 0x7B, 0xE5, 0xDE, 0xA9, 0x77, 0xDA, 0xB5, 0xEE, 0x59, 0xB6, 0xEB, 0xDD, 0xB6, 0x69, 0xD6, 0xBF, 0xE8, 0x55, 0xEF, 0xB9, 0xD6, 0xED, 0xB5, 0x5B, +0xAB, 0xFF, 0xFD, 0xF7, 0xFF, 0x01, 0x01, 0x01, 0x01, 0xE1, 0xC1, 0x81, 0x03, 0x05, 0x0F, 0x1D, 0x2F, 0x7E, 0x01, 0x00, 0x01, 0x01, 0xFF, 0xFE, 0x03, 0x01, 0x01, 0x00, 0xF1, 0xF0, 0xF1, 0x71, 0xF1, 0xF1, 0xB1, 0xF1, 0x01, 0x01, 0x01, 0x03, 0xFE, 0xFF, 0x01, 0x01, 0x01, 0x01, 0xBE, 0x1B, 0x0D, 0x07, 0x03, 0x41, 0xE1, 0xF1, 0xF9, 0x6D, 0xFF, 0xFF, 0x00, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xEB, 0x3E, 0x0D, 0x03, 0x01, 0x41, 0x71, 0x70, 0x41, 0x01, 0x03, 0x0E, 0x3B, 0xEF, 0xFE, 0xFB, 0xEE, 0x7D, 0xF7, 0xFF, +0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xF8, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF0, 0xF0, 0xF0, 0xF8, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xF0, 0xF1, 0xF3, 0xF7, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xF3, 0xF0, 0xF0, 0xF0, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF0, 0xF0, 0xF0, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +``` + +```python +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +lcd.init(horizontal=True) +lcd.data(bytearray(b'\x80\x00\x00\x80\x00\x00\x80\x00\x00\x80\x00\x00\x80\x00\x00\x80\x00\x00\x80\x80\x40\x40\x40\x80\x80\xC0\xC0\x40\xC0\xA0\xE0\xC0\xE0\xE0\xF0\xF0\xF8\xF8\xF8\xFC\xFC\xFE\xEE\xF4\xF0\xF0\x70\x30\x00\x80\x00\x00\x80\x00\x0C\x9C\x1C\x38\xB8\x38\x38\xB8\xF8\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF0\xF8\xF8\xF8\xF8\x88\x20\x8A\x20\x08\x22\x08\x00\x0A\x00\x00\x02\x80\x71\xBA\xDA\xFD\xDD\xED\xDE\xEE\xF7\xFF\xFB\xFD\xFD\xFE\xFF\x7F\x3F\x1F\x9F\x3F\x7F\x6F\x0F\xAF\x1F\xBF\x3E\x3C\x7A\x78\x70\x22\x88\xA0\x2A\x80\x08\x62\xE0\xE0\xF2\xF0\x58\xDA\xF8\xFC\x92\xFE\xFF\xFF\xD3\xFF\xFD\xF3\xE1\xF0\xF9\x7F\xBF\x3F\x8F\x2F\x4F\xAF\x0F\x4F\xA7\x0F\xAF\x87\x2F\x82\x80\x20\xC0\x80\x80\x50\x40\xC4\xD0\xA0\xE8\xE4\xEA\xFF\xFB\xFD\xFF\xFF\xFF\xFF\xFF\xEF\x4F\x27\x53\xA8\x54\x29\x4A\xB5\x82\xAC\xA1\x8A\xB6\x50\x4D\x32\xA4\x4A\xB4\xA9\x4A\x52\xB4\xAA\x45\xA8\xDA\x22\xAC\xD2\x2A\x52\xA8\x52\x4C\xB0\xAD\x43\x5B\xB3\x45\xA8\x5B\xA3\xAB\x55\xA8\x52\x54\xA9\x56\xA8\x45\xBA\xA4\x49\x5A\xA2\x54\xAA\x52\xFE\xFF\xFF\xFE\xFD\xFF\xFF\xFF\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F\xFF\xFE\xBF\x7F\xBF\xBF\xFF\xDF\xBF\x5F\xDF\x7F\xDF\x7F\xDF\xAF\x7F\xEE\x8E\xF1\x6E\x99\xF7\x6A\xDD\xB2\x6E\xD5\x7A\xD7\xAC\x75\xDB\x6D\xD5\x7A\xD7\xAC\x7B\xE5\xDE\xA9\x77\xDA\xB5\xEE\x59\xB6\xEB\xDD\xB6\x69\xD6\xBF\xE8\x55\xEF\xB9\xD6\xED\xB5\x5B\xAB\xFF\xFD\xF7\xFF\x01\x01\x01\x01\xE1\xC1\x81\x03\x05\x0F\x1D\x2F\x7E\x01\x00\x01\x01\xFF\xFE\x03\x01\x01\x00\xF1\xF0\xF1\x71\xF1\xF1\xB1\xF1\x01\x01\x01\x03\xFE\xFF\x01\x01\x01\x01\xBE\x1B\x0D\x07\x03\x41\xE1\xF1\xF9\x6D\xFF\xFF\x00\x01\x01\x01\xFF\xFF\xEB\x3E\x0D\x03\x01\x41\x71\x70\x41\x01\x03\x0E\x3B\xEF\xFE\xFB\xEE\x7D\xF7\xFF\xFF\xFF\xFF\xFE\xFF\xF0\xF0\xF0\xF0\xFF\xFF\xFF\xFF\xFE\xFC\xF8\xF0\xF0\xF0\xF0\xF0\xF0\xFF\xFF\xF8\xF0\xF0\xF0\xF1\xF1\xF1\xF1\xF1\xF1\xF1\xF1\xF0\xF0\xF0\xF8\xFF\xFF\xF0\xF0\xF0\xF0\xFF\xFF\xFE\xFC\xF8\xF0\xF0\xF1\xF3\xF7\xFF\xFF\xF0\xF0\xF0\xF0\xFF\xF3\xF0\xF0\xF0\xFC\xFC\xFC\xFC\xFC\xFC\xFC\xFC\xF0\xF0\xF0\xF3\xFF\xFF\xFF\xFF\xFF')) +``` + + +### Vertical addressing mode: + +![Vertical](../../docs/vertical-addressing.png) + +```python +0x80, 0x88, 0x82, 0xFE, 0xAB, 0xFF, 0x00, 0x20, 0x80, 0xFF, 0xFF, 0xFF, 0x00, 0x8A, 0x20, 0xFF, 0xFD, 0xFF, 0x80, 0x20, 0xC0, 0xFE, 0xF7, 0xFE, 0x00, 0x08, 0x80, 0xFD, 0xFF, 0xFF, 0x00, 0x22, 0x80, 0xFF, 0x01, 0xF0, 0x80, 0x08, 0x50, 0xFF, 0x01, 0xF0, 0x00, 0x00, 0x40, 0xFF, 0x01, 0xF0, 0x00, 0x0A, 0xC4, 0xFE, 0x01, 0xF0, 0x80, 0x00, 0xD0, 0xFF, 0xE1, 0xFF, 0x00, 0x00, 0xA0, 0xFF, 0xC1, 0xFF, 0x00, 0x02, 0xE8, 0xFF, 0x81, 0xFF, 0x80, 0x80, 0xE4, 0xFF, 0x03, 0xFF, 0x00, 0x71, 0xEA, 0xFF, 0x05, 0xFE, 0x00, 0xBA, 0xFF, 0xFF, +0x0F, 0xFC, 0x80, 0xDA, 0xFB, 0xFF, 0x1D, 0xF8, 0x00, 0xFD, 0xFD, 0xFF, 0x2F, 0xF0, 0x00, 0xDD, 0xFF, 0xFF, 0x7E, 0xF0, 0x80, 0xED, 0xFF, 0xFF, 0x01, 0xF0, 0x80, 0xDE, 0xFF, 0xFF, 0x00, 0xF0, 0x40, 0xEE, 0xFF, 0xFF, 0x01, 0xF0, 0x40, 0xF7, 0xFF, 0x7F, 0x01, 0xF0, 0x40, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0x80, 0xFB, 0x4F, 0xFE, 0xFE, 0xFF, 0x80, 0xFD, 0x27, 0xBF, 0x03, 0xF8, 0xC0, 0xFD, 0x53, 0x7F, 0x01, 0xF0, 0xC0, 0xFE, 0xA8, 0xBF, 0x01, 0xF0, 0x40, 0xFF, 0x54, 0xBF, 0x00, 0xF0, 0xC0, 0x7F, 0x29, 0xFF, 0xF1, 0xF1, 0xA0, 0x3F, +0x4A, 0xDF, 0xF0, 0xF1, 0xE0, 0x1F, 0xB5, 0xBF, 0xF1, 0xF1, 0xC0, 0x9F, 0x82, 0x5F, 0x71, 0xF1, 0xE0, 0x3F, 0xAC, 0xDF, 0xF1, 0xF1, 0xE0, 0x7F, 0xA1, 0x7F, 0xF1, 0xF1, 0xF0, 0x6F, 0x8A, 0xDF, 0xB1, 0xF1, 0xF0, 0x0F, 0xB6, 0x7F, 0xF1, 0xF1, 0xF8, 0xAF, 0x50, 0xDF, 0x01, 0xF0, 0xF8, 0x1F, 0x4D, 0xAF, 0x01, 0xF0, 0xF8, 0xBF, 0x32, 0x7F, 0x01, 0xF0, 0xFC, 0x3E, 0xA4, 0xEE, 0x03, 0xF8, 0xFC, 0x3C, 0x4A, 0x8E, 0xFE, 0xFF, 0xFE, 0x7A, 0xB4, 0xF1, 0xFF, 0xFF, 0xEE, 0x78, 0xA9, 0x6E, 0x01, 0xF0, 0xF4, 0x70, 0x4A, 0x99, 0x01, 0xF0, +0xF0, 0x22, 0x52, 0xF7, 0x01, 0xF0, 0xF0, 0x88, 0xB4, 0x6A, 0x01, 0xF0, 0x70, 0xA0, 0xAA, 0xDD, 0xBE, 0xFF, 0x30, 0x2A, 0x45, 0xB2, 0x1B, 0xFF, 0x00, 0x80, 0xA8, 0x6E, 0x0D, 0xFE, 0x80, 0x08, 0xDA, 0xD5, 0x07, 0xFC, 0x00, 0x62, 0x22, 0x7A, 0x03, 0xF8, 0x00, 0xE0, 0xAC, 0xD7, 0x41, 0xF0, 0x80, 0xE0, 0xD2, 0xAC, 0xE1, 0xF0, 0x00, 0xF2, 0x2A, 0x75, 0xF1, 0xF1, 0x0C, 0xF0, 0x52, 0xDB, 0xF9, 0xF3, 0x9C, 0x58, 0xA8, 0x6D, 0x6D, 0xF7, 0x1C, 0xDA, 0x52, 0xD5, 0xFF, 0xFF, 0x38, 0xF8, 0x4C, 0x7A, 0xFF, 0xFF, 0xB8, 0xFC, 0xB0, 0xD7, +0x00, 0xF0, 0x38, 0x92, 0xAD, 0xAC, 0x01, 0xF0, 0x38, 0xFE, 0x43, 0x7B, 0x01, 0xF0, 0xB8, 0xFF, 0x5B, 0xE5, 0x01, 0xF0, 0xF8, 0xFF, 0xB3, 0xDE, 0xFF, 0xFF, 0xF0, 0xD3, 0x45, 0xA9, 0xFF, 0xF3, 0xF0, 0xFF, 0xA8, 0x77, 0xEB, 0xF0, 0xF0, 0xFD, 0x5B, 0xDA, 0x3E, 0xF0, 0xF0, 0xF3, 0xA3, 0xB5, 0x0D, 0xF0, 0xF0, 0xE1, 0xAB, 0xEE, 0x03, 0xFC, 0xF0, 0xF0, 0x55, 0x59, 0x01, 0xFC, 0xF0, 0xF9, 0xA8, 0xB6, 0x41, 0xFC, 0xF0, 0x7F, 0x52, 0xEB, 0x71, 0xFC, 0xF0, 0xBF, 0x54, 0xDD, 0x70, 0xFC, 0xF0, 0x3F, 0xA9, 0xB6, 0x41, 0xFC, 0xF0, 0x8F, +0x56, 0x69, 0x01, 0xFC, 0xF0, 0x2F, 0xA8, 0xD6, 0x03, 0xFC, 0xF0, 0x4F, 0x45, 0xBF, 0x0E, 0xF0, 0xF0, 0xAF, 0xBA, 0xE8, 0x3B, 0xF0, 0xF0, 0x0F, 0xA4, 0x55, 0xEF, 0xF0, 0xF0, 0x4F, 0x49, 0xEF, 0xFE, 0xF3, 0xF0, 0xA7, 0x5A, 0xB9, 0xFB, 0xFF, 0xF8, 0x0F, 0xA2, 0xD6, 0xEE, 0xFF, 0xF8, 0xAF, 0x54, 0xED, 0x7D, 0xFF, 0xF8, 0x87, 0xAA, 0xB5, 0xF7, 0xFF, 0xF8, 0x2F, 0x52, 0x5B, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +``` + +```python +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +lcd.init(horizontal=False) +lcd.data(bytearray(b'\x80\x88\x82\xFE\xAB\xFF\x00\x20\x80\xFF\xFF\xFF\x00\x8A\x20\xFF\xFD\xFF\x80\x20\xC0\xFE\xF7\xFE\x00\x08\x80\xFD\xFF\xFF\x00\x22\x80\xFF\x01\xF0\x80\x08\x50\xFF\x01\xF0\x00\x00\x40\xFF\x01\xF0\x00\x0A\xC4\xFE\x01\xF0\x80\x00\xD0\xFF\xE1\xFF\x00\x00\xA0\xFF\xC1\xFF\x00\x02\xE8\xFF\x81\xFF\x80\x80\xE4\xFF\x03\xFF\x00\x71\xEA\xFF\x05\xFE\x00\xBA\xFF\xFF\x0F\xFC\x80\xDA\xFB\xFF\x1D\xF8\x00\xFD\xFD\xFF\x2F\xF0\x00\xDD\xFF\xFF\x7E\xF0\x80\xED\xFF\xFF\x01\xF0\x80\xDE\xFF\xFF\x00\xF0\x40\xEE\xFF\xFF\x01\xF0\x40\xF7\xFF\x7F\x01\xF0\x40\xFF\xEF\xFF\xFF\xFF\x80\xFB\x4F\xFE\xFE\xFF\x80\xFD\x27\xBF\x03\xF8\xC0\xFD\x53\x7F\x01\xF0\xC0\xFE\xA8\xBF\x01\xF0\x40\xFF\x54\xBF\x00\xF0\xC0\x7F\x29\xFF\xF1\xF1\xA0\x3F\x4A\xDF\xF0\xF1\xE0\x1F\xB5\xBF\xF1\xF1\xC0\x9F\x82\x5F\x71\xF1\xE0\x3F\xAC\xDF\xF1\xF1\xE0\x7F\xA1\x7F\xF1\xF1\xF0\x6F\x8A\xDF\xB1\xF1\xF0\x0F\xB6\x7F\xF1\xF1\xF8\xAF\x50\xDF\x01\xF0\xF8\x1F\x4D\xAF\x01\xF0\xF8\xBF\x32\x7F\x01\xF0\xFC\x3E\xA4\xEE\x03\xF8\xFC\x3C\x4A\x8E\xFE\xFF\xFE\x7A\xB4\xF1\xFF\xFF\xEE\x78\xA9\x6E\x01\xF0\xF4\x70\x4A\x99\x01\xF0\xF0\x22\x52\xF7\x01\xF0\xF0\x88\xB4\x6A\x01\xF0\x70\xA0\xAA\xDD\xBE\xFF\x30\x2A\x45\xB2\x1B\xFF\x00\x80\xA8\x6E\x0D\xFE\x80\x08\xDA\xD5\x07\xFC\x00\x62\x22\x7A\x03\xF8\x00\xE0\xAC\xD7\x41\xF0\x80\xE0\xD2\xAC\xE1\xF0\x00\xF2\x2A\x75\xF1\xF1\x0C\xF0\x52\xDB\xF9\xF3\x9C\x58\xA8\x6D\x6D\xF7\x1C\xDA\x52\xD5\xFF\xFF\x38\xF8\x4C\x7A\xFF\xFF\xB8\xFC\xB0\xD7\x00\xF0\x38\x92\xAD\xAC\x01\xF0\x38\xFE\x43\x7B\x01\xF0\xB8\xFF\x5B\xE5\x01\xF0\xF8\xFF\xB3\xDE\xFF\xFF\xF0\xD3\x45\xA9\xFF\xF3\xF0\xFF\xA8\x77\xEB\xF0\xF0\xFD\x5B\xDA\x3E\xF0\xF0\xF3\xA3\xB5\x0D\xF0\xF0\xE1\xAB\xEE\x03\xFC\xF0\xF0\x55\x59\x01\xFC\xF0\xF9\xA8\xB6\x41\xFC\xF0\x7F\x52\xEB\x71\xFC\xF0\xBF\x54\xDD\x70\xFC\xF0\x3F\xA9\xB6\x41\xFC\xF0\x8F\x56\x69\x01\xFC\xF0\x2F\xA8\xD6\x03\xFC\xF0\x4F\x45\xBF\x0E\xF0\xF0\xAF\xBA\xE8\x3B\xF0\xF0\x0F\xA4\x55\xEF\xF0\xF0\x4F\x49\xEF\xFE\xF3\xF0\xA7\x5A\xB9\xFB\xFF\xF8\x0F\xA2\xD6\xEE\xFF\xF8\xAF\x54\xED\x7D\xFF\xF8\x87\xAA\xB5\xF7\xFF\xF8\x2F\x52\x5B\xFF\xFF')) +``` + +## Example 2 + +Draw one byte at a time. + +```python +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) +lcd.init() +``` + +### Horizontal addressing mode: + +```python +lcd.clear() +lcd.position(0, 0) +lcd.addressing(horizontal=True) + +# pixels 0,0 to 0,7 +lcd.data(bytearray(b'\xff')) + +# pixels 1,0 to 1,7 +lcd.data(bytearray(b'\xff')) + +# pixels 2,0 to 2,7 +lcd.data(bytearray(b'\xff')) + +# pixels 3,0 to 3,7 +lcd.data(bytearray(b'\xff')) +``` + +### Vertical addressing mode: + +```python +lcd.clear() +lcd.position(0, 0) +lcd.addressing(horizontal=False) + +# pixels 0,0 to 0,7 +lcd.data(bytearray(b'\xff')) + +# pixels 0,8 to 0,15 +lcd.data(bytearray(b'\xff')) + +# pixels 0,16 to 0,23 +lcd.data(bytearray(b'\xff')) + +# pixels 0,24 to 0,31 +lcd.data(bytearray(b'\xff')) + +# pixels 0,32 to 0,39 +lcd.data(bytearray(b'\xff')) + +# pixels 0,40 to 0,47 +lcd.data(bytearray(b'\xff')) + +# pixels 1,0 to 1,7 +lcd.data(bytearray(b'\xff')) +``` + +## Example 3 + +### Stripes + +```python +lcd.clear() +lcd.addressing(horizontal=True) +# \\\\ +lcd.data(bytearray([0x11,0x22,0x44,0x88]*126)) +# //// +lcd.data(bytearray([0x88,0x44,0x22,0x11]*126)) +# \/\/ +lcd.data(bytearray([0x81,0x42,0x24,0x18]*126)) +# |||| +lcd.data(bytearray([0xff,0x00]*252)) +# ==== +lcd.data(bytearray([0x55]*504)) +``` diff --git a/examples/backlight_pwm.py b/examples/backlight_pwm.py new file mode 100644 index 0000000..64aa56d --- /dev/null +++ b/examples/backlight_pwm.py @@ -0,0 +1,25 @@ +# Backlight PWM + +import pcd8544 +from machine import Pin, SPI, PWM + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) + +# backlight on +bl = Pin(12, Pin.OUT, value=1) + +# backlight dimming +bl_pwm = PWM(bl) +bl_pwm.freq(500) +bl_pwm.duty(0) # off +bl_pwm.duty(512) # dim +bl_pwm.duty(1024) # bright + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +# test pattern (50% on) +lcd.data(bytearray([0x55, 0xAA] * 42 * 6)) diff --git a/examples/framebuf.py b/examples/framebuf.py new file mode 100644 index 0000000..f647446 --- /dev/null +++ b/examples/framebuf.py @@ -0,0 +1,74 @@ +# Using the MicroPython 1.9 framebuffer + +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +import framebuf +buffer = bytearray((pcd8544.HEIGHT // 8) * pcd8544.WIDTH) +framebuf = framebuf.FrameBuffer(buffer, pcd8544.WIDTH, pcd8544.HEIGHT, framebuf.MONO_VLSB) + +# fill(color) +framebuf.fill(1) +lcd.data(buffer) + +# text(string, x, y, color) +framebuf.fill(0) +framebuf.text('Nokia 5110', 0, 0, 1) +framebuf.text('PCD8544', 0, 10, 1) +framebuf.text('84x48', 0, 20, 1) +framebuf.text('uPython1.9', 0, 30, 1) +framebuf.text('ESP8266', 0, 40, 1) +lcd.data(buffer) + +# pixel(x, y, colour) +framebuf.fill(0) +framebuf.pixel(0, 0, 1) +framebuf.pixel(0, 2, 1) +framebuf.pixel(2, 0, 1) +framebuf.pixel(2, 2, 1) +lcd.data(buffer) + +# line(x1, y1, x2, y2, color) +framebuf.fill(0) +framebuf.line(0, 47, 83, 0, 1) +framebuf.line(83, 47, 0, 0, 1) +lcd.data(buffer) + +# hline(x, y, w, color) +framebuf.fill(0) +framebuf.hline(0, 10, 20, 1) +framebuf.hline(0, 20, 41, 1) +framebuf.hline(0, 30, 62, 1) +framebuf.hline(0, 40, 83, 1) +lcd.data(buffer) + +# vline(x, y, h, color) +framebuf.fill(0) +framebuf.vline(10, 0, 11, 1) +framebuf.vline(20, 0, 23, 1) +framebuf.vline(30, 0, 35, 1) +framebuf.vline(40, 0, 47, 1) +lcd.data(buffer) + +# rect(x, y, w, h, c) +framebuf.fill(0) +framebuf.rect(0, 0, 20, 20, 1) +framebuf.rect(10, 10, 20, 20, 1) +framebuf.rect(20, 20, 20, 20, 1) +lcd.data(buffer) + +# fill_rect(x, y, w, h, c) +framebuf.fill(0) +framebuf.fill_rect(0, 0, 20, 20, 1) +framebuf.fill_rect(20, 20, 20, 20, 1) +framebuf.fill_rect(10, 10, 20, 20, 0) +lcd.data(buffer) diff --git a/examples/framebuf_extend.py b/examples/framebuf_extend.py new file mode 100644 index 0000000..de61b8d --- /dev/null +++ b/examples/framebuf_extend.py @@ -0,0 +1,26 @@ +# Extending PCD8544 with Framebuf methods + +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544_FRAMEBUF(spi, cs, dc, rst) + +# fill(color) +lcd.fill(1) +lcd.show() + +# text(string, x, y, color) +lcd.fill(0) +lcd.text('Nokia 5110', 0, 0, 1) +lcd.text('PCD8544', 0, 10, 1) +lcd.text('84x48', 0, 20, 1) +lcd.text('uPython1.9', 0, 30, 1) +lcd.text('ESP8266', 0, 40, 1) +lcd.show() diff --git a/examples/framebuf_inherit.py b/examples/framebuf_inherit.py new file mode 100644 index 0000000..2294a4b --- /dev/null +++ b/examples/framebuf_inherit.py @@ -0,0 +1,25 @@ +# Inherting the framebuffer + +import pcd8544_fb +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544_fb.PCD8544_FB(spi, cs, dc, rst) + +lcd.text('Hello', 0, 0, 1) +lcd.pixel(30, 30, 1) +lcd.pixel(31, 31, 1) +lcd.pixel(32, 32, 1) +lcd.pixel(33, 33, 1) +lcd.pixel(34, 34, 1) +lcd.hline(0, 20, 8, 1) +lcd.vline(30, 8, 8, 1) +lcd.rect(40, 16, 8, 8, 1) +lcd.fill_rect(16, 32, 8, 8, 1) +lcd.show() diff --git a/examples/http-get/http_get_image.py b/examples/http-get/http_get_image.py new file mode 100644 index 0000000..d03590e --- /dev/null +++ b/examples/http-get/http_get_image.py @@ -0,0 +1,33 @@ +# Load a remote image with urequests + +# You need to be connected to the internet to fetch the image +import network +sta_if = network.WLAN(network.STA_IF) +sta_if.active(True) +sta_if.scan() +sta_if.connect('SSID', 'PASSWORD') # change me + + +# Install urequests with upip, or simply copy the file to your board +import urequests + +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +lcd.init() + +# fetch remote image and display +# image is 84x48 pixels, 504 bytes, in horizontal address format +# see wemos.gif +r = urequests.get('http://202.4.227.150/wemos.dat') +lcd.data(r.content) +r.close() diff --git a/examples/http-get/urequests.py b/examples/http-get/urequests.py new file mode 100644 index 0000000..acb220e --- /dev/null +++ b/examples/http-get/urequests.py @@ -0,0 +1,124 @@ +import usocket + +class Response: + + def __init__(self, f): + self.raw = f + self.encoding = "utf-8" + self._cached = None + + def close(self): + if self.raw: + self.raw.close() + self.raw = None + self._cached = None + + @property + def content(self): + if self._cached is None: + try: + self._cached = self.raw.read() + finally: + self.raw.close() + self.raw = None + return self._cached + + @property + def text(self): + return str(self.content, self.encoding) + + def json(self): + import ujson + return ujson.loads(self.content) + + +def request(method, url, data=None, json=None, headers={}, stream=None): + try: + proto, dummy, host, path = url.split("/", 3) + except ValueError: + proto, dummy, host = url.split("/", 2) + path = "" + if proto == "http:": + port = 80 + elif proto == "https:": + import ussl + port = 443 + else: + raise ValueError("Unsupported protocol: " + proto) + + if ":" in host: + host, port = host.split(":", 1) + port = int(port) + + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) + ai = ai[0] + + s = usocket.socket(ai[0], ai[1], ai[2]) + try: + s.connect(ai[-1]) + if proto == "https:": + s = ussl.wrap_socket(s, server_hostname=host) + s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) + if not "Host" in headers: + s.write(b"Host: %s\r\n" % host) + # Iterate over keys to avoid tuple alloc + for k in headers: + s.write(k) + s.write(b": ") + s.write(headers[k]) + s.write(b"\r\n") + if json is not None: + assert data is None + import ujson + data = ujson.dumps(json) + s.write(b"Content-Type: application/json\r\n") + if data: + s.write(b"Content-Length: %d\r\n" % len(data)) + s.write(b"\r\n") + if data: + s.write(data) + + l = s.readline() + #print(l) + l = l.split(None, 2) + status = int(l[1]) + reason = "" + if len(l) > 2: + reason = l[2].rstrip() + while True: + l = s.readline() + if not l or l == b"\r\n": + break + #print(l) + if l.startswith(b"Transfer-Encoding:"): + if b"chunked" in l: + raise ValueError("Unsupported " + l) + elif l.startswith(b"Location:") and not 200 <= status <= 299: + raise NotImplementedError("Redirects not yet supported") + except OSError: + s.close() + raise + + resp = Response(s) + resp.status_code = status + resp.reason = reason + return resp + + +def head(url, **kw): + return request("HEAD", url, **kw) + +def get(url, **kw): + return request("GET", url, **kw) + +def post(url, **kw): + return request("POST", url, **kw) + +def put(url, **kw): + return request("PUT", url, **kw) + +def patch(url, **kw): + return request("PATCH", url, **kw) + +def delete(url, **kw): + return request("DELETE", url, **kw) diff --git a/examples/http-get/wemos.dat b/examples/http-get/wemos.dat new file mode 100644 index 0000000..5abd730 Binary files /dev/null and b/examples/http-get/wemos.dat differ diff --git a/examples/http-get/wemos.gif b/examples/http-get/wemos.gif new file mode 100644 index 0000000..df58b0a Binary files /dev/null and b/examples/http-get/wemos.gif differ diff --git a/examples/http-get/wemos.py b/examples/http-get/wemos.py new file mode 100644 index 0000000..76cd717 --- /dev/null +++ b/examples/http-get/wemos.py @@ -0,0 +1,2 @@ +# horizontal addressing +bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xE0\xF8\xFC\x7C\x1C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x38\x78\xF8\xF0\xC0\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x80\xC0\xE0\xE0\xF0\x70\x78\x38\x38\x38\x38\x38\x38\x38\x38\x38\x78\x70\xF0\xE0\xE0\xC0\x80\x00\x00\x00\xF0\xFF\xFF\x7F\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x07\x3F\xFF\xFE\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x80\x80\x80\x80\x00\x00\x00\x00\x00\x00\x00\x81\x83\x83\x89\x9C\x3C\x1E\x8E\xCF\xCF\xE7\xE7\xE7\xE7\xE7\xCF\xCF\x9E\x3E\x3C\x18\x01\x03\x03\x00\x00\x00\x7F\xFF\xFF\xF8\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\xC0\xE0\xE0\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\x7F\x00\x00\xC0\xF0\xF0\xB8\x98\x98\x98\xF8\xF0\xE0\x00\x00\xFF\xFF\xFF\x0F\x3F\xFE\xF8\xC0\x00\x80\xF0\xFE\x1F\x0F\xFF\xFF\xFF\x00\x00\x00\xC1\xE3\xF1\xF0\xF0\xF1\xF1\xE3\x81\x00\x00\xE0\xF0\xF8\xD8\x98\x98\x18\x10\x00\x01\x07\x0F\x1F\x1E\x3C\x3C\x38\x3C\x3C\x3C\x1E\x1F\x0F\x07\x0F\x1F\x1E\x3C\x3C\x38\x38\x38\x3C\x1E\x1F\x0F\x07\x01\x00\x00\x00\x07\x0F\x1F\x1D\x19\x19\x19\x19\x19\x01\x00\x00\x1F\x1F\x1F\x00\x00\x01\x0F\x1F\x1F\x1F\x07\x00\x00\x00\x1F\x1F\x1F\x00\x00\x00\x01\x07\x07\x0F\x0F\x07\x07\x03\x00\x00\x00\x18\x19\x19\x19\x1B\x1F\x1F\x0F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') diff --git a/examples/qr-code/qr-code.gif b/examples/qr-code/qr-code.gif new file mode 100644 index 0000000..41d00de Binary files /dev/null and b/examples/qr-code/qr-code.gif differ diff --git a/examples/qr-code/qr_code.py b/examples/qr-code/qr_code.py new file mode 100644 index 0000000..d1047f0 --- /dev/null +++ b/examples/qr-code/qr_code.py @@ -0,0 +1,29 @@ +# QR Code +# You'll have to scan it to find out what it is. + +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +import framebuf +buffer = bytearray((pcd8544.HEIGHT // 8) * pcd8544.WIDTH) +fbuf = framebuf.FrameBuffer(buffer, pcd8544.WIDTH, pcd8544.HEIGHT, framebuf.MONO_VLSB) + +fbuf.fill(0) + +# QR Code - col major msb +# See qr-code.gif +qr = bytearray(b'\x7F\x41\x5D\x5D\x5D\x41\x7F\x00\xF6\x1A\xF3\x80\x70\x38\xEB\xB7\x60\x00\x7F\x41\x5D\x5D\x5D\x41\x7F\x47\x8B\xA4\xF5\x96\xF6\x55\x89\x98\x8A\x18\xCB\x72\x9B\x73\x00\xD3\x1E\x7B\x79\xC5\x30\x61\x95\x76\xFD\x05\x74\x75\x75\x04\xFD\x01\xA6\xBD\x96\x91\xBA\xD6\x68\x4F\x9F\xD1\x15\x71\x7F\xBF\x69\x0C\x46\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x01\x01\x00\x01\x01\x00\x00\x00\x01\x01') +qr_fbuf = framebuf.FrameBuffer(qr, 25, 25, framebuf.MONO_VLSB) + +fbuf.blit(qr_fbuf, int((pcd8544.WIDTH-25)/2), int((pcd8544.HEIGHT-25)/2), 0) + +lcd.clear() +lcd.data(buffer) diff --git a/examples/screen_saver.py b/examples/screen_saver.py new file mode 100644 index 0000000..12764b5 --- /dev/null +++ b/examples/screen_saver.py @@ -0,0 +1,63 @@ +# Screen Saver +# Move a bitmap around, bounce it off walls like an old screensaver + +import pcd8544 +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) +lcd = pcd8544.PCD8544(spi, cs, dc, rst) + +import framebuf +buffer = bytearray((pcd8544.HEIGHT // 8) * pcd8544.WIDTH) +fbuf = framebuf.FrameBuffer(buffer, pcd8544.WIDTH, pcd8544.HEIGHT, framebuf.MONO_VLSB) + +# smiley 15x15 - col major msb +smiley = bytearray(b'\xE0\x38\xE4\x22\xA2\xE1\xE1\x61\xE1\x21\xA2\xE2\xE4\x38\xE0\x03\x0C\x10\x21\x21\x41\x48\x48\x48\x49\x25\x21\x10\x0C\x03') +smiley_w = 15 +smiley_h = 15 +smiley_fbuf = framebuf.FrameBuffer(smiley, smiley_w, smiley_h, framebuf.MONO_VLSB) + +# area the smiley can move in +bounds_w = pcd8544.WIDTH - smiley_w +bounds_h = pcd8544.HEIGHT - smiley_h + +# direction smiley is moving +move_x = 1 +move_y = 1 + +# pause between displaying frames +from time import sleep_ms +pause = 100 + +# start position +x = 1 +y = 1 + +def render(): + global x + global y + global move_x + global move_y + # Draw the bitmap + fbuf.fill(0) + fbuf.blit(smiley_fbuf, x, y, 0) + lcd.data(buffer) + + sleep_ms(pause) + + # Move down right until hit bounds + # Then flip increment to decrement to bounce off the wall + x = x + move_x + y = y + move_y + if (x <= 0 or x >= bounds_w): + move_x = -move_x + if (y <= 0 or y >= bounds_h): + move_y = -move_y + +while(True): + render() diff --git a/examples/sine_wave.py b/examples/sine_wave.py new file mode 100644 index 0000000..5b497c0 --- /dev/null +++ b/examples/sine_wave.py @@ -0,0 +1,59 @@ +# Sine waves + +import time +import math + +import pcd8544_fb +from machine import Pin, SPI + +spi = SPI(1) +spi.init(baudrate=2000000, polarity=0, phase=0) +cs = Pin(2) +dc = Pin(15) +rst = Pin(0) +bl = Pin(12, Pin.OUT, value=1) + +lcd = pcd8544_fb.PCD8544_FB(spi, cs, dc, rst) + + +def draw_sin(amplitude, freq, phase, yoffset=24): + for i in range(freq): + y = int((math.sin((i + phase) * 0.017453) * amplitude) + yoffset) + x = int((84 / freq) * i) + lcd.pixel(x, y, 1) + lcd.show() + +def draw_cos(amplitude, freq, phase, yoffset=24): + for i in range(freq): + y = int((math.cos((i + phase) * 0.017453) * amplitude) + yoffset) + x = int((84 / freq) * i) + lcd.pixel(x, y, 1) + lcd.show() + + +# big wave +lcd.fill(0) +draw_sin(20, 360, 0) + +# little wave +lcd.fill(0) +draw_sin(10, 5*360, 0) + +# tiny wave +draw_sin(5, 10*360, 0) + +# two waves +lcd.fill(0) +draw_sin(10, 4*360, 0, 12*1) +draw_cos(10, 4*360, 0, 12*3) + +# three waves +lcd.fill(0) +draw_sin(20, 360, 0) +draw_sin(15, 2*360, 30) +draw_sin(10, 4*360, 60) + +# lots of waves +lcd.fill(0) +for i in range(0,360,30): + draw_sin(20, 360, i) diff --git a/pcd8544.py b/pcd8544.py index 66fe40c..bac58e8 100644 --- a/pcd8544.py +++ b/pcd8544.py @@ -1,8 +1,33 @@ -# MicroPython PCD8544 84x48 LCD driver, eg Nokia 5110 LCD. +""" +MicroPython Nokia 5110 PCD8544 84x48 LCD driver +https://github.com/mcauser/micropython-pcd8544 + +MIT License +Copyright (c) 2016-2018 Mike Causer + +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. +""" from micropython import const from ustruct import pack from utime import sleep_us +import framebuf # Function set 0010 0xxx FUNCTION_SET = const(0x20) @@ -39,16 +64,20 @@ COL_ADDR = const(0x80) # x pos (0~83) BANK_ADDR = const(0x40) # y pos, in banks of 8 rows (0~5) +# Display dimensions +WIDTH = const(0x54) # 84 +HEIGHT = const(0x30) # 48 + class PCD8544: def __init__(self, spi, cs, dc, rst=None): - self.width = 84 - self.height = 48 - self.spi = spi self.cs = cs # chip enable, active LOW self.dc = dc # data HIGH, command LOW self.rst = rst # reset, active LOW + self.height = HEIGHT # For Writer class + self.width = WIDTH + self.cs.init(self.cs.OUT, value=1) self.dc.init(self.dc.OUT, value=0) @@ -60,17 +89,14 @@ def __init__(self, spi, cs, dc, rst=None): def init(self, horizontal=True, contrast=0x3f, bias=BIAS_1_40, temp=TEMP_COEFF_2): # power up, horizontal addressing, basic instruction set self.fn = FUNCTION_SET - - # switch to vertical addressing - if not horizontal: - self.fn |= ADDRESSING_VERT - + self.addressing(horizontal) self.contrast(contrast, bias, temp) self.cmd(DISPLAY_NORMAL) self.clear() def reset(self): # issue reset impulse to reset the display + # you need to call power_on() or init() to resume self.rst(1) sleep_us(100) self.rst(0) @@ -108,12 +134,20 @@ def invert(self, invert): def clear(self): # clear DDRAM, reset x,y position to 0,0 - self.data([0] * (self.height * self.width // 8)) + self.data([0] * (HEIGHT * WIDTH // 8)) self.position(0, 0) + def addressing(self, horizontal=True): + # vertical or horizontal addressing + if horizontal: + self.fn &= ~ADDRESSING_VERT + else: + self.fn |= ADDRESSING_VERT + self.cmd(self.fn) + def position(self, x, y): # set cursor to column x (0~83), bank y (0~5) - self.cmd(COL_ADDR | x) # set x pos (0~83) + self.cmd(COL_ADDR | x) # set x pos (0~83) self.cmd(BANK_ADDR | y) # set y pos (0~5) def cmd(self, command): @@ -127,3 +161,41 @@ def data(self, data): self.cs(0) self.spi.write(pack('B'*len(data), *data)) self.cs(1) + + +class PCD8544_FRAMEBUF(PCD8544): + def __init__(self, spi, cs, dc, rst=None): + super().__init__(spi, cs, dc, rst) + self.buf = bytearray((HEIGHT // 8) * WIDTH) + self.fbuf = framebuf.FrameBuffer(self.buf, WIDTH, HEIGHT, framebuf.MONO_VLSB) + + def fill(self, col): + self.fbuf.fill(col) + + def pixel(self, x, y, col): + self.fbuf.pixel(x, y, col) + + def scroll(self, dx, dy): + self.fbuf.scroll(dx, dy) + # software scroll + + def text(self, string, x, y, col): + self.fbuf.text(string, x, y, col) + + def line(self, x1, y1, x2, y2, col): + self.fbuf.line(x1, y1, x2, y2, col) + + def hline(self, x, y, w, col): + self.fbuf.hline(x, y, w, col) + + def vline(self, x, y, h, col): + self.fbuf.vline(x, y, h, col) + + def rect(self, x, y, w, h, col): + self.fbuf.rect(x, y, w, h, col) + + def fill_rect(self, x, y, w, h, col): + self.fbuf.fill_rect(x, y, w, h, col) + + def show(self): + self.data(self.buf) diff --git a/pcd8544_fb.py b/pcd8544_fb.py new file mode 100644 index 0000000..f7fc5a0 --- /dev/null +++ b/pcd8544_fb.py @@ -0,0 +1,170 @@ +""" +MicroPython Nokia 5110 PCD8544 84x48 LCD driver +https://github.com/mcauser/micropython-pcd8544 + +MIT License +Copyright (c) 2016-2018 Mike Causer + +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. +""" + +from micropython import const +from ustruct import pack +from utime import sleep_us +import framebuf + +# Function set 0010 0xxx +FUNCTION_SET = const(0x20) +POWER_DOWN = const(0x04) +ADDRESSING_VERT = const(0x02) +EXTENDED_INSTR = const(0x01) + +# Display control 0000 1x0x +DISPLAY_BLANK = const(0x08) +DISPLAY_ALL = const(0x09) +DISPLAY_NORMAL = const(0x0c) +DISPLAY_INVERSE = const(0x0d) + +# Temperature control 0000 01xx +TEMP_COEFF_0 = const(0x04) +TEMP_COEFF_1 = const(0x05) +TEMP_COEFF_2 = const(0x06) # default +TEMP_COEFF_3 = const(0x07) + +# Bias system 0001 0xxx +BIAS_1_100 = const(0x10) +BIAS_1_80 = const(0x11) +BIAS_1_65 = const(0x12) +BIAS_1_48 = const(0x13) +BIAS_1_40 = const(0x14) # default +BIAS_1_24 = const(0x15) +BIAS_1_18 = const(0x16) +BIAS_1_10 = const(0x17) + +# Set operation voltage +SET_VOP = const(0x80) + +# DDRAM addresses +COL_ADDR = const(0x80) # x pos (0~83) +BANK_ADDR = const(0x40) # y pos, in banks of 8 rows (0~5) + +# Display dimensions +WIDTH = const(0x54) # 84 +HEIGHT = const(0x30) # 48 + +class PCD8544_FB(framebuf.FrameBuffer): + def __init__(self, spi, cs, dc, rst=None): + self.spi = spi + self.cs = cs # chip enable, active LOW + self.dc = dc # data HIGH, command LOW + self.rst = rst # reset, active LOW + + self.height = HEIGHT # For Writer class + self.width = WIDTH + + self.cs.init(self.cs.OUT, value=1) + self.dc.init(self.dc.OUT, value=0) + + if self.rst: + self.rst.init(self.rst.OUT, value=1) + + self.buf = bytearray((HEIGHT // 8) * WIDTH) + super().__init__(self.buf, WIDTH, HEIGHT, framebuf.MONO_VLSB) + + self.reset() + self.init() + + def init(self, horizontal=True, contrast=0x3f, bias=BIAS_1_40, temp=TEMP_COEFF_2): + # power up, horizontal addressing, basic instruction set + self.fn = FUNCTION_SET + self.addressing(horizontal) + self.contrast(contrast, bias, temp) + self.cmd(DISPLAY_NORMAL) + self.clear() + + def reset(self): + # issue reset impulse to reset the display + # you need to call power_on() or init() to resume + self.rst(1) + sleep_us(100) + self.rst(0) + sleep_us(100) # reset impulse has to be >100 ns and <100 ms + self.rst(1) + sleep_us(100) + + def power_on(self): + self.cs(1) + self.fn &= ~POWER_DOWN + self.cmd(self.fn) + + def power_off(self): + self.fn |= POWER_DOWN + self.cmd(self.fn) + + def contrast(self, contrast=0x3f, bias=BIAS_1_40, temp=TEMP_COEFF_2): + for cmd in ( + # extended instruction set is required to set temp, bias and vop + self.fn | EXTENDED_INSTR, + # set temperature coefficient + temp, + # set bias system (n=3 recommended mux rate 1:40/1:34) + bias, + # set contrast with operating voltage (0x00~0x7f) + # 0x00 = 3.00V, 0x3f = 6.84V, 0x7f = 10.68V + # starting at 3.06V, each bit increments voltage by 0.06V at room temperature + SET_VOP | contrast, + # revert to basic instruction set + self.fn & ~EXTENDED_INSTR): + self.cmd(cmd) + + def invert(self, invert): + self.cmd(DISPLAY_INVERSE if invert else DISPLAY_NORMAL) + + def clear(self): + # clear DDRAM, reset x,y position to 0,0 + self.data([0] * (HEIGHT * WIDTH // 8)) + self.position(0, 0) + + def addressing(self, horizontal=True): + # vertical or horizontal addressing + if horizontal: + self.fn &= ~ADDRESSING_VERT + else: + self.fn |= ADDRESSING_VERT + self.cmd(self.fn) + + def position(self, x, y): + # set cursor to column x (0~83), bank y (0~5) + self.cmd(COL_ADDR | x) # set x pos (0~83) + self.cmd(BANK_ADDR | y) # set y pos (0~5) + + def cmd(self, command): + self.dc(0) + self.cs(0) + self.spi.write(bytearray([command])) + self.cs(1) + + def data(self, data): + self.dc(1) + self.cs(0) + self.spi.write(pack('B'*len(data), *data)) + self.cs(1) + + def show(self): + self.data(self.buf)