First part of SSD130x Oled controller support - Init - Set pixels and tiles (8x8...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Sun, 16 Oct 2016 15:37:52 +0000 (17:37 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
extdrv/ssd130x_oled_driver.c [new file with mode: 0644]
include/extdrv/ssd130x_oled_driver.h [new file with mode: 0644]

diff --git a/extdrv/ssd130x_oled_driver.c b/extdrv/ssd130x_oled_driver.c
new file mode 100644 (file)
index 0000000..f8b2d20
--- /dev/null
@@ -0,0 +1,410 @@
+/****************************************************************************
+ *   extdrv/ssd130x_oled_driver.c
+ *
+ * I2C Driver for 128x64 oled display drivers
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+#include "core/system.h"
+#include "core/systick.h"
+#include "core/pio.h"
+#include "lib/stdint.h"
+#include "lib/errno.h"
+#include "lib/string.h"
+#include "drivers/gpio.h"
+#include "drivers/i2c.h"
+
+#include "extdrv/ssd130x_oled_driver.h"
+
+
+/***************************************************************************** */
+
+/* SSD1306 starts with control reg set to 0x7F and in normal display mode,
+ *  which is equivalent to 0xA4 command.
+ * (Refer to chapter 8.5 of SSD1306 manual)
+ *
+ * The power on sequence is described in chapter 8.9 of SSD1306 manual.
+ *  After VDD power stable, Reset signal should be pulled low for at least 3us
+ *  and then VCC turned on.
+ *  Once VCC stable, send 0xAF command to turn display ON and wait for 100ms.
+ *
+ * The power off sequence starts by sending 0xAE command, then turn off VCC,
+ *  wait for 100ms, and turn off VDD.
+ *
+ * The Graphic Display Data RAM (GDDRAM) is 128 x 64 bits divided in 8 pages
+ *  of 128 bytes each. One page represents 8 lines of 128 dots.
+ * When writing one byte to one page, the byte fills one collumn (8 vertical
+ *  pixels), and the next byte fills the next collumn, and so on.
+ * (Refer to chapter 8.7 of SSD1306 manual)
+ *
+ * No data read is available in serial (SPI and I2C ?) mode.
+ * The serial interface mode is always in write mode.
+ *
+ * The GDDRAM column address pointer will be increased automatically by one
+ *  after each data write.
+ */
+
+
+ /*
+ * Note : This driver does not implemment and support page adressing. All page
+ *  addressing commands are not supported.
+ * This affects the following commands :
+ *   - All commands 0x00 to 0x1F are not supported (set column start for page
+ *       addressing mode).
+ *   - ssd130x_set_mem_addressing_mode() (0x20) supports only
+ *       SSD130x_ADDR_TYPE_HORIZONTAL and SSD130x_ADDR_TYPE_VERTICAL
+ *   - Set page start address (0xB0 to 0xB7)
+ *
+ * Note some other commands are not supported:
+ *   - Set display start line (0x40 to 0x7F)
+ *   - Set segment remap (0xA0 and 0xA1)
+ *   - Set multiplex ratio (0xA8)
+ *   - Set display clock divide ration and oscilator frequency (0xD5)
+ *   - Set pre-charge period (0xD9)
+ *   - Set COM pins hardware configuration (0xDA)
+ *   - Set Vcomh deselect level (0xDB)
+ *   - No OP (0xE3)
+ */
+
+/***************************************************************************** */
+/* Commands */
+#define CMD_BUF_SIZE 24
+int ssd130x_send_command(struct oled_display* conf, uint8_t cmd, uint8_t* data, uint8_t len)
+{
+       char cmd_buf[CMD_BUF_SIZE] = { conf->address, SSD130x_NEXT_BYTE_CMD, cmd, };
+       int ret = 0;
+
+       if (len > (CMD_BUF_SIZE - 3)) {
+               return -EINVAL;
+       }
+
+       if (len != 0) {
+               int i = 0;
+               for (i = 0; i < len; i++) {
+                       cmd_buf[ 3 + (2 * i) ] = SSD130x_NEXT_BYTE_CMD;
+                       cmd_buf[ 4 + (2 * i) ] = data[i];
+               }
+       }
+       ret = i2c_write(conf->bus_num, cmd_buf, (3 + (len * 2)), NULL);
+       if (ret != (3 + (len * 2))) {
+               conf->probe_ok = 0;
+               return ret;
+       }
+       return 0;
+}
+
+
+/* Set memory adressing mode (0x20)
+ * There are 3 different memory addressing mode in SSD130x: page addressing
+ *  mode, horizontal addressing mode and vertical addressing mode.
+ * This command sets the way of memory addressing into one of the above three
+ *   modes.
+ * (Refer to chapter 10.1.3 of SSD1306 manual)
+ *
+ * Note : This driver does not implemment and support page adressing. All page
+ *  addressing commands are not supported.
+ */
+int ssd130x_set_mem_addressing_mode(struct oled_display* conf, uint8_t type)
+{
+       return ssd130x_send_command(conf, SSD130x_CMD_ADDR_TYPE, &type, 1);
+}
+
+/* Set column address (0x21)
+ * This triple byte command specifies column start address and end address of
+ *  the display data RAM.
+ * This command also sets the column address pointer to column start address.
+ * This pointer is used to define the current read/write column address in
+ *  graphic display data RAM.
+ * If horizontal address increment mode is enabled by command 20h, after
+ *  finishing read/write one column data, it is incremented automatically to
+ *  the next column address.
+ * Whenever the column address pointer finishes accessing the end column
+ *  address, it is reset back to start column address and the row address is
+ *  incremented to the next row.
+ */
+int ssd130x_set_column_address(struct oled_display* conf, uint8_t col_start, uint8_t col_end)
+{
+       uint8_t buf[2] = { col_start, col_end };
+       return ssd130x_send_command(conf, SSD130x_CMD_COL_ADDR, buf, 2);
+}
+
+/* Set page address (0x22)
+ * This triple byte command specifies page start address and end address of the
+ *  display data RAM.
+ * This command also sets the page address pointer to page start address.
+ * This pointer is used to define the current read/write page address in
+ *  graphic display data RAM.
+ * If vertical address increment mode is enabled by command 20h, after
+ *  finishing read/write one page data, it is incremented automatically to the
+ *  next page address.
+ * Whenever the page address pointer finishes accessing the end page address,
+ *  it is reset back to start page address.
+ */
+int ssd130x_set_page_address(struct oled_display* conf, uint8_t page_start, uint8_t page_end)
+{
+       uint8_t buf[2] = { page_start, page_end, };
+       return ssd130x_send_command(conf, SSD130x_CMD_PAGE_ADDR, buf, 2);
+}
+
+/* Set contrast (0x81)
+ * The contrast value is between 0 and 256
+ */
+int ssd130x_set_contrast(struct oled_display* conf)
+{
+       return ssd130x_send_command(conf, SSD130x_CMD_CONTRAST, &(conf->contrast), 1);
+}
+
+/* Set display ON from GDDRAM (0xA4) or regardless of GDDRAM (0xA5)
+ * use_ram is either SSD130x_DISP_RAM or SSD130x_DISP_BLANK
+ */
+int ssd130x_set_display_on(struct oled_display* conf, uint8_t use_ram)
+{
+       if (use_ram == SSD130x_DISP_BLANK) {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_NORAM, NULL, 0);
+       } else {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_RAM, NULL, 0);
+       }
+}
+
+/* Set display to normal or reverse video
+ * status is either SSD130x_DISP_NORMAL or SSD130x_DISP_REVERSE
+ */
+int ssd130x_display_video_reverse(struct oled_display* conf)
+{
+       if (conf->video_mode == SSD130x_DISP_REVERSE) {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_REVERSE, NULL, 0);
+       } else {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_NORMAL, NULL, 0);
+       }
+}
+
+/* Set display ON/OFF
+ * status is either SSD130x_DISP_ON or SSD130x_DISP_OFF
+ */
+int ssd130x_display_power(struct oled_display* conf, uint8_t status)
+{
+       if (status == SSD130x_DISP_OFF) {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_OFF, NULL, 0);
+       } else {
+               return ssd130x_send_command(conf, SSD130x_CMD_DISP_ON, NULL, 0);
+       }
+}
+
+/* Set scan direction (0xC0 / 0xC8)
+ * Scan direction is either SSD130x_SCAN_TOP_BOTTOM or SSD130x_SCAN_BOTTOM_TOP
+ */
+int ssd130x_set_scan_direction(struct oled_display* conf)
+{
+       if (conf->scan_dir == SSD130x_SCAN_TOP_BOTTOM) {
+               return ssd130x_send_command(conf, SSD130x_CMD_COM_SCAN_NORMAL, NULL, 0);
+       } else {
+               return ssd130x_send_command(conf, SSD130x_CMD_COM_SCAN_REVERSE, NULL, 0);
+       }
+}
+/* Set read direction (0xA0 / 0xA1)
+ * Scan direction is either SSD130x_CMD_SEG0_MAP_RIGHT or SSD130x_CMD_SEG0_MAP_LEFT
+ */
+int ssd130x_set_read_direction(struct oled_display* conf)
+{
+       if (conf->read_dir == SSD130x_RIGHT_TO_LEFT) {
+               return ssd130x_send_command(conf, SSD130x_CMD_SEG0_MAP_RIGHT, NULL, 0);
+       } else {
+               return ssd130x_send_command(conf, SSD130x_CMD_SEG0_MAP_LEFT, NULL, 0);
+       }
+}
+
+/* Set display offset
+ * Move the display up or down by a given number of lines
+ * FIXME : Check influence of COM output scan direction.
+ */
+int ssd130x_set_display_offset(struct oled_display* conf, uint8_t dir, uint8_t nb_lines)
+{
+       uint8_t offset = 0;
+       if (nb_lines >= SSD130x_NB_LINES) {
+               return -EINVAL;
+       }
+       if (dir == SSD130x_MOVE_TOP) {
+               offset = SSD130x_OFFSET_DATA(nb_lines);
+       } else {
+               offset = SSD130x_OFFSET_DATA(64 - nb_lines);
+       }
+       return ssd130x_send_command(conf, SSD130x_CMD_DISPLAY_OFFSET, &offset, 1);
+}
+
+
+
+/***************************************************************************** */
+/* Init */
+int ssd130x_display_off(struct oled_display* conf)
+{
+       return ssd130x_display_power(conf, SSD130x_DISP_OFF);
+}
+
+int ssd130x_display_on(struct oled_display* conf)
+{
+       int ret = 0;
+
+       /* Display OFF */
+       ret = ssd130x_display_power(conf, SSD130x_DISP_OFF);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_mem_addressing_mode(conf, SSD130x_ADDR_TYPE_HORIZONTAL);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_scan_direction(conf);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_read_direction(conf);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_display_on(conf, SSD130x_DISP_RAM);
+       if (ret != 0) {
+               return ret;
+       }
+
+       return ssd130x_display_power(conf, SSD130x_DISP_ON);
+}
+
+
+
+/***************************************************************************** */
+/* Data */
+
+/* Our internal buffer for the whole display.
+ * Graphical data starts at byte 4. The first four bytes are here for temporary
+ *  storage of display data during I2C frame transfer.
+ */
+#define GDDRAM_SIZE   (128 * 8)
+static uint8_t gddram[ 4 + GDDRAM_SIZE ];
+static uint8_t* gddram_start = (gddram + 4);
+
+
+int ssd130x_send_data(struct oled_display* conf, uint8_t* start, uint16_t len)
+{
+       int ret = 0;
+
+       /* Check that start and satrt + len are within buffer */
+
+       /* Copy previous two bytes to storage area (gddram[0] and gddram[1]) */
+       gddram[0] = *(start - 2);
+       gddram[1] = *(start - 1);
+
+       /* Setup I2C transfer */
+       *(start - 2) = conf->address;
+       *(start - 1) = SSD130x_DATA_ONLY;
+
+       /* Send data on I2C bus */
+       ret = i2c_write(conf->bus_num, (start - 2), (2 + len), NULL);
+
+       /* Restore gddram data */
+       *(start - 2) = gddram[0];
+       *(start - 1) = gddram[1];
+
+       if (ret != (2 + len)) {
+               return ret;
+       }
+       return len;
+}
+
+/* Set whole display to given value */
+int ssd130x_display_set(struct oled_display* conf, uint8_t val)
+{
+       memset(gddram_start, val, GDDRAM_SIZE);
+       return 0;
+}
+
+
+/* Update what is really displayed */
+int ssd130x_display_full_screen(struct oled_display* conf)
+{
+       int ret = 0;
+
+       ret = ssd130x_set_column_address(conf, 0, 127);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_page_address(conf, 0, 7);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_send_data(conf, gddram_start, GDDRAM_SIZE);
+       if (ret != GDDRAM_SIZE) {
+               return ret;
+       }
+       return 0;
+}
+
+/* Change a "tile" in the GDDRAM memory.
+ * A tile is a 8x8 pixels region, aligned on a 8x8 grid representation of the display.
+ *  x0 and y0 are in number of tiles.
+ */
+int ssd130x_update_tile(struct oled_display* conf, uint8_t x0, uint8_t y0)
+{
+       uint8_t* addr = gddram_start + ((y0 >> 3) * 128) + x0;
+       int ret = 0;
+
+       ret = ssd130x_set_column_address(conf, (x0 * 8), (((x0 + 1) * 8) - 1));
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_set_page_address(conf, y0, y0);
+       if (ret != 0) {
+               return ret;
+       }
+       ret = ssd130x_send_data(conf, addr, 8);
+       if (ret != 0) {
+               return ret;
+       }
+       return 0;
+}
+
+int ssd130x_update_modified(struct oled_display* conf)
+{
+       return 0;
+}
+
+/* Change our internal buffer, without actually displaying the changes */
+int ssd130x_set_pixel(struct oled_display* conf, uint8_t x0, uint8_t y0, uint8_t state)
+{
+       uint8_t* addr = gddram_start + ((y0 >> 3) * 128) + x0;
+       if (state != 0) {
+               *addr |=  (0x01 << (7 - (x0 & 0x03)));
+       } else {
+               *addr &= ~(0x01 << (7 - (x0 & 0x03)));
+       }
+       return 0;
+}
+
+/* Change a "tile" in the bitmap memory.
+ * A tile is a 8x8 pixels region, aligned on a 8x8 grid representation of the display.
+ *  x0 and y0 are in number of tiles.
+ */
+int ssd130x_set_tile(struct oled_display* conf, uint8_t x0, uint8_t y0, uint8_t* tile)
+{
+       uint8_t* addr = gddram_start + (y0 * 128) + (x0 * 8);
+       memcpy(addr, tile, 8);
+       return 0;
+}
+
diff --git a/include/extdrv/ssd130x_oled_driver.h b/include/extdrv/ssd130x_oled_driver.h
new file mode 100644 (file)
index 0000000..19f055d
--- /dev/null
@@ -0,0 +1,338 @@
+/****************************************************************************
+ *   extdrv/ssd130x_oled_driver.h
+ *
+ * I2C Driver for 128x64 oled display drivers
+ *
+ * Copyright 2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef EXTDRV_SSD130X_OLED_DRIVER_H
+#define EXTDRV_SSD130X_OLED_DRIVER_H
+
+#include "lib/stdint.h"
+
+
+/***************************************************************************** */
+/* Oled Display */
+struct oled_display {
+       uint8_t address; /* 8 bits address */
+       uint8_t bus_num; /* I2C bus number */
+       uint8_t probe_ok;
+       uint8_t video_mode;
+       uint8_t contrast;
+       uint8_t scan_dir;
+       uint8_t read_dir;
+       uint8_t display_offset_dir;
+       uint8_t display_offset;
+};
+
+#define SSD130x_NB_LINES   64
+#define SSD130x_NB_PAGES   8
+#define SSD130x_NB_COL     128
+
+enum ssd130x_defs {
+       SSD130x_DISP_OFF = 0,
+       SSD130x_DISP_ON,
+       SSD130x_DISP_RAM,
+       SSD130x_DISP_BLANK,
+       /* For reverse video */
+       SSD130x_DISP_NORMAL,
+       SSD130x_DISP_REVERSE,
+       /* For scan dirrection */
+       SSD130x_SCAN_TOP_BOTTOM,
+       SSD130x_SCAN_BOTTOM_TOP,
+       SSD130x_RIGHT_TO_LEFT,
+       SSD130x_LEFT_TO_RIGHT,
+       /* For display offset */
+       SSD130x_MOVE_TOP,
+       SSD130x_MOVE_BOTTOM,
+};
+
+
+#define SSD130x_DATA_ONLY       0x40
+#define SSD130x_NEXT_BYTE_DATA  0xC0
+#define SSD130x_NEXT_BYTE_CMD   0x80
+
+/* Display controll */
+#define SSD130x_CMD_CONTRAST           0x81
+#define SSD130x_CMD_DISP_RAM           0xA4
+#define SSD130x_CMD_DISP_NORAM         0xA5
+#define SSD130x_CMD_DISP_NORMAL        0xA6
+#define SSD130x_CMD_DISP_REVERSE       0xA7
+#define SSD130x_CMD_DISP_OFF           0xAE
+#define SSD130x_CMD_DISP_ON            0xAF
+
+/* Scrolling controll */
+#define SSD130x_CMD_SCROLL_RIGHT       0x26
+#define SSD130x_CMD_SCROLL_LEFT        0x27
+#define SSD130x_CMD_VSCROLL_RIGHT      0x29
+#define SSD130x_CMD_VSCROLL_LEFT       0x2A
+#define SSD130x_CMD_STOP_SCROLL        0x2E
+#define SSD130x_CMD_START_SCROLL       0x2F
+#define SSD130x_CMD_VSCROLL_REGION     0xA3
+/* Data bytes for scrolling controll */
+#define SSD130x_SCROLL_DATA_DUMMY  0x00
+#define SSD130x_SCROLL_DATA_END    0xFF
+#define SSD130x_SCROLL_DATA_START_PAGE(x) ((x) & 0x07)
+#define SSD130x_SCROLL_DATA_END_PAGE(x)   ((x) & 0x07)
+#define SSD130x_SCROLL_DATA_ROWS(x)   ((x) & 0x3F)
+#define SSD130x_SCROLL_DATA_STEP(x) ((x) & 0x07)
+/* Scroll steps definitions */
+#define SSD130x_SCROLL_2_FRAMES    0x07
+#define SSD130x_SCROLL_3_FRAMES    0x04
+#define SSD130x_SCROLL_4_FRAMES    0x05
+#define SSD130x_SCROLL_5_FRAMES    0x00
+#define SSD130x_SCROLL_25_FRAMES   0x06
+#define SSD130x_SCROLL_64_FRAMES   0x01
+#define SSD130x_SCROLL_128_FRAMES  0x02
+#define SSD130x_SCROLL_256_FRAMES  0x03
+
+/* GDDRAM Adressing */
+#define SSD130x_CMD_ADDR_TYPE          0x20
+/* Data bytes for adressing mode election */
+#define SSD130x_ADDR_TYPE_HORIZONTAL 0x00
+#define SSD130x_ADDR_TYPE_VERTICAL   0x01
+#define SSD130x_ADDR_TYPE_PAGE       0x02
+
+/* GDDRAM Page adressing mode */
+#define SSD130x_CMD_COL_LOW_NIBLE(x)   (0x00 + ((x) & 0x0F))
+#define SSD130x_CMD_COL_HIGH_NIBLE(x)  (0x10 + ((x) & 0x0F))
+#define SSD130x_CMD_PAGE_START_ADDR(x) (0xB0 + ((x) & 0x07))
+
+/* GDDRAM horizontal or vertical addressing mode */
+#define SSD130x_CMD_COL_ADDR           0x21
+#define SSD130x_CMD_PAGE_ADDR          0x22
+/* Data bytes for horizontal or vertical adressing mode */
+#define SSD130x_ADDR_COL(x)       ((x) & 0x7F)
+#define SSD130x_ADDR_PAGE(x)      ((x) & 0x07)
+
+/* Hardware configuration */
+#define SSD130x_CMD_START_LINE(x)      (0x40 + ((x) & 0x3F))
+#define SSD130x_CMD_SEG0_MAP_RIGHT     0xA1
+#define SSD130x_CMD_SEG0_MAP_LEFT      0xA0
+
+/* Hardware configuration : Mux ratio */
+#define SSD130x_CMD_SET_MUX            0xA8
+/* Set mux ratio Data to N+1 (Values for N from 0 to 14 are invalid) */
+#define SSD130x_MUX_DATA(x)       ((x) & 0x3F)  /* Reset is N=63 (64 mux) */
+
+/* Hardware configuration : COM Scan */
+#define SSD130x_CMD_COM_SCAN_NORMAL    0xC0  /* Reset mode : top to bottom */
+#define SSD130x_CMD_COM_SCAN_REVERSE   0xC8  /* Bottom to top */
+#define SSD130x_CMD_DISPLAY_OFFSET     0xD3
+/* Data for display offset (COM shift) */
+#define SSD130x_OFFSET_DATA(x)    ((x) & 0x3F)
+#define SSD130x_CMD_COM_PIN_CONF       0xDA
+/* Data for COM pins hardware configuration */
+#define SSD130x_COM_SEQUENTIAL    (0x00 << 4)
+#define SSD130x_COM_ALTERNATIVE   (0x01 << 4) /* Reset mode */
+#define SSD130x_COM_NO_REMAP      (0x00 << 5) /* Reset mode */
+#define SSD130x_COM_REMAP         (0x01 << 5)
+
+/* Timing and driving scheme : Clock */
+#define SSD130x_CMD_DISP_CLK_DIV       0xD5
+#define SSD130x_CLK_DIV(x)        ((x) & 0x0F)  /* Set to N+1 (Default is 0+1) */
+#define SSD130x_CLK_FREQ(x)       (((x) & 0x0F) << 4)  /* Reset is 0x80 */
+
+/* Timing and driving scheme : Precharge */
+#define SSD130x_CMD_SET_PRECHARGE      0xD9
+#define SSD130x_PRECHARGE_PHASE1(x)    ((x) & 0x0F)  /* Default to 2, 0 is invalid */
+#define SSD130x_PRECHARGE_PHASE2(x)    (((x) & 0x0F) << 4) /* Default to 2, 0 is invalid */
+
+/* Timing and driving scheme : Voltage */
+#define SSD130x_CMD_VCOM_LEVEL         0xDB
+#define SSD130x_VCOM_065    0x00
+#define SSD130x_VCOM_077    0x20
+#define SSD130x_VCOM_083    0x30
+
+/* NO-OP */
+#define SSD130x_CMD_NOP                0xE3
+
+/* Status register read */
+#define SSD130x_STATUS_ON     (0x01 << 6)
+
+
+/* SSD1306 starts with control reg set to 0x7F and in normal display mode,
+ *  which is equivalent to 0xA4 command.
+ * (Refer to chapter 8.5 of SSD1306 manual)
+ *
+ * The power on sequence is described in chapter 8.9 of SSD1306 manual.
+ *  After VDD power stable, Reset signal should be pulled low for at least 3us
+ *  and then VCC turned on.
+ *  Once VCC stable, send 0xAF command to turn display ON and wait for 100ms.
+ *
+ * The power off sequence starts by sending 0xAE command, then turn off VCC,
+ *  wait for 100ms, and turn off VDD.
+ *
+ * The Graphic Display Data RAM (GDDRAM) is 128 x 64 bits divided in 8 pages
+ *  of 128 bytes each. One page represents 8 lines of 128 dots.
+ * When writing one byte to one page, the byte fills one collumn (8 vertical
+ *  pixels), and the next byte fills the next collumn, and so on.
+ * (Refer to chapter 8.7 of SSD1306 manual)
+ *
+ * No data read is available in serial (SPI and I2C ?) mode.
+ * The serial interface mode is always in write mode.
+ *
+ * The GDDRAM column address pointer will be increased automatically by one
+ *  after each data write.
+ */
+
+
+ /*
+ * Note : This driver does not implemment and support page adressing. All page
+ *  addressing commands are not supported.
+ * This affects the following commands :
+ *   - All commands 0x00 to 0x1F are not supported (set column start for page
+ *       addressing mode).
+ *   - ssd130x_set_mem_addressing_mode() (0x20) supports only
+ *       SSD130x_ADDR_TYPE_HORIZONTAL and SSD130x_ADDR_TYPE_VERTICAL
+ *   - Set page start address (0xB0 to 0xB7)
+ *
+ * Note some other commands are not supported:
+ *   - Set display start line (0x40 to 0x7F)
+ *   - Set segment remap (0xA0 and 0xA1)
+ *   - Set multiplex ratio (0xA8)
+ *   - Set display clock divide ration and oscilator frequency (0xD5)
+ *   - Set pre-charge period (0xD9)
+ *   - Set COM pins hardware configuration (0xDA)
+ *   - Set Vcomh deselect level (0xDB)
+ *   - No OP (0xE3)
+ */
+
+/***************************************************************************** */
+/* Commands */
+int ssd130x_send_command(struct oled_display* conf, uint8_t cmd, uint8_t* data, uint8_t len);
+
+
+/* Set memory adressing mode (0x20)
+ * There are 3 different memory addressing mode in SSD130x: page addressing
+ *  mode, horizontal addressing mode and vertical addressing mode
+ *  - SSD130x_ADDR_TYPE_HORIZONTAL
+ *  - SSD130x_ADDR_TYPE_VERTICAL
+ *  - SSD130x_ADDR_TYPE_PAGE
+ * This command sets the way of memory addressing into one of the above three
+ *   modes.
+ * (Refer to chapter 10.1.3 of SSD1306 manual)
+ *
+ * Note : This driver does not implemment and support page adressing. All page
+ *  addressing commands are not supported.
+ */
+int ssd130x_set_mem_addressing_mode(struct oled_display* conf, uint8_t type);
+
+/* Set column address (0x21)
+ * This triple byte command specifies column start address and end address of
+ *  the display data RAM.
+ * This command also sets the column address pointer to column start address.
+ * This pointer is used to define the current read/write column address in
+ *  graphic display data RAM.
+ * If horizontal address increment mode is enabled by command 20h, after
+ *  finishing read/write one column data, it is incremented automatically to
+ *  the next column address.
+ * Whenever the column address pointer finishes accessing the end column
+ *  address, it is reset back to start column address and the row address is
+ *  incremented to the next row.
+ */
+int ssd130x_set_column_address(struct oled_display* conf, uint8_t col_start, uint8_t col_end);
+
+/* Set page address (0x22)
+ * This triple byte command specifies page start address and end address of the
+ *  display data RAM.
+ * This command also sets the page address pointer to page start address.
+ * This pointer is used to define the current read/write page address in
+ *  graphic display data RAM.
+ * If vertical address increment mode is enabled by command 20h, after
+ *  finishing read/write one page data, it is incremented automatically to the
+ *  next page address.
+ * Whenever the page address pointer finishes accessing the end page address,
+ *  it is reset back to start page address.
+ */
+int ssd130x_set_page_address(struct oled_display* conf, uint8_t page_start, uint8_t page_end);
+
+/* Set contrast (0x81)
+ * The contrast value is between 0 and 256
+ */
+int ssd130x_set_contrast(struct oled_display* conf);
+
+/* Set display ON from GDDRAM (0xA4) or regardless of GDDRAM (0xA5)
+ * use_ram is either SSD130x_DISP_RAM or SSD130x_DISP_BLANK
+ */
+int ssd130x_set_display_on(struct oled_display* conf, uint8_t use_ram);
+
+/* Set display to normal or reverse video
+ * status is either SSD130x_DISP_NORMAL or SSD130x_DISP_REVERSE
+ */
+int ssd130x_display_video_reverse(struct oled_display* conf);
+
+/* Set display ON/OFF
+ * status is either SSD130x_DISP_ON or SSD130x_DISP_OFF
+ */
+int ssd130x_display_power(struct oled_display* conf, uint8_t status);
+
+/* Set scan direction (0xC0 / 0xC8)
+ * Scan direction is either SSD130x_SCAN_TOP_BOTTOM or SSD130x_SCAN_BOTTOM_TOP
+ */
+int ssd130x_set_scan_direction(struct oled_display* conf);
+
+/* Set read direction (0xA0 / 0xA1)
+ * Scan direction is either SSD130x_CMD_SEG0_MAP_RIGHT or SSD130x_CMD_SEG0_MAP_LEFT
+ */
+int ssd130x_set_read_direction(struct oled_display* conf);
+
+/* Set display offset
+ * Move the display up or down by a given number of lines
+ * FIXME : Check influence of COM output scan direction.
+ */
+int ssd130x_set_display_offset(struct oled_display* conf, uint8_t dir, uint8_t nb_lines);
+
+
+
+/***************************************************************************** */
+/* Init */
+int ssd130x_display_on(struct oled_display* conf);
+int ssd130x_display_off(struct oled_display* conf);
+
+int ssd130x_display_init(struct oled_display* conf);
+
+
+
+/***************************************************************************** */
+/* Data */
+
+/* Our internal buffer for the whole display.
+ * Graphical data starts at byte 4. The first four bytes are here for temporary
+ *  storage of display data during I2C frame transfer.
+ */
+int ssd130x_send_data(struct oled_display* conf, uint8_t* start, uint16_t len);
+
+/* Set whole display to given value */
+int ssd130x_display_set(struct oled_display* conf, uint8_t val);
+
+
+/* Update what is really displayed */
+int ssd130x_display_full_screen(struct oled_display* conf);
+int ssd130x_update_tile(struct oled_display* conf, uint8_t x0, uint8_t y0);
+int ssd130x_update_region(struct oled_display* conf,
+                                                       uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1);
+int ssd130x_update_modified(struct oled_display* conf);
+
+/* Change our internal buffer, without actually displaying the changes */
+int ssd130x_set_pixel(struct oled_display* conf, uint8_t x0, uint8_t y0, uint8_t state);
+int ssd130x_set_tile(struct oled_display* conf, uint8_t x0, uint8_t y0, uint8_t* tile);
+
+
+#endif /* EXTDRV_SSD130X_OLED_DRIVER_H */