raspberrypi / pico-examples

BSD 3-Clause "New" or "Revised" License
2.83k stars 820 forks source link

The pio_i2c example's write operation corrupt every subsequent transaction. #168

Open ithinuel opened 2 years ago

ithinuel commented 2 years ago

Tested using an NXP LM75B temperature sensor.

Minimal code to reproduce:

#include <stdio.h>

#include "pico/stdlib.h"
#include "pio_i2c.h"

#define PIN_SDA 20
#define PIN_SCL 21

// data must be at least 2 bytes long.
void print_temperature(uint8_t *data) {
    int16_t data_i16 = ((int16_t)data[0]) << 8 | (data[1] & 0xE0);
    float temp = (float)data_i16 * 0.125;
    printf("%0.2fC\n", temp);
}

int main() {
    stdio_init_all();

    PIO pio = pio0;
    uint sm = 0;
    uint offset = pio_add_program(pio, &i2c_program);
    i2c_program_init(pio, sm, offset, PIN_SDA, PIN_SCL);

    uint8_t data[2] = {0};
    // LM75B's default pointer is to temperature register.
    pio_i2c_read_blocking(pio, sm, 0x48, data, 2);
    print_temperature(data);

    // Set pointer register to Temperature register.
    data[0] = 0x0;
    pio_i2c_write_blocking(pio, sm, 0x48, data, 1);

    // Read again temperature now corrupted
    pio_i2c_read_blocking(pio, sm, 0x48, data, 2);
    print_temperature(data);

    printf("Done.\n");
    return 0;
}
ithinuel commented 2 years ago

The issue seems to lie in the ISR's state after a write operation (where autopush is disabled but the ISR isn't cleared).

yiding commented 2 months ago

I noticed this as well. adding a mov isr, null to the beginning of do_byte in i2c.pio seems to resolve this...

There's also additional posts in the RPI forums about this issue: https://forums.raspberrypi.com/viewtopic.php?t=340111 https://forums.raspberrypi.com/viewtopic.php?p=2138637

In the forum discussions it's not clear how ISR count can end up in a de-synchronized state, since the pio program will always loop 8 bits.