Teensy 4.1 SPI1 BUG at > 150MHZ

wasserwiesel

Well-known member
Hi there,
My teensy 4.1 is connected to the mpu9250 over SPI1.
I found out that when I use a CPU Speed of 150mhz it works. It doesn't work at any other speed.
The security of my project is a top priority and when I consider that there are still bugs in this area, it makes me a little nervous. Is it even safe then, with 150Mhz?
I actually wanted to order my own PCB with an SPI connection to the IMU.
Should I use i2C or is there a quick fix?

I would be happy if someone could help me.


Here is my Code:

Code:
#include "MPU9250.h"

// SPI
#define PIN_MISO1             39
#define PIN_MOSI1             26
#define PIN_SCK1               27
#define PIN_CS1                 38

MPU9250 IMU(SPI1, PIN_CS1);

int status;

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

  SPI1.setMISO(PIN_MISO1);
  SPI1.setCS(PIN_CS1);

  pinMode (PIN_CS1, OUTPUT);
  digitalWrite(PIN_CS1, HIGH);  

  status = IMU.begin();
  
  if (status < 0) {
    Serial.println("IMU initialization unsuccessful");
    Serial.println("Check IMU wiring or try cycling power");
    Serial.print("Status: ");
    Serial.println(status);
    while (1) {}
  }
}
 
which library you are using?
could not find a MPU9250 for SPI that is compatible with your code
 
use
SPI1.beginTransaction(SPISettings(4'000'000, MSBFIRST, SPI_MODE0));

spi commands

SPI1.endTransaction();

or similar to control SPI speed (here 4 MHz)
 
the SPI speed and the CPU speed are total different clocks
.
However, if you use code where SPI clock speed is derived from CPU clock then you may run into issues.
If this happens in old low speed avr code, I don't know.

But this is a problem of mpu9250 library and not of SPI or Teensy.
SPI works for all CPU speeds

Suggest to inspect the library code and act accordingly.
 
Again not sure where you are getting 150Mhz?

From the header file of that library:
Code:
    const uint32_t SPI_LS_CLOCK = 1000000;  // 1 MHz
    const uint32_t SPI_HS_CLOCK = 15000000; // 15 MHz

And from the source file:
Code:
f( _useSPI ){
    // begin the transaction
    if(_useSPIHS){
      _spi->beginTransaction(SPISettings(SPI_HS_CLOCK, MSBFIRST, SPI_MODE3));
    }
    else{
      _spi->beginTransaction(SPISettings(SPI_LS_CLOCK, MSBFIRST, SPI_MODE3));
    }

So it either wants to run at 1 or 15mhz.

Now if you actually try for something like 150mhz, the SPI code to set the actual clock will:

Get the top level SPI clock configuration: Which I believe defaults to (720000000/3) = 240mhz.

And then you try to compute, the divisor: 240mhz/150mhz = 1 Which we then see is < 2 so we set _ccr = LPSPI_CCR_SCKDIV(div) ...

As that value +2 is the main divisor into the clock... 240mhz/2 = 120mhz which by default configuration is the fastest that SPI will run.
 
At 600MHZ CPU Speed and nanoseconddelay(120) its works only randomly, therefore I will wait 500ns for safety reasons. But what does this mean? - Is the digitalWrite too slow?
 
Again not sure where you are getting 150Mhz?

From the header file of that library:
Code:
    const uint32_t SPI_LS_CLOCK = 1000000;  // 1 MHz
    const uint32_t SPI_HS_CLOCK = 15000000; // 15 MHz

And from the source file:
Code:
f( _useSPI ){
    // begin the transaction
    if(_useSPIHS){
      _spi->beginTransaction(SPISettings(SPI_HS_CLOCK, MSBFIRST, SPI_MODE3));
    }
    else{
      _spi->beginTransaction(SPISettings(SPI_LS_CLOCK, MSBFIRST, SPI_MODE3));
    }

So it either wants to run at 1 or 15mhz.

Now if you actually try for something like 150mhz, the SPI code to set the actual clock will:

Get the top level SPI clock configuration: Which I believe defaults to (720000000/3) = 240mhz.

And then you try to compute, the divisor: 240mhz/150mhz = 1 Which we then see is < 2 so we set _ccr = LPSPI_CCR_SCKDIV(div) ...

As that value +2 is the main divisor into the clock... 240mhz/2 = 120mhz which by default configuration is the fastest that SPI will run.

150 MHZ CPU Speed
 
Sorry I missed it was the CPU speed.

I assume that you saw there is a Pull Request, waiting to be pulled in by @mjs513...

https://github.com/bolderflight/MPU9250/pull/64

It is typically not that digitalWrite is too slow, but that the teensy 4 is so fast that after we enable the CS pin then try to begin the SPI communications, the device is not fast enough to respond.

It is interesting that the changes were approved last July but still not incorporated?

Also depending on which version of Teensyduino you are using. I believe it was last March we changed slightly the clocking of starting up of transfers that maybe adds in a slight bit of time to start and end of transfers to allow for delaying of automatic add some delay in. But this is mostly for when hardware CS is used, but the side effect is it should do it in this case as well.
However my guess is, that it may still not be enough of a delay for that device
 
Just tried a couple things moved it over to SPI and used CS = 10.

See failure to correctly who I am register - getting 112 instead of 113/115. There is already a delaynanoseconds(200) with beginTransactions. This happens for the whole range of Clock speeds. Its also using MODE3 for some reason.
 
Just tried a couple things moved it over to SPI and used CS = 10.

See failure to correctly who I am register - getting 112 instead of 113/115. There is already a delaynanoseconds(200) with beginTransactions. This happens for the whole range of Clock speeds. Its also using MODE3 for some reason.

Yes, I had the same behavior, sometimes even different whoAmI values without changing anything. But I am amazed why such errors occur with so few people. However, an idea just occurred to me. The SPI traces on my PCB are 2-4cm long. Maybe they are too long, given that the traces from Teensy4.x are already very thin. Therefore, longer delays may be necessary. What do you think? Its just an idea ...
 
Sorry I missed it was the CPU speed.

I assume that you saw there is a Pull Request, waiting to be pulled in by @mjs513...

https://github.com/bolderflight/MPU9250/pull/64

It is typically not that digitalWrite is too slow, but that the teensy 4 is so fast that after we enable the CS pin then try to begin the SPI communications, the device is not fast enough to respond.

It is interesting that the changes were approved last July but still not incorporated?

Also depending on which version of Teensyduino you are using. I believe it was last March we changed slightly the clocking of starting up of transfers that maybe adds in a slight bit of time to start and end of transfers to allow for delaying of automatic add some delay in. But this is mostly for when hardware CS is used, but the side effect is it should do it in this case as well.
However my guess is, that it may still not be enough of a delay for that device

No problem, thank you for your help!
Yes I saw that pull request, thats how I came to the nanodelay solution.
I see, so the teensy is too fast - doesn't sound so bad ;)
I updated arduino and teensyduino to the latest version.

So with a higher delay like 500 it should be save to use?
 
No problem, thank you for your help!
Yes I saw that pull request, thats how I came to the nanodelay solution.
I see, so the teensy is too fast - doesn't sound so bad ;)
I updated arduino and teensyduino to the latest version.

So with a higher delay like 500 it should be save to use?

Don't think the problem is with Teensy as @WMXZ stated. Believe there is a problem with SPI implementation with the library.

As a quick and dirty test for SPI with the MPU-9250 I updated an old test library I had ported for SPI with the MPU9250, specifically with the setting up of the AK8963. Was having the same problem with my lib except my just keeps going and just reads the accel/gyro with 0's reported for the magnetometer. Problem now is to sort out the issue with SPI and the magnetometer.


EDIT: Right now i2c seems to working fine.
The other issue I have is that the WhoIAm val for my 9250 is 112 not 113/115 as identified in the lib. Think I ran into this before.
 
With my own lib (not the borderflight) I have similar issues. I get only the gyro X and Acc X values. But with the delay it is working. So it is rather a problem with teensy 4.x, it is just to fast as KurtE already said, and this makes sense.
 
There are times I have wondered how much it would work in cases like this, if we did something like:

#define SPI_MODE_SLOWCS 0x80

So maybe one could do something like: SPI.beginTransaction(SPISettings(3000000, MSBFIRST, SPI_MODE3 | SPI_MODE_SLOWCS));

And then change some of the processing where currently it goes like:
Code:
		_ccr = LPSPI_CCR_SCKDIV(div) | LPSPI_CCR_DBT(div/2) | LPSPI_CCR_PCSSCK(div/2);
That maybe if flag is set you change it to something like:
Code:
		_ccr = LPSPI_CCR_SCKDIV(div) | LPSPI_CCR_DBT(div) | LPSPI_CCR_PCSSCK(div);
And see if that helps...
 
Ok just got back to this. Unfortunately I deleted my original message before hitting save so this is just a summary. Tested with 4 different breakout boards:

Breakout Board A: DOA both for I2C and SPI. Scanner doesnt even see the chip.
Breakout Board B: Can get Accel/Gyro/Mag data when in I2C mode but only Accel/Gyro when using SPI using both the BolderFlight and my ported lib. Note Mag data not avail even at 150Mhz for me.
Breakout Board C: Works perfectly in I2C or SPI mode using the BolderFlight or my version. Can get data across SPI at 600Mhz clock without a problem. No issues.
Breakout Board D: This is the embeddedmasters MPU-9250 breakout board that I modified to use SPI. Works both with the BolderFlight and my ported lib at 600Mhz.

This is typical data from bolderflight for the T4.1 at 600Mhz clock for both breakout C and D:
Code:
0.134082	0.354360	-9.629970	0.000734	-0.000012	-0.000892	-5.856405	-12.366448	39.088379	24.504358
0.138871	0.354360	-9.620393	0.000068	-0.000145	-0.000093	-5.324004	-13.262568	37.194229	24.492378
0.119716	0.363937	-9.649124	0.000867	0.000121	0.000306	-5.501471	-12.008001	37.366425	24.504358
0.134082	0.320839	-9.644336	0.000467	0.000121	-0.001292	-7.453606	-12.904120	37.194229	24.507353
0.124505	0.330417	-9.673068	-0.000065	-0.000811	-0.000626	-4.969070	-12.187225	37.883011	24.477402

This is sample data using my lib, again for C/D boards:
Code:
Gyro : -5.06,1.34,-0.18, Acc : 0.08,0.14,0.98, Mag : 0.00,-8.40,44.49
Gyro : -3.23,2.44,-0.43, Acc : 0.07,0.14,0.98, Mag : 0.00,-8.40,44.49
Gyro : -4.88,1.77,-0.24, Acc : 0.07,0.15,0.97, Mag : 0.00,-8.40,44.49
Gyro : -3.90,2.07,-0.18, Acc : 0.07,0.14,0.97, Mag : 0.00,-8.40,44.49

I had also tried A/B boards on a T3.5 and they didn't work on that board as well.
 
Just as a quick update I reconfigured BreakoutBoard D to use SPI1:
Code:
#define PIN_MISO1             1
#define PIN_MOSI1             26
#define PIN_SCK1               27
#define PIN_CS1                 10 or 0

Again using BolderFlight lib with a T4.1 at 600Mhz, I am not seeing any problem:
Code:
0.134082	0.330417	-9.658702	0.001518	-0.000489	-0.000501	-4.081737	-12.366448	40.121555	24.444454
0.129293	0.344783	-9.658702	-0.000346	0.000577	0.000032	-4.259203	-12.545672	39.260574	24.435469
0.134082	0.335205	-9.677856	0.000053	0.000710	0.000698	-5.501471	-12.008001	40.810337	24.438465
0.143659	0.344783	-9.634759	-0.000479	-0.000489	-0.001033	-5.501471	-11.291105	40.121555	24.432474
0.148448	0.335205	-9.639547	0.000985	-0.000489	-0.000234	-5.501471	-11.291105	40.121555	24.441460
 
Thank you very much mjs513 for your testing! Good to see, that it's working on your side. My confidence in the Teesy is increasing thanks to you and I will continoue to use spi Mode.
I remember in past I came across conversations where people had problems or different behavior with the mpu9250 using spi.
One reason more for me to order my own PCB with SPI only configuration. Although the schematic actually looks correct from that breakout board...
Nevertheless, I will continue to use a delay after the DigitalWrite in the future, because I know from experience that when such strange phenomena occur, in most cases it is simply the time factor that can be used to fix these issues. The real sourse of these behaviours is hard to find. Whether the origin is too long ables, an unfortunate combination of resistors or capacitors that lead to noise, there are many possible factors.
But thanks to you guys we found a solution.

By the way, this is the Board I am using for my prototype.

https://www.amazon.de/MakerHawk-MPU...ild=1&keywords=mpu+9250&qid=1610267386&sr=8-3
 
Thank you very much mjs513 for your testing! Good to see, that it's working on your side. My confidence in the Teesy is increasing thanks to you and I will continoue to use spi Mode.
I remember in past I came across conversations where people had problems or different behavior with the mpu9250 using spi.
One reason more for me to order my own PCB with SPI only configuration. Although the schematic actually looks correct from that breakout board...
Nevertheless, I will continue to use a delay after the DigitalWrite in the future, because I know from experience that when such strange phenomena occur, in most cases it is simply the time factor that can be used to fix these issues. The real sourse of these behaviours is hard to find. Whether the origin is too long ables, an unfortunate combination of resistors or capacitors that lead to noise, there are many possible factors.
But thanks to you guys we found a solution.

By the way, this is the Board I am using for my prototype.

https://www.amazon.de/MakerHawk-MPU...ild=1&keywords=mpu+9250&qid=1610267386&sr=8-3

That looks like my breakout board C that I tested successfully with the SPI.

Just for reference I am using the Bolderflight library with the 200 ns delays which is the PR that was referenced. However, the standalone version I have doesn't use the delays. Just an added data point

Are you still having an issue at 600Mhz?
 
Last edited:
Back
Top