i fucked up my repos
This commit is contained in:
5
CMakeLists.txt
Normal file
5
CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "st7920.c"
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
REQUIRES esp_driver_spi esp_driver_gpio
|
||||||
|
)
|
||||||
9
README.md
Normal file
9
README.md
Normal file
@@ -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
|
||||||
156
st7920.c
Normal file
156
st7920.c
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
#include "st7920.h"
|
||||||
|
#include <string.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
105
st7920.h
Normal file
105
st7920.h
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user