Skip to content

Instantly share code, notes, and snippets.

@andreban
Last active December 16, 2021 14:37
Show Gist options
  • Save andreban/c311733165ffc8e7e36ced1378458b34 to your computer and use it in GitHub Desktop.
Save andreban/c311733165ffc8e7e36ced1378458b34 to your computer and use it in GitHub Desktop.
An ST7789 driver for the Raspberry Pi Pico that works for screens that have the CS channel and those that don't.
#include "pico/stdlib.h"
#include "st7789.h"
St7789::St7789(
uint8_t width,
uint8_t height,
uint16_t *frame_buffer,
spi_inst *spi,
int8_t pin_dc,
int8_t pin_reset,
int8_t pin_sck,
int8_t pin_mosi,
int8_t pin_cs,
int8_t pin_miso) {
this->spi = spi;
this->width = width;
this->height = height;
this->pin_reset = pin_reset;
this->pin_dc = pin_dc;
this->frame_buffer = frame_buffer;
this->pin_sck = pin_sck;
this->pin_mosi = pin_mosi;
this->pin_cs = pin_cs;
this->pin_miso = pin_miso;
}
void St7789::sleep_mode(bool value) {
if (value) {
send_command(ST7789_SLPIN);
} else {
send_command(ST7789_SLPOUT);
}
}
void St7789::set_invert_mode(bool invert) {
if (invert) {
send_command(ST7789_INVON);
} else {
send_command(ST7789_INVOFF);
}
}
void St7789::set_color_mode(uint8_t mode) {
send_command(ST7789_COLMOD, mode);
}
void St7789::hard_reset() {
gpio_put(pin_reset, 1);
sleep_ms(50);
gpio_put(pin_reset, 0);
sleep_ms(50);
gpio_put(pin_reset, 1);
sleep_ms(150);
}
void St7789::soft_reset() {
send_command(ST7789_SWRESET);
sleep_ms(150);
}
void St7789::update() {
send_command(ST7789_RAMWR, width * height * sizeof(uint16_t), (uint8_t *) frame_buffer);
}
void St7789::init_pins() {
gpio_set_function(pin_dc, GPIO_FUNC_SIO); // Same as "gpio_init(PIN_DC);
gpio_set_dir(pin_dc, GPIO_OUT);
if (pin_reset != -1) {
gpio_set_function(pin_reset, GPIO_FUNC_SIO); // gpio_init(PIN_RESET);
gpio_set_dir(pin_reset, GPIO_OUT);
}
if (pin_cs != -1) {
gpio_set_function(pin_cs, GPIO_FUNC_SIO);
gpio_set_dir(pin_cs, GPIO_OUT);
}
spi_init(spi, spi_baudrate);
gpio_set_function(pin_sck, GPIO_FUNC_SPI);
gpio_set_function(pin_mosi, GPIO_FUNC_SPI);
if(pin_miso != -1) {
gpio_set_function(pin_miso, GPIO_FUNC_SPI);
}
// Pimoroni's pico use CS, with the default config. The cheap screen doesn't have CS and needs to require setting
// polarity to 1.
if (pin_cs == -1) {
spi_set_format(spi, 8, SPI_CPOL_1, SPI_CPHA_0, SPI_MSB_FIRST);
}
}
void St7789::init() {
init_pins();
if (pin_reset != -1) {
hard_reset();
}
soft_reset();
sleep_mode(false);
sleep_ms(50);
if(width == 240 && height == 240) {
send_command(ST7789_MADCTL,0x04); // row/column addressing order - rgb pixel order
send_command(ST7789_TEON, 0x00); // enable frame sync signal if used
}
if(width == 240 && height == 135) {
send_command(ST7789_MADCTL,0x70);
}
set_color_mode(COLOR_MODE_16BIT);
set_invert_mode(true);
sleep_ms(10);
send_command(ST7789_NORON);
sleep_ms(10);
send_command(ST7789_DISPON);
// setup correct addressing window
if(width == 240 && height == 240) {
send_command(ST7789_CASET, 4, new uint8_t[4]{0x00, 0x00, 0x00, 0xef}); // 0 .. 239 columns
send_command(ST7789_RASET, 4, new uint8_t[4]{0x00, 0x00, 0x00, 0xef}); // 0 .. 239 rows
}
if(width == 240 && height == 135) {
send_command(ST7789_RASET, 4, new uint8_t[4]{0x00, 0x35, 0x00, 0xbb}); // 53 .. 187 (135 rows)
send_command(ST7789_CASET, 4, new uint8_t[4]{0x00, 0x28, 0x01, 0x17}); // 40 .. 279 (240 columns)
}
}
void St7789::send_command(uint8_t command, uint len, uint8_t *data) {
if (pin_cs != -1) {
gpio_put(pin_cs, 0);
}
gpio_put(pin_dc, 0);
spi_write_blocking(spi, &command, 1);
if (data) {
gpio_put(pin_dc, 1);
spi_write_blocking(spi, data, len);
}
if (pin_cs != -1) {
gpio_put(pin_cs, 1);
}
}
void St7789::send_command(uint8_t command, uint8_t data) {
send_command(command, 1, &data);
}
void St7789::send_command(uint8_t command) {
send_command(command, 0, nullptr);
}
#include "hardware/spi.h"
// ST7789 Commands
const uint8_t ST7789_SWRESET = 0x01;
const uint8_t ST7789_SLPIN = 0x10;
const uint8_t ST7789_SLPOUT = 0x11;
const uint8_t ST7789_NORON = 0x13;
const uint8_t ST7789_INVOFF = 0x20;
const uint8_t ST7789_INVON = 0x21;
const uint8_t ST7789_DISPON = 0x29;
const uint8_t ST7789_CASET = 0x2A;
const uint8_t ST7789_RASET = 0x2B;
const uint8_t ST7789_RAMWR = 0x2C;
const uint8_t ST7789_RAMRD = 0x2E;
const uint8_t ST7789_TEON = 0x35;
const uint8_t ST7789_MADCTL = 0x36;
const uint8_t ST7789_COLMOD = 0x3A;
// Color Modes
const uint8_t COLOR_MODE_65K = 0x50;
const uint8_t COLOR_MODE_262K = 0x60;
const uint8_t COLOR_MODE_12BIT = 0x03;
const uint8_t COLOR_MODE_16BIT = 0x05;
const uint8_t COLOR_MODE_18BIT = 0x06;
const uint8_t COLOR_MODE_16M = 0x07;
// Display Rendering controls?
const uint8_t ST7789_MADCTL_MY = 0x80;
const uint8_t ST7789_MADCTL_MX = 0x40;
const uint8_t ST7789_MADCTL_MV = 0x20;
const uint8_t ST7789_MADCTL_ML = 0x10;
const uint8_t ST7789_MADCTL_BGR = 0x08;
const uint8_t ST7789_MADCTL_MH = 0x04;
const uint8_t ST7789_MADCTL_RGB = 0x00;
/**
* A driver for ST7789 screens. Supports both boards that feature the CS pin and boards that don't.
*/
class St7789 {
private:
spi_inst *spi;
uint8_t width;
uint8_t height;
int8_t pin_reset;
int8_t pin_dc;
int8_t pin_sck;
int8_t pin_mosi;
int8_t pin_cs;
int8_t pin_miso;
uint16_t *frame_buffer;
uint32_t spi_baudrate = 64 * 1024 * 1024;
void send_command(uint8_t command, uint len, uint8_t *data);
void send_command(uint8_t command, uint8_t data);
void send_command(uint8_t command);
void init_pins();
void hard_reset();
void soft_reset();
void sleep_mode(bool value);
void set_invert_mode(bool invert);
void set_color_mode(uint8_t mode);
public:
/**
* Creates an instance of the St7789 driver.
*
* @param width screen width.
* @param height screen height.
* @param frame_buffer an uint16_t buffer with size width * height.
* @param spi the SPI device to use.
* @param pin_dc the Data Control (DC) pin.
* @param pin_reset the Reset (RES) pin. Use -1 for boards that don't have a reset pin.
* @param pin_sck the Clock (SCK / SCL) pin.
* @param pin_mosi the data (MOSI / SDA) pin.
* @param pin_cs the CS pin. Use -1 for boards that don't have a reset pin.
* @param pin_miso the MISO pin. Unused by the code. Can be set to -1.
*/
St7789(
uint8_t width,
uint8_t height,
uint16_t *frame_buffer,
spi_inst *spi,
int8_t pin_dc,
int8_t pin_reset,
int8_t pin_sck,
int8_t pin_mosi,
int8_t pin_cs = -1,
int8_t pin_miso = -1);
/**
* Initializes the pins and the screen.
*/
void init();
/**
* Updates the screen with the content in the buffer.
*/
void update();
/**
* Creates an instance of the St7789 driver using the default values for the Pimoroni Pico Display.
* @param buffer an uint16_t buffer with size of 240 * 135.
* @return an instance of the St7789 driver.
*/
static St7789 pimoroni_display(uint16_t *buffer) {
return St7789(
240,
135,
buffer,
spi0,
16,
-1,
18,
19,
17,
-1);
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment