--- /dev/null
+/****************************************************************************
+ * 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;
+}
+
+
+
--- /dev/null
+/****************************************************************************
+ * 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 */