diff options
author | Andreas Regel <andreas.regel@gmx.de> | 2014-10-19 22:01:27 +0200 |
---|---|---|
committer | Andreas Regel <andreas.regel@gmx.de> | 2016-04-01 23:44:13 +0200 |
commit | b8977439a9e142cb870fd6bbe79dab152e93100d (patch) | |
tree | 8e726548576981cb158cdb9ac82faf7c301efb65 | |
parent | 831b3ad714a036edab6b63f84040ab8b32a43ef4 (diff) | |
download | graphlcd-base-b8977439a9e142cb870fd6bbe79dab152e93100d.tar.gz graphlcd-base-b8977439a9e142cb870fd6bbe79dab152e93100d.tar.bz2 |
Add driver for SSD1306 OLED display on raspberry pi
-rw-r--r-- | Make.config | 2 | ||||
-rw-r--r-- | glcddrivers/Makefile | 6 | ||||
-rw-r--r-- | glcddrivers/drivers.c | 10 | ||||
-rw-r--r-- | glcddrivers/drivers.h | 3 | ||||
-rw-r--r-- | glcddrivers/ssd1306.c | 340 | ||||
-rw-r--r-- | glcddrivers/ssd1306.h | 53 | ||||
-rw-r--r-- | graphlcd.conf | 11 |
7 files changed, 425 insertions, 0 deletions
diff --git a/Make.config b/Make.config index 29b82ef..19cbfd8 100644 --- a/Make.config +++ b/Make.config @@ -83,3 +83,5 @@ HAVE_DRIVER_VNCSERVER=1 # uncomment this variable if you want to enable the experimental support for picoLCD 256x64 #HAVE_DRIVER_picoLCD_256x64=1 + +#HAVE_DRIVER_SSD1306=1 diff --git a/glcddrivers/Makefile b/glcddrivers/Makefile index b09f468..dcf0c5f 100644 --- a/glcddrivers/Makefile +++ b/glcddrivers/Makefile @@ -51,6 +51,12 @@ ifeq ($(shell libvncserver-config --version >/dev/null && echo 1), 1) endif endif +ifeq ($(HAVE_DRIVER_SSD1306), 1) + DEFINES += -DHAVE_DRIVER_SSD1306 + OBJS += ssd1306.o + LIBS += -lwiringPi +endif + ### Implicit rules: %.o: %.c diff --git a/glcddrivers/drivers.c b/glcddrivers/drivers.c index e720c0e..44e19c1 100644 --- a/glcddrivers/drivers.c +++ b/glcddrivers/drivers.c @@ -39,6 +39,9 @@ #ifdef HAVE_DRIVER_VNCSERVER #include "vncserver.h" #endif +#ifdef HAVE_DRIVER_SSD1306 +#include "ssd1306.h" +#endif namespace GLCD { @@ -72,6 +75,9 @@ tDriver drivers[] = #ifdef HAVE_DRIVER_VNCSERVER {"vncserver", kDriverVncServer}, #endif +#ifdef HAVE_DRIVER_SSD1306 + {"ssd1306", kDriverSSD1306}, +#endif {"", kDriverUnknown} }; @@ -142,6 +148,10 @@ cDriver * CreateDriver(int driverID, cDriverConfig * config) case kDriverVncServer: return new cDriverVncServer(config); #endif +#ifdef HAVE_DRIVER_SSD1306 + case kDriverSSD1306: + return new cDriverSSD1306(config); +#endif case kDriverUnknown: default: return NULL; diff --git a/glcddrivers/drivers.h b/glcddrivers/drivers.h index 3968e81..f3bfbbf 100644 --- a/glcddrivers/drivers.h +++ b/glcddrivers/drivers.h @@ -49,6 +49,9 @@ enum eDriver #ifdef HAVE_DRIVER_VNCSERVER kDriverVncServer = 19, #endif +#ifdef HAVE_DRIVER_SSD1306 + kDriverSSD1306 = 20, +#endif kDriverSerDisp = 100, kDriverG15daemon = 200 }; diff --git a/glcddrivers/ssd1306.c b/glcddrivers/ssd1306.c new file mode 100644 index 0000000..852ea13 --- /dev/null +++ b/glcddrivers/ssd1306.c @@ -0,0 +1,340 @@ +/* + * GraphLCD driver library + * + * ssd1306.c - SSD1306 OLED driver class + * + * This file is released under the GNU General Public License. Refer + * to the COPYING file distributed with this package. + * + * (c) 2014 Andreas Regel <andreas.regel AT powarman.de> + */ + +#include <stdint.h> +#include <syslog.h> +#include <unistd.h> +#include <cstring> + +#include <wiringPi.h> +#include <wiringPiSPI.h> + +#include "common.h" +#include "config.h" +#include "ssd1306.h" + + +namespace GLCD +{ + +const int kLcdWidth = 128; +const int kLcdHeight = 64; + +const int kSpiBus = 0; + +const int kGpioReset = 15; +const int kGpioDC = 16; + +const uint8_t kCmdSetLowerColumn = 0x00; +const uint8_t kCmdSetHigherColumn = 0x10; +const uint8_t kCmdSetMemoryAddressingMode = 0x20; +const uint8_t kCmdSetColumnAddress = 0x21; +const uint8_t kCmdSetPageAddress = 0x22; +const uint8_t kCmdSetDisplayStartLine = 0x40; +const uint8_t kCmdSetContrast = 0x81; +const uint8_t kCmdSetChargePump = 0x8D; +const uint8_t kCmdSetSegmentReMap = 0xA0; +const uint8_t kCmdEntireDisplayOnResume = 0xA4; +const uint8_t kCmdEntireDisplayOn = 0xA5; +const uint8_t kCmdSetNormalDisplay = 0xA6; +const uint8_t kCmdSetInverseDisplay = 0xA7; +const uint8_t kCmdSetMultiplexRatio = 0xA8; +const uint8_t kCmdSetDisplayOff = 0xAE; +const uint8_t kCmdSetDisplayOn = 0xAF; +const uint8_t kCmdSetPageStart = 0xB0; +const uint8_t kCmdSetComScanInc = 0xC0; +const uint8_t kCmdSetComScanDec = 0xC8; +const uint8_t kCmdSetDisplayOffset = 0xD3; +const uint8_t kCmdSetDisplayClockDiv = 0xD5; +const uint8_t kCmdSetPreChargePeriod = 0xD9; +const uint8_t kCmdSetComPins = 0xDA; +const uint8_t kCmdSetVComDeselectLevel = 0xDB; + + +cDriverSSD1306::cDriverSSD1306(cDriverConfig * config) +: cDriver(config) +{ + refreshCounter = 0; + + wiringPiSetup(); +} + +cDriverSSD1306::~cDriverSSD1306() +{ + //delete port; +} + +int cDriverSSD1306::Init() +{ + int x; + + width = config->width; + if (width <= 0) + width = kLcdWidth; + height = config->height; + if (height <= 0) + height = kLcdHeight; + + for (unsigned int i = 0; i < config->options.size(); i++) + { + if (config->options[i].name == "") + { + } + } + + // setup lcd array (wanted state) + newLCD = new unsigned char*[width]; + if (newLCD) + { + for (x = 0; x < width; x++) + { + newLCD[x] = new unsigned char[(height + 7) / 8]; + memset(newLCD[x], 0, (height + 7) / 8); + } + } + // setup lcd array (current state) + oldLCD = new unsigned char*[width]; + if (oldLCD) + { + for (x = 0; x < width; x++) + { + oldLCD[x] = new unsigned char[(height + 7) / 8]; + memset(oldLCD[x], 0, (height + 7) / 8); + } + } + + if (config->device == "") + { + return -1; + } + + pinMode(kGpioReset, OUTPUT); + pinMode(kGpioDC, OUTPUT); + + digitalWrite(kGpioReset, HIGH); + digitalWrite(kGpioDC, LOW); + + wiringPiSPISetup(kSpiBus, 1000000); + + /* reset display */ + Reset(); + usleep(1000); + + WriteCommand(kCmdSetDisplayOff); + + if (height == 64) + { + WriteCommand(kCmdSetMultiplexRatio, 0x3F); + WriteCommand(kCmdSetComPins, 0x12); + } + else if (height == 32) + { + WriteCommand(kCmdSetMultiplexRatio, 0x1F); + WriteCommand(kCmdSetComPins, 0x02); + } + + WriteCommand(kCmdSetDisplayOffset, 0x00); + WriteCommand(kCmdSetDisplayStartLine | 0x00); + WriteCommand(kCmdSetMemoryAddressingMode, 0x01); + WriteCommand(kCmdSetSegmentReMap | 0x01); + WriteCommand(kCmdSetComScanDec); + WriteCommand(kCmdSetContrast, config->brightness * 255 / 100); + WriteCommand(kCmdEntireDisplayOnResume); + WriteCommand(kCmdSetNormalDisplay); + WriteCommand(kCmdSetDisplayClockDiv, 0x80); + WriteCommand(kCmdSetChargePump, 0x14); + WriteCommand(kCmdSetDisplayOn); + + *oldConfig = *config; + + // clear display + Clear(); + + syslog(LOG_INFO, "%s: SSD1306 initialized.\n", config->name.c_str()); + return 0; +} + +int cDriverSSD1306::DeInit() +{ + int x; + // free lcd array (wanted state) + if (newLCD) + { + for (x = 0; x < width; x++) + { + delete[] newLCD[x]; + } + delete[] newLCD; + } + // free lcd array (current state) + if (oldLCD) + { + for (x = 0; x < width; x++) + { + delete[] oldLCD[x]; + } + delete[] oldLCD; + } + + return 0; +} + +int cDriverSSD1306::CheckSetup() +{ + if (config->device != oldConfig->device || + config->width != oldConfig->width || + config->height != oldConfig->height) + { + DeInit(); + Init(); + return 0; + } + + if (config->upsideDown != oldConfig->upsideDown || + config->invert != oldConfig->invert) + { + oldConfig->upsideDown = config->upsideDown; + oldConfig->invert = config->invert; + return 1; + } + return 0; +} + +void cDriverSSD1306::Clear() +{ + for (int x = 0; x < width; x++) + memset(newLCD[x], 0, (height + 7) / 8); +} + + +void cDriverSSD1306::SetPixel(int x, int y, uint32_t data) +{ + if (x >= width || y >= height) + return; + + if (config->upsideDown) + { + x = width - 1 - x; + y = height - 1 - y; + } + + int offset = (y % 8); + if (data == GRAPHLCD_White) + newLCD[x][y / 8] |= (1 << offset); + else + newLCD[x][y / 8] &= ( 0xFF ^ (1 << offset) ); +} + + +#if 0 +void cDriverSSD1306::Set8Pixels(int x, int y, unsigned char data) +{ + if (x >= width || y >= height) + return; + + if (!config->upsideDown) + { + int offset = 7 - (y % 8); + for (int i = 0; i < 8; i++) + { + newLCD[x + i][y / 8] |= ((data >> (7 - i)) << offset) & (1 << offset); + } + } + else + { + x = width - 1 - x; + y = height - 1 - y; + int offset = 7 - (y % 8); + for (int i = 0; i < 8; i++) + { + newLCD[x - i][y / 8] |= ((data >> (7 - i)) << offset) & (1 << offset); + } + } +} +#endif + +void cDriverSSD1306::Refresh(bool refreshAll) +{ + int x; + int y; + uint8_t numPages = (height + 7) / 8; + unsigned char data[16]; + + if (CheckSetup() == 1) + refreshAll = true; + + if (config->refreshDisplay > 0) + { + refreshCounter = (refreshCounter + 1) % config->refreshDisplay; + if (!refreshAll && !refreshCounter) + refreshAll = true; + } + + refreshAll = true; + if (refreshAll) + { + WriteCommand(kCmdSetColumnAddress, 0, width - 1); + WriteCommand(kCmdSetPageAddress, 0, numPages - 1); + for (x = 0; x < width; x++) + { + for (y = 0; y < numPages; y++) + { + data[y] = (newLCD[x][y]) ^ (config->invert ? 0xff : 0x00); + } + WriteData(data, numPages); + memcpy(oldLCD[x], newLCD[x], numPages); + } + // and reset RefreshCounter + refreshCounter = 0; + } + else + { + // draw only the changed bytes + } +} + +void cDriverSSD1306::SetBrightness(unsigned int percent) +{ + WriteCommand(kCmdSetContrast, percent * 255 / 100); +} + +void cDriverSSD1306::Reset() +{ + digitalWrite(kGpioReset, LOW); + usleep(1000); + digitalWrite(kGpioReset, HIGH); +} + +void cDriverSSD1306::WriteCommand(uint8_t command) +{ + wiringPiSPIDataRW(kSpiBus, &command, 1); +} + +void cDriverSSD1306::WriteCommand(uint8_t command, uint8_t argument) +{ + uint8_t buffer[2] = {command, argument}; + wiringPiSPIDataRW(kSpiBus, buffer, 2); +} + +void cDriverSSD1306::WriteCommand(uint8_t command, uint8_t argument1, uint8_t argument2) +{ + uint8_t buffer[3] = {command, argument1, argument2}; + wiringPiSPIDataRW(kSpiBus, buffer, 3); +} + +void cDriverSSD1306::WriteData(uint8_t * buffer, uint32_t length) +{ + digitalWrite(kGpioDC, HIGH); + wiringPiSPIDataRW(kSpiBus, buffer, length); + digitalWrite(kGpioDC, LOW); +} + +} // end of namespace diff --git a/glcddrivers/ssd1306.h b/glcddrivers/ssd1306.h new file mode 100644 index 0000000..5d06b4c --- /dev/null +++ b/glcddrivers/ssd1306.h @@ -0,0 +1,53 @@ +/* + * GraphLCD driver library + * + * ssd1306.h - SSD1306 OLED driver class + * + * This file is released under the GNU General Public License. Refer + * to the COPYING file distributed with this package. + * + * (c) 2014 Andreas Regel <andreas.regel AT powarman.de> + */ + +#ifndef _GLCDDRIVERS_SSD1306_H_ +#define _GLCDDRIVERS_SSD1306_H_ + +#include "driver.h" + +namespace GLCD +{ + +class cDriverConfig; + +class cDriverSSD1306 : public cDriver +{ +private: + unsigned char ** newLCD; // wanted state + unsigned char ** oldLCD; // current state + int refreshCounter; + + int CheckSetup(); + + void Reset(); + void WriteCommand(uint8_t command); + void WriteCommand(uint8_t command, uint8_t argument); + void WriteCommand(uint8_t command, uint8_t argument1, uint8_t argument2); + void WriteData(uint8_t * buffer, uint32_t length); + +public: + cDriverSSD1306(cDriverConfig * config); + virtual ~cDriverSSD1306(); + + virtual int Init(); + virtual int DeInit(); + + virtual void Clear(); + virtual void SetPixel(int x, int y, uint32_t data); + //virtual void Set8Pixels(int x, int y, unsigned char data); + virtual void Refresh(bool refreshAll = false); + virtual void SetBrightness(unsigned int percent); +}; + +} // end of namespace + +#endif diff --git a/graphlcd.conf b/graphlcd.conf index e14a589..66ee396 100644 --- a/graphlcd.conf +++ b/graphlcd.conf @@ -661,3 +661,14 @@ Height=240 # HttpDir # Webclients directory of libvncserver installation (only needed for webclient access) #HttpDir=/usr/share/webclients + +######################################################################## + +[ssd1306] +# ssd1306 driver +# This is a driver module for the SSD1306 OLED display controller. +# Default size: 128 x 64 +Driver=ssd1306 +Device=0 +Width=128 +Height=64 |