From ba79b825c545276f157a80bb4b980b7b11cb35df Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Thu, 24 Sep 2020 06:16:11 +0200 Subject: [PATCH] Add first version ST7735 TFT driver support --- extdrv/st7735_tft_driver.c | 294 +++++++++++++++++++++++++++++ include/extdrv/st7735_tft_driver.h | 176 +++++++++++++++++ 2 files changed, 470 insertions(+) create mode 100644 extdrv/st7735_tft_driver.c create mode 100644 include/extdrv/st7735_tft_driver.h diff --git a/extdrv/st7735_tft_driver.c b/extdrv/st7735_tft_driver.c new file mode 100644 index 0000000..925c005 --- /dev/null +++ b/extdrv/st7735_tft_driver.c @@ -0,0 +1,294 @@ +/**************************************************************************** + * extdrv/st7735_tft_driver.c + * + * SPI Driver for 128x160 TFT display drivers + * + * Copyright 2020 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 index 0000000..aa68c54 --- /dev/null +++ b/include/extdrv/st7735_tft_driver.h @@ -0,0 +1,176 @@ +/**************************************************************************** + * extdrv/st7735_tft_driver.h + * + * SPI Driver for 128x160 TFT display drivers + * + * Copyright 2020 Nathael Pajani + * + * + * 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 . + * + *************************************************************************** */ + +#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 */ -- 2.43.0