From 40dad2e4cdfea02cfcf4615169eea9fef3821430 Mon Sep 17 00:00:00 2001 From: Drath Vader Date: Sat, 27 Jun 2026 20:04:13 +0200 Subject: [PATCH] i fucked up my repos --- CMakeLists.txt | 5 ++ README.md | 9 +++ st7920.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ st7920.h | 105 +++++++++++++++++++++++++++++++++ 4 files changed, 275 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 st7920.c create mode 100644 st7920.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..70caec2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "st7920.c" + INCLUDE_DIRS "." + REQUIRES esp_driver_spi esp_driver_gpio +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..de8364b --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# ST7920 display driver +For esp-idf since there's no fucking driver for it appearently + +Ngl a clanker wrote most of it, thx claude. I made it better, cleaner, more optimized (?) and more readable. The clanker got it to work but man the code was ass. + +It's geared to work with my own gfx library, it exposes draw/clear pixel funcs. Nothing else, other than basic init functions and backlight. + +### Todo +- idk add backlight pwm maybe? it does work fine rn \ No newline at end of file diff --git a/st7920.c b/st7920.c new file mode 100644 index 0000000..955142b --- /dev/null +++ b/st7920.c @@ -0,0 +1,156 @@ +#include "st7920.h" +#include +#include "esp_log.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" + +/* ST7920 display driver lib + * regretfully a clanker wrote most of this. first try too, good shit claude + * i did make make the code cleaner, less ass in geneneral and optimized it a bit */ + +static const char *TAG = "ST7920"; + +// ST7920 SPI protocol uses specific byte sequences +// Format: 11111ABC where ABC identifies sync, R/W, RS bits +#define ST7920_SYNC_CMD 0xF8 // 11111000 - Sync + Command +#define ST7920_SYNC_DATA 0xFA // 11111010 - Sync + Data + +// Timing delays (in microseconds) +#define ST7920_DELAY_CMD 72 +#define ST7920_DELAY_DATA 72 +#define ST7920_DELAY_RESET 100000 // 100ms + +static esp_err_t st7920_spi_send(const st7920_device_t *dev, const uint8_t sync_byte, const uint8_t data) { + spi_transaction_t trans = { + .flags = 0, + .length = 24, // 3 bytes = 24 bits + .tx_data = {sync_byte, data & 0xF0, (data << 4) & 0xF0}, + .rx_buffer = NULL, + }; + trans.flags = SPI_TRANS_USE_TXDATA; + + const esp_err_t ret = spi_device_polling_transmit(dev->spi, &trans); + return ret; +} + +esp_err_t st7920_send_cmd(const st7920_device_t *dev, const uint8_t cmd) { + const esp_err_t ret = st7920_spi_send(dev, ST7920_SYNC_CMD, cmd); + ets_delay_us(ST7920_DELAY_CMD); + return ret; +} + +esp_err_t st7920_send_data(const st7920_device_t *dev, const uint8_t data) { + const esp_err_t ret = st7920_spi_send(dev, ST7920_SYNC_DATA, data); + ets_delay_us(ST7920_DELAY_DATA); + return ret; +} + +esp_err_t st7920_init(const st7920_config_t *config, st7920_device_t *device) { + memset(device, 0, sizeof(st7920_device_t)); // clears framebuffer too + device->pin_cs = config->pin_cs; + device->pin_bl = config->pin_bl; + + // Configure CS pin + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << config->pin_cs), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&io_conf); + gpio_set_level(config->pin_cs, 1); + + // Configure BL pin if provided + if (config->pin_bl >= 0) { + io_conf.pin_bit_mask = (1ULL << config->pin_bl); // reuse part of ocnfig from CS pin + gpio_config(&io_conf); + st7920_set_backlight(device, 0); + } + + // Configure SPI device + const spi_device_interface_config_t dev_cfg = { + .clock_speed_hz = config->spi_clock_speed_hz, + .mode = 3, // CPOL=1, CPHA=1 (required by ST7920) + .spics_io_num = -1, // We manage CS manually + .queue_size = 1, + .flags = SPI_DEVICE_NO_DUMMY, + }; + + const esp_err_t ret = spi_bus_add_device(config->spi_host, &dev_cfg, &device->spi); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to add SPI device: %s", esp_err_to_name(ret)); + return ret; + } + + // start talking to the thing + vTaskDelay(pdMS_TO_TICKS(50)); // Wait after reset + st7920_send_cmd(device, ST7920_CMD_BASIC_FUNC); + vTaskDelay(pdMS_TO_TICKS(1)); + st7920_send_cmd(device, ST7920_CMD_BASIC_FUNC); + ets_delay_us(100); + st7920_send_cmd(device, ST7920_CMD_CLEAR); + vTaskDelay(pdMS_TO_TICKS(10)); // Clear needs longer time + st7920_send_cmd(device, ST7920_CMD_ENTRY_MODE); + ets_delay_us(100); + st7920_send_cmd(device, ST7920_CMD_GRAPHIC_ON); + ets_delay_us(100); + st7920_send_cmd(device, ST7920_CMD_DISPLAY_ON); + ets_delay_us(100); + + ESP_LOGI(TAG, "display init done"); + return ESP_OK; +} + +esp_err_t st7920_clear(st7920_device_t *dev) { + memset(dev->framebuffer, 0, sizeof(dev->framebuffer)); + + // Clear display RAM + st7920_send_cmd(dev, ST7920_CMD_BASIC_FUNC); + st7920_send_cmd(dev, ST7920_CMD_CLEAR); + vTaskDelay(pdMS_TO_TICKS(10)); + st7920_send_cmd(dev, ST7920_CMD_GRAPHIC_ON); + + return ESP_OK; +} + +esp_err_t st7920_set_backlight(const st7920_device_t *dev, const bool backlight) { + return gpio_set_level(dev->pin_bl, backlight); +} + +esp_err_t st7920_update_screen(const st7920_device_t *dev) { + // ST7920 is actually two 128x32 disps, we send row by row to each of them + for (uint8_t y = 0; y < 64; y++) { + const bool upper_half = y < 32; + st7920_send_cmd(dev, ST7920_CMD_SET_GRAPHIC_ADDR | (upper_half ? y : y - 32)); // adjust y pos if we're on 2nd screen + st7920_send_cmd(dev, ST7920_CMD_SET_GRAPHIC_ADDR | (upper_half ? 0x00 : 0x08)); // 2nd screen has a different addr + + for (uint8_t x = 0; x < 16; x++) + st7920_send_data(dev, dev->framebuffer[y * 16 + x]); + } + + return ESP_OK; +} + +static void st7920_set_pixel_color(st7920_device_t *dev, const uint8_t x, const uint8_t y, const uint8_t color) { + if (x >= ST7920_WIDTH || y >= ST7920_HEIGHT) + return; + + const uint16_t byte_index = (y * (ST7920_WIDTH / 8)) + (x / 8); + const uint8_t bit_index = 7 - (x % 8); + + if (color) + dev->framebuffer[byte_index] |= (1 << bit_index); + else + dev->framebuffer[byte_index] &= ~(1 << bit_index); +} + +void st7920_draw_pixel(void *dev, const uint8_t x, const uint8_t y) { + st7920_set_pixel_color(dev, x, y, 1); +} + +void st7920_clear_pixel(void *dev, const uint8_t x, const uint8_t y) { + st7920_set_pixel_color(dev, x, y, 0); +} \ No newline at end of file diff --git a/st7920.h b/st7920.h new file mode 100644 index 0000000..e01d705 --- /dev/null +++ b/st7920.h @@ -0,0 +1,105 @@ +#ifndef ST7920_H +#define ST7920_H + +#include "esp_err.h" +#include "driver/spi_master.h" + +// Display dimensions +#define ST7920_WIDTH 128 +#define ST7920_HEIGHT 64 + +// SPI commands for ST7920 +#define ST7920_CMD_BASIC_FUNC 0x30 // Basic instruction set +#define ST7920_CMD_EXTENDED_FUNC 0x34 // Extended instruction set +#define ST7920_CMD_DISPLAY_ON 0x0C // Display ON +#define ST7920_CMD_DISPLAY_OFF 0x08 // Display OFF +#define ST7920_CMD_CLEAR 0x01 // Clear display +#define ST7920_CMD_ENTRY_MODE 0x06 // Entry mode +#define ST7920_CMD_HOME 0x02 // Return home +#define ST7920_CMD_GRAPHIC_ON 0x36 // Extended + Graphic mode ON +#define ST7920_CMD_SET_GRAPHIC_ADDR 0x80 // Set GDRAM address + +// Configuration structure +typedef struct { + spi_host_device_t spi_host; + int pin_cs; + int pin_bl; + int spi_clock_speed_hz; +} st7920_config_t; + +// Handle for ST7920 device +typedef struct { + spi_device_handle_t spi; + int pin_cs; + int pin_bl; + uint8_t framebuffer[ST7920_WIDTH * ST7920_HEIGHT / 8]; +} st7920_device_t; + +/** + * @brief Initialize ST7920 display + * + * @param config Configuration structure + * @param out_dev Pointer to store device handle + * @return esp_err_t ESP_OK on success + */ +esp_err_t st7920_init(const st7920_config_t *config, st7920_device_t *device); + +/** + * @brief Send command to ST7920 + * + * @param dev Device handle + * @param cmd Command byte + * @return esp_err_t ESP_OK on success + */ +esp_err_t st7920_send_cmd(const st7920_device_t *dev, uint8_t cmd); + +/** + * @brief Send data to ST7920 + * + * @param dev Device handle + * @param data Data byte + * @return esp_err_t ESP_OK on success + */ +esp_err_t st7920_send_data(const st7920_device_t *dev, uint8_t data); + +/** + * @brief Clear the display + * + * @param dev Device handle + * @return esp_err_t ESP_OK on success + */ +esp_err_t st7920_clear(st7920_device_t *dev); + +/** + * @brief Set backlight on or off + * + * @param dev Device handle + * @param backlight Backlight enabled + */ +esp_err_t st7920_set_backlight(const st7920_device_t *dev, bool backlight); + +/** + * @brief Update the entire display with framebuffer contents + * + * @param dev Device handle + * @return esp_err_t ESP_OK on success + */ +esp_err_t st7920_update_screen(const st7920_device_t *dev); + +/** + * @brief Turn a pixel ON at given coordinates + * @param dev Device handle + * @param x x coordinate + * @param y y coordinate + */ +void st7920_draw_pixel(void *dev, uint8_t x, uint8_t y); + +/** + * @brief Turn a pixel OFF at given coordinates + * @param dev Device handle + * @param x x coordinate + * @param y y coordinate + */ +void st7920_clear_pixel(void *dev, uint8_t x, uint8_t y); + +#endif // ST7920_H