Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 7503

General • I2C operation does not survive jump to user program

$
0
0
RP2040 dual-core bootloader + user app: I2C devices seen in bootloader, none in user app
Platform: Pico W (RP2040)

Architecture concept:
- Core 0 runs a resident bootloader (handles BLE uplink, system bring-up).
- Core 1 runs a dynamically loaded “user app” at a fixed flash offset (e.g., 0x100A0000). The bootloader sets SP/VTOR for the user, then BLX into user entry.
- Buses/pins: I2C0 (SDA/SCL = 16/17), I2C1 (SDA/SCL = 14/15)
- Devices: OLED at 0x3C (I2C0), multiple sensors on I2C1

Symptom
- Bootloader scan (Core 1 wrapper) finds all devices (OLED + sensors).
- User app scan (immediately at entry, even before BSS/DATA init) finds 0 devices.

We have tried doing an i2c scan DIRECTLY before the jump to user app, on Core 1, and it works and discovers the devices.
Then we have done an i2c scan in the user app directly after bss and data initialization and it does not discover the devices.

We verified that the the jump to user space goes directly to main. bss_init_asm() and data_init_asm() are assembly routines that clear bss data and copy FLASH->RAM for initialized data. We've verified the the i2c0 and i2c1 static variables hold the right addresses for the HW registers.

Does anyone have any experience with anything similar to this?

Essential bootloader code:

Code:

    // I2C scan using Pico SDK before launching user app    printf("Core 1: Wrapper I2C scan (Pico SDK):\n");    bootloader_i2c_scan(i2c0, 16, 17, "I2C0");    bootloader_i2c_scan(i2c1, 14, 15, "I2C1");        // Jump to user app (skipping dangerous init code)    // Note: This could potentially fault if the address is invalid    int result = 0;    printf("Core 1: About to call user_main()...\n");    result = user_main();    printf("Core 1: user_main() returned with code %d\n", result);        ...       // Bootloader I2C scan functionstatic void bootloader_i2c_scan(i2c_inst_t *i2c_instance, uint sda_pin, uint scl_pin, const char *bus_name) {    // Initialize I2C with Pico SDK directly    i2c_init(i2c_instance, 100000);  // Start at 100kHz for reliability    gpio_set_function(sda_pin, GPIO_FUNC_I2C);    gpio_set_function(scl_pin, GPIO_FUNC_I2C);    gpio_pull_up(sda_pin);    gpio_pull_up(scl_pin);        printf("%s bootloader scan: ", bus_name);    int devices_found = 0;        for (int addr = 1; addr < 128; addr++) {        uint8_t dummy_data = 0x00;        int result = i2c_write_timeout_us(i2c_instance, addr, &dummy_data, 1, false, 10000); // 10ms timeout        if (result >= 0) {            printf("0x%02X ", addr);            devices_found++;        }    }        if (devices_found == 0) {        printf("none");    }    printf(" (%d found)\n", devices_found);        // Deinitialize to avoid conflicts with user applications    i2c_deinit(i2c_instance);}

Essential user code:

Code:

__attribute__((section(".text.main")))int main(void) {    // Initialize BSS and data sections using assembly (won't clobber aeabi_mem_funcs)    bss_init_asm();    data_init_asm();        // Now initialize memory functions (safe to call once after assembly init)    __aeabi_mem_init();        // Initialize the user framework first (needed for IPC communication)    user_framework_init();    // IDENTICAL I2C scan using Pico SDK after device framework initialization    user_serial_printf("User I2C scan (Pico SDK) %p %p:\n", i2c0, i2c1);    sleep_ms(20);    user_i2c_scan(i2c0, PIN_I2C0_SDA, PIN_I2C0_SCL, "I2C0");    sleep_ms(20);    user_i2c_scan(i2c1, PIN_I2C1_SDA, PIN_I2C1_SCL, "I2C1");        ... }        static void user_i2c_scan(i2c_inst_t *i2c_instance, uint sda_pin, uint scl_pin, const char *bus_name) {    // Initialize I2C with Pico SDK directly - IDENTICAL to bootloader    i2c_init(i2c_instance, 100000);  // Start at 100kHz for reliability    gpio_set_function(sda_pin, GPIO_FUNC_I2C);    gpio_set_function(scl_pin, GPIO_FUNC_I2C);    gpio_pull_up(sda_pin);    gpio_pull_up(scl_pin);        user_serial_printf("%s user scan: ", bus_name);    int devices_found = 0;        for (int addr = 1; addr < 128; addr++) {        uint8_t dummy_data = 0x00;        int result = i2c_write_timeout_us(i2c_instance, addr, &dummy_data, 1, false, 10000); // 10ms timeout        if (result >= 0) {            user_serial_printf("0x%02X ", addr);            devices_found++;        }    }        if (devices_found == 0) {        user_serial_printf("none");    }    user_serial_printf(" (%d found)\n", devices_found);        // Deinitialize to avoid conflicts with device framework    i2c_deinit(i2c_instance);}

Statistics: Posted by kevin_mirrorai — Sun Oct 26, 2025 5:25 pm — Replies 2 — Views 59



Viewing all articles
Browse latest Browse all 7503

Trending Articles