Shop OBEX P1 Docs P2 Docs Learn Events
I2C dual bus question - Page 2 — Parallax Forums

I2C dual bus question

2»

Comments

  • TappermanTapperman Posts: 319
    edited 2015-04-17 11:55
    OK … I’m not getting a lot of input from forum users, so I guess we’ll just post all advances that I make along the way here. This is more for my benefit really, than anyone else. But, someone may see something that they can pop in with and make a contribution.

    From my last post, to Peter J, I mention that I was struggling with the start\bit routine. I was [size=+1]over[/size] thinking the issue … since I already know a device that adheres to the I2C standard for a slave will operate on front/rear … I can ignore what the rear bus is doing and concentrate on just ‘standard’ behavior for the start\bit routine.

    With one exception, “the front side of the bus has top priority.”

    So, the next issue is how do I detect a collision? For now, I’m using this arrangement during the testing:
            ┌────────┐
            │        │  330
            │   P    ├────┐
            │   R    ├──────┻────────>  SCK
            │   O    ├──────┳────────>  SDA
            │   P    ├────┘
            │        │  330
            └────────┘ 
    


    So, has anyone built a multi master routine? I checked out OBEX and saw Mike Green made a spin version (very nice), and someone else made a assembly version out of it. But, the start\bit routine does not check to see if another master is using the bus at the moment … So I will be flying solo on this one.

    ... Tim
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-17 14:16
    I have a multi-master object in this archive, only tested using multiple cogs on the same Prop but it performed flawlessly, however I wouldn't expect it work with scl and sda reversed, not without modification. Are you intending to have two i2c master objects running simultaneously in two different cogs, one for normal and one for reverse? If that's the case then it seems a bit of a waste, since only the forward or the reverse bus is going to be operating at any given time. My Spin-based driver shows how both forward and reversed may be accomplished in a single cog. You could even use a standard driver, start it with the normal pins for the front bus, then stop and restart it with the reverse pins for the back bus.

    btw, I have discovered an actual application for this idea. The CY8C9520A is a very nifty IO expander, but it has a bit of a drawback in that it stretches the clock following every device byte, so it interferes with the Prop bootloader which drives the clock and data lines high and low. I modified my reversed-pins Spin driver further to make it open-drain compatible, and got it working on same pins as the eeprom. The CY8C9520 is the only clock-stretching slave I have experience with, but there's no reason this shouldn't work with all of 'em.

    Chris

    Thanks for the explanation of the state machine, but I'm afraid I'm still not getting it, and you're more than welcome to use the graphic.
  • jmgjmg Posts: 15,173
    edited 2015-04-17 15:02
    ChrisGadd wrote: »
    btw, I have discovered an actual application for this idea. The CY8C9520A is a very nifty IO expander, but it has a bit of a drawback in that it stretches the clock following every device byte, so it interferes with the Prop bootloader which drives the clock and data lines high and low. I modified my reversed-pins Spin driver further to make it open-drain compatible, and got it working on same pins as the eeprom. The CY8C9520 is the only clock-stretching slave I have experience with, but there's no reason this shouldn't work with all of 'em.
    CY8C9520 looks interesting, seems MCU based, with highish Icc.

    Do you find it stretch the clock after every byte ? By how much ?
    I think you are saying it does this stretch, even when not addressed itself ?

    Data only says master needs support stretch, and says this re "EEPROM"
    [" When a 64-byte boundary is crossed in the EEPROM, the I2C clock is stretched while the device performs
    an EEPROM write sequence.
    If the end of available EEPROM space is reached, then further writes are responded to with a NAK."]
  • jmgjmg Posts: 15,173
    edited 2015-04-17 15:08
    Tapperman wrote: »
    .... But, the start\bit routine does not check to see if another master is using the bus at the moment … So I will be flying solo on this one.
    Polling always has aperture issues, so you cannot be 100% certain you catch 'other master' use.
    Normally, Multimaster arbitration follows every bit, and releases when it finds PinLevel <> Expected level
  • TappermanTapperman Posts: 319
    edited 2015-04-17 15:54
    jmg wrote: »
    Polling always has aperture issues, so you cannot be 100% certain you catch 'other master' use.
    Normally, Multimaster arbitration follows every bit, and releases when it finds PinLevel <> Expected level

    Yeah ... I saw that with the CAN standard ... simply lovely how the lower id number wins out over the high number device ... absolute genius ... but, I don't think I need to resort to any 'funny' techniques? Should be able to stay within I2C standard and still operate ... was just trying to 'visualize' my approach for detecting activity by another master on the bus ... haven't been able to see any other code by the 'heavy weights' on this forum regarding 'multi-master' start\bit routine.

    My logic tells me that "just before 'start\bit' occurs, I should have a counter on the data_pin, detecting 'negative' edges. Any count in PHSA/B would clear PHSA/B wait (maybe 511 bit paces) and try up to two more times before 'strike three' is reached? Otherwise, seize bus as normal.

    So, the 3 ways you enter this routine are:

    1) The bus is 'free/idle' and not seized by this master ... both data and clock are '1' and PHSA/B is zero.
    2) This master already has the bus seized (shown by DIRA) and data and clock are low.
    3) The bus is in use by another master.


    ... Tim

    BTW - Thanks for input.
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-17 16:15
    jmg wrote: »
    CY8C9520 looks interesting, seems MCU based, with highish Icc.

    Do you find it stretch the clock after every byte ? By how much ?
    I think you are saying it does this stretch, even when not addressed itself ?

    Data only says master needs support stretch, and says this re "EEPROM"
    [" When a 64-byte boundary is crossed in the EEPROM, the I2C clock is stretched while the device performs
    an EEPROM write sequence.
    If the end of available EEPROM space is reached, then further writes are responded to with a NAK."]

    Yep, it stretches after every single device ID byte, between the r/w bit and the ack bit, in order to determine who is being addressed. If it is being addressed, it continues to stretch the clocks following the register address bytes and data bytes, for both the EEPROM and the IO functions. It does not stretch clocks after it determines that some other device is being addressed.
    The stretch duration varies depending on how many bytes it receives (more bytes, the longer the duration) but seems to be between 400us and 800us.

    I2C traffic
  • jmgjmg Posts: 15,173
    edited 2015-04-17 16:18
    Tapperman wrote: »
    Yeah ... I saw that with the CAN standard ... simply lovely how the lower id number wins out over the high number device ... absolute genius ...

    My logic tells me that "just before 'start\bit' occurs, I should have a counter on the data_pin, detecting 'negative' edges.
    That reduces the aperture, but does not eliminate it entirely.
    'negative' edge detect will have a few SysClks of aperture, more careful edge timing can check to 1 sysclk, but that still leaves the case where two masters fluke identical-cycle access. Both get expected-edge-time success.
    In that case, the bit-by-bit check is needed to resolve & also covers masters in different Props.

    idea : If you are ok this only works on same-prop masters, then maybe doing a hub access right before the Start, and using gated-timer timing, can ensure the phase-lock cannot occur,
    The Hub access I think will adjust the phase of following opcodes, and they cannot then both drive the pin on same sysclk edge.
  • jmgjmg Posts: 15,173
    edited 2015-04-17 16:26
    ChrisGadd wrote: »
    Yep, it stretches after every single device ID byte, between the r/w bit and the ack bit, in order to determine who is being addressed....
    The stretch duration varies depending on how many bytes it receives (more bytes, the longer the duration) but seems to be between 400us and 800us.

    I2C traffic

    Nice i2c monitor, what is that ?
    Sounds like they use a (slow) MCU with software address matching & the stretch in all ID cases is going to surprise many.
    Makes it look like a wait-for-scl high, is a good addition to any i2c library.
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-17 16:38
    jmg wrote: »
    Nice i2c monitor, what is that ?
    Sounds like they use a (slow) MCU with software address matching & the stretch in all ID cases is going to surprise many.
    Makes it look like a wait-for-scl high, is a good addition to any i2c library.

    I'm using a Digilent Electronics Explorer board, they also have an Analog Discovery device which is more akin to a Propscope.

    Yeah, it is a very slow controller in that IO expander; playing with it some more I got just under 2ms of stretching in some instances. Even if it weren't interfering with the Prop bootloader, I wouldn't want something like that on a bus I was using with other devices.
  • TappermanTapperman Posts: 319
    edited 2015-04-17 16:44
    jmg wrote: »
    ... look like a wait-for-scl high, is a good addition to any i2c library.

    Agreed!

    ... Tim
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-17 17:07
    Clock-stretching slaves don't really seem to be that common, and the check for scl high does slow the drivers a bit. My Spin-based push-pull (non-clock-stretch-checking) I2C driver is capable of ~28Kbps, the open-drain Spin driver which does check runs at about 20Kbps. My push-pull PASM driver is capable of 1Mbps, the open-drain version with checks is limited to ~425Kbps.
  • TappermanTapperman Posts: 319
    edited 2015-04-17 17:12
    ChrisGadd wrote: »
    My push-pull PASM driver is capable of 1Mbps, the open-drain version with checks is limited to ~425Kbps.

    Wow ... that tool you have is sweet! And the information you've provide is really helpful ... thanks ever so much.

    ... Tim
  • jmgjmg Posts: 15,173
    edited 2015-04-17 17:13
    Checking will always take time, but that seems like quite a hit on the PASM case ?
    Is that doing more than just waiting on SCL high ?
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-17 17:56
    jmg wrote: »
    Checking will always take time, but that seems like quite a hit on the PASM case ?
    Is that doing more than just waiting on SCL high ?
    I'm perhaps being overcautious with the checks, or perhaps not. While my IO expander stretches the clock before the ack pulse, I know there are other devices that stretch after the ack, so two checks are necessary to ensure compatibility with all devices. And the check_stretch routine is a bit more complex due to the desire to maintain the bitrate if no stretching occurs, and to guarantee that a waitcnt won't be missed if stretching does occur.
    check_stretch
                            test      SCL_mask,ina                wc                ' Check for stretching
              if_c          jmp       check_stretch_ret                             '  return if no stretching
                            mov       t1,delay_target                               ' Loop for 1 pulse width for clock to return high
                            sub       t1,cnt
                            cmps      t1,#0                       wc
              if_nc         jmp       #check_stretch
                            waitpeq   SCL_mask,SCL_mask                             ' Wait however long it takes for clock to return high
                            mov       delay_target,bit_delay                        ' Re-establish the bitrate
                            add       delay_target,cnt          
    check_stretch_ret       ret
    

    Previous versions were using this, but I abandoned it after realizing the discrepancy between the desired and actual bitrates; a setting of 400Kbps was actually sending at ~355Kbps.
                            waitpeq   SCL_mask,SCL_mask                             ' test for clock-stretching
                            mov       cnt,I2C_bit_delay                             ' re-establish bitrate 
                            add       cnt,cnt                                                                             
                            waitcnt   cnt,I2C_bit_delay
    
  • jmgjmg Posts: 15,173
    edited 2015-04-17 18:08
    ChrisGadd wrote: »
    Previous versions were using this, but I abandoned it after realizing the discrepancy between the desired and actual bitrates; a setting of 400Kbps was actually sending at ~355Kbps.
                            waitpeq   SCL_mask,SCL_mask                             ' test for clock-stretching
                            mov       cnt,I2C_bit_delay                             ' re-establish bitrate 
                            add       cnt,cnt                                                                             
                            waitcnt   cnt,I2C_bit_delay
    
    It makes sense that bit_Delay would need a tweak, as waitpeq adds to that., (and there will also be some rise time effects on large pullup values), but that should not impact (corrected) baud speeds, until you run out of wait time...
  • TappermanTapperman Posts: 319
    edited 2015-04-18 00:05
    ChrisGadd wrote: »
    Clock-stretching slaves don't really seem to be that common, and the check for scl high does slow the drivers a bit.

    I'm curious about something. What if during the time we transmit the device number (open drain), we slowed the clock rate down. Then, after receiving the acknowledge from the device, we switch to push pull in full speed? At present, you're the logical one to test this, since you have the uncommon chip.

    Any thoughts? By the way, does that chip stretch the clock bit for every device number broadcast over the bus?

    ... Tim
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-18 11:26
    jmg wrote: »
    It makes sense that bit_Delay would need a tweak, as waitpeq adds to that., (and there will also be some rise time effects on large pullup values), but that should not impact (corrected) baud speeds, until you run out of wait time...

    You're right, of course, simply checking the clock line shouldn't have resulted in that much of a difference. I realized that I was checking on the wrong side of a waitcnt in one place, fixing that boosted to max speed considerably. In examining that, I found a potentially fatal flaw where the clock might float high just before the bit delay expired and still miss the waitcnt. Here's my re-worked logic:
                            andn      dira,SCL_mask
    'check_stretch                                                                 
                            waitcnt   delay_target,bit_delay                        ' allow 1/4 bit width for clock to go high - an unstretched clock always manages to float high within this time, even with 10K pullups
                            test      SCL_mask,ina                wc
              if_c          jmp       #:lower
                            waitpeq   SCL_mask,SCL_mask                             ' wait for clock to go high
                            mov       delay_target,bit_delay                        ' re-establish bitrate
                            add       delay_target,cnt
    :lower
                            waitcnt   delay_target,bit_delay
    
    It's also written in-line, eliminating the time wasted on calls and rets. The same idea is used with the read bit routines which require a couple instructions before the final waitcnt, which limits the max speed to just over 900Kbps.
    Tapperman wrote: »
    I'm curious about something. What if during the time we transmit the device number (open drain), we slowed the clock rate down. Then, after receiving the acknowledge from the device, we switch to push pull in full speed? At present, you're the logical one to test this, since you have the uncommon chip.
    The bitrate doesn't seem to have much, if any, affect on the duration of the clock stretching. The 20Kbps Spin driver and PASM driver running at 100Kbps (max speed for this device) both stretched the clock between 300us and 750us.
    By the way, does that chip stretch the clock bit for every device number broadcast over the bus?
    Yes, it stretches the clock for every device byte. If the CY8C is being addressed, it continues stretching the clocks for every byte it receives. If some other device is being addressed, say an EEPROM, the register and data bytes following the device ID are ignored by the CY8C, but the device ID always gets stretched.
    This is the only clock-stretching slave I have experience with, though I know some other devices stretch the clock after the ack bit and before the 1st bit of the next byte, and I'd guess those wouldn't stretch clocks for other devices on the bus.

    High-level, general-purpose drivers are tricky due to the endless variations in how manufacturers design their I2C devices. Most pressure sensors, for instance, take a device byte followed by a command byte--what would normally be a register byte--and no data bytes. The ADS1000 A/D converter likewise doesn't use registers, configuration is written immediately after the device byte. For reading, it expects the device ID with r/w bit set, after which two data bytes are clocked out. It's for these edge cases that I now include an arbitrary method in my drivers, send any number of bytes and read any number back.
  • TappermanTapperman Posts: 319
    edited 2015-04-18 14:28
    ChrisGadd wrote: »
    I have a multi-master object in this archive

    Wow ... I missed that!! That's precisely what I was trying to write ... and you have it written already! Thanks.

    I see you tested yours in two cogs ... that was what I was going to try in my earlier post! I'm a little slow folks.

    ... Tim

    PS - Going blind slowly
  • TappermanTapperman Posts: 319
    edited 2015-04-20 08:21
    ChrisGadd wrote: »
    ... tested using multiple cogs on the same Prop but it performed flawlessly, however I wouldn't expect it work with scl and sda reversed ...

    If it sticks to the bus standard, it should?

    BTW, I'm curious about this snippet from Multi-Mstr\I2C start ... what are the marked lines doing?
    :loop1                                                                          ' This loop allows time for the bus to float up to idle
                            test      idle_mask,ina               wc,wz
              if_nc_and_nz  jmp       #:loop2_init
    >                       mov       cnt,target_cnt
    >                       sub       cnt,cnt
    >                       cmps      cnt,#0                      wc
              if_nc         jmp       #:loop1
                            jmp       #main                                         ' Abort if bus doesn't return to idle within 1/4 bit-width
    

    I thought 'cnt' was read-only in each cog? First time I've seen it the dest.

    ... Tim

    PS - The Manual says "CNT is a read-only pseudo-register; when used as an instruction’s source operand, it reads the current value of the System Counter. Do not use CNT as the destination operand; that only results in reading and modifying the shadow register whose address CNT occupies."
  • jmgjmg Posts: 15,173
    edited 2015-04-20 14:13
    Tapperman wrote: »

    BTW, I'm curious about this snippet from Multi-Mstr\I2C start ... what are the marked lines doing?
    :loop1                                                                          ' This loop allows time for the bus to float up to idle
                            test      idle_mask,ina               wc,wz
              if_nc_and_nz  jmp       #:loop2_init
    >                       mov       cnt,target_cnt
    >                       sub       cnt,cnt
    >                       cmps      cnt,#0                      wc
              if_nc         jmp       #:loop1
                            jmp       #main                                         ' Abort if bus doesn't return to idle within 1/4 bit-width
    

    I thought 'cnt' was read-only in each cog? First time I've seen it the dest.

    ... Tim

    PS - The Manual says "CNT is a read-only pseudo-register; when used as an instruction’s source operand, it reads the current value of the System Counter. Do not use CNT as the destination operand; that only results in reading and modifying the shadow register whose address CNT occupies."

    I think that is a 'trick' that does not make for read-able code - effectively that overlays a TMP_VAR with the shadow register.
    By using the shadow register, it saves using a register elsewhere, but should really be declared with a name less lazy.

    The whole CNT handling in Prop is a good argument for MACROs.
  • ChrisGaddChrisGadd Posts: 310
    edited 2015-04-20 16:41
    To answer the question, shadow registers are registers that can only be operated on through the destination field. In addition to cnt, there is also a shadow par and shadow ina/inb. It's not that uncommon a trick, and the code is only unreadable 'til you learn it. I like using cnt in the dest field as it's a pre-defined, aptly-named register in a role that it seems ideally suited for.
      mov      cnt,interval_delay
      add      cnt,cnt
      waitcnt  cnt,interval_delay
    
    ' does the same thing as
      
      mov      delay_target,interval_delay
      add      delay_target,cnt
      waitcnt  delay_target,interval_delay
    
  • jmgjmg Posts: 15,173
    edited 2015-04-20 17:09
    ChrisGadd wrote: »
    .... and the code is only unreadable 'til you learn it.
    The same can also be said of Swahili and Arabic ... ;)
    ChrisGadd wrote: »
    I like using cnt in the dest field as it's a pre-defined, aptly-named register in a role that it seems ideally suited for.
    Using the shadow register address makes sense, but using the same name is not so sensible.
    add cnt,cnt
    visibly doubles count, but wait, no, magical overlay use means that is not what really happens, in this special case...
    Code is less maintainable and less portable across designers.

    More polished would be a tool chain that pre-defined shadow registers (and also checked their misuse)
      mov      sr_cnt,interval_delay
      add      sr_cnt,cnt
      waitcnt  sr_cnt,interval_delay
    
  • TappermanTapperman Posts: 319
    edited 2015-04-21 12:05
    Working this Stick Adapter into the bus ... and seems to work ok? Guess I can make a board for it now ... free up my bread board.

    I find the /INT does not work ... unless you send the stop bit ... then I lower the clock line and wait for the return pulse on the data line.

    This is big improvement over my old stick adapter ... it had 2 timing caps and was producing results that needed scaling to each other ... a pain to calibrate.

    This adapter uses the same timing cap for both axis, producing results that are 'comparable' to each other without the need for scaling.

    ... Tim

    PS - Tried many different approaches ... but could not get original image to load ... using the schematic instead from express pc
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-05-07 11:55
    The marked lines are doing the same thing that the following lines would do:
                           mov       scratch,target_cnt
                           sub       scratch,cnt
                           cmps      scratch,#0                      wc
    
    When cnt is used in the destination, its shadow register gets used instead. It's just a way of saving a register she you're running low.

    -Phil
Sign In or Register to comment.