224 lines
6.5 KiB
C
224 lines
6.5 KiB
C
/*
|
|
* Visual tests for framebuffer modes that are not commonly used by the high-level API.
|
|
* Currently, includes 2 pixel per byte (ppB) with static origin color and 8ppB with static origin
|
|
* color.
|
|
*
|
|
* After running this, you should see two identical test images, with a "ladder" of black triangles
|
|
* next to a black rectangle with a ladder of white triangles on it.
|
|
*/
|
|
|
|
#include <esp_heap_caps.h>
|
|
#include <esp_log.h>
|
|
#include <esp_sleep.h>
|
|
#include <esp_timer.h>
|
|
#include <esp_types.h>
|
|
#include <esp_assert.h>
|
|
#include <freertos/FreeRTOS.h>
|
|
#include <freertos/task.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <epdiy.h>
|
|
|
|
#include "epd_display.h"
|
|
#include "sdkconfig.h"
|
|
|
|
#define WAVEFORM EPD_BUILTIN_WAVEFORM
|
|
|
|
// choose the default demo board depending on the architecture
|
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
|
#define DEMO_BOARD epd_board_v6
|
|
#elif defined(CONFIG_IDF_TARGET_ESP32S3)
|
|
#define DEMO_BOARD epd_board_v7
|
|
#endif
|
|
|
|
// Singular framebuffer to use for all of the tests.
|
|
// Allocated for 2ppB, the least compact that we test here.
|
|
uint8_t* framebuffer;
|
|
int fb_size;
|
|
|
|
static inline void checkError(enum EpdDrawError err) {
|
|
if (err != EPD_DRAW_SUCCESS) {
|
|
ESP_LOGE("demo", "draw error: %X", err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears the screen to white and resets the framebuffer.
|
|
*/
|
|
void clear() {
|
|
epd_poweron();
|
|
epd_clear();
|
|
epd_poweroff();
|
|
memset(framebuffer, 0xFF, fb_size);
|
|
}
|
|
|
|
/**
|
|
* Clear an example area on the screen to white, does not reset the framebuffer.
|
|
*/
|
|
void clear_area(EpdRect clear_area) {
|
|
epd_poweron();
|
|
epd_clear_area(clear_area);
|
|
epd_poweroff();
|
|
memset(framebuffer, 0xFF, fb_size);
|
|
}
|
|
|
|
/**
|
|
* Draw triangles at varying alignments into the framebuffer in 8ppB mode.
|
|
* start_line, start_column specify the start position.
|
|
* The bits that belong to a triangle are flipped, i.e., it is drawn at the
|
|
* inverse color to the background it is drawn onto.
|
|
*/
|
|
void draw_8bpp_triangles(int start_line, int start_column) {
|
|
start_column /= 8;
|
|
int line_bytes = epd_width() / 8;
|
|
|
|
for (int align = 0; align < 16; align++) {
|
|
for (int height = 0; height < 16; height++) {
|
|
for (int len = 0; len <= height; len++) {
|
|
int line = (start_line + 16 * align + height);
|
|
int column = align + len;
|
|
uint8_t* line_address = framebuffer + (line_bytes * line);
|
|
*(line_address + start_column + column / 8) ^= 1 << (column % 8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draw triangles at varying alignments into the framebuffer in 2ppB mode.
|
|
* start_line, start_column specify the start position.
|
|
* color specifies the color to draw in.
|
|
*/
|
|
void draw_2bpp_triangles(int start_line, int start_column, uint8_t color) {
|
|
int height = 16;
|
|
|
|
for (int align = 0; align < 16; align++) {
|
|
int x0 = start_column + align;
|
|
int y0 = start_line + height * align;
|
|
int x1 = x0;
|
|
int y1 = y0 + height - 1;
|
|
int x2 = x0 + height - 1;
|
|
int y2 = y0 + height - 1;
|
|
|
|
epd_fill_triangle(x0, y0, x1, y1, x2, y2, color, framebuffer);
|
|
}
|
|
}
|
|
|
|
void test_8ppB() {
|
|
EpdRect area = epd_full_screen();
|
|
enum EpdDrawMode mode;
|
|
|
|
// bytes in a line in 8ppB mode
|
|
int line_bytes = epd_width() / 8;
|
|
|
|
int start_line = 100;
|
|
|
|
// draw differently aligned black triangles to check for uniformity
|
|
draw_8bpp_triangles(start_line, 80);
|
|
|
|
int black_start_column = 160;
|
|
|
|
// draw a black area
|
|
for (int line = 0; line < 300; line++) {
|
|
uint8_t* line_address = framebuffer + (line_bytes * (start_line + line));
|
|
memset(line_address + black_start_column / 8, 0, 32);
|
|
}
|
|
|
|
// update the display. In the first update, white pixels are no-opps,
|
|
// in the second update, black pixels are no-ops.
|
|
epd_poweron();
|
|
mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_WHITE;
|
|
checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2));
|
|
epd_poweroff();
|
|
|
|
// draw white triangles on the black background
|
|
draw_8bpp_triangles(start_line, black_start_column + 16);
|
|
|
|
epd_poweron();
|
|
mode = MODE_PACKING_8PPB | MODE_DU | PREVIOUSLY_BLACK;
|
|
checkError(epd_draw_base(area, framebuffer, area, mode, 25, NULL, NULL, &epdiy_ED047TC2));
|
|
epd_poweroff();
|
|
}
|
|
|
|
void test_2ppB() {
|
|
EpdRect area = epd_full_screen();
|
|
enum EpdDrawMode mode;
|
|
int start_column = 500;
|
|
int start_line = 100;
|
|
|
|
// draw differently aligned black triangles to check for uniformity
|
|
draw_2bpp_triangles(start_line, start_column, 0);
|
|
|
|
int black_start_column = start_column + 60;
|
|
|
|
// draw a black area
|
|
EpdRect black_area = { .x = black_start_column, .y = 100, .width = 256, .height = 300 };
|
|
epd_fill_rect(black_area, 0, framebuffer);
|
|
|
|
// Do not overdraw the 8ppB image
|
|
uint8_t* drawn_columns = malloc(epd_width() / 2);
|
|
assert(drawn_columns != NULL);
|
|
memset(drawn_columns, 0, epd_width() / 2);
|
|
memset(drawn_columns + start_column / 2, 255, (epd_width() - start_column) / 2);
|
|
|
|
// update the display. In the first update, white pixels are no-opps,
|
|
// in the second update, black pixels are no-ops.
|
|
epd_poweron();
|
|
mode = MODE_PACKING_2PPB | MODE_DU | PREVIOUSLY_WHITE;
|
|
checkError(
|
|
epd_draw_base(area, framebuffer, area, mode, 25, NULL, drawn_columns, &epdiy_ED047TC2)
|
|
);
|
|
epd_poweroff();
|
|
|
|
// draw white triangles on the black background
|
|
draw_2bpp_triangles(start_line, black_start_column + 16, 255);
|
|
|
|
epd_poweron();
|
|
mode = MODE_PACKING_2PPB | MODE_DU | PREVIOUSLY_BLACK;
|
|
checkError(
|
|
epd_draw_base(area, framebuffer, area, mode, 25, NULL, drawn_columns, &epdiy_ED047TC2)
|
|
);
|
|
epd_poweroff();
|
|
|
|
free(drawn_columns);
|
|
}
|
|
|
|
void app_main() {
|
|
epd_init(&DEMO_BOARD, &ED060XC3, EPD_OPTIONS_DEFAULT);
|
|
|
|
// Set VCOM for boards that allow to set this in software (in mV).
|
|
// This will print an error if unsupported. In this case,
|
|
// set VCOM using the hardware potentiometer and delete this line.
|
|
epd_set_vcom(2100);
|
|
|
|
epd_set_lcd_pixel_clock_MHz(10);
|
|
|
|
fb_size = epd_width() * epd_height() / 2;
|
|
framebuffer = heap_caps_aligned_alloc(16, fb_size, MALLOC_CAP_SPIRAM);
|
|
|
|
clear();
|
|
|
|
test_8ppB();
|
|
|
|
memset(framebuffer, 0xFF, fb_size);
|
|
|
|
test_2ppB();
|
|
|
|
// Clear at different positions
|
|
EpdRect area = { .x = 100, .y = epd_height() - 200, .width = 80, .height = 100 };
|
|
clear_area(area);
|
|
area.width += 1;
|
|
area.x += 100;
|
|
clear_area(area);
|
|
area.x += 101;
|
|
clear_area(area);
|
|
|
|
printf("going to sleep...\n");
|
|
epd_deinit();
|
|
esp_deep_sleep_start();
|
|
}
|