Skip to content

Commit

Permalink
Merge pull request #389 from mathertel/master
Browse files Browse the repository at this point in the history
Arduino GFX display driver for SSD1306
  • Loading branch information
moononournation authored Nov 30, 2023
2 parents 0d71e26 + 9cebe9f commit 074104e
Show file tree
Hide file tree
Showing 6 changed files with 350 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/Arduino_GFX_Library.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include "display/Arduino_SEPS525.h"
#include "display/Arduino_SH1106.h"
#include "display/Arduino_SSD1283A.h"
#include "display/Arduino_SSD1306.h"
#include "display/Arduino_SSD1331.h"
#include "display/Arduino_SSD1351.h"
#include "display/Arduino_ST7735.h"
Expand Down
37 changes: 31 additions & 6 deletions src/canvas/Arduino_Canvas_Mono.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#include "Arduino_Canvas_Mono.h"

Arduino_Canvas_Mono::Arduino_Canvas_Mono(int16_t w, int16_t h, Arduino_G *output, int16_t output_x, int16_t output_y, bool verticalByte)
: Arduino_GFX(w, h), _output(output), _output_x(output_x), _output_y(output_y), _verticalByte(verticalByte)
: Arduino_GFX(w, h), _output(output), _output_x(output_x), _output_y(output_y), _verticalByte(verticalByte),
_canvas_width(w), _canvas_height(h)
{
}

Expand Down Expand Up @@ -56,10 +57,35 @@ bool Arduino_Canvas_Mono::begin(int32_t speed)

void Arduino_Canvas_Mono::writePixelPreclipped(int16_t x, int16_t y, uint16_t color)
{
// rotate to the orientation in the bitmap buffer
if (_rotation)
{
int16_t tmp;

switch (_rotation)
{
case 1: // 90° right
tmp = y;
y = x;
x = _canvas_width - 1 - tmp;
break;
case 2: // 180°
x = _canvas_width - 1 - x;
y = _canvas_height - 1 - y;
break;
case 3: // 270° right == 90° left
tmp = y;
y = _canvas_height - 1 - x;
x = tmp;
break;
}
}

// change the pixel in the original orientation of the bitmap buffer
if (_verticalByte)
{
// vertical buffer layout: 1 byte in the buffer contains 8 vertical pixels
int32_t pos = x + (y / 8) * _width;
int32_t pos = x + (y / 8) * _canvas_width;

if (color & 0b1000010000010000)
{
Expand All @@ -70,13 +96,12 @@ void Arduino_Canvas_Mono::writePixelPreclipped(int16_t x, int16_t y, uint16_t co
_framebuffer[pos] &= ~(1 << (y & 7));
}
}

else
{
// horizontal buffer layout: 1 byte in the buffer contains 8 horizontal pixels
int16_t w = (_width + 7) / 8;
int16_t w = (_canvas_width + 7) / 8;
int32_t pos = y * w + x / 8;

if (color & 0b1000010000010000)
{
_framebuffer[pos] |= 0x80 >> (x & 7);
Expand All @@ -91,7 +116,7 @@ void Arduino_Canvas_Mono::writePixelPreclipped(int16_t x, int16_t y, uint16_t co
void Arduino_Canvas_Mono::flush()
{
if (_output)
_output->drawBitmap(_output_x, _output_y, _framebuffer, _width, _height, WHITE, BLACK);
_output->drawBitmap(_output_x, _output_y, _framebuffer, _canvas_width, _canvas_height, WHITE, BLACK);
}

uint8_t *Arduino_Canvas_Mono::getFramebuffer()
Expand Down
1 change: 1 addition & 0 deletions src/canvas/Arduino_Canvas_Mono.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Arduino_Canvas_Mono : public Arduino_GFX
uint8_t *_framebuffer = nullptr;
Arduino_G *_output = nullptr;
int16_t _output_x, _output_y;
int16_t _canvas_width, _canvas_height; // width and height of canvas buffer
bool _verticalByte;

private:
Expand Down
15 changes: 15 additions & 0 deletions src/canvas/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# About the canvas mono class of the GFX library

There are some Display Chips on the market that can display a small amount of graphic data but
don't offer much functionality on it's own on other than data transfer. Examples are the OLED
Displays Chips SSD1306 or SH1106.

These displays need an in memory copy of the graphics and updating the display is implemented by
a bulk data transfer of the internal memory to the display.

Therefore the Canvas Mono class must be initialized in a way the internal memory fits to the
memory inside the graphic driver chip including the width and height of the display memory and
the orientation of a byte to either horizontal or vertical.

Bei changing the orientation of the canvas the graphics elements like lines or characters are positioned on the display memory accordingly. When the rotating in 90 or 270 degrees der width and height of the logical display will be swapped but not the memory organization.

263 changes: 263 additions & 0 deletions src/display/Arduino_SSD1306.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Arduino GFX display driver for SSD1306

#include "Arduino_DataBus.h"
#include "Arduino_SSD1306.h"

// SSD1306 commands, See Datasheet
#define SSD1306_MEMORYMODE 0x20
#define SSD1306_COLUMNADDR 0x21
#define SSD1306_PAGEADDR 0x22
#define SSD1306_SETCONTRAST 0x81
#define SSD1306_CHARGEPUMP 0x8D
#define SSD1306_SEGREMAP 0xA0
#define SSD1306_SEGREMAPINV 0xA1
#define SSD1306_DISPLAYALLON_RESUME 0xA4
#define SSD1306_NORMALDISPLAY 0xA6
#define SSD1306_SETMULTIPLEX 0xA8
#define SSD1306_DISPLAYOFF 0xAE
#define SSD1306_DISPLAYON 0xAF
#define SSD1306_COMSCANDEC 0xC8
#define SSD1306_SETDISPLAYOFFSET 0xD3
#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
#define SSD1306_SETPRECHARGE 0xD9
#define SSD1306_SETCOMPINS 0xDA
#define SSD1306_SETVCOMDETECT 0xDB

#define SSD1306_SETLOWCOLUMN 0x00
#define SSD1306_SETHIGHCOLUMN 0x10
#define SSD1306_SETSTARTLINE 0x40

#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
#define SSD1306_DEACTIVATE_SCROLL 0x2E
#define SSD1306_ACTIVATE_SCROLL 0x2F
#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3

Arduino_SSD1306::Arduino_SSD1306(Arduino_DataBus *bus, int8_t rst, int16_t w, int16_t h)
: Arduino_G(w, h), _bus(bus), _rst(rst)
{
_rotation = 0;
_pages = (h + 7) / 8;
}

bool Arduino_SSD1306::begin(int32_t speed)
{
Serial.println("SSD1306::begin()");

if (!_bus)
{
// println("SSD1306::bus not given");
}
else if (!_bus->begin(speed))
{
// println("SSD1306::bus not started");
return false;
}

// println("SSD1306::Initialize Display");

if (_rst != GFX_NOT_DEFINED)
{
pinMode(_rst, OUTPUT);
digitalWrite(_rst, HIGH);
delay(20);
digitalWrite(_rst, LOW);
delay(20);
digitalWrite(_rst, HIGH);
delay(20);
}

// display parameter default values
uint8_t comPins = 0x12;
_colStart = 0;
_colEnd = WIDTH - 1;

if ((WIDTH == 128) && (HEIGHT == 32))
{
comPins = 0x02;
_contrast = 0x8F;
}
else if ((WIDTH == 128) && (HEIGHT == 64))
{
comPins = 0x12;
_contrast = 0x9F;
}
else if ((WIDTH == 72) && (HEIGHT == 40))
{
comPins = 0x12;
_contrast = 0x82;
_colStart = 28;
_colEnd = 28 + WIDTH - 1;
}
else if ((WIDTH == 96) && (HEIGHT == 16))
{
comPins = 0x2;
_contrast = 0x10;
}
else
{
// Other screen varieties -- TBD
}

static const uint8_t init_sequence[] = {
BEGIN_WRITE,
WRITE_BYTES, 25,
SSD1306_DISPLAYOFF, // 0xAE
SSD1306_SETCONTRAST, _contrast, // 0x81 nn
SSD1306_NORMALDISPLAY, // 0xA6
SSD1306_DEACTIVATE_SCROLL, // 0x2E
SSD1306_MEMORYMODE, 0x00, // 0x20 00 Horizontal addressing mode
SSD1306_SEGREMAPINV, // 0xA1
SSD1306_SETMULTIPLEX, (uint8_t)(HEIGHT - 1), // 0xA8 nn
SSD1306_COMSCANDEC, // 0xC8
SSD1306_SETDISPLAYOFFSET, 0x00, // 0xD3 00 no offset
SSD1306_SETCOMPINS, comPins, // 0xDA nn
SSD1306_SETDISPLAYCLOCKDIV, 0x80, // 0xD5 0x80
SSD1306_SETPRECHARGE, 0x22, // 0xd9 0x22
SSD1306_SETVCOMDETECT, 0x40, // 0xDB 0x40
SSD1306_CHARGEPUMP, 0x14, // 0x8D 0x14
SSD1306_SETSTARTLINE | 0x0, // 0x40 line #0
SSD1306_DISPLAYALLON_RESUME, // 0xA4
END_WRITE,

DELAY, 100,

BEGIN_WRITE,
WRITE_BYTES, 2,
0x00, // Co = 0, D/C = 0
SSD1306_DISPLAYON,
END_WRITE};

_bus->batchOperation(init_sequence, sizeof(init_sequence));

return true;
}

void Arduino_SSD1306::setBrightness(uint8_t brightness){
// _sendCommand(SSD1306_SETCONTRAST);
// ??? _sendCommand((brightness < 50) ? 0 : _contrast); ???
// _sendCommand((brightness < 50) ? 0 : 0x8f);
}; // setBrightness

void Arduino_SSD1306::drawBitmap(int16_t xStart, int16_t yStart, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, uint16_t bg)
{
printf("SSD1306::drawBitmap %d/%d w:%d h:%d\n", xStart, yStart, w, h);
uint16_t bufferLength = TWI_BUFFER_LENGTH;

#if 0
// print bitmap on Serial log
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
uint8_t b = bitmap[(y / 8) * w + x];
if (b & (1 << (y % 8)))
{
Serial.print('#');
}
else
{
Serial.print('.');
}
}
Serial.println();
}
Serial.println();
#endif

#if 1
// transfer the whole bitmap
for (uint8_t p = 0; p < _pages; p++)
{
uint8_t *pptr = bitmap + (p * w); // page start pointer
uint16_t bytesOut = 0;

// start page sequence
_bus->beginWrite();
// ==> _bus->writeBytes( SSD1306_PAGEADDR);

_bus->write(SSD1306_PAGEADDR);
_bus->write(0); // Page start address
_bus->write(0x7); // Page end (not really, but works here)

_bus->write(SSD1306_COLUMNADDR);
_bus->write(_colStart);
_bus->write(_colEnd);
// set column
_bus->endWrite();

// send out page data
for (int x = 0; x < w; x++)
{
if (!bytesOut)
{
_bus->beginWrite();
_bus->write((uint8_t)0x40);
bytesOut = 1;
}

_bus->write(*pptr++);
bytesOut++;

if (bytesOut == bufferLength)
{
_bus->endWrite();
bytesOut = 0;
}
}
if (bytesOut)
{
_bus->endWrite();
}
}
#endif
} // drawBitmap()

void Arduino_SSD1306::drawIndexedBitmap(int16_t, int16_t, uint8_t *, uint16_t *, int16_t, int16_t, int16_t)
{
// println("SSD1306::Not Implemented drawIndexedBitmap()");
}

void Arduino_SSD1306::draw3bitRGBBitmap(int16_t, int16_t, uint8_t *bitmap, int16_t w, int16_t h)
{
// println("SSD1306::Not Implemented draw3bitRGBBitmap()");
}

void Arduino_SSD1306::draw16bitRGBBitmap(int16_t, int16_t, uint16_t *, int16_t, int16_t)
{
// println("SSD1306::Not Implemented draw16bitRGBBitmap()");
}

void Arduino_SSD1306::draw24bitRGBBitmap(int16_t, int16_t, uint8_t *, int16_t, int16_t)
{
// println("SSD1306::Not Implemented draw24bitRGBBitmap()");
}

void Arduino_SSD1306::invertDisplay(bool i)
{
// _bus->sendCommand(i ? ILI9488_INVON : ILI9488_INVOFF);
}

void Arduino_SSD1306::displayOn(void)
{
uint8_t seq[] = {
BEGIN_WRITE,
WRITE_BYTES, 2,
0x00, // sequence of commands
SSD1306_DISPLAYON,
END_WRITE};
_bus->batchOperation(seq, sizeof(seq));
}

void Arduino_SSD1306::displayOff(void)
{
uint8_t seq[] = {
BEGIN_WRITE,
WRITE_BYTES, 2,
0x00, // sequence of commands
SSD1306_DISPLAYOFF,
END_WRITE};
_bus->batchOperation(seq, sizeof(seq));
}
Loading

0 comments on commit 074104e

Please sign in to comment.