259 lines
7.7 KiB
C
259 lines
7.7 KiB
C
// 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_init_fast(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);
|
|
|
|
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));
|
|
|
|
epd7in5_v3_send_command(epd, 0x50);
|
|
epd7in5_v3_send_data(epd, 0x10);
|
|
epd7in5_v3_send_data(epd, 0x07);
|
|
|
|
epd7in5_v3_send_command(epd, 0x04);
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
epd7in5_v3_busy_wait(epd);
|
|
|
|
epd7in5_v3_send_command(epd, 0x06);
|
|
epd7in5_v3_send_data(epd, 0x27);
|
|
epd7in5_v3_send_data(epd, 0x27);
|
|
epd7in5_v3_send_data(epd, 0x18);
|
|
epd7in5_v3_send_data(epd, 0x17);
|
|
|
|
epd7in5_v3_send_command(epd, 0xE0);
|
|
epd7in5_v3_send_data(epd, 0x02);
|
|
epd7in5_v3_send_command(epd, 0xE5);
|
|
epd7in5_v3_send_data(epd, 0x5A);
|
|
|
|
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;
|
|
}
|