Skip to content

Commit 2591847

Browse files
authored
Refactor and test LUT functions
Refactors waveform lookup calculations to be more maintainable and adds tests to avoid regressions. In the process, line masking is simplified and ordering problems in 2ppB mode are fixed (this should fix #284). 8ppB now supports both PREVIOUSLY_BLACK and PREVIOUSLY_WHITE.
1 parent 560b642 commit 2591847

26 files changed

+3591
-377
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
format-check:
77
runs-on: ubuntu-latest
88
container:
9-
image: "espressif/idf:release-v5.2"
9+
image: "espressif/idf:release-v5.3"
1010
steps:
1111
- uses: actions/checkout@v4
1212
- run: |

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ set(app_sources "src/epdiy.c"
1010
"src/output_common/lut.S"
1111
"src/output_common/line_queue.c"
1212
"src/output_common/render_context.c"
13+
"src/output_common/render_method.c"
1314
"src/font.c"
1415
"src/displays.c"
1516
"src/diff.S"

examples/fb_mode_test/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
cmake_minimum_required(VERSION 3.10.0)
2+
set(EXTRA_COMPONENT_DIRS "../../")
3+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
4+
project(firmware)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
set(app_sources "main.c")
2+
3+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
4+
idf_component_register(SRCS ${app_sources} REQUIRES epdiy)

examples/fb_mode_test/main/main.c

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* Visual tests for framebuffer modes that are not commonly used by the high-level API.
3+
* Currently, includes 2 pixel per byte (ppB) with static origin color and 8ppB with static origin
4+
* color.
5+
*
6+
* After running this, you should see two identical test images, with a "ladder" of black triangles
7+
* next to a black rectangle with a ladder of white triangles on it.
8+
*/
9+
10+
#include <esp_heap_caps.h>
11+
#include <esp_log.h>
12+
#include <esp_sleep.h>
13+
#include <esp_timer.h>
14+
#include <esp_types.h>
15+
#include <esp_assert.h>
16+
#include <freertos/FreeRTOS.h>
17+
#include <freertos/task.h>
18+
#include <inttypes.h>
19+
#include <stdint.h>
20+
#include <stdio.h>
21+
#include <stdlib.h>
22+
#include <string.h>
23+
24+
#include <epdiy.h>
25+
26+
#include "sdkconfig.h"
27+
28+
#define WAVEFORM EPD_BUILTIN_WAVEFORM
29+
30+
// choose the default demo board depending on the architecture
31+
#ifdef CONFIG_IDF_TARGET_ESP32
32+
#define DEMO_BOARD epd_board_v6
33+
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
34+
#define DEMO_BOARD epd_board_v7
35+
#endif
36+
37+
// Singular framebuffer to use for all of the tests.
38+
// Allocated for 2ppB, the least compact that we test here.
39+
uint8_t* framebuffer;
40+
int fb_size;
41+
42+
static inline void checkError(enum EpdDrawError err) {
43+
if (err != EPD_DRAW_SUCCESS) {
44+
ESP_LOGE("demo", "draw error: %X", err);
45+
}
46+
}
47+
48+
/**
49+
* Clears the screen to white and resets the framebuffer.
50+
*/
51+
void clear() {
52+
epd_poweron();
53+
epd_clear();
54+
epd_poweroff();
55+
memset(framebuffer, 0xFF, fb_size);
56+
}
57+
58+
/**
59+
* Draw triangles at varying alignments into the framebuffer in 8ppB mode.
60+
* start_line, start_column specify the start position.
61+
* The bits that belong to a triangle are flipped, i.e., it is drawn at the
62+
* inverse color to the background it is drawn onto.
63+
*/
64+
void draw_8bpp_triangles(int start_line, int start_column) {
65+
start_column /= 8;
66+
int line_bytes = epd_width() / 8;
67+
68+
for (int align = 0; align < 16; align++) {
69+
for (int height = 0; height < 16; height++) {
70+
for (int len = 0; len <= height; len++) {
71+
int line = (start_line + 16 * align + height);
72+
int column = align + len;
73+
uint8_t* line_address = framebuffer + (line_bytes * line);
74+
*(line_address + start_column + column / 8) ^= 1 << (column % 8);
75+
}
76+
}
77+
}
78+
}
79+
80+
/**
81+
* Draw triangles at varying alignments into the framebuffer in 2ppB mode.
82+
* start_line, start_column specify the start position.
83+
* color specifies the color to draw in.
84+
*/
85+
void draw_2bpp_triangles(int start_line, int start_column, uint8_t color) {
86+
int height = 16;
87+
88+
for (int align = 0; align < 16; align++) {
89+
int x0 = start_column + align;
90+
int y0 = start_line + height * align;
91+
int x1 = x0;
92+
int y1 = y0 + height - 1;
93+
int x2 = x0 + height - 1;
94+
int y2 = y0 + height - 1;
95+
96+
epd_fill_triangle(x0, y0, x1, y1, x2, y2, color, framebuffer);
97+
}
98+
}
99+
100+
void test_8ppB() {
101+
EpdRect area = epd_full_screen();
102+
enum EpdDrawMode mode;
103+
104+
// bytes in a line in 8ppB mode
105+
int line_bytes = epd_width() / 8;
106+
107+
int start_line = 100;
108+
109+
// draw differently aligned black triangles to check for uniformity
110+
draw_8bpp_triangles(start_line, 80);
111+
112+
int black_start_column = 160;
113+
114+
// draw a black area
115+
for (int line = 0; line < 300; line++) {
116+
uint8_t* line_address = framebuffer + (line_bytes * (start_line + line));
117+
memset(line_address + black_start_column / 8, 0, 32);
118+
}
119+
120+
// update the display. In the first update, white pixels are no-opps,
121+
// in the second update, black pixels are no-ops.
122+
epd_poweron();
123+
mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_WHITE;
124+
checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2));
125+
epd_poweroff();
126+
127+
// draw white triangles on the black background
128+
draw_8bpp_triangles(start_line, black_start_column + 16);
129+
130+
epd_poweron();
131+
mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_BLACK;
132+
checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2));
133+
epd_poweroff();
134+
}
135+
136+
void test_2ppB() {
137+
EpdRect area = epd_full_screen();
138+
enum EpdDrawMode mode;
139+
int start_column = 500;
140+
int start_line = 100;
141+
142+
// draw differently aligned black triangles to check for uniformity
143+
draw_2bpp_triangles(start_line, start_column, 0);
144+
145+
int black_start_column = start_column + 60;
146+
147+
// draw a black area
148+
EpdRect black_area = { .x = black_start_column, .y = 100, .width = 256, .height = 300 };
149+
epd_fill_rect(black_area, 0, framebuffer);
150+
151+
// Do not overdraw the 8ppB image
152+
uint8_t* drawn_columns = malloc(epd_width() / 2);
153+
assert(drawn_columns != NULL);
154+
memset(drawn_columns, 0, epd_width() / 2);
155+
memset(drawn_columns + start_column / 2, 255, (epd_width() - start_column) / 2);
156+
157+
// update the display. In the first update, white pixels are no-opps,
158+
// in the second update, black pixels are no-ops.
159+
epd_poweron();
160+
mode = MODE_PACKING_2PPB | MODE_DU | PREVIOUSLY_WHITE;
161+
checkError(
162+
epd_draw_base(area, framebuffer, area, mode, 25, NULL, drawn_columns, &epdiy_ED047TC2)
163+
);
164+
epd_poweroff();
165+
166+
// draw white triangles on the black background
167+
draw_2bpp_triangles(start_line, black_start_column + 16, 255);
168+
169+
epd_poweron();
170+
mode = MODE_PACKING_2PPB | MODE_DU | PREVIOUSLY_BLACK;
171+
checkError(
172+
epd_draw_base(area, framebuffer, area, mode, 25, NULL, drawn_columns, &epdiy_ED047TC2)
173+
);
174+
epd_poweroff();
175+
176+
free(drawn_columns);
177+
}
178+
179+
void app_main() {
180+
epd_init(&DEMO_BOARD, &ED060XC3, EPD_OPTIONS_DEFAULT);
181+
182+
// Set VCOM for boards that allow to set this in software (in mV).
183+
// This will print an error if unsupported. In this case,
184+
// set VCOM using the hardware potentiometer and delete this line.
185+
epd_set_vcom(2100);
186+
187+
epd_set_lcd_pixel_clock_MHz(10);
188+
189+
fb_size = epd_width() * epd_height() / 2;
190+
framebuffer = heap_caps_aligned_alloc(16, fb_size, MALLOC_CAP_SPIRAM);
191+
192+
clear();
193+
194+
test_8ppB();
195+
196+
memset(framebuffer, 0xFF, fb_size);
197+
198+
test_2ppB();
199+
200+
printf("going to sleep...\n");
201+
epd_deinit();
202+
esp_deep_sleep_start();
203+
}

examples/fb_mode_test/sdkconfig.defaults

Whitespace-only changes.

0 commit comments

Comments
 (0)