SPI.transfer() not blocking / code executing out of order? (T 4.1)

LWorm

Member
Hello, I'm encountering a strange problems I haven't seen before on Teensy (using Teensy 4.1). I am writing a test platform that uses SPI and am encountering an issue where the CS returns high before the SPI. transfer completes. You can see this in the attached oscilloscope image on the yellow waveform. I am using PlatformIO as the programming environment and the Teensy is running at default settings (600 MHz, etc.). I have tried running this code from via PlatformIO on both Windows and MAC and see the same result. So far I have tried switching CS pins, digitalWrite and digitalWriteFast, creating a new project from scratch, and changing the SPI frequency. The only solution I have encountered so far is to place a delay (commented out in the code below) to block the CS from going high until the transactions have completed. I wanted to attempt using the Arduino IDE but I am having trouble getting 2.X running on my laptop due to some javascript issues.

I have been using Teensys for about 7 years now and have never seen anything like this, but it has been about a year since I have done any development with this platform so its possible I am making a dumb mistake but what I am doing now looks pretty much identical to my code for previous chips which functions. Any ideas or assistance would be greatly appreciated!

Code:
// Include necessary Arduino libraries
#include <Arduino.h>
#include <SPI.h>

// Include local libraries and headers
#include "pin_definitions.h"

void setup() 
{
  SPI.begin();
  pinMode(SPI_CS_DAC_PIN, OUTPUT);
  digitalWrite(SPI_CS_DAC_PIN, HIGH);

}

void loop() 
{
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));

    digitalWrite(SPI_CS_DAC_PIN, LOW);
    SPI.transfer(0x08);
    SPI.transfer16(32000);
    // delayMicroseconds(29);
    digitalWrite(SPI_CS_DAC_PIN, HIGH);
    SPI.endTransaction();

  delay(1000);
}

IMG_1012.jpg
 
I think this SPI class assumes that you use the CS pin, which is pin 10 (or alternatively 36 or 37 after using SPI.setCS(pin) ).
The toggling of CS will then be automatic.
But you cannot just pick any pin anymore.

Your SPI_CS_DAC_PIN is?
 
Ah, I had been using pin 25 for this device's chip select as 10 is to be used for a different slave. Does this mean it is no longer possible to have multiple slave devices on a single SPI bus on Teensy without hardcoding the delay?

Edit: After testing I still see this issue with pin 10.
 
Last edited:
You are free to use any digital IO pin you want for CS for SPI.

The SPI.setCS() pin is for those sketches and libraries that do more hardcore low level programming of the SPI interface.
For example some display drivers will use it. They are not utilized as much on the T4.x as on the T3.x as the processors
handles them differently.

The SPI.transfer(val);
Should not return until the SPI hardware says it has data to return, which should only happen after it has received and processed the last bit of the transfer.
So your setting it high after the return should work fine.... However some devices might need a gap of time between when the transfer completes and when the CS is deasserted.
 
Thanks KurtE, that had been my experience. If you look at the waveform you can see the CS goes high almost immediately after the SPI clock has started so it is not an issue with needling one additional clock cycle of CS in this case.

I think my next steps will be to try on a new Teesny once one arrives and to attempt to get the Arduino IDE functioning and attempting it there. If anyone else has suggestions on what to try it would be greatly appreciated.
 
You are free to use any digital IO pin you want for CS for SPI.

The SPI.setCS() pin is for those sketches and libraries that do more hardcore low level programming of the SPI interface.
For example some display drivers will use it. They are not utilized as much on the T4.x as on the T3.x as the processors
handles them differently.

The SPI.transfer(val);
Should not return until the SPI hardware says it has data to return, which should only happen after it has received and processed the last bit of the transfer.
So your setting it high after the return should work fine.... However some devices might need a gap of time between when the transfer completes and when the CS is deasserted.

This could be a bug in the SPI.transfer16() library code.
It has this line of code
while ((port().RSR & LPSPI_RSR_RXEMPTY)) ; // wait while the RSR fifo is empty...
Which may not do what was hoped for when the compiler optimizer is on....
However, the 8 bit transfer seems to fail also... Maybe also the optimizer that causes trouble with the while(1) {};?
 
The 8 bit transfer works as far as the expected data appearing in the MOSI waveform (purple in the capture), but yes it does fail in the sense that it does not block the CS from going high.

It does seem like it could be a compiler optimization issue, but this is an area I've never explored in Teensy/Arduino development. Haven't been able to find much online concerning enabling or disabling any optimization in PlatformIO.
 
It does seem like it could be a compiler optimization issue, but this is an area I've never explored in Teensy/Arduino development. Haven't been able to find much online concerning enabling or disabling any optimization in PlatformIO.
See this link for details on setting build optimizations in PlatformIO
 
Last edited:
I tried to compile your program, but I'm missing pin_definitions.h.

Code:
/tmp/arduino_modified_sketch_63735/sketch_aug24b.ino:6:10: fatal error: pin_definitions.h: No such file or directory
    6 | #include "pin_definitions.h"
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
Error compiling for board Teensy 4.1.
 
I tried to compile your program, but I'm missing pin_definitions.h.

Code:
/tmp/arduino_modified_sketch_63735/sketch_aug24b.ino:6:10: fatal error: pin_definitions.h: No such file or directory
    6 | #include "pin_definitions.h"
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.
Error compiling for board Teensy 4.1.

Apologies Paul, the relevant code there is as follows:
Code:
#define SPI_CS_DAC_PIN 25
 
Apologies Paul, the relevant code there is as follows:
Code:
#define SPI_CS_DAC_PIN 25


Unexpected observation: when starting a new sketch on my PC and looking at the scope, I get to see this:
TEK00003.PNG

With the same code:
Code:
// Include necessary Arduino libraries
#include <Arduino.h>
#include <SPI.h>

// Include local libraries and headers
//#include "pin_definitions.h"
#define SPI_CS_DAC_PIN 25
void setup() 
{
  SPI.begin();
  pinMode(SPI_CS_DAC_PIN, OUTPUT);
  digitalWrite(SPI_CS_DAC_PIN, HIGH);

}

void loop() 
{
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));

    digitalWrite(SPI_CS_DAC_PIN, LOW);
    SPI.transfer(0x08);
    SPI.transfer16(32000);
    // delayMicroseconds(29);
    digitalWrite(SPI_CS_DAC_PIN, HIGH);
    SPI.endTransaction();

  delay(1000);
}
Arduino Version: 2.1.1 CLI Version: 0.32.3

So what is it that makes your code compile to something different??
 
Thank you everyone for your help! The solution ended up being to add the following like to my platformio.ini file:

Code:
platform_packages = platformio/toolchain-gccarmnoneeabi-teensy@^1.110301.0

After adding this line, PIO downloaded the relevant package file and seems to have made it the default as after commenting this line back out the SPI still functions correctly with the CS.
 
Old thread, but I started seeing exactly this issue today (also using platformio). But I’m already using v1.110301.0 of the toolchain, and having tested my hardware with the same code compiled in Teensyduino vs. platformio I see the same “early return” problem in both.

It’s possible that the version bump fixed it for OP, but in my case I’m 99% sure it’s a hardware issue – I extended the SPI lines with a Flat Flexible Cable, and now I’m seeing some significant ringing on the clock and data lines. Running exactly the same code with no FFC attached and just scoping the Teensy pins, I don’t see the same issue. The CS pin is held low until the full byte is transferred.

If i understand correctly, ringing lines might cause the Teensy to overcount clock cycles, which could make the SPI library think it has completed the byte transfer early.

I’m going to try adding 100ohm resistors to the transmitting end of the SPI clock/MOSI lines as recommended elsewhere on the forum and see if that damps the ringing and solves the “early return” issue.
 
If the problem continues, please try with Arduino IDE, just in case this turns out to be some subtle difference with PlatformIO settings. Because Arduino IDE doesn't make compiler commands and other details editable, it's a "known quantity" for testing and reproducing strange problems.

If this is reproducible with Arduino IDE, please show some photos of the wiring. I'd like to try to reproduce it here.
 
Adding a 100ohm resistor solved the problem. I added it to the transmitting end of the SPI clock (next to Teensy pin 13). It eliminated the ringing from the clock signal transients and all the SPI devices are working fine without having to add any delays. So it was definitely a hardware issue in my case. Or I guess a hardware issue causing a software issue. :)

FWIW my SPI clock signal is travelling ~32cm now to its furthest destination, so it’s maybe not surprising that the resistor is needed:

Teensy pin > 100ohm resistor > 2cm PCB trace > 10cm FFC > 20cm PCB trace > SSD1306 OLED display​
 
Back
Top