GithubHelp home page GithubHelp logo

pico_i2c_slave's People

Contributors

vmilea avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pico_i2c_slave's Issues

I2C_SLAVE_FINISH before transmission is done

I am using a pycom device as I2C master running MicroPython.
When I try to send/read some bytes, this is the order that the pico_i2c_slave i2c_slave_handler triggers:

  1. I2C_SLAVE_RECEIVE
  2. I2C_SLAVE_FINISH
  3. I2C_SLAVE_RECEIVE

The first (1) package is correct, but then the "I2C_SLAVE_FINISH" is hit before the actual data is recieved.
I have tried this with 4 bytes, and the same happens.
What could cause this? Is there something I can try to do?

Pycom command

from machine import I2C
i2c = I2C(0, I2C.MASTER, baudrate=100000)
i2c.writeto_mem(0x17, 0x00, 0x03)

4 Bytes:

from machine import I2C
i2c = I2C(0, I2C.MASTER, baudrate=100000)
i2c.writeto_mem(0x17, 0x00, b'\x01\x02\x03\x04')
  1. I2C_SLAVE_RECEIVE
  2. I2C_SLAVE_FINISH
  3. I2C_SLAVE_RECEIVE
  4. I2C_SLAVE_RECEIVE
  5. I2C_SLAVE_RECEIVE
  6. I2C_SLAVE_RECEIVE

`i2c_write_byte` discards byte if TX FIFO buffer is full

Hi thx for the library code u provided. This got me a working back and forth comm with an ESP32 with wire library.

I had to add some stall loops to the i2c_write_byte function to transmit multiple bytes (20) in a row. Without the code only 17 went thru.

I added the i2c_wait_until_ready_to_write function, which I then use in i2c_write_byte to make sure the buffer is ready to send.

/**
 * \brief Checks the Tx FIFO if not full
 * If full stall for maximum `max_wait_loops` times, 
 * returns false if the buffer was still full.
 * 
 * \param i2c I2C instance.
 * \param max_wait_loops stall this amount of loops until buffer is ready.
 */
    static inline bool i2c_wait_until_ready_to_write(i2c_inst_t *i2c, uint16_t max_wait_loops)
    {
        i2c_hw_t *hw = i2c_get_hw(i2c);
        while (!(hw->status & I2C_IC_STATUS_TFNF_BITS))
        {
            if (max_wait_loops-- == 0)
                return false;
        }
        return true;
    }

/**
 * \brief Push a byte into I2C Tx FIFO.
 * 
 * \param i2c I2C instance.
 * \param value Byte value.
 */
    static inline void i2c_write_byte(i2c_inst_t *i2c, uint8_t value)
    {
        i2c_hw_t *hw = i2c_get_hw(i2c);
        i2c_wait_until_ready_to_write(i2c, 3000);                     // required to wait for tx buffer to send
        hard_assert_if(I2C, !(hw->status & I2C_IC_STATUS_TFNF_BITS)); // Tx FIFO must not be full
        hw->data_cmd = value;
    }

maybe there is a better way to request a flush/send.

IRQ callback invoked just once?

I am utilizing your library as you suggested in issue #2 namely

Produce the data in your main loop, and consume it in the callback. If there's nothing ready to send yet, simply return from the callback and it will be invoked again. For producer-consumer scheme there's buffering and synchronization to deal with.

I am using pico/util/queue.h to pass the data from the main thread to the interrupt (I also experimented simply with volatile variables). The IRQ gets called without data being ready, and for my application this is a guarantee that will happen all the time, but for now I am just doing a test in which the data is not ready at the beginning. Based on what you wrote above, I expected that the callback would be automatically called a few more times, until the data was ready. Instead, the data gets prepared, but the IRQ callback is never called again, and everything comes to a stall.

I could make a dummy packet for these situations, but I really want to avoid that, since it's a lot of unnecessary traffic (and some additional code on both the sender and receiver side to deal with) which I really want to avoid. As such, can you please elaborate more on the "it will be invoked again" part? In what circumstances will that happen? Just making a single call to i2c_read_blocking on the controller side, and nothing else on the worker side, does not trigger that re-invocation, only a single invocation (from which I return as discussed above) and then a hang. Should I use timeouts on the controller and re-invoke the read? Or anything else on the callback to sign that the IRQ should not be cleared?

Sorry for being such a pester, I really counted on I2C to work in both directions and encountering all these issues is a huge disappointment about using the picos for my project.

transfer_in_progress gets stuck to true sometimes at higher bus speeds

Hello! First of all thank you for creating this awesome library.
We've used it in the firmware for an open source device we're building (https://github.com/badgeteam/mch2022-firmware-rp2040/tree/renze/i2c-debug).

We're encountering a problem: if we talk to the RP2040 at low speeds (8kHz bus speed) everything is stable but once we increase the bus speed to 100kHz (or 400kHz) we found that the RP2040 can get into a state where slave->transfer_in_progress gets stuck high until a new transfer is started from the bus master.

We're using that flag to implement a simple form of locking on the register map, preventing registers to be used by the firmware until the transfer is done. Thus when it gets stuck on true the firmware doesn't handle requests anymore.

What bus speed did you test this library at and do you have any idea what might be causing the transfer_in_progress flag to get stuck to true until a new transfer gets it unstuck again?

I2C bus blocked when Pico I2C in slave mode

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

Only with interrupts?

Thanks for providing this example, in fact the Raspberry Pi Pico C/C++ SDK is really incomplete in this regard!!!

Are interrupts like your library does really the only way to implement I2C slave on the Pico?

I need to do something like the following on my pico slave

while(true) {
    prepare some stuff
    write some_stuff to the i2c bus # ok to block
}

Of course I could change that code to use interrupt, but it becomes unnecessary more complicated, so I tried using i2c_write_blocking() and i2c_write_raw_blocking() but those do not seem to be working. Is it really so, or am I doing anything wrong?

Intermittent i2c slave mode errors, interrupts delayed or fired in the wrong order resulting in data corruption

Hi, I have been using your very useful library to create some test code to try and get to the bottom of an issue I originally encountered using I2C slave mode with Arduino-Pico.

I recreated a test scenario using pure SDK and your library and found similar issues, the issues are fully detailed in this post along with scope traces.
raspberrypi/pico-sdk#1102

The issues are only really noticeable at I2C bus speeds above 100KHz.
Earle Philhower added a recent commit to his Arduino-Pico Wire API implementation which provides a workaround for the bulk of the issues, getting the failure rate down to around 0.02%, from originally being up to 50% at 1Mhz bus speed. (fast mode plus)

It looks to me like the remaining issues are with the SDK or maybe the hardware.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.