SPISlave_T4

tonton81

Well-known member
SPISlave_T4 has been released!

You may download it at https://github.com/tonton81/SPISlave_T4
An example is supplied as well.

It is based on TSPISlave for Teensy 3.x, except it has an additional feature. Not using a callback will print out data being received to serial monitor.

So far Teensy 4.0/4.1 are supported for SPI only on pins 10, 11, 12, 13. No additional pins needed (well, common ground is obviously a plus).

I don't have the SPI1 or SPI2 pins broken out, but the library can easily support them as I am accessing the memory blocks directly with calculated offsets, rather than independant register names. Until then, SPI. is only supported.

My setup consisted of a Teensy LC SPI master sending data to a Teensy 4.0 slave.
Code:
Pin layout for SPI:

[U][B]LC[/B][/U]      [U][B]T4[/B][/U]

12      11
11      12
10      10
13      13


No alternate pins are broken out for SPI on Teensy 4.0/4.1.

Slave mode on Teensy 4 supports 8, 16, and 32bit transfers selectable in the constructor. Note that if you set 16 or 32bits, you need to send all the bits. Like for 16 bit you need to send 2x8bit transfers or one transfer16, or 4x 8bit transfers for 32bit or 2x transfer16's for 32bit.

Enjoy!
 
Last edited:
As a spy? just don't hookup the return line so it wont try to send anything to it, it should be able to print out what it sees on the spi bus with the callback disabled
 
Hey that is an interesting idea. We could setup the library to also be an SPI bus sniffer. The T4.x is capable of swapping the MISO/MOSI pins so you can log & sniff data going to and from the SPI devices. The outgoing pin could be disabled in sniff mode as well.
 
Just updated the library. It now supports:

mySPI.swapPins() --> swaps MISO with MOSI, so you can connect teensies together directly pin-to-pin:
Code:
13   13
12   12
11   11
10   10

and sniffer() : --> disables the slave output pin so it listens to traffic only.
 
Hi! Thanks for the libary! I have two questions.

1. How would I go about to make the library work with an active-high setting for the CS? (That is, the CS will go high and then the clock signal will be sent, then CS goes low again).

2. I had a Teensy 4.1 working as a slave. I was trying to send from the teensy-as-a-slave always the same 5 bits. For example {1, 2, 3, 4, 5} but on the master device I was getting somthing like {2, 3, 4, 5, 1}. I tried leaving the clock and CS connected but made a loopback between the Teensy's MISO and MOSI I would get {3, 4, 5, 1, 2}. There were 3 extra bytes I was getting before what I actually wanted to push, namely: {0, 255, 5}. I thought pushing some extra bytes before starting pushing these with the loop but that was breaking the mySPI.active() early somehow.

Thanks!
 
1) currently only SPI_MODE0 is supported/hard coded, CS polarity is not changeable yet without editing the source config & testing

2) the initial 2 bytes are from the SPI slave config FIFO, they must be shifted out before your actual data ends up shifting out of the slave
 
1) currently only SPI_MODE0 is supported/hard coded, CS polarity is not changeable yet without editing the source config & testing

2) the initial 2 bytes are from the SPI slave config FIFO, they must be shifted out before your actual data ends up shifting out of the slave

Many thanks!
 
2) the initial 2 bytes are from the SPI slave config FIFO, they must be shifted out before your actual data ends up shifting out of the slave

How would I go about to shift out the bytes? I was writing some extra bytes to be able to then have the correct order but the output order was not consistent.

The master is sending the CS and clock and allowing for 5 bytes to come through. For the following code I get consistent output (11, 15, 14, 13, 12). (I am checking the output with a logic analyzer):

Code:
#include "SPISlave_T4.h"
SPISlave_T4<&SPI, SPI_8_BITS> mySPI;

void setup() {
  Serial.begin(115200);
  while (!Serial);
  Serial.println("Begin");
  mySPI.begin();
  mySPI.onReceive(myFunc);
}

void loop() {
}

void myFunc() {
  while (mySPI.active()) {
    if (mySPI.available()) {
      mySPI.pushr(15);
      mySPI.pushr(14);
      mySPI.pushr(13);
      mySPI.pushr(12);
      mySPI.pushr(11);
    }
  }
}
 
just make 2 dummy bytes maybe 0xFF, can be anything, then write them from the master before the actual payload. if you are overriding the pusr with bytes then that will work also
 
SPI Slave without interrupts??

Hello,

Will this only work using interrupts?

My issue seems to be that the RX Interrupt is happening too fast, and hanging up my Teensy. If I disable the interrupt, will the rx bits get written to the FIFO automatically?

I am having trouble using the teensy4.0 to receive data from the AD7764 ADC. (https://www.analog.com/media/en/technical-documentation/data-sheets/ad7764.pdf)
I have data coming in at a frequency of about 156kHz, and the clock signal coming in at 20MHz.

To start I just want to read the latest 32 bit sample, like once a second. So I am not worried about timing, except that I can't slow down the ADC. Using this library as is makes the teensy hang as soon as the interrupts are enabled in the begin() function.

Thanks for your help!
 
you need to drive pin 10 high to exit the SPI slave ISR. If it is asserted (low) yes the interrupt holds until the CS line de-asserts
 
you need to drive pin 10 high to exit the SPI slave ISR. If it is asserted (low) yes the interrupt holds until the CS line de-asserts

Pin10 is connected to the frame sync output from the ADC. So it is driven low while the ADC is outputting data. It is driven low for 32 bits at 20MHz (1.6uS) then driven high again.
I think the issue is that the next time it is driven low again is just about 7uS later. Is 7uS enough time to get out of the ISR and prepare for the next?
 
Hi,

I am currently using this library to communicate via SPI with an openBCI. The openBCI uses a pic32 as the master that controls SD card, analog to digital converter and a RF module. I would like to add to this board a teensy so I can add more features to the board. Currently I am trying to send the date to the openBCI just to test the implementation.

Before working with the OpenBCI, I tested the slave code with another teensy that serves as a master. They both worked properly, I sent a command with the teensy#1 (Master), then the teensy #2 (slave) calculated the date and sent it to the master.

When I do the same with the pic32 the data stream from the pic32 gets corrupted only when I connect the MISO to the board. When I connect the CS, the SCK and the MOSI while checking with the oscilloscope, the MISO sends the data correctly from the teensy (slave) to the oscilloscope. At the moment I connect the MISO to the pic, the pin voltage changes completely (both devices use a 3.3V architecture).

I am adding some pictures to show the problem.

Correct (before attaching MISO):
correct.jpg


Problem (after attaching MISO):

problem.jpg

I tried just connecting the MISO to the Pic and the same problem happened. I also tried changing the MISO and MOSI pins with the swap Pins functions, that didn't work either. Maybe if someone knows about this problem or something similar. Maybe the voltages are not the same in both ports and are generating a type of shortcircuit.

Looking forward to your replies and thank you for your responses.
 
Are your common grounds connected?

Yes they are connected.

I was checking the voltages, and the MISO port from the PIC32 is always HIGH and goes down and back again when there is data available. In the Teensy4, the pin is always LOW and when there is data it goes up and down. Could this be the problem? Since the "default" voltages are opposite it might generate a problem?
Is there a way to change this polarization of the MISO pin? That is the only idea I have at the moment.
 
Last edited:
Could you guide me a bit about where could I change that?
Is it too complicated to do? Maybe I can try to change it.
 
you can modify the CPOL & CHPA register, check the release manual for the correct value, in slave mode section
 
just make 2 dummy bytes maybe 0xFF, can be anything, then write them from the master before the actual payload. if you are overriding the pusr with bytes then that will work also


Hi tonton81,

I tried to use SPISlave_T4 as described above, but I have found it impossible to receive the data in correct order.
I can send/receive up to 3 bytes correctly, but then when I tried sending more data I got it 'mixed up'.

My working code is bellow:

Teensy 4.1 SPI SLAVE
main.cpp
Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include "SPISlave_T4.h"


SPISlave_T4<&SPI, SPI_8_BITS> mySPI;


volatile int distanceUSFL = 0;
volatile int distanceUSFR = 0;
volatile int distanceUSBL = 0;
volatile int distanceUSBR = 0;


void ultrasonicSensorFrontLeft(){
	while (1) {
    	int incomingByte;
		int distanceMSB;
		int distanceLSB;
		int distanceCRC;
		int distanceUSFLT;

		if (Serial1.available() > 0) {
			incomingByte = Serial1.read();

			if(incomingByte==255){
            	distanceMSB=Serial1.read();
            	distanceLSB=Serial1.read();
            	distanceCRC=Serial1.read();
            	distanceUSFLT=((distanceMSB*256)+distanceLSB)/10; 
				if(distanceUSFLT>19 && distanceUSFLT<201){ 
					distanceUSFL=distanceUSFLT;
				}
        	}
		}
		threads.yield();
	}
}

void ultrasonicSensorFrontRight() {
	while (1) {
    	int incomingByte;
		int distanceMSB;
		int distanceLSB;
		int distanceCRC;
		int distanceUSFRT;

		if (Serial2.available() > 0) {
			incomingByte = Serial2.read();

			if(incomingByte==255){
            	distanceMSB=Serial2.read();
            	distanceLSB=Serial2.read();
            	distanceCRC=Serial2.read();
            	distanceUSFRT=((distanceMSB*256)+distanceLSB)/10; 
				if(distanceUSFRT>19 && distanceUSFRT<201){ 
					distanceUSFR=distanceUSFRT;
				}
        	}
		}
		threads.yield();
	}
}

void ultrasonicSensorBackLeft(){
	while (1) {
    	int incomingByte;
		int distanceMSB;
		int distanceLSB;
		int distanceCRC;
		int distanceUSBLT;

		if (Serial2.available() > 0) {
			incomingByte = Serial3.read();

			if(incomingByte==255){
            	distanceMSB=Serial3.read();
            	distanceLSB=Serial3.read();
            	distanceCRC=Serial3.read();
            	distanceUSBLT=((distanceMSB*256)+distanceLSB)/10; 
				if(distanceUSBLT>19 && distanceUSBLT<201){ 
					distanceUSBL=distanceUSBLT;
				}
        	}
		}
		threads.yield();
	}
}

void ultrasonicSensorBacktRight() {
	while (1) {
    	int incomingByte;
		int distanceMSB;
		int distanceLSB;
		int distanceCRC;
		int distanceUSBRT;

		if (Serial2.available() > 0) {
			incomingByte = Serial4.read();

			if(incomingByte==255){
            	distanceMSB=Serial4.read();
            	distanceLSB=Serial4.read();
            	distanceCRC=Serial4.read();
            	distanceUSBRT=((distanceMSB*256)+distanceLSB)/10; 
				if(distanceUSBRT>19 && distanceUSBRT<201){ 
					distanceUSBR=distanceUSBRT;
				}
        	}
		}
		threads.yield();
	}
}

void myFunc() {
  	//Serial.println("START: ");

	while ( mySPI.active() ) {
    	if (mySPI.available()) {
			mySPI.pushr(1);
			Serial.println(mySPI.popr());
			mySPI.pushr(2);
			Serial.println(mySPI.popr());
			mySPI.pushr(3);
			Serial.println(mySPI.popr());
      	}
  	}
	
	//Serial.println("END");
}


void setup() {
  	Serial.begin(115200);
  	while (!Serial);

	mySPI.begin();
	mySPI.swapPins();
  	mySPI.onReceive(myFunc);

	Serial.print("Begin...");


	Serial1.begin(9600);
	Serial2.begin(9600);
	Serial3.begin(9600);
	Serial4.begin(9600);

	threads.addThread(ultrasonicSensorFrontLeft);
	threads.addThread(ultrasonicSensorFrontRight);
	threads.addThread(ultrasonicSensorBackLeft);
  	threads.addThread(ultrasonicSensorBacktRight);

}

void loop() {
	Serial.print("millis: "); Serial.println(millis());
  	delay(1000);
}


Raspberry PI 4 SPI MASTER
App.java
Code:
        public static class Task_1 implements Runnable {
                public void run() {
                        try
                        {
                                SPI.Init();
                                SPI.Refresh(true);
                                int ans=0;
                                SPI.SendByte(255);
                                SPI.SendByte(255);
                                while (true) {
                                        SPI.SendByte(101);
                                        ans=SPI.ReceiveByte();
                                        System.out.println("---> " + ans + "\n");
                                        SPI.SendByte(102);
                                        ans=SPI.ReceiveByte();
                                        System.out.println("---> " + ans + "\n");
                                        SPI.SendByte(103);
                                        ans=SPI.ReceiveByte();
                                        System.out.println("---> " + ans + "\n");
                                        System.out.println("--------------------------------------\n");
                                        Thread.sleep(1000);
                                }
                        }
                        catch (InterruptedException e)
                        {
                        }
                }
        }

SPI.java
Code:
        public static void Init() {
                Busy=true;
                int fd = Spi.wiringPiSPISetup(0, 4000000);
                if (fd <= -1) {
                        System.out.println(" ==>> SPI SETUP FAILED");
                        Busy=false;
                        return;
                }
                Busy=false;
        }

        public static int ReceiveByte() throws InterruptedException {
                Busy=true;
                byte SPIbufer[] = new byte[1];
                Spi.wiringPiSPIDataRW(0, SPIbufer, 1);
                //Thread.sleep(5);
                //System.out.println("<<<<... " + SPIbufer[0]);
                Busy=false;
                return (int)SPIbufer[0]&0xff;
        }

        public static void SendByte(int SPIByte) throws InterruptedException {
                Busy=true;
                byte SPIbufer[] = new byte[1];
                SPIbufer[0] = (byte)SPIByte;
                //System.out.println(">>>>... " + SPIbufer[0]);
                Spi.wiringPiSPIDataRW(0, SPIbufer, 1);
                //Thread.sleep(5);
                Busy=false;
        }

The above code gives the following output:
screen1.jpg

After adding a code that sends/receives 4th byte I got the following output:
screen2.jpg


Can you please advice on what the problem might be?

Best Regards
Marek
 
you're pushing and popping 3 times per every byte transfer, it supposed to be done only once per byte

Code:
	while ( mySPI.active() ) {
    	if (mySPI.available()) {
			mySPI.pushr(1);
			Serial.println(mySPI.popr());
			mySPI.pushr(2);
			Serial.println(mySPI.popr());
			mySPI.pushr(3);
			Serial.println(mySPI.popr());
      	}
 
you're pushing and popping 3 times per every byte transfer, it supposed to be done only once per byte]

I am not sure I understand, so what I did was copping this from your example:

Code:
void myFunc() {
  Serial.println("START: ");
  uint8_t arr[] = { 3, 2, 8, 3, 10, 11, 33, 13, 14 };
  uint8_t i = 0;
  while ( mySPI.active() ) {
    if (mySPI.available()) {
      if ( i++ > sizeof(arr) ) i = 0;
      mySPI.pushr(arr[i]);
      Serial.print("VALUE: ");
      Serial.println(mySPI.popr());
    }
  }
  Serial.println("END");
}

After sending/receiving 8 bytes I got this:


---> 2

---> 2

---> 2

---> 2

---> 2

---> 255

---> 255

---> 2

---> 2

--------------------------------------


Sorry for this question (I'm not a professional developer) but as I understand this should give 3, 2, 8, 3, 10, 11, 33, 13, 14 from the array. Am I right?
 
Back
Top