Add first version ST7735 TFT driver support
authorNathael Pajani <nathael.pajani@ed3l.fr>
Thu, 24 Sep 2020 04:16:11 +0000 (06:16 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
extdrv/st7735_tft_driver.c [new file with mode: 0644]
include/extdrv/st7735_tft_driver.h [new file with mode: 0644]

diff --git a/extdrv/st7735_tft_driver.c b/extdrv/st7735_tft_driver.c
new file mode 100644 (file)
index 0000000..925c005
--- /dev/null
@@ -0,0 +1,294 @@
+/****************************************************************************
+ *   extdrv/st7735_tft_driver.c
+ *
+ * SPI Driver for 128x160 TFT display drivers
+ *
+ * Copyright 2020 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 3 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/errno.h"
+#include "lib/string.h"
+#include "lib/stdint.h"
+#include "drivers/gpio.h"
+#include "drivers/ssp.h"
+#include "extdrv/st7735_tft_driver.h"
+
+int st7735_spi_send_command(struct st7735_display* conf, uint8_t cmd, uint8_t* data, uint32_t len)
+{
+       int ret = 0;
+       /* Send command word */
+       gpio_clear(conf->gpio_cs); /* Chip select */
+       gpio_clear(conf->gpio_dc); /* Command mode */
+       spi_transfer_single_frame(conf->bus_num, cmd); /* Send command word */
+
+       /* Switch to data mode if there's data to send */
+       if (len != 0) {
+               gpio_set(conf->gpio_dc); /* Enter data mode */
+               /* Send data */
+               ret = spi_transfer_multiple_frames(conf->bus_num, data, NULL, len, LPC_SSP_WORD_LEN(8));
+               gpio_clear(conf->gpio_dc); /* Leave data mode */
+       }
+       gpio_set(conf->gpio_cs); /* Release chip select */
+
+       return ret;
+}
+
+int st7735_spi_read(struct st7735_display* conf, uint8_t cmd, uint8_t* data, uint32_t len)
+{
+       /* Send command word */
+       gpio_clear(conf->gpio_cs); /* Chip select */
+       gpio_clear(conf->gpio_dc); /* Command mode */
+       spi_transfer_single_frame(conf->bus_num, cmd); /* Send command word */
+       
+       /* Switch to data mode if there's data to read */
+       if (len != 0) {
+               gpio_set(conf->gpio_dc); /* Enter data mode */
+               /* Read data */
+               spi_transfer_multiple_frames(conf->bus_num, NULL, data, len, LPC_SSP_WORD_LEN(8));
+               gpio_clear(conf->gpio_dc); /* Leave data mode */
+       }
+       gpio_set(conf->gpio_cs); /* Release chip select */
+
+       return 0;
+}
+
+
+/* Send data to the memory buffer
+ * Note that the data is sent to the begginning of the current region, not
+ * appended after the last data sent.
+ */
+int st7735_spi_send_data(struct st7735_display* conf, uint8_t* data, uint32_t len)
+{
+       /* Switch to data transfer mode and send data */
+       st7735_spi_send_command(conf, ST77xx_MEM_WRITE, data, len);
+       return 0;
+}
+
+int st7735_spi_append_data(struct st7735_display* conf, uint8_t* data, uint32_t len)
+{
+       /* FIXME */
+       return 0;
+}
+
+/* Set origin and size of the region to which the following data will be sent */
+int st7735_spi_set_addr(struct st7735_display* conf, uint8_t x, uint8_t y, uint8_t w, uint8_t h)
+{
+       uint8_t data[4];
+       if (((x + w) > conf->x_width) || ((y + h) > conf->y_height)) {
+               return -1;
+       }
+       data[0] = 0; data[2] = 0;
+       /* Send origin and end of filled rectangle */
+       data[1] = x; data[3] = (x + w - 1);
+       st7735_spi_send_command(conf, ST77xx_COL_ADDR_SET, data, 4);
+       data[1] = y; data[3] = (y + h - 1);
+       st7735_spi_send_command(conf, ST77xx_ROW_ADDR_SET, data, 4);
+
+       return 0;
+}
+
+#define PIXEL_LINE_BUF_SIZE  (128 * 2)
+int st7735_spi_erase_display(struct st7735_display* conf)
+{
+       int i = 0;
+       uint8_t data[PIXEL_LINE_BUF_SIZE];
+       /* Set region to whole screen */
+       st7735_spi_set_addr(conf, 0, 0, ST77xx_NB_COL, ST77xx_NB_LINES);
+
+       /* Prepare buffer */
+       memset(data, 0, PIXEL_LINE_BUF_SIZE);
+
+       /* Switch to data transfer mode and send data */
+       /* Send command word */
+       gpio_clear(conf->gpio_cs); /* Chip select */
+       gpio_clear(conf->gpio_dc); /* Command mode */
+       spi_transfer_single_frame(conf->bus_num, ST77xx_MEM_WRITE); /* Send command word */
+       gpio_set(conf->gpio_dc); /* Enter data mode */
+       /* Send data */
+       for (i = 0; i < ST77xx_NB_LINES; i++) {
+               spi_transfer_multiple_frames(conf->bus_num, data, NULL, PIXEL_LINE_BUF_SIZE, LPC_SSP_WORD_LEN(8));
+       }
+       gpio_clear(conf->gpio_dc); /* Leave data mode */
+       gpio_set(conf->gpio_cs); /* Release chip select */
+       return 0;
+}
+
+
+/* Set the color of a single pixel directly on screen.
+ * Use this only for very small display updates.
+ * Try using video buffer instead whenever possible */ 
+int st7735_spi_set_pixel(struct st7735_display* conf, uint8_t x, uint8_t y, uint16_t color)
+{
+       /* Make sure pixel is within display boudaries */
+       if ((x > conf->x_width) || (y > conf->y_height)) {
+               return -1;
+       }
+       /* Send set_addr command */
+       st7735_spi_set_addr(conf, x, y, 1, 1);
+       /* Send color info */
+       st7735_spi_send_data(conf, (uint8_t*)&color, 2);
+
+       return 0;
+}
+
+int st7735_spi_display_on(struct st7735_display* conf)
+{
+       return st7735_spi_send_command(conf, ST77xx_DISPLAY_ON, NULL, 0);
+}
+
+int st7735_spi_display_off(struct st7735_display* conf)
+{
+       return st7735_spi_send_command(conf, ST77xx_DISPLAY_OFF, NULL, 0);
+}
+
+int st7735_spi_display_enter_sleep(struct st7735_display* conf)
+{
+       return st7735_spi_send_command(conf, ST77xx_SLP_IN, NULL, 0);
+}
+
+int st7735_spi_display_wakeup(struct st7735_display* conf)
+{
+       return st7735_spi_send_command(conf, ST77xx_SLP_OUT, NULL, 0);
+}
+
+/* Read the Display IDs
+ * data must be 4 bytes long at least
+ *   data[0] is unused
+ *   data[1] is LCD module's manufacturer ID (ST77xx_READ_ID1)
+ *   data[2] is LCD module/driver version ID (ST77xx_READ_ID2)
+ *   data[3] is LCD module/driver ID (ST77xx_READ_ID3)
+ */
+int st7735_spi_read_display_id(struct st7735_display* conf, uint8_t* data)
+{
+       return st7735_spi_read(conf, ST77xx_RD_DISP_ID, data, 4);
+}
+
+/* Read the Display Status
+ * data must be 5 bytes long at least
+ *   data[0] is unused
+ *   data[1] is 
+ *   data[2] is 
+ *   data[3] is 
+ *   data[4] is 
+ */
+int st7735_spi_read_display_status(struct st7735_display* conf, uint8_t* data)
+{
+       return st7735_spi_read(conf, ST77xx_RD_DISP_STATUS, data, 5);
+}
+
+
+/* Set display color mode */
+int st7735_spi_set_color_mode(struct st7735_display* conf, uint8_t mode)
+{
+       return st7735_spi_send_command(conf, ST77xx_COLOR_MODE, &mode, 1);
+}
+
+/* Set refresh mode (Memory data access control
+ * mode is a bit mask set from :
+ *   3 bits to control MCU to memory write/read direction
+ *   ST77xx_MAD_MEMY_ROW : Change Row address order when sending data to mem
+ *   ST77xx_MAD_MEMX_COL : Change Column address order when sending data to mem
+ *   ST77xx_MAD_MEMV_EXCHANGE : Row/Collumn exchange when sending data to mem
+ *   3 bits to control memory to display refresh direction
+ *   ST77xx_MAD_VERT_REFERSH_TOP_BOTTOM or ST77xx_MAD_VERT_REFERSH_BOTTOM_TOP
+ *   ST77xx_MAD_RGB_ORDER or ST77xx_MAD_BGR_ORDER
+ *   ST77xx_MAD_HORIZ_REFRESH_LEFT_RIGHT or ST77xx_MAD_HORIZ_REFRESH_RIGHT_LEFT
+ * Default is "left to right", "top to bottom" and RGB order and directions.
+ */
+int st7735_spi_set_refresh_mode(struct st7735_display* conf, uint8_t mode)
+{
+    return st7735_spi_send_command(conf, ST77xx_MADCTL_SET, &mode, 1);
+}
+
+
+
+static struct st7735_ops st7735_spi_ops = {
+       .send_command = st7735_spi_send_command,
+       .read = st7735_spi_read,
+       .send_data = st7735_spi_send_data,
+       .set_addr = st7735_spi_set_addr,
+       .set_pixel = st7735_spi_set_pixel,
+       .display_on = st7735_spi_display_on,
+       .display_off = st7735_spi_display_off,
+       .display_enter_sleep = st7735_spi_display_enter_sleep,
+       .display_wakeup = st7735_spi_display_wakeup,
+       .read_display_id = st7735_spi_read_display_id,
+       .read_display_status = st7735_spi_read_display_status,
+       .set_color_mode = st7735_spi_set_color_mode,
+       .set_refresh_mode = st7735_spi_set_refresh_mode,
+       .erase_display = st7735_spi_erase_display,
+};
+
+/*
+ * ST7735 Initialisation in SPI mode.
+ * All SPI initialisation must be made before calling this function.
+ * SPI must be in MSB-first bit order
+ * Remember that this function takes quite a long time !
+ */
+int st7735_spi_init(struct st7735_display* conf)
+{
+       /* Check that SPI mode is selected */
+       if (conf->bus_type != ST77xx_BUS_SPI) {
+               return -1;
+       }
+
+       /* Configure chip-select, data/command switch and reset pin */
+       config_gpio(&conf->gpio_cs, 0, GPIO_DIR_OUT, 1); /* CS released */
+       config_gpio(&conf->gpio_dc, 0, GPIO_DIR_OUT, 1); /* Data mode */
+       /* Perform a hardware reset */
+       config_gpio(&conf->gpio_reset, 0, GPIO_DIR_OUT, 0);
+       usleep(5); /* at least 3us */
+       gpio_set(conf->gpio_reset);
+       msleep(125);
+       /* Then software reset */
+       st7735_spi_send_command(conf, ST77xx_SW_RESET, NULL, 0);
+       msleep(125);
+       /* Configure */
+       st7735_spi_set_color_mode(conf, ST77xx_16bits_COLOR);
+       st7735_spi_set_refresh_mode(conf, (ST77xx_MAD_RGB_ORDER));
+
+       /* Set LUT table */
+       {
+               uint8_t data[128];
+               int i = 0;
+               /* Red */
+               for (i = 0; i < 32; i++) {
+                       data[i] = (i << 1);
+               }
+               /* Green */
+               for (i = 0; i < 64; i++) {
+                       data[i + 32] = i;
+               }
+               /* Blue */
+               for (i = 0; i < 32; i++) {
+                       data[i + 96] = (i << 1);
+               }
+               st7735_spi_send_command(conf, ST77xx_LUT, data, 128);
+       }
+
+       /* Set operations */
+       conf->ops = &st7735_spi_ops;
+
+        return 0;
+}
+
+
+
diff --git a/include/extdrv/st7735_tft_driver.h b/include/extdrv/st7735_tft_driver.h
new file mode 100644 (file)
index 0000000..aa68c54
--- /dev/null
@@ -0,0 +1,176 @@
+/****************************************************************************
+ *   extdrv/st7735_tft_driver.h
+ *
+ * SPI Driver for 128x160 TFT display drivers
+ *
+ * Copyright 2020 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 3 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_ST7735_TFT_DRIVER_H
+#define EXTDRV_ST7735_TFT_DRIVER_H
+
+#include "lib/stdint.h"
+#include "drivers/gpio.h"
+
+
+/***************************************************************************** */
+/* ST7735 TFT Display */
+
+
+#define ST77xx_BUS_SPI 0
+#define ST77xx_BUS_I2C 1
+
+/* lines or rows (height / y)
+ * columns (width / x)
+ */
+
+#define ST77xx_NB_LINES   160
+#define ST77xx_NB_COL     128
+
+struct st7735_ops;
+
+struct st7735_display {
+       uint8_t  bus_type; /* I2C or SPI - Only SPI supported as of 2020-09-10 */
+       uint8_t  bus_num; /* I2C/SPI bus number */
+       uint8_t  address; /* 8 bits address (I2C) */
+       uint8_t  probe_ok;
+       uint8_t  video_mode;
+       uint8_t  x_width; /* 0 = left */
+       uint8_t  y_height; /* 0 = top */
+       uint8_t  contrast;
+       uint8_t  scan_dir;
+       uint8_t  read_dir;
+       uint8_t  display_offset_dir;
+       uint8_t  display_offset;
+       uint8_t* gddram;
+       /* internal */
+       uint8_t  fullscreen;
+       uint8_t  async;
+       /* spi */
+       struct pio gpio_dc; /* Data / command select (high is data mode) */
+       struct pio gpio_cs; /* Chip select */
+       struct pio gpio_reset; /* Reset */
+       struct st7735_ops* ops;
+};
+
+struct st7735_ops {
+       int (*send_command)(struct st7735_display* conf, uint8_t cmd, uint8_t* data, uint32_t len);
+       int (*read)(struct st7735_display* conf, uint8_t cmd, uint8_t* data, uint32_t len);
+       int (*send_data)(struct st7735_display* conf, uint8_t* data, uint32_t len);
+       int (*set_addr)(struct st7735_display* conf, uint8_t x, uint8_t y, uint8_t w, uint8_t h);
+       int (*set_pixel)(struct st7735_display* conf, uint8_t x, uint8_t y, uint16_t color);
+       int (*display_on)(struct st7735_display* conf);
+       int (*display_off)(struct st7735_display* conf);
+       int (*display_enter_sleep)(struct st7735_display* conf);
+       int (*display_wakeup)(struct st7735_display* conf);
+       int (*read_display_id)(struct st7735_display* conf, uint8_t* data);
+       int (*read_display_status)(struct st7735_display* conf, uint8_t* data);
+       int (*set_color_mode)(struct st7735_display* conf, uint8_t mode);
+       int (*set_refresh_mode)(struct st7735_display* conf, uint8_t mode);
+       int (*erase_display)(struct st7735_display* conf);
+};
+
+/* Note that GM should be '11' for 128x160 screens and '00' for 132x162 screens */
+enum st77xx_defs {
+       ST77xx_NOP = 0x00, /* No operation */
+       ST77xx_SW_RESET,   /* Software reset */
+       /* Read */
+       ST77xx_RD_DISP_ID = 0x04, /* Read Display ID */
+       ST77xx_RD_DISP_STATUS = 0x09, /* Read Display Status */
+       ST77xx_RD_DISP_PM, /* Read Dispay Power */
+       ST77xx_RD_DISP_MADCTL, /* Read Display Memory data access control */
+       ST77xx_RD_DISP_COLMOD, /* Read Display Pixel */
+       ST77xx_RD_DISP_IM, /* Read Display Image */
+       ST77xx_RD_DISP_SM, /* Read Display Signal */
+       /* Sleep control */
+       ST77xx_SLP_IN = 0x10, /* Enter sleep and booster OFF */
+       ST77xx_SLP_OUT, /* Exit sleep and booster ON */
+       ST77xx_PTL_ON,  /* Partial mode ON */
+       ST77xx_NOR_ON,  /* Normal mode ON (partial OFF) */
+       /* Inversion */
+       ST77xx_INV_OFF = 0x20, /* Display Inversion OFF */
+       ST77xx_INV_ON, /* Display Inversion ON */
+       /* Gamma */
+       ST77xx_GAMA_SET = 0x26, /* Gamma curve select */
+       /* Display on/off */
+       ST77xx_DISPLAY_OFF = 0x28, /* Display OFF */
+       ST77xx_DISPLAY_ON, /* Display ON */
+       /* Data */
+       ST77xx_COL_ADDR_SET = 0x2A, /* Column (x & w) adress set */
+       ST77xx_ROW_ADDR_SET, /* Row address (y & h) set */
+       ST77xx_MEM_WRITE, /* Memory Write */
+       ST77xx_LUT, /* LUT for 4k, 65k, 262k colors */
+       ST77xx_MEM_READ, /* Memory Read */
+       ST77xx_PARTIAL_ADDR_SET = 0x30, /* Partial start/end address set */
+       ST77xx_TEAR_OFF = 0x34, /* Tearing effect line OFF */
+       ST77xx_TEAR_ON, /* Tearing effect mode set and ON */
+       ST77xx_MADCTL_SET, /* Set memory data access control */
+#define ST77xx_MAD_MEMY_ROW (0x01 << 7)  /* MY */
+#define ST77xx_MAD_MEMX_COL (0x01 << 6)  /* MX */
+#define ST77xx_MAD_MEMV_EXCHANGE (0x01 << 5) /* MV */
+#define ST77xx_MAD_VERT_REFERSH_TOP_BOTTOM (0x00 << 4) /* ML */
+#define ST77xx_MAD_VERT_REFERSH_BOTTOM_TOP (0x01 << 4)
+#define ST77xx_MAD_RGB_ORDER (0x00 << 3) /* RGB or BGR */
+#define ST77xx_MAD_BGR_ORDER (0x01 << 3)
+#define ST77xx_MAD_HORIZ_REFRESH_LEFT_RIGHT (0x00 << 2) /* MH */
+#define ST77xx_MAD_HORIZ_REFRESH_RIGHT_LEFT (0x01 << 2)
+       ST77xx_IDLE_OFF = 0x38, /* Idle mode off */
+       ST77xx_IDLE_ON, /* Idle mode on */
+       ST77xx_COLOR_MODE, /* Interface pixel format (Color mode) */
+#define ST77xx_12bits_COLOR (0x03)
+#define ST77xx_16bits_COLOR (0x05)
+#define ST77xx_18bits_COLOR (0x06)
+       ST77xx_READ_ID1 = 0xDA, /* Read ID 1 (LCD module's manufacturer ID) */
+       ST77xx_READ_ID2, /* Read ID 2 (LCD module/driver version ID) */
+       ST77xx_READ_ID3, /* Read ID 3 (LCD module/driver ID) */
+       /* Frame rate is fosc/((RTN x 2 + 40) x (LINE + FP + BP))
+        *      fosc = 625kHz.   RTN : 4 bits, FP and BP : 6 bits.
+        *  RTN : Return. FP : Front Porch. BP : Back Porch
+        *  LINE is 160 or 162.
+        * For FORMAT_CTRL3, first 3 words are for dot inversion mode, next three for line inversion.
+        * Default is approx 60Hz. Max is approx 98Hz
+        * Default for GM = '11' (160 lines) : RTN = 1, FP = 0x2C, BP = 0x2B
+        */
+       ST77xx_FORMAT_CTRL1 = 0xB1, /* Frame rate control - normal mode (full colors) */
+       ST77xx_FORMAT_CTRL2, /* Frame rate control - Idle mode (8 colors) */
+       ST77xx_FORMAT_CTRL3, /* Frame rate control - Partial mode + full colors */
+       ST77xx_INVERSION_CTRL, /* Display inversion control */
+       ST77xx_POWER_CTRL1 = 0xC0, /* Power control setting - GVDD Voltage */
+       ST77xx_POWER_CTRL2, /* Power control setting - VGH/VGL voltage */
+       ST77xx_POWER_CTRL3, /* Power control setting - normal mode */
+       ST77xx_POWER_CTRL4, /* Power control setting - idle mode */
+       ST77xx_POWER_CTRL5, /* Power control setting - partial mode */
+       ST77xx_VCOM_CTRL, /* VCOM voltage control */
+       ST77xx_VCOM_OFFSET = 0xC7, /* VCOM offset control */
+       ST77xx_WRITE_ID2 = 0xD1, /* Set LCM version code */
+       ST77xx_GAMMA_POS = 0xE0, /* Gamma adjustment, positive polarity */
+       ST77xx_GAMMA_NEG, /* Gamma adjustment, negative polarity */
+};
+
+
+
+ /*
+ * Note that some commands are not supported:
+ */
+int st7735_spi_init(struct st7735_display* conf);
+
+/***************************************************************************** */
+/* Commands */
+
+
+#endif /* EXTDRV_ST7735_TFT_DRIVER_H */