Login
1 branch 0 tags
Ben (Desktop/Arch) Topbar 7eca3af 1 month ago 37 Commits
moon / firmware / esp32 / main / st7735.c
#include "st7735.h"

#include <string.h>
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// GPIO pin assignments (ESP32-S3)
#define PIN_CS    4
#define PIN_DC    6
#define PIN_RST   5

#define PIN_MOSI  7
#define PIN_SCK   8

// ST7735 commands
#define ST7735_SWRESET 0x01
#define ST7735_SLPOUT  0x11
#define ST7735_INVON   0x21
#define ST7735_DISPON  0x29
#define ST7735_CASET   0x2A
#define ST7735_RASET   0x2B
#define ST7735_RAMWR   0x2C
#define ST7735_MADCTL  0x36
#define ST7735_COLMOD  0x3A

// Display offsets (adjust for your specific module)
#define X_OFFSET 0
#define Y_OFFSET 0

static spi_device_handle_t spi_dev;

// Pre-transfer callback to set DC pin
static void IRAM_ATTR spi_pre_transfer_cb(spi_transaction_t *t) {
    int dc = (int)t->user;
    gpio_set_level(PIN_DC, dc);
}

static void st7735_write_cmd(uint8_t cmd) {
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &cmd,
        .user = (void*)0,  // DC=0 for command
    };
    spi_device_polling_transmit(spi_dev, &t);
}

static void st7735_write_data(const uint8_t *data, size_t len) {
    if (len == 0) return;
    spi_transaction_t t = {
        .length = len * 8,
        .tx_buffer = data,
        .user = (void*)1,  // DC=1 for data
    };
    spi_device_polling_transmit(spi_dev, &t);
}

static void st7735_write_data_byte(uint8_t data) {
    st7735_write_data(&data, 1);
}

void st7735_init(void) {
    // Configure GPIO pins
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << PIN_DC) | (1ULL << PIN_RST),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_conf);

    // Hardware reset
    gpio_set_level(PIN_RST, 0);
    vTaskDelay(pdMS_TO_TICKS(20));
    gpio_set_level(PIN_RST, 1);
    vTaskDelay(pdMS_TO_TICKS(20));

    // Configure SPI bus
    spi_bus_config_t buscfg = {
        .mosi_io_num = PIN_MOSI,
        .miso_io_num = -1,
        .sclk_io_num = PIN_SCK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 160 * 32 * 3,  // 32 lines at 3 bytes/pixel (RGB666)
    };
    spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);

    // Configure SPI device
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 80 * 1000 * 1000,  // 40 MHz
        .mode = 0,
        .spics_io_num = PIN_CS,
        .queue_size = 1,
        .pre_cb = spi_pre_transfer_cb,
    };
    spi_bus_add_device(SPI2_HOST, &devcfg, &spi_dev);

    // ST7735 initialization sequence
    st7735_write_cmd(ST7735_SWRESET);
    vTaskDelay(pdMS_TO_TICKS(30));

    st7735_write_cmd(ST7735_SLPOUT);
    vTaskDelay(pdMS_TO_TICKS(30));

    // Set color mode to RGB666 (18-bit, 3 bytes per pixel)
    st7735_write_cmd(ST7735_COLMOD);
    st7735_write_data_byte(0x06);  // 18-bit RGB666

    // Set display orientation (MV + MX for landscape 160x128)
    st7735_write_cmd(ST7735_MADCTL);
    st7735_write_data_byte(0x60);

    // Enable display
    st7735_write_cmd(ST7735_DISPON);
    vTaskDelay(pdMS_TO_TICKS(20));
}

void st7735_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
    // Column address set
    st7735_write_cmd(ST7735_CASET);
    uint8_t caset_data[] = {
        0, (uint8_t)(x0 + X_OFFSET),
        0, (uint8_t)(x1 + X_OFFSET)
    };
    st7735_write_data(caset_data, 4);

    // Row address set
    st7735_write_cmd(ST7735_RASET);
    uint8_t raset_data[] = {
        0, (uint8_t)(y0 + Y_OFFSET),
        0, (uint8_t)(y1 + Y_OFFSET)
    };
    st7735_write_data(raset_data, 4);

    // Write to RAM
    st7735_write_cmd(ST7735_RAMWR);
}

void st7735_write_pixels(const uint16_t *data, size_t len) {
    st7735_write_data((const uint8_t*)data, len * 2);
}

void st7735_write_pixels_rgb666(const uint8_t *data, size_t pixel_count) {
    st7735_write_data(data, pixel_count * 3);
}