Skip to content

Commit

Permalink
add unit test for 8ppB, previously white mode
Browse files Browse the repository at this point in the history
  • Loading branch information
vroland committed Aug 4, 2024
1 parent 0f4b73c commit 295160e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 48 deletions.
18 changes: 15 additions & 3 deletions examples/dragon/main/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include "ED097TC2.h"
#include "dragon.h"
#include "epd_highlevel.h"
#include "epdiy.h"
Expand All @@ -27,14 +28,25 @@ void idf_loop() {

epd_copy_to_framebuffer(dragon_area, dragon_data, epd_hl_get_framebuffer(&hl));

enum EpdDrawError _err = epd_hl_update_screen(&hl, MODE_GC16, temperature);
epd_draw_base(
dragon_area,
epd_hl_get_framebuffer(&hl),
dragon_area,
MODE_GC16 | MODE_PACKING_2PPB | PREVIOUSLY_WHITE,
temperature,
NULL,
NULL,
&ed097tc2
);

// enum EpdDrawError _err = epd_hl_update_screen(&hl, MODE_GC16, temperature);
epd_poweroff();

vTaskDelay(1000);
vTaskDelay(100000);
}

void idf_setup() {
epd_init(&DEMO_BOARD, &ED097TC2, EPD_LUT_64K);
epd_init(&DEMO_BOARD, &ED097TC2, EPD_LUT_1K);
epd_set_vcom(1560);
hl = epd_hl_init(EPD_BUILTIN_WAVEFORM);
}
Expand Down
4 changes: 2 additions & 2 deletions examples/weather/main/ArduinoJson.h
Original file line number Diff line number Diff line change
Expand Up @@ -4546,8 +4546,8 @@ struct BoundedReader<TSource*, typename enable_if<IsCharOrVoid<TSource>::value>:
public:
explicit BoundedReader(const void* ptr, size_t len)
: IteratorReader<const char*>(
reinterpret_cast<const char*>(ptr), reinterpret_cast<const char*>(ptr) + len
) {}
reinterpret_cast<const char*>(ptr), reinterpret_cast<const char*>(ptr) + len
) {}
};
template <typename TArray>
struct Reader<ElementProxy<TArray>, void> : Reader<char*, void> {
Expand Down
13 changes: 6 additions & 7 deletions examples/www-image/main/jpg-render.c
Original file line number Diff line number Diff line change
Expand Up @@ -367,15 +367,14 @@ static void http_post(void) {
* either in URL or as host and path parameters.
* FIX: Uncommenting cert_pem restarts even if providing the right certificate
*/
esp_http_client_config_t config
= {.url = IMG_URL,
.event_handler = _http_event_handler,
.buffer_size = HTTP_RECEIVE_BUFFER_SIZE,
.disable_auto_redirect = false,
esp_http_client_config_t config = { .url = IMG_URL,
.event_handler = _http_event_handler,
.buffer_size = HTTP_RECEIVE_BUFFER_SIZE,
.disable_auto_redirect = false,
#if VALIDATE_SSL_CERTIFICATE == true
.cert_pem = (char*)server_cert_pem_start
.cert_pem = (char*)server_cert_pem_start
#endif
};
};
esp_http_client_handle_t client = esp_http_client_init(&config);

#if DEBUG_VERBOSE
Expand Down
11 changes: 5 additions & 6 deletions examples/www-image/main/jpgdec-render.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,13 @@ static void http_post(void) {
* NOTE: All the configuration parameters for http_client must be specified
* either in URL or as host and path parameters.
*/
esp_http_client_config_t config
= {.url = IMG_URL,
esp_http_client_config_t config = { .url = IMG_URL,
#if VALIDATE_SSL_CERTIFICATE == true
.cert_pem = (char*)server_cert_pem_start,
.cert_pem = (char*)server_cert_pem_start,
#endif
.disable_auto_redirect = false,
.event_handler = _http_event_handler,
.buffer_size = HTTP_RECEIVE_BUFFER_SIZE };
.disable_auto_redirect = false,
.event_handler = _http_event_handler,
.buffer_size = HTTP_RECEIVE_BUFFER_SIZE };

esp_http_client_handle_t client = esp_http_client_init(&config);

Expand Down
55 changes: 34 additions & 21 deletions src/output_common/lut.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
* Since we disable the PSRAM workaround here for performance reasons.
*/

/* Python script for generating the 1bpp lookup table:
/* Python script for generating the 8ppB lookup table:
* for i in range(256):
number = 0;
for b in range(8):
if not (i & (b << 1)):
number |= 1 << (2*b)
print ('0x%04x,'%number)
*/
const uint32_t lut_1bpp_black[256] = {
const uint32_t lut_8ppB_black[256] = {
0x5555, 0x5554, 0x5551, 0x5550, 0x5545, 0x5544, 0x5541, 0x5540, 0x5515, 0x5514, 0x5511, 0x5510,
0x5505, 0x5504, 0x5501, 0x5500, 0x5455, 0x5454, 0x5451, 0x5450, 0x5445, 0x5444, 0x5441, 0x5440,
0x5415, 0x5414, 0x5411, 0x5410, 0x5405, 0x5404, 0x5401, 0x5400, 0x5155, 0x5154, 0x5151, 0x5150,
Expand Down Expand Up @@ -92,18 +92,31 @@ nibble_shift_buffer_right(uint8_t* buf, uint32_t len) {
}
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_1bpp(
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_8ppB(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* lut, uint32_t epd_width
) {
uint32_t* wide_epd_input = (uint32_t*)epd_input;
uint8_t* data_ptr = (uint8_t*)line_data;
uint32_t* lut_32 = (uint32_t*)lut;
// this is reversed for little-endian, but this is later compensated
// through the output peripheral.
for (uint32_t j = 0; j < epd_width / 16; j++) {
for (int j = 0; j < epd_width / 16; j++) {
uint8_t v1 = *(data_ptr++);
uint8_t v2 = *(data_ptr++);
wide_epd_input[j] = (lut_32[v1] << 16) | lut_32[v2];
wide_epd_input[j] = (lut_32[v2] << 16) | lut_32[v1];
}

// Account for missing line end if epd_width is not divisible by 16.
// We assume divisibility by 4.
for (int j = 0; j < (epd_width % 16) / 4; j++) {
uint8_t nibble = *data_ptr;
if (j % 2 == 1) {
nibble = nibble >> 4;
data_ptr++;
} else {
nibble = nibble & 0xF;
}
epd_input[(epd_width / 16) * 4 + j] = lut_32[nibble];
}
}

Expand Down Expand Up @@ -179,7 +192,7 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_1ppB_64k(
}
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_lut_64k(
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_2ppB_lut_64k(
const uint32_t* line_data, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
const uint16_t* line_data_16 = (const uint16_t*)line_data;
Expand All @@ -192,7 +205,7 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_lut_64k(
/**
* Look up 4 pixels in a 1K LUT with fixed "from" value.
*/
__attribute__((optimize("O3"))) static uint8_t lookup_pixels_4bpp_1k(
__attribute__((optimize("O3"))) static uint8_t lookup_pixels_2ppB_1k(
uint16_t in, const uint8_t* conversion_lut, uint8_t from
) {
uint8_t v;
Expand All @@ -211,10 +224,10 @@ __attribute__((optimize("O3"))) static uint8_t lookup_pixels_4bpp_1k(
}

/**
* Calculate EPD input for a 4bpp buffer, but with a difference image LUT.
* Calculate EPD input for a 2ppB buffer, but with a difference image LUT.
* This is used for small-LUT mode.
*/
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut(
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_2ppB_1k_lut(
const uint32_t* ld,
uint8_t* epd_input,
const uint8_t* conversion_lut,
Expand All @@ -224,20 +237,20 @@ __attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut(
const uint16_t* line_data_16 = (const uint16_t*)ld;

for (uint32_t j = 0; j < epd_width / 4; j++) {
epd_input[j] = lookup_pixels_4bpp_1k(*(line_data_16++), conversion_lut, from);
epd_input[j] = lookup_pixels_2ppB_1k(*(line_data_16++), conversion_lut, from);
};
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut_white(
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_2ppB_1k_lut_white(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
calc_epd_input_4bpp_1k_lut(ld, epd_input, conversion_lut, 0xF, epd_width);
calc_epd_input_2ppB_1k_lut(ld, epd_input, conversion_lut, 0xF, epd_width);
}

__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_4bpp_1k_lut_black(
__attribute__((optimize("O3"))) void IRAM_ATTR calc_epd_input_2ppB_1k_lut_black(
const uint32_t* ld, uint8_t* epd_input, const uint8_t* conversion_lut, uint32_t epd_width
) {
calc_epd_input_4bpp_1k_lut(ld, epd_input, conversion_lut, 0x0, epd_width);
calc_epd_input_2ppB_1k_lut(ld, epd_input, conversion_lut, 0x0, epd_width);
}

///////////////////////////// Calculate Lookup Tables
Expand Down Expand Up @@ -378,7 +391,7 @@ static void build_2ppB_lut_64k_from_15(uint8_t* lut, const EpdWaveformPhases* ph
}

static void build_8ppB_lut_256b_from_15(uint8_t* lut, const EpdWaveformPhases* phases, int frame) {
memcpy(lut, lut_1bpp_black, sizeof(lut_1bpp_black));
memcpy(lut, lut_8ppB_black, sizeof(lut_8ppB_black));
}

LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) {
Expand All @@ -401,32 +414,32 @@ LutFunctionPair find_lut_functions(enum EpdDrawMode mode, uint32_t lut_size) {
if (lut_size >= 1 << 16) {
if (mode & PREVIOUSLY_WHITE) {
pair.build_func = &build_2ppB_lut_64k_from_15;
pair.lookup_func = &calc_epd_input_4bpp_lut_64k;
pair.lookup_func = &calc_epd_input_2ppB_lut_64k;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
pair.build_func = &build_2ppB_lut_64k_from_0;
pair.lookup_func = &calc_epd_input_4bpp_lut_64k;
pair.lookup_func = &calc_epd_input_2ppB_lut_64k;
return pair;
}
} else if (lut_size >= 1024) {
if (mode & PREVIOUSLY_WHITE) {
pair.build_func = &build_2ppB_lut_1k;
pair.lookup_func = &calc_epd_input_4bpp_1k_lut_white;
pair.lookup_func = &calc_epd_input_2ppB_1k_lut_white;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
pair.build_func = &build_2ppB_lut_1k;
pair.lookup_func = &calc_epd_input_4bpp_1k_lut_black;
pair.lookup_func = &calc_epd_input_2ppB_1k_lut_black;
return pair;
}
}
} else if (mode & MODE_PACKING_8PPB) {
if (lut_size < sizeof(lut_1bpp_black)) {
if (lut_size < sizeof(lut_8ppB_black)) {
return pair;
}

if (mode & PREVIOUSLY_WHITE) {
pair.build_func = &build_8ppB_lut_256b_from_15;
pair.lookup_func = &calc_epd_input_1bpp;
pair.lookup_func = &calc_epd_input_8ppB;
return pair;
} else if (mode & PREVIOUSLY_BLACK) {
// FIXME: to implement
Expand Down
5 changes: 3 additions & 2 deletions src/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ static inline int rounded_display_height() {
*
* don't inline for to ensure availability in tests.
*/
void __attribute__((noinline))
_epd_populate_line_mask(uint8_t* line_mask, const uint8_t* dirty_columns, int mask_len) {
void __attribute__((noinline)) _epd_populate_line_mask(
uint8_t* line_mask, const uint8_t* dirty_columns, int mask_len
) {
if (dirty_columns == NULL) {
memset(line_mask, 0xFF, mask_len);
} else {
Expand Down
42 changes: 35 additions & 7 deletions test/test_lut.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ static const uint8_t result_pattern_2ppB_white[8]
= { 0x00, 0x01, 0x50, 0x55, 0x55, 0x55, 0x00, 0x55 };
static const uint8_t result_pattern_2ppB_black[8]
= { 0xAA, 0xA8, 0x0A, 0x82, 0xAA, 0xAA, 0xAA, 0x20 };
static const uint8_t result_pattern_8ppB[32]
= { 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55,
0x55, 0x54, 0x55, 0x55, 0x54, 0x44, 0x11, 0x44, 0x11, 0x11, 0x44,
0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x15, 0x55 };

typedef void (*lut_func_t)(const uint32_t*, uint8_t*, const uint8_t*, uint32_t);
static uint8_t waveform_phases[16][4];
Expand All @@ -43,7 +47,7 @@ typedef struct {
uint8_t* expected_line;
uint8_t* lut;
/// Ratio of input bytes to output bytes
int in_out_ratio;
float in_out_ratio;
int example_len_px;
} LutTestBuffers;

Expand Down Expand Up @@ -71,8 +75,10 @@ static void lut_test_buffers_fill(LutTestBuffers* bufs, const uint8_t* result_pa
// initialize test and check patterns
for (int i = 0; i < bufs->example_len_px; i++) {
bufs->line_data[i] = input_data_pattern[i % sizeof(input_data_pattern)];
bufs->expected_line[i / bufs->in_out_ratio]
= result_pattern[(i / bufs->in_out_ratio) % result_pattern_len];
}

for (int i = 0; i < bufs->example_len_px / bufs->in_out_ratio; i++) {
bufs->expected_line[i] = result_pattern[i % result_pattern_len];
}

memset(bufs->lut, 0, 1 << 16);
Expand All @@ -86,12 +92,12 @@ static void lut_test_buffers_fill(LutTestBuffers* bufs, const uint8_t* result_pa
* Allocates and populates buffers for LUT tests.
*/
static void lut_test_buffers_init(
LutTestBuffers* bufs, int example_len_px, const uint8_t* result_pattern, int in_out_ratio
LutTestBuffers* bufs, int example_len_px, const uint8_t* result_pattern, float in_out_ratio
) {
uint32_t caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
bufs->line_data = heap_caps_aligned_alloc(16, example_len_px, caps);
bufs->result_line = heap_caps_aligned_alloc(16, example_len_px / in_out_ratio, caps);
bufs->expected_line = heap_caps_aligned_alloc(16, example_len_px / in_out_ratio, caps);
bufs->result_line = heap_caps_aligned_alloc(16, (int)(example_len_px / in_out_ratio), caps);
bufs->expected_line = heap_caps_aligned_alloc(16, (int)(example_len_px / in_out_ratio), caps);
bufs->lut = heap_caps_malloc(1 << 16, caps);
bufs->example_len_px = example_len_px;
bufs->in_out_ratio = in_out_ratio;
Expand Down Expand Up @@ -124,6 +130,12 @@ static void IRAM_ATTR test_with_alignments(LutTestBuffers* bufs, lut_func_t lut_
// test combinations of start / end missalignment in four byte steps
for (int start_offset = 0; start_offset <= 16; start_offset += 4) {
for (int end_offset = 0; end_offset <= 16; end_offset += 4) {
/// for 8ppB buffers, we skip 4 byte start offset since an input byte encodes 2 output
/// bytes, there is no way to shift the output by just one byte.
if (bufs->in_out_ratio < 1.0 && (start_offset % 8) == 4) {
continue;
}

int unaligned_len = len - end_offset - start_offset;

memset(bufs->result_line, 0, out_len);
Expand All @@ -141,7 +153,9 @@ static void IRAM_ATTR test_with_alignments(LutTestBuffers* bufs, lut_func_t lut_
uint64_t start = esp_timer_get_time();
for (int i = 0; i < 100; i++) {
lut_func(
(uint32_t*)(bufs->line_data + start_offset * bufs->in_out_ratio / 4),
// We shift the alignment of the input data by four pixels
(uint32_t*)(bufs->line_data + (int)(start_offset * bufs->in_out_ratio / 4)),
// we shift the alignment of the result line by one byte
bufs->result_line + start_offset / 4,
bufs->lut,
unaligned_len
Expand Down Expand Up @@ -235,3 +249,17 @@ TEST_CASE("2ppB lookup LCD, 1k LUT, previously black", "[epdiy,unit,lut]") {

diff_test_buffers_free(&bufs);
}

TEST_CASE("8ppB lookup LCD, 1k LUT, previously white", "[epdiy,unit,lut]") {
LutTestBuffers bufs;
lut_test_buffers_init(&bufs, DEFAULT_EXAMPLE_LEN / 2, result_pattern_8ppB, 0.5);

enum EpdDrawMode mode = MODE_DU | MODE_PACKING_8PPB | PREVIOUSLY_WHITE;
LutFunctionPair func_pair = find_lut_functions(mode, 1 << 10);
TEST_ASSERT_NOT_NULL(func_pair.build_func);
TEST_ASSERT_NOT_NULL(func_pair.lookup_func);
func_pair.build_func(bufs.lut, &test_waveform, 0);
test_with_alignments(&bufs, func_pair.lookup_func);

diff_test_buffers_free(&bufs);
}

0 comments on commit 295160e

Please sign in to comment.