initial game template
This commit is contained in:
613
basic1.cpp
613
basic1.cpp
@@ -3,7 +3,35 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 4.0" TFT ST7796 with Touch Screen and SD Card Demo
|
||||
* ============================================================================
|
||||
* REACTIVE GAME TEMPLATE - Event-Driven Architecture for RP2350
|
||||
* ============================================================================
|
||||
*
|
||||
* This template provides a clean, reactive architecture for building games
|
||||
* and interactive applications on Raspberry Pi Pico with displays.
|
||||
*
|
||||
* KEY FEATURES:
|
||||
* - Event-driven: Display only updates when input is received
|
||||
* - Power efficient: Uses __wfi() to sleep between inputs
|
||||
* - E-ink optimized: Minimizes screen refreshes
|
||||
* - Interrupt-driven: Touch and button handling via interrupts
|
||||
* - Modular: Clear separation of input, game logic, and rendering
|
||||
*
|
||||
* ARCHITECTURE:
|
||||
* 1. Interrupt handlers set flags (kept minimal)
|
||||
* 2. Main loop processes input events
|
||||
* 3. Game logic updates state based on events
|
||||
* 4. Screen refreshes only when changes occur
|
||||
*
|
||||
* HOW TO CREATE YOUR OWN GAME:
|
||||
* ============================================================================
|
||||
* 1. Modify GameState structure with your game variables
|
||||
* 2. Implement game_init() to set initial values
|
||||
* 3. Implement game_update() to handle input and update state
|
||||
* 4. Implement game_draw() to render your game graphics
|
||||
* 5. Adjust GameConfig for your game's needs
|
||||
* 6. The reactive loop and input system work automatically!
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
@@ -26,6 +54,72 @@ bi_decl(bi_program_description("4.0\" TFT ST7796 with Touch and SD Card Demo"));
|
||||
bi_decl(bi_program_version_string("0.1"));
|
||||
bi_decl(bi_program_build_date_string(__DATE__));
|
||||
|
||||
// ============================================================================
|
||||
// INPUT EVENT STRUCTURES
|
||||
// ============================================================================
|
||||
|
||||
// Input event types
|
||||
enum InputType {
|
||||
INPUT_NONE = 0,
|
||||
INPUT_TOUCH_DOWN,
|
||||
INPUT_TOUCH_MOVE,
|
||||
INPUT_TOUCH_UP,
|
||||
INPUT_BUTTON_0,
|
||||
INPUT_BUTTON_1,
|
||||
INPUT_GESTURE
|
||||
};
|
||||
|
||||
// Unified input event structure
|
||||
struct InputEvent {
|
||||
InputType type;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint8_t gesture_code; // For gesture events
|
||||
uint8_t button_id; // For button events
|
||||
uint8_t pressure; // Touch pressure/weight
|
||||
bool valid; // Set to true if event is valid
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// GAME STATE AND CONFIGURATION
|
||||
// ============================================================================
|
||||
|
||||
// Game state - customize this for your game
|
||||
struct GameState {
|
||||
// Drawing game state
|
||||
int16_t last_x;
|
||||
int16_t last_y;
|
||||
bool is_drawing;
|
||||
|
||||
// General game state
|
||||
uint32_t score;
|
||||
bool game_over;
|
||||
uint32_t frame_count;
|
||||
|
||||
// UI state
|
||||
uint8_t progress_value; // Progress bar value (0-100)
|
||||
uint8_t focused_button; // Which button has focus (0 or 1)
|
||||
uint32_t button1_clicks; // Count clicks on button 1
|
||||
uint32_t button2_clicks; // Count clicks on button 2
|
||||
|
||||
// Statistics
|
||||
uint32_t touch_success_count;
|
||||
uint32_t touch_fail_count;
|
||||
};
|
||||
|
||||
// Game configuration - adjust these for your game
|
||||
struct GameConfig {
|
||||
uint32_t touch_debounce_ms; // Touch polling rate
|
||||
uint32_t button_debounce_ms; // Button debounce delay
|
||||
bool enable_gestures; // Enable gesture recognition
|
||||
bool enable_continuous_draw; // Allow continuous drawing while touched
|
||||
bool debug_verbose; // Print debug messages
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// INTERRUPT HANDLERS (Keep these minimal!)
|
||||
// ============================================================================
|
||||
|
||||
// Touch interrupt handling
|
||||
volatile bool touch_interrupt_flag = false;
|
||||
volatile bool touch_event_down = false;
|
||||
@@ -54,42 +148,12 @@ void touch_interrupt_handler(uint gpio, uint32_t events) {
|
||||
// Main loop will handle the actual touch reading
|
||||
touch_interrupt_flag = true;
|
||||
|
||||
// Optional: track which edge triggered (for debugging)
|
||||
// Track which edge triggered (down vs up)
|
||||
if (events & GPIO_IRQ_EDGE_FALL) {
|
||||
touch_event_down = true;
|
||||
printf("Touch DOWN event detected\n");
|
||||
}
|
||||
if (events & GPIO_IRQ_EDGE_RISE) {
|
||||
touch_event_down = false;
|
||||
printf("Touch UP event detected\n");
|
||||
}
|
||||
TouchData touch_data;
|
||||
touch->read_touch(&touch_data);
|
||||
|
||||
|
||||
int16_t x = touch_data.points[0].x;
|
||||
int16_t y = touch_data.points[0].y;
|
||||
uint8_t event = touch_data.points[0].event;
|
||||
uint8_t id = touch_data.points[0].id;
|
||||
uint8_t weight = touch_data.points[0].pressure;
|
||||
uint8_t gesture = touch_data.gesture;
|
||||
|
||||
// Display detailed touch information including weight and gesture
|
||||
printf("Touch: X=%d Y=%d Event=%d ID=%d Weight=%d\n",
|
||||
x, y, event, id, weight);
|
||||
|
||||
// Display gesture if detected (non-zero)
|
||||
if (gesture != 0) {
|
||||
const char* gesture_name = "Unknown";
|
||||
switch(gesture) {
|
||||
case 0x10: gesture_name = "Move Up"; break;
|
||||
case 0x14: gesture_name = "Move Right"; break;
|
||||
case 0x18: gesture_name = "Move Down"; break;
|
||||
case 0x1C: gesture_name = "Move Left"; break;
|
||||
case 0x48: gesture_name = "Zoom In"; break;
|
||||
case 0x49: gesture_name = "Zoom Out"; break;
|
||||
}
|
||||
printf(" Gesture=0x%02X (%s)\n", gesture, gesture_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,12 +174,10 @@ void button_interrupt_handler(uint gpio, uint32_t events) {
|
||||
if (events & GPIO_IRQ_EDGE_FALL) {
|
||||
if (gpio == BUTTON_KEY0_PIN) {
|
||||
button_key0_pressed = true;
|
||||
printf("Button KEY0 pressed\n");
|
||||
}
|
||||
#ifdef BUTTON_KEY1_PIN
|
||||
else if (gpio == BUTTON_KEY1_PIN) {
|
||||
button_key1_pressed = true;
|
||||
printf("Button KEY1 pressed\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -145,6 +207,322 @@ void refresh_screen(const uint8_t *buffer, LowLevelDisplay* display) {
|
||||
display->refresh();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// INPUT PROCESSING
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Get human-readable gesture name
|
||||
*
|
||||
* @param gesture_code Gesture code from touch controller
|
||||
* @return Constant string with gesture name
|
||||
*/
|
||||
const char* get_gesture_name(uint8_t gesture_code) {
|
||||
switch(gesture_code) {
|
||||
case 0x10: return "Move Up";
|
||||
case 0x14: return "Move Right";
|
||||
case 0x18: return "Move Down";
|
||||
case 0x1C: return "Move Left";
|
||||
case 0x48: return "Zoom In";
|
||||
case 0x49: return "Zoom Out";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process touch input and convert to InputEvent
|
||||
*
|
||||
* Reads touch data from controller and creates appropriate InputEvent.
|
||||
* Handles debouncing and filtering internally.
|
||||
*
|
||||
* @param config Game configuration
|
||||
* @param last_time Pointer to last touch time for debouncing
|
||||
* @return InputEvent structure (valid=false if no valid input)
|
||||
*/
|
||||
InputEvent process_touch_input(const GameConfig& config, uint32_t* last_time) {
|
||||
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||
|
||||
// Check if touch interrupt flag is set
|
||||
if (!touch_interrupt_flag) {
|
||||
return event; // No touch event
|
||||
}
|
||||
|
||||
// Don't clear the flag yet - we may still be processing continuous touch
|
||||
|
||||
// Check if touch is active
|
||||
if (!touch_event_down) {
|
||||
// Touch released
|
||||
touch_interrupt_flag = false;
|
||||
event.type = INPUT_TOUCH_UP;
|
||||
event.valid = true;
|
||||
return event;
|
||||
}
|
||||
|
||||
// Touch is down - check debounce timing
|
||||
uint32_t now = to_ms_since_boot(get_absolute_time());
|
||||
if (now - *last_time < config.touch_debounce_ms) {
|
||||
return event; // Too soon, skip
|
||||
}
|
||||
|
||||
// Read touch data
|
||||
TouchData touch_data;
|
||||
if (!touch || !touch->read_touch(&touch_data)) {
|
||||
return event; // Read failed
|
||||
}
|
||||
|
||||
// Populate event structure
|
||||
event.x = touch_data.points[0].x;
|
||||
event.y = touch_data.points[0].y;
|
||||
event.pressure = touch_data.points[0].pressure;
|
||||
event.gesture_code = touch_data.gesture;
|
||||
event.valid = true;
|
||||
|
||||
// Determine event type
|
||||
if (*last_time == 0) {
|
||||
event.type = INPUT_TOUCH_DOWN;
|
||||
} else {
|
||||
event.type = INPUT_TOUCH_MOVE;
|
||||
}
|
||||
|
||||
// Handle gesture events
|
||||
if (config.enable_gestures && touch_data.gesture != 0) {
|
||||
event.type = INPUT_GESTURE;
|
||||
if (config.debug_verbose) {
|
||||
printf("Gesture: 0x%02X (%s)\n", event.gesture_code, get_gesture_name(event.gesture_code));
|
||||
}
|
||||
}
|
||||
|
||||
*last_time = now;
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process button input and convert to InputEvent
|
||||
*
|
||||
* Checks button flags and verifies button state with debouncing.
|
||||
* Clears flags after processing.
|
||||
*
|
||||
* @param config Game configuration
|
||||
* @return InputEvent structure (valid=false if no valid input)
|
||||
*/
|
||||
InputEvent process_button_input(const GameConfig& config) {
|
||||
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||
|
||||
#ifdef BUTTON_KEY0_PIN
|
||||
// Check KEY0
|
||||
if (button_key0_pressed) {
|
||||
button_key0_pressed = false;
|
||||
sleep_ms(config.button_debounce_ms);
|
||||
|
||||
if (gpio_get(BUTTON_KEY0_PIN) == 0) { // Verify still pressed
|
||||
event.type = INPUT_BUTTON_0;
|
||||
event.button_id = 0;
|
||||
event.valid = true;
|
||||
|
||||
if (config.debug_verbose) {
|
||||
printf("Button KEY0 action triggered\n");
|
||||
}
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BUTTON_KEY1_PIN
|
||||
// Check KEY1
|
||||
if (button_key1_pressed) {
|
||||
button_key1_pressed = false;
|
||||
sleep_ms(config.button_debounce_ms);
|
||||
|
||||
if (gpio_get(BUTTON_KEY1_PIN) == 0) { // Verify still pressed
|
||||
event.type = INPUT_BUTTON_1;
|
||||
event.button_id = 1;
|
||||
event.valid = true;
|
||||
|
||||
if (config.debug_verbose) {
|
||||
printf("Button KEY1 action triggered\n");
|
||||
}
|
||||
return event;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// GAME LOGIC (Customize this section for your game!)
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @brief Initialize game state
|
||||
*
|
||||
* Called once at startup to set initial game values.
|
||||
* Customize this for your game.
|
||||
*
|
||||
* @param state Pointer to GameState to initialize
|
||||
*/
|
||||
void game_init(GameState* state) {
|
||||
state->last_x = -1;
|
||||
state->last_y = -1;
|
||||
state->is_drawing = false;
|
||||
state->score = 0;
|
||||
state->game_over = false;
|
||||
state->frame_count = 0;
|
||||
state->progress_value = 50; // Start at 50%
|
||||
state->focused_button = 0; // Start with first button focused
|
||||
state->button1_clicks = 0;
|
||||
state->button2_clicks = 0;
|
||||
state->touch_success_count = 0;
|
||||
state->touch_fail_count = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update game state based on input event
|
||||
*
|
||||
* This is where your game logic goes.
|
||||
* Called whenever an input event occurs.
|
||||
*
|
||||
* @param state Pointer to GameState to update
|
||||
* @param input Input event to process
|
||||
* @param config Game configuration
|
||||
* @param renderer Renderer for drawing operations
|
||||
* @return true if screen needs refresh (drawing occurred)
|
||||
*/
|
||||
bool game_update(GameState* state, const InputEvent& input, const GameConfig& config, LowLevelRenderer* renderer) {
|
||||
bool needs_refresh = false;
|
||||
|
||||
switch (input.type) {
|
||||
case INPUT_TOUCH_DOWN:
|
||||
// Start new drawing stroke
|
||||
state->last_x = input.x;
|
||||
state->last_y = input.y;
|
||||
state->is_drawing = true;
|
||||
state->touch_success_count++;
|
||||
break;
|
||||
|
||||
case INPUT_TOUCH_MOVE:
|
||||
// Continue drawing stroke
|
||||
if (config.enable_continuous_draw && state->is_drawing) {
|
||||
if (state->last_x >= 0 && state->last_y >= 0) {
|
||||
// Draw line from last position
|
||||
renderer->draw_line(state->last_x, state->last_y, input.x, input.y, true);
|
||||
needs_refresh = true;
|
||||
}
|
||||
state->last_x = input.x;
|
||||
state->last_y = input.y;
|
||||
state->touch_success_count++;
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT_TOUCH_UP:
|
||||
// End drawing stroke
|
||||
state->is_drawing = false;
|
||||
state->last_x = -1;
|
||||
state->last_y = -1;
|
||||
needs_refresh = true; // Final refresh to show complete stroke
|
||||
break;
|
||||
|
||||
case INPUT_BUTTON_0:
|
||||
// KEY0: Switch focus between buttons
|
||||
state->focused_button = (state->focused_button == 0) ? 1 : 0;
|
||||
needs_refresh = true;
|
||||
if (config.debug_verbose) {
|
||||
printf("Focus switched to button %d\n", state->focused_button);
|
||||
}
|
||||
break;
|
||||
|
||||
case INPUT_BUTTON_1:
|
||||
// KEY1: Activate the focused button
|
||||
if (state->focused_button == 0) {
|
||||
state->button1_clicks++;
|
||||
if (config.debug_verbose) {
|
||||
printf("Button 1 clicked! Total: %d\n", state->button1_clicks);
|
||||
}
|
||||
} else {
|
||||
state->button2_clicks++;
|
||||
if (config.debug_verbose) {
|
||||
printf("Button 2 clicked! Total: %d\n", state->button2_clicks);
|
||||
}
|
||||
}
|
||||
needs_refresh = true;
|
||||
break;
|
||||
|
||||
case INPUT_GESTURE:
|
||||
// Handle gesture
|
||||
if (config.debug_verbose) {
|
||||
printf("Gesture detected: %s\n", get_gesture_name(input.gesture_code));
|
||||
}
|
||||
// Add gesture-specific actions here
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
state->frame_count++;
|
||||
return needs_refresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw game graphics to screen buffer
|
||||
*
|
||||
* All initial UI drawing operations go here.
|
||||
* Called once at startup to create the initial screen.
|
||||
*
|
||||
* @param state Pointer to current GameState
|
||||
* @param renderer Renderer for drawing primitives
|
||||
* @param gui GUI system for widgets (optional)
|
||||
*/
|
||||
void game_draw(const GameState* state, LowLevelRenderer* renderer, LowLevelGUI* gui) {
|
||||
// Draw main window
|
||||
LowLevelWindow *w1 = gui->draw_new_window(10, 10, V_WIDTH - 20, V_HEIGHT - 20, "Button Game");
|
||||
|
||||
// Draw instructions using text
|
||||
renderer->set_font(&font_5x5_obj);
|
||||
renderer->draw_string(20, 50, "KEY0: Switch Focus", true);
|
||||
renderer->draw_string(20, 65, "KEY1: Click Button", true);
|
||||
|
||||
// Create button labels with click counts
|
||||
char btn1_label[30];
|
||||
snprintf(btn1_label, sizeof(btn1_label), "BTN 1 (%d)", state->button1_clicks);
|
||||
|
||||
char btn2_label[30];
|
||||
snprintf(btn2_label, sizeof(btn2_label), "BTN 2 (%d)", state->button2_clicks);
|
||||
|
||||
// Draw Button 1 using GUI button element
|
||||
// pressed=true shows it's focused/selected
|
||||
gui->draw_button(w1, 10, 90, btn1_label, state->focused_button == 0, true);
|
||||
|
||||
// Draw Button 2 using GUI button element
|
||||
gui->draw_button(w1, 10, 140, btn2_label, state->focused_button == 1, true);
|
||||
|
||||
// Draw status indicators using GUI elements
|
||||
// Show which button is focused
|
||||
if (state->focused_button == 0) {
|
||||
gui->draw_radio_button(w1, 200, 100, "Active", true);
|
||||
} else {
|
||||
gui->draw_radio_button(w1, 200, 100, "Active", false);
|
||||
}
|
||||
|
||||
if (state->focused_button == 1) {
|
||||
gui->draw_radio_button(w1, 200, 150, "Active", true);
|
||||
} else {
|
||||
gui->draw_radio_button(w1, 200, 150, "Active", false);
|
||||
}
|
||||
|
||||
// Show total interactions with a status bar
|
||||
uint32_t total_clicks = state->button1_clicks + state->button2_clicks;
|
||||
int percentage = (total_clicks > 0) ? ((state->button1_clicks * 100) / total_clicks) : 50;
|
||||
|
||||
char total_str[20];
|
||||
snprintf(total_str, sizeof(total_str), "%d", total_clicks);
|
||||
gui->draw_status_bar(w1, 10, 200, 270, "TOTAL CLICKS", "BTN1 vs BTN2 Ratio", percentage, total_str);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MAIN PROGRAM
|
||||
// ============================================================================
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -191,13 +569,26 @@ int main()
|
||||
// Now clear to black for drawing
|
||||
display->clear(false); // Clear to black
|
||||
|
||||
// Initialize renderer and GUI system
|
||||
LowLevelRenderer renderer(bit_buffer, V_WIDTH, V_HEIGHT);
|
||||
renderer.set_font(&font_5x5_obj);
|
||||
LowLevelGUI gui = LowLevelGUI(&renderer, font_BMplain_obj);
|
||||
LowLevelWindow *w1 = gui.draw_new_window(15, 15, V_WIDTH - 30, V_HEIGHT - 30, "Main Window");
|
||||
gui.draw_status_bar(w1, 10, 40, 200,
|
||||
"PANELS", "Weekly Average Charge", 65, "190KWH");
|
||||
gui.draw_circular_gauge(w1, 10, 100 - 10, 200, "SYSTEM EFF.", 68);
|
||||
|
||||
// Initialize game configuration
|
||||
GameConfig config = {
|
||||
.touch_debounce_ms = 10,
|
||||
.button_debounce_ms = 50,
|
||||
.enable_gestures = true,
|
||||
.enable_continuous_draw = true,
|
||||
.debug_verbose = false
|
||||
};
|
||||
|
||||
// Initialize game state
|
||||
GameState game_state;
|
||||
game_init(&game_state);
|
||||
|
||||
// Draw initial game graphics
|
||||
game_draw(&game_state, &renderer, &gui);
|
||||
|
||||
// Refresh the screen with the rendered GUI
|
||||
refresh_screen(bit_buffer, display);
|
||||
@@ -260,131 +651,47 @@ int main()
|
||||
// printf("SD Card initialization failed or no card present\n");
|
||||
// }
|
||||
|
||||
// Main loop - handle touch events
|
||||
int last_x = -1, last_y = -1;
|
||||
// ========================================================================
|
||||
// REACTIVE GAME LOOP
|
||||
// ========================================================================
|
||||
// The loop sleeps until an interrupt occurs, then:
|
||||
// 1. Process input (button or touch)
|
||||
// 2. Update game state based on input
|
||||
// 3. Redraw only if game_update() indicates changes occurred
|
||||
// This is ideal for e-ink displays (minimal refreshes) and power efficiency
|
||||
// ========================================================================
|
||||
|
||||
// Touch debouncing
|
||||
uint32_t last_touch_time = 0;
|
||||
const uint32_t debounce_ms = 10; // Poll touch every 10ms (100 times per second)
|
||||
bool was_touched = false;
|
||||
int touch_fail_count = 0;
|
||||
int touch_success_count = 0;
|
||||
|
||||
while (1) {
|
||||
// Sleep until interrupt wakes us up (very power efficient!)
|
||||
// Te(); // Wait For Event - CPU sleeps until interrupt or evenurs
|
||||
__wfi(); // Wait For Interrupt - CPU sleeps until any interrupt
|
||||
__wfi(); // Wait For Interrupt - CPU sleeps until any interrupt occurs
|
||||
|
||||
#ifdef BUTTON_KEY0_PIN
|
||||
// Handle button presses with debouncing
|
||||
if (button_key0_pressed) {
|
||||
button_key0_pressed = false;
|
||||
sleep_ms(50); // Debounce delay
|
||||
if (gpio_get(BUTTON_KEY0_PIN) == 0) { // Verify button still pressed
|
||||
printf("Button KEY0 action triggered\n");
|
||||
// TODO: Add your KEY0 action here (e.g., focus next widget)
|
||||
InputEvent input = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||
bool needs_refresh = false;
|
||||
|
||||
// 1. Process button input first (higher priority)
|
||||
input = process_button_input(config);
|
||||
if (input.valid) {
|
||||
needs_refresh = game_update(&game_state, input, config, &renderer);
|
||||
}
|
||||
|
||||
// 2. Process touch input (if no button was pressed)
|
||||
if (!input.valid) {
|
||||
input = process_touch_input(config, &last_touch_time);
|
||||
if (input.valid) {
|
||||
needs_refresh = game_update(&game_state, input, config, &renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (button_key1_pressed) {
|
||||
button_key1_pressed = false;
|
||||
sleep_ms(50); // Debounce delay
|
||||
if (gpio_get(BUTTON_KEY1_PIN) == 0) { // Verify button still pressed
|
||||
printf("Button KEY1 action triggered\n");
|
||||
// TODO: Add your KEY1 action here (e.g., activate focused widget)
|
||||
// 3. Redraw and refresh screen only if needed
|
||||
if (needs_refresh) {
|
||||
// For button presses or touch release, redraw entire UI
|
||||
if (input.type == INPUT_BUTTON_0 || input.type == INPUT_BUTTON_1 || input.type == INPUT_TOUCH_UP) {
|
||||
// Clear buffer and redraw entire UI with updated state
|
||||
memset(bit_buffer, 0, V_WIDTH * V_HEIGHT / 8);
|
||||
game_draw(&game_state, &renderer, &gui);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if our touch interrupt flag was set
|
||||
if (!touch_interrupt_flag) {
|
||||
continue; // Woken by different interrupt, go back to sleep
|
||||
}
|
||||
|
||||
// Clear the flag
|
||||
touch_interrupt_flag = false;
|
||||
|
||||
while(touch_event_down){
|
||||
uint32_t now = to_ms_since_boot(get_absolute_time());
|
||||
|
||||
// Check if enough time has passed since last touch check (debounce)
|
||||
if (now - last_touch_time < debounce_ms) {
|
||||
//continue;
|
||||
}
|
||||
//printf("Touch interrupt event detected (event_down=%d)\n", touch_event_down);
|
||||
|
||||
// Touch interrupt occurred - read the data
|
||||
// is_touched() will check INT pin and confirm via I2C if needed
|
||||
//if (touch && touch->is_touched()) {
|
||||
// Now read full touch data via I2C (already confirmed by INT pin)
|
||||
TouchData touch_data;
|
||||
if (!touch->read_touch(&touch_data)) {
|
||||
// Read failed or no actual touch data
|
||||
touch_fail_count++;
|
||||
//was_touched = false;
|
||||
//last_x = -1;
|
||||
//last_y = -1;
|
||||
//last_touch_time = now;
|
||||
continue;
|
||||
}
|
||||
|
||||
touch_success_count++;
|
||||
|
||||
int16_t x = touch_data.points[0].x;
|
||||
int16_t y = touch_data.points[0].y;
|
||||
uint8_t event = touch_data.points[0].event;
|
||||
uint8_t id = touch_data.points[0].id;
|
||||
uint8_t weight = touch_data.points[0].pressure;
|
||||
uint8_t gesture = touch_data.gesture;
|
||||
|
||||
// Display detailed touch information including weight and gesture
|
||||
// printf("Touch: X=%d Y=%d Event=%d ID=%d Weight=%d",
|
||||
// x, y, event, id, weight);
|
||||
|
||||
// Display gesture if detected (non-zero)
|
||||
if (gesture != 0) {
|
||||
const char* gesture_name = "Unknown";
|
||||
switch(gesture) {
|
||||
case 0x10: gesture_name = "Move Up"; break;
|
||||
case 0x14: gesture_name = "Move Right"; break;
|
||||
case 0x18: gesture_name = "Move Down"; break;
|
||||
case 0x1C: gesture_name = "Move Left"; break;
|
||||
case 0x48: gesture_name = "Zoom In"; break;
|
||||
case 0x49: gesture_name = "Zoom Out"; break;
|
||||
}
|
||||
printf(" Gesture=0x%02X (%s)", gesture, gesture_name);
|
||||
}
|
||||
|
||||
// printf(" [Success:%d Fail:%d]\n", touch_success_count, touch_fail_count);
|
||||
|
||||
|
||||
// Check if touch is in title area to clear screen
|
||||
|
||||
// Draw line from last position (for smooth drawing)
|
||||
if (last_x >= 0 && last_y >= 0) {
|
||||
int dx = abs(x - last_x);
|
||||
int dy = abs(y - last_y);
|
||||
// Only draw line if movement is reasonable (filter noise)
|
||||
//if (dx < 50 && dy < 50) {
|
||||
renderer.draw_line(last_x, last_y, x, y, true);
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
|
||||
was_touched = true;
|
||||
last_touch_time = now;
|
||||
//} else {
|
||||
// INT pin triggered but no touch data (likely release event)
|
||||
|
||||
//}
|
||||
}
|
||||
if (was_touched) {
|
||||
last_x = -1;
|
||||
last_y = -1;
|
||||
was_touched = false;
|
||||
refresh_screen(bit_buffer, display);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user