summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Regel <andreas.regel@gmx.de>2014-10-19 22:01:27 +0200
committerAndreas Regel <andreas.regel@gmx.de>2016-04-01 23:44:13 +0200
commitb8977439a9e142cb870fd6bbe79dab152e93100d (patch)
tree8e726548576981cb158cdb9ac82faf7c301efb65
parent831b3ad714a036edab6b63f84040ab8b32a43ef4 (diff)
downloadgraphlcd-base-b8977439a9e142cb870fd6bbe79dab152e93100d.tar.gz
graphlcd-base-b8977439a9e142cb870fd6bbe79dab152e93100d.tar.bz2
Add driver for SSD1306 OLED display on raspberry pi
-rw-r--r--Make.config2
-rw-r--r--glcddrivers/Makefile6
-rw-r--r--glcddrivers/drivers.c10
-rw-r--r--glcddrivers/drivers.h3
-rw-r--r--glcddrivers/ssd1306.c340
-rw-r--r--glcddrivers/ssd1306.h53
-rw-r--r--graphlcd.conf11
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