SPISlave_T4

available() has a bug?

Hey @tonton81,

I'm working on adding slave mode to all the Teensy's, using your libraries (TSPISlave and SPISlave_T4) as inspiration. Thank you very much for writing them!

I noticed that your "available()" function in the SPISlave_T4 library uses the following code:

Code:
SPISlave_T4_FUNC bool SPISlave_T4_OPT::available() {
    SLAVE_PORT_ADDR;
    return ( (SLAVE_SR & (1UL << 8)) ) ? 1 : 0;
}

So basically the function returns true if bit 8 in the SR register is set. From what I can see in the reference manual, that bit is WCF (Word Complete Flag). However I think what you really want to test here is the RDF flag (Read Data Flag) in bit 1. That bit indicates whether the number of words in the FIFO buffer is greater than the RXWATER setting in FCR (which your code sets to 0).

I must say I just got started on the Teensy 4, and I haven't tested your code yet. So I apologize if I have it wrong. But reading through the documentation I figured that RDF would be the obvious flag to test and apparently WCF simply tests if the currently arriving data frame (byte/word/longword) has been received completely. So I wonder if this could be why others reported above that it was behaving strangely.

By the way, your code might be easier to read if you would use the symbolic names from imxrt.h instead of values. With my proposed change, the code would then change into the following (I think):

Code:
SPISlave_T4_FUNC bool SPISlave_T4_OPT::available() {
    // Change the following into a macro. Or better: calculate the address at construction time and keep the pointer as member variable
    volatile IMXRT_LPSPI_t *spiAddr = (volatile IMXRT_LPSPI_t*)(0x40394000 + (0x4000 * _portnum));

    return (spiAddr.SR & LPSPI_SR_RDF) != 0; // My change
    // Your code would be equivalent to: return (spiAddr.SR & LPSPI_SR_WCF) != 0;
}

Thanks!

===Jac
 
Last edited:
for future support of other SPIx interfaces, that won't be possible as you specified static addressing, the code was designed to take variable bus addresses into consideration since the offsets are identical but the start addressing is different per object
 
for future support of other SPIx interfaces, that won't be possible as you specified static addressing

??? I'm not sure what you're referring to, but I used the same code as you did to calculate the address, based on the _portnum member variable. The only difference is that I cast the pointer to a volatile IMXRT_LPSPI_t pointer instead of a volatile uint32_t.

Would you care to comment on my remark that the "available( )" function should test bit 1 instead of bit 8?

Thanks!

===Jac
 
not without testing of course, it's not on my bench right now

the pointer is still volatile but with offsets, but i am saying you are using the hardcoded SPI address which is what the code takes from the bus used, so instead of hardcoding 3 sets with conditions, only 1 check is needed with the template for that object
 
not without testing of course, it's not on my bench right now

Fair enough. I intend to test it myself too, probably later today.

the pointer is still volatile but with offsets, but i am saying you are using the hardcoded SPI address which is what the code takes from the bus used, so instead of hardcoding 3 sets with conditions, only 1 check is needed with the template for that object

The entire struct is volatile, so any operation on any field in the struct that might normally get optimized (e.g. setting a bit and then clearing it again) won't (shouldn't) get optimized away because of the volatile keyword. It doesn't matter whether you use a volatile int pointer with an offset, or a volatile struct pointer with a field name.

The Teensyduino declares three instances of the same class and uses the base address as one of the constructor parameters. The pointer gets stored in the instance and gets used for all hardware operations as needed, via the port() function. No templates needed.

By the way, I think the Teensyduino code has a minor problem too when it comes to volatile-ness of the struct: imxrt.h doesn't use volatile in its declaration of the location (e.g. IMXRT_LPSPI4_S), and in SPI.cpp/SPI.h the pointer gets passed around and stored as a uintptr_t, and converted back to a IMXRT_LPSPI_t * by the port() function. I think the use of the port() function avoids unwanted optimization but that could (should?) have been accomplished by declaring the pointers with the volatile keyword in imxrt.h.

===Jac
 
Last edited:
Morning @tonton81

Just ran into a problem - rather confusing. While compiling mstransfer got a strange error from the SPISlave library so as a test I compiled the example sketch (spi_slave.ino) for the T41 and sure enough got the same error message:
Code:
C:\Users\Merli\AppData\Local\Temp\arduino_build_430336\libraries\SPI_MSTransfer-master\SPI_MSTransfer.cpp.o: In function `lpspi4_slave_isr()':
D:\Users\Merli\Documents\Arduino\libraries\SPISlave_T4-main/[COLOR="#FF0000"]SPISlave_T4.tpp:19: multiple definition of `lpspi4_slave_isr()'[/COLOR]
C:\Users\Merli\AppData\Local\Temp\arduino_build_430336\sketch\spi_slave.ino.cpp.o:D:\Users\Merli\Documents\Arduino\libraries\[COLOR="#FF0000"]SPISlave_T4-main/SPISlave_T4.tpp:19: first defined here[/COLOR]
c:/users/merli/appdata/local/arduino15/packages/teensy/tools/teensy-compile/1.56.1/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
collect2.exe: error: ld returned 1 exit status

Not sure how to fix it?
 
Code:
void lpspi4_slave_isr() {
  _LPSPI4->SLAVE_ISR();
}

try declaring it as static? or maybe removing the #include in the tpp file for thr .h file, the h. points to the tpp anyways
 
Code:
void lpspi4_slave_isr() {
  _LPSPI4->SLAVE_ISR();
}

try declaring it as static? or maybe removing the #include in the tpp file for thr .h file, the h. points to the tpp anyways

Thanks Tony - wanted to let you know.

EDIT: This worked:
Code:
void static lpspi4_slave_isr() {
  _LPSPI4->SLAVE_ISR();
}
 
Last edited:
Hi, so I've always used your 3.x version of this library but now I've tried to port my already existing project to the T4 version and the problematic thing is that the library doesn't detect the active() state right. Im using a nordic as a master and am manually setting and clearing the CS pin but the program doesn't exit my ISR callback anymore.
I also tried your example and it has the same problem.
 
verify the CS, SCK, and GND pins are properly secured

Okay done, everything is fine (I verified by running a simple script just reading out the pin status which is high when there's no data send)

I can see the SCK running alright by just looking at the LED ON PIN 13. Basically as soon as the lines are connected, my teensy will always stay in the callback function

all the data is coming in just fine, but also only if I enable .sniffer().. does this have to do anything with my problem?
 
your MISO &MOSI pins may be reversed, in sniffer mode it basically snoops on what a master is sending on that line
 
your MISO &MOSI pins may be reversed, in sniffer mode it basically snoops on what a master is sending on that line

nope, that's not it. I actually get all the right data but after quitting transmission the application doesn't exit the callback.
 
I am stumped. Trying to get the slave code working on a T4.0 while using a T4.1 as the master. Not sure I have the master correctly set up:
Code:
#include <SPI.h>

void setup() {
  // initialize the digital pin as an output.
  pinMode(10, OUTPUT);
  SPI.begin();

}

void loop() {
  for(uint8_t i = 0; i < 9; i++){
      SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
      digitalWriteFast(10, LOW);
      Serial.println(SPI.transfer(i));
      delayMicroseconds(1);
      digitalWriteFast (10, HIGH);
      SPI.endTransaction();
  }
  Serial.println("=============");
  delay(500);
}
 
@tonton81

Was digging into the code a bit more and I don't think myFunc is ever being assigned via the OnReceive call. When I run the master with this slight change in the loop:
Code:
      SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
      digitalWriteFast(10, LOW);
      for(uint8_t i = 0; i < 9; i++){
        Serial.println(SPI.transfer(0));
      }
      delayMicroseconds(1);
      digitalWriteFast (10, HIGH);
      SPI.endTransaction();
  Serial.println("=============");
  delay(500);
this is what I am seeing in the slave window:
Code:
millis: 7416
0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 
millis: 8416
0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 
millis: 9416
0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0

and this is in the master:
Code:
=============
0
0
255
0
0
0
0
0
0
=============

It looks like its bypassing the function assignment:
Code:
  if ( _spihandler ) {
    _spihandler();
    SLAVE_SR = 0x3F00;
    asm volatile ("dsb");
    return;
  }
and printing from here:
Code:
  while ( !(SLAVE_SR & (1UL << 9)) ) { /* FCF: Frame Complete Flag, set when PCS deasserts */
    if ( SLAVE_SR & (1UL << 11) ) { /* transmit error, clear flag, check cabling */
      SLAVE_SR = (1UL << 11);
      transmit_errors++;
    }
    if ( (SLAVE_SR & (1UL << 8)) ) { /* WCF set */
      uint32_t val = SLAVE_RDR;
      Serial.print(val); Serial.print(" ");
      SLAVE_TDR = val;
      SLAVE_SR = (1UL << 8); /* Clear WCF */
    }
  }
  Serial.println();
only other place that the slave code prints data out. Maybe its related to this change I made: https://forum.pjrc.com/threads/66389-SPISlave_T4?p=301214&viewfull=1#post301214
 
your MISO &MOSI pins may be reversed, in sniffer mode it basically snoops on what a master is sending on that line

I think I found what causes the error but don't quite understand why its different here than in the 3.x version.

if I disable the interrupt and read the spirit line in the main loop, I need to clear the line by calling mySPI.popr()which is obvious but I don't understand why it doesn't work in the interrupt as intended. I read every single available byte with popr() but still stay in the interrupt even if I stop transmission.
 
For me, I got this working (though only in the main loop as I said) but Im dropping bytes sometimes. This also didnt happen with the 3.x version. What's different here? Its the same master sending the same packages but they are not well received.
 
For me, I got this working (though only in the main loop as I said) but Im dropping bytes sometimes. This also didnt happen with the 3.x version. What's different here? Its the same master sending the same packages but they are not well received.

Understood - for me I need it working with the interrupt mode. Yep - added in the onRecieve(myFunc) handler as well.
 
Ok finally figured out what was going on after several days. Such a simple fix to make it work.

Change this:
Code:
  mySPI.begin();
  mySPI.onReceive(myFunc);
to
Code:
  mySPI.onReceive(myFunc);
  mySPI.begin();
and it works as expected.
 
Ok finally figured out what was going on after several days. Such a simple fix to make it work.

Change this:
Code:
  mySPI.begin();
  mySPI.onReceive(myFunc);
to
Code:
  mySPI.onReceive(myFunc);
  mySPI.begin();
and it works as expected.

Good but painful find Mike.
 
Compiling error...

Hi !

Beginner (well, I guess...) question here.

I'm trying to compile the SPISlave_T4 sample with Arduino IDE, but I got this error : SPISlave_T4.tpp: No such file or directory
In which folder are the header and template files supposed to be ? I just put them in the main project location...


Thx for your help :)
 
Back
Top