Shop Learn
I2C/SMbus monitor — Parallax Forums

I2C/SMbus monitor

evanhevanh Posts: 11,685
edited 2021-08-30 08:26 in Propeller 2

I've just delved into I2C transactions in recent weeks. I've now started on making a monitor for observing activity. Yet again, I have to give kudos to Chip for an instruction set with conditional execution ... I've built the layer-2 capturing, in assembly, without any need for state tracker changes being decided via extra branching. The only branching, other than a subroutine, is the state changes! In assembly, that's a huge saving on mental resources to prevent bugs, not to mention it producing efficient code execution.

Of course, the speed of the prop2 means assembly wasn't really necessary at all. I'm comfortable with pasm2 so I took it as a challenge to do a high efficiency version. Sysclock can probably be quite low. Below 20 MHz for 100 kHz SCL.

As of yet, there's no Spin/C/Basic coding done and no layer-3 done. I vaguely intend to make it portable equipment.

First draft (entirely untested):

DAT
ORG  0
smb_monitor
        fltl    mbpin
        fltl    sclpin
        fltl    sdapin
        hubset  ##$4000_0060 | boundedtap   ' deglitch "filt0", 8 samples over 12.5% of bit-period
        wrpin   m_pinmon, sclpin        ' Schmitt-triggered and deglitched
        wrpin   m_pinmon, sdapin
        wrpin   #1, mbpin           ' mailbox repository for byte count (buffer index)
        dirh    mbpin
        wrfast  #$100, buffadr          ' 16 kByte ring-buffer

        testb   sclpin, #5  wc
    if_nc   sets    get_sample, #ina    ' if port A
    if_c    sets    get_sample, #inb    ' if port B
        bitl    sclpin, #5
        bitl    sdapin, #5


bus_unsynced
        call    #get_sample
    if_nz   jmp #bus_unsynced       ' loop while SCL low

        getct   pa
        addct1  pa, ticks50us       ' start 50 us timeout
        mov oldsam, sample      ' latch SDA on SCL rise
unsynced_scl_high
        call    #get_sample
    if_c    jct1    #idle_scl_high      ' if SDA high and timed-out then synced
        testb   oldsam, sdapin  xorc
if_nc_and_z jmp #unsynced_scl_high  ' loop while SCL high and SDA steady
    if_nz   jmp #bus_unsynced       ' ensure SCL high still


idle_scl_high
        testb   sample, sdapin  wc
        testbn  oldsam, sdapin  andc    ' if SDA rise
    if_c    wfword  #%01            ' indicates a stop bit
    if_c    add bytecount, #1
        wxpin   bytecount, mbpin
.idle_loop
        call    #get_sample
    if_z    jmp #.idle_loop     ' ignore SDA until SCL falls
    if_c    jmp #bus_unsynced       ' if SDA high then not a start


        getct   pa
        addct2  pa, ticks25ms       ' start 25 ms timeout
        mov count, #9       ' data bits, including ACK/NAK
        mov databyte, #1        ' indicates a start bit
bit_scl_low
        jct2    #bus_unsynced       ' error on timeout
        call    #get_sample
    if_nz   jmp #bit_scl_low        ' loop until SCL rises

        getct   pa
        addct1  pa, ticks50us       ' start 50 us timeout
        rcl databyte, #1        ' msbit first
        mov oldsam, sample      ' latch SDA on SCL rise
bit_scl_high
        jct1    #bus_unsynced       ' error on timeout
        call    #get_sample
        testb   oldsam, sdapin  xorc
if_c_and_z  jmp #bus_unsynced       ' error if SDA has changed
    if_z    jmp #bit_scl_high       ' loop until SCL falls
        djnz    count, #bit_scl_low ' loop for next bit


        shl databyte, #2        ' reserved for error/stop
        wfword  databyte        ' start + completed byte + ACK
        add bytecount, #1
char_scl_low
        jct2    #bus_unsynced       ' error on timeout
        call    #get_sample
    if_z    jmp #char_scl_low       ' ignore SDA until SCL rises

        getct   pa
        addct1  pa, ticks50us       ' start 50 us timeout
        rcl databyte, #1        ' possible next byte
        mov oldsam, sample      ' latch SDA on SCL rise
char_scl_high
        jct1    #bus_unsynced       ' error on timeout
        call    #get_sample
        testb   oldsam, sdapin  xorc
if_nc_and_z jmp #char_scl_high      ' loop until SCL falls or SDA changes

    if_c    jmp #idle_scl_high      ' if SDA changed then stop/restart
        mov count, #8       ' else next byte has already started
        mov databyte, #0
        jmp #bit_scl_low



get_sample
        mov sample, 0-0     '=== sample pins ===
        testb   sample, sdapin  wc  ' check SDA level
    _ret_   testb   sample, sclpin  wz  ' check SCL level



bytecount   long    0
ticks50us   long    (_clkfreq + 10000) / 20000
ticks25ms   long    (_clkfreq + 20) / 40
m_pinmon    long    P_SYNC_IO | P_SCHMITT_A | P_FILT0_AB | P_HIGH_FLOAT | P_LOW_FLOAT
m_pinsim    long    P_SYNC_IO | P_SCHMITT_A | P_FILT0_AB | P_LOW_1MA | P_HIGH_1MA

buffadr     long    0
sclpin      long    0
sdapin      long    0
mbpin       long    0
sample      long    0
oldsam      long    0
count       long    0
databyte    long    0

Comments

  • evanhevanh Posts: 11,685

    I suppose I should ask if anyone has done such a monitor already?

  • TubularTubular Posts: 4,406

    Definitely interested in this Evanh. We're starting to do a whole lot more with i2c

  • Is it intended to be something along the lines of I2CDriver?
    Well, the monitoring part anyway; on second look, it seems that can do some sort of active testing, too.
    Either way, yes, tools like that are great to have. 👍

  • evanhevanh Posts: 11,685

    Not a Propeller by the looks. Oh, Forth! That was unexpected. That's one language that bamboozles me.

  • ChrisGaddChrisGadd Posts: 290
    edited 2021-09-03 19:49

    I don't at all understand how your code works, but here's my take on a i2c monitor.
    It's essentially a stripped down version of my P1 slave object, ported to the P2 and with all of the writer pieces removed, converts received bits into an ASCII string.

  • evanhevanh Posts: 11,685

    Thanks Chris. I'll have a read of that.

    The monitoring process does not attempt to drive the bus at any stage. It's not really a master nor slave, it passively sits and watches. So the code is less as a result. It should be able to record both sent commands and the responses to those commands. The recording will indicate the various I2C bus states as the recording progresses. Maybe even time-stamped.

  • evanhevanh Posts: 11,685
    edited 2021-09-03 23:23

    Okay, yep, you've got it like that. And the whole test wrapper is nice. Good stuff.

    You'll note I've used TESTB, on a "sample" of the pins, rather than TESTP on the live pins. This prevents glitches from creating logic bugs in the state tracking.

    There's a few logic differences in the state tracker too. It may not be important. I have yet to do any testing.

Sign In or Register to comment.