/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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(); }