Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: TSPISlave Library

  1. #1
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,337

    TSPISlave Library

    I have seen alot of SPI slave questions, seeing as SPI_MST is more or less a protocol and controller in itself. I decided to write a user end library that's capable of running multiple SPI slave ports on Teensy 3.x and LC, for users interested in writing their own data handling in a simple interface. The functions provided allow ease of access for the user without divulging into specific registers, especially on the Teensy LC. The sketch will work on all T3.x/LC models without any other differences in the commands. I tried to make it as simple as I could for the user, and all SPI ports (SPI0,1,2) on T3/LC support independant 8 or 16 bit mode via constructor.

    However unless anyone has better suggestions, I had no other idea but to use uint8_t's for the pins, perhaps enums would have been better?

    Irregardless, this is where I am at:

    Code:
    #include "TSPISlave.h"
    
    TSPISlave mySPI = TSPISlave(SPI2, 51, 52, 53, 43, 8); // MISO,MOSI,SCK,CS,8bit transfers
    void setup() {
      Serial.begin(115200);
      mySPI.onReceive(myFunc); // callback for mySPI
    }
    
    void loop() {
      delay(1000);
      Serial.println(millis());
    }
    
    void myFunc() {
      Serial.println("START: ");
      uint16_t arr[2] = { 0x1111, 0x2222 };
      uint8_t i = 0;
      while ( mySPI.active() ) {
        if (mySPI.available()) {
          mySPI.pushr(arr[i++]);
          Serial.print("VALUE: 0x");
          Serial.println(mySPI.popr(), HEX);
        }
      }
      Serial.println("END");
    }

    I started working on this since yesturday and finally got the LC running on SPI0 and SPI1 before I finished off testing SPI1 and 2 of T3.5

    The master has no library, you can send data to the slave as you wish.
    while ( mySPI.active() ) allows the user to handle multiple bytes/words within the same transaction, an array is there as an example to shift out data for the master on sequential transfers
    available() must be checked before read/writing the data

    popr and pushr are used to read and write respectively the data once the available() is triggered
    popr and pushr also handle the DL and DH registers of the LC so the user wouldn't need to worry about that, essentially making the sketch work accross all recent teensy boards
    if anyone has constructor ideas or just to leave that as is, let me know, I should have a release uploaded later today. I tested all SPI busses on LC and T3.x before writing this post

    T3.x connections:
    MISO -> MISO
    MOSI -> MOSI

    LC connections:
    MISO -> MOSI
    MOSI -> MISO

    Also on the LC, you should run your pushr() after the popr(), as LC uses same register for both, youll need to push the data last

    https://github.com/tonton81/TSPISlave

    EDIT, just posted the library, I will update the supported SPI pin list for each port in a few minutes

    Tony
    Last edited by tonton81; 12-09-2018 at 08:05 PM.

  2. #2
    This library has been helpful to me, thanks! But, I'm unable to get past some behavior related tolatency.

    Here's what I'd like to do:
    The master sends the slave a command. The slave processor does stuff with it and gets the response ready, typically a couple of bytes of status info, so that when the next command is issued by the master, the response is clocked from slave to master in the usual way. The commands don't show up very often, so there's plenty of time to get ready.

    The problem I'm having is that I can't figure out how to set things up so that the first byte that is clocked out, is not "stale" data left over from the end of the last write cycle. There seems to be a one-byte queue somewhere. I'm using SPI1 and the ref manual says SPI1 has a TX FIFO size of 1. Is this the cause of the behavior? If so, can the FIFO be bypassed?

    I'm using 8-bit mode, but the same happens in 16-bit mode.

    Here's some code I've been using for testing. I've stripped it down to almost nothing. The payload variable is slowly incremented giving me plenty of time to read it out multiple times and look at the results. The SPI master is a T3.6, but the problem isn't there. I'm analyzing the bus traffic on a scope, so I can see that I always get one byte of the old value, after "payload" has been updated.

    cheers, Doug

    Code:
    #include "TSPISlave.h"
    
    uint8_t miso = 1;
    uint8_t mosi = 0;
    uint8_t sck = 32;
    uint8_t cs = 31;
    uint8_t spimode = 8;
    uint8_t i=0;
    volatile uint8_t payload;
    
    TSPISlave mySPI = TSPISlave(SPI1, miso, mosi, sck, cs, spimode);
    
    void setup() {
      Serial.begin(115200);
      mySPI.onReceive(myFunc);
    }
    
    void loop() {
      delay(4000);
      Serial.println(i);
      cli();
      payload=i;
      sei();
      i++;
    }
    
    void myFunc() {
      Serial.println("START: ");
      uint8_t i = 0;
    //  while ( mySPI.active() ) {
        if (mySPI.available()) {
          mySPI.pushr(payload);
          Serial.print("VALUE: 0x");
          Serial.println(mySPI.popr(), HEX);
        }
    //  }
      Serial.println("END");
    }

  3. #3
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,337
    "i" should be declared volatile uint8_t (your sharing it between the loop() and an ISR), but as for SPI, the last byte set in the slave is the one transfered back to master when master writes to slave. Slave has to set it in advance. Thats why usually the master sends a first instruction byte or dword, then dummy bytes to read the actual data that the slave is putting in the buffer. The slave first byte/dword is irrelevent during assertion, that word/byte just tells the slave what the next data it should put in the TX for the next master transfer, and the rest of data should be in sync after that

  4. #4
    Thanks for the reply. (volatile uint8_t i fixed).

    I'm not sure I follow. Are you saying that the first byte of the slave message should always be ignored? That seems a bit surprising.

    I get that this is usually ok because typically the master isn't yet interested in the reply because it's still sending out a command. I could take that approach and make it work, but it seems that there should be some sort of mechanism to flush out the old byte and be ready with a well-formed reply.

    In my situation I receive a command, then I've got hardware stuff to do (non Teensy) before I can be sure that everything is happy and working ok. Generally everything will be ok, so it seemed nice and clean to receive an SPI command and then many mS later, when the next command comes in, reply with a status. Because I'm responding to the *previous* command it seems I should be ready to go with good data immediately.

    Looking at the manual, there's a register to bypass the FIFO, so I've been experimenting with that. I have found no change in behavior, but I can also believe I didn't flip the bit correctly. This is out of my comfort zone... There's also a bit for flushing the FIFO, but I suspect that won't do what I want if the FIFO is still in the chain.

    Thanks very much
    Doug

  5. #5
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    3,337
    Unfortunately the first master byte gets whatever is in the slave pushr register. So if you change it in the ISR it's too late, thats for the next transfer. You should use while active() because this keeps your loop from resetting the variable while the master is still asserting the slave. The slave is supposed to take the first master transfer, lets say 0x25 value, and decide that for that value set the pushr for the next value amd the next byte, even if its a dummy from master, the master will receive what you set in the slave. That while loop is also a way to send an array of data in case the master sends several dummy bytes after 0x25, you can keep incrementing the array indice and set the next pushr with that value.

  6. #6
    Yes, I understand the issues with the while loop and clocking out the data. That's all working fine in my real program. (My test was just a very stripped down example. )

    I guess there's just no way to clear out that byte because ultimately that lowest level of that hardware block is a shift register that's clocked externally. I guess it can't clock out that byte without becoming a master (which would not be sensible).

    I can certainly make this work just fine. I was just looking for that extra "niceness".
    cheers,
    Doug

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •