Teensy 3.0 as SPI Slave

Status
Not open for further replies.
Hi,

Regarding the SPI slave rxtx8() and rxtx16(), is it possible to send and receive data of different lengths using the current library?

thanks,
Brian
 
Easy answer, no. The chip has built-in SPI funtions, and it only supports 8 or 16 bits, according to the datasheets.
 
Sorry. Worded my question wrong.
Is it possible to say send 1 byte to the slave, and then receive 4 bytes from the slave (i.e. the number of bytes received is unequal to the number of bytes sent).
 
Not exactly. When you load (say 4 bytes) into the slave's "push" registers, they have to be clocked out by the master. So you would send 4 bytes from the master and it clocks data into the slave, while using those same clocks to get data out of the slave. Both master and slave are both sending and receiving at the same time.

This is actually really handy, but it means your slave's outgoing data has to be loaded (in advance) before you master sends anything.

Also, on a side note, I found that after the master clocks out its outgoing data, it is good to do a small delay (like 50 uS) before trying to "pop" any data received. This is the time it takes for it to process any data that came in. If you dont, you will see weird results.
 
When you say it's good to do a small delay before trying to "pop" any data received, is this on the slave?

Thanks for your help.
 
just to let you know teensy 3.1 working okay as a SPI slave, have an odd issue with the CS line having to be held contantly low but it's all working okay
 
I was wondering if I need a pul up resistor on the CS line, may be something with the Yun hardware. I have a look and let you know how it goes
 
I'm confused now, I need the cs low at all times? I have a multi-slave configuration in which I've got two teensy 3.1s as slaves, and some other spi devices.
 
I have the following setup
Teensy Arduino Yun
12 ------- MOSI ----------- 4
13 -------- CLK ----------- 3
11 -------- MISO --------- 1
10 -------- CS ------------ 5
Ground between the two

this is the Yun code
Code:
// inslude the SPI library:
//  ssh root@ yourYunsName.local 'telnet localhost 6571'
#include <Bridge.h>
#include <YunServer.h>
#include <YunClient.h>
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;

void setup() 
{
//  Serial.begin(9600);

Bridge.begin();
  Console.begin();

  // Wait for Console port to connect
  while (!Console);

  Console.println("Hi, what's your name?");

  // set the slaveSelectPin as an output:
  pinMode (10, OUTPUT);
  
 // SPI.setClockDivider(SPI_CLOCK_DIV2);
//  SPI.setDataMode(SPI_MODE0);
  
  digitalWrite(10, HIGH);
  
  // initialize SPI:
  SPI.begin();
}

void loop() 
{
int i, j;
  
  
  do{
      Console.println("spi transfer");
       digitalWrite(10, LOW);

       i = SPI.transfer(0x55);
        Console.println(i);
        i = SPI.transfer(0x01);
       Console.println(i);
      digitalWrite(10, HIGH);

        delay(1000);
  }
  while(1);

}

void digitalPotWrite(int address, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin, LOW);
  
  //  send in the address and value via SPI:
  SPI.transfer(address);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin, HIGH);
}

Teensy code
Code:
#include <t3spi.h>

//Initialize T3SPI class as SPI_SLAVE
T3SPI SPI_SLAVE;

//The number of integers per data packet
//MUST be the same as defined on the MASTER device
#define dataLength  2

//Initialize the arrays for incoming data
volatile uint8_t data[dataLength] = {};
//volatile uint16_t data[dataLength] = {};

//Initialize the arrays for outgoing data
volatile uint8_t returnData[dataLength] = {};
//volatile uint16_t returnData[dataLength] = {};


void setup(){
  
  Serial.begin(115200);
  
  //Begin SPI in SLAVE (SCK pin, MOSI pin, MISO pin, CS pin)
  SPI_SLAVE.begin_SLAVE(SCK, MOSI, MISO, CS0);
  
  //Set the CTAR0_SLAVE0 (Frame Size, SPI Mode)
  SPI_SLAVE.setCTAR_SLAVE(8, SPI_MODE0);
  //SPI_SLAVE.setCTAR_SLAVE(16, SPI_MODE0);
  
  //SPI_SLAVE
  
  
  //Enable the SPI0 Interrupt
  NVIC_ENABLE_IRQ(IRQ_SPI0);
  
  //Poputlate the array of outgoing data
  for (int i=0; i<dataLength; i++){
    returnData[i]=0x55;}
}

void loop()
{



  //Capture the time before receiving data
  if (SPI_SLAVE.dataPointer==0 && SPI_SLAVE.packetCT==0){
    SPI_SLAVE.timeStamp1=micros();}  
 
  //Capture the time when transfer is done
  if (SPI_SLAVE.packetCT==1){
    SPI_SLAVE.timeStamp2=micros();

    //Print data received & data sent
    for (int i=0; i<dataLength; i++){
      Serial.print("data[");
      Serial.print(i);
      Serial.print("]: ");
      Serial.print(data[i]);
      Serial.print("   returnData[");
      Serial.print(i);
      Serial.print("]: ");
      Serial.println(returnData[i]);
      Serial.flush();}
  
      //Print statistics for the previous transfer
      SPI_SLAVE.printStatistics(dataLength); 
   
    //Reset the packet count   
    SPI_SLAVE.packetCT=0;}
}

//Interrupt Service Routine to handle incoming data
void spi0_isr(void)
{
 
  
  //Function to handle data
  SPI_SLAVE.rxtx8 (data, returnData, dataLength);

  //SPI_SLAVE.rxtx16(data, returnData, dataLength);
}

All the code is pretty simple, but it's odd with the CS line and I am sure it's working but just not 100% sure. If I connect the CS line to Yun pin 10 I can see the SPI transfer and it's transferring data . If the CS is un-connected I still get SPI transfer and if SPI is held high (5v) there is no SPI transfer. I would have expected it only to work with the CS connected to YUN GPIO CS, as this would trigger the SPI ISR and then SPI transfer. But, the SPI seems to be working on may be CLK? I need to get a new scope, then I can try to decode the CS & CLK lines.

This setup works okay with Teensy 3.1 and Yun.
 
Hi,
I have tested the library with 2 Teensy31 and another sensor and it works well and I have a couple of questions:
Did someone had a chance to use it with Ethernet or SD card ?
Can the ibrary be used at higher speed than 24 MHZ ?
Thank you, Paul
 
Can the ibrary be used at higher speed than 24 MHZ ?
Probably not. I believe it's the max (and that's very fast)

On another note, Paul, is there any chance of SPI slave support on the core SPI library any time soon?

thanks.
 
Can the ibrary be used at higher speed than 24 MHZ ?
Probably not. I believe it's the max (and that's very fast)

In slave mode, SPI is limited to F_BUS / 4. Master mode can go up to F_BUS / 2.

When Teensy runs at 96 or 48 MHz, F_BUS is 48 MHz.

Also, the chip datasheet says the hardware's maximum is 12.5 MHz, regardless of F_BUS. At least that's the official spec. If you overclock Teensy to 120 MHz, perhaps it might be able to actually work up to 15 MHz, since F_BUS is 60 MHz in that mode. Maybe? That'd be 2.5 MHz beyond the official spec, but then running the CPU at 120 MHz far pver the official 72 MHz spec, but seems to work pretty well (at least at room temperature and 3.3V)


On another note, Paul, is there any chance of SPI slave support on the core SPI library any time soon?

No. The main SPI library is master only.

When/if slave mode becomes well supported, it'd need to be a separate library.
 
Last edited:
I'm trying to get the teensy 3.1 to work as a slave to a TI MCU master. The master has a continuous clock and the enable stays low across multiple frames. Do I need to set the CONT_SCKE bit in the SPIx_MCR register, or is that just for a master? And the master must have CPHA = 0 (sample on rising edge), and the freescale datasheet for the teensy MCU says continuous clock is only supported CPHA = 1 (section 45.4.5). Am I out of luck then?
 
Last edited:
So the library works, according to several people who have downloaded it. Here are the major issues:
1) I changed some of the ctar0's to ctar_0. Thats my bad. You will probably need to change them back to ctar0.
2) the data being off a few indexes, or, the data from the master is not aligned to the data in the slave.

Here is an example of the problem:
The master sends out 1, 2, 3, 4 in four separate SPI transactions.
wait 20 microsecond...
The slave reads 0, 1, 2, 3 in four seperate pop's. (pop is the term used to "grab" the incoming data, like serial.read(). )

For some reason, and I can't figure out why, the slave seems to boot with a 0 value already inside the spi register. I can't seem to prevent this, but I have been able to do a Syncing process to get the data aligned. But this is of limited help, it doesn't always work. I do this by having the master send a sync byte, and the slave has to read the correct byte before moving on.

I have noticed this: When a transmission is completed, you can (and probably should) wait a little bit (maybe 20 or 50 microseconds) before "POPPING" the data from the slave spi register. This seems to make it run more efficiently. I think this is the amount of time it takes for the data to get clocked into the correct registers inside the chip.

As I have said, I'm not sure why these issues occur, because its using the chips native spi functions. I've tried all kinds of ideas, and spent a lot of time trying to get the master/slave data to align correctly, and still have no good answer. I'm very sorry. But I sincerely hope that someone smarter than me can come up with a solution.

On another note, Paul once made a comment that we might get faster speeds if the data uses one of the DRAM flags to get shifted into the RAM. I don't know how to do this, and I'm not gonna try to learn. But it might help.
 
So the library works, according to several people who have downloaded it. Here are the major issues:
...
2) the data being off a few indexes, or, the data from the master is not aligned to the data in the slave.

I was receiving corrupted data, particularly the first received byte. The manual specifies the SPI module must be halted before clearing the receive fifo. Doing so fixed my problem. Here is an example of before and after:

Before:
SPI0_MCR |= SPI_MCR_CLR_RXF;

After:
SPI0_MCR |= SPI_MCR_HALT; //halt the SPI module
while (SPI0_SR & SPI_SR_TXRXS); //wait for the SPI module to stop
//clear the fifo, keeping the SPI halted
SPI0_MCR |= SPI_MCR_CLR_RXF + SPI_MCR_HALT;
SPI0_MCR &= ~SPI_MCR_HALT; //Start the SPI module running again
while (!(SPI0_SR & SPI_SR_TXRXS)); //verify SPI module running

And thank you, btmcmahan, for posting the library.
 
Really, so that fixed the first byte sync error? Could have sworn I tried that, but I must have tried a hundred different things, maybe I missed that.

So, when you say you halt the spi before clearing the fifo, do you only do it once during the initialization phase? I assume you aren't halting before popping data from the fifo?

But wow, that's great to know! I'll have to implement this on my robot!
 
...So, when you say you halt the spi before clearing the fifo, do you only do it once during the initialization phase? I assume you aren't halting before popping data from the fifo?

I clear the read fifo at the top of every txrx call, before entering the write/read for loop.

I also added this line after the SPI_WAIT call but before reading the fifo:
while (!(SPI0_SR & SPI_SR_RXCTR)); //Make sure rx fifo has data before reading

This should not be necessary, but once things started working, I didn't remove it.
Hope this works for you, too.
 
@btmcmahan may I ask why is this part of the code written like this:
Code:
void T3SPI::rxtx16(volatile uint16_t *dataIN, volatile uint16_t *dataOUT, int length){
	dataIN[dataPointer] = SPI0_POPR;
	dataPointer++;
	if (dataPointer == length){
		dataPointer=0;
		packetCT++;}
  SPI0_PUSHR_SLAVE = dataOUT[dataPointer];  
  SPI0_SR |= SPI_SR_RFDF;
}
And not like this...
Code:
void T3SPI::rxtx16(volatile uint16_t *dataIN, volatile uint16_t *dataOUT, int length){
	dataIN[dataPointer] = SPI0_POPR;
        SPI0_PUSHR_SLAVE = dataOUT[dataPointer];  
	dataPointer++;
	if (dataPointer == length){
		dataPointer=0;
		packetCT++;}
  SPI0_SR |= SPI_SR_RFDF;
}

In the first code snippet you read what the master sends and then the dataPointer is incrimented and the you send byte 1 back to the master.. So byte 0 is never sent?
Thank you :)
 
It's been over 3 years since I posted this code, so I really can't remember why I did it that way. I probably had a reason, but I don't remember what it was. But it worked for my purposes at the time. Feel free to change it to however it works best for you.
 
I know that this is an old thread but I had a need for testing something using a Teensy in Slave Mode. So I managed to get it partly working on the T3.2, T3.5 and the T3.6. However, I ran into a problem when I want in receive and transmit from the slave. The problem seems to be in one function:
Code:
oid T3SPI::rxtx16(volatile uint16_t *dataIN, volatile uint16_t *dataOUT, int length){
	dataIN[dataPointer] = SPI0_POPR;
	dataPointer++;
	if (dataPointer == length){
		dataPointer=0;
		packetCT++;
		delay(2);
	}
  SPI0_PUSHR_SLAVE = dataOUT[dataPointer];  
  SPI0_SR |= SPI_SR_RFDF;
}
The offending line is
Code:
  SPI0_PUSHR_SLAVE = dataOUT[dataPointer];
If I leave it in the received data is corrupted. If I remove it the received data is printed fine and matches what is sent from the master. That one line is used to transmit the data per post #18. Not sure if @btmcmahan got it working or not?

If any one has any ideas please let me know.

Thanks
Mike

EDIT: I did try the change recommended in post #47 but still didn't work.

EDIT2: Easy fix:
Code:
        SPI0_PUSHR_SLAVE = dataOUT[0];
to get it show the received data correctly but all 0's are transmitted.
 
Last edited:
Hi,

I struggled with the T3SPI Library for quite a while when I wanted to implement a simple SPI slave (no criticism of author). I simply wanted to receive 8 bit / MSB / Mode0 data as an SPI Slave. This code snippet below achieves that. I'm not using interrupts and Im on the standard pins for SPI on MK20DX256 AKA Teensy 3.2. Hope it helps someone.

Code:
#include <Arduino.h>
#include "mk20dx128.h"
#include "core_pins.h"
#include "stdintextended.h"

#define SPI_START() (SPI0_MCR &= ~SPI_MCR_HALT & ~SPI_MCR_MDIS)
#define SPI_STOP()  (SPI0_MCR |= SPI_MCR_HALT | SPI_MCR_MDIS)

void SPIPrintConfig(void); 
u8 SPIRead(void); 

void setup()
{
  delay(3000);      // Allow time for the serial connection to be established following reset/programming
  Serial.begin(115200);

  SIM_SCGC6 |= SIM_SCGC6_SPI0;  // enable clock to SPI.

  SPI_STOP();

  CORE_PIN13_CONFIG = PORT_PCR_MUX(2);
  CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2);
  CORE_PIN12_CONFIG = PORT_PCR_MUX(2);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(2);
 
   // Slave mode
  SPI0_MCR &= ~SPI_MCR_MSTR;
  // SPI0_CTAR0_SLAVE config
  SPI0_CTAR0_SLAVE = 0; 
  // Frame Size
  SPI0_CTAR0_SLAVE |= SPI_CTAR_FMSZ(7);
  // Clock Polarity
  SPI0_CTAR0_SLAVE &= ~SPI_CTAR_CPOL; 
  // Clock Phase
  SPI0_CTAR0_SLAVE &= ~SPI_CTAR_CPHA;
  // Clear RX FIFO
  SPI0_MCR |= SPI_MCR_CLR_RXF; 
  // Clear TX FIFO
  SPI0_MCR |= SPI_MCR_CLR_TXF; 
  // Disable TX FIFO
  SPI0_MCR |= SPI_MCR_DIS_TXF;
  // Disable RX FIFO 
  SPI0_MCR |= SPI_MCR_DIS_RXF; 
  
  SPI_START(); 

  SPIPrintConfig();
}

void loop()
{
  u8 rxData; 
  
  while(1)
  {
    rxData = SPIRead(); 
    Serial.print("RX = ");
    Serial.print(rxData, HEX);
    Serial.println();      
  }  
}

u8 SPIRead(void)
{
  u8 rxData = 0xAA; 

  SPI0_SR |= SPI_SR_TCF;
  SPI0_PUSHR_SLAVE = 0xFF; 

  while ((SPI0_SR & SPI_SR_TCF) == 0)
  {        
  }  

  rxData = (u8)SPI0_POPR;
  return rxData; 
}


void SPIPrintConfig(void) 
{
  uint32_t CTAR=0;
  
  Serial.println("SPIO STATUS");
  Serial.println();
  Serial.println("                 33222222222211111111110000000000");
  Serial.println("                 10987654321098765432109876543210");
  Serial.println("                 --------------------------------");
  Serial.print("SPIO_MCR:        ");
 
  for (unsigned int mask = 0x80000000; mask; mask >>= 1) 
  {
    Serial.print(mask&SPI0_MCR?'1':'0');
  }

  Serial.println();
  Serial.print("SPIO_CTAR_SLAVE: ");
  CTAR = SPI0_CTAR0_SLAVE;

  for (unsigned int mask = 0x80000000; mask; mask >>= 1) 
  {
    Serial.print(mask&CTAR?'1':'0');
  }
      
  Serial.println();
  Serial.print("SPI0_SR:         ");
  for (unsigned int mask = 0x80000000; mask; mask >>= 1) 
  {
    Serial.print(mask&SPI0_SR?'1':'0');
  }
    
  Serial.println();
  Serial.print("SPI0_RSER:       ");
  for (unsigned int mask = 0x80000000; mask; mask >>= 1) 
  {
    Serial.print(mask&SPI0_RSER?'1':'0');
  }
  Serial.println(); 
  Serial.println(); 
  Serial.println();   
}
 
Status
Not open for further replies.
Back
Top