215 lines
5.1 KiB
C
215 lines
5.1 KiB
C
/*
|
|
* Copyright (c) 2021 Arm Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "hardware/gpio.h"
|
|
#include "pico/stdlib.h"
|
|
|
|
#include "st7789.h"
|
|
|
|
static struct st7789_config st7789_cfg;
|
|
static uint16_t st7789_width;
|
|
static uint16_t st7789_height;
|
|
static bool st7789_data_mode = false;
|
|
|
|
void st7789_cmd(uint8_t cmd, const uint8_t *data, size_t len) {
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
} else {
|
|
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
|
|
}
|
|
st7789_data_mode = false;
|
|
|
|
sleep_us(1);
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_put(st7789_cfg.gpio_cs, 0);
|
|
}
|
|
gpio_put(st7789_cfg.gpio_dc, 0);
|
|
sleep_us(1);
|
|
|
|
spi_write_blocking(st7789_cfg.spi, &cmd, sizeof(cmd));
|
|
|
|
if (len) {
|
|
sleep_us(1);
|
|
gpio_put(st7789_cfg.gpio_dc, 1);
|
|
sleep_us(1);
|
|
|
|
spi_write_blocking(st7789_cfg.spi, data, len);
|
|
}
|
|
|
|
sleep_us(1);
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_put(st7789_cfg.gpio_cs, 1);
|
|
}
|
|
gpio_put(st7789_cfg.gpio_dc, 1);
|
|
sleep_us(1);
|
|
}
|
|
|
|
void st7789_caset(uint16_t xs, uint16_t xe) {
|
|
uint8_t data[] = {
|
|
xs >> 8,
|
|
xs & 0xff,
|
|
xe >> 8,
|
|
xe & 0xff,
|
|
};
|
|
|
|
// CASET (2Ah): Column Address Set
|
|
st7789_cmd(0x2a, data, sizeof(data));
|
|
}
|
|
|
|
void st7789_raset(uint16_t ys, uint16_t ye) {
|
|
uint8_t data[] = {
|
|
ys >> 8,
|
|
ys & 0xff,
|
|
ye >> 8,
|
|
ye & 0xff,
|
|
};
|
|
|
|
// RASET (2Bh): Row Address Set
|
|
st7789_cmd(0x2b, data, sizeof(data));
|
|
}
|
|
|
|
void st7789_init(const struct st7789_config *config, uint16_t width, uint16_t height) {
|
|
memcpy(&st7789_cfg, config, sizeof(st7789_cfg));
|
|
st7789_width = width;
|
|
st7789_height = height;
|
|
|
|
spi_init(st7789_cfg.spi, 125 * 1000 * 1000);
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
} else {
|
|
spi_set_format(st7789_cfg.spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
|
|
}
|
|
|
|
gpio_set_function(st7789_cfg.gpio_din, GPIO_FUNC_SPI);
|
|
gpio_set_function(st7789_cfg.gpio_clk, GPIO_FUNC_SPI);
|
|
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_init(st7789_cfg.gpio_cs);
|
|
}
|
|
gpio_init(st7789_cfg.gpio_dc);
|
|
gpio_init(st7789_cfg.gpio_rst);
|
|
gpio_init(st7789_cfg.gpio_bl);
|
|
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_set_dir(st7789_cfg.gpio_cs, GPIO_OUT);
|
|
}
|
|
gpio_set_dir(st7789_cfg.gpio_dc, GPIO_OUT);
|
|
gpio_set_dir(st7789_cfg.gpio_rst, GPIO_OUT);
|
|
gpio_set_dir(st7789_cfg.gpio_bl, GPIO_OUT);
|
|
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_put(st7789_cfg.gpio_cs, 1);
|
|
}
|
|
gpio_put(st7789_cfg.gpio_dc, 1);
|
|
gpio_put(st7789_cfg.gpio_rst, 1);
|
|
sleep_ms(100);
|
|
|
|
// SWRESET (01h): Software Reset
|
|
st7789_cmd(0x01, NULL, 0);
|
|
sleep_ms(150);
|
|
|
|
// SLPOUT (11h): Sleep Out
|
|
st7789_cmd(0x11, NULL, 0);
|
|
sleep_ms(50);
|
|
|
|
// COLMOD (3Ah): Interface Pixel Format
|
|
// - RGB interface color format = 65K of RGB interface
|
|
// - Control interface color format = 16bit/pixel
|
|
st7789_cmd(0x3a, (uint8_t[]){0x55}, 1);
|
|
sleep_ms(10);
|
|
|
|
// MADCTL (36h): Memory Data Access Control
|
|
// - Page Address Order = Top to Bottom
|
|
// - Column Address Order = Left to Right
|
|
// - Page/Column Order = Normal Mode
|
|
// - Line Address Order = LCD Refresh Top to Bottom
|
|
// - RGB/BGR Order = RGB
|
|
// - Display Data Latch Data Order = LCD Refresh Left to Right
|
|
st7789_cmd(0x36, (uint8_t[]){0xA0}, 1);
|
|
|
|
st7789_caset(0, width);
|
|
st7789_raset(0, height);
|
|
|
|
// INVON (21h): Display Inversion On
|
|
st7789_cmd(0x21, NULL, 0);
|
|
sleep_ms(10);
|
|
|
|
// NORON (13h): Normal Display Mode On
|
|
st7789_cmd(0x13, NULL, 0);
|
|
sleep_ms(10);
|
|
|
|
// DISPON (29h): Display On
|
|
st7789_cmd(0x29, NULL, 0);
|
|
sleep_ms(10);
|
|
|
|
st7789_fill(0x0000);
|
|
sleep_ms(50);
|
|
gpio_put(st7789_cfg.gpio_bl, 1);
|
|
}
|
|
|
|
void st7789_ramwr() {
|
|
sleep_us(1);
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_put(st7789_cfg.gpio_cs, 0);
|
|
}
|
|
gpio_put(st7789_cfg.gpio_dc, 0);
|
|
sleep_us(1);
|
|
|
|
// RAMWR (2Ch): Memory Write
|
|
uint8_t cmd = 0x2c;
|
|
spi_write_blocking(st7789_cfg.spi, &cmd, sizeof(cmd));
|
|
|
|
sleep_us(1);
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
gpio_put(st7789_cfg.gpio_cs, 0);
|
|
}
|
|
gpio_put(st7789_cfg.gpio_dc, 1);
|
|
sleep_us(1);
|
|
}
|
|
|
|
void st7789_write(const void *data, size_t len) {
|
|
if (!st7789_data_mode) {
|
|
st7789_ramwr();
|
|
|
|
if (st7789_cfg.gpio_cs > -1) {
|
|
spi_set_format(st7789_cfg.spi, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
|
|
} else {
|
|
spi_set_format(st7789_cfg.spi, 16, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
|
|
}
|
|
|
|
st7789_data_mode = true;
|
|
}
|
|
|
|
spi_write16_blocking(st7789_cfg.spi, data, len / 2);
|
|
}
|
|
|
|
void st7789_put(uint16_t pixel) { st7789_write(&pixel, sizeof(pixel)); }
|
|
|
|
void st7789_fill(uint16_t pixel) {
|
|
int num_pixels = st7789_width * st7789_height;
|
|
|
|
st7789_set_cursor(0, 0);
|
|
|
|
for (int i = 0; i < num_pixels; i++) {
|
|
st7789_put(pixel);
|
|
}
|
|
}
|
|
|
|
void st7789_set_cursor(uint16_t x, uint16_t y) {
|
|
st7789_caset(x, st7789_width);
|
|
st7789_raset(y, st7789_height);
|
|
}
|
|
|
|
void st7789_vertical_scroll(uint16_t row) {
|
|
uint8_t data[] = {(row >> 8) & 0xff, row & 0x00ff};
|
|
|
|
// VSCSAD (37h): Vertical Scroll Start Address of RAM
|
|
st7789_cmd(0x37, data, sizeof(data));
|
|
}
|