179 lines
4.9 KiB
C
179 lines
4.9 KiB
C
|
|
||
|
#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;
|
||
|
}
|