Issues configuring Nucleo-L476RG board as an I2C Slave

Hey everybody, I am having issues running my Nucleo-L476RG as an I2C Slave

I found this example code using the I2CSlave class as a part of Mbed OS

#include <mbed.h>
#include "SerialStream.h"

int main()
{
    I2CSlave slave(SDA, SCL);
    slave.address(addr << 1); //keep actual address

    BufferedSerial serial(USBTX, USBRX, 115200);    
    SerialStream<BufferedSerial> pc(serial);

    pc.printf("I2C confiigured on 0x%X\n", addr);

    char buf[10];
    char msg[] = "Slave!";

    while (1) {
        int i = slave.receive();
        switch (i) {
            case I2CSlave::ReadAddressed: {
                int res = slave.write(msg, strlen(msg) + 1); // Includes null char
                pc.printf("wrote msg %d\n", res);
                break;
            }
            case I2CSlave::WriteGeneral:
                slave.read(buf, 10);
                pc.printf("Read G: %s\n", buf);
                break;
            case I2CSlave::WriteAddressed:
                slave.read(buf, 10);
                pc.printf("Read A: %s\n", buf);
                break;
        }
        for (int i = 0; i < 10; i++) {
            buf[i] = 0;    // Clear buffer
        }
    }
}

and I am attempting to interface with the board using a raspberry-pi 4 running the following Rust program

use rppal::i2c::I2c;

macro_rules! input {
    {} => {{
        input!("")
    }};

    ($a:expr) => {{
        use std::io;
        use std::io::Write;

        print!("{}", $a);
        let _ = io::stdout().flush();

        let mut line = String::new();
        io::stdin().read_line(&mut line).expect("Error reading from stdin");
        line.trim().to_string()
    }};
}

const ADDR: u16 = 0x34;

fn main() {
    let mut i2c = I2c::new().unwrap();
    i2c.set_slave_address(ADDR).unwrap();
    println!("{}", i2c.clock_speed().unwrap());

    loop {
        match input!("w or r").as_str() {
            "w" => {
                let msg: [u8; 6] = [65, 66, 67, 68, 69, 0];
                match i2c.write(&msg) {
                    Ok(_) => {},
                    Err(n) => {
                        println!("could not transmit, {}", n);
                    },
                };
            },
            "r" => {
                let mut buf: [u8; 100] = [0u8; 100];
                let num =  match i2c.read(&mut buf) {
                    Ok(n) => {n},
                    Err(n) => {println!("error could not read, {}", n);0},
                };
                let msg = &buf[0..num];
                let msg = match from_utf8(msg) {
                    Ok(n) => {n},
                    Err(_) => {
                        println!("=========");
                        for i in 0..num {
                            println!("\t{}", msg[i]);
                        }
                        println!("==========");
                        ""
                    },
                };
                println!("msg: {}", msg);
            },
            _ => {},
        }
    }
}

basically it prompts the user for input, w or r, and either writes to the slave or reads from the slave

I have gotten the write command to work


left is mbed console, right is raspi console

however when attempting to run a read command I get the following

the stm32 reports that the packet was sent perfectly, while the raspi gets errno 5, then errno 110 on consecutive attempts

I have also noticed that running sudo i2cdetect -y 1 on the raspberry pi console takes ~1 second to scan each address, which, along with the connection timed out errors from above, point to the idea that upon trying to write to the I2C master, the stm32 causes some kind of interference along the data line.

so far I have tried removing the print statements to see if it was a timing issue, as well as adding external pullup resistors both 2.2k and 4.7k (note the write command works perfectly fine with both resistors and no resistors)

Im not sure I can do much else because I don’t have a scope.
Any Ideas? Thanks.

EDIT 1

I managed to get access to an oscilloscope and have determined that when the master tries to read from the slave, the clock is stuck in a low position. Im not sure if this is due to a fault with the master or the slave, but ill keep looking

Hello,

because in i2c_api.c are not any changes between families (exclude STM32H7), then it is possible to compare it with any STM32 target (exclude STM32H7), I think.

I did a test with Nucleo-F303k8 and RPI3 in 2020. I do not know anything about Rust, but maybe you can try to reproduce similar steps without any script directly from console with an already build in driver (I do not know if the library what I used is also possible to use with Pi4, but it was a part of Raspberry Pi OS). With this you can verified if the result will be same.

Maybe helpful can be also this
Raspberry Pi Raspberry Pi I2c | Raspberry Pi (electronicwings.com)

BR, Jan

1 Like

thanks so much, I ended up trying out the console commands like you said.

The problem turned out to be a slight nuance with the library i am using. For anyone interested the i2c library im using has the following syntax to read from a slave

let mut buf: [u8; 100] = [0u8; 100];
let num =  i2c.read(&mut buf).unrwap()

the important thing to note here is that the read function does not take in a number of bytes to read, but instead tries to fill up the entire buffer.

This causes an issue on the pi, where it tries to read more bytes than are being sent and is put into an unrecoverable state.

so far, I have only been able to fix this with a hard reboot of the pi. This state explains the slowness of i2cdetect aswell as the future lack of ability to read / write bytes.

The strange part is, this appears to be a problem with only the raspberry pi, as reading too many bytes with pigs results in the same error as my rust script. while on my Nucleo board

slave.read(buf, 10);
pc.printf("Read A: %s\n", buf);

we read 10 bytes regardless of how many we write and it still works.

Anyways my final setup to get everything working is …

  • Gnd → Gnd

  • SDA → Bread Board → (SDA, 2.2k Ohm → 3.3V)

  • SCL → Bread Board → (SCL, 2.2k Ohm → 3.3V)

    and then only dealing with absolute known packet sizes

tl;dr, but afaik the problem with PI and I2C can be that the PI hardware does not support clock stretching.