SPISlave_T4

SPI protocol lacks multi-master arbitration or other sharing mechanisms. How do you imagine 2 masters could possibly work on any SPI bus?
 
this library setups only one LPSPI as slave. Even if you managed to setup 2 or more slave on LPSPI you'd need to be able to manage the 2 ports simuletaneously, and if interrupts are involved, good luck
 
SPI protocol lacks multi-master arbitration or other sharing mechanisms. How do you imagine 2 masters could possibly work on any SPI bus?
Two seperate SPI busses, with the Teensy behaving as slave on both of them.


this library setups only one LPSPI as slave. Even if you managed to setup 2 or more slave on LPSPI you'd need to be able to manage the 2 ports simuletaneously, and if interrupts are involved, good luck
Don't most processors have some form of interrupt scheduling / something to manage two interrupts happing at the same time or close proximity?

I did a timing check running one SPI Slave, and you could easily handle 4 SPI slaves theoretically within the time between the clock line going H->L and the time it begins to output the first bit from the byte.
set output high as interrupt starts, then low as interrupt finishes, and it looks like below:
Y6ou1F8.png

Code:
void myFunc() {
  noInterrupts();
  digitalWrite(5, HIGH);
  //while ( mySPI.active() ) {
    if (mySPI.available()) {
      mySPI.pushr(l<<1);
    }
  //}
  digitalWrite(5, LOW);
  interrupts();
}
 
it all depends on how long your interrupts are, and the speeds of the SPI obviously especially when pushing data. if the slave interrupt is not ready to receive because of another interrupt you may lose data. Your example shows a single data transfer, if your purpose is to test a single byte/word vs arrays of data and processing without including other running code while 2 SPI are transferring streams of data, then this could be fine, otherwise something might happen, data may corrupt, and then people might complain about libraries not working without considering this
 
it all depends on how long your interrupts are, and the speeds of the SPI obviously especially when pushing data. if the slave interrupt is not ready to receive because of another interrupt you may lose data. Your example shows a single data transfer, if your purpose is to test a single byte/word vs arrays of data and processing without including other running code while 2 SPI are transferring streams of data, then this could be fine, otherwise something might happen, data may corrupt, and then people might complain about libraries not working without considering this

Well in this case, I'm only doing single data packet (26bit) transfers in response to a non-continuous clock signal. Basically using it to fake being a 24bit SSI encoder.
 
Sorry, was unclear, I need the teensy to act as SPI slave to two different masters!

it's a curious system but I feel it should be possible. You'd need to refer to the datasheet and make your own copy of the library and adjust to use one of the other SPI peripherals.

I copied the library to make some changes, a brief note about it here:

https://forum.pjrc.com/threads/66389-SPISlave_T4?p=308305&viewfull=1#post308305

Look into the information about word and frame size. It is possible to trigger the interrupt only after the transaction is fully complete and the data is ready in the buffer. I don't see why it couldn't work with two masters because the buffer will be there and the interrupts will be serviced in order. However, this does require a deep dive into the datasheet and careful reading about the pin mux, etc.

I'd be interested just out of curiosity what kind of system you are building that requires two masters connected to one peripheral system.
 
Basically I'm not using the SPI as SPI, but faking the SSI protocol. for an 24 bit SSI comms, it turns out if you set the SPI bus to 26 bits (n+2) and then shift the data 1 to the left (data<<1) it basically works as SSI encoder protocol.

I'm using the teensy as a master to control two servo drives that are e-camming to the SSI encoder input on their second encoder port. It works *really well* compared to step and direction, allowing you to do the slow movements, and also do high speed movements, without the issue of pulse frequency limits (very high resolution means slow movement limit, or if you want high speed with pulse/direction you need to decrease the resolution)

an SSI encoder recieves pulse and then outputs data in sequence with it.
https://en.wikipedia.org/wiki/Synchronous_Serial_Interface

I wonder if there is a better implementation method... clock frequency is up to 1.5MHz in my case, though it only requests position update at 10-20kHz. Still, that's close to the PWM frequency of a servo drive, so why does it need to update faster then that, haha.
 
interesting but I don't know if I fully understand. is the servo drive the master for two SSI channels and it thinks there are 2 encoders (slave) ?

but you are doing something where there are not encoders but you want to create the signals for the servo drive - to get it to do what you want?
 
I'll go through the whole thing, might be a lot of stuff you already know, but clarity is nice for other readers:

So basically a Position Loop of a servo drive has two numbers it cares about [Actual Position] and [Target Position]
It runs a whole load of maths and moves the motor, optimizing to make the [Actual Position] as close to [Target Position] as possible.

the [Actual Position] is typically read by an encoder on the motor / load, e.g. Quadrature Incremental, Sin-Cos Analog or Absolute (SSI, BiSS-C, even SPI) to name a few.

the [Target Position] can be set by a huge range of ways:
many servo drives support writing programs to change the target position based upon timing or i/o.
sometimes you want to use an analog input, and scale / reference the position to say +/-10V = +/-1m from reference. (wouldn't personally recommend this for noise reasons - generally works better in the velocity loop with position fed back to the 'controller'
other times you command the position over fieldbus, like EtherCAT or CANOpen, Ethernet/IP or other such ones.

You can also link some servo drives (such as the ones I have) to follow an encoder input, so you could time one conveyor belt, or a rotating blade to a conveyor belt for example.

In my case, as a low latancy, fast way to link a servo drive to a low cost controller (Teensy) my plan is to make the Teensy pretend to be the encoder, which, in this case, runs an SSI protocol.
The great thing about this, is as opposed to step/direction, you can't loose steps, or find youself not able to generate them fast enough (also very computationally intensive to calculate it out and output the Pulse steps)

as a result I'm able to get the [Target Position] in at upwards of 10-20kHz, far, FAR faster then any fieldbus, and with far less computational resources. You'd just have to add a small bit of I/O to handle drive enable, homing etc. (or use a weak implementation of the fieldbus for that)
 
I'm trying to test with Teensy 4.1 SPI Master and Teensy 4.1 SPI Slave communication, but SPI communication is not working.

Environment is
OS: Windows 11
Arduino 1.8.19
Library: Teensyduino 1.56

After downloading as zip from https://github.com/tonton81/SPI_MSTransfer_T4.git, I added the library in Arduino.

In Master Monitor of Teensy 4.1 one

4784114
State: 1

Detected slaves:
No slaves detected, check connections.

4785116

SPI Master Teensy 4.1 is output on the serial monitor as above.
Slave Teensy 4.1 prints millis:925709 on the serial monitor.

The SPI pins were connected 1:1 between the #1 Teensy 4.1 board and #2 Teensy 4.1 board.

Master to Slave
Teensy 4.1 to Teensy 4.1
CS - 10 to CS - 10
MOSI-11 to MOSI-11
MISO - 12 to MISO - 12
SCK - 13 to SCK - 13

The example is the same as git repository, so I didn't upload it.

Could you please confirm? and give me the solution?
 
can you try SPI_MSTransfer_T4 library example, that should work just for sanity check, because if it doesn't, it's most likely a connection or cross-wire issue.
check if the demo works on both your T4's and let me know
 
I have tried SPI_MSTransfer_T4 library example, checked the sanity check, and checked the connections.

When I check it with an oscilloscope, it shows the waveform keeps disapearing(gone) and appearing repeatedly with a period of 1 second according to the source code.

Test Code as below:
same as git of SPI_MSTransfer_T4 library example


<MASTER.ino>
#include "SPI.h"

#include "SPI_MSTransfer_MASTER.h"
SPI_MSTransfer_MASTER<&SPI, 10, 0x1234> mySPI1234;
SPI_MSTransfer_MASTER<&SPI, 10, 0x4567> mySPI4567;

void setup() {
Serial.begin(115200);
SPI.begin();
pinMode(10, OUTPUT);
digitalWrite(10, 1);
mySPI1234.begin();
mySPI4567.begin();
}

void loop() {

static uint32_t t = millis();
if ( millis() - t > 1000 ) {
Serial.println(millis());

uint16_t buf[5] = { 0xF1, 0xF2, 0xDEAD, 0xF4, 0xBEEF };
uint16_t buf2[5] = { 0xBEEF, 0xF7, 0xF8, 0xF9, 0xDEAD };
mySPI1234.transfer16(buf2, 5, random(0x1000, 0x8000));
mySPI4567.transfer16(buf, 5, random(0x1000, 0x8000));

static bool flip = 0;
flip = !flip;
mySPI1234.digitalWrite(6, flip);
// mySPI1234.pinMode(5, INPUT);
bool moo = mySPI1234.digitalRead(6);
Serial.print("State: "); Serial.println(moo);
mySPI1234.detectSlaves();

mySPI1234.pinMode(5, INPUT);
t = millis();
}

}


SPI_MSTransfer_SLAVE

#include "SPI_MSTransfer_T4.h"
SPI_MSTransfer_T4<&SPI, 0x1234> mySPI;

void setup() {
Serial.begin(115200);
mySPI.begin();
mySPI.onTransfer(myCB);
}

void loop() {
mySPI.events();
static uint32_t t = millis();
if ( millis() - t > 1000 ) {
Serial.print("millis: "); Serial.println(millis());
t = millis();
}
}

void myCB(uint16_t *buffer, uint16_t length, AsyncMST info) {
for ( int i = 0; i < length; i++ ) {
Serial.print(buffer, HEX); Serial.print(" ");
}
Serial.print(" --> Length: "); Serial.print(length);
Serial.print(" --> PacketID: "); Serial.println(info.packetID, HEX);
}

Master Serial Monitor

1484378
State: 1

Detected slaves:
No slaves detected, check connections.

1485380
State: 1

Detected slaves:
No slaves detected, check connections.


Slave Serial Monitor

millis: 1427713
millis: 1428714
millis: 1429715
millis: 1430716
millis: 1431717
millis: 1432718
millis: 1433719
millis: 1434720
millis: 1435721
millis: 1436722
millis: 1437723
millis: 1438724
millis: 1439725
millis: 1440726
millis: 1441727
millis: 1442728
millis: 1443729


Connections

Master to Slave
Teensy 4.1 to Teensy 4.1
CS - 10 to CS - 10
MOSI-11 to MOSI-11
MISO - 12 to MISO - 12
SCK - 13 to SCK - 13
GND to GND

<picture>
View attachment 29261

<oscilloscope>
Ch1(CS)_Ch2(MOSI)_Ch3(MISO)_Ch4(SCK)
View attachment 29263

the waveform in detail
Ch1(CS)_Ch2(MOSI)_Ch3(MISO)_Ch4(SCK)
View attachment 29262

Ch1(CS)_Ch2(MOSI)_Ch3(MISO)_Ch4(SCK)_2.png
 
Last edited:
Hello,

in SPISlave_T4.tpp as well as in SPI_MSTransfer_T4.tpp there is this part of code which enables the an LPSPI-Interrupt:
Code:
SLAVE_IER = 0x1; /* RX Interrupt */
This sets Bit 0 of the IER to 1, but according to chapter 48.5.1.6.4 of the IMXRT1060 reference manual this Bit (Bit 0) is TDIE and enables the "Transmit Data Interrupt" and not the "Receive Data Interrupt". RDIE is Bit 1 of the IER, and the correct code for setting RDIE would be "SLAVE_IER = (0x1 << 1);" or "SLAVE_IER = 0x2;".

Was this intentional or is it a potential bug in the SPISlave_T4-Library?

Thanks and best regards
Neni
 
yup you are correct the tdie bit on the 1062 is used because there was an issue using the rdie. i dont remember what but it took a long time to work out the kinks and qwirks just to get it stable (and functional) (try to google the rt1062 spi slave info and you'll see everyone has issues getting slave mode to run in general)

last issue was specific grounding and specific DSE pin settings, but things could change as other issues dissapear. you could try and set bit 2 and see what happens? :) (dont forget to clear the necessary flags as well end of isr, or mod the conditions, if any)
 
Last edited:
Hi tonton81,

Thank you for the work you've done. I am trying to setup Teensy 4.1 as an SPI slave. I've been trying to use the library you wrote but not able to get it to work using your example code.

Ultimately, I want the Master to be a Raspberry Pi and Slave to be the Teensy 4.1. However, before getting that to work, I'm trying to establish SPI communication with an Arduino Uno as a Master. The Master code works, we tried seeing the output on an oscilloscope and it is transferring what we expect. The Master (Arduino Uno) code is below:
Code:
#include <SPI.h>

uint8_t arr[8] = {1, 2, 3, 4, 32, 64, 128, 255};

void setup() {
  // initialize the digital pin as an output.
  pinMode(10, OUTPUT);
  Serial.begin(9600);
  while(!Serial);
  SPI.begin();
  digitalWrite(10, HIGH);
}

void loop() {
  SPI.beginTransaction(SPISettings(200000, MSBFIRST, SPI_MODE0));
  digitalWrite(10, LOW);
  for(uint8_t i = 0; i < 8; i++){
    //Serial.println(SPI.transfer(0x55));
    //SPI.transfer(0x55);
    SPI.transfer(arr[i]);
  }
  delayMicroseconds(1);
  digitalWrite(10, HIGH);
  SPI.endTransaction();
  Serial.println("=============");
  delay(100);
}

Note: we tried both MSB and LSB modes.

However, the Slave (Teensy 4.1 using SPISlave_T4 library) is not working. It neither receives the data being transferred or send back data. The MISO line also doesn't show anything on oscilloscope. Our current wiring is 10-10, 11-12, 12-11, 13-13 (not using swapPins). Also, we tried printing the contents of some registers. RDR is empty. RSR_RXEMPTY is 0. The code running on the Slave is essentially the sample code in the repo.

Code:
#include "SPISlave_T4.h"
SPISlave_T4<&SPI, SPI_8_BITS> mySPI;
byte returnVal = 0xEE;

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

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

void myFunc() {
  Serial.println("START: ");
  // uint8_t arr[] = { 3, 2, 8, 3, 10, 11, 33, 13, 14 };
  //uint8_t arr[] = {0x03, 0x02, 0x05, 0x06};
  uint8_t i = 0;
  while ( mySPI.active() ) {
    if (mySPI.available()) {
      mySPI.pushr(2);
      uint8_t receive = mySPI.popr();
      Serial.println(receive, HEX);
      //if ( i++ > sizeof(arr) ) i = 0;
      // Serial.print("\t i = ");
      // Serial.print(i);
      //mySPI.pushr(receive);
      //Serial.print("\tVALUE: ");
      //Serial.print(mySPI.popr());
    }
  }
  Serial.print("\tEND\n");
}

Any tips or suggestions would be of help. Thank you!
 
Hello,

do you found a solution?
I have got the same problem. I'm using a 4.1.
As soon i call the function 'mySPI.begin();' my slave freezes.
If I use SPISlave_T4<&SPI1, SPI_8_BITS> mySPI; the slave is not freezing, but no communication will work. So I think the problem must be in this function:

Code:
SPISlave_T4_FUNC SPISlave_T4_OPT::SPISlave_T4() {
  if ( port == &SPI ) {
    _LPSPI4 = this;
    _portnum = 3;
    CCM_CCGR1 |= (3UL << 6);
    nvic_irq = 32 + _portnum;
    _VectorsRam[16 + nvic_irq] = lpspi4_slave_isr;

    /* Alternate pins not broken out on Teensy 4.0/4.1 for LPSPI4 */
    SLAVE_PINS_ADDR;
    spiAddr[0] = 0; /* PCS0_SELECT_INPUT */
    spiAddr[1] = 0; /* SCK_SELECT_INPUT */
    spiAddr[2] = 0; /* SDI_SELECT_INPUT */
    spiAddr[3] = 0; /* SDO_SELECT_INPUT */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_03 = 0x3; /* LPSPI4 SCK (CLK) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_01 = 0x3; /* LPSPI4 SDI (MISO) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_02 = 0x3; /* LPSPI4 SDO (MOSI) */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_00 = 0x3; /* LPSPI4 PCS0 (CS) */
  } 
}

Any help would be great
 
Spi1

last update only supports SPI, not SPI1, so currently no support is implemented in last update
Hello, i'm trying using sp1.
First of all, i've changed so:

Code:
SPISlave_T4_FUNC SPISlave_T4_OPT::SPISlave_T4() {
 if ( port == &SPI1 ) {
    _LPSPI3 = this;
	
	// 0=LPSPI1 1=LPSPI2 2=LPSPI3 3=LPSPI4
    _portnum = 2;
	
	//(pag 1079)
	// 
    CCM_CCGR1 |= (3UL << (_portnum*2));
	
	
    nvic_irq = 32 + _portnum;
    _VectorsRam[16 + nvic_irq] = lpspi4_slave_isr;

    SLAVE_PINS_ADDR;
	
	/* 
	(pag 840)
	LPSP1
	PCS0_SELECT_INPUT 
	0 GPIO_SD_B0_01_ALT4 — Selecting Pad: GPIO_SD_B0_01 for Mode: ALT4
 	1 GPIO_EMC_30_ALT3 — Selecting Pad: GPIO_EMC_30 for Mode: ALT3
	SCK_SELECT_INPUT
	0 GPIO_EMC_27_ALT3 — Selecting Pad: GPIO_EMC_27 for Mode: ALT3
	1 GPIO_SD_B0_00_ALT4 — Selecting Pad: GPIO_SD_B0_00 for Mode: ALT4
	...
	
	LPSP3
	PCS0_SELECT_INPUT
	0 GPIO_AD_B0_03_ALT7 — Selecting Pad: GPIO_AD_B0_03 for Mode: ALT7
	1 GPIO_AD_B1_12_ALT2 — Selecting Pad: GPIO_AD_B1_12 for Mode: ALT2
	
	SCK_SELECT_INPUT
	0 GPIO_AD_B0_00_ALT7 — Selecting Pad: GPIO_AD_B0_00 for Mode: ALT7
	1 GPIO_AD_B1_15_ALT2 — Selecting Pad: GPIO_AD_B1_15 for Mode: ALT2
	
	SDI_SELECT_INPUT
	0 GPIO_AD_B0_02_ALT7 — Selecting Pad: GPIO_AD_B0_02 for Mode: ALT7
	1 GPIO_AD_B1_13_ALT2 — Selecting Pad: GPIO_AD_B1_13 for Mode: ALT2
	
	SDO_SELECT_INPUT
	0 GPIO_AD_B0_01_ALT7 — Selecting Pad: GPIO_AD_B0_01 for Mode: ALT7
	1 GPIO_AD_B1_14_ALT2 — Selecting Pad: GPIO_AD_B1_14 for Mode: ALT2
*/
    spiAddr[0] = 0; /* PCS0_SELECT_INPUT */
    spiAddr[1] = 1; /* SCK_SELECT_INPUT */
    spiAddr[2] = 0; /* SDI_SELECT_INPUT */
    spiAddr[3] = 1; /* SDO_SELECT_INPUT */
		
    IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_03 = 0x7; /*ALT7 LPSPI3 PCS0 (CS1) pin 0 */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_15 = 0x2; /*ALT2 LPSPI3 SCK (CLK1) pin 27 */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_02 = 0x7; /*ALT7 LPSPI3 SDI (MISO1) pin 1 */
    IOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_14 = 0x2; /*ALT2 LPSPI3 SDO (MOSI1) 26 */
    
	
  } 
}


but it doesn't seem to work: the interrupt routine is only called once.
Am I skipping a few steps?
For changing the spimode to 3 or so, it's right to only set the flags CPOL and CPHA or there's some other trick to do?
 
Ok now it works, in mode 3 also:

Code:
#define SLAVE_TCR_REFRESH spiAddr[24] = (0UL << 27) | LPSPI_TCR_FRAMESZ(bits - 1) | 0xC0000000

There was an error in the interrupt function
 
Back
Top