Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2C bus blocked when Pico I2C in slave mode #1519

Closed
MTBfs opened this issue Oct 2, 2023 · 6 comments
Closed

I2C bus blocked when Pico I2C in slave mode #1519

MTBfs opened this issue Oct 2, 2023 · 6 comments

Comments

@MTBfs
Copy link

MTBfs commented Oct 2, 2023

Hello,

I have a problem where I unfortunately do not get on.

I have a setup with a Raspberry Pi 3B+ as master, a Pico as master or slave and another slave. The Pico only acts as master when the Raspberry Pi is off.

Now I have the problem that when the Pico is switched as slave, I can't find any slaves via i2cdetect -y 1 on the Pi and the processing of the command takes a very long time. If both Pico and Pi are test configured as master I find the second slave and the command is also processed very quickly. The values of the slave are now of course not interpretable because of the two masters.
What I can also see is that the I2C interrupt handler is not called on the Pico. Other interrupts and also timers work correctly though.

Does anyone have an idea where my problem could be. Thank you very much.

Unfortunately I have the problem also in this trimmed down code variant:

#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/time.h"
#include "hardware/i2c.h"
#include "pico/i2c_slave.h"
#include "hardware/irq.h"

#define SDA_PIN 4
#define SCL_PIN 5
#define PWR_OFF_PIN 2
#define PI_PWRD_PIN 0
#define I2C_INSTANCE i2c0

int main();
void i2cInit();
void i2cSwitchToSlave();
void i2cSwitchToMaster();
void irqI2C(i2c_inst_t *i2c, i2c_slave_event_t event);
bool i2cTimer(struct repeating_timer *t);
void i2cMasterConfig();
void i2cMasterCalib();
void pwrMgmtInit();
void irqHandler(uint gpio, uint32_t events);

int main(){
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    gpio_put(PICO_DEFAULT_LED_PIN, 1);
    sleep_ms(100);
    pwrMgmtInit();
    i2cInit();
    while(1){
        tight_loop_contents();
    }
}

void i2cInit(){
    gpio_init(SDA_PIN);
    gpio_init(SCL_PIN);
    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);
    
    i2c_init(I2C_INSTANCE, 400*1000);
    
    i2cMasterCalib();
    i2cMasterConfig();
    add_repeating_timer_ms(1000, i2cTimer, NULL, &timerI2C);
    
    if(gpio_get(PI_PWRD_PIN)){
        i2cSwitchToSlave();
    }
}

void i2cSwitchToSlave(){
    cancel_repeating_timer(&timerI2C);
    i2c_slave_init(I2C_INSTANCE,0x55,&irqI2C);
}

void i2cSwitchToMaster(){
    i2c_slave_deinit(I2C_INSTANCE);
    i2cMasterCalib();
    i2cMasterConfig();
    add_repeating_timer_ms(2000, i2cTimer, NULL, &timerI2C);
}

void irqI2C(i2c_inst_t *i2c, i2c_slave_event_t event){
    if ((i2c == I2C_INSTANCE) && (event == I2C_SLAVE_RECEIVE)) {
        uint8_t value[10];
        uint8_t bytesCnt = i2c_get_read_available(i2c);
        if (bytesCnt > 1) {
            i2c_read_raw_blocking(i2c,value,bytesCnt);
            switch(value[0]){
                case 0x00:
                    break;
                case 0x01:
                    break;
                case 0x02:
                    break;
                case 0x03:
                    break;
                case 0x04:
                    break;
            }
        }
    }
}

bool i2cTimer(repeating_timer *t){
    i2cMasterCalib();
    uint16_t val = 0;
    uint8_t percent = 0;
    bool loading = false;
    uint8_t buf[2] = {0x02, 0x00};
    i2c_write_blocking(i2c0, 0x42, buf, 1, true);
    i2c_read_blocking(i2c0, 0x42, buf, 2, false);
    val = (buf[0] * 256) + buf[1];
    percent = (val - 6) / 2.4 * 100;
    if(percent > 100){
        percent = 100;
    }
    if(percent < 0){
        percent = 0;
    }
    buf[0] = 0x04;
    buf[1] = 0x00;
    i2c_write_blocking(i2c0, 0x42, buf, 1, true);
    i2c_read_blocking(i2c0, 0x42, buf, 2, false);
    val = (buf[0] * 256) + buf[1];
    return true;
}

void i2cMasterConfig(){
    uint16_t config = 0x01 << 13 | 0x03 << 11 | 0x0D << 7 | 0x0D << 3 | 0x07;
    uint8_t buf [3];
    buf[0] = 0x00;
    buf[1] = config & 0xFF;
    buf[2] = (config & 0xFF00) >> 8;
    i2c_write_blocking(i2c0, 0x42, buf, 3, true);
}

void i2cMasterCalib(){
    uint8_t buf[3] = {0x05, 0x10, 0x00};
    i2c_write_blocking(i2c0, 0x42, buf, 3, true);
}

void pwrMgmtInit(){
    gpio_init(PI_PWRD_PIN);
    gpio_set_dir(PI_PWRD_PIN, GPIO_IN);

    gpio_pull_down(PI_PWRD_PIN);
    gpio_pull_down(PWR_OFF_PIN);

    gpio_set_irq_enabled(PI_PWRD_PIN, GPIO_IRQ_EDGE_RISE, true);
    gpio_set_irq_enabled_with_callback(PWR_OFF_PIN, GPIO_IRQ_EDGE_FALL, true, &irqHandler);
}

void irqHandler(uint gpio, uint32_t events){
    if((gpio == PI_PWRD_PIN) && (events == GPIO_IRQ_EDGE_RISE)){
        if(mbs->setPIstate(booting)){
            i2cSwitchToSlave();
        }
    }

    if((gpio == PWR_OFF_PIN) && (events == GPIO_IRQ_EDGE_FALL)){
        if(mbs->setPIstate(off)){
            i2cSwitchToMaster();
        }
    }
}

Thank you very much for your help.

Many greetings,

Fabian

@lurch
Copy link
Contributor

lurch commented Oct 3, 2023

This isn't something I know much about myself, but perhaps one of these other issues might have useful information for you? https://github.com/raspberrypi/pico-sdk/issues?q=is%3Aissue+i2c+slave

And see also https://github.com/raspberrypi/pico-examples/tree/master/i2c/slave_mem_i2c in case that helps.

@MTBfs
Copy link
Author

MTBfs commented Oct 4, 2023

Thank you very much for the answer.
I followed the example (slave_mem_i2c), so I don't know where the problem could be.
Maybe my problem is related to this issue: #505

I will test tonight how it behaves when I flash the software via USB instead of via SWD. However, the I2C slave is running on core 0.

I'll let you know when I know something new.
Thanks.

@MTBfs
Copy link
Author

MTBfs commented Oct 5, 2023

Unfortunatly my problem has nothing to to with the mentioned isse.

I thought there could be an problem, that the irq could not be enabled, but the irq is enabled.

@MTBfs
Copy link
Author

MTBfs commented Nov 13, 2023

I was able to solve the problem.
i2cdetect read from the slave, but I had not implemented this option. This means that the slave detected that it should write something to the bus and blocked the bus. But then it never wrote anything to the bus, so it was permanently blocked.

@MTBfs MTBfs closed this as completed Nov 13, 2023
@RobinHeitz
Copy link

RobinHeitz commented Jul 9, 2024

I was able to solve the problem. i2cdetect read from the slave, but I had not implemented this option. This means that the slave detected that it should write something to the bus and blocked the bus. But then it never wrote anything to the bus, so it was permanently blocked.

Can you show what code worked out for you?
I'm not able to see it in i2cdetect -y 1 (Pi).

// Pi connection
#define PICO_I2C_SLAVE_ADDR 0x30
#define PI_I2C_PORT i2c0
#define PI_SDA_PIN 16
#define PI_SCL_PIN 17

void i2c0_irq_handler() {
  printf("Interrupt!!!");
  gpio_put(GPIO_GREEN, green_on);
  green_on = !green_on;
  i2c_write_byte_raw(PI_I2C_PORT, 0x99);
}

int main() {
  stdio_init_all();

  adc_init();
  adc_gpio_init(26);
  adc_select_input(1);

  // timer setup, not related to i2c
  struct repeating_timer timer;
  add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer);

  // Initialise the Wi-Fi chip
  if (cyw43_arch_init()) {
    printf("Wi-Fi init failed\n");
    return -1;
  }
  gpio_init(GPIO_GREEN);
  gpio_set_dir(GPIO_GREEN, GPIO_OUT);
  gpio_init(GPIO_RED);
  gpio_set_dir(GPIO_RED, GPIO_OUT);
  gpio_init(GPIO_YELLOW);
  gpio_set_dir(GPIO_YELLOW, GPIO_OUT);

  // gpio_put(GPIO_RED, 1);
  gpio_put(GPIO_YELLOW, 1);
  gpio_put(GPIO_GREEN, 1);

  // Example to turn on the Pico W LED
  cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);


  //////////////////////////////////////////////////////////////////////////////////
  // Setup I2C0 for PI


  i2c_init(PI_I2C_PORT, 100 * 1000);
  i2c_set_slave_mode(PI_I2C_PORT, true, PICO_I2C_SLAVE_ADDR);
  gpio_set_function(PI_SDA_PIN, GPIO_FUNC_I2C);
  gpio_set_function(PI_SCL_PIN, GPIO_FUNC_I2C);
  gpio_pull_up(PI_SDA_PIN);
  gpio_pull_up(PI_SCL_PIN);

  irq_set_exclusive_handler(I2C0_BASE, i2c0_irq_handler);
  irq_set_enabled(I2C0_IRQ, true);
  
  //////////////////////////////////////////////////////////////////////////////////

   while(true) {

    tight_loop_contents();
  }
}

@MTBfs
Copy link
Author

MTBfs commented Jul 11, 2024

Sorry for my late reply.

In my code I divide between I2C_SLAVE_RECEIVE and I2C_SLAVE_REQUEST, when there comes a request, I do i2c_write_raw_blocking(i2c,0x00,1);. I think it was working, but I haven't used this anymore, because I normaly only send data to the pico and this works all fine.

I think that a problem could be, when you write to the bus when there was no request for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants