From 1ce0c9944ef0c1925ef16d495a51602a088926bb Mon Sep 17 00:00:00 2001 From: imi415 Date: Fri, 22 Jan 2021 19:20:37 +0000 Subject: [PATCH] New driver. --- CMakeLists.txt | 6 +- des_epd_impl.c | 167 +++++++++++++++++++++++++++++ include/config.h | 32 ++++++ include/des_epd_impl.h | 33 ++++++ lib/gdew0213m21/gdew0213m21_data.h | 67 ++++++++++++ lib/gdew0213m21/gdew0213m21_epd.c | 104 ++++++++++++++---- lib/gdew0213m21/gdew0213m21_epd.h | 15 ++- main.c | 23 +++- 8 files changed, 420 insertions(+), 27 deletions(-) create mode 100644 des_epd_impl.c create mode 100644 include/config.h create mode 100644 include/des_epd_impl.h create mode 100644 lib/gdew0213m21/gdew0213m21_data.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e710f5..21668be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,11 +6,15 @@ set(CMAKE_C_STANDARD 11) set(SOURCES main.c + des_epd_impl.c lib/gdew0213m21/gdew0213m21_epd.c ) include_directories( + include lib/gdew0213m21 ) -add_executable(epd_lvgl ${SOURCES}) \ No newline at end of file +add_executable(epd_lvgl ${SOURCES}) + +target_link_libraries(epd_lvgl gpiod) \ No newline at end of file diff --git a/des_epd_impl.c b/des_epd_impl.c new file mode 100644 index 0000000..10a8fe6 --- /dev/null +++ b/des_epd_impl.c @@ -0,0 +1,167 @@ +#include "des_epd_impl.h" + +_des_impl_t *_des_epd_impl_init(void) { + int spi_fd = open(CONFIG_SPIDEV_FILENAME, O_RDWR); + if(spi_fd < 0) return NULL; + + uint32_t spi_mode = 0; // Nothing to be set in default mode. + + int ret = ioctl(spi_fd, SPI_IOC_WR_MODE32, &spi_mode); + if(ret == -1) goto spi_err_out; + + ret = ioctl(spi_fd, SPI_IOC_RD_MODE32, &spi_mode); + if(ret == -1) goto spi_err_out; + + uint32_t spi_word_len = 8; + + ret= ioctl(spi_fd, SPI_IOC_WR_BITS_PER_WORD, &spi_word_len); + if(ret == -1) goto spi_err_out; + + ret = ioctl(spi_fd, SPI_IOC_RD_BITS_PER_WORD, &spi_word_len); + if(ret == -1) goto spi_err_out; + + uint32_t spi_speed = 10000000; // 10MHz + + ret = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); + if(ret == -1) goto spi_err_out; + + ret = ioctl(spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &spi_speed); + if(ret == -1) goto spi_err_out; + + _des_impl_t *impl = malloc(sizeof(_des_impl_t)); + if(impl == NULL) goto spi_err_out; + + impl->spi_fd = spi_fd; + + impl->gpio_chip = gpiod_chip_open(CONFIG_GPIO_CHIP); + if(impl->gpio_chip == NULL) goto impl_err_out; + + impl->gpio_line_res = gpiod_chip_get_line(impl->gpio_chip, CONFIG_RES_PIN); + if(impl->gpio_line_res == NULL) goto gpiod_err_out; + + impl->gpio_line_dc = gpiod_chip_get_line(impl->gpio_chip, CONFIG_DC_PIN); + if(impl->gpio_line_dc == NULL) goto gpiod_err_out; + + impl->gpio_line_busy = gpiod_chip_get_line(impl->gpio_chip, CONFIG_BUSY_PIN); + if(impl->gpio_line_busy == NULL) goto gpiod_err_out; + + ret = gpiod_line_request_output(impl->gpio_line_dc, CONFIG_GPIO_CONSUMER, 1); + if(ret != 0) goto gpiod_err_out; + + ret = gpiod_line_request_output(impl->gpio_line_res, CONFIG_GPIO_CONSUMER, 1); + if(ret != 0) goto gpiod_err_out; + + ret = gpiod_line_request_input(impl->gpio_line_busy, CONFIG_GPIO_CONSUMER); + if(ret != 0) goto gpiod_err_out; + + return impl; + +gpiod_err_out: + gpiod_chip_close(impl->gpio_chip); + +impl_err_out: + free(impl); + +spi_err_out: + close(spi_fd); + return NULL; +} + +int _des_epd_impl_deinit(_des_impl_t *handle) { + close(handle->spi_fd); + gpiod_chip_close(handle->gpio_chip); + free(handle); + return 0; +} + +des_epd_ret_t _des_epd_write_cmd(void *handle, uint8_t *cmd, uint8_t len) { + _des_impl_t *impl = handle; + + int ret = gpiod_line_set_value(impl->gpio_line_dc, 0); + if(ret) return DES_EPD_ERROR; + + struct spi_ioc_transfer cmd_tr = { + .tx_buf = (unsigned long)cmd, + .rx_buf = (unsigned long)NULL, + .len = 1, + .bits_per_word = 8 + }; + + ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &cmd_tr); + if(ret < 1) return DES_EPD_ERROR; + + if(len > 1) { + ret = gpiod_line_set_value(impl->gpio_line_dc, 1); // Set DC pin + if(ret) return DES_EPD_ERROR; + cmd_tr.tx_buf = (unsigned long)&cmd[1]; //2nd transfer + cmd_tr.len = len - 1; + + ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &cmd_tr); + if(ret < len - 1) return DES_EPD_ERROR; + } + + return DES_EPD_OK; +} + +des_epd_ret_t _des_epd_write_data(void *handle, uint8_t *data, uint16_t len) { + _des_impl_t *impl = handle; + + int ret = gpiod_line_set_value(impl->gpio_line_dc, 1); + + struct spi_ioc_transfer data_tr = { + .tx_buf = (unsigned long)data, + .rx_buf = (unsigned long)NULL, + .len = len, + .bits_per_word = 8 + }; + + ret = ioctl(impl->spi_fd, SPI_IOC_MESSAGE(1), &data_tr); + if(ret < len) return DES_EPD_ERROR; + + return DES_EPD_OK; +} + +des_epd_ret_t _des_epd_reset(void *handle) { + _des_impl_t *impl = handle; + + usleep(10 * 1000); + + int ret = gpiod_line_set_value(impl->gpio_line_res, 0); + if(ret) return DES_EPD_ERROR; + + usleep(10 * 1000); + + ret = gpiod_line_set_value(impl->gpio_line_res, 1); + if(ret) return DES_EPD_ERROR; + + usleep(10 * 1000); + + ret = gpiod_line_set_value(impl->gpio_line_res, 0); + if(ret) return DES_EPD_ERROR; + + usleep(10 * 1000); + + ret = gpiod_line_set_value(impl->gpio_line_res, 1); + if(ret) return DES_EPD_ERROR; + + usleep(10 * 1000); + + return DES_EPD_OK; +} + +des_epd_ret_t _des_epd_poll_busy(void *handle) { + _des_impl_t *impl = handle; + uint32_t i = 0; + usleep(1 * 1000); + while(i < CONFIG_MAX_POLLING_MSEC) { + int ret = gpiod_line_get_value(impl->gpio_line_busy); + if(ret != 1) { + i += 10; + usleep(10 * 1000); + } + else { + return DES_EPD_OK; + } + } + return DES_EPD_ERROR; +} \ No newline at end of file diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..6540991 --- /dev/null +++ b/include/config.h @@ -0,0 +1,32 @@ +#ifndef __CONFIG_H +#define __CONFIG_H + +#ifndef CONFIG_SPIDEV_FILENAME +#define CONFIG_SPIDEV_FILENAME "/dev/spidev0.1" +#endif + +#ifndef CONFIG_GPIO_CONSUMER +#define CONFIG_GPIO_CONSUMER "epd_lvgl" +#endif + +#ifndef CONFIG_GPIO_CHIP +#define CONFIG_GPIO_CHIP "/dev/gpiochip0" +#endif + +#ifndef CONFIG_RES_PIN +#define CONFIG_RES_PIN 23 +#endif + +#ifndef CONFIG_DC_PIN +#define CONFIG_DC_PIN 24 +#endif + +#ifndef CONFIG_BUSY_PIN +#define CONFIG_BUSY_PIN 22 +#endif + +#ifndef CONFIG_MAX_POLLING_MSEC +#define CONFIG_MAX_POLLING_MSEC 30000 // 30s +#endif + +#endif \ No newline at end of file diff --git a/include/des_epd_impl.h b/include/des_epd_impl.h new file mode 100644 index 0000000..e5bc80d --- /dev/null +++ b/include/des_epd_impl.h @@ -0,0 +1,33 @@ +#ifndef __DES_EPD_IMPL_H +#define __DES_EPD_IMPL_H + +// SPIDEV +#include +#include +#include +#include +#include + +// LIBGPIOD +#include + +// USER +#include "gdew0213m21_epd.h" +#include "config.h" + +typedef struct { + int spi_fd; + struct gpiod_chip *gpio_chip; + struct gpiod_line *gpio_line_res; + struct gpiod_line *gpio_line_dc; + struct gpiod_line *gpio_line_busy; +} _des_impl_t; + +_des_impl_t *_des_epd_impl_init(void); +int _des_epd_impl_deinit(_des_impl_t *handle); +des_epd_ret_t _des_epd_write_cmd(void *handle, uint8_t *cmd, uint8_t len); +des_epd_ret_t _des_epd_write_data(void *handle, uint8_t *data, uint16_t len); +des_epd_ret_t _des_epd_reset(void *handle); +des_epd_ret_t _des_epd_poll_busy(void *handle); + +#endif \ No newline at end of file diff --git a/lib/gdew0213m21/gdew0213m21_data.h b/lib/gdew0213m21/gdew0213m21_data.h new file mode 100644 index 0000000..97e9ade --- /dev/null +++ b/lib/gdew0213m21/gdew0213m21_data.h @@ -0,0 +1,67 @@ +uint8_t gdew0213m21_init_sequence[] = { + 0x01, 0x00, 0x1F, + 0x01, 0x50, 0x97 +}; + +uint8_t gdew0213m21_init_sequence_part[] = { + 0x03, 0x06, 0x17, 0x17, 0x1F, // boost soft start + 0x04, 0x01, 0x03, 0x00, 0x2B, 0x2B, // Power settings + 0x01, 0x00, 0xBF, // Panel setting, LUT from OTP + 0x01, 0x30, 0x3C, + 0x03, 0x61, 0x68, 0x00, 0xD4, + 0x01, 0x82, 0x12, // VCOM DC settings + 0x01, 0x50, 0x47 +}; + +uint8_t gdew0213m21_lut_vcom[] = { + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uint8_t gdew0213m21_lut_ww[] = { + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uint8_t gdew0213m21_lut_bw[] = { + 0x20, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uint8_t gdew0213m21_lut_wb[] = { + 0x10, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uint8_t gdew0213m21_lut_bb[] = { + 0x00, 0x01, 0x20, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; \ No newline at end of file diff --git a/lib/gdew0213m21/gdew0213m21_epd.c b/lib/gdew0213m21/gdew0213m21_epd.c index 80cc770..e55d326 100644 --- a/lib/gdew0213m21/gdew0213m21_epd.c +++ b/lib/gdew0213m21/gdew0213m21_epd.c @@ -1,35 +1,93 @@ #include "gdew0213m21_epd.h" - -uint8_t gdew0213m21_init_sequence[] = { - 0x03, 0x06, 0x17, 0x17, 0x1F, // boost soft start - 0x04, 0x01, 0x03, 0x00, 0x2B, 0x2B, // Power settings - 0x01, 0x00, 0xBF, // Panel setting, LUT from OTP - 0x01, 0x30, 0x3C, - 0x03, 0x61, 0x68, 0x00, 0xD4, - 0x01, 0x82, 0x12, // VCOM DC settings - 0x01, 0x50, 0x47 -}; +#include "gdew0213m21_data.h" des_epd_ret_t _des_epd_hardware_reset(des_epd_t *epd) { - // + DES_EPD_ERROR_CHECK(epd->cb.reset_cb(epd->user_data)); + return DES_EPD_OK; } -des_epd_ret_t _des_epd_software_reset(des_epd_t *epd) { - // -} - -des_epd_ret_t _des_epd_init_seq(des_epd_t *epd) { - uint16_t i = 0; - while(i < sizeof(DES_EPD_PANEL_SELECTION)) { - DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &DES_EPD_PANEL_SELECTION[i + 1], DES_EPD_PANEL_SELECTION[i] + 1)); - i += DES_EPD_PANEL_SELECTION[i] + 2; - } +des_epd_ret_t _des_epd_upload_lut(des_epd_t *epd) { + uint8_t cmd = 0x20; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DES_EPD_LUT_VCOM, sizeof(DES_EPD_LUT_VCOM))); + cmd = 0x21; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DES_EPD_LUT_WW, sizeof(DES_EPD_LUT_WW))); + cmd = 0x22; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DES_EPD_LUT_BW, sizeof(DES_EPD_LUT_BW))); + cmd = 0x23; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DES_EPD_LUT_WB, sizeof(DES_EPD_LUT_WB))); + cmd = 0x24; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DES_EPD_LUT_BB, sizeof(DES_EPD_LUT_BB))); return DES_EPD_OK; } -des_epd_ret_t des_epd_init(des_epd_t *epd) { - DES_EPD_ERROR_CHECK(_des_epd_init_seq(epd)); +des_epd_ret_t _des_epd_power_up(des_epd_t *epd) { + uint8_t cmd = 0x04; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + + return DES_EPD_OK; +} + +des_epd_ret_t _des_epd_power_down(des_epd_t *epd) { + uint8_t cmd[5] = {0x50, 0xF7, 0x02, 0x07, 0xA5}; // VCOM and data interval setting + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, cmd, 0x02)); + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd[2], 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd[3], 0x02)); + + return DES_EPD_OK; +} + +des_epd_ret_t _des_epd_init_seq(des_epd_t *epd, uint8_t partial) { + uint16_t i = 0; + if(partial) { + while(i < sizeof(DES_EPD_PANEL_SELECTION_PART)) { + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &DES_EPD_PANEL_SELECTION_PART[i + 1], DES_EPD_PANEL_SELECTION_PART[i] + 1)); + i += DES_EPD_PANEL_SELECTION_PART[i] + 2; + } + } + else { + while(i < sizeof(DES_EPD_PANEL_SELECTION)) { + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &DES_EPD_PANEL_SELECTION[i + 1], DES_EPD_PANEL_SELECTION[i] + 1)); + i += DES_EPD_PANEL_SELECTION[i] + 2; + } + } + + return DES_EPD_OK; +} + +des_epd_ret_t des_epd_init(des_epd_t *epd, uint8_t partial) { + DES_EPD_ERROR_CHECK(_des_epd_hardware_reset(epd)); + DES_EPD_ERROR_CHECK(_des_epd_init_seq(epd, partial)); + if(partial) { + DES_EPD_ERROR_CHECK(_des_epd_upload_lut(epd)); + } + DES_EPD_ERROR_CHECK(_des_epd_power_up(epd)); + + uint8_t data[2756]; + memset(data, 0xFF, 2756); + + uint8_t cmd = 0x10; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, data, 2756)); + + memset(data, 0xFF, 2756); + + cmd = 0x13; + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, data, 2756)); + + cmd = 0x12; // Refresh!! + DES_EPD_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &cmd, 0x01)); + DES_EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + + DES_EPD_ERROR_CHECK(_des_epd_power_down(epd)); return DES_EPD_OK; } \ No newline at end of file diff --git a/lib/gdew0213m21/gdew0213m21_epd.h b/lib/gdew0213m21/gdew0213m21_epd.h index 74483b8..7f8cdd2 100644 --- a/lib/gdew0213m21/gdew0213m21_epd.h +++ b/lib/gdew0213m21/gdew0213m21_epd.h @@ -3,6 +3,17 @@ #include #define DES_EPD_PANEL_SELECTION gdew0213m21_init_sequence +#define DES_EPD_PANEL_SELECTION_PART gdew0213m21_init_sequence_part + +#if DES_EPD_PANEL_SELECTION == gdew0213m21_init_sequence + +#define DES_EPD_LUT_VCOM gdew0213m21_lut_vcom +#define DES_EPD_LUT_WW gdew0213m21_lut_ww +#define DES_EPD_LUT_BW gdew0213m21_lut_bw +#define DES_EPD_LUT_WB gdew0213m21_lut_wb +#define DES_EPD_LUT_BB gdew0213m21_lut_bb + +#endif typedef enum { DES_EPD_OK, @@ -13,7 +24,7 @@ typedef struct { des_epd_ret_t (*write_cmd_cb)(void *handle, uint8_t *cmd, uint8_t len); des_epd_ret_t (*write_data_cb)(void *handle, uint8_t *data, uint16_t len); des_epd_ret_t (*reset_cb)(void *handle); - des_epd_ret_t (*check_busy_cb)(void *handle); + des_epd_ret_t (*poll_busy_cb)(void *handle); } des_epd_cb_t; typedef struct { @@ -23,6 +34,6 @@ typedef struct { #define DES_EPD_ERROR_CHECK(x) if(x != DES_EPD_OK) return DES_EPD_ERROR -des_epd_ret_t des_epd_init(des_epd_t *epd); +des_epd_ret_t des_epd_init(des_epd_t *epd, uint8_t partial); #endif \ No newline at end of file diff --git a/main.c b/main.c index 2798ba6..d654e00 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,29 @@ #include #include "gdew0213m21_epd.h" +#include "des_epd_impl.h" + +des_epd_t g_epd = { + .cb = { + .reset_cb = _des_epd_reset, + .poll_busy_cb = _des_epd_poll_busy, + .write_cmd_cb = _des_epd_write_cmd, + .write_data_cb = _des_epd_write_data + }, + .user_data = NULL +}; int main() { - printf("Hello World!\n"); + printf("Starting EPD example app!\n"); + + g_epd.user_data = _des_epd_impl_init(); + if(g_epd.user_data == NULL) return -1; + + des_epd_ret_t ret = des_epd_init(&g_epd, 0); + if(ret == DES_EPD_OK) { + printf("EPD init OK!\n"); + } + + _des_epd_impl_deinit(g_epd.user_data); return 0; } \ No newline at end of file