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.
 
Back
Top