├── .gitignore ├── library.properties ├── .travis.yml ├── LICENSE.txt ├── examples ├── HelloWorld │ └── HelloWorld.ino └── Thermometer │ └── Thermometer.ino ├── README.md ├── PCD8544.h ├── charset.cpp └── PCD8544.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=PCD8544 2 | version=1.4.4 3 | author=Carlos Rodrigues 4 | maintainer=Carlos Rodrigues 5 | sentence=Philips PCD8544 or compatible LCD library. 6 | paragraph=PCD8544 supports monochrome LCDs most commonly found on old Nokia phones. This is a minimal library intended for low memory usage. 7 | category=Display 8 | url=https://github.com/carlosefr/pcd8544 9 | architectures=* 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | sudo: false 8 | dist: trusty 9 | 10 | env: 11 | - ARDUINO_VERSION=1.8.5 12 | 13 | before_install: 14 | - curl -sSL http://downloads.arduino.cc/arduino-${ARDUINO_VERSION}-linux64.tar.xz | tar Jxf - 15 | - sudo mv arduino-${ARDUINO_VERSION} /usr/local/share/arduino 16 | - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino 17 | 18 | install: 19 | - ln -s $PWD /usr/local/share/arduino/libraries/PCD8544 20 | 21 | script: 22 | - arduino --verify --board arduino:avr:uno $PWD/examples/HelloWorld/HelloWorld.ino 23 | - arduino --verify --board arduino:avr:uno $PWD/examples/Thermometer/Thermometer.ino 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Carlos Rodrigues 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/HelloWorld/HelloWorld.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. 3 | * 4 | * Copyright (c) 2010 Carlos Rodrigues 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * To use this sketch, connect the eight pins from your LCD like thus: 27 | * 28 | * Pin 1 -> +3.3V (rightmost, when facing the display head-on) 29 | * Pin 2 -> Arduino digital pin 3 30 | * Pin 3 -> Arduino digital pin 4 31 | * Pin 4 -> Arduino digital pin 5 32 | * Pin 5 -> Arduino digital pin 7 33 | * Pin 6 -> Ground 34 | * Pin 7 -> 10uF capacitor -> Ground 35 | * Pin 8 -> Arduino digital pin 6 36 | * 37 | * Since these LCDs are +3.3V devices, you have to add extra components to 38 | * connect it to the digital pins of the Arduino (not necessary if you are 39 | * using a 3.3V variant of the Arduino, such as Sparkfun's Arduino Pro). 40 | */ 41 | 42 | 43 | #include 44 | 45 | 46 | // A custom glyph (a smiley)... 47 | static const byte glyph[] = { B00010000, B00110100, B00110000, B00110100, B00010000 }; 48 | 49 | 50 | static PCD8544 lcd; 51 | 52 | 53 | void setup() { 54 | // PCD8544-compatible displays may have a different resolution... 55 | lcd.begin(84, 48); 56 | 57 | // Add the smiley to position "0" of the ASCII table... 58 | lcd.createChar(0, glyph); 59 | } 60 | 61 | 62 | void loop() { 63 | // Just to show the program is alive... 64 | static int counter = 0; 65 | 66 | // Write a piece of text on the first line... 67 | lcd.setCursor(0, 0); 68 | lcd.print("Hello, World!"); 69 | 70 | // Write the counter on the second line... 71 | lcd.setCursor(0, 1); 72 | lcd.print(counter, DEC); 73 | lcd.write(' '); 74 | lcd.write(0); // write the smiley 75 | 76 | // Use a potentiometer to set the LCD contrast... 77 | // short level = map(analogRead(A0), 0, 1023, 0, 127); 78 | // lcd.setContrast(level); 79 | 80 | delay(200); 81 | counter++; 82 | } 83 | 84 | 85 | /* EOF - HelloWorld.ino */ 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What is it? 2 | =========== 3 | 4 | **PCD8544** is a library for the [Arduino](http://arduino.cc/) to interface with LCDs based on the 5 | Philips PCD8544 controller ([datasheet](https://github.com/carlosefr/pcd8544/blob/docs/docs/pcd8544.pdf?raw=true)) 6 | or compatibles. These displays are commonly found on older monochrome mobile phones, such as the 7 | [Nokia 3310](http://en.wikipedia.org/wiki/Nokia_3310) or [5110](http://en.wikipedia.org/wiki/Nokia_5110), 8 | so if you have one of these stuck in a drawer, take it out and start hacking away! :) 9 | 10 | This library is meant to have a minimal memory footprint, small enough to be usable in an [ATtiny85](https://www.microchip.com/wwwproducts/en/ATtiny85) with enough room left for your code. If you need graphics and other features and can spare the resources, check out the [library](https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library) from Adafruit. 11 | 12 | ![PCD8544.png](https://raw.githubusercontent.com/carlosefr/pcd8544/gh-pages/PCD8544.jpg) 13 | 14 | Installation 15 | ============ 16 | 17 | Download the latest zip file from the [releases](https://github.com/carlosefr/pcd8544/releases) section. Then open it from the `Sketch > Include Library > Add .ZIP Library...` menu inside the Arduino IDE and a new "PCD8544" entry should appear in the `Sketch > Include Library` and `File > Examples` menus. 18 | 19 | ![Arduino IDE](https://raw.githubusercontent.com/carlosefr/pcd8544/gh-pages/screenshot-01.png) 20 | 21 | How it Works 22 | ============ 23 | 24 | To use this library, you must first connect your LCD to the proper pins on the Arduino. 25 | For a Nokia 3310 display the connections would be the following: 26 | 27 | Display Pin | Arduino Pin 28 | ------------------|------------ 29 | Pin 1 | +3.3V Pin 30 | Pin 2 (SCLK) | Digital Pin 3 31 | Pin 3 (SDIN/MOSI) | Digital Pin 4 32 | Pin 4 (D/C) | Digital Pin 5 33 | Pin 5 (SCE) | Digital Pin 7 34 | Pin 6 | Ground Pin 35 | Pin 7 | 10uF capacitor to Ground Pin 36 | Pin 8 (RST) | Digital Pin 6 37 | 38 | For this display model, "Pin 1" is the leftmost pin when facing the back of the display with the connector on top. 39 | 40 | Nokia 5110 displays are slightly different. They have an external oscillator pin between pins 5 and 6 which should 41 | be connected to +3.3V. I haven't used one of these myself, so please see the diagrams on 42 | [this page](http://serdisplib.sourceforge.net/ser/pcd8544.html) for more details. 43 | 44 | Since these LCDs are **3.3V** devices, you should add 45 | [extra components](http://www.sparkfun.com/commerce/tutorial_info.php?tutorials_id=65) to 46 | connect it to the digital pins of the Arduino (not necessary if you are using a 3.3V variant 47 | of the Arduino, such as the [Arduino Pro](http://www.arduino.cc/en/Main/ArduinoBoardPro)). However, the I/O pins 48 | are supposed to be 5V tolerant, so you can get by with 1K resistors in series with each pin if you like to live 49 | dangerously. 50 | 51 | Now, take a moment and read through the included [`HelloWorld.ino`](examples/HelloWorld/HelloWorld.ino) example. 52 | It shows how to use the basic features of the library. There is also another 53 | [`Thermometer.ino`](examples/Thermometer/Thermometer.ino) example that demonstrates bitmapped graphics and charts. 54 | 55 | Custom Symbols 56 | ============== 57 | 58 | The library allows the use of custom bitmap symbols (5x8), defined by an array of five bytes. 59 | To make it easy to create custom symbols, there's a graphical glyph editor 60 | [available online](http://carlosefr.github.io/pcd8544/). 61 | -------------------------------------------------------------------------------- /examples/Thermometer/Thermometer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Thermometer - read temperature using an LM35 sensor and display it on a PCD8544 LCD. 3 | * 4 | * Copyright (c) 2010 Carlos Rodrigues 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #include 27 | 28 | 29 | static const byte sensorPin = 0; 30 | static const byte ledPin = 13; 31 | 32 | // The dimensions of the LCD (in pixels)... 33 | static const byte LCD_WIDTH = 84; 34 | static const byte LCD_HEIGHT = 48; 35 | 36 | // The number of lines for the temperature chart... 37 | static const byte CHART_HEIGHT = 5; 38 | 39 | // A custom "degrees" symbol... 40 | static const byte DEGREES_CHAR = 1; 41 | static const byte degrees_glyph[] = { 0x00, 0x07, 0x05, 0x07, 0x00 }; 42 | 43 | // A bitmap graphic (10x2) of a thermometer... 44 | static const byte THERMO_WIDTH = 10; 45 | static const byte THERMO_HEIGHT = 2; 46 | static const byte thermometer[] = { 0x00, 0x00, 0x48, 0xfe, 0x01, 0xfe, 0x00, 0x02, 0x05, 0x02, 47 | 0x00, 0x00, 0x62, 0xff, 0xfe, 0xff, 0x60, 0x00, 0x00, 0x00}; 48 | 49 | static PCD8544 lcd; 50 | 51 | 52 | void setup() { 53 | lcd.begin(LCD_WIDTH, LCD_HEIGHT); 54 | 55 | // Register the custom symbol... 56 | lcd.createChar(DEGREES_CHAR, degrees_glyph); 57 | 58 | pinMode(ledPin, OUTPUT); 59 | 60 | // The internal 1.1V reference provides for better 61 | // resolution from the LM35, and is also more stable 62 | // when powered from either a battery or USB... 63 | analogReference(INTERNAL); 64 | } 65 | 66 | 67 | void loop() { 68 | // Start beyond the edge of the screen... 69 | static byte xChart = LCD_WIDTH; 70 | 71 | digitalWrite(ledPin, HIGH); 72 | 73 | // Read the temperature (in celsius)... 74 | float temp = (1.1 * analogRead(sensorPin) * 100.0) / 1024.0; 75 | 76 | // Print the temperature (using the custom "degrees" symbol)... 77 | lcd.setCursor(0, 0); 78 | lcd.print("Temp: "); 79 | lcd.print(temp, 1); 80 | lcd.print(" \001C "); 81 | 82 | // Draw the thermometer bitmap at the bottom left corner... 83 | lcd.setCursor(0, LCD_HEIGHT/8 - THERMO_HEIGHT); 84 | lcd.drawBitmap(thermometer, THERMO_WIDTH, THERMO_HEIGHT); 85 | 86 | // Wrap the chart's current position... 87 | if (xChart >= LCD_WIDTH) { 88 | xChart = THERMO_WIDTH + 2; 89 | } 90 | 91 | // Update the temperature chart... 92 | lcd.setCursor(xChart, 1); 93 | lcd.drawColumn(CHART_HEIGHT, map(temp, 0, 45, 0, CHART_HEIGHT*8)); // ...clipped to the 0-45C range. 94 | lcd.drawColumn(CHART_HEIGHT, 0); // ...with a clear marker to see the current chart position. 95 | 96 | xChart++; 97 | 98 | digitalWrite(ledPin, LOW); 99 | delay(500); 100 | } 101 | 102 | 103 | /* EOF - Thermometer.ino */ 104 | 105 | -------------------------------------------------------------------------------- /PCD8544.h: -------------------------------------------------------------------------------- 1 | /* 2 | * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. 3 | * 4 | * Copyright (c) 2010 Carlos Rodrigues 5 | * 6 | * Apr 24, 2020: Breakout board variant details added by Mike Pfleger 7 | * 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | 29 | #ifndef PCD8544_H 30 | #define PCD8544_H 31 | 32 | 33 | #include 34 | 35 | 36 | // Chip variants supported (ST7576 is experimental)... 37 | #define CHIP_PCD8544 0 38 | #define CHIP_ST7576 1 39 | 40 | 41 | class PCD8544: public Print { 42 | public: 43 | /* 44 | * Please verify the particular variant of your Nokia LCD module before 45 | * applying power. 46 | * 47 | * The PCD8544 "Nokia" LCD modules seem to come in two popular variants, 48 | * which we will refer to as "AdaFruit" and "Sparkfun", referring to the 49 | * popular online vendors. The pinout differences are detailed here for 50 | * reference: 51 | * 52 | * AdaFruit Sparkfun function notes 53 | * -----------+-----------+----------+---------------------------------- 54 | * 1 2 GND 55 | * 2 1 VCC 56 | * 3 7 CLK / SCLK 57 | * 4 6 DIN / MOSI 58 | * 5 5 D/C 59 | * 6 3 CS / SCE 60 | * 7 4 RST 61 | * 8 8 LED no Ilim resistors on "Sparkfun" 62 | */ 63 | 64 | // All the pins can be changed from the default values... 65 | PCD8544(uint8_t sclk = 3, /* clock (display pin 2) */ 66 | uint8_t sdin = 4, /* data-in (display pin 3) */ 67 | uint8_t dc = 5, /* data select (display pin 4) */ 68 | uint8_t reset = 6, /* reset (display pin 8) */ 69 | uint8_t sce = 7); /* enable (display pin 5) */ 70 | 71 | // Display initialization (dimensions in pixels)... 72 | void begin(uint8_t width=84, uint8_t height=48, uint8_t model=CHIP_PCD8544); 73 | void stop(); 74 | 75 | // Erase everything on the display... 76 | void clear(); 77 | void clearLine(); // ...or just the current line 78 | 79 | // Control the display's power state... 80 | void setPower(bool on); 81 | 82 | // For compatibility with the LiquidCrystal library... 83 | void display(); 84 | void noDisplay(); 85 | 86 | // Activate white-on-black mode... 87 | void setInverse(bool enabled); // ...whole display 88 | void setInverseOutput(bool enabled); // ...future writes 89 | 90 | // Set display contrast level (0-127)... 91 | void setContrast(uint8_t level); 92 | 93 | // Place the cursor at the start of the current line... 94 | void home(); 95 | 96 | // Place the cursor at position (column, line)... 97 | void setCursor(uint8_t column, uint8_t line); 98 | 99 | // Assign a user-defined glyph (5x8) to an ASCII character (0-31)... 100 | void createChar(uint8_t chr, const uint8_t *glyph); 101 | 102 | // Write an ASCII character at the current cursor position (7-bit)... 103 | virtual size_t write(uint8_t chr); 104 | 105 | // Draw a bitmap at the current cursor position... 106 | void drawBitmap(const uint8_t *data, uint8_t columns, uint8_t lines); 107 | 108 | // Draw a chart element at the current cursor position... 109 | void drawColumn(uint8_t lines, uint8_t value); 110 | 111 | private: 112 | uint8_t pin_sclk; 113 | uint8_t pin_sdin; 114 | uint8_t pin_dc; 115 | uint8_t pin_reset; 116 | uint8_t pin_sce; 117 | 118 | // Chip variant in use... 119 | uint8_t model; 120 | 121 | // The size of the display, in pixels... 122 | uint8_t width; 123 | uint8_t height; 124 | 125 | // Current cursor position... 126 | uint8_t column; 127 | uint8_t line; 128 | 129 | // Current output mode for writes (doesn't apply to draws)... 130 | bool inverse_output = false; 131 | 132 | // User-defined glyphs (below the ASCII space character)... 133 | const uint8_t *custom[' ']; 134 | 135 | // Send a command or data to the display... 136 | void send(uint8_t type, uint8_t data); 137 | }; 138 | 139 | 140 | #endif /* PCD8544_H */ 141 | 142 | 143 | /* vim: set expandtab ts=4 sw=4: */ 144 | -------------------------------------------------------------------------------- /charset.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. 3 | * 4 | * Copyright (c) 2010 Carlos Rodrigues 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #if defined (__XTENSA__) 27 | #include 28 | #else 29 | #include 30 | #endif 31 | 32 | 33 | // The 7-bit ASCII character set... 34 | const PROGMEM uint8_t charset[][5] = { 35 | { 0x00, 0x00, 0x00, 0x00, 0x00 }, // 20 space 36 | { 0x00, 0x00, 0x5f, 0x00, 0x00 }, // 21 ! 37 | { 0x00, 0x07, 0x00, 0x07, 0x00 }, // 22 " 38 | { 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // 23 # 39 | { 0x24, 0x2a, 0x7f, 0x2a, 0x12 }, // 24 $ 40 | { 0x23, 0x13, 0x08, 0x64, 0x62 }, // 25 % 41 | { 0x36, 0x49, 0x55, 0x22, 0x50 }, // 26 & 42 | { 0x00, 0x05, 0x03, 0x00, 0x00 }, // 27 ' 43 | { 0x00, 0x1c, 0x22, 0x41, 0x00 }, // 28 ( 44 | { 0x00, 0x41, 0x22, 0x1c, 0x00 }, // 29 ) 45 | { 0x14, 0x08, 0x3e, 0x08, 0x14 }, // 2a * 46 | { 0x08, 0x08, 0x3e, 0x08, 0x08 }, // 2b + 47 | { 0x00, 0x50, 0x30, 0x00, 0x00 }, // 2c , 48 | { 0x08, 0x08, 0x08, 0x08, 0x08 }, // 2d - 49 | { 0x00, 0x60, 0x60, 0x00, 0x00 }, // 2e . 50 | { 0x20, 0x10, 0x08, 0x04, 0x02 }, // 2f / 51 | { 0x3e, 0x51, 0x49, 0x45, 0x3e }, // 30 0 52 | { 0x00, 0x42, 0x7f, 0x40, 0x00 }, // 31 1 53 | { 0x42, 0x61, 0x51, 0x49, 0x46 }, // 32 2 54 | { 0x21, 0x41, 0x45, 0x4b, 0x31 }, // 33 3 55 | { 0x18, 0x14, 0x12, 0x7f, 0x10 }, // 34 4 56 | { 0x27, 0x45, 0x45, 0x45, 0x39 }, // 35 5 57 | { 0x3c, 0x4a, 0x49, 0x49, 0x30 }, // 36 6 58 | { 0x01, 0x71, 0x09, 0x05, 0x03 }, // 37 7 59 | { 0x36, 0x49, 0x49, 0x49, 0x36 }, // 38 8 60 | { 0x06, 0x49, 0x49, 0x29, 0x1e }, // 39 9 61 | { 0x00, 0x36, 0x36, 0x00, 0x00 }, // 3a : 62 | { 0x00, 0x56, 0x36, 0x00, 0x00 }, // 3b ; 63 | { 0x08, 0x14, 0x22, 0x41, 0x00 }, // 3c < 64 | { 0x14, 0x14, 0x14, 0x14, 0x14 }, // 3d = 65 | { 0x00, 0x41, 0x22, 0x14, 0x08 }, // 3e > 66 | { 0x02, 0x01, 0x51, 0x09, 0x06 }, // 3f ? 67 | { 0x32, 0x49, 0x79, 0x41, 0x3e }, // 40 @ 68 | { 0x7e, 0x11, 0x11, 0x11, 0x7e }, // 41 A 69 | { 0x7f, 0x49, 0x49, 0x49, 0x36 }, // 42 B 70 | { 0x3e, 0x41, 0x41, 0x41, 0x22 }, // 43 C 71 | { 0x7f, 0x41, 0x41, 0x22, 0x1c }, // 44 D 72 | { 0x7f, 0x49, 0x49, 0x49, 0x41 }, // 45 E 73 | { 0x7f, 0x09, 0x09, 0x09, 0x01 }, // 46 F 74 | { 0x3e, 0x41, 0x49, 0x49, 0x7a }, // 47 G 75 | { 0x7f, 0x08, 0x08, 0x08, 0x7f }, // 48 H 76 | { 0x00, 0x41, 0x7f, 0x41, 0x00 }, // 49 I 77 | { 0x20, 0x40, 0x41, 0x3f, 0x01 }, // 4a J 78 | { 0x7f, 0x08, 0x14, 0x22, 0x41 }, // 4b K 79 | { 0x7f, 0x40, 0x40, 0x40, 0x40 }, // 4c L 80 | { 0x7f, 0x02, 0x0c, 0x02, 0x7f }, // 4d M 81 | { 0x7f, 0x04, 0x08, 0x10, 0x7f }, // 4e N 82 | { 0x3e, 0x41, 0x41, 0x41, 0x3e }, // 4f O 83 | { 0x7f, 0x09, 0x09, 0x09, 0x06 }, // 50 P 84 | { 0x3e, 0x41, 0x51, 0x21, 0x5e }, // 51 Q 85 | { 0x7f, 0x09, 0x19, 0x29, 0x46 }, // 52 R 86 | { 0x46, 0x49, 0x49, 0x49, 0x31 }, // 53 S 87 | { 0x01, 0x01, 0x7f, 0x01, 0x01 }, // 54 T 88 | { 0x3f, 0x40, 0x40, 0x40, 0x3f }, // 55 U 89 | { 0x1f, 0x20, 0x40, 0x20, 0x1f }, // 56 V 90 | { 0x3f, 0x40, 0x38, 0x40, 0x3f }, // 57 W 91 | { 0x63, 0x14, 0x08, 0x14, 0x63 }, // 58 X 92 | { 0x07, 0x08, 0x70, 0x08, 0x07 }, // 59 Y 93 | { 0x61, 0x51, 0x49, 0x45, 0x43 }, // 5a Z 94 | { 0x00, 0x7f, 0x41, 0x41, 0x00 }, // 5b [ 95 | { 0x02, 0x04, 0x08, 0x10, 0x20 }, // 5c backslash 96 | { 0x00, 0x41, 0x41, 0x7f, 0x00 }, // 5d ] 97 | { 0x04, 0x02, 0x01, 0x02, 0x04 }, // 5e ^ 98 | { 0x40, 0x40, 0x40, 0x40, 0x40 }, // 5f _ 99 | { 0x00, 0x01, 0x02, 0x04, 0x00 }, // 60 ` 100 | { 0x20, 0x54, 0x54, 0x54, 0x78 }, // 61 a 101 | { 0x7f, 0x48, 0x44, 0x44, 0x38 }, // 62 b 102 | { 0x38, 0x44, 0x44, 0x44, 0x20 }, // 63 c 103 | { 0x38, 0x44, 0x44, 0x48, 0x7f }, // 64 d 104 | { 0x38, 0x54, 0x54, 0x54, 0x18 }, // 65 e 105 | { 0x08, 0x7e, 0x09, 0x01, 0x02 }, // 66 f 106 | { 0x0c, 0x52, 0x52, 0x52, 0x3e }, // 67 g 107 | { 0x7f, 0x08, 0x04, 0x04, 0x78 }, // 68 h 108 | { 0x00, 0x44, 0x7d, 0x40, 0x00 }, // 69 i 109 | { 0x20, 0x40, 0x44, 0x3d, 0x00 }, // 6a j 110 | { 0x7f, 0x10, 0x28, 0x44, 0x00 }, // 6b k 111 | { 0x00, 0x41, 0x7f, 0x40, 0x00 }, // 6c l 112 | { 0x7c, 0x04, 0x18, 0x04, 0x78 }, // 6d m 113 | { 0x7c, 0x08, 0x04, 0x04, 0x78 }, // 6e n 114 | { 0x38, 0x44, 0x44, 0x44, 0x38 }, // 6f o 115 | { 0x7c, 0x14, 0x14, 0x14, 0x08 }, // 70 p 116 | { 0x08, 0x14, 0x14, 0x18, 0x7c }, // 71 q 117 | { 0x7c, 0x08, 0x04, 0x04, 0x08 }, // 72 r 118 | { 0x48, 0x54, 0x54, 0x54, 0x20 }, // 73 s 119 | { 0x04, 0x3f, 0x44, 0x40, 0x20 }, // 74 t 120 | { 0x3c, 0x40, 0x40, 0x20, 0x7c }, // 75 u 121 | { 0x1c, 0x20, 0x40, 0x20, 0x1c }, // 76 v 122 | { 0x3c, 0x40, 0x30, 0x40, 0x3c }, // 77 w 123 | { 0x44, 0x28, 0x10, 0x28, 0x44 }, // 78 x 124 | { 0x0c, 0x50, 0x50, 0x50, 0x3c }, // 79 y 125 | { 0x44, 0x64, 0x54, 0x4c, 0x44 }, // 7a z 126 | { 0x00, 0x08, 0x36, 0x41, 0x00 }, // 7b { 127 | { 0x00, 0x00, 0x7f, 0x00, 0x00 }, // 7c | 128 | { 0x00, 0x41, 0x36, 0x08, 0x00 }, // 7d } 129 | { 0x10, 0x08, 0x08, 0x10, 0x08 }, // 7e ~ 130 | { 0x00, 0x00, 0x00, 0x00, 0x00 } // 7f 131 | }; 132 | 133 | 134 | /* vim: set expandtab ts=4 sw=4: */ 135 | -------------------------------------------------------------------------------- /PCD8544.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * PCD8544 - Interface with Philips PCD8544 (or compatible) LCDs. 3 | * 4 | * Copyright (c) 2010 Carlos Rodrigues 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | 26 | #include "PCD8544.h" 27 | 28 | #include 29 | 30 | #if defined (__XTENSA__) 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | 37 | #define PCD8544_CMD LOW 38 | #define PCD8544_DATA HIGH 39 | 40 | 41 | /* 42 | * If this was a ".h", it would get added to sketches when using 43 | * the "Sketch -> Import Library..." menu on the Arduino IDE... 44 | */ 45 | #include "charset.cpp" 46 | 47 | 48 | PCD8544::PCD8544(uint8_t sclk, uint8_t sdin, uint8_t dc, uint8_t reset, uint8_t sce): 49 | pin_sclk(sclk), 50 | pin_sdin(sdin), 51 | pin_dc(dc), 52 | pin_reset(reset), 53 | pin_sce(sce) 54 | {} 55 | 56 | 57 | void PCD8544::begin(uint8_t width, uint8_t height, uint8_t model) 58 | { 59 | this->width = width; 60 | this->height = height; 61 | 62 | // Only two chip variants are currently known/supported... 63 | this->model = (model == CHIP_ST7576) ? CHIP_ST7576 : CHIP_PCD8544; 64 | 65 | this->column = 0; 66 | this->line = 0; 67 | 68 | // Sanitize the custom glyphs... 69 | memset(this->custom, 0, sizeof(this->custom)); 70 | 71 | // All pins are outputs (these displays cannot be read)... 72 | pinMode(this->pin_sclk, OUTPUT); 73 | pinMode(this->pin_sdin, OUTPUT); 74 | pinMode(this->pin_dc, OUTPUT); 75 | pinMode(this->pin_reset, OUTPUT); 76 | pinMode(this->pin_sce, OUTPUT); 77 | 78 | // Reset the controller state... 79 | digitalWrite(this->pin_reset, HIGH); 80 | digitalWrite(this->pin_sce, HIGH); 81 | digitalWrite(this->pin_reset, LOW); 82 | delay(100); 83 | digitalWrite(this->pin_reset, HIGH); 84 | 85 | // Set the LCD parameters... 86 | this->send(PCD8544_CMD, 0x21); // extended instruction set control (H=1) 87 | this->send(PCD8544_CMD, 0x13); // bias system (1:48) 88 | 89 | if (this->model == CHIP_ST7576) { 90 | this->send(PCD8544_CMD, 0xe0); // higher Vop, too faint at default 91 | this->send(PCD8544_CMD, 0x05); // partial display mode 92 | } else { 93 | this->send(PCD8544_CMD, 0xc2); // default Vop (3.06 + 66 * 0.06 = 7V) 94 | } 95 | 96 | this->send(PCD8544_CMD, 0x20); // extended instruction set control (H=0) 97 | this->send(PCD8544_CMD, 0x09); // all display segments on 98 | 99 | // Clear RAM contents... 100 | this->clear(); 101 | 102 | // Activate LCD... 103 | this->send(PCD8544_CMD, 0x08); // display blank 104 | this->send(PCD8544_CMD, 0x0c); // normal mode (0x0d = inverse mode) 105 | delay(100); 106 | 107 | // Place the cursor at the origin... 108 | this->send(PCD8544_CMD, 0x80); 109 | this->send(PCD8544_CMD, 0x40); 110 | } 111 | 112 | 113 | void PCD8544::stop() 114 | { 115 | this->clear(); 116 | this->setPower(false); 117 | } 118 | 119 | 120 | void PCD8544::clear() 121 | { 122 | this->setCursor(0, 0); 123 | 124 | for (uint16_t i = 0; i < this->width * (this->height/8); i++) { 125 | this->send(PCD8544_DATA, 0x00); 126 | } 127 | 128 | this->setCursor(0, 0); 129 | } 130 | 131 | 132 | void PCD8544::clearLine() 133 | { 134 | this->setCursor(0, this->line); 135 | 136 | for (uint8_t i = 0; i < this->width; i++) { 137 | this->send(PCD8544_DATA, 0x00); 138 | } 139 | 140 | this->setCursor(0, this->line); 141 | } 142 | 143 | 144 | void PCD8544::setPower(bool on) 145 | { 146 | this->send(PCD8544_CMD, on ? 0x20 : 0x24); 147 | } 148 | 149 | 150 | inline void PCD8544::display() 151 | { 152 | this->setPower(true); 153 | } 154 | 155 | 156 | inline void PCD8544::noDisplay() 157 | { 158 | this->setPower(false); 159 | } 160 | 161 | 162 | void PCD8544::setInverse(bool enabled) 163 | { 164 | this->send(PCD8544_CMD, enabled ? 0x0d : 0x0c); 165 | } 166 | 167 | 168 | void PCD8544::setInverseOutput(bool enabled) 169 | { 170 | this->inverse_output = enabled; 171 | } 172 | 173 | 174 | void PCD8544::setContrast(uint8_t level) 175 | { 176 | // The PCD8544 datasheet specifies a maximum Vop of 8.5V for safe 177 | // operation in low temperatures, which limits the contrast level. 178 | if (this->model == CHIP_PCD8544 && level > 90) { 179 | level = 90; // Vop = 3.06 + 90 * 0.06 = 8.46V 180 | } 181 | 182 | // The ST7576 datasheet specifies a minimum Vop of 4V. 183 | if (this->model == CHIP_ST7576 && level < 36) { 184 | level = 36; // Vop = 2.94 + 36 * 0.03 = 4.02V 185 | } 186 | 187 | this->send(PCD8544_CMD, 0x21); // extended instruction set control (H=1) 188 | this->send(PCD8544_CMD, 0x80 | (level & 0x7f)); 189 | this->send(PCD8544_CMD, 0x20); // extended instruction set control (H=0) 190 | } 191 | 192 | 193 | void PCD8544::home() 194 | { 195 | this->setCursor(0, this->line); 196 | } 197 | 198 | 199 | void PCD8544::setCursor(uint8_t column, uint8_t line) 200 | { 201 | this->column = (column % this->width); 202 | this->line = (line % (this->height/9 + 1)); 203 | 204 | this->send(PCD8544_CMD, 0x80 | this->column); 205 | this->send(PCD8544_CMD, 0x40 | this->line); 206 | } 207 | 208 | 209 | void PCD8544::createChar(uint8_t chr, const uint8_t *glyph) 210 | { 211 | // ASCII 0-31 only... 212 | if (chr >= ' ') { 213 | return; 214 | } 215 | 216 | this->custom[chr] = glyph; 217 | } 218 | 219 | 220 | size_t PCD8544::write(uint8_t chr) 221 | { 222 | // ASCII 7-bit only... 223 | if (chr >= 0x80) { 224 | return 0; 225 | } 226 | 227 | const uint8_t *glyph; 228 | uint8_t pgm_buffer[5]; 229 | 230 | if (chr >= ' ') { 231 | // Regular ASCII characters are kept in flash to save RAM... 232 | memcpy_P(pgm_buffer, &charset[chr - ' '], sizeof(pgm_buffer)); 233 | glyph = pgm_buffer; 234 | } else { 235 | // Custom glyphs, on the other hand, are stored in RAM... 236 | if (this->custom[chr]) { 237 | glyph = this->custom[chr]; 238 | } else { 239 | // Default to a space character if unset... 240 | memcpy_P(pgm_buffer, &charset[0], sizeof(pgm_buffer)); 241 | glyph = pgm_buffer; 242 | } 243 | } 244 | 245 | // Output one column at a time... 246 | for (uint8_t i = 0; i < 5; i++) { 247 | this->send(PCD8544_DATA, this->inverse_output ? ~glyph[i] : glyph[i]); 248 | } 249 | 250 | // One column between characters... 251 | this->send(PCD8544_DATA, this->inverse_output ? 0xff : 0x00); 252 | 253 | // Update the cursor position... 254 | this->column = (this->column + 6) % this->width; 255 | 256 | if (this->column == 0) { 257 | this->line = (this->line + 1) % (this->height/9 + 1); 258 | } 259 | 260 | return 1; 261 | } 262 | 263 | 264 | void PCD8544::drawBitmap(const uint8_t *data, uint8_t columns, uint8_t lines) 265 | { 266 | uint8_t scolumn = this->column; 267 | uint8_t sline = this->line; 268 | 269 | // The bitmap will be clipped at the right/bottom edge of the display... 270 | uint8_t mx = (scolumn + columns > this->width) ? (this->width - scolumn) : columns; 271 | uint8_t my = (sline + lines > this->height/8) ? (this->height/8 - sline) : lines; 272 | 273 | for (uint8_t y = 0; y < my; y++) { 274 | this->setCursor(scolumn, sline + y); 275 | 276 | for (uint8_t x = 0; x < mx; x++) { 277 | this->send(PCD8544_DATA, data[y * columns + x]); 278 | } 279 | } 280 | 281 | // Leave the cursor in a consistent position... 282 | this->setCursor(scolumn + columns, sline); 283 | } 284 | 285 | 286 | void PCD8544::drawColumn(uint8_t lines, uint8_t value) 287 | { 288 | uint8_t scolumn = this->column; 289 | uint8_t sline = this->line; 290 | 291 | // Keep "value" within range... 292 | if (value > lines*8) { 293 | value = lines*8; 294 | } 295 | 296 | // Find the line where "value" resides... 297 | uint8_t mark = (lines*8 - 1 - value)/8; 298 | 299 | // Clear the lines above the mark... 300 | for (uint8_t line = 0; line < mark; line++) { 301 | this->setCursor(scolumn, sline + line); 302 | this->send(PCD8544_DATA, 0x00); 303 | } 304 | 305 | // Compute the byte to draw at the "mark" line... 306 | uint8_t b = 0xff; 307 | for (uint8_t i = 0; i < lines*8 - mark*8 - value; i++) { 308 | b <<= 1; 309 | } 310 | 311 | this->setCursor(scolumn, sline + mark); 312 | this->send(PCD8544_DATA, b); 313 | 314 | // Fill the lines below the mark... 315 | for (uint8_t line = mark + 1; line < lines; line++) { 316 | this->setCursor(scolumn, sline + line); 317 | this->send(PCD8544_DATA, 0xff); 318 | } 319 | 320 | // Leave the cursor in a consistent position... 321 | this->setCursor(scolumn + 1, sline); 322 | } 323 | 324 | 325 | void PCD8544::send(uint8_t type, uint8_t data) 326 | { 327 | digitalWrite(this->pin_dc, type); 328 | 329 | digitalWrite(this->pin_sce, LOW); 330 | shiftOut(this->pin_sdin, this->pin_sclk, MSBFIRST, data); 331 | digitalWrite(this->pin_sce, HIGH); 332 | } 333 | 334 | 335 | /* vim: set expandtab ts=4 sw=4: */ 336 | --------------------------------------------------------------------------------