joan2937 / pigpio

pigpio is a C library for the Raspberry which allows control of the General Purpose Input Outputs (GPIO).
The Unlicense
1.45k stars 407 forks source link

Issues using bsc_i2c slave with a slow master #537

Closed johnsoupir closed 2 years ago

johnsoupir commented 2 years ago

I'm working on a project using a Raspberry Pi as an I2C slave with an industrial control board master. I started from the example in the documentation and modified it to detect if incoming data was a read or write based on the number of bytes (if there is a better way to do this I missed it). Two bytes (0x11 0xAB) = a write, and the value (0xAB) is stored in an array. After the expected number of bytes is read the outgoing data is added to the FIFO/buffer. One byte messages are assumed to be a read, so the code just prints some info to the screen.

I tested the code with a 2nd Raspberry Pi emulating the master control board and the slave was able to successfully respond to the dummy master. However, when connected to the actual master everything crashes and burns (details ahead). I connected an oscilloscope to investigate and measured the master clock frequency as 333Hz (not a typo, actually 333 HERTZ. Confirmed with logic analyzer.).

I then set up my dummy master Pi with software I2C and set the delay to replicate the slow master. This gave the same crash and burn, confirming that the slow master is causing issues.

The problem: It appears bsc_i2c is returning values before the write from the master has been completed. For example: if the master is sending [ address, register, value ] lets say [ 0x13, 0x11, 0xAB ] the function will return [ 0x13, 0x11 ] and then on the next run return [ 0xAB ]. After that the master keeps sending more messages like [ 0x13, 0x11, 0xAC ] and the values continue coming out in random groupings of 1-3 bytes.

The really awful "fix": I threw a delay in the beginning of the i2c function and messed with different times. I found that a ~82ms delay will sometimes kind of work, but not reliable at all. Usually goes wrong after a couple reads and writes.

def i2c(id,tick): time.sleep(0.082) s, b, d = pi.bsc_i2c(I2C_ADDR)

I know a 333Hz master is a very unusual use case, but if there's any way to make this work I would be unbelievably happy. Any help or suggestions are very appreciated. Here's a cleaned up version of my code including the delay that makes it sort of work.


#!/usr/bin/env python
import time
import pigpio
from binascii import hexlify

I2C_ADDR=0x13

byteArray = [ 0x55, 0xAA, 0x06, 0x83, 0x58, 0x00, 0xF1, 0x00 ]

def i2c(id, tick):

    global pi
    global byteCount
    global endByte
    time.sleep(0.082)
    s, b, d = pi.bsc_i2c(I2C_ADDR)

    if b:
        if (len(d)>1):
            print("Write operation!")
            #Is this the first byte?
            print("D[1]=" + str(d[1]))
            if(d[1]==85):
                print("FIRST CONTROL BYTE!!!")
                #This is the first byte, reset byte counter
                byteCount=0
            print("byteCount: " + str(byteCount))
            #Store new byte in array
            byteArray[byteCount]=d[1]
            print("Set byte " + str(byteCount) + " to " + hex(d[1]))

            if(byteCount>=endByte):
                print("##### ADDING CONTROL BYTES TO BUFFER #####")
                s, b, d = pi.bsc_i2c(I2C_ADDR,byteArray)
                endByte=7

            byteCount+=1

        elif d[0] == 17: 
            #Had only one byte and it was 0x11, so this is a read
                print("READ OPERATION ON 0X11!!!")
                #Master is reading from buffer, cool

        else:
            #We have no idea. Somthing is broken.
                print("UNKNOWN OPPERATION!")

pi = pigpio.pi()

byteCount=0
endByte=7

if not pi.connected:
    exit()

# Respond to BSC slave activity
e = pi.event_callback(pigpio.EVENT_BSC, i2c)
pi.bsc_i2c(I2C_ADDR) # Configure BSC as I2C slave
time.sleep(600)
e.cancel()
pi.bsc_i2c(0) # Disable BSC peripheral
pi.stop
guymcswain commented 2 years ago

I'll look into it in more depth later but this caught my eye:

I then set up my dummy master Pi with software I2C and set the delay to replicate the slow master. This gave the same crash and burn, confirming that the slow master is causing issues.

Can you then run your dummy master at a faster rate and confirm if it is working? If this also fails maybe its not related to timing.

johnsoupir commented 2 years ago

@guymcswain thanks for checking into this!

I should have mentioned that I did bump my dummy master up to about 22khz and was able to read and write perfectly.

I believe it was also working with the full speed hardware I2C, but I was just running the example at that point. I'll wire it up to the hardware I2C at 100khz and verify tomorrow morning.

I could also start stepping down the dummy master speed and find the point where things start to go wrong if that would be helpful.

guymcswain commented 2 years ago

Also, please specify the hardware and os revisions that you are running.

johnsoupir commented 2 years ago

SOLVED!!! Turns out there is no problem with the library currently. It was just my old Raspbian and/or the older pigpio running on it. Sorry for the erroneous issue and thank you @guymcswain for taking the time to respond to this. Awesome library, and thank you to all the maintainers!

I checked the software running on the slave (Rpi 3B+) and discovered it was running an old Raspbian (Stretch from early 2018) and had not been updated in a while. Should have checked the OS and updated when I pulled it out. Hindsight is 20/20 and all.

Flashed a fresh Raspbian and made sure pigpio was up to date, and that did it! Looked around in the code a little and noticed the version of pigpio I had calls _pigpio_command_ext inside bsc_control, while the latest version calls _pigpio_command_ext_nolock. I don't understand the inner workings well enough to know if this was related to that function or not. Anyway, if somebody ends up here from google, check your pigpio version and update. If you don't have _pigpio_command_ext_nolock you'll know you've got an old version or your update failed.