From d19a2ca6395a31b3cd3ef491c3ed3a6785475e1d Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Wed, 28 Jan 2026 23:23:49 -0500 Subject: [PATCH] touch with abtraction working, SD is not working --- basic1 copy.cpp | 201 +++++++++++++++++++ basic1.cpp | 127 ++++++------ basic1_adafruit_feather_rp2350.uf2 | Bin 152064 -> 152064 bytes basic1_pico2.uf2 | Bin 152064 -> 152064 bytes basic1_pico2_w.uf2 | Bin 152064 -> 152064 bytes board_config.h | 35 ++-- build_and_flash.sh | 109 ++++------ display/low_level_touch_ft6336u.cpp | 14 ++ lib/ft6336u/ft6336u.c | 135 ++++++++----- lib/ft6336u/ft6336u.c.bak | 296 ++++++++++++++++++++++++++++ lib/ft6336u/ft6336u.h | 28 ++- lib/sd_card/sd_card.c | 5 +- lib/st7796/st7796.c | 15 +- 13 files changed, 756 insertions(+), 209 deletions(-) create mode 100644 basic1 copy.cpp create mode 100644 lib/ft6336u/ft6336u.c.bak diff --git a/basic1 copy.cpp b/basic1 copy.cpp new file mode 100644 index 0000000..870d978 --- /dev/null +++ b/basic1 copy.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * 4.0" TFT ST7796 with Touch Screen and SD Card Demo - DIRECT DRIVER TEST + */ + +#include "pico/stdlib.h" +#include "pico/binary_info.h" +#include "board_config.h" // Board-specific pin configuration +#include "sd_card.h" +#include +#include +#include +#include "display/low_level_render.h" +#include "display/low_level_gui.h" +#include "display/low_level_display.h" +#include "lib/ft6336u/ft6336u.h" // Direct driver instead of abstraction + + +// Binary info for RP2350 - ensures proper boot image structure +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__)); + +// Screen dimensions and configuration from board_config.h +const int V_WIDTH = DISPLAY_WIDTH; +const int V_HEIGHT = DISPLAY_HEIGHT; + +// Touch indicator settings +#define TOUCH_RADIUS 10 + +uint8_t bit_buffer[V_WIDTH * V_HEIGHT / 8]; + +/** + * @brief Refresh the screen with the 1-bit buffer + * + * Displays work directly with 1-bit monochrome buffers. + * The display driver internally converts to its native format (RGB565, etc.) + * + * @param buffer Pointer to 1-bit framebuffer (width*height/8 bytes) + * @param display Pointer to display abstraction layer + */ +void refresh_screen(const uint8_t *buffer, LowLevelDisplay* display) { + display->draw_buffer(buffer); + display->refresh(); +} + + + + +int main() +{ + // Initialize standard I/O for debugging with timeout + // This prevents hanging when USB is not connected + stdio_init_all(); + sleep_ms(5000); // Wait for USB connection (if present) + + printf("\n=== %s Demo ===\n", BOARD_NAME); + + // Create display abstraction using factory method + // The factory handles all board-specific configuration internally + LowLevelDisplay* display = LowLevelDisplay::create((DisplayType)DISPLAY_TYPE_SELECTED, V_WIDTH, V_HEIGHT); + + if (!display) { + printf("Failed to create display!\n"); + return -1; + } + + printf("Initializing 4.0\" TFT with Touch and SD Card...\n"); + + // Initialize the display + if (!display->init()) { + printf("Display initialization failed!\n"); + delete display; + return -1; + } + + display->clear(false); // Clear to black + + 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); + + // Refresh the screen with the rendered GUI + refresh_screen(bit_buffer, display); + + // Initialize touch screen using DIRECT DRIVER (bypassing abstraction) + printf("\n=== Direct FT6336U Touch Driver Test ===\n"); + + ft6336u_config_t touch_config = { + .i2c = i2c1, + .gpio_sda = 2, + .gpio_scl = 3, + .gpio_rst = 28, + .gpio_int = 25, + .screen_width = V_WIDTH, + .screen_height = V_HEIGHT, + .swap_xy = true, + .invert_x = true, + .invert_y = false + }; + + printf("Touch Config:\n"); + printf(" I2C: i2c1, SDA: 2, SCL: 3\n"); + printf(" RST: 28, INT: 25\n"); + printf(" Screen: %dx%d\n", V_WIDTH, V_HEIGHT); + printf(" Transforms: swap_xy=%d, invert_x=%d, invert_y=%d\n\n", + touch_config.swap_xy, touch_config.invert_x, touch_config.invert_y); + fflush(stdout); + + bool touch_ok = ft6336u_init(&touch_config); + if (touch_ok) { + printf("Touch initialized successfully!\n"); + printf("Chip ID: 0x%02X, FW Version: 0x%02X\n\n", + ft6336u_get_chip_id(), ft6336u_get_firmware_version()); + + // Run communication test + printf("Running I2C communication test...\n"); + ft6336u_test_i2c(); + printf("\n"); + } else { + printf("Touch initialization FAILED!\n\n"); + } + + // Test SD card and FatFS + if (sd_card_init_with_board_config()) { + sd_card_test_fatfs(); + } else { + printf("SD Card initialization failed or no card present\n"); + } + + printf("\n=== Entering Touch Test Loop ===\n"); + printf("Touch the screen to see coordinates...\n\n"); + fflush(stdout); + + // Main loop - handle touch events with direct driver + int last_x = -1, last_y = -1; + + // Touch debouncing + uint32_t last_touch_time = 0; + const uint32_t debounce_ms = 20; + bool was_touched = false; + int touch_fail_count = 0; + int touch_success_count = 0; + + while (1) { + uint32_t now = to_ms_since_boot(get_absolute_time()); + + if (now - last_touch_time < debounce_ms) { + sleep_ms(1); + continue; + } + + bool is_touched = touch_ok && ft6336u_is_touched(); + + if (is_touched) { + ft6336u_touch_data_t touch_data; + + if (ft6336u_read_touch(&touch_data)) { + touch_success_count++; + + if (touch_data.touch_count > 0) { + int16_t x = touch_data.points[0].x; + int16_t y = touch_data.points[0].y; + + printf("Touch: X=%d, Y=%d, Event=%d [Success: %d, Fail: %d]\n", + x, y, touch_data.points[0].event, + touch_success_count, touch_fail_count); + fflush(stdout); + + last_x = x; + last_y = y; + was_touched = true; + last_touch_time = now; + } + } else { + touch_fail_count++; + if (touch_fail_count % 10 == 0) { + printf("Touch read failed (count: %d)\n", touch_fail_count); + fflush(stdout); + } + } + } else { + if (was_touched) { + last_x = -1; + last_y = -1; + was_touched = false; + } + } + + sleep_ms(5); + } + + return 0; +} diff --git a/basic1.cpp b/basic1.cpp index 92c33e6..291efd5 100644 --- a/basic1.cpp +++ b/basic1.cpp @@ -98,18 +98,19 @@ int main() printf("Touch initialized successfully\n"); // Run communication test if available + // Note: Commented out as it may hang on some hardware configurations printf("\nRunning touch reliability test...\n"); touch->test_communication(); - printf("\n"); + printf("...\n"); } else { printf("Touch initialization failed or not configured\n"); } // Test SD card and FatFS if (sd_card_init_with_board_config()) { - sd_card_test_fatfs(); + sd_card_test_fatfs(); } else { - printf("SD Card initialization failed or no card present\n"); + printf("SD Card initialization failed or no card present\n"); } // Main loop - handle touch events @@ -117,7 +118,7 @@ int main() // Touch debouncing uint32_t last_touch_time = 0; - const uint32_t debounce_ms = 20; // Minimum time between touch reads + 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; @@ -125,71 +126,65 @@ int main() printf("Entering main touch loop...\n"); while (1) { - uint32_t now = to_ms_since_boot(get_absolute_time()); + // Always sleep to prevent tight loop and allow other operations + sleep_us(100); - // Check if enough time has passed since last touch check - if (now - last_touch_time < debounce_ms) { - sleep_ms(1); - continue; - } + // Check INT pin directly (LOW = touch detected, no I2C transaction needed!) + // Much faster than I2C read and doesn't interfere with other operations + bool int_pin_low = !gpio_get(TOUCH_INT_PIN); - bool is_touched = touch && touch->is_touched(); + // Only process if INT pin indicates touch data available + if (int_pin_low) { + uint32_t now = to_ms_since_boot(get_absolute_time()); - // Only process touch if state changed or still touching - if (is_touched) { - TouchData touch_data; - - if (touch->read_touch(&touch_data)) { - touch_success_count++; - - if (touch_data.touch_count > 0) { - int16_t x = touch_data.points[0].x; - int16_t y = touch_data.points[0].y; - - // Only print occasionally to avoid flooding serial - //if (touch_success_count % 5 == 0) { - printf("Touch: X=%d, Y=%d, Event=%d [Success: %d, Fail: %d]\n", - x, y, touch_data.points[0].event, - touch_success_count, touch_fail_count); - //} - - // Check if touch is in title area to clear screen - if (y < 30) { - if (!was_touched) { // Only on new touch - // Clear drawing area in bit buffer - renderer.draw_filled_rectangle(11, 130, V_WIDTH - 11 - 11, V_HEIGHT - 11 - 130, false, 1); - refresh_screen(bit_buffer, display); - printf("Drawing area cleared\n"); - } - } - // Draw in touch area (white line) - else if (y > 100) { - - // 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); - refresh_screen(bit_buffer, display); - } - } - - last_x = x; - last_y = y; - } - - was_touched = true; - last_touch_time = now; - } - } else { - // Touch detected but read failed - touch_fail_count++; - if (touch_fail_count % 10 == 0) { - printf("Touch read failed (count: %d)\n", touch_fail_count); - } + // Check if enough time has passed since last touch check (debounce) + if (now - last_touch_time < debounce_ms) { + //continue; } + // Now read full touch data via I2C + TouchData touch_data; + if (!touch->read_touch(&touch_data) || touch_data.touch_count == 0) { + // 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; + + printf("Touch: X=%d Y=%d Event=%d ID=%d Weight=%d Gesture=0x%02X [S:%d F:%d]\n", + x, y, event, id, weight, gesture, + 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); + refresh_screen(bit_buffer, display); + } + } + + last_x = x; + last_y = y; + + was_touched = true; + last_touch_time = now; } else { // Reset last position when not touching if (was_touched) { @@ -198,8 +193,6 @@ int main() was_touched = false; } } - - sleep_ms(5); // Faster polling for better responsiveness } return 0; diff --git a/basic1_adafruit_feather_rp2350.uf2 b/basic1_adafruit_feather_rp2350.uf2 index 50c29ab5db7173b775d23bd22eed0e91fc5cdecb..ced26a7852da928c6f92eb4f32eca43a7c56ce0d 100644 GIT binary patch delta 13664 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 delta 13755 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 diff --git a/basic1_pico2.uf2 b/basic1_pico2.uf2 index 3b7ba3011f3c8e31d35f19024f383be908014942..72a200f6529d2a926f33e95726fe6798ee142ec8 100644 GIT binary patch delta 13675 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 EAABY;k^lez diff --git a/basic1_pico2_w.uf2 b/basic1_pico2_w.uf2 index ef11fdb0423b4a140f434b769d9edb168863738d..8c400c6c72c90547b990cdb58b28d372e5e4b781 100644 GIT binary patch delta 13675 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 delta 13755 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 EAABY;k^lez diff --git a/board_config.h b/board_config.h index 6c901f6..30c031d 100644 --- a/board_config.h +++ b/board_config.h @@ -24,27 +24,30 @@ #define TOUCH_INVERT_X true #define TOUCH_INVERT_Y false - // SPI pins for display + // SPI pins for display - Feather RP2350 with 4.0" TFT #define DISPLAY_SPI_PORT spi1 - #define DISPLAY_SCK_PIN 18 - #define DISPLAY_MOSI_PIN 19 - #define DISPLAY_MISO_PIN 20 - #define DISPLAY_CS_PIN 17 - #define DISPLAY_DC_PIN 16 - #define DISPLAY_RST_PIN 15 - #define DISPLAY_BL_PIN 14 - #define DISPLAY_BUSY_PIN 13 // For e-paper displays + #define DISPLAY_SCK_PIN 10 // D10 (SCK) + #define DISPLAY_MOSI_PIN 11 // D11 (MOSI) + #define DISPLAY_MISO_PIN 20 // Not used for display + #define DISPLAY_CS_PIN 7 // D13 (CS) + #define DISPLAY_DC_PIN 4 // D4 (DC) + #define DISPLAY_RST_PIN 9 // D9 (RST) + #define DISPLAY_BL_PIN 6 // D6 (Backlight) + #define DISPLAY_BUSY_PIN 13 // For e-paper displays - // I2C pins for touch - #define TOUCH_I2C_PORT i2c0 - #define TOUCH_SDA_PIN 4 - #define TOUCH_SCL_PIN 5 - #define TOUCH_INT_PIN 6 - #define TOUCH_RST_PIN 7 + // I2C pins for touch - Feather I2C default + #define TOUCH_I2C_PORT i2c1 + #define TOUCH_SDA_PIN 2 + #define TOUCH_SCL_PIN 3 + #define TOUCH_INT_PIN 25 + #define TOUCH_RST_PIN 28 // SD card pins (shared SPI with display) #define SD_SPI_PORT spi1 - #define SD_CS_PIN 10 + #define SD_CS_PIN 5 + #define SD_MISO_PIN 24 + #define SD_MOSI_PIN 11 + #define SD_SCK_PIN 10 #elif defined(PICO_BOARD) && (PICO_BOARD == pico2 || PICO_BOARD == pico2_w) // Raspberry Pi Pico 2 / Pico 2 W pinout diff --git a/build_and_flash.sh b/build_and_flash.sh index 10640bd..7b5a35e 100755 --- a/build_and_flash.sh +++ b/build_and_flash.sh @@ -8,16 +8,30 @@ set -e # Exit on error PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BUILD_DIR="${PROJECT_DIR}/build" UF2_FILE="${BUILD_DIR}/basic1.uf2" +NINJA="${HOME}/.pico-sdk/ninja/v1.12.1/ninja" echo "==========================================" echo " Pico RP2350 Build and Flash Script" echo "==========================================" echo "" +# Check if build directory exists and has build.ninja +if [ ! -f "${BUILD_DIR}/build.ninja" ]; then + echo "⚠ Build not configured. Running cmake..." + cd "${PROJECT_DIR}" + cmake -B build -G Ninja -DPICO_BOARD=adafruit_feather_rp2350 + echo "" +fi + # Step 1: Build the project echo "Step 1: Building project..." cd "${BUILD_DIR}" -make -j4 + +if [ -f "${NINJA}" ]; then + "${NINJA}" +else + ninja +fi if [ $? -ne 0 ]; then echo "❌ Build failed!" @@ -36,75 +50,34 @@ fi echo "UF2 file created: ${UF2_FILE}" echo "" -# Step 2: Flash to board -echo "Step 2: Flashing to board..." +# Step 2: Flash to board using picotool +echo "Step 2: Flashing to board using picotool..." +echo "Make sure your board is connected and in BOOTSEL mode" +echo "(Hold BOOTSEL button while plugging in USB)" echo "" -echo "Choose flashing method:" -echo " 1) Use picotool (board must be connected and in BOOTSEL mode)" -echo " 2) Manual copy (mount board as USB drive and copy UF2)" -echo "" -read -p "Enter choice (1 or 2): " choice -case $choice in - 1) - echo "" - echo "Using picotool to flash..." - echo "Make sure your board is connected and in BOOTSEL mode" - echo "(Hold BOOTSEL button while plugging in USB)" - echo "" - read -p "Press Enter when ready..." - - PICOTOOL="${HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool" - - if [ ! -f "${PICOTOOL}" ]; then - echo "❌ picotool not found at: ${PICOTOOL}" - exit 1 - fi - - # Try to load the UF2 - "${PICOTOOL}" load "${UF2_FILE}" -fx - - if [ $? -eq 0 ]; then - echo "" - echo "✓ Successfully flashed to board!" - echo "✓ Board will automatically reboot and run the program" - else - echo "" - echo "❌ Flash failed! Make sure:" - echo " - Board is in BOOTSEL mode (hold BOOTSEL while plugging in)" - echo " - USB cable is connected" - echo " - You have permission to access USB devices" - fi - ;; - 2) - echo "" - echo "Manual copy instructions:" - echo "1. Hold BOOTSEL button on your board" - echo "2. Plug in USB cable (or press RESET while holding BOOTSEL)" - echo "3. Board should appear as USB drive (RPI-RP2)" - echo "4. Copy this file to the drive:" - echo " ${UF2_FILE}" - echo "5. Board will automatically reboot when copy completes" - echo "" - - # Try to detect if RPI-RP2 drive is mounted - if [ -d "/Volumes/RPI-RP2" ]; then - echo "✓ Detected RPI-RP2 drive at /Volumes/RPI-RP2" - read -p "Copy UF2 file now? (y/n): " copy_now - if [ "$copy_now" = "y" ] || [ "$copy_now" = "Y" ]; then - cp "${UF2_FILE}" /Volumes/RPI-RP2/ - echo "✓ File copied! Board will reboot automatically..." - fi - else - echo "⚠ RPI-RP2 drive not detected" - echo "Please manually copy the UF2 file when the drive appears" - fi - ;; - *) - echo "Invalid choice" - exit 1 - ;; -esac +PICOTOOL="${HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool" + +if [ ! -f "${PICOTOOL}" ]; then + echo "❌ picotool not found at: ${PICOTOOL}" + exit 1 +fi + +# Try to load the UF2 +"${PICOTOOL}" load "${UF2_FILE}" -fx + +if [ $? -eq 0 ]; then + echo "" + echo "✓ Successfully flashed to board!" + echo "✓ Board will automatically reboot and run the program" +else + echo "" + echo "❌ Flash failed! Make sure:" + echo " - Board is in BOOTSEL mode (hold BOOTSEL while plugging in)" + echo " - USB cable is connected" + echo " - You have permission to access USB devices" + exit 1 +fi echo "" echo "==========================================" diff --git a/display/low_level_touch_ft6336u.cpp b/display/low_level_touch_ft6336u.cpp index 1391df4..972924d 100644 --- a/display/low_level_touch_ft6336u.cpp +++ b/display/low_level_touch_ft6336u.cpp @@ -36,6 +36,20 @@ bool LowLevelTouchFT6336U::init() { .invert_y = invert_y }; + // Print the complete configuration for debugging + printf("\n=== FT6336U Touch Configuration ===\n"); + printf(" I2C Port: %s\n", config.i2c == i2c0 ? "i2c0" : "i2c1"); + printf(" SDA Pin: %d\n", config.gpio_sda); + printf(" SCL Pin: %d\n", config.gpio_scl); + printf(" RST Pin: %d\n", config.gpio_rst); + printf(" INT Pin: %d\n", config.gpio_int); + printf(" Screen: %dx%d\n", config.screen_width, config.screen_height); + printf(" Swap XY: %s\n", config.swap_xy ? "true" : "false"); + printf(" Invert X: %s\n", config.invert_x ? "true" : "false"); + printf(" Invert Y: %s\n", config.invert_y ? "true" : "false"); + printf("===================================\n\n"); + fflush(stdout); + initialized = ft6336u_init(&config); if (initialized) { diff --git a/lib/ft6336u/ft6336u.c b/lib/ft6336u/ft6336u.c index b91ea31..46319a4 100644 --- a/lib/ft6336u/ft6336u.c +++ b/lib/ft6336u/ft6336u.c @@ -7,6 +7,7 @@ #include #include +static ft6336u_config_t g_config_storage; static const ft6336u_config_t *g_config = NULL; // Helper function to write a register @@ -18,29 +19,48 @@ static bool ft6336u_write_reg(uint8_t reg, uint8_t value) { // Helper function to read a register static bool ft6336u_read_reg(uint8_t reg, uint8_t *value) { - int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); - if (result != 1) { - printf("[FT6336U] I2C write failed: result=%d (expected 1)\n", result); - if (result == PICO_ERROR_GENERIC) printf(" Error: PICO_ERROR_GENERIC\n"); - if (result == PICO_ERROR_TIMEOUT) printf(" Error: PICO_ERROR_TIMEOUT\n"); - return false; + // Retry up to 3 times like Arduino library + for (int retry = 0; retry < 3; retry++) { + int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); + if (result != 1) { + sleep_us(1000); // 1ms delay before retry + continue; + } + + // Add delay after write, before read (like Arduino library does) + sleep_us(10000); // 10ms delay + + result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, value, 1, false); + if (result == 1) { + return true; + } + + sleep_us(1000); // 1ms delay before retry } - - result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, value, 1, false); - if (result != 1) { - printf("[FT6336U] I2C read failed: result=%d (expected 1)\n", result); - return false; - } - return true; + return false; } // Helper function to read multiple registers static bool ft6336u_read_regs(uint8_t reg, uint8_t *buf, size_t len) { - int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); - if (result != 1) return false; - - result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, buf, len, false); - return result == (int)len; + // Retry up to 3 times + for (int retry = 0; retry < 3; retry++) { + int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); + if (result != 1) { + sleep_us(1000); // 1ms delay before retry + continue; + } + + // Add delay after write, before read (like Arduino library does) + sleep_us(10000); // 10ms delay + + result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, buf, len, false); + if (result == (int)len) { + return true; + } + + sleep_us(1000); // 1ms delay before retry + } + return false; } bool ft6336u_init(const ft6336u_config_t *config) { @@ -53,7 +73,9 @@ bool ft6336u_init(const ft6336u_config_t *config) { printf("[FT6336U] Pins: SDA=%d, SCL=%d, RST=%d, INT=%d\n", config->gpio_sda, config->gpio_scl, config->gpio_rst, config->gpio_int); - g_config = config; + // Copy config to static storage to avoid dangling pointer + memcpy(&g_config_storage, config, sizeof(ft6336u_config_t)); + g_config = &g_config_storage; // Initialize I2C printf("[FT6336U] Initializing I2C at 400 kHz...\n"); @@ -115,6 +137,17 @@ bool ft6336u_init(const ft6336u_config_t *config) { printf("[FT6336U] WARNING: Failed to set device mode\n"); } + // Enable trigger mode for better gesture and interrupt support + printf("[FT6336U] Enabling trigger mode...\n"); + if (!ft6336u_write_reg(FT6336U_REG_G_MODE, FT6336U_G_MODE_TRIGGER)) { + printf("[FT6336U] WARNING: Failed to set G_MODE\n"); + } + + // Verify gesture mode was set + uint8_t g_mode = ft6336u_get_g_mode(); + printf("[FT6336U] G_MODE: 0x%02X (%s mode)\n", + g_mode, g_mode == FT6336U_G_MODE_TRIGGER ? "trigger" : "polling"); + printf("[FT6336U] Initialization complete!\n"); return true; } @@ -124,27 +157,13 @@ bool ft6336u_read_touch(ft6336u_touch_data_t *data) { memset(data, 0, sizeof(ft6336u_touch_data_t)); - // Small delay to ensure touch controller is ready - sleep_us(100); - - // Read gesture ID (optional, skip if causing issues) + // Read gesture ID ft6336u_read_reg(FT6336U_REG_GESTURE_ID, &data->gesture); - // Read number of touch points - retry on failure + // Read number of touch points (with retries) uint8_t td_status; - int retry_count = 3; - bool success = false; - - for (int retry = 0; retry < retry_count; retry++) { - if (ft6336u_read_reg(FT6336U_REG_TD_STATUS, &td_status)) { - success = true; - break; - } - sleep_us(500); // Brief delay before retry - } - - if (!success) { - return false; // Failed after retries + if (!ft6336u_read_reg(FT6336U_REG_TD_STATUS, &td_status)) { + return false; // I2C error } data->touch_count = td_status & 0x0F; @@ -157,22 +176,12 @@ bool ft6336u_read_touch(ft6336u_touch_data_t *data) { return true; } - // Read touch point data (6 bytes per point) + // Read touch point data (6 bytes per point: XH, XL, YH, YL, WEIGHT, MISC) for (int i = 0; i < data->touch_count; i++) { uint8_t reg_base = (i == 0) ? FT6336U_REG_P1_XH : FT6336U_REG_P2_XH; uint8_t buf[6]; - // Retry read if it fails - bool read_success = false; - for (int retry = 0; retry < 3; retry++) { - if (ft6336u_read_regs(reg_base, buf, 6)) { - read_success = true; - break; - } - sleep_us(200); - } - - if (!read_success) { + if (!ft6336u_read_regs(reg_base, buf, 6)) { return false; } @@ -181,7 +190,8 @@ bool ft6336u_read_touch(ft6336u_touch_data_t *data) { uint16_t raw_x = ((buf[0] & 0x0F) << 8) | buf[1]; uint16_t raw_y = ((buf[2] & 0x0F) << 8) | buf[3]; data->points[i].id = (buf[2] >> 4) & 0x0F; - data->points[i].weight = buf[4]; + data->points[i].weight = buf[4]; // Touch pressure/area + data->points[i].misc = (buf[5] >> 4) & 0x0F; // Touch area (upper nibble) // Apply coordinate transformations if (g_config->swap_xy) { @@ -294,3 +304,28 @@ bool ft6336u_test_i2c(void) { return overall; } + +bool ft6336u_set_g_mode(uint8_t mode) { + if (g_config == NULL) return false; + return ft6336u_write_reg(FT6336U_REG_G_MODE, mode); +} + +uint8_t ft6336u_get_g_mode(void) { + if (g_config == NULL) return 0xFF; + + uint8_t g_mode; + if (!ft6336u_read_reg(FT6336U_REG_G_MODE, &g_mode)) { + return 0xFF; + } + return g_mode; +} + +uint8_t ft6336u_get_power_mode(void) { + if (g_config == NULL) return 0xFF; + + uint8_t pwr_mode; + if (!ft6336u_read_reg(FT6336U_REG_POWER_MODE, &pwr_mode)) { + return 0xFF; + } + return pwr_mode; +} diff --git a/lib/ft6336u/ft6336u.c.bak b/lib/ft6336u/ft6336u.c.bak new file mode 100644 index 0000000..b91ea31 --- /dev/null +++ b/lib/ft6336u/ft6336u.c.bak @@ -0,0 +1,296 @@ +/* + * FT6336U Capacitive Touch Screen Driver Implementation + */ + +#include "ft6336u.h" +#include "hardware/gpio.h" +#include +#include + +static const ft6336u_config_t *g_config = NULL; + +// Helper function to write a register +static bool ft6336u_write_reg(uint8_t reg, uint8_t value) { + uint8_t buf[2] = {reg, value}; + int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, buf, 2, false); + return result == 2; +} + +// Helper function to read a register +static bool ft6336u_read_reg(uint8_t reg, uint8_t *value) { + int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); + if (result != 1) { + printf("[FT6336U] I2C write failed: result=%d (expected 1)\n", result); + if (result == PICO_ERROR_GENERIC) printf(" Error: PICO_ERROR_GENERIC\n"); + if (result == PICO_ERROR_TIMEOUT) printf(" Error: PICO_ERROR_TIMEOUT\n"); + return false; + } + + result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, value, 1, false); + if (result != 1) { + printf("[FT6336U] I2C read failed: result=%d (expected 1)\n", result); + return false; + } + return true; +} + +// Helper function to read multiple registers +static bool ft6336u_read_regs(uint8_t reg, uint8_t *buf, size_t len) { + int result = i2c_write_blocking(g_config->i2c, FT6336U_ADDR, ®, 1, true); + if (result != 1) return false; + + result = i2c_read_blocking(g_config->i2c, FT6336U_ADDR, buf, len, false); + return result == (int)len; +} + +bool ft6336u_init(const ft6336u_config_t *config) { + if (config == NULL) { + printf("[FT6336U] ERROR: config is NULL\n"); + return false; + } + + printf("[FT6336U] Initializing touch controller...\n"); + printf("[FT6336U] Pins: SDA=%d, SCL=%d, RST=%d, INT=%d\n", + config->gpio_sda, config->gpio_scl, config->gpio_rst, config->gpio_int); + + g_config = config; + + // Initialize I2C + printf("[FT6336U] Initializing I2C at 400 kHz...\n"); + uint actual_freq = i2c_init(config->i2c, 400000); // 400 kHz + printf("[FT6336U] I2C actual frequency: %u Hz\n", actual_freq); + + gpio_set_function(config->gpio_sda, GPIO_FUNC_I2C); + gpio_set_function(config->gpio_scl, GPIO_FUNC_I2C); + gpio_pull_up(config->gpio_sda); + gpio_pull_up(config->gpio_scl); + printf("[FT6336U] I2C pins configured\n"); + + // Initialize reset pin + gpio_init(config->gpio_rst); + gpio_set_dir(config->gpio_rst, GPIO_OUT); + printf("[FT6336U] Reset pin configured\n"); + + // Initialize interrupt pin (input with pull-up) + gpio_init(config->gpio_int); + gpio_set_dir(config->gpio_int, GPIO_IN); + gpio_pull_up(config->gpio_int); + printf("[FT6336U] Interrupt pin configured\n"); + + // Reset the touch controller + printf("[FT6336U] Resetting touch controller...\n"); + gpio_put(config->gpio_rst, 0); + sleep_ms(10); + gpio_put(config->gpio_rst, 1); + sleep_ms(300); // Wait for chip to initialize + printf("[FT6336U] Reset complete, reading chip ID...\n"); + + // Verify chip ID + uint8_t chip_id = ft6336u_get_chip_id(); + printf("[FT6336U] Chip ID read attempt 1: 0x%02X (expected 0x64)\n", chip_id); + + if (chip_id != 0x64) { + // Try again - sometimes first read fails + printf("[FT6336U] First read failed, retrying...\n"); + sleep_ms(100); + chip_id = ft6336u_get_chip_id(); + printf("[FT6336U] Chip ID read attempt 2: 0x%02X\n", chip_id); + + if (chip_id != 0x64) { + printf("[FT6336U] ERROR: Invalid chip ID! Check I2C wiring and address.\n"); + printf("[FT6336U] Possible issues:\n"); + printf(" - I2C pins not connected properly\n"); + printf(" - Touch controller not powered\n"); + printf(" - Wrong I2C address (using 0x%02X)\n", FT6336U_ADDR); + printf(" - I2C pull-up resistors missing\n"); + return false; + } + } + + printf("[FT6336U] Chip ID verified successfully!\n"); + + // Set to normal operating mode + printf("[FT6336U] Setting normal operating mode...\n"); + if (!ft6336u_write_reg(FT6336U_REG_DEVICE_MODE, 0x00)) { + printf("[FT6336U] WARNING: Failed to set device mode\n"); + } + + printf("[FT6336U] Initialization complete!\n"); + return true; +} + +bool ft6336u_read_touch(ft6336u_touch_data_t *data) { + if (data == NULL || g_config == NULL) return false; + + memset(data, 0, sizeof(ft6336u_touch_data_t)); + + // Small delay to ensure touch controller is ready + sleep_us(100); + + // Read gesture ID (optional, skip if causing issues) + ft6336u_read_reg(FT6336U_REG_GESTURE_ID, &data->gesture); + + // Read number of touch points - retry on failure + uint8_t td_status; + int retry_count = 3; + bool success = false; + + for (int retry = 0; retry < retry_count; retry++) { + if (ft6336u_read_reg(FT6336U_REG_TD_STATUS, &td_status)) { + success = true; + break; + } + sleep_us(500); // Brief delay before retry + } + + if (!success) { + return false; // Failed after retries + } + + data->touch_count = td_status & 0x0F; + if (data->touch_count > FT6336U_MAX_TOUCH_POINTS) { + data->touch_count = FT6336U_MAX_TOUCH_POINTS; + } + + // If no touches, return early + if (data->touch_count == 0) { + return true; + } + + // Read touch point data (6 bytes per point) + for (int i = 0; i < data->touch_count; i++) { + uint8_t reg_base = (i == 0) ? FT6336U_REG_P1_XH : FT6336U_REG_P2_XH; + uint8_t buf[6]; + + // Retry read if it fails + bool read_success = false; + for (int retry = 0; retry < 3; retry++) { + if (ft6336u_read_regs(reg_base, buf, 6)) { + read_success = true; + break; + } + sleep_us(200); + } + + if (!read_success) { + return false; + } + + // Parse touch point data + data->points[i].event = (buf[0] >> 6) & 0x03; + uint16_t raw_x = ((buf[0] & 0x0F) << 8) | buf[1]; + uint16_t raw_y = ((buf[2] & 0x0F) << 8) | buf[3]; + data->points[i].id = (buf[2] >> 4) & 0x0F; + data->points[i].weight = buf[4]; + + // Apply coordinate transformations + if (g_config->swap_xy) { + uint16_t temp = raw_x; + raw_x = raw_y; + raw_y = temp; + } + + if (g_config->invert_x) { + raw_x = g_config->screen_width - 1 - raw_x; + } + + if (g_config->invert_y) { + raw_y = g_config->screen_height - 1 - raw_y; + } + + data->points[i].x = raw_x; + data->points[i].y = raw_y; + + // Ensure coordinates are within screen bounds + if (data->points[i].x >= g_config->screen_width) { + data->points[i].x = g_config->screen_width - 1; + } + if (data->points[i].y >= g_config->screen_height) { + data->points[i].y = g_config->screen_height - 1; + } + } + + return true; +} + +bool ft6336u_is_touched(void) { + if (g_config == NULL) return false; + + // More reliable: Read TD_STATUS register directly + // INT pin can be unreliable due to timing/noise + uint8_t td_status; + if (!ft6336u_read_reg(FT6336U_REG_TD_STATUS, &td_status)) { + return false; // I2C error, assume not touched + } + + uint8_t touch_count = td_status & 0x0F; + return touch_count > 0; +} + +uint8_t ft6336u_get_chip_id(void) { + if (g_config == NULL) return 0xFF; + + uint8_t chip_id; + if (!ft6336u_read_reg(FT6336U_REG_CHIPID, &chip_id)) { + return 0xFF; + } + return chip_id; +} + +uint8_t ft6336u_get_firmware_version(void) { + if (g_config == NULL) return 0xFF; + + uint8_t fw_ver; + if (!ft6336u_read_reg(FT6336U_REG_FIRMID, &fw_ver)) { + return 0xFF; + } + return fw_ver; +} + +void ft6336u_set_interrupt_callback(void (*callback)(uint gpio, uint32_t events)) { + if (g_config == NULL || callback == NULL) return; + + // Enable interrupt on falling edge (touch detected) + gpio_set_irq_enabled_with_callback(g_config->gpio_int, + GPIO_IRQ_EDGE_FALL, + true, + callback); +} + +bool ft6336u_test_i2c(void) { + if (g_config == NULL) { + printf("[FT6336U] Test failed: not initialized\n"); + return false; + } + + printf("[FT6336U] Testing I2C communication...\n"); + + // Test 1: Read chip ID + uint8_t chip_id = ft6336u_get_chip_id(); + printf(" Chip ID: 0x%02X (expected 0x64) - %s\n", + chip_id, chip_id == 0x64 ? "PASS" : "FAIL"); + + // Test 2: Read firmware version + uint8_t fw_ver = ft6336u_get_firmware_version(); + printf(" Firmware: 0x%02X - %s\n", + fw_ver, fw_ver != 0xFF ? "PASS" : "FAIL"); + + // Test 3: Read TD_STATUS multiple times + int success_count = 0; + const int test_count = 10; + for (int i = 0; i < test_count; i++) { + uint8_t status; + if (ft6336u_read_reg(FT6336U_REG_TD_STATUS, &status)) { + success_count++; + } + sleep_ms(10); + } + printf(" TD_STATUS reads: %d/%d successful - %s\n", + success_count, test_count, + success_count == test_count ? "PASS" : "WARN"); + + bool overall = (chip_id == 0x64) && (fw_ver != 0xFF) && (success_count >= test_count - 2); + printf("[FT6336U] I2C test: %s\n", overall ? "PASS" : "FAIL"); + + return overall; +} diff --git a/lib/ft6336u/ft6336u.h b/lib/ft6336u/ft6336u.h index 3c0b82d..f51400e 100644 --- a/lib/ft6336u/ft6336u.h +++ b/lib/ft6336u/ft6336u.h @@ -38,9 +38,15 @@ extern "C" { #define FT6336U_REG_P2_MISC 0x0E #define FT6336U_REG_CHIPID 0xA3 // Chip ID (should read 0x64) +#define FT6336U_REG_G_MODE 0xA4 // Interrupt mode (polling or trigger) +#define FT6336U_REG_POWER_MODE 0xA5 // Power mode #define FT6336U_REG_FIRMID 0xA6 #define FT6336U_REG_VENDID 0xA8 +// G_MODE values +#define FT6336U_G_MODE_POLLING 0x00 +#define FT6336U_G_MODE_TRIGGER 0x01 + // Touch event types #define FT6336U_EVENT_PRESS_DOWN 0x00 #define FT6336U_EVENT_LIFT_UP 0x01 @@ -56,7 +62,8 @@ typedef struct { uint16_t y; uint8_t event; // Press down, lift up, contact, no event uint8_t id; // Touch point ID - uint8_t weight; // Touch pressure/area + uint8_t weight; // Touch pressure/area (0-255) + uint8_t misc; // Touch area (0-15, upper nibble of MISC register) } ft6336u_touch_point_t; // Touch data structure @@ -124,6 +131,25 @@ void ft6336u_set_interrupt_callback(void (*callback)(uint gpio, uint32_t events) */ bool ft6336u_test_i2c(void); +/** + * Set G_MODE register (polling vs trigger mode) + * @param mode 0 for polling mode, 1 for trigger mode + * @return true if successful + */ +bool ft6336u_set_g_mode(uint8_t mode); + +/** + * Get current G_MODE setting + * @return Current G_MODE value or 0xFF on error + */ +uint8_t ft6336u_get_g_mode(void); + +/** + * Get current power mode + * @return Current power mode or 0xFF on error + */ +uint8_t ft6336u_get_power_mode(void); + #ifdef __cplusplus } #endif diff --git a/lib/sd_card/sd_card.c b/lib/sd_card/sd_card.c index cba2f91..5842765 100644 --- a/lib/sd_card/sd_card.c +++ b/lib/sd_card/sd_card.c @@ -9,6 +9,7 @@ #include #include +static sd_card_config_t g_config_storage; static const sd_card_config_t *g_config = NULL; static sd_card_info_t g_card_info = {0}; @@ -91,7 +92,9 @@ static uint8_t sd_card_send_acmd(uint8_t acmd, uint32_t arg) { bool sd_card_init(const sd_card_config_t *config) { if (config == NULL) return false; - g_config = config; + // Copy config to static storage to avoid dangling pointer + memcpy(&g_config_storage, config, sizeof(sd_card_config_t)); + g_config = &g_config_storage; memset(&g_card_info, 0, sizeof(g_card_info)); // Initialize CS pin (active low) diff --git a/lib/st7796/st7796.c b/lib/st7796/st7796.c index 7cabefc..ccbedb9 100644 --- a/lib/st7796/st7796.c +++ b/lib/st7796/st7796.c @@ -112,7 +112,8 @@ // Global state variables // These hold the current display configuration -static const struct st7796_config *config; // Pin and SPI configuration +static struct st7796_config config_storage; // Static storage for config copy +static const struct st7796_config *config; // Pin and SPI configuration static uint16_t width; // Display width in pixels (e.g., 480) static uint16_t height; // Display height in pixels (e.g., 320) static uint16_t x_offset; // X offset for display alignment (currently 0) @@ -321,7 +322,9 @@ static void set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { * @param h Display height (320 for landscape) */ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) { - config = c; + // Copy config to static storage to avoid dangling pointer + memcpy(&config_storage, c, sizeof(struct st7796_config)); + config = &config_storage; width = w; height = h; @@ -401,10 +404,10 @@ void st7796_init(const struct st7796_config *c, uint16_t w, uint16_t h) { write_command_with_data(ST7796_MADCTL, &data, 1); sleep_ms(10); - // Display Inversion - try OFF first - // Some displays look better with INVON, others with INVOFF - // If colors look wrong, try: write_command(ST7796_INVON); - write_command(ST7796_INVOFF); + // Display Inversion - try ON for displays that need it + // Some displays need INVON, others need INVOFF + // If this doesn't work, try: write_command(ST7796_INVOFF); + write_command(ST7796_INVON); sleep_ms(10); // Normal Display Mode On