Compare commits

..

No commits in common. "master" and "ee058f9d781c88ea75227afd3b2f0dd83f5fbeb8" have entirely different histories.

4 changed files with 53 additions and 107 deletions

View File

@ -55,13 +55,9 @@ depg0213_ret_t _epd_write_data_cb(void *handle, uint8_t *data, uint16_t len) {
## Notes:
* Pixels of this panel is organized in the similar way as SSD1306, but has 212 columns and 13 pages.
Pixels of this panel is organized in the similar way as SSD1306, but has 212 columns and 13 pages.
Therefore, when `HORIZONTAL` or `HORIZONTAL_INVERSE` modes are selected, the frame needs to be
transferred as pages. However, this controller chip has the function which can change the
self-increment direction of the internal pointer, so `VERTICAL` modes are not affected anyway.
* In `REVERSE` modes, the scanning direction of pixels in one byte are also inversed, therefore,
Shift operation needs to be different for both `HORIZONTAL_REVERSE` and `VERTICAL_REVERSE` directions.
![A figure explaining the layout of the panel](assets/panel_organization.png)
![A figure explaining pixel organization](assets/pixel_organization.png)
![A figure explaining the layout of the panel](assets/panel_organization.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

View File

@ -9,7 +9,10 @@ uint8_t depg0213_dke_init_sequence[] = {
0x01, 0x03, 0x17, // Gate voltage settings
0x03, 0x04, 0x41, 0xAC, 0x32, // Source voltage settings
0x01, 0x3A, 0x02, // Frame setting 1
0x01, 0x3B, 0x0D // Frame setting 2
0x01, 0x3B, 0x0D, // Frame setting 2
0x01, 0x18, 0x80, // Set built-in temperature sensor
0x01, 0x22, 0xB1, // Load LUT 1
0x00, 0x20 // Load LUT 2
};
uint8_t depg0213_dke_lut_full[] = {
@ -49,17 +52,6 @@ depg0213_ret_t _depg0213_software_reset(depg0213_epd_t *epd) {
uint8_t sw_reset_cmd = 0x12; // SW RST
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &sw_reset_cmd, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
return DEPG0213_OK;
}
depg0213_ret_t _depg0213_hardware_reset(depg0213_epd_t *epd) {
DEPG0213_ERROR_CHECK(epd->cb.reset_cb(epd->user_data));
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
epd->deep_sleep = 0;
return DEPG0213_OK;
}
@ -75,34 +67,37 @@ depg0213_ret_t _depg0213_init_seq(depg0213_epd_t *epd) {
return DEPG0213_OK;
}
depg0213_ret_t _depg0213_load_lut(depg0213_epd_t *epd) {
#if(!DEPG0213_LUT_OTP)
depg0213_ret_t _depg0213_load_lut(depg0213_epd_t *epd) {
uint8_t lut_command = 0x32;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &lut_command, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, DEPG0213_LUT_FULL_SELECTION, 70));
#else
uint8_t lut_command[5] = { 0x18, 0x80, 0x22, 0xB1, 0x20 };
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, lut_command, 0x02));
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &lut_command[2], 0x02));
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &lut_command[4], 0x01));
// The end of the sequence is load LUT from OTP memory.
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
#endif
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, DEPG0213_LUT_FULL_SELECTION, 70));
return DEPG0213_OK;
}
#endif
depg0213_ret_t depg0213_epd_init(depg0213_epd_t *epd) {
// HW Reset
DEPG0213_ERROR_CHECK(epd->cb.reset_cb(epd->user_data));
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
// Software reset follows hardware reset
DEPG0213_ERROR_CHECK(_depg0213_hardware_reset(epd));
// SW Reset
DEPG0213_ERROR_CHECK(_depg0213_software_reset(epd));
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
DEPG0213_ERROR_CHECK(depg0213_epd_direction(epd, DEPG0213_VERTICAL));
// Send initialization chants
DEPG0213_ERROR_CHECK(_depg0213_init_seq(epd));
DEPG0213_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data));
// Write LUT
#if(!DEPG0213_LUT_OTP)
DEPG0213_ERROR_CHECK(_depg0213_load_lut(epd));
#endif
epd->deep_sleep = 0;
return DEPG0213_OK;
}
@ -122,62 +117,49 @@ depg0213_ret_t depg0213_epd_update(depg0213_epd_t *epd) {
return DEPG0213_OK;
}
depg0213_ret_t depg0213_epd_load(depg0213_epd_t *epd, uint8_t *bw_image, uint8_t *red_image,
uint16_t x_start, uint16_t x_end, uint16_t y_start, uint16_t y_end) {
depg0213_ret_t depg0213_epd_load(depg0213_epd_t *epd, uint8_t *bw_image, uint8_t *red_image) {
if(epd->deep_sleep) {
DEPG0213_ERROR_CHECK(depg0213_epd_init(epd));
}
DEPG0213_ERROR_CHECK(depg0213_epd_window(epd, epd->direction, x_start, x_end, y_start, y_end));
uint8_t command[5] = { 0x4E, 0x0C - x_start / 8, 0x4F, 0xD3 - y_start, 0x00 };
uint8_t command[5] = { 0x4E, 0x0C, 0x4F, 0xD3, 0x00 };
switch(epd->direction) {
case DEPG0213_VERTICAL_INVERSE:
// X->0x00
command[1] = x_start / 8;
command[1] = 0x00;
// Y->0x00
command[3] = y_start;
command[3] = 0x00;
break;
case DEPG0213_HORIZONTAL: // X-Y inversed
case DEPG0213_HORIZONTAL:
// X->0x00
command[1] = y_start / 8;
command[1] = 0x00;
// Y->0xD3
command[3] = 0xD3 - x_start;
// NO CHANGE FOR Y AXIS
break;
case DEPG0213_HORIZONTAL_INVERSE: // X-Y inversed
// X->0x0C
command[1] = 0x0C - y_start / 8;
case DEPG0213_HORIZONTAL_INVERSE:
// NO CHANGE FOR X AXIS
// Y-> 0x00
command[3] = x_start;
command[3] = 0x00;
break;
case DEPG0213_VERTICAL:
default:
break;
}
uint16_t transfer_bytes = (y_end - y_start + 1) * (x_end - x_start + 1) / 8;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, command, 0x02));
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &command[2], 0x03));
uint8_t wr_command = 0x24;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &wr_command, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, bw_image, 2756)); // 104 / 8 * 212
wr_command = 0x26;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &wr_command, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, red_image, 2756)); // 104 / 8 * 212
uint8_t wr_command;
if(bw_image != NULL) {
wr_command = 0x24;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &wr_command, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, bw_image, transfer_bytes));
}
if(red_image != NULL) {
wr_command = 0x26;
DEPG0213_ERROR_CHECK(epd->cb.write_cmd_cb(epd->user_data, &wr_command, 0x01));
DEPG0213_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, red_image, transfer_bytes));
}
DEPG0213_ERROR_CHECK(depg0213_epd_update(epd));
return DEPG0213_OK;
}
@ -193,69 +175,42 @@ depg0213_ret_t depg0213_epd_deepsleep(depg0213_epd_t *epd) {
return DEPG0213_OK;
}
depg0213_ret_t depg0213_epd_window(depg0213_epd_t *epd, depg0213_direction_t direction,
uint16_t x_start, uint16_t x_end, uint16_t y_start, uint16_t y_end) {
if(epd->deep_sleep) {
DEPG0213_ERROR_CHECK(depg0213_epd_init(epd));
}
// Sanity checks
if(x_end < x_start || y_end < y_start) return DEPG0213_ERROR;
if(direction == DEPG0213_VERTICAL || direction == DEPG0213_VERTICAL_INVERSE) {
if(x_end > 103 || y_end > 211) {
return DEPG0213_ERROR;
}
if(x_start % 8 != 0) return DEPG0213_ERROR;
if((x_end + 1) % 8 != 0) return DEPG0213_ERROR;
}
else {
if(x_end > 211 || y_end > 103) {
return DEPG0213_ERROR;
}
if(y_start % 8 != 0) return DEPG0213_ERROR;
if((y_end + 1) % 8 != 0) return DEPG0213_ERROR;
}
depg0213_ret_t depg0213_epd_direction(depg0213_epd_t *epd, depg0213_direction_t direction) {
// Default mode: VERTICAL, X: 0x0C->0x00, Y: 0xD3->0x00, AM-|Y-|X-
uint8_t cmd_ram_x_address[] = { 0x44, 0x0C - x_start / 8, 0x0C - x_end / 8 }; // CMD, START, END
uint8_t cmd_ram_y_address[] = { 0x45, 0xD3 - y_start, 0x00, 0xD3 - y_end, 0x00 }; // CMD, START_L, START_H, END_L, END_H
uint8_t cmd_ram_x_address[] = { 0x44, 0x0C, 0x00 }; // CMD, START, END
uint8_t cmd_ram_y_address[] = { 0x45, 0xD3, 0x00, 0x00, 0x00 }; // CMD, START_L, START_H, END_L, END_H
uint8_t cmd_data_entry[] = { 0x11, 0x00 }; // CMD, SCAN_X->DESC, SCAN_Y->DESC, AM->0 [2:0]=AM|Y+|X+
switch(direction) {
case DEPG0213_VERTICAL_INVERSE:
// START: 0x00, END: 0x0C
cmd_ram_x_address[1] = x_start / 8;
cmd_ram_x_address[2] = x_end / 8;
cmd_ram_x_address[1] = 0x00;
cmd_ram_x_address[2] = 0x0C;
// START: 0x00, END: 0xD3
cmd_ram_y_address[1] = y_start;
cmd_ram_y_address[3] = y_end;
cmd_ram_y_address[1] = 0x00;
cmd_ram_y_address[3] = 0xD3;
// AM-|Y+|X+
cmd_data_entry[1] = 0x03;
break;
case DEPG0213_HORIZONTAL:
// START: 0x00, END: 0x0C
cmd_ram_x_address[1] = y_start / 8;
cmd_ram_x_address[2] = y_end / 8;
cmd_ram_x_address[1] = 0x00;
cmd_ram_x_address[2] = 0x0C;
// NO CHANGE FOR Y AXIS
cmd_ram_y_address[1] = 0xD3 - x_start;
cmd_ram_y_address[3] = 0xD3 - x_end;
// AM+|Y-|X+
cmd_data_entry[1] = 0x05;
break;
case DEPG0213_HORIZONTAL_INVERSE:
// NO CHANGE FOR X AXIS
cmd_ram_x_address[1] = 0x0C - y_start / 8;
cmd_ram_x_address[2] = 0x0C - y_end / 8;
// START: 0x00, END: 0xD3
cmd_ram_y_address[1] = x_start;
cmd_ram_y_address[3] = x_end;
cmd_ram_y_address[1] = 0x00;
cmd_ram_y_address[3] = 0xD3;
// AM+|Y+|X-
cmd_data_entry[1] = 0x06;

View File

@ -2,13 +2,10 @@
#define __depg0213_EPD_H
#include <stdint.h>
#include <stddef.h>
#define DEPG0213_PANEL_SELECTION depg0213_dke_init_sequence
#ifndef DEPG0213_LUT_OTP
#define DEPG0213_LUT_OTP 0
#endif
#if(!DEPG0213_LUT_OTP)
@ -47,10 +44,8 @@ typedef struct {
depg0213_ret_t depg0213_epd_init(depg0213_epd_t *epd);
depg0213_ret_t depg0213_epd_update(depg0213_epd_t *epd);
depg0213_ret_t depg0213_epd_load(depg0213_epd_t *epd, uint8_t *bw_image, uint8_t *red_image,
uint16_t x_start, uint16_t x_end, uint16_t y_start, uint16_t y_end);
depg0213_ret_t depg0213_epd_load(depg0213_epd_t *epd, uint8_t *bw_image, uint8_t *red_image);
depg0213_ret_t depg0213_epd_deepsleep(depg0213_epd_t *epd);
depg0213_ret_t depg0213_epd_window(depg0213_epd_t *epd, depg0213_direction_t direction,
uint16_t x_start, uint16_t x_end, uint16_t y_start, uint16_t y_end);
depg0213_ret_t depg0213_epd_direction(depg0213_epd_t *epd, depg0213_direction_t direction);
#endif