Shop OBEX P1 Docs P2 Docs Learn Events
P2 smart pins I2C and SPI mode 3 configuration! — Parallax Forums

P2 smart pins I2C and SPI mode 3 configuration!

Hi All,

Does anyone have an example of I2C bus using the smart pins? I am currently bit-banging the bus and it works but looks like the timing isn't ideal. I would like something a bit more reliable.

I know I can bit-bang SPI and get fairly decent results at low speeds but fear that at higher speeds say like 4 or even 8 MHz I would need to use the P2 smart pins to clock data in and out reliably. The application I am using the SPI bus for requires high reliability. The SPI bus will be controlling high-power stepper drivers and the bus will be active the entire time sending control data and receiving status information.

I have found a few discussions here and there about SPI but nothing much about smart pins and SPI. Does anyone have examples of SPI mode 3? I need to send 40 bits per transaction (five bytes). The chips will be daisy-chained to create a long shift register. So this long shift register would be 40 bits x the number of drivers connected to the bus.

Any help is greatly appreciated and I know you all don't disappoint :smiley:

«1

Comments

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-05-04 23:29

    There is no smart pin I2C mode. You can use pin configuration features to enable pull-ups if you don't have physical resistors on the bus. Again, I'm a Spin programmer, but I think Ross has made it pretty easy to employ the same features in Catalina.

    You can to SPI with smart pins. I've attached a simple flash interface for the P2 that uses this the SPI modes (one for MOSI, one for MISO, one for SCLK). For mode three, you'll want to invert the clock. Change this line

      m := P_PULSE | P_OE                                           ' spi clock mode 
    

    to

      m := P_PULSE | P_OE | P_INVERT_A                              ' spi clock mode 
    
  • @JonnyMac said:
    There is no smart pin I2C mode. You can use pin configuration features to enable pull-ups if you don't have physical resistors on the bus. Again, I'm a Spin programmer, but I think Ross has made it pretty easy to employ the same features in Catalina.

    You can to SPI with smart pins. I've attached a simple flash interface for the P2 that uses this the SPI modes (one for MOSI, one for MISO, one for SCLK). For mode three, you'll want to invert the clock. Change this line

      m := P_PULSE | P_OE                                           ' spi clock mode 
    

    to

      m := P_PULSE | P_OE | P_INVERT_A                              ' spi clock mode 
    

    Thanks Jon I will give it a try :smile:

  • jmgjmg Posts: 15,175

    @enorton said:
    Does anyone have an example of I2C bus using the smart pins?

    Did you mean i2c or SPI here ?
    i2c is usually just bit-banged, as the speeds are modest, and some live checking is needed for ACK or clock stretch (if done by slaves)
    START and STOP would always be managed in SW, but I suppose in theory you could use burst clocking and two parallel smart pins in 9 bit TX/RX to send/check data and acks.

    I know I can bit-bang SPI and get fairly decent results at low speeds but fear that at higher speeds say like 4 or even 8 MHz I would need to use the P2 smart pins to clock data in and out reliably.
    The application I am using the SPI bus for requires high reliability. The SPI bus will be controlling high-power stepper drivers and the bus will be active the entire time sending control data and receiving status information.

    SW vs HW should not really be any less reliable, the advantage of HW is that the SW has more time to do other stuff

    I need to send 40 bits per transaction (five bytes). The chips will be daisy-chained to create a long shift register. So this long shift register would be 40 bits x the number of drivers connected to the bus.

    If you have high power systems and chances of live connection, you may like to add checks for clock glitches.
    That's where an external spike is large enough to be seen by any one of the daisy chain parts.
    One check is to create a ring, ie have the last output feed back into P2 to be checked for alignment.

  • @jmg said:

    @enorton said:
    Does anyone have an example of I2C bus using the smart pins?

    Did you mean i2c or SPI here ?
    i2c is usually just bit-banged, as the speeds are modest, and some live checking is needed for ACK or clock stretch (if done by slaves)
    START and STOP would always be managed in SW, but I suppose in theory you could use burst clocking and two parallel smart pins in 9 bit TX/RX to send/check data and acks.

    I need both I2C and SPI. Ah, I see what you mean. Maybe with I2C it has to be bit banged. I'm used to microcontrollers having the hardware peripherals to do this sort of stuff for me. I have become lazy with this and now using the P2 I have to get creative and make the protocols from examples everyone provides which isn't bad it's just different.

    I know I can bit-bang SPI and get fairly decent results at low speeds but fear that at higher speeds say like 4 or even 8 MHz I would need to use the P2 smart pins to clock data in and out reliably.
    The application I am using the SPI bus for requires high reliability. The SPI bus will be controlling high-power stepper drivers and the bus will be active the entire time sending control data and receiving status information.

    SW vs HW should not really be any less reliable, the advantage of HW is that the SW has more time to do other stuff

    Well, I would sleep better at night knowing the bits are twiddling like they should, and the machine isn't going to cause any problems in operation. I am thinking of safety here more than anything.

    I need to send 40 bits per transaction (five bytes). The chips will be daisy-chained to create a long shift register. So this long shift register would be 40 bits x the number of drivers connected to the bus.

    If you have high power systems and chances of live connection, you may like to add checks for clock glitches.
    That's where an external spike is large enough to be seen by any one of the daisy chain parts.
    One check is to create a ring, ie have the last output feed back into P2 to be checked for alignment.

    Yes, when I design the board, it will have safety built in for scenarios like this.

  • evanhevanh Posts: 16,027

    A heads-up on high-performance data rates:
    Tx smartpins are impacted by some I/O staging registers. You won't notice it at slower data rates but once you are approching sysclock frequency (A low divider ratio) you'll start to see a data phase lag with respect to SPI clock. Divider ratios below 8 are very obvious and need attention to compensate - Usually flip the clock edge as a start.
    Rx smartpins don't have to worry about the staging registers as there the signal paths are identical for both data and clock. They can operate up to sysclock/3 ratio.

    That's for when the Prop2 is the master device. As s slave device there's another extra problem of the SPI clock pins not being true clock inputs so there is a extra level of clock jitter introduced between the external SPI clock and the Prop2's internal sysclock.

  • jmgjmg Posts: 15,175

    @enorton said:

    @jmg said:

    @enorton said:
    Does anyone have an example of I2C bus using the smart pins?

    Did you mean i2c or SPI here ?
    i2c is usually just bit-banged, as the speeds are modest, and some live checking is needed for ACK or clock stretch (if done by slaves)
    START and STOP would always be managed in SW, but I suppose in theory you could use burst clocking and two parallel smart pins in 9 bit TX/RX to send/check data and acks.

    I need both I2C and SPI. Ah, I see what you mean. Maybe with I2C it has to be bit banged.
    I'm used to microcontrollers having the hardware peripherals to do this sort of stuff for me.

    Even where MCU have had i2c peripherals, for master i2c I've tended to use SW.
    I've enabled the hardware only when i2c slave mode was needed.

    If you have high power systems and chances of live connection, you may like to add checks for clock glitches.
    That's where an external spike is large enough to be seen by any one of the daisy chain parts.
    One check is to create a ring, ie have the last output feed back into P2 to be checked for alignment.

    Yes, when I design the board, it will have safety built in for scenarios like this.

    IIRC there is also some pin filtering available, but not easy to find full info.
    That could allow a higher sysclk to give a majority vote noise filter, at least for the P2 4~8MHz SPI. It does not help the remote chips.

    Data mentions
    100 A, B, both filtered using global filt0 settings
    101 A, B, both filtered using global filt1 settings
    110 A, B, both filtered using global filt2 settings
    111 A, B, both filtered using global filt3 settings

    and a diagram appears here, tho I thought the filter CLK was variable ?

    https://forums.parallax.com/discussion/comment/1519013/#Comment_1519013

  • @jmg said:

    @enorton said:

    @jmg said:

    @enorton said:
    Does anyone have an example of I2C bus using the smart pins?

    Did you mean i2c or SPI here ?
    i2c is usually just bit-banged, as the speeds are modest, and some live checking is needed for ACK or clock stretch (if done by slaves)
    START and STOP would always be managed in SW, but I suppose in theory you could use burst clocking and two parallel smart pins in 9 bit TX/RX to send/check data and acks.

    I need both I2C and SPI. Ah, I see what you mean. Maybe with I2C it has to be bit banged.
    I'm used to microcontrollers having the hardware peripherals to do this sort of stuff for me.

    Even where MCU have had i2c peripherals, for master i2c I've tended to use SW.
    I've enabled the hardware only when i2c slave mode was needed.

    If you have high power systems and chances of live connection, you may like to add checks for clock glitches.
    That's where an external spike is large enough to be seen by any one of the daisy chain parts.
    One check is to create a ring, ie have the last output feed back into P2 to be checked for alignment.

    Yes, when I design the board, it will have safety built in for scenarios like this.

    IIRC there is also some pin filtering available, but not easy to find full info.
    That could allow a higher sysclk to give a majority vote noise filter, at least for the P2 4~8MHz SPI. It does not help the remote chips.

    Data mentions
    100 A, B, both filtered using global filt0 settings
    101 A, B, both filtered using global filt1 settings
    110 A, B, both filtered using global filt2 settings
    111 A, B, both filtered using global filt3 settings

    and a diagram appears here, tho I thought the filter CLK was variable ?

    https://forums.parallax.com/discussion/comment/1519013/#Comment_1519013

    I will buffer the remote chips to give more drive strength. I'll look into the filtering stuff. Thank you for pointing this out :smile:

  • @JonnyMac said:
    There is no smart pin I2C mode. You can use pin configuration features to enable pull-ups if you don't have physical resistors on the bus. Again, I'm a Spin programmer, but I think Ross has made it pretty easy to employ the same features in Catalina.

    You can to SPI with smart pins. I've attached a simple flash interface for the P2 that uses this the SPI modes (one for MOSI, one for MISO, one for SCLK). For mode three, you'll want to invert the clock. Change this line

      m := P_PULSE | P_OE                                           ' spi clock mode 
    

    to

      m := P_PULSE | P_OE | P_INVERT_A                              ' spi clock mode 
    

    Hi Jon,

    What are these: %0_00000, %1_00000, 1_000? What do they do? Are they binary?

  • Yes, in Spin, % designates binary, and $ designates hex.

  • @JonnyMac said:
    Yes, in Spin, % designates binary, and $ designates hex.

    Ah, ok, I thought so I just wanted to make sure. Now when you write say for instance:

    %0_00000 it equals: 0
    %1_00000 it equals: 1
    1_000 equals: 1000

    Is the underscore sort of like a comma separator? Did I get that right?

  • @JonnyMac said:
    Yes, in Spin, % designates binary, and $ designates hex.

    I also noticed this:

    x.word[0] := 2 #> (clkfreq / (khz * 1_000)) <# $FFFF ' ticks in period
    x.word[1] := x.word[0] >> 1 ' ticks in low cycle (50%)

    Is the x.word a struct or is it an array of 2?

    Also, what does the #> and <# do?

    Is there a SPIN2 to C syntax chart explaining what these operators do?

  • evanhevanh Posts: 16,027

    @enorton said:
    %0_00000 it equals: 0

    yes.

    %1_00000 it equals: 1

    0b100000 or 0x20 or 32 in C.

    1_000 equals: 1000

    0b1000 or 8 in C.

    Is the underscore sort of like a comma separator? Did I get that right?

    Yes, just for visuals. The usual reason for placement of underscores is to indicate the hardware's bit groupings within a mode word.

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-05-05 14:41

    %1_00000 it equals: 1

    No. The underscore is ignored by the Spin compiler; %100000 is decimal 32.

    In Spin, one can have longs (32 bits), words (16 bits), and bytes (8 bits). Subsections of a value can be isolated, hence x.word[1] is the high 16 bits of x, x.byte[0] is the low 8 bits. You're correct in thinking that a long can be treated as an array of two words, or an array of four bytes. A word can be treated as an array of two bytes. Spin treats all variables as arrays of bits, so I could do something like this:

      repeat i from 0 to 15 step 2
        x.byte[3].[i] := 1
    

    Also, what does the #> and <# do?

    This are the minimum (floor) and maximum (ceiling) operators. I use that particular style to constrain a value between two endpoints

      x := 2 #> x <# $FFFF
    

    ... forces x to be at least two and no more than $FFFF. Another way:

      x := (x < 2) ? 2 : x
      x := (x > $FFFF) ? $FFFF : x
    

    Is there a SPIN2 to C syntax chart explaining what these operators do?

    Not that I'm aware of. This link will take you to the operators section of the online Spin2 docs
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/edit#heading=h.tztltoskeb64

  • evanhevanh Posts: 16,027

    @enorton said:
    x.word[0] := 2 #> (clkfreq / (khz * 1_000)) <# $FFFF ' ticks in period
    x.word[1] := x.word[0] >> 1 ' ticks in low cycle (50%)

    Is the x.word a struct or is it an array of 2?

    Kind of an array. WORD means a 16-bit memory access using double-byte sized indexing starting from the symbol's address. The symbol doesn't have to be any specific type or size - No safeties, all memory addressable.

    Also, what does the #> and <# do?

    Bounding like min()/max(). I keep forgetting which is which and have to look them up every time.

    Is there a SPIN2 to C syntax chart explaining what these operators do?

    There is the Spin2 manual, see Google Doc named "Spin 2 Language Documentation" - https://www.parallax.com/propeller-2/documentation/

  • @evanh said:

    @enorton said:
    x.word[0] := 2 #> (clkfreq / (khz * 1_000)) <# $FFFF ' ticks in period
    x.word[1] := x.word[0] >> 1 ' ticks in low cycle (50%)

    Is the x.word a struct or is it an array of 2?

    Kind of an array. WORD means a 16-bit memory access using double-byte sized indexing starting from the symbol's address. The symbol doesn't have to be any specific type or size - No safeties, all memory addressable.

    Also, what does the #> and <# do?

    Bounding like min()/max(). I keep forgetting which is which and have to look them up every time.

    Is there a SPIN2 to C syntax chart explaining what these operators do?

    There is the Spin2 manual, see Google Doc named "Spin 2 Language Documentation" - https://www.parallax.com/propeller-2/documentation/

    Thank you for this :smile:

  • @evanh said:

    @enorton said:
    %0_00000 it equals: 0

    yes.

    %1_00000 it equals: 1

    0b100000 or 0x20 or 32 in C.

    1_000 equals: 1000

    0b1000 or 8 in C.

    Is the underscore sort of like a comma separator? Did I get that right?

    Yes, just for visuals. The usual reason for placement of underscores is to indicate the hardware's bit groupings within a mode word.

    Ahhh, ok, I see now. SPIN2 is different and hard to wrap my brain around...

  • @JonnyMac said:

    %1_00000 it equals: 1

    No. The underscore is ignored by the Spin compiler; %100000 is decimal 32.

    In Spin, one can have longs (32 bits), words (16 bits), and bytes (8 bits). Subsections of a value can be isolated, hence x.word[1] is the high 16 bits of x, x.byte[0] is the low 8 bits. You're correct in thinking that a long can be treated as an array of two words, or an array of four bytes. A word can be treated as an array of two bytes. Spin treats all variables as arrays of bits, so I could do something like this:

      repeat i from 0 to 15 step 2
        x.byte[3].[i] := 1
    

    Also, what does the #> and <# do?

    This are the minimum (floor) and maximum (ceiling) operators. I use that particular style to constrain a value between two endpoints

      x := 2 #> x <# $FFFF
    

    ... forces x to be at least two and no more than $FFFF. Another way:

      x := (x < 2) ? 2 : x
      x := (x > $FFFF) ? $FFFF : x
    

    Is there a SPIN2 to C syntax chart explaining what these operators do?

    Not that I'm aware of. This link will take you to the operators section of the online Spin2 docs
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/edit#heading=h.tztltoskeb64

    Ok, thank you. I think I know of a way to write the bounds in c :smile:

  • @JonnyMac said:

    %1_00000 it equals: 1

    No. The underscore is ignored by the Spin compiler; %100000 is decimal 32.

    In Spin, one can have longs (32 bits), words (16 bits), and bytes (8 bits). Subsections of a value can be isolated, hence x.word[1] is the high 16 bits of x, x.byte[0] is the low 8 bits. You're correct in thinking that a long can be treated as an array of two words, or an array of four bytes. A word can be treated as an array of two bytes. Spin treats all variables as arrays of bits, so I could do something like this:

      repeat i from 0 to 15 step 2
        x.byte[3].[i] := 1
    

    Also, what does the #> and <# do?

    This are the minimum (floor) and maximum (ceiling) operators. I use that particular style to constrain a value between two endpoints

      x := 2 #> x <# $FFFF
    

    ... forces x to be at least two and no more than $FFFF. Another way:

      x := (x < 2) ? 2 : x
      x := (x > $FFFF) ? $FFFF : x
    

    Is there a SPIN2 to C syntax chart explaining what these operators do?

    Not that I'm aware of. This link will take you to the operators section of the online Spin2 docs
    -- https://docs.google.com/document/d/16qVkmA6Co5fUNKJHF6pBfGfDupuRwDtf-wyieh_fbqw/edit#heading=h.tztltoskeb64

    On an off topic question, how sensitive are the P2 pins? Can they be damaged easily by ESD or other weird events? For some reason I can't get pin 0 to toggle for the life of me :neutral:

  • evanhevanh Posts: 16,027
    edited 2023-05-05 16:22

    Is it only pin P0? Often the common pin group all fails together. The Eval Boards had groups of eight pins commoned to each LDO regulator ... so they would fail in groups of eight when accidentally using 5 volts or something.

  • For some reason I can't get pin 0 to toggle for the life of me.

    Evan (as always) makes great points about the pins. I have found them to be pretty rugged, once wiring a 5v output from a device to an output pin on the P2; it seemed to kill the pin -- until the next day when the pin started working again. I can only guess, but the short may have overheated the output driver and when it cooled it started working again.

    If I may make an observation: you ask a lot of question (good!) but you rarely, if ever, share snippets of code that are giving you problems (bad). Paraphrasing Jerry Maguire, "Help others help you by sharing the code that is not working as you wish."

  • Ok, thank you. I think I know of a way to write the bounds in c

    In C you could make small changes

      x = (x < 2) ? 2 : x;
      x = (x > 0xFFFF) ? 0xFFFF : x;
    

    or, perhaps his is more efficient

      if (x < 2) {
        x = 2;
      } 
      else if (x > 0xFFFF) {
        x = 0xFFFF;
      }
    
  • I screwed the pooch on pin 0 somehow or somewhere in the code it's being held low somehow but I looked and cannot find it errrgh. Pins 1 through 7 work fine which is really weird...

  • @JonnyMac said:

    Ok, thank you. I think I know of a way to write the bounds in c

    In C you could make small changes

      x = (x < 2) ? 2 : x;
      x = (x > 0xFFFF) ? 0xFFFF : x;
    

    or, perhaps his is more efficient

      if (x < 2) {
        x = 2;
      } 
      else if (x > 0xFFFF) {
        x = 0xFFFF;
      }
    

    I did this:

    typedef union
    {
    struct {
    uint16_t x_sclk_lsb; // X val lower
    uint16_t x_sclk_msb; // X val upper
    }x_sclk_s;
    uint32_t x_sclk_byte;

    }x_sclk_t;

    x_sclk_t x_sclk_reg;

    And then did this:

    x_sclk_reg.x_sclk_s.x_sclk_lsb = x_sclk_reg.x_sclk_s.x_sclk_lsb >= 2 && (_clockfreq() / (spi_clk_freq * 0x8)) && x_sclk_reg.x_sclk_s.x_sclk_lsb <= 0xFFFF;
    x_sclk_reg.x_sclk_s.x_sclk_msb = x_sclk_reg.x_sclk_s.x_sclk_lsb >> 1;

    I know it looks like a disaster :smiley:

  • @JonnyMac said:

    For some reason I can't get pin 0 to toggle for the life of me.

    Evan (as always) makes great points about the pins. I have found them to be pretty rugged, once wiring a 5v output from a device to an output pin on the P2; it seemed to kill the pin -- until the next day when the pin started working again. I can only guess, but the short may have overheated the output driver and when it cooled it started working again.

    If I may make an observation: you ask a lot of question (good!) but you rarely, if ever, share snippets of code that are giving you problems (bad). Paraphrasing Jerry Maguire, "Help others help you by sharing the code that is not working as you wish."

    I'm ashamed of my poor programming mistakes lol. I don't have a degree in computer science, so I am learning as I go. I know C pretty well but not the greatest at it. All of you are smarter than me for sure :smiley:

  • @evanh said:
    Is it only pin P0? Often the common pin group all fails together. The Eval Boards had groups of eight pins commoned to each LDO regulator ... so they would fail in groups of eight when accidentally using 5 volts or something.

    Yeah, I see the regulator groups on the schematic. It is really odd that it is ONLY pin 0. I use _pinl(0); and _pinh(0); to try and toggle it but nothing. Pins 1 through 7 are fine and can toggle them all day long so I must have done something stupid. :neutral:

  • @enorton said:
    I screwed the pooch on pin 0 somehow or somewhere in the code it's being held low somehow but I looked and cannot find it errrgh. Pins 1 through 7 work fine which is really weird...

    Are you trying that in a fresh program? If you've written some sort of smart mode to Pin 0, that could cause it to not react to drive.

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-05-05 22:18

    I know it looks like a disaster

    Your words... My mentor was an old-school character actor named Cliff Osmond. He was discovered by the legendary director, Billy Wilder, and did several movies with Walter Matthau and Jack Lemmon (who sponsored him into the Academy). Cliff was a very smart man, which lead him to teaching (acting). One of his admonitions was, "Always go for simplicity, because simplicity is elegance, and elegance is best."

    Again, I'm not a C programmer (though I understand the C syntax), so I might translate my SPI connections to the P2 flash like this (not tested).

      _pinh(SF_CS);
    
      m = P_SYNC_RX | (((SF_SCK-SF_MISO) & 0b111) << 24);
      x = 0b000000 | (8-1);
      _pinstart(SF_MISO, m, x, 0); 
      _pinf(SF_MISO);
    
      m = P_SYNC_TX | P_OE | (((SF_SCK-SF_MOSI) & 0b111) << 24);
      x = 0b000000 | (8-1);
      _pinstart(SF_MOSI, m, x, 0); 
      _pinf(SF_MOSI);
    
      m = P_PULSE | P_OE;
      x = _clockfreq() / spiFreq;
      if (x < 2) {
        x = 2;
      }
      else if (x > 0xFFFF) {
        x = 0xFFFF;
      }
      x |= (x >> 1) << 16;
      _pinstart(SF_SCK, m, x, 0);
    

    I know that my code is verbose -- my goal is clarity. Note that I de-activate (reset) the SPI MOSI and MISO pins until they're used.

  • dgatelydgately Posts: 1,631
    edited 2023-05-05 19:49

    @evanh said:

    1_000 equals: 1000

    0b1000 or 8 in C.

    Um... 16 in decimal. $10 in hexadecimal (spin2), right? (and, 0x10 in C)

  • JonnyMacJonnyMac Posts: 9,158
    edited 2023-05-06 00:00

    0b1000 is 8 (there are only three 0s to the right of 1). I had to zoom in. :)

  • @Wuerfel_21 said:

    @enorton said:
    I screwed the pooch on pin 0 somehow or somewhere in the code it's being held low somehow but I looked and cannot find it errrgh. Pins 1 through 7 work fine which is really weird...

    Are you trying that in a fresh program? If you've written some sort of smart mode to Pin 0, that could cause it to not react to drive.

    I looked and couldn't find anything that jumps out at me :neutral:

Sign In or Register to comment.