#include "impl_epd.h" #define EPD_GPIO_MOSI GPIO_NUM_15 #define EPD_GPIO_SCLK GPIO_NUM_14 #define EPD_GPIO_BUSY GPIO_NUM_11 #define EPD_GPIO_CS GPIO_NUM_13 #define EPD_GPIO_RES GPIO_NUM_10 #define EPD_GPIO_DC GPIO_NUM_12 #define EPD_SPI_HOST SPI2_HOST #define EPD_SPI_SPEED (16 * 1000 * 1000) /* 16MHz */ #define EPD_SPI_MAX_XFER_SIZE (1024) /* Pre-transfer callback for SPI driver. */ static void impl_epd_spi_pre_transfer_cb(spi_transaction_t *txn) { uint32_t dc_value = (uint32_t)txn->user; gpio_set_level(EPD_GPIO_DC, dc_value); } static void IRAM_ATTR impl_epd_busy_irq_handler(void *arg) { impl_epd_handle_t *epd = arg; BaseType_t xHigherPriorityTaskWoken; xSemaphoreGiveFromISR(epd->busy_semaphore, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } esp_err_t impl_epd_init(void *handle) { esp_err_t ret; spi_bus_config_t bus_config = { .mosi_io_num = EPD_GPIO_MOSI, .sclk_io_num = EPD_GPIO_SCLK, .miso_io_num = -1, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = EPD_SPI_MAX_XFER_SIZE, }; spi_device_interface_config_t device_config = { .clock_speed_hz = EPD_SPI_SPEED, .mode = 0, .spics_io_num = EPD_GPIO_CS, .queue_size = 7, .pre_cb = impl_epd_spi_pre_transfer_cb, }; impl_epd_handle_t *epd = handle; /* Initialize SPI bus */ ret = spi_bus_initialize(EPD_SPI_HOST, &bus_config, SPI_DMA_CH_AUTO); ESP_ERROR_CHECK(ret); ret = spi_bus_add_device(EPD_SPI_HOST, &device_config, &epd->spi); ESP_ERROR_CHECK(ret); /* Initialize RTOS components */ epd->busy_semaphore = xSemaphoreCreateBinary(); if (epd->busy_semaphore == NULL) { return ESP_FAIL; } /* Initialize auxillary IOs */ gpio_set_direction(EPD_GPIO_BUSY, GPIO_MODE_INPUT); gpio_set_direction(EPD_GPIO_DC, GPIO_MODE_OUTPUT); gpio_set_direction(EPD_GPIO_RES, GPIO_MODE_OUTPUT); // Set up EPD busy interrupt gpio_pullup_en(EPD_GPIO_BUSY); gpio_set_intr_type(EPD_GPIO_BUSY, GPIO_INTR_POSEDGE); gpio_isr_handler_add(EPD_GPIO_BUSY, impl_epd_busy_irq_handler, epd); /* Release RESET pin */ gpio_set_level(EPD_GPIO_RES, 1); return ret; } epd_ret_t impl_epd_write_command(void *handle, uint8_t *command, uint32_t len) { esp_err_t ret = EPD_OK; spi_transaction_t txn; impl_epd_handle_t *epd = handle; memset(&txn, 0x0, sizeof(txn)); txn.length = 8; txn.tx_buffer = command; txn.user = (void *)0; ret = spi_device_polling_transmit(epd->spi, &txn); if (ret != ESP_OK) ret = EPD_FAIL; if (len > 1) { txn.length = 8 * (len - 1); txn.tx_buffer = &command[1]; txn.user = (void *)1; ret = spi_device_polling_transmit(epd->spi, &txn); if (ret != ESP_OK) ret = EPD_FAIL; } return ret; } epd_ret_t impl_epd_write_data(void *handle, uint8_t *data, uint32_t len) { esp_err_t ret = EPD_OK; spi_transaction_t txn; impl_epd_handle_t *epd = handle; uint8_t *dma_buffer = heap_caps_malloc(EPD_SPI_MAX_XFER_SIZE, MALLOC_CAP_DMA); if (dma_buffer == NULL) return EPD_FAIL; memset(&txn, 0x0, sizeof(txn)); uint8_t has_partial = (len % EPD_SPI_MAX_XFER_SIZE ? 1 : 0); uint32_t txn_count = len / EPD_SPI_MAX_XFER_SIZE + has_partial; txn.user = (void *)1; for (uint32_t i = 0; i < txn_count; i++) { uint16_t txn_bytes = EPD_SPI_MAX_XFER_SIZE; if ((i == txn_count - 1) && has_partial) { txn_bytes = len % EPD_SPI_MAX_XFER_SIZE; } memcpy(dma_buffer, &data[i * EPD_SPI_MAX_XFER_SIZE], txn_bytes); txn.length = 8 * txn_bytes; txn.rxlength = 0; txn.tx_buffer = dma_buffer; ret = spi_device_polling_transmit(epd->spi, &txn); if (ret != ESP_OK) ret = EPD_FAIL; } free(dma_buffer); return ret; } epd_ret_t impl_epd_reset(void *handle) { for (uint8_t i = 0; i < 4; i++) { gpio_set_level(EPD_GPIO_RES, 0); vTaskDelay(pdMS_TO_TICKS(10)); gpio_set_level(EPD_GPIO_RES, 1); vTaskDelay(pdMS_TO_TICKS(10)); } vTaskDelay(pdMS_TO_TICKS(50)); return EPD_OK; } epd_ret_t impl_epd_poll_busy(void *handle) { impl_epd_handle_t *epd = handle; if (gpio_get_level(EPD_GPIO_BUSY) == 1) { return EPD_OK; } gpio_intr_enable(EPD_GPIO_BUSY); if (xSemaphoreTake(epd->busy_semaphore, pdMS_TO_TICKS(10000)) != pdTRUE) { gpio_intr_disable(EPD_GPIO_BUSY); return EPD_FAIL; } gpio_intr_disable(EPD_GPIO_BUSY); return EPD_OK; } epd_ret_t impl_epd_delay(void *handle, uint32_t msec) { vTaskDelay(pdMS_TO_TICKS(msec)); return EPD_OK; }