oled: add support for SPI ssd130x displays
authorCyprien Laplace <cyprien@cypou.net>
Sat, 24 Jun 2017 20:47:22 +0000 (16:47 -0400)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
For I2C displays, the bus_type needs to be added:

+   .bus_type = SSD130x_BUS_I2C,
    .address = DISPLAY_ADDR,
    .bus_num = I2C0,

To use a SPI display:

1/ add the SSP include:

    #include "drivers/ssp.h"

2/ add the SPI pins to the init table:

    /* SPI : Display */
    { LPC_SSP0_SCLK_PIO_0_14, LPC_IO_DIGITAL },
    { LPC_SSP0_MISO_PIO_0_16, LPC_IO_DIGITAL },
    { LPC_SSP0_MOSI_PIO_0_17, LPC_IO_DIGITAL },

3/ update the oled_display conf:

    .bus_type = SSD130x_BUS_SPI,
    .bus_num = SSP_BUS_0,
    .gpio_dc = LPC_GPIO_0_0,
    .gpio_cs = LPC_GPIO_1_0,
    .gpio_rst = LPC_GPIO_0_31,

4/ initialize the SPI bus:

    ssp_master_on(SSP_BUS_0, LPC_SSP_FRAME_SPI, 8, 8*1000*1000);

drivers/ssp.c
extdrv/ssd130x_oled_driver.c
include/extdrv/ssd130x_oled_driver.h

index 353d75e..4e83d32 100644 (file)
@@ -189,8 +189,9 @@ static int spi_transfer_bytes(struct lpc_ssp* ssp, uint8_t* data_out, uint8_t* d
 
                /* Read some of the replies, but stop if there's still data to send and the fifo
                 *  is running short */
-               while ((ssp->status & LPC_SSP_ST_RX_NOT_EMPTY) &&
-                               !((count < size) && (ssp->raw_int_status & LPC_SSP_INTR_TX_HALF_EMPTY))) {
+               while ((ssp->status & LPC_SSP_ST_RX_FULL) ||
+                ((ssp->status & LPC_SSP_ST_RX_NOT_EMPTY) &&
+                                !((count < size) && (ssp->raw_int_status & LPC_SSP_INTR_TX_HALF_EMPTY)))) {
                        /* Read the data (mandatory) */
                        data_read = (uint8_t)ssp->data;
                        if (data_in != NULL) {
index 772d251..1fca0c7 100644 (file)
@@ -31,6 +31,7 @@
 #include "lib/string.h"
 #include "drivers/gpio.h"
 #include "drivers/i2c.h"
+#include "drivers/ssp.h"
 
 #include "extdrv/ssd130x_oled_driver.h"
 
 #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;
-       }
+       int i;
 
-       if (len != 0) {
-               int i = 0;
+       if (conf->bus_type == SSD130x_BUS_SPI) {
+               gpio_clear(conf->gpio_dc);
+               gpio_clear(conf->gpio_cs);
+               spi_transfer_single_frame(conf->bus_num, cmd);
                for (i = 0; i < len; i++) {
-                       cmd_buf[ 3 + (2 * i) ] = SSD130x_NEXT_BYTE_CMD;
-                       cmd_buf[ 4 + (2 * i) ] = data[i];
+                       spi_transfer_single_frame(conf->bus_num, data[i]);
                }
-       }
-       do {
-               ret = i2c_write(conf->bus_num, cmd_buf, (3 + (len * 2)), NULL);
-       } while (ret == -EAGAIN);
-       if (ret != (3 + (len * 2))) {
-               conf->probe_ok = 0;
-               return ret;
+               gpio_set(conf->gpio_cs);
+       } else if (conf->bus_type == SSD130x_BUS_I2C) {
+               char cmd_buf[CMD_BUF_SIZE] = { conf->address, SSD130x_NEXT_BYTE_CMD, cmd, };
+               int ret;
+
+               if (2 * len > (CMD_BUF_SIZE - 3)) {
+                       return -EINVAL;
+               }
+
+               if (len != 0) {
+                       for (i = 0; i < len; i++) {
+                               cmd_buf[ 3 + (2 * i) ] = SSD130x_NEXT_BYTE_CMD;
+                               cmd_buf[ 4 + (2 * i) ] = data[i];
+                       }
+               }
+
+               do {
+                       ret = i2c_write(conf->bus_num, cmd_buf, (3 + (len * 2)), NULL);
+               } while (ret == -EAGAIN);
+               if (ret != (3 + (len * 2))) {
+                       conf->probe_ok = 0;
+                       return ret;
+               }
+       } else {
+               return -EPROTO;
        }
        return 0;
 }
@@ -267,13 +282,24 @@ int ssd130x_display_on(struct oled_display* conf)
 
        conf->fullscreen = 0;
 
+       if (conf->bus_type == SSD130x_BUS_SPI) {
+               config_gpio(&conf->gpio_cs, 0, GPIO_DIR_OUT, 1);
+               config_gpio(&conf->gpio_dc, 0, GPIO_DIR_OUT, 1);
+
+               config_gpio(&conf->gpio_rst, 0, GPIO_DIR_OUT, 0);
+               usleep(5); /* at least 3us */
+               gpio_set(conf->gpio_rst);
+               msleep(1);
+       }
+
        /* Display OFF */
        ret = ssd130x_display_power(conf, SSD130x_DISP_OFF);  /* 0xAE */
        if (ret != 0) {
                return ret;
        }
+
        if (conf->charge_pump == SSD130x_INTERNAL_PUMP) {
-               val = SSD130x_CMD_CHARGE_INTERN;
+               val = SSD130x_CMD_CHARGE_INTERN; /* 0x14 */
                ssd130x_send_command(conf, SSD130x_CMD_CHARGE_PUMP, &val, 1); /* 0x8D */
        }
 
@@ -321,40 +347,52 @@ int ssd130x_display_on(struct oled_display* conf)
 
 int ssd130x_send_data(struct oled_display* conf, uint8_t* start, uint16_t len)
 {
-       int (*write)(uint8_t, const void *, size_t, const void*);
-       int ret = 0;
+       int ret;
+
+       if (conf->bus_type == SSD130x_BUS_SPI) {
+               gpio_set(conf->gpio_dc);
+               gpio_clear(conf->gpio_cs);
+               ret = spi_transfer_multiple_frames(conf->bus_num, start, NULL, len, 8);
+               gpio_set(conf->gpio_cs);
+               if (ret != len) {
+                       return ret;
+               }
+       } else if (conf->bus_type == SSD130x_BUS_I2C) {
+               int (*write)(uint8_t, const void *, size_t, const void*);
 
-       /* Check that start and satrt + len are within buffer */
+               /* Check that start and satrt + len are within buffer */
 
-       /* Copy previous two bytes to storage area (gddram[0] and gddram[1]) */
-       conf->gddram[0] = *(start - 2);
-       conf->gddram[1] = *(start - 1);
+               /* Copy previous two bytes to storage area (gddram[0] and gddram[1]) */
+               conf->gddram[0] = *(start - 2);
+               conf->gddram[1] = *(start - 1);
 
-       /* Setup I2C transfer */
-       *(start - 2) = conf->address;
-       *(start - 1) = SSD130x_DATA_ONLY;
+               /* Setup I2C transfer */
+               *(start - 2) = conf->address;
+               *(start - 1) = SSD130x_DATA_ONLY;
 
-       /* Send data on I2C bus */
-       write = conf->async ? i2c_write_async : i2c_write;
-       do {
-               ret = write(conf->bus_num, (start - 2), (2 + len), NULL);
-       } while (ret == -EAGAIN);
+               /* Send data on I2C bus */
+               write = conf->async ? i2c_write_async : i2c_write;
+               do {
+                       ret = write(conf->bus_num, (start - 2), (2 + len), NULL);
+               } while (ret == -EAGAIN);
 
-       /* Restore gddram data */
-       *(start - 2) = conf->gddram[0];
-       *(start - 1) = conf->gddram[1];
+               /* Restore gddram data */
+               *(start - 2) = conf->gddram[0];
+               *(start - 1) = conf->gddram[1];
 
-       if (ret != (2 + len)) {
-               return ret;
+               if (ret != (2 + len)) {
+                       return ret;
+               }
+       } else {
+               return -EPROTO;
        }
-       return len;
+       return 0;
 }
 
 /* Update what is really displayed */
 int ssd130x_display_full_screen(struct oled_display* conf)
 {
-       int (*write)(uint8_t, const void *, size_t, const void*);
-       int ret = 0;
+       int ret;
 
        if (!conf->fullscreen) {
                ret = ssd130x_set_column_address(conf, 0, 127);
@@ -368,16 +406,22 @@ int ssd130x_display_full_screen(struct oled_display* conf)
                conf->fullscreen = 1;
        }
 
-       /* Setup I2C transfer */
-       *(conf->gddram + 2) = conf->address;
-       *(conf->gddram + 3) = SSD130x_DATA_ONLY;
-
-       /* Send data on I2C bus */
-       write = conf->async ? i2c_write_async : i2c_write;
-       do {
-               ret = write(conf->bus_num, conf->gddram + 2, 2 + GDDRAM_SIZE, NULL);
-       } while (ret == -EAGAIN);
-
+       if (conf->bus_type == SSD130x_BUS_SPI) {
+               ret = ssd130x_send_data(conf, conf->gddram + 4, GDDRAM_SIZE);
+       } else if (conf->bus_type == SSD130x_BUS_I2C) {
+               /* Setup I2C transfer */
+               *(conf->gddram + 2) = conf->address;
+               *(conf->gddram + 3) = SSD130x_DATA_ONLY;
+
+               /* Send data on I2C bus */
+               int (*write)(uint8_t, const void *, size_t, const void*);
+               write = conf->async ? i2c_write_async : i2c_write;
+               do {
+                       ret = write(conf->bus_num, conf->gddram + 2, 2 + GDDRAM_SIZE, NULL);
+               } while (ret == -EAGAIN);
+       } else {
+               return -EPROTO;
+       }
        return ret;
 }
 
index 83a972b..d4b0cec 100644 (file)
 #define EXTDRV_SSD130X_OLED_DRIVER_H
 
 #include "lib/stdint.h"
+#include "drivers/gpio.h"
 
 
 /***************************************************************************** */
 /* Oled Display */
 struct oled_display {
+       uint8_t  bus_type; /* I2C or SPI */
        uint8_t  address; /* 8 bits address */
-       uint8_t  bus_num; /* I2C bus number */
+       uint8_t  bus_num; /* I2C/SPI bus number */
        uint8_t  probe_ok;
        uint8_t  charge_pump;
        uint8_t  video_mode;
@@ -42,6 +44,10 @@ struct oled_display {
        uint8_t  display_offset;
        uint8_t* gddram;
        uint8_t  async;
+       /* spi */
+       struct pio gpio_dc;
+       struct pio gpio_cs;
+       struct pio gpio_rst;
        /* internal */
        uint8_t  fullscreen;
 };
@@ -66,6 +72,9 @@ enum ssd130x_defs {
        /* For display offset */
        SSD130x_MOVE_TOP,
        SSD130x_MOVE_BOTTOM,
+       /* I2C or SPI */
+       SSD130x_BUS_I2C,
+       SSD130x_BUS_SPI,
 };