aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Bodenmann <joel@unormal.org>2013-08-26 00:50:47 +0200
committerJoel Bodenmann <joel@unormal.org>2013-08-26 00:50:47 +0200
commitfa5962eef0094d1b174c8763e456b2c8127a4341 (patch)
treeb7909ee51817e42096cde13643a14b5c4ae001bc
parent273d747b01c707decca7c4a5b50300434db99f87 (diff)
downloaduGFX-fa5962eef0094d1b174c8763e456b2c8127a4341.tar.gz
uGFX-fa5962eef0094d1b174c8763e456b2c8127a4341.tar.bz2
uGFX-fa5962eef0094d1b174c8763e456b2c8127a4341.zip
Added SSD1306 driver by user goeck
-rw-r--r--drivers/gdisp/SSD1306/SSD1306.h60
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld.c646
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld.mk5
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h137
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h139
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld_board_template.h74
-rw-r--r--drivers/gdisp/SSD1306/gdisp_lld_config.h32
-rw-r--r--drivers/gdisp/SSD1306/readme.txt27
-rw-r--r--releases.txt1
9 files changed, 1121 insertions, 0 deletions
diff --git a/drivers/gdisp/SSD1306/SSD1306.h b/drivers/gdisp/SSD1306/SSD1306.h
new file mode 100644
index 00000000..38507d48
--- /dev/null
+++ b/drivers/gdisp/SSD1306/SSD1306.h
@@ -0,0 +1,60 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://chibios-gfx.com/license.html
+ */
+
+#ifndef _SSD1306_H
+#define _SSD1306_H
+
+#define SSD1306_BLACK 0
+#define SSD1306_WHITE 1
+
+#define SSD1306_SETCONTRAST 0x81
+#define SSD1306_DISPLAYALLON_RESUME 0xA4
+#define SSD1306_DISPLAYALLON 0xA5
+#define SSD1306_NORMALDISPLAY 0xA6
+#define SSD1306_INVERTDISPLAY 0xA7
+#define SSD1306_DISPLAYOFF 0xAE
+#define SSD1306_DISPLAYON 0xAF
+
+#define SSD1306_SETDISPLAYOFFSET 0xD3
+#define SSD1306_SETCOMPINS 0xDA
+
+#define SSD1306_SETVCOMDETECT 0xDB
+
+#define SSD1306_SETDISPLAYCLOCKDIV 0xD5
+#define SSD1306_SETPRECHARGE 0xD9
+#define SSD1306_ENABLE_CHARGE_PUMP 0x8D
+
+#define SSD1306_SETMULTIPLEX 0xA8
+#define SSD1306_SETSTARTLINE 0x40
+
+#define SSD1306_MEMORYMODE 0x20
+#define SSD1306_HV_COLUMN_ADDRESS 0x21
+#define SSD1306_HV_PAGE_ADDRESS 0x22
+#define SSD1306_PAM_PAGE_START 0xB0
+
+#define SSD1306_COMSCANINC 0xC0
+#define SSD1306_COMSCANDEC 0xC8
+
+#define SSD1306_SEGREMAP 0xA0
+
+#define SSD1306_CHARGEPUMP 0x8D
+
+#define SSD1306_EXTERNALVCC 0x1
+#define SSD1306_SWITCHCAPVCC 0x2
+
+// Scrolling #defines
+#define SSD1306_SCROLL_ACTIVATE 0x2F
+#define SSD1306_SCROLL_DEACTIVATE 0x2E
+#define SSD1306_SCROLL_SET_VERTICAL_SCROLL_AREA 0xA3
+#define SSD1306_SCROLL_HORIZONTAL_RIGHT 0x26
+#define SSD1306_SCROLL_HORIZONTAL_LEFT 0x27
+#define SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT 0x29
+#define SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_LEFT 0x2A
+
+#endif /* _SSD1306_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld.c b/drivers/gdisp/SSD1306/gdisp_lld.c
new file mode 100644
index 00000000..81fd1d66
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld.c
@@ -0,0 +1,646 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://chibios-gfx.com/license.html
+ */
+
+#include "gfx.h"
+
+#include "SSD1306.h"
+
+#if GFX_USE_GDISP || defined(__DOXYGEN__)
+
+/* Include the emulation code for things we don't support */
+#include "gdisp/lld/emulation.c"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#ifndef GDISP_SCREEN_HEIGHT
+ #define GDISP_SCREEN_HEIGHT 64
+#endif
+#ifndef GDISP_SCREEN_WIDTH
+ #define GDISP_SCREEN_WIDTH 128
+#endif
+
+#define GDISP_INITIAL_CONTRAST 0xFF
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+// Include wiring specific header
+#include "gdisp_lld_board_example_i2c.h"
+
+// Some common routines and macros
+#define delay(us) gfxSleepMicroseconds(us)
+#define delayms(ms) gfxSleepMilliseconds(ms)
+
+// The memory buffer for the display
+static uint8_t gdisp_buffer[GDISP_SCREEN_HEIGHT * GDISP_SCREEN_WIDTH / 8];
+
+/** Set the display to normal or inverse.
+ * @param[in] value 0 for normal mode, or 1 for inverse mode.
+ * @notapi
+ */
+static void invert_display(uint8_t i) {
+ write_cmd(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
+}
+
+/** Turn the whole display off.
+ * Sends the display to sleep, but leaves RAM intact.
+ * @notapi
+ */
+static void display_off(){
+ write_cmd(SSD1306_DISPLAYOFF);
+}
+
+/** Turn the whole display on.
+ * Wakes up this display following a sleep() call.
+ * @notapi
+ */
+static void display_on() {
+ write_cmd(SSD1306_DISPLAYON);
+}
+
+/** Set the vertical shift by COM.
+ * @param[in] value The number of rows to shift, from 0 - 63.
+ * @notapi
+*/
+static void set_display_offset(unsigned char value) {
+ write_cmd(SSD1306_SETDISPLAYOFFSET);
+ write_cmd(value & 0x3F);
+}
+
+/** Set the display contrast.
+ * @param[in] value The contrast, from 1 to 256.
+ * @notapi
+ */
+static void set_contrast(unsigned char value) {
+ write_cmd(SSD1306_SETCONTRAST);
+ write_cmd(value);
+}
+
+/** Set the display start line. This is the line at which the display will start rendering.
+ * @param[in] value A value from 0 to 63 denoting the line to start at.
+ * @notapi
+ */
+static void set_display_start_line(unsigned char value) {
+ write_cmd(SSD1306_SETSTARTLINE | value);
+}
+
+/** Set the segment remap state. This allows the module to be addressed as if flipped horizontally.
+ * NOTE: Changing this setting has no effect on data already in the module's GDDRAM.
+ * @param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped).
+ * @notapi
+ */
+static void set_segment_remap(unsigned char value) {
+ write_cmd(value ? SSD1306_SEGREMAP+1 : SSD1306_SEGREMAP);
+}
+
+/** Set the multiplex ratio.
+ * @param[in] value MUX will be set to (value+1). Valid values range from 15 to 63 - MUX 16 to 64.
+ * @notapi
+ */
+static void set_multiplex_ratio(unsigned char value) {
+ write_cmd(SSD1306_SETMULTIPLEX);
+ write_cmd(value & 0x3F);
+}
+
+/** Set COM output scan direction. If the display is active, this will immediately vertically
+ * flip the display.
+ * @param[in] value 0 = Scan from COM0 (default), 1 = reversed (scan from COM[N-1]).
+ * @notapi
+ */
+static void set_com_output_scan_direction(unsigned char value) {
+ write_cmd(value ? SSD1306_COMSCANDEC : SSD1306_COMSCANINC);
+}
+
+static void set_com_pins_hardware_configuration(unsigned char sequential, unsigned char lr_remap) {
+ write_cmd(SSD1306_SETCOMPINS);
+ write_cmd(0x02 | ((sequential & 1) << 4) | ((lr_remap & 1) << 5));
+}
+
+/** Flip display content horizontally.
+ * NOTE: This only flips display content, but doesn't turn the char writing around.
+ * You have to unmirror everything manually.
+ * @param[in] value 0 = column address 0 = segment 0 (the default), 1 = column address 127 = segment 0 (flipped).
+ * @notapi
+ */
+static void flip_display(unsigned char enable) {
+ if( enable && GDISP.Orientation == GDISP_ROTATE_0) {
+ set_com_output_scan_direction(0);
+ set_segment_remap(0);
+ GDISP.Orientation = GDISP_ROTATE_0;
+ }
+ if( !enable && GDISP.Orientation == GDISP_ROTATE_180) {
+ set_com_output_scan_direction(1);
+ set_segment_remap(1);
+ GDISP.Orientation = GDISP_ROTATE_180;
+ }
+ else
+ return;
+}
+
+/** Perform a "no operation".
+ * @notapi
+ */
+static void nop() {
+ write_cmd(0xE3);
+}
+
+/** Page Addressing Mode: Set the column start address register for
+ * page addressing mode.
+ * @param[in] address The address (full byte).
+ * @notapi
+ */
+static void set_start_address_pam(unsigned char address)
+{
+ // "Set Lower Column Start Address for Page Addressing Mode"
+ write_cmd(address & 0x0F);
+
+ // "Set Higher Column Start Address for Page Addressing Mode"
+ write_cmd((address << 4) & 0x0F);
+}
+
+/** Set memory addressing mode to the given value.
+ * @param[in] mode 0 for Horizontal addressing mode,\n 1 for Vertical addressing mode,\n or 2 for Page addressing mode (PAM). 2 is the default.
+ * @notapi
+ */
+static void set_memory_addressing_mode(unsigned char mode)
+{
+ write_cmd(SSD1306_MEMORYMODE);
+ write_cmd(mode & 0x3);
+}
+
+/** Set column address range for horizontal/vertical addressing mode.
+ * @param[in] start Column start address, 0 - 127.
+ * @param[in] end Column end address, 0 - 127.
+ * @notapi
+ */
+static void set_column_address_hvam(unsigned char start, unsigned char end)
+{
+ write_cmd(SSD1306_HV_COLUMN_ADDRESS);
+ write_cmd(start & 0x7F);
+ write_cmd(end & 0x7F);
+}
+
+/** Set page start and end address for horizontal/vertical addressing mode.
+ * @param[in] start The start page, 0 - 7.
+ * @param[in] end The end page, 0 - 7.
+ * @notapi
+ */
+static void set_page_address_hvam(unsigned char start, unsigned char end)
+{
+ write_cmd(SSD1306_HV_PAGE_ADDRESS);
+ write_cmd(start & 0x07);
+ write_cmd(end & 0x07);
+}
+
+/** Set the GDDRAM page start address for page addressing mode.
+ * @param[in] address The start page, 0 - 7.
+ * @notapi
+ */
+static void set_page_start_pam(unsigned char address)
+{
+ write_cmd(SSD1306_PAM_PAGE_START | (address & 0x07));
+}
+
+/** Set the display clock divide ratio and the oscillator frequency.
+ * @param[in] ratio The divide ratio, default is 0.
+ * @param[in] frequency The oscillator frequency, 0 - 127. Default is 8.
+ * @notapi
+ */
+static void set_display_clock_ratio_and_frequency(unsigned char ratio, unsigned char frequency)
+{
+ write_cmd(SSD1306_SETDISPLAYCLOCKDIV);
+ write_cmd((ratio & 0x0F) | ((frequency & 0x0F) << 4));
+}
+
+/** Set the precharge period.
+ * @param[in] phase1 Phase 1 period in DCLK clocks. 1 - 15, default is 2.
+ * @param[in] phase2 Phase 2 period in DCLK clocks. 1 - 15, default is 2.
+ * @notapi
+ */
+static void set_precharge_period(unsigned char phase1, unsigned char phase2)
+{
+ write_cmd(SSD1306_SETPRECHARGE);
+ write_cmd((phase1 & 0x0F) | ((phase2 & 0x0F ) << 4));
+}
+
+/** Set the Vcomh deselect level.
+ * @param[in] level @p 0 = 0.65 x Vcc, @p 1 = 0.77 x Vcc (default), @p 2 = 0.83 x Vcc.
+ * @notapi
+ */
+static void set_vcomh_deselect_level(unsigned char level)
+{
+ write_cmd(SSD1306_SETVCOMDETECT);
+ write_cmd((level & 0x03) << 4);
+}
+
+/** Enable/disable charge pump.
+ * @param[in] enable 0 to disable, 1 to enable the internal charge pump.
+ * @notapi
+ */
+static void set_charge_pump(unsigned char enable)
+{
+ write_cmd(SSD1306_ENABLE_CHARGE_PUMP);
+ write_cmd(enable ? 0x14 : 0x10);
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/* ---- Required Routines ---- */
+/*
+ The following 2 routines are required.
+ All other routines are optional.
+*/
+
+/**
+ * @brief Low level GDISP driver initialization.
+ *
+ * @notapi
+ */
+bool_t gdisp_lld_init(void) {
+ // Initialize your display
+ init_board();
+
+ // Hardware reset.
+ setpin_reset(TRUE);
+ delayms(1);
+ setpin_reset(FALSE);
+ delayms(10);
+ setpin_reset(TRUE);
+
+ // Get the bus for the following initialization commands.
+ acquire_bus();
+
+ display_off();
+ set_display_clock_ratio_and_frequency(0, 8);
+ #if GDISP_SCREEN_HEIGHT == 64
+ set_multiplex_ratio(0x3F); // 1/64 duty
+ #endif
+ #if GDISP_SCREEN_HEIGHT == 32
+ set_multiplex_ratio(0x1F); // 1/32 duty
+ #endif
+ set_precharge_period(0xF, 0x01); //
+ set_display_offset(0); //
+ set_display_start_line(0); //
+ set_charge_pump(1); // Enable internal charge pump.
+ set_memory_addressing_mode(0); // horizontal addressing mode; across then down //act like ks0108 (horizontal addressing mode)
+ set_segment_remap(1); //
+ set_com_output_scan_direction(1); //
+ #if GDISP_SCREEN_HEIGHT == 64
+ set_com_pins_hardware_configuration(1, 0);
+ #endif
+ #if GDISP_SCREEN_HEIGHT == 32
+ set_com_pins_hardware_configuration(0, 1);
+ #endif
+ set_contrast(GDISP_INITIAL_CONTRAST); // Set initial contrast.
+ set_vcomh_deselect_level(1); //
+ display_on(); // Turn on OLED panel.
+ invert_display(0); // Disable Inversion of display.
+ set_column_address_hvam(0, 127); //
+ set_page_address_hvam(0, 7); //
+
+ release_bus();
+
+ gdisp_lld_display();
+
+ // Initialize the GDISP structure
+ GDISP.Width = GDISP_SCREEN_WIDTH;
+ GDISP.Height = GDISP_SCREEN_HEIGHT;
+ GDISP.Orientation = GDISP_ROTATE_0;
+ GDISP.Powermode = powerOn;
+ GDISP.Contrast = GDISP_INITIAL_CONTRAST;
+ #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
+ GDISP.clipx0 = 0;
+ GDISP.clipy0 = 0;
+ GDISP.clipx1 = GDISP.Width;
+ GDISP.clipy1 = GDISP.Height;
+ #endif
+ return TRUE;
+}
+
+/**
+ * @brief Draws a pixel on the display.
+ *
+ * @param[in] x X location of the pixel
+ * @param[in] y Y location of the pixel
+ * @param[in] color The color of the pixel
+ *
+ * @notapi
+ */
+void gdisp_lld_draw_pixel(coord_t x, coord_t y, color_t color) {
+ #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
+ if (x < GDISP.clipx0 || y < GDISP.clipy0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
+ #endif
+
+ if (color == SSD1306_WHITE)
+ gdisp_buffer[x+ (y/8)*GDISP_SCREEN_WIDTH] |= (1<<y%8);
+ else
+ gdisp_buffer[x+ (y/8)*GDISP_SCREEN_WIDTH] &= ~(1<<y%8);
+}
+
+void gdisp_lld_display() {
+ set_display_start_line(0);
+
+ /* We're sending half a line in one X-mission.*/
+ uint8_t command[GDISP_SCREEN_WIDTH/2],
+ cmdLength = sizeof(command)/sizeof(command[0]),
+ parts = GDISP_SCREEN_WIDTH/cmdLength;
+
+ for(int i=0; i<GDISP_SCREEN_HEIGHT/8; i++){
+ for(int j = 0; j<parts; j++){
+ memmove(command, &gdisp_buffer[i*GDISP_SCREEN_WIDTH + j*cmdLength], cmdLength);
+ write_data(command, cmdLength);
+ }
+ }
+}
+
+/* ---- Optional Routines ---- */
+/*
+ All the below routines are optional.
+ Defining them will increase speed but everything
+ will work if they are not defined.
+ If you are not using a routine - turn it off using
+ the appropriate GDISP_HARDWARE_XXXX macro.
+ Don't bother coding for obvious similar routines if
+ there is no performance penalty as the emulation software
+ makes a good job of using similar routines.
+ eg. If gfillarea() is defined there is little
+ point in defining clear() unless the
+ performance bonus is significant.
+ For good performance it is suggested to implement
+ fillarea() and blitarea().
+*/
+
+#if (GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL) || defined(__DOXYGEN__)
+ /**
+ * @brief Scroll vertically a section of the screen.
+ * @note Optional.
+ * @note If x,y + cx,cy is off the screen, the result is undefined.
+ * @note If lines is >= cy, it is equivalent to a area fill with bgcolor.
+ *
+ * @param[in] x, y The start of the area to be scrolled
+ * @param[in] cx, cy The size of the area to be scrolled
+ * @param[in] lines The number of lines to scroll (Can be positive or negative)
+ * @param[in] bgcolor The color to fill the newly exposed area.
+ *
+ * @notapi
+ */
+ void gdisp_lld_vertical_scroll(coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) {
+ #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
+ if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; }
+ if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; }
+ if (!lines || cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
+ if (x+cx > GDISP.clipx1) cx = GDISP.clipx1 - x;
+ if (y+cy > GDISP.clipy1) cy = GDISP.clipy1 - y;
+ #endif
+
+ /* See datasheet table T10-1 for this*/
+ uint8_t fHeight = (uint8_t)gdispGetFontMetric(gwinGetDefaultFont(), fontLineSpacing);
+ set_multiplex_ratio(GDISP_SCREEN_HEIGHT - fHeight+1);
+ set_display_offset(fHeight-2);
+
+ /* Scrolling animation.*/
+ for(int i=0; i<fHeight; i++){
+ set_display_start_line(i);
+ gfxSleepMilliseconds(10);
+ }
+
+ /* Shift buffer up a font line.*/
+ for (int i = 0; i < GDISP_SCREEN_WIDTH*(GDISP_SCREEN_HEIGHT/8-1); i++) {
+ gdisp_buffer[i] = gdisp_buffer[i+GDISP_SCREEN_WIDTH*(fHeight/8)] >> fHeight % 8;
+ gdisp_buffer[i] |= gdisp_buffer[i+GDISP_SCREEN_WIDTH*(fHeight/8 + 1)] << (8 - fHeight%8);
+ }
+
+ /* Clear last page.*/
+ memset( &gdisp_buffer[GDISP_SCREEN_HEIGHT*GDISP_SCREEN_WIDTH/8 - GDISP_SCREEN_WIDTH*2], SSD1306_BLACK, GDISP_SCREEN_WIDTH*2);
+
+ /* Update display.*/
+ gdisp_lld_display();
+ }
+
+ /**
+ * @warning Implementation only fully supports left and right...some command issues here.
+ * Activate a scroll for rows start through stop.
+ * Hint, the display is 16 rows tall. To scroll the whole display, run:
+ * @code
+ * display.scrollright(0x00, 0x0F)
+ * @endcode
+ * @param[in] start The start of the area to be scrolled
+ * @param[in] stop The size of the area to be scrolled
+ * @param[in] dir direction of scrolling
+ * [left, right, up, down, up_right, up_left, down_left, down_right]
+ * @note Optional. *
+ *
+ * @notapi
+ */
+ void gdisp_lld_start_scroll(uint8_t dir, uint8_t start, uint8_t stop, uint8_t interval){
+// if(dir == GDISP_SCROLL_RIGHT || GDISP_SCROLL_LEFT || GDISP_SCROLL_UP) {
+// switch (dir) {
+// case GDISP_SCROLL_RIGHT:
+// write_cmd(SSD1306_SCROLL_HORIZONTAL_RIGHT);
+// break;
+// case GDISP_SCROLL_LEFT:
+// write_cmd(SSD1306_SCROLL_HORIZONTAL_LEFT);
+// break;
+// }
+// write_cmd(0X00); // Dummy byte.
+// write_cmd(start & 0x07); // Define start page address.
+// switch (interval) { // Set time interval between each scroll step (5 frames)
+// case 2: write_cmd(0x07); break; // 111b
+// case 3: write_cmd(0x04); break; // 100b
+// case 4: write_cmd(0x05); break; // 101b
+// case 5: write_cmd(0x00); break; // 000b
+// case 25: write_cmd(0x06); break; // 110b
+// case 64: write_cmd(0x01); break; // 001b
+// case 128: write_cmd(0x02); break; // 010b
+// case 256: write_cmd(0x03); break; // 011b
+// default:
+// // default to 2 frame interval
+// write_cmd(0x07); break;
+// }
+// write_cmd(stop & 0x07); // Define stop page address
+// write_cmd(0X01); // Set vertical scrolling offset as no row.
+// write_cmd(0XFF); // Undocumented but needed.
+// write_cmd(SSD1306_SCROLL_ACTIVATE);
+// }
+// else if(dir == GDISP_SCROLL_UP || GDISP_SCROLL_DOWN) {
+// switch (dir) {
+// case GDISP_SCROLL_UP:
+// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT);
+// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT);
+// break;
+//
+// case GDISP_SCROLL_DOWN:
+// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT);
+// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_LEFT);
+// break;
+// }
+// write_cmd(0X00); // Dummy byte.
+// write_cmd(start); // Define start page address.
+// write_cmd(0X00); // Set time interval between each scroll step (5 frames)
+// write_cmd(stop); // Define stop page address
+// write_cmd(0X01); // Set vertical scrolling offset as no row.
+// write_cmd(SSD1306_SCROLL_ACTIVATE);
+// gdisp_lld_set_vertical_scroll_area(0x00, GDISP_SCREEN_HEIGHT-10);
+// write_cmd(SSD1306_SCROLL_VERTICAL_AND_HORIZONTAL_RIGHT);
+// write_cmd(0X00); // Dummy byte.
+// write_cmd(start); // Define start page address.
+// write_cmd(0X00); // Set time interval between each scroll step (5 frames)
+// write_cmd(stop); // Define stop page address
+// write_cmd(0X03); // Set vertical scrolling offset as no row.
+// write_cmd(SSD1306_SCROLL_ACTIVATE);
+// }
+ }
+
+ /**
+ * Sets vertical scroll area of display.
+ * @param[in] start The start of the area to be scrolled [y coordinate]
+ * @param[in] stop The size of the area to be scrolled [y coordinate]
+ * @note Optional. *
+ *
+ * @notapi
+ */
+ void gdisp_lld_set_vertical_scroll_area(uint8_t start, uint8_t stop){
+ write_cmd(SSD1306_SCROLL_SET_VERTICAL_SCROLL_AREA);
+ write_cmd(start);
+ write_cmd(stop);
+ }
+
+ /** Deactivate the continuous scroll set up with start_horizontal_scroll() or
+ * start_vertical_and_horizontal_scroll().
+ * @see set_horizontal_scroll, set_vertical_and_horizontal_scroll
+ * @notapi
+ */
+ void gdisp_lld_stop_scroll(void){
+ write_cmd(SSD1306_SCROLL_DEACTIVATE);
+ }
+#endif // GDISP_NEED_SCROLL
+
+#if GDISP_HARDWARE_FILLS || defined(__DOXYGEN__)
+ void gdisp_lld_fill_area(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) {
+ #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
+ if (x < GDISP.clipx0) { cx -= GDISP.clipx0 - x; x = GDISP.clipx0; }
+ if (y < GDISP.clipy0) { cy -= GDISP.clipy0 - y; y = GDISP.clipy0; }
+ if (cx <= 0 || cy <= 0 || x >= GDISP.clipx1 || y >= GDISP.clipy1) return;
+ if (x+cx > GDISP.clipx1) cx = GDISP.clipx1 - x;
+ if (y+cy > GDISP.clipy1) cy = GDISP.clipy1 - y;
+ #endif
+
+ for(int i=x; i<x+cx; i++) {
+ for(int j=y; j<y+cy; j++) {
+ gdisp_lld_draw_pixel(i,j,color);
+ }
+ }
+ }
+#endif // GDISP_HARDWARE_FILLS
+
+#if (GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL) || defined(__DOXYGEN__)
+ /**
+ * @brief Driver Control
+ * @details Unsupported control codes are ignored.
+ * @note The value parameter should always be typecast to (void *).
+ * @note There are some predefined and some specific to the low level driver.
+ * @note GDISP_CONTROL_POWER - Takes a gdisp_powermode_t
+ * GDISP_CONTROL_ORIENTATION - Takes a gdisp_orientation_t
+ * GDISP_CONTROL_BACKLIGHT - Takes an int from 0 to 100. For a driver
+ * that only supports off/on anything other
+ * than zero is on.
+ * GDISP_CONTROL_CONTRAST - Takes an int from 0 to 100.
+ * GDISP_CONTROL_LLD - Low level driver control constants start at
+ * this value.
+ *
+ * @param[in] what What to do.
+ * @param[in] value The value to use (always cast to a void *).
+ *
+ * @notapi
+ */
+ void gdisp_lld_control(unsigned what, void *value) {
+ switch(what) {
+ case GDISP_CONTROL_POWER:
+ if (GDISP.Powermode == (gdisp_powermode_t)value)
+ return;
+ switch((gdisp_powermode_t)value) {
+ case powerOff:
+ display_off();
+ case powerSleep:
+ display_off();
+ case powerDeepSleep:
+ display_off();
+ case powerOn:
+ display_on();
+ default:
+ return;
+ }
+ GDISP.Powermode = (gdisp_powermode_t)value;
+ return;
+ case GDISP_CONTROL_ORIENTATION:
+ if (GDISP.Orientation == (gdisp_orientation_t)value)
+ return;
+ switch((gdisp_orientation_t)value) {
+ case GDISP_ROTATE_0:
+ flip_display(0);
+ GDISP.Height = GDISP_SCREEN_HEIGHT;
+ GDISP.Width = GDISP_SCREEN_WIDTH;
+ break;
+ case GDISP_ROTATE_180:
+ flip_display(1);
+ GDISP.Height = GDISP_SCREEN_HEIGHT;
+ GDISP.Width = GDISP_SCREEN_WIDTH;
+ break;
+ default:
+ return;
+ }
+ #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
+ GDISP.clipx0 = 0;
+ GDISP.clipy0 = 0;
+ GDISP.clipx1 = GDISP.Width;
+ GDISP.clipy1 = GDISP.Height;
+ #endif
+ GDISP.Orientation = (gdisp_orientation_t)value;
+ return;
+ case GDISP_CONTROL_CONTRAST:
+ if ((unsigned)value > 100)
+ value = (void *)100;
+ if (GDISP.Contrast == (uint8_t)((float)((uint8_t)value) * 256.0/100.0) )
+ return;
+ set_contrast((uint8_t)((float)((uint8_t)value) * 256.0/100.0) );
+ GDISP.Contrast = (unsigned)value;
+ return;
+ }
+ }
+#endif // GDISP_NEED_CONTROL
+
+/**
+ * Let the display blink several times by means of invert and invert back.
+ * @param num number of blink cycles to do
+ * @param speed milliseconds to wait between toggling inversion
+ * @param wait milliseconds to wait before start of all blink cycles and after finishing blink cycles
+ * @notapi
+ */
+ void gdisp_lld_display_blink(uint8_t num, uint16_t speed, uint16_t wait){
+ uint8_t inv = 0;
+
+ gfxSleepMilliseconds(wait);
+ for(int i=0; i<2*num; i++) {
+ inv ^= 1;
+ invert_display(inv);
+ gfxSleepMilliseconds(speed);
+ }
+ gfxSleepMilliseconds(wait);
+}
+
+#endif // GFX_USE_GDISP
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld.mk b/drivers/gdisp/SSD1306/gdisp_lld.mk
new file mode 100644
index 00000000..2a2e1364
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld.mk
@@ -0,0 +1,5 @@
+# List the required driver.
+GFXSRC += $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.c
+
+# Required include directories
+GFXINC += $(GFXLIB)/drivers/gdisp/SSD1306
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h
new file mode 100644
index 00000000..0e7e153a
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld_board_example_i2c.h
@@ -0,0 +1,137 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+#define SSD1306_RESET_PORT GPIOB
+#define SSD1306_RESET_PIN 5
+
+/**
+ * The default slave address is 0x3D, (talking about
+ * only the real address part here) and the slave
+ * address can be changed to 0x3C by soldering the
+ * SA0 pads on the bottom side of the module.
+ *
+ * b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0
+ * --------------------------------------
+ * 0 | 1 | 1 | 1 | 1 | 0 |SA0 | R/W
+ */
+#define SSD1306_I2C_ADDRESS 0x3D
+#define SSD1306_SDA_PORT GPIOB
+#define SSD1306_SDA_PIN 7
+#define SSD1306_SCL_PORT GPIOB
+#define SSD1306_SCL_PIN 6
+#define SET_RST palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+#define CLR_RST palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+int8_t vccstate;
+int32_t row_offset = 0;
+
+// I2C configuration structure.
+static I2CConfig i2cconfig;
+
+
+/**
+ * @brief Initialize the board for the display.
+ * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins
+ * @notapi
+ */
+static inline void init_board(void) {
+
+ // RESET pin.
+ palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+
+
+ /*
+ * Initializes the I2C driver 1. The I2C1 signals are routed as follows:
+ * PB6 - SCL.
+ * PB7 - SDA.
+ * Timing value comes from ST I2C config tool (xls):
+ * 0x00901D2B; // 100kHz Standard Mode
+ * 0x00300444; // 100kHz Fast Mode
+ * 0x0030020A; // 400kHz Fast Mode
+ * 0x00100002; // 800kHz Fast Mode +
+ */
+ i2cconfig.timingr = 0x00100002; // 800kHz Fast Mode+
+ i2cInit();
+ palSetPadMode(SSD1306_SCL_PORT, SSD1306_SCL_PIN, PAL_MODE_ALTERNATE(1));
+ palSetPadMode(SSD1306_SDA_PORT, SSD1306_SDA_PIN, PAL_MODE_ALTERNATE(1));
+ vccstate = SSD1306_SWITCHCAPVCC;
+}
+
+/**
+ * @brief Set or clear the lcd reset pin.
+ * @param[in] state TRUE = lcd in reset, FALSE = normal operation
+ * @notapi
+ */
+static inline void setpin_reset(bool_t state) {
+ if(state)
+ SET_RST
+ else
+ CLR_RST
+}
+
+/**
+ * @brief Set the lcd back-light level.
+ * @param[in] percent 0 to 100%
+ * @notapi
+ */
+static inline void set_backlight(uint8_t percent) {
+ // Since we are on OLED no backlight needed
+}
+
+/**
+ * @brief Take exclusive control of the bus
+ * @notapi
+ */
+static inline void acquire_bus(void) {
+ i2cAcquireBus(&I2CD1);
+}
+
+/**
+ * @brief Release exclusive control of the bus
+ * @notapi
+ */
+static inline void release_bus(void) {
+ i2cReleaseBus(&I2CD1);
+}
+
+/**
+ * @brief Send command to the display.
+ * @param[in] cmd The command to send *
+ * @notapi
+ */
+static inline void write_cmd(uint8_t cmd) {
+ uint8_t command[] = { 0x00, // Co = 0, D/C = 0
+ cmd },
+ txLength = sizeof(command)/sizeof(command[0]),
+ rxLength = 0;
+ i2cStart(&I2CD1, &i2cconfig);
+ i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10));
+ i2cStop(&I2CD1);
+}
+
+/**
+ * @brief Send data to the display.
+ * @param[in] data The data to send
+ * @notapi
+ */
+static inline void write_data(uint8_t* data, uint16_t length) {
+ uint8_t command[length+1],
+ txLength = length+1,
+ rxLength = 0;
+ command[0] = 0x40; // Co = 0, D/C = 1
+ memmove(&command[1], data, length);
+
+ i2cStart(&I2CD1, &i2cconfig);
+ i2cMasterTransmitTimeout(&I2CD1, SSD1306_I2C_ADDRESS, command, txLength, NULL, rxLength, MS2ST(10));
+ i2cStop(&I2CD1);
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h b/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h
new file mode 100644
index 00000000..232cd58c
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld_board_example_spi.h
@@ -0,0 +1,139 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+#define SSD1306_RESET_PORT GPIOB
+#define SSD1306_RESET_PIN 5
+#define SSD1306_MISO_PORT GPIOB
+#define SSD1306_MISO_PIN 8
+#define SSD1306_MOSI_PORT GPIOB
+#define SSD1306_MOSI_PIN 7
+#define SSD1306_SCK_PORT GPIOB
+#define SSD1306_SCK_PIN 6
+#define SSD1306_CS_PORT GPIOB
+#define SSD1306_CS_PIN 5
+#define SET_RST palSetPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+#define CLR_RST palClearPad(SSD1306_RESET_PORT, SSD1306_RESET_PIN);
+int8_t vccstate;
+int32_t row_offset = 0;
+
+/*
+ * SPI1 configuration structure.
+ * Speed 42MHz, CPHA=0, CPOL=0, 8bits frames, MSb transmitted first.
+ * The slave select line is the pin 4 on the port GPIOA.
+ */
+static const SPIConfig spi1config = {
+ NULL,
+ /* HW dependent part.*/
+ SSD1306_MISO_PORT,
+ SSD1306_MISO_PIN,
+ 0
+ //SPI_CR1_BR_0
+};
+
+/**
+ * @brief Initialize the board for the display.
+ * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins
+ * @notapi
+ */
+static inline void init_board(void) {
+
+ // RESET pin.
+ palSetPadMode(SSD1306_RESET_PORT, SSD1306_RESET_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+
+ spiInit();
+ palSetPadMode(SSD1306_MISO_PORT, SSD1306_MISO_PIN, PAL_MODE_ALTERNATE(1)|
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(SSD1306_MOSI_PORT, SSD1306_MOSI_PIN, PAL_MODE_ALTERNATE(1)|
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPadMode(SSD1306_SCK_PORT, SSD1306_SCK_PIN, PAL_MODE_ALTERNATE(1)|
+ PAL_STM32_OSPEED_HIGHEST);
+ palSetPad(SSD1306_CS_PORT, SSD1306_CS_PIN);
+ palSetPadMode(SSD1306_CS_PORT, SSD1306_CS_PIN, PAL_MODE_ALTERNATE(1)|
+ PAL_STM32_OSPEED_HIGHEST);
+ vccstate = SSD1306_SWITCHCAPVCC;
+}
+
+/**
+ * @brief Set or clear the lcd reset pin.
+ * @param[in] state TRUE = lcd in reset, FALSE = normal operation
+ * @notapi
+ */
+static inline void setpin_reset(bool_t state) {
+ if(state)
+ SET_RST
+ else
+ CLR_RST
+}
+
+/**
+ * @brief Set the lcd back-light level.
+ * @param[in] percent 0 to 100%
+ * @notapi
+ */
+static inline void set_backlight(uint8_t percent) {
+ // Since we are on OLED no backlight needed
+}
+
+/**
+ * @brief Take exclusive control of the bus
+ * @notapi
+ */
+static inline void acquire_bus(void) {
+ spiAcquireBus(&SPID1);
+}
+
+/**
+ * @brief Release exclusive control of the bus
+ * @notapi
+ */
+static inline void release_bus(void) {
+ spiReleaseBus(&SPID1);
+}
+
+/**
+ * @brief Send command to the display.
+ * @param[in] cmd The command to send *
+ * @notapi
+ */
+static inline void write_cmd(uint8_t cmd) {
+ uint8_t command[] = { 0x00, // Co = 0, D/C = 0
+ cmd },
+ txLength = sizeof(command)/sizeof(command[0]),
+ rxLength = 0;
+
+ spiStart(&SPID1, &spi1config);
+ spiSelect(&SPID1);
+ spiStartSend(&SPID1, txLength, command);
+ spiUnselect(&SPID1);
+ spiStop(&SPID1);
+}
+
+/**
+ * @brief Send data to the display.
+ * @param[in] data The data to send
+ * @notapi
+ */
+static inline void write_data(uint8_t* data, uint16_t length) {
+ uint8_t command[length+1],
+ txLength = length+1;
+ command[0] = 0x40; // Co = 0, D/C = 1
+ memmove(&command[1], data, length);
+
+ spiStart(&SPID1, &spi1config);
+ spiSelect(&SPID1);
+ spiStartSend(&SPID1, txLength, command);
+ spiUnselect(&SPID1);
+ spiStop(&SPID1);
+}
+
+
+#endif /* _GDISP_LLD_BOARD_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_board_template.h b/drivers/gdisp/SSD1306/gdisp_lld_board_template.h
new file mode 100644
index 00000000..48028e83
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld_board_template.h
@@ -0,0 +1,74 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://ugfx.org/license.html
+ */
+
+#ifndef _GDISP_LLD_BOARD_H
+#define _GDISP_LLD_BOARD_H
+
+/**
+ * @brief Initialize the board for the display.
+ * @notes This board definition uses GPIO and assumes exclusive access to these GPIO pins
+ * @notapi
+ */
+static inline void init_board(void) {
+
+}
+
+/**
+ * @brief Set or clear the lcd reset pin.
+ * @param[in] state TRUE = lcd in reset, FALSE = normal operation
+ * @notapi
+ */
+static inline void setpin_reset(bool_t state) {
+
+}
+
+/**
+ * @brief Set the lcd back-light level.
+ * @param[in] percent 0 to 100%
+ * @notapi
+ */
+static inline void set_backlight(uint8_t percent) {
+ // Since we are on OLED no backlight needed
+}
+
+/**
+ * @brief Take exclusive control of the bus
+ * @notapi
+ */
+static inline void acquire_bus(void) {
+
+}
+
+/**
+ * @brief Release exclusive control of the bus
+ * @notapi
+ */
+static inline void release_bus(void) {
+
+}
+
+/**
+ * @brief Send command to the display.
+ * @param[in] cmd The command to send *
+ * @notapi
+ */
+static inline void write_cmd(uint8_t cmd) {
+
+}
+
+/**
+ * @brief Send data to the display.
+ * @param[in] data The data to send
+ * @notapi
+ */
+static inline void write_data(uint8_t* data, uint16_t length) {
+
+}
+
+#endif /* _GDISP_LLD_BOARD_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/gdisp_lld_config.h b/drivers/gdisp/SSD1306/gdisp_lld_config.h
new file mode 100644
index 00000000..158c3e15
--- /dev/null
+++ b/drivers/gdisp/SSD1306/gdisp_lld_config.h
@@ -0,0 +1,32 @@
+/*
+ * This file is subject to the terms of the GFX License. If a copy of
+ * the license was not distributed with this file, you can obtain one at:
+ *
+ * http://chibios-gfx.com/license.html
+ */
+
+#ifndef _GDISP_LLD_CONFIG_H
+#define _GDISP_LLD_CONFIG_H
+
+#if GFX_USE_GDISP
+
+/*===========================================================================*/
+/* Driver hardware support. */
+/*===========================================================================*/
+
+#define GDISP_DRIVER_NAME "SSD1306"
+
+#define GDISP_HARDWARE_CLEARS FALSE
+#define GDISP_HARDWARE_FILLS TRUE
+#define GDISP_HARDWARE_BITFILLS FALSE
+#define GDISP_HARDWARE_SCROLL TRUE
+#define GDISP_HARDWARE_PIXELREAD FALSE
+#define GDISP_HARDWARE_CONTROL TRUE
+
+#define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_MONO
+
+#endif /* GFX_USE_GDISP */
+
+#endif /* _GDISP_LLD_CONFIG_H */
+/** @} */
+
diff --git a/drivers/gdisp/SSD1306/readme.txt b/drivers/gdisp/SSD1306/readme.txt
new file mode 100644
index 00000000..6f0220be
--- /dev/null
+++ b/drivers/gdisp/SSD1306/readme.txt
@@ -0,0 +1,27 @@
+Description:
+
+Driver for OLED with 4-wire serial interface and I²C/SPI interface
+
+Know restictions:
+- Driver works only with SSD1306 hooked up over I2C or SPI (include corresponding header)
+- Driver is written for 128x64 pixel displays (128x32 are only partly supported and need small further work)
+- after using uGFX subsystem gdisp_lld_display() has to be called "by hand" to push framebuffer to display
+
+To use this driver:
+
+1. Add in your gfxconf.h:
+ a) #define GFX_USE_GDISP TRUE
+
+ b) Any optional high level driver defines (see gdisp.h) eg: GDISP_NEED_MULTITHREAD
+
+ c) The following are optional - define them if you are not using the defaults below:
+ #define GDISP_SCREEN_WIDTH 128
+ #define GDISP_SCREEN_HEIGHT 64
+
+2. If you are not using a known board then create a gdisp_lld_board.h file according to
+ given example files (or just stick with them) and ensure it is on your include path.
+
+3. To your makefile add the following lines:
+ include $(GFXLIB)/drivers/gdisp/SSD1306/gdisp_lld.mk
+
+4. Call gdisp_lld_display() every time you want to update display content
diff --git a/releases.txt b/releases.txt
index f7e35003..b657960f 100644
--- a/releases.txt
+++ b/releases.txt
@@ -9,6 +9,7 @@ FEATURE: GWIN list boxes.
FIX: POSIX port removed, now dedicated OS-X and Linux ports
FIX: Several bugfixes
FEATURE: mcufont integration
+FEATURE: SSD1306 driver by user goeck
*** changes after 1.7 ***