Init
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
#include "st7789.h"
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "fonts.h"
|
||||
#include "pins.h"
|
||||
#include <stdint.h>
|
||||
|
||||
static spi_device_handle_t lcd_spi;
|
||||
|
||||
/* ---------------- LOW LEVEL SPI ---------------- */
|
||||
|
||||
static void spi_tx(const void *data, size_t len) {
|
||||
spi_transaction_t t = {
|
||||
.length = len * 8,
|
||||
.tx_buffer = data,
|
||||
.flags = 0,
|
||||
};
|
||||
spi_device_transmit(lcd_spi, &t);
|
||||
}
|
||||
|
||||
void st7789_write_cmd(uint8_t cmd) {
|
||||
gpio_set_level(LCD_RS, 0);
|
||||
spi_tx(&cmd, 1);
|
||||
}
|
||||
|
||||
void st7789_write_data(const void *data, size_t len) {
|
||||
gpio_set_level(LCD_RS, 1);
|
||||
spi_tx(data, len);
|
||||
}
|
||||
|
||||
/* commands */
|
||||
|
||||
/* ---------------- WINDOW ---------------- */
|
||||
|
||||
void st7789_set_window(uint16_t xs, uint16_t ys, uint16_t xe, uint16_t ye) {
|
||||
uint8_t data[4];
|
||||
|
||||
st7789_write_cmd(0x2A);
|
||||
data[0] = (xs + X_OFFSET) >> 8;
|
||||
data[1] = (xs + X_OFFSET) & 0xFF;
|
||||
data[2] = (xe + X_OFFSET) >> 8;
|
||||
data[3] = (xe + X_OFFSET) & 0xFF;
|
||||
st7789_write_data(data, 4);
|
||||
|
||||
st7789_write_cmd(0x2B);
|
||||
data[0] = (ys + Y_OFFSET) >> 8;
|
||||
data[1] = (ys + Y_OFFSET) & 0xFF;
|
||||
data[2] = (ye + Y_OFFSET) >> 8;
|
||||
data[3] = (ye + Y_OFFSET) & 0xFF;
|
||||
st7789_write_data(data, 4);
|
||||
|
||||
st7789_write_cmd(0x2C);
|
||||
}
|
||||
|
||||
/* ---------------- PIXELS ---------------- */
|
||||
|
||||
void st7789_push_pixels(const uint16_t *pixels, size_t count) {
|
||||
gpio_set_level(LCD_RS, 1);
|
||||
|
||||
const uint8_t *data = (const uint8_t *)pixels;
|
||||
size_t bytes = count * 2;
|
||||
|
||||
while (bytes > 0) {
|
||||
size_t chunk = (bytes > SPI_MAX_CHUNK) ? SPI_MAX_CHUNK : bytes;
|
||||
|
||||
spi_transaction_t t = {
|
||||
.length = chunk * 8,
|
||||
.tx_buffer = data,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
spi_device_transmit(lcd_spi, &t);
|
||||
|
||||
data += chunk;
|
||||
bytes -= chunk;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------- INIT ---------------- */
|
||||
|
||||
static void st7789_reset(void) {
|
||||
gpio_set_level(LCD_RESET, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
gpio_set_level(LCD_RESET, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
}
|
||||
|
||||
void st7789_init(void) {
|
||||
gpio_config_t io = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << LCD_RS) | (1ULL << LCD_RESET) | (1ULL << LCD_CS),
|
||||
};
|
||||
gpio_config(&io);
|
||||
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = LCD_DAT,
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = LCD_SCK,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
|
||||
spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO);
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = 40000000,
|
||||
.mode = 0,
|
||||
.spics_io_num = LCD_CS,
|
||||
.queue_size = 1,
|
||||
};
|
||||
|
||||
spi_bus_add_device(SPI3_HOST, &devcfg, &lcd_spi);
|
||||
|
||||
st7789_reset();
|
||||
|
||||
st7789_write_cmd(0x01); // SWRESET
|
||||
vTaskDelay(pdMS_TO_TICKS(150));
|
||||
|
||||
st7789_write_cmd(0x11); // SLPOUT
|
||||
vTaskDelay(pdMS_TO_TICKS(120));
|
||||
|
||||
uint8_t colmod = 0x55; // RGB565 stable
|
||||
st7789_write_cmd(0x3A);
|
||||
st7789_write_data(&colmod, 1);
|
||||
|
||||
uint8_t madctl = 0x70; // typical working orientation (adjust if needed)
|
||||
st7789_write_cmd(0x36);
|
||||
st7789_write_data(&madctl, 1);
|
||||
|
||||
st7789_write_cmd(0x21); // INVERT ON (fixes many panels)
|
||||
|
||||
st7789_write_cmd(0x29); // DISPON
|
||||
vTaskDelay(pdMS_TO_TICKS(20));
|
||||
}
|
||||
|
||||
/*rendering*/
|
||||
|
||||
uint16_t framebuffer[LCD_WIDTH * LCD_HEIGHT];
|
||||
|
||||
static uint16_t fb_dirty_x0 = LCD_WIDTH;
|
||||
static uint16_t fb_dirty_y0 = LCD_HEIGHT;
|
||||
static uint16_t fb_dirty_x1 = 0;
|
||||
static uint16_t fb_dirty_y1 = 0;
|
||||
|
||||
static inline void fb_mark_dirty(uint16_t x, uint16_t y) {
|
||||
if (x < fb_dirty_x0)
|
||||
fb_dirty_x0 = x;
|
||||
if (y < fb_dirty_y0)
|
||||
fb_dirty_y0 = y;
|
||||
if (x > fb_dirty_x1)
|
||||
fb_dirty_x1 = x;
|
||||
if (y > fb_dirty_y1)
|
||||
fb_dirty_y1 = y;
|
||||
}
|
||||
|
||||
void fb_set_pixel(uint16_t x, uint16_t y, uint16_t color) {
|
||||
if (x >= LCD_WIDTH || y >= LCD_HEIGHT)
|
||||
return;
|
||||
|
||||
framebuffer[y * LCD_WIDTH + x] = color;
|
||||
fb_mark_dirty(x, y);
|
||||
}
|
||||
|
||||
void st7789_flush_full(void) {
|
||||
st7789_set_window(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
|
||||
st7789_push_pixels(framebuffer, LCD_WIDTH * LCD_HEIGHT);
|
||||
}
|
||||
|
||||
void st7789_flush(void) {
|
||||
if (fb_dirty_x1 < fb_dirty_x0 || fb_dirty_y1 < fb_dirty_y0)
|
||||
return; // nothing changed
|
||||
|
||||
uint16_t x0 = fb_dirty_x0;
|
||||
uint16_t y0 = fb_dirty_y0;
|
||||
uint16_t x1 = fb_dirty_x1;
|
||||
uint16_t y1 = fb_dirty_y1;
|
||||
|
||||
st7789_set_window(x0, y0, x1, y1);
|
||||
|
||||
for (uint16_t y = y0; y <= y1; y++) {
|
||||
st7789_push_pixels(&framebuffer[y * LCD_WIDTH + x0], (x1 - x0 + 1));
|
||||
}
|
||||
|
||||
// reset dirty region
|
||||
fb_dirty_x0 = LCD_WIDTH;
|
||||
fb_dirty_y0 = LCD_HEIGHT;
|
||||
fb_dirty_x1 = 0;
|
||||
fb_dirty_y1 = 0;
|
||||
}
|
||||
|
||||
/* ---------------- DRAWING ---------------- */
|
||||
|
||||
void st7789_fill(uint16_t color) {
|
||||
for (int i = 0; i < LCD_WIDTH * LCD_HEIGHT; i++)
|
||||
framebuffer[i] = color;
|
||||
}
|
||||
|
||||
void st7789_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
||||
uint16_t color) {
|
||||
for (uint16_t yy = 0; yy < h; yy++) {
|
||||
for (uint16_t xx = 0; xx < w; xx++) {
|
||||
fb_set_pixel(x + xx, y + yy, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void st7789_draw_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
||||
uint16_t color) {
|
||||
if (w == 0 || h == 0)
|
||||
return;
|
||||
|
||||
uint16_t x2 = x + w - 1;
|
||||
uint16_t y2 = y + h - 1;
|
||||
|
||||
// top + bottom
|
||||
for (uint16_t i = x; i <= x2; i++) {
|
||||
fb_set_pixel(i, y, color);
|
||||
fb_set_pixel(i, y2, color);
|
||||
}
|
||||
|
||||
// left + right
|
||||
for (uint16_t j = y; j <= y2; j++) {
|
||||
fb_set_pixel(x, j, color);
|
||||
fb_set_pixel(x2, j, color);
|
||||
}
|
||||
}
|
||||
|
||||
void st7789_blit_rgb565(uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
||||
const uint16_t *data) {
|
||||
for (uint16_t yy = 0; yy < h; yy++) {
|
||||
uint16_t *dst = &framebuffer[(y + yy) * LCD_WIDTH + x];
|
||||
const uint16_t *src = &data[yy * w];
|
||||
|
||||
memcpy(dst, src, w * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
fb_mark_dirty(x, y);
|
||||
fb_mark_dirty(x + w - 1, y + h - 1);
|
||||
}
|
||||
|
||||
/* ---------------- TEXT ---------------- */
|
||||
|
||||
void st7789_draw_char(uint16_t x, uint16_t y, char c, uint16_t fg, uint16_t bg,
|
||||
bool drawBG, const uint8_t font[][5]) {
|
||||
if (c < 32 || c > 127)
|
||||
return;
|
||||
|
||||
const uint8_t *glyph = font[(uint8_t) c];
|
||||
|
||||
for (uint16_t xx = 0; xx < 5; xx++) {
|
||||
uint8_t col = glyph[xx];
|
||||
|
||||
for (uint16_t yy = 0; yy < 8; yy++) {
|
||||
uint16_t px = x + xx;
|
||||
uint16_t py = y + yy;
|
||||
|
||||
if (col & (1 << yy)) {
|
||||
fb_set_pixel(px, py, fg);
|
||||
} else {
|
||||
if (drawBG) { // TRANSPARENT
|
||||
fb_set_pixel(px, py, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fb_mark_dirty(x, y);
|
||||
fb_mark_dirty(x + 5, y + 7);
|
||||
}
|
||||
|
||||
void st7789_draw_string(uint16_t xOld, uint16_t yOld, const char *s,
|
||||
uint16_t fg, uint16_t bg, bool drawBG, const uint8_t font[][5]) {
|
||||
uint16_t x = xOld;
|
||||
uint16_t y = yOld;
|
||||
while (*s) {
|
||||
if (*s == '\n') {
|
||||
y += 5;
|
||||
x = xOld;
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
st7789_draw_char(x, y, *s, fg, bg, drawBG, font);
|
||||
x += 6;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user