New driver.

This commit is contained in:
imi415 2021-01-22 19:20:37 +00:00
parent 3a9a9415d7
commit 1ce0c9944e
8 changed files with 420 additions and 27 deletions

View File

@ -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})
add_executable(epd_lvgl ${SOURCES})
target_link_libraries(epd_lvgl gpiod)

167
des_epd_impl.c Normal file
View File

@ -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;
}

32
include/config.h Normal file
View File

@ -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

33
include/des_epd_impl.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef __DES_EPD_IMPL_H
#define __DES_EPD_IMPL_H
// SPIDEV
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
// LIBGPIOD
#include <gpiod.h>
// 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

View File

@ -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,
};

View File

@ -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;
}

View File

@ -3,6 +3,17 @@
#include <stdint.h>
#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

23
main.c
View File

@ -1,8 +1,29 @@
#include <stdio.h>
#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;
}