diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp new file mode 100644 index 00000000..638025ab --- /dev/null +++ b/drivers/st7789/st7789.cpp @@ -0,0 +1,151 @@ +#include "st7789.hpp" + +#include "hardware/dma.h" + +namespace pimoroni { + + void ST7789::init() { + spi_init(spi, spi_baud); + + gpio_set_function(dc, GPIO_FUNC_SIO); + gpio_set_dir(dc, GPIO_OUT); + + gpio_set_function(cs, GPIO_FUNC_SIO); + gpio_set_dir(cs, GPIO_OUT); + + // if framesync is enabled then the fs pin is toggled + // high during vsync + if(vsync != -1) { + gpio_set_function(fs, GPIO_FUNC_SIO); + gpio_set_dir(fs, GPIO_IN); + gpio_set_pulls(fs, false, true) + } + + // if a backlight pin is provided then set it up for + // pwm control + if(bl != -1) { + pwm_config cfg = pwm_get_default_config(); + pwm_config_wrap(&cfg, 65535); + pwm_init(pwm_gpio_to_slice(bl), &cfg, true); + gpio_set_function(bl, GPIO_FUNC_PWM); + } + + gpio_set_function(sck, GPIO_FUNC_SPI); + gpio_set_function(mosi, GPIO_FUNC_SPI); + + if(miso != -1) { + gpio_set_function(miso, GPIO_FUNC_SPI); + } + + // initialise dma channel for transmitting pixel data to screen + dma_channel = dma_claim_unused_channel(true); + dma_channel_config config = dma_channel_get_default_config(dma_channel); + //channel_config_set_bswap(&config, true); // byte swap to reverse little endian + + dma_channel_configure( + dma_channel, + &config, + &spi_get_hw(spi)->dr, + buffer, + sizeof(uint16_t) * width * height, + false); + } + + void ST7789::init_240x240() { + init(); + + width = 240; + height = 240; + row_stride = width * sizeof(uint16_t); + + // only initialise the buffer if one hasn't been supplied + if(!buffer) { + buffer = malloc(sizeof(uint16_t) * width * height); + } + + // initialise our standard 240x240 LCD as 16-bit colour + // with an RGB subpixel element order. + command(register::SWRESET); + + sleep_ms(150); + + command(register::MADCTL, 1, "\x04"); // row/column addressing order - rgb pixel order + command(register::TEON, 1, "\x00"); // enable frame sync signal if used + command(register::COLMOD, 1, "\x05"); // 16 bits per pixel + + command(register::SLPOUT); // leave sleep mode + command(register::DISPON); // turn display on + + sleep_ms(100); + + // set writing "window" to the full display + command(register::CASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 columns + command(register::RASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 rows + + // put into write mode + command(register::RAMWR); + } + + void ST7789::init_240x135() { + init(); + + width = 240; + height = 135; + row_stride = width * sizeof(uint16_t); + + // only initialise the buffer if one hasn't been supplied + if(!buffer) { + buffer = malloc(sizeof(uint16_t) * width * height); + } + + // initialise our standard 240x135 LCD as 16-bit colour + // with an RGB subpixel element order. + command(register::SWRESET); + + sleep_ms(150); + + command(register::MADCTL, 1, "\x70"); + command(register::COLMOD, 1, "\x05"); + + command(register::SLPOUT); // leave sleep mode + command(register::DISPON); // turn display on + + sleep_ms(100); + + command(register::RASET, 4, "\x00\x35\x00\xbb"); // 53 .. 187 (135 rows) + command(register::CASET, 4, "\x00\x28\x01\x17"); // 40 .. 279 (240 columns) + } + + void ST7789::command(uint8_t command, size_t len = 0, const char *data = NULL) { + gpio_put(pin::CS, 0); + + gpio_put(pin::DC, 0); // command mode + spi_write_blocking(spi0, &command, 1); + + if(data) { + gpio_put(pin::DC, 1); // data mode + spi_write_blocking(spi0, (const uint8_t*)data, len); + } + + gpio_put(pin::CS, 1); + } + + void ST7789::update() { + dma_channel_wait_for_finish_blocking(dma_channel); + + command(register::RAMWR); + dma_channel_start(dma_channel); + } + + void ST7789::set_backlight(uint8_t brightness) { + // gamma correct the provided 0-255 brightness value onto a + // 0-65535 range for the pwm counter + float gamma = 2.8; + uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65536.0f + 0.5f); + pwm_set_gpio_level(bl, value); + } + + void ST7789::vsync_callback(gpio_irq_callback_t callback) { + gpio_set_irq_enabled_with_callback(vsync, GPIO_IRQ_EDGE_RISE, true, &callback); + } +} \ No newline at end of file diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp new file mode 100644 index 00000000..10faabb0 --- /dev/null +++ b/drivers/st7789/st7789.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include "hardware/spi.h" + +namespace pimoroni { + + class ST7789 { + spi_inst_t *spi = spi0_hw; + + // framebuffer for storing pixel data + uint16_t *buffer; + uint32_t dma_channel; + + // screen properties + uint16_t width; + uint16_t height; + uint16_t row_stride; + + // interface pins with our standard defaults where appropriate + int8_t sck = 18; + int8_t mosi = 19; + int8_t miso = -1; // we generally don't use this pin + int8_t cs = 17; + int8_t dc = 16; + int8_t bl = 20; + int8_t vsync = -1; // only available on some products + + uint32_t spi_baud = 64 * 1024 * 1024; + + public: + ST7789(spi_inst_t *spi, uint8_t cs, uint8_t dc, uint8_t sck, uint8_t mosi, uint8_t miso = -1) : + spi(spi), cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso) {} + + void init(); + void init_240x240(); + void init_240x235(); + void vsync_callback(gpio_irq_callback_t callback); + void update(); + + uint16_t pen(uint8_t r, uint8_t g, uint8_t b); + + enum register { + SWRESET = 0x01, + TEON = 0x35, + MADCTL = 0x36, + COLMOD = 0x3A, + GCTRL = 0xB7, + VCOMS = 0xBB, + LCMCTRL = 0xC0, + VDVVRHEN = 0xC2, + VRHS = 0xC3, + VDVS = 0xC4, + FRCTRL2 = 0xC6, + PWRCTRL1 = 0xD0, + FRMCTR1 = 0xB1, + FRMCTR2 = 0xB2, + GMCTRP1 = 0xE0, + GMCTRN1 = 0xE1, + INVOFF = 0x20, + SLPOUT = 0x11, + DISPON = 0x29, + GAMSET = 0x26, + DISPOFF = 0x28, + RAMWR = 0x2C, + INVON = 0x21, + CASET = 0x2A, + RASET = 0x2B + }; + }; + +}