Shop OBEX P1 Docs P2 Docs Learn Events
P2 Stepper Motor using Trinamics TMC2590 over SPI — Parallax Forums

P2 Stepper Motor using Trinamics TMC2590 over SPI

mjaved47mjaved47 Posts: 21
edited 2021-04-14 08:03 in Propeller 2

Greetings everyone. It has not been that long since I came to learn about P2. While I was going through various options for a project, a friend of mine suggested P2. And it's great to learn about such an active community here.

I have been checking resources about P2 and trying to get along the Spin2 language, at a complete beginner level. I don't have any prior experience with Spin1 either. Although I have been using other platforms such as Arduino and RaspberryPi for a while now. I wonder if there's a structured series of tutorials for a complete beginner on Spin2.

Moreover, has anyone tried controlling Trinamics motor drivers over SPI using P2? In my project, I intend to control multiple stepper motors using TMC2590 in SPI Mode.

Thanks in advance.

Comments

  • I have just used the TMC2208 to drive two stepper motors though. They are very quiet, even when you turn them on, nothing. I think they are not powered but when I supply the steps to them they turn just like that.

    Unlike you, I have not crossed over to the SPIN side.

    SPI works great with both the P1 and the P2.

    Mike

  • What platform do you preferably use or would suggest to the P2 beginners to get started?

  • It's all about the journey.

    If you like programming in C, you can continue to do that with little effort. The flexprop compiler works great and I use Visual Code to do the programming.

    Check out these projects to see how it's done.
    P2 Edge RoadSteer
    P2 Edge Goes for a ride
    P2 Edge SCARA does Tower

    Mike

  • mjaved47mjaved47 Posts: 21
    edited 2021-04-14 12:15

    Check out these projects to see how it's done.

    Indeed, I have been going through them. They are interesting and inspiring. Great job!

    If any, could you please share a template or an example for P2 SPI?

  • Ah, yes, the documentation for a lot of the C code functions were born on the P1 and documented in SimpleIDE.

    Most of those functions are now in FlexProp but not the documentation part of it.

    Anyway here is an example of the SPI HX8357 Display. I believe this display has Arduino code as well from Adafruit.

    Mike

  • If any, could you please share a template or an example for P2 SPI?

    I've attached my P2 programming template. It includes a serial library for serial coms. If you're using Propeller Tool for Spin you can configure this code as your P2 template and then load it via File\New menu or by right-clicking on an editor tab.

    I take a step-by-step approach, which I suggest you consider here since you're learning a device and a language. The Px series does not have specific SPI hardware built in so we synthesize SPI operations in code. This is easy and gives us a lot of flexibility. Luckily, it seems you only need one method to handle SPI with the TMC2590; you send it 20 bits and get 20 bits back. You can start with an experiment that communicates with a single device. Start by defining IO pins for the SPI connection.

    con
    
      MC_CS  =  0 { O }                                             ' chip select
      MC_SCK =  1 { O }                                             ' clock
      MC_SDO =  2 { O }                                             ' data out (to device.sdi pin)
      MC_SDI =  3 { I }                                             ' data in (from device.sdo pin)
    

    Remember that the data output from one device connects to the data input on the other, and vice-versa. Somewhere in your program you'll want to initialize the pins. My programs have a method called setup() that is called at the beginning to configure IO and objects. Your setup might look like this (assuming you want to output via a terminal).

    pub setup()
    
    '' Configure IO and objects for application
    
      term.tstart(BR_TERM)                                          ' start terminal io
    
      pinhigh(MC_CS)                                                ' de-select TMC2590
      pinhigh(MC_SCK)                                               ' idle state of SCK is high
      pinlow(MC_SDO)
      pinclear(MC_SDI)
    

    And now you could have a simple method to transfer values between the P2 and the TMC2590. Based on experience with the P2 and similar code, my guess is that this will run at about 500kHz if you use a 200MHz clock for the P2. This is well under the max speed of the device which means that -- once you have a firm grip on communications with it -- you could improve the code using inline assembly, and possibly even using P2 smart pins.

    pub transfer(outval) : inval | x
    
      pinlow(MC_CS)                                                 ' select device
    
      repeat x from 19 to 0                                         ' transfer 20 bits
        pinwrite(MC_SDO, outval.[x])                                ' output bit
        pinlow(MC_SCK)                                              ' clock it
        pinhigh(MC_SCK)
        inval.[x] := pinread(MC_SDI)                                ' get input bit
    
      pinhigh(MC_CS)
    

    Once you're fully confident in the connection to the TMC2590, it would be a good idea to create an object (library) for it. That will simplify your code when connecting to multiple devices.

  • JonnyMacJonnyMac Posts: 8,927
    edited 2021-04-14 18:51

    I did an online class introducing new Spin programmers to SPI using the flash chip that is on all P2 boards (holds the program). I started with a very simple version that uses Spin (as in my examples above). I updated that to use inline PASM2 for speed. I also did a version using smart pins. For small transfers I just find it easiest to go the inline PASM2 route; you get the speed without the work involved setting up and dealing with the smart pins.

    Assuming the code above works, an object for that device might have this variation of the method. Of course, that object would have a start method that allows the caller to pass the SPI pins used for the device, and -- optionally -- the SPI clock speed. Once you know the pins and have configured them, this method would handle the transfer at high speed.

    pub transfer(outval) : inval | cs_, sck_, sdo_, sdi_, tix
    
      longmove(@cs_, @cs, 5)                                        ' copy pins & 1/4 bit timing
    
      org
                    ror       outval, #20                           ' adjust msb to outval.31
    
                    drvl      cs_                                   ' select
                    waitx     tix
    
                    rep       #10, #20
                     shl      outval, #1                    wc      ' get bit31
                     drvc     sdo_                                  ' output to SDO
                     waitx    tix                                   ' let it settle
                     drvl     sck_                                  ' clock the bit
                     waitx    tix
                     waitx    tix
                     drvh     sck_
                     waitx    tix
                     testp    sdi_                          wc      ' sample SDI
                     rcl      inval, #1                             ' move into inval (MSBFIRST)
    
                    drvh      cs_                                   ' deselect
      end
    

    I've attached my SPI objects for the flash so you can compare the Spin, inline PASM, and smart pin variations.

  • Thank you for your valued suggestions @JonnyMac. I will be following all that you said and will be sharing any the advancements here as well.

    There're two questions that I would request you to clarify as well:

    Is it not true "The smart pins are really tuned to mode 0 SPI"?
    Is there a way they can be configured to handle any of the modes?

  • I can't see where smart pins here helps at all. It takes just as much code as just doing bit banging.

    Also if you use some ones name it adds a space at the end of it. This space needs to be preserved otherwise it doesn't work.

    Mike

  • Is it not true "The smart pins are really tuned to mode 0 SPI"?
    Is there a way they can be configured to handle any of the modes?

    The SPI modes of smart pins are easiest to use with mode 0. That said, there is some flexibility, though we're still at the early stages of documentation so it can take a bit of work to sort out. Before we do, though, I agree with Mike that smart pins are not useful in this specific case -- the inline PASM I provided above will give you the speed you want and is just easier to deal with.

    But... if you really want to try smart pins, you have to configure them. Start with the clock

      m := P_PULSE | P_OE | P_INVERT_OUTPUT                         ' spi clock mode
      x.word[0] := 2 #> (clkfreq / 2_000_000) <# $FFFF              ' ticks in period (2MHz)
      x.word[1] := x.word[0] >> 1                                   ' ticks in low cycle (50%)
      pinstart(sck, m, x, 0)                                        ' activate smart pin, no pulses
    

    This will setup the pin passed in sck as an inverted clock (CPOL = 1). The last 0 in pinstart() configures the pin but doesn't output any clocks

    Now, the SDO (old name MOSI) pin

      m := P_SYNC_TX | P_OE | P_INVERT_B                            ' spi tx mode
      m |= ((sck-sdo) & %111) << 24                                 ' add SCK offset (B pin)
      x := %1_00000 | (20-1)                                        ' start/stop mode, 20 bits
      pinstart(sdo, m, x, 0)                                        ' activate smart pin
      pinfloat(sdo)                                                 ' reset/disable until used
    

    The normal behavior of SYNC_TX is to output the bit on the rising edge of the clock. Well, we have inverted the clock output, so we have to re-invert it here so that the data output is on the falling edge of the clock (as shown in the docs).

    And here's the SDI (old name MISO) pin

      m := P_SYNC_RX                                                ' spi rx mode
      m |= ((sck-sdi) & %111) << 24                                 ' add SCK offset (B pin)
      x := %0_00000 | (20-1)                                        ' sample ahead of b pin rise, 8 bits
      pinstart(sdi, m, x, 0)                                        ' activate smart pin
      pinfloat(sdi)                                                 ' reset/disable until used
    

    The SYNC_RX mode samples on the rising clock edge which works for us because that happens in the middle of the bit period (the device setups its SDO pin just after the falling edge).

    As Mike points out, it's a lot of setup for the simple interaction for this device which is why I wouldn't do it here. Finally, the transfer routine.

    pub transfer(outbits) : inbits
    
    '' Transfer 20 bits to TMC2590
    '' -- returns 20-bit value
    
      pinlow(cs)
    
      outbits rev= 19                                               ' reverse 20 LSBs
    
      wypin(sdo, outbits)                                           ' prep/enable sdo (mosi)
      pinlow(sdo)
    
      pinlow(sdi)                                                   ' enable sdi (miso)
    
      wypin(sck, 20)                                                ' do 20 clocks
      repeat until pinread(sck)                                     ' wait for clocking to finish
    
      inbits := rdpin(sdi) rev 19                                   ' read input
    
      pinhigh(cs)
    
      pinfloat(sdo)                                                 ' release/reset spi io
      pinfloat(sdi)
    

    As you can see, after setting up the smart pins the code is still nearly as long as the simpler mechanism of using inline PASM.

    Fair warning: None of this code is tested. That said, I have a lot of experience with Spin, and feel confident this is a good starting point should you want to test it.

  • I'm attempting to communicate with an AD7124-8 ADC which requires SPI Mode 3 (SCK idles high, with data to its ADC's DIN input shifted out of the P2's SDO pin at falling edge of SCK). This requires ......

       "m := P_SYNC_TX | P_OE | P_INVERT_B" setup of the SDO pin, and the
       "m := P_PULSE | P_OE | P_INVERT_OUTPUT" setup of the SCK 
    

    ..... as described by JonnyMac in the previous post. Note his 'Fair Warning' disclaimer that this is an untested possible solution for Mode 3 SPI operation.

    I'm using Jon's "jm_ez_spi_demo.SPIN2" and "jm_ez_spi.SPIN2" code as a starting point for code development.

    Observations on my scope show that the first data bit shifted out of the SDO pin (which should be the value of the shift register's LSB bit) is present at the SDO pin IMMEDIATELY following execution of the following two lines of code [ see the jm_ez_spi.spin2 file for shiftout() routine for details ] :

         wypin(sdo,value)      'Write of value to be shifted out to ADC DIN input
         pinl(sdo)                    'DIR=1, Smart Pin Reset Released
    
    

    This behavior agrees withe the Parallax Propeller 2 documentation for the "%11100 = synchronous serial transmit" function which states:

    "During reset (DIR=0) the output is held low. Upon release of reset, the output will reflect the LSB of the output word written by any WYPIN during reset."

    In SPI Mode 3, the leading falling edge of the first SCK pulse is expected to trigger outputting of the first data bit from the SDO output pin, then after the trailing rising edge of the first SCK pulse, the ADC DIN pin is expected to sample this initial data bit. Unfortunately, the first data bit value is already present following "pinl(sdo)" execution, so at the first leading (falling ) edge of the SCK pulse the NEXT data bit value is output instead, and the ADC DIN pin samples this incorrect value.

    Within the shiftout() routine, I was able to finally achieve communication with the ADC's ID register by using two code fixes:

    'PREPARE DATA TO BE SHIFTED OUT LSB-BIT OF SHIFT REGISTER
      if (mode == MSBFIRST)                                         
         if (bits < 32)                                              
              value <<= (32-bits-1)                'FIX #1 ... shift data to the left by one less bit ( creates a leading 'dummy' bit to be shifted out first following the bit-reversal instruction below)
         value rev= 31                                   ' reverse 31+1 LSBs and zero-extend
    
    
     'PREPARE SDO PIN TO TRANSMIT DATA
      wxpin(sdo, %1_00000 | (bits-1+1))         ' configure sdo bits:  X[5]=1 selects start-stop update mode, X[4:0]=bits-1 == word size 'bits'
                                                                           'FIX #2 .... for bits=8, this wxpin instruction outputs 9 bits of data at the SDO pin, instead of 8.
                                                                            '             .... The first bit output is the 'dummy' bit created by the 'shift data left by one less' instruction above.     
      wypin(sdo, value)                                       ' load data value to be transmitted by this pin
      pinl(sdo)                                                      ' DIR=1 
    
    

    As the above solution will not work when shifting out 32 bits, and is specific to 'mode 3' only, I would have to agree with others on this forum that the use of 'smart pin' mode for SPI communication is far too complicated. I see Jon's attached SPIN2 code above which uses inline assembly to accomplish SPI communication. Does this code represent the most up to date implementation of SPI under SPIN2 ? Perhaps someone has a robust implementation of the equivalent of Jon's "jm_ez_spi.spin2" code, ideally one that allows for SPI mode specification ?

  • I use PASM2 based off JonnyMac's implementation and hacked a CPOL/CPHA feature in. You can find it here: https://github.com/deets/unifhy-rocket-engine-test-stand/blob/master/modul2/P2/spin/deets_spi.spin2

    I use it for an ADS1256 that also wants mode 3. It "works for me", but that might be a glitch / implementation detail of the actual SPI implementation.

Sign In or Register to comment.