Added WFH0420CZ35 driver.

master
imi415 1 year ago
commit 75095ea8cb
Signed by: imi415
GPG Key ID: 17F01E106F9F5E0A

2
.gitignore vendored

@ -0,0 +1,2 @@
/cmake-build-*

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.10)
project(epd-spi)
set(EPD_SOURCES
"src/epd_common.c"
"src/epd_wfh0420cz35.c"
)
set(EPD_INCLUDES
"include"
)
add_library(${PROJECT_NAME} ${EPD_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ${EPD_INCLUDES})

@ -0,0 +1,27 @@
#ifndef EPD_COMMON_H
#define EPD_COMMON_H
#include <stddef.h>
#include <stdint.h>
typedef enum { EPD_OK = 0, EPD_FAIL } epd_ret_t;
typedef struct {
epd_ret_t (*write_command_cb)(void *handle, uint8_t *command, uint32_t len);
epd_ret_t (*write_data_cb)(void *handle, uint8_t *data, uint32_t len);
epd_ret_t (*reset_cb)(void *handle);
epd_ret_t (*poll_busy_cb)(void *handle);
epd_ret_t (*delay_cb)(void *handle, uint32_t msec);
} epd_cb_t;
typedef struct {
uint32_t x_start;
uint32_t x_end;
uint32_t y_start;
uint32_t y_end;
} epd_coord_t;
#define EPD_ASSERT(x) if(!(x)) for(;;) { /* ABORT. */}
#define EPD_ERROR_CHECK(x) if(x != EPD_OK) return EPD_FAIL
#endif

@ -0,0 +1,8 @@
#ifndef EPD_PRIVATE_H
#define EPD_PRIVATE_H
#include "epd_common.h"
epd_ret_t epd_common_execute_sequence(epd_cb_t *cb, void *user_data, uint8_t *seq, uint32_t seq_len);
#endif

@ -0,0 +1,13 @@
#ifndef EPD_WFH0420CZ35_H
#define EPD_WFH0420CZ35_h
#include "epd_common.h"
typedef struct {
void *user_data;
epd_cb_t cb;
} epd_wfh0420_t;
epd_ret_t epd_wfh0420_upload(epd_wfh0420_t *epd, epd_coord_t *coord, uint8_t *red_data, uint8_t *bw_data);
#endif

@ -0,0 +1,23 @@
#include "epd_common.h"
/**
* @brief Execute command sequence.
* Sequence format: 1 byte parameter length, 1 byte command, [length] bytes params.
* Parameter length does not include command itself.
* @param cb epd_cb_t callback
* @param user_data user pointer
* @param seq sequence array
* @param seq_len sequence length
* @return epd_ret_t
*/
epd_ret_t epd_common_execute_sequence(epd_cb_t *cb, void *user_data, uint8_t *seq, uint32_t seq_len) {
EPD_ASSERT(cb->write_command_cb != NULL);
uint32_t i = 0;
while(i < seq_len) {
EPD_ERROR_CHECK(cb->write_command_cb(user_data, &seq[i + 1], seq[i] + 1));
i += seq[i] + 2;
}
return EPD_OK;
}

@ -0,0 +1,118 @@
#include "epd_wfh0420cz35.h"
#include "epd_private.h"
#define DTM1 0x10
#define DTM2 0x13
static uint8_t epd_wfh0420_init_sequence[] = {
0x03, 0x06, 0x17, 0x17, 0x17,
};
static epd_ret_t epd_wfh0420_init(epd_wfh0420_t *epd) {
EPD_ASSERT(epd);
EPD_ASSERT(epd->cb.reset_cb);
EPD_ASSERT(epd->cb.write_command_cb);
EPD_ASSERT(epd->cb.poll_busy_cb);
EPD_ERROR_CHECK(epd->cb.reset_cb(epd->user_data));
EPD_ERROR_CHECK(epd_common_execute_sequence(&epd->cb, epd->user_data, epd_wfh0420_init_sequence,
sizeof(epd_wfh0420_init_sequence)));
// Issue power on command
uint8_t tx_buf[2] = {0x04, 0x00};
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01));
EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
// Issue panel setting command, LUT from OTP.
tx_buf[0] = 0x00;
tx_buf[1] = 0x0F;
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x02));
return EPD_OK;
}
static epd_ret_t epd_wfh0420_partial(epd_wfh0420_t *epd, epd_coord_t *coord) {
EPD_ASSERT(epd);
EPD_ASSERT(epd->cb.write_command_cb);
// Partial In
uint8_t tx_buf[10] = {0x91, 0x00};
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 1));
// Partial window
tx_buf[0] = 0x90;
tx_buf[1] = (coord->x_start >> 8U) & 0x01U;
tx_buf[2] = coord->x_start & 0xF8U; // Bank number, last 3 LSBs must be zero.
tx_buf[3] = (coord->x_end >> 8U) & 0x01U;
tx_buf[4] = coord->x_end & 0xF8U; // Same as above.
tx_buf[5] = (coord->y_start >> 8U) & 0x01U;
tx_buf[6] = coord->y_start & 0xFFU;
tx_buf[7] = (coord->y_end >> 8U) & 0x01U;
tx_buf[8] = coord->y_end & 0xFFU;
tx_buf[9] = 0x01; // Only scan partial area.
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 10));
return EPD_OK;
}
static epd_ret_t epd_wfh0420_dtm(epd_wfh0420_t *epd, uint8_t dtm_num, uint8_t *data, uint32_t len) {
EPD_ASSERT(epd);
EPD_ASSERT(epd->cb.write_command_cb);
uint8_t tx_buf[2] = {dtm_num};
// Issue DTM1 or DTM2 command
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01));
// Send data
EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, data, len));
// Issue DSP command.
tx_buf[0] = 0x11;
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01));
return EPD_OK;
}
static epd_ret_t epd_wfh0420_update_and_sleep(epd_wfh0420_t *epd) {
EPD_ASSERT(epd);
EPD_ASSERT(epd->cb.write_command_cb);
EPD_ASSERT(epd->cb.delay_cb);
// Issue DRF command
uint8_t tx_buf[2] = {0x12, 0x00};
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01));
// Delay 100ms and wait for busy
EPD_ERROR_CHECK(epd->cb.delay_cb(epd->user_data, 100));
EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
// Issue POF command
tx_buf[0] = 0x02;
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01));
EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
// Issue DSLP command
tx_buf[0] = 0x07;
tx_buf[1] = 0xA5;
EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x02));
return EPD_OK;
}
epd_ret_t epd_wfh0420_upload(epd_wfh0420_t *epd, epd_coord_t *coord, uint8_t *red_data, uint8_t *bw_data) {
EPD_ERROR_CHECK(epd_wfh0420_init(epd));
EPD_ERROR_CHECK(epd_wfh0420_partial(epd, coord));
uint8_t bank_start = coord->x_start / 8; // Wrap to bank
uint8_t bank_end = coord->x_end / 8; // Wrap to bank
uint32_t tx_len = (bank_end - bank_start + 1) * (coord->y_end - coord->y_start + 1);
EPD_ERROR_CHECK(epd_wfh0420_dtm(epd, DTM1, bw_data, tx_len));
EPD_ERROR_CHECK(epd_wfh0420_dtm(epd, DTM2, red_data, tx_len));
EPD_ERROR_CHECK(epd_wfh0420_update_and_sleep(epd));
return EPD_OK;
}
Loading…
Cancel
Save