working by line
This commit is contained in:
84
DISPLAY_REFRESH.md
Normal file
84
DISPLAY_REFRESH.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Display Refresh Mechanism
|
||||||
|
|
||||||
|
This document explains the dual-core architecture used to handle USB keyboard input and e-Paper display updates efficiently on the Raspberry Pi Pico.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
The system utilizes both cores of the RP2040 to ensure that the slow refresh rate of the e-Paper display does not block or lag the USB keyboard input.
|
||||||
|
|
||||||
|
### Core 0: Input Handling (Producer)
|
||||||
|
|
||||||
|
* **Role:** Handles USB Host tasks, processes keyboard reports, and manages the text buffer.
|
||||||
|
* **Behavior:**
|
||||||
|
1. Receives key presses via TinyUSB callbacks.
|
||||||
|
2. Updates the local text buffer (`g_entry_list`).
|
||||||
|
3. Triggers a display update via `send_display_update()`.
|
||||||
|
* **Non-Blocking Transmission:**
|
||||||
|
* When an update is needed, Core 0 allocates a `DisplayMessage` on the heap.
|
||||||
|
* It attempts to push the message pointer to the Multicore FIFO.
|
||||||
|
* **Critical Optimization:** It uses `multicore_fifo_wready()` to check if the FIFO has space.
|
||||||
|
* **If Ready:** Pushes the message.
|
||||||
|
* **If Full:** Immediately frees the message and skips the update. This ensures Core 0 never waits for Core 1, keeping typing responsive.
|
||||||
|
|
||||||
|
### Core 1: Display Driver (Consumer)
|
||||||
|
|
||||||
|
* **Role:** Manages the e-Paper hardware and performs the actual drawing operations.
|
||||||
|
* **Behavior:**
|
||||||
|
1. Initializes the e-Paper display.
|
||||||
|
2. Waits for messages in the Multicore FIFO.
|
||||||
|
* **Smart Refresh (Accumulation):**
|
||||||
|
* When Core 1 wakes up to process a message, it first checks if *more* messages have arrived while it was sleeping or busy.
|
||||||
|
* **FIFO Draining:** It loops through the FIFO, popping and freeing all intermediate messages, keeping only the **latest** one.
|
||||||
|
* This ensures that if the user types "Hello World" quickly, Core 1 might skip rendering "Hello " and jump straight to rendering "Hello World", preventing a queue of obsolete updates from slowing down the display.
|
||||||
|
|
||||||
|
## Data Flow Diagram
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant User
|
||||||
|
participant Core0 as Core 0 (USB/Input)
|
||||||
|
participant FIFO as Multicore FIFO
|
||||||
|
participant Core1 as Core 1 (Display)
|
||||||
|
|
||||||
|
User->>Core0: Types 'A'
|
||||||
|
Core0->>FIFO: Push Msg('A')
|
||||||
|
|
||||||
|
User->>Core0: Types 'B'
|
||||||
|
Core0->>FIFO: Push Msg('AB')
|
||||||
|
|
||||||
|
Note over Core1: Busy refreshing...
|
||||||
|
|
||||||
|
User->>Core0: Types 'C'
|
||||||
|
alt FIFO is Full
|
||||||
|
Core0->>Core0: Drop Msg('ABC') (Free memory)
|
||||||
|
else FIFO has space
|
||||||
|
Core0->>FIFO: Push Msg('ABC')
|
||||||
|
end
|
||||||
|
|
||||||
|
Core1->>FIFO: Pop Msg('A')
|
||||||
|
Note over Core1: Checks FIFO for newer msgs
|
||||||
|
FIFO->>Core1: Msg('AB') exists
|
||||||
|
Core1->>Core1: Free Msg('A'), use Msg('AB')
|
||||||
|
|
||||||
|
FIFO->>Core1: Msg('ABC') exists
|
||||||
|
Core1->>Core1: Free Msg('AB'), use Msg('ABC')
|
||||||
|
|
||||||
|
Core1->>Display: Render('ABC')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Functions
|
||||||
|
|
||||||
|
* **`send_display_update(const char *entry, bool finish_line)`**:
|
||||||
|
* Allocates memory.
|
||||||
|
* Checks FIFO status.
|
||||||
|
* Pushes or drops message.
|
||||||
|
* **`core1_display_init()`**:
|
||||||
|
* Main loop for Core 1.
|
||||||
|
* Drains FIFO to find the latest message.
|
||||||
|
* Calls e-Paper drawing functions.
|
||||||
|
|
||||||
|
## Memory Management
|
||||||
|
|
||||||
|
* Messages are `malloc`'d by Core 0.
|
||||||
|
* Messages are `free`'d by Core 1 (after processing or dropping).
|
||||||
|
* If Core 0 drops a message because the FIFO is full, it `free`s it immediately.
|
||||||
@@ -144,13 +144,19 @@ void send_display_update(const char *entry, bool finish_line) {
|
|||||||
if (g_force_full_refresh) {
|
if (g_force_full_refresh) {
|
||||||
printf("[Core 0] Forcing full refresh this update\n");
|
printf("[Core 0] Forcing full refresh this update\n");
|
||||||
g_force_full_refresh = false; // Reset flag after setting it in message
|
g_force_full_refresh = false; // Reset flag after setting it in message
|
||||||
}
|
|
||||||
|
|
||||||
// Setup partial refresh region (text area at top of display)
|
// Full text area refresh
|
||||||
msg->xstart = 0;
|
msg->xstart = 0;
|
||||||
msg->ystart = 50; // Start below the header
|
msg->ystart = 50; // Start below the header
|
||||||
msg->xend = 800; // Full width
|
msg->xend = 800; // Full width
|
||||||
msg->yend = 480; // Full height from header onwards
|
msg->yend = 480; // Full height from header onwards
|
||||||
|
} else {
|
||||||
|
// Partial refresh of ONLY the current line
|
||||||
|
msg->xstart = 0;
|
||||||
|
msg->ystart = 50 + (g_entry_list.count * 25);
|
||||||
|
msg->xend = 800;
|
||||||
|
msg->yend = msg->ystart + 25;
|
||||||
|
}
|
||||||
|
|
||||||
// Send message pointer to core 1 if FIFO has space
|
// Send message pointer to core 1 if FIFO has space
|
||||||
if (multicore_fifo_wready()) {
|
if (multicore_fifo_wready()) {
|
||||||
@@ -207,11 +213,15 @@ void core1_display_init() {
|
|||||||
|
|
||||||
Paint_SelectImage(g_epd_image);
|
Paint_SelectImage(g_epd_image);
|
||||||
Paint_Clear(WHITE);
|
Paint_Clear(WHITE);
|
||||||
|
|
||||||
|
// Calculate offset for partial refresh
|
||||||
|
UBYTE *image_ptr = g_epd_image + (msg->ystart * (EPD_7IN5B_V2_WIDTH / 8));
|
||||||
|
|
||||||
if(!msg->use_partial){
|
if(!msg->use_partial){
|
||||||
// for loop 5 times display white partial
|
// for loop 5 times display white partial
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(i = 0; i < 1; i++){
|
for(i = 0; i < 1; i++){
|
||||||
EPD_7IN5B_V2_Display_Partial(g_epd_image, msg->xstart, msg->ystart, msg->xend, msg->yend);
|
EPD_7IN5B_V2_Display_Partial(image_ptr, msg->xstart, msg->ystart, msg->xend, msg->yend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +231,7 @@ void core1_display_init() {
|
|||||||
int start_index = msg->entries.count > 10 ? msg->entries.count - 10 : 0;
|
int start_index = msg->entries.count > 10 ? msg->entries.count - 10 : 0;
|
||||||
for (int i = start_index; i < msg->entries.count; i++) {
|
for (int i = start_index; i < msg->entries.count; i++) {
|
||||||
if (y_pos + 25 < 480) { //Don't draw beyond screen
|
if (y_pos + 25 < 480) { //Don't draw beyond screen
|
||||||
Paint_DrawString_EN(20, i*25, msg->entries.entries[i], &Font16, WHITE, BLACK);
|
Paint_DrawString_EN(20, 50 + i*25, msg->entries.entries[i], &Font16, WHITE, BLACK);
|
||||||
y_pos += 25; // Space between entries
|
y_pos += 25; // Space between entries
|
||||||
} else {
|
} else {
|
||||||
printf("Skipping entry to avoid overflow\n");
|
printf("Skipping entry to avoid overflow\n");
|
||||||
@@ -229,7 +239,7 @@ void core1_display_init() {
|
|||||||
}
|
}
|
||||||
// Use partial or full refresh
|
// Use partial or full refresh
|
||||||
printf("[Core 1] Using partial refresh\n");
|
printf("[Core 1] Using partial refresh\n");
|
||||||
EPD_7IN5B_V2_Display_Partial(g_epd_image, msg->xstart, msg->ystart, msg->xend, msg->yend);
|
EPD_7IN5B_V2_Display_Partial(image_ptr, msg->xstart, msg->ystart, msg->xend, msg->yend);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the message (it was allocated by core 0)
|
// Free the message (it was allocated by core 0)
|
||||||
@@ -418,8 +428,7 @@ static void process_kbd_report(hid_keyboard_report_t const *report) {
|
|||||||
// Update OLED
|
// Update OLED
|
||||||
if (g_display_manager) g_display_manager->refresh(g_input_buffer, g_last_echo);
|
if (g_display_manager) g_display_manager->refresh(g_input_buffer, g_last_echo);
|
||||||
|
|
||||||
// Update e-Paper ONLY if space (per user request)
|
// Update e-Paper on every keystroke
|
||||||
if (ch == ' ') {
|
|
||||||
send_display_update(g_input_buffer, false);
|
send_display_update(g_input_buffer, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,7 +436,6 @@ static void process_kbd_report(hid_keyboard_report_t const *report) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
prev_report = *report;
|
prev_report = *report;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user