Compare commits
9 Commits
ee058f9d78
...
master
Author | SHA1 | Date |
---|---|---|
imi415 | 77b9c803d8 | |
imi415 | 67d4ac4491 | |
imi415 | 39a56c97c7 | |
imi415 | 4c31481910 | |
imi415 | 4d9faf27b7 | |
imi415 | 435a1379d3 | |
imi415 | 8abed07c23 | |
imi415 | b60a55be73 | |
imi415 | 59c7cbd276 |
|
@ -55,9 +55,13 @@ 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.
|
||||
|
||||
![A figure explaining the layout of the panel](assets/panel_organization.png)
|
||||
* 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)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
143
depg0213_epd.c
143
depg0213_epd.c
|
@ -9,10 +9,7 @@ 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, 0x18, 0x80, // Set built-in temperature sensor
|
||||
0x01, 0x22, 0xB1, // Load LUT 1
|
||||
0x00, 0x20 // Load LUT 2
|
||||
0x01, 0x3B, 0x0D // Frame setting 2
|
||||
};
|
||||
|
||||
uint8_t depg0213_dke_lut_full[] = {
|
||||
|
@ -52,6 +49,17 @@ 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;
|
||||
}
|
||||
|
@ -67,37 +75,34 @@ depg0213_ret_t _depg0213_init_seq(depg0213_epd_t *epd) {
|
|||
return DEPG0213_OK;
|
||||
}
|
||||
|
||||
#if(!DEPG0213_LUT_OTP)
|
||||
|
||||
depg0213_ret_t _depg0213_load_lut(depg0213_epd_t *epd) {
|
||||
#if(!DEPG0213_LUT_OTP)
|
||||
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_cmd_cb(epd->user_data, DEPG0213_LUT_FULL_SELECTION, 70));
|
||||
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
|
||||
|
||||
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));
|
||||
|
||||
// SW Reset
|
||||
// Software reset follows hardware reset
|
||||
DEPG0213_ERROR_CHECK(_depg0213_hardware_reset(epd));
|
||||
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));
|
||||
|
||||
#if(!DEPG0213_LUT_OTP)
|
||||
// Write LUT
|
||||
DEPG0213_ERROR_CHECK(_depg0213_load_lut(epd));
|
||||
#endif
|
||||
|
||||
epd->deep_sleep = 0;
|
||||
|
||||
return DEPG0213_OK;
|
||||
}
|
||||
|
@ -117,49 +122,62 @@ 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) {
|
||||
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) {
|
||||
|
||||
if(epd->deep_sleep) {
|
||||
DEPG0213_ERROR_CHECK(depg0213_epd_init(epd));
|
||||
}
|
||||
|
||||
uint8_t command[5] = { 0x4E, 0x0C, 0x4F, 0xD3, 0x00 };
|
||||
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 };
|
||||
|
||||
switch(epd->direction) {
|
||||
case DEPG0213_VERTICAL_INVERSE:
|
||||
// X->0x00
|
||||
command[1] = 0x00;
|
||||
command[1] = x_start / 8;
|
||||
|
||||
// Y->0x00
|
||||
command[3] = 0x00;
|
||||
command[3] = y_start;
|
||||
break;
|
||||
case DEPG0213_HORIZONTAL:
|
||||
case DEPG0213_HORIZONTAL: // X-Y inversed
|
||||
// X->0x00
|
||||
command[1] = 0x00;
|
||||
command[1] = y_start / 8;
|
||||
|
||||
// NO CHANGE FOR Y AXIS
|
||||
// Y->0xD3
|
||||
command[3] = 0xD3 - x_start;
|
||||
break;
|
||||
case DEPG0213_HORIZONTAL_INVERSE:
|
||||
// NO CHANGE FOR X AXIS
|
||||
case DEPG0213_HORIZONTAL_INVERSE: // X-Y inversed
|
||||
// X->0x0C
|
||||
command[1] = 0x0C - y_start / 8;
|
||||
|
||||
// Y-> 0x00
|
||||
command[3] = 0x00;
|
||||
command[3] = x_start;
|
||||
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
|
||||
|
||||
DEPG0213_ERROR_CHECK(depg0213_epd_update(epd));
|
||||
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));
|
||||
}
|
||||
|
||||
return DEPG0213_OK;
|
||||
}
|
||||
|
@ -175,42 +193,69 @@ depg0213_ret_t depg0213_epd_deepsleep(depg0213_epd_t *epd) {
|
|||
return DEPG0213_OK;
|
||||
}
|
||||
|
||||
depg0213_ret_t depg0213_epd_direction(depg0213_epd_t *epd, depg0213_direction_t direction) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Default mode: VERTICAL, X: 0x0C->0x00, Y: 0xD3->0x00, AM-|Y-|X-
|
||||
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_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_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] = 0x00;
|
||||
cmd_ram_x_address[2] = 0x0C;
|
||||
cmd_ram_x_address[1] = x_start / 8;
|
||||
cmd_ram_x_address[2] = x_end / 8;
|
||||
|
||||
// START: 0x00, END: 0xD3
|
||||
cmd_ram_y_address[1] = 0x00;
|
||||
cmd_ram_y_address[3] = 0xD3;
|
||||
cmd_ram_y_address[1] = y_start;
|
||||
cmd_ram_y_address[3] = y_end;
|
||||
|
||||
// AM-|Y+|X+
|
||||
cmd_data_entry[1] = 0x03;
|
||||
break;
|
||||
case DEPG0213_HORIZONTAL:
|
||||
// START: 0x00, END: 0x0C
|
||||
cmd_ram_x_address[1] = 0x00;
|
||||
cmd_ram_x_address[2] = 0x0C;
|
||||
cmd_ram_x_address[1] = y_start / 8;
|
||||
cmd_ram_x_address[2] = y_end / 8;
|
||||
|
||||
// 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] = 0x00;
|
||||
cmd_ram_y_address[3] = 0xD3;
|
||||
cmd_ram_y_address[1] = x_start;
|
||||
cmd_ram_y_address[3] = x_end;
|
||||
|
||||
// AM+|Y+|X-
|
||||
cmd_data_entry[1] = 0x06;
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
#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)
|
||||
|
||||
|
@ -44,8 +47,10 @@ 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);
|
||||
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_deepsleep(depg0213_epd_t *epd);
|
||||
depg0213_ret_t depg0213_epd_direction(depg0213_epd_t *epd, depg0213_direction_t direction);
|
||||
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);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue