// epd7in5_v3.c #include "epd7in5.h" #include "driver/gpio.h" #define TIMEOUT_MS 1000 static esp_err_t gpio_setup(int pin, gpio_mode_t mode) { gpio_config_t io_conf = { .pin_bit_mask = 1ULL << pin, .mode = mode, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; return gpio_config(&io_conf); } #define ESP_RETURN_ON_ERROR(x, y, z) x esp_err_t epd7in5_v3_create(epd7in5_v3_t *epd, spi_host_device_t host) { esp_err_t ret; epd->pin_rst = EPD_RST_PIN; epd->pin_dc = EPD_DC_PIN; epd->pin_cs = EPD_CS_PIN; epd->pin_busy = EPD_BUSY_PIN; epd->width = EPD_WIDTH; epd->height = EPD_HEIGHT; epd->spi_host = host; // 1) configure GPIOs ESP_RETURN_ON_ERROR(gpio_setup(epd->pin_rst, GPIO_MODE_OUTPUT), "rst gpio", ret); ESP_RETURN_ON_ERROR(gpio_setup(epd->pin_dc, GPIO_MODE_OUTPUT), "dc gpio", ret); ESP_RETURN_ON_ERROR(gpio_setup(epd->pin_cs, GPIO_MODE_OUTPUT), "cs gpio", ret); ESP_RETURN_ON_ERROR(gpio_setup(epd->pin_busy,GPIO_MODE_INPUT), "busy gpio", ret); // 2) initialize SPI bus if needed, then add device spi_bus_config_t buscfg = { .mosi_io_num = 11/* your MOSI pin */, .miso_io_num = 13/* your MISO pin */, .sclk_io_num = 12/* your SCLK pin */, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = epd->width * epd->height / 8 + 16 }; ESP_RETURN_ON_ERROR(spi_bus_initialize(epd->spi_host, &buscfg, SPI_DMA_CH_AUTO), "bus init", ret); return ESP_OK; } esp_err_t epd7in5_v3_init(epd7in5_v3_t *epd) { spi_device_interface_config_t devcfg = { .clock_speed_hz = 20 * 1000 * 1000, // 2 MHz .mode = 0, // SPI mode 0 .spics_io_num = epd->pin_cs, .queue_size = 1, .flags = SPI_DEVICE_HALFDUPLEX // use .flags |= SPI_DEVICE_3WIRE if necessary }; ESP_RETURN_ON_ERROR(spi_bus_add_device(epd->spi_host, &devcfg, &epd->spi), "add device", ret); // 3) reset panel gpio_set_level(epd->pin_rst, 1); vTaskDelay(pdMS_TO_TICKS(20)); gpio_set_level(epd->pin_rst, 0); vTaskDelay(pdMS_TO_TICKS(4)); gpio_set_level(epd->pin_rst, 1); vTaskDelay(pdMS_TO_TICKS(20)); // 4) initial sequence (matches epd7in5_v3::Init) epd7in5_v3_send_command(epd, CMD_BOOSTER_SOFT_START); epd7in5_v3_send_data(epd, 0x17); epd7in5_v3_send_data(epd, 0x17); epd7in5_v3_send_data(epd, 0x28); epd7in5_v3_send_data(epd, 0x17); epd7in5_v3_send_command(epd, CMD_POWER_SETTING); epd7in5_v3_send_data(epd, 0x07); epd7in5_v3_send_data(epd, 0x07); epd7in5_v3_send_data(epd, 0x28); epd7in5_v3_send_data(epd, 0x17); epd7in5_v3_send_command(epd, CMD_POWER_ON); vTaskDelay(pdMS_TO_TICKS(100)); epd7in5_v3_busy_wait(epd); epd7in5_v3_send_command(epd, CMD_PANEL_SETTING); epd7in5_v3_send_data(epd, 0x1F); epd7in5_v3_send_command(epd, CMD_RESOLUTION_SETTING); epd7in5_v3_send_data(epd, 0x03); epd7in5_v3_send_data(epd, 0x20); epd7in5_v3_send_data(epd, 0x01); epd7in5_v3_send_data(epd, 0xE0); epd7in5_v3_send_command(epd, CMD_DUAL_SPI); epd7in5_v3_send_data(epd, 0x00); epd7in5_v3_send_command(epd, CMD_VCOM_AND_DATA_INTERVAL_SETTING); epd7in5_v3_send_data(epd, 0x10); epd7in5_v3_send_data(epd, 0x07); epd7in5_v3_send_command(epd, CMD_TCON_SETTING); epd7in5_v3_send_data(epd, 0x22); return ESP_OK; } esp_err_t epd7in5_v3_send_command(epd7in5_v3_t *epd, uint8_t cmd) { esp_err_t ret; spi_transaction_t t = { .flags = SPI_TRANS_USE_TXDATA, .tx_data = { cmd, 0, 0, 0 }, .length = 8, }; gpio_set_level(epd->pin_dc, 0); ret = spi_device_polling_transmit(epd->spi, &t); return ret; } esp_err_t epd7in5_v3_send_data(epd7in5_v3_t *epd, uint8_t data) { esp_err_t ret; spi_transaction_t t = { .flags = SPI_TRANS_USE_TXDATA, .tx_data = { data, 0, 0, 0 }, .length = 8, }; gpio_set_level(epd->pin_dc, 1); ret = spi_device_polling_transmit(epd->spi, &t); return ret; } esp_err_t epd7in5_v3_send_data_block(epd7in5_v3_t *epd, const uint8_t *data, size_t len) { esp_err_t ret; spi_transaction_t t = { .tx_buffer = data, .length = len * 8, }; gpio_set_level(epd->pin_dc, 1); ret = spi_device_polling_transmit(epd->spi, &t); return ret; } void epd7in5_v3_busy_wait(epd7in5_v3_t *epd) { // send dummy 0x71 until BUSY goes high do { epd7in5_v3_send_command(epd, 0x71); vTaskDelay(pdMS_TO_TICKS(10)); } while (gpio_get_level(epd->pin_busy) == 0); vTaskDelay(pdMS_TO_TICKS(20)); } esp_err_t epd7in5_v3_display(epd7in5_v3_t *epd, const uint8_t *image) { size_t width_bytes = (epd->width + 7) / 8; size_t img_size = width_bytes * epd->height; epd7in5_v3_send_command(epd, CMD_DISPLAY_START_TRANSMISSION_1); // white buffer: for (size_t i = 0; i < img_size; i++) { epd7in5_v3_send_data(epd, 0xFF); } epd7in5_v3_send_command(epd, CMD_DISPLAY_START_TRANSMISSION_2); for (size_t i = 0; i < img_size; i += width_bytes) { epd7in5_v3_send_data_block(epd, &image[i], width_bytes); } epd7in5_v3_send_command(epd, CMD_DISPLAY_REFRESH); vTaskDelay(pdMS_TO_TICKS(100)); epd7in5_v3_busy_wait(epd); return ESP_OK; } esp_err_t epd7in5_v3_clear(epd7in5_v3_t *epd) { size_t len = (epd->width + 7) / 8 * epd->height; // Stage 1: paint white epd7in5_v3_send_command(epd, CMD_DISPLAY_START_TRANSMISSION_1); for (size_t i = 0; i < len; i++) { epd7in5_v3_send_data(epd, 0xFF); } // Stage 2: paint black epd7in5_v3_send_command(epd, CMD_DISPLAY_START_TRANSMISSION_2); for (size_t i = 0; i < len; i++) { epd7in5_v3_send_data(epd, 0x00); } // Refresh epd7in5_v3_send_command(epd, CMD_DISPLAY_REFRESH); vTaskDelay(pdMS_TO_TICKS(100)); epd7in5_v3_busy_wait(epd); return ESP_OK; } esp_err_t epd7in5_v3_sleep(epd7in5_v3_t *epd) { epd7in5_v3_send_command(epd, 0x50); epd7in5_v3_send_data(epd, 0xF7); epd7in5_v3_send_command(epd, CMD_POWER_OFF); epd7in5_v3_busy_wait(epd); epd7in5_v3_send_command(epd, 0x07); epd7in5_v3_send_data(epd, 0xA5); vTaskDelay(pdMS_TO_TICKS(2000)); spi_bus_remove_device(epd->spi); gpio_set_level(epd->pin_rst, 0); gpio_set_level(epd->pin_dc, 0); return ESP_OK; }