Teensy 4? testing mbed NXP MXRT1050-EVKB (600 Mhz M7)

I updated post #1. Schematic does show 24 MHz crystal and 32KHz crystal (RTC). I tested RTC with mbed API and measured -46 ppm drift.

Also added SPI data rate tests.

I couldn't get the SDK raw Ethernet example to run.
 
Last edited:
I just ran Kris Winer's MPU-9250 sketch along with his 9250 breakout board. Based on his notes:

I have also added a program to allow sensor fusion using the MPU-9250 9-axis motion sensor with the STM32F401 Nucleo board using the mbed compiler. The STM32F401 achieves a sensor fusion filter update rate using the Madgwick MARG fusion filter of 4800 Hz running the M4 Cortex ARM processor at 84 MHz; compare to the sensor fusion update rate of 2120 Hz achieved using the same filter with the Teensy 3.1 running its M4 Cortex ARM processor at 96 MHz.

On the imrtx1050 his sketch I got 8117.8 Hz update rate.

For comparison I ran it on a Teensy 3.5 the sensor fusion update rate was 6365.16 Hz with the T3.5 overclocked to 120MHz.

EDIT: based @manitou's comment I tried the MCXpresso udp example. While on the serial monitor I saw:
Code:
************************************************
 UDP Echo example
************************************************
 IPv4 Address     : 192.168.0.102
 IPv4 Subnet mask : 255.255.255.0
 IPv4 Gateway     : 192.168.0.100
************************************************
which matches the read me, when I run the echo tool I just get timeouts. May be my router settings, not sure
 
Last edited:
for testing the mpu-9250 am using the mbed toolchain. For the UDP tests I used the MCXpresso. However, I tried to run a udp example using the mbed compiler but couldn't get it to compile.

I just finished a second test with the MPU-9250 code but using a i2c frequency of 1,000,000. It just bothered me that there was not more of a delta with the update rates. The results were as follows:
Code:
T3.5           14,388.33 Hz
1050           22,005.26 Hz
 
I couldn't get the SDK ethernet example (enet) to run under mbed, but I did get it to build and run using mcuxpresso IDE. Also tested lwIP UDP and TCP. Post #1 updated.
 
Last edited:
After talking with Onehorse on his GitHub page about some strange timings I was getting and getting interrupt version to work correctly. I ported over the ESP32 version so it would run on the Teensy and then on the 1050. The new version fixed a few issues. But others now seem to be cropping up when using the interrupts on 1050 using the mbed compiler.

First, I changed what was I was using for the timing measure. What I do now is measure the timing from reading the data from the MPU-9250 and 40 iterations of the filter:
Code:
Arduino version - polling:
   tstart = micros();
     // If intPin goes high, all data registers have new data
   //if(newData == true) {  // On interrupt, read data
   if (readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01) {  // On interrupt, check if data ready interrupt
     newData = false;  // reset newData flag
     readMPU9250Data(MPU9250Data); // INT cleared on any read
   
    // Now we'll calculate the accleration value into actual g's
     ax = (float)MPU9250Data[0]*aRes - accelBias[0];  // get actual g value, this depends on scale being set
     ay = (float)MPU9250Data[1]*aRes - accelBias[1];   
     az = (float)MPU9250Data[2]*aRes - accelBias[2];  

    // Calculate the gyro value into actual degrees per second
     gx = (float)MPU9250Data[4]*gRes;  // get actual gyro value, this depends on scale being set
     gy = (float)MPU9250Data[5]*gRes;  
     gz = (float)MPU9250Data[6]*gRes; 
  
    newMagData = (readByte(AK8963_ADDRESS, AK8963_ST1) & 0x01);
    if(newMagData == true) { // wait for magnetometer data ready bit to be set
      readMagData(magCount);  // Read the x/y/z adc values
  
    // Calculate the magnetometer values in milliGauss
    // Include factory calibration per data sheet and user environmental corrections
      mx = (float)magCount[0]*mRes*magCalibration[0] - magBias[0];  // get actual magnetometer value, this depends on scale being set
      my = (float)magCount[1]*mRes*magCalibration[1] - magBias[1];  
      mz = (float)magCount[2]*mRes*magCalibration[2] - magBias[2];  
      mx *= magScale[0];
      my *= magScale[1];
      mz *= magScale[2]; 
    }
    
    for(uint8_t i = 0; i < 40; i++) { // iterate a fixed number of times per data read cycle  
      Now = micros();
      deltat = ((Now - lastUpdate)/1000000.0f); // set integration time by time elapsed since last filter update
      lastUpdate = Now;
  
      sum += deltat; // sum for averaging filter update rate
      sumCount++;
      MadgwickQuaternionUpdate(-ax, ay, az, gx*pi/180.0f, -gy*pi/180.0f, -gz*pi/180.0f,  my,  -mx, mz);
    }

    tstop = micros();
    deltat1 = ((tstop - tstart)); // set integration time by time elapsed since last filter update

    sum1 += deltat1; // sum for averaging filter update rate
    sumCount1++;
    if(sumCount1 == 1000){
      Serial.println("Filter Timing:");
      Serial.print(sumCount1); Serial.print(", "); Serial.println(sum1);
      Serial.println("Avg us: "); Serial.println(sum1/sumCount1);
      Serial.println();
      sum1 = 0;
      sumCount1 = 0;
    }
 }
Its easy enough to change to measure the timings for a hard interrupt. Anyway what I got using this method:
Code:
1050
Reg interrupt
	 400khz ----- 634 us
	1000khz ----- 304 us

with interrupt pin
	 400khz ----- xxx us
	1000khz ----- 124 us

T3.5
Reg interrupt
	 400khz ----- 1074 us
	1000khz -----  682 us
interrupt pin
	  400khz ====  958 us
	 1000khz ====  621 us

I would be leery of putting any stock into the times using the interrupt pin on the 1050. Interrupts just don't seem to work properly on the mbed side.
 
Here is the mbed mpu9250 code I used a few years back
https://os.mbed.com/users/manitou/code/mpu9250/
To run on the EVKB board, i changed from mbed.bld to using mbed-os build library. There is a USE_ISR flag for the different modes. I think I had to change i2c to use A4,A5 and use D12 as interrupt pin. My EVKB/M7 results were with 400khz I2c
USR_ISR 0 : average rate = 4852.834961
USR_ISR 1 : average rate = 171395.093750 (weird? and its not tracking board movement)

FWIW, same program on mbed K64F works for both no ISR (rate 3942) and ISR (28909).

Interrupts just don't seem to work properly on the mbed side
My GPS PPS test used pin interrupts (albeit only one tick per second), and it worked fine on EVKB.

EDIT: Oooops, pin 12 is what the program used with ye ol mbed K64F, but for EVKB board pins 10-13 are not connected to the arduino header. My program has a readycnt that counts data-ready interrupts, and its 0 when using pin 12. it should be 100 per 500 ms. So switching to pin 2 for the interrupt pin, M7 board rate is 113738, which is about 5 times K64F rate (@120mhz), which would be expected for the 600 MHz M7.
 
Last edited:
Hmm. The difference in the version I am using is this single line when using the interrupt, I am also using d2 instead of D12:
Code:
 mpu9250.readByte(MPU9250_ADDRESS, INT_STATUS);  //? need this with ISR

I will give it a try to see if it works with my version. BTW. That was pretty much the sketch I was using in my intial tests, no test for mag ready. I am going to do some more testing to see what occurs. I will keep you posted

UPDATE: That construct didn't work for me but what did was reading the isr directly:

Code:
        //if (mpu9250.readByte(MPU9250_ADDRESS, INT_STATUS) & 0x01) {
          if(isrPin.read() == 1) {
            newData=false;
            //mpu9250.readByte(MPU9250_ADDRESS, INT_STATUS);  //? need this with ISR
                    
            mpu9250.readMPU9250Data(MPU9250Data); // INT cleared on any read
EDIT: Forgot to post the results:
Code:
Reg interrupt
	 400khz ----- 634 us
	1000khz ----- 304 us

with interrupt pin
	 400khz ----- 572 us
	1000khz ----- 247 us

UPDATE2: I just copied the code over from the link for the sketch you are using as well as the mpu9250.h and its not working. Oh, the weirdness that you see is "171395" using the isr is probably because the isr is not firing. That what kind of sent be down this path of investigation.
Code:
1050

If I change the way the interrupt works by reading it directly I get at 400k
[CODE]average rate = 1751.004395
even stranger hah. But everything including the angles are updating

You might want to check this discussion out that I was having with Onehorse: https://github.com/kriswiner/MPU9250/issues/325
 
Last edited:
FYI, I updated my post #34. Using pin 12 for the ISR pin is bad for the mbed M7 eval board where pins 10-13 are not connected to the "arduino" header. M7 rate is 113738. What's interesting about those earlier tests was onehorse's STM32L4 (dragonfly). The dragonfly I2C API supported DMA, so you could start your I2C DMA's for the sensor data and continue to do fusion updates and get a high rate for an 80 MHz MCU.
 
I2C with the MPU9250 still driving me a little on the crazy side but don't think its an i2c issue. Over the last day or so did a modification of the code to see how many times the interrupt fired per 1000 loop cycles and got some very strange and disturbing results:
1. For both 400k and 1000khz i2c frequency it was showing only 8 or 9 interrupt firings using the polling method
2. If I switched over to a pin interrupt it increased to 12 and 27 respectfully
3. If I did the same measurement on the T3.5 it was only showing about 2 hits for any method and frequency
4. On the Teensy 3.5 I used the same timing measurements but using Brian Taylors library and it showed that for every 1000 cycles the pin interrupt fired 1000 times and the update rates made a heck of a lot more sense to me.

I started the process of porting over the library to the mbed side but getting an error that I just can't figure out and can't seem to find anything on in a google search. Error message ==>
Code:
Error: "MPU9250::MPU9250(mbed::I2C &, std::uint8_t)" provides no initializer for in "MPU9250/MPU9250.cpp", Line: 28, Col: 2

In the .h file I have:
Code:
class MPU9250{
  public:
    
    MPU9250(I2C &i2c, uint8_t addr); 
    ~MPU9250();   …...

on the .cpp file:
Code:
MPU9250::MPU9250(I2C &i2c, uint8_t addr)
{
    _i2c = &i2c;
    _address = addr  << 1;
    _useSPI = false; // set to use I2C
}

If you have an ideas please let me know.
 
I2C with the MPU9250 still driving me a little on the crazy side but don't think its an i2c issue. Over the last day or so did a modification of the code to see how many times the interrupt fired per 1000 loop cycles and got some very strange and disturbing results:
1. For both 400k and 1000khz i2c frequency it was showing only 8 or 9 interrupt firings using the polling method
2. If I switched over to a pin interrupt it increased to 12 and 27 respectfully
3. If I did the same measurement on the T3.5 it was only showing about 2 hits for any method and frequency
4. On the Teensy 3.5 I used the same timing measurements but using Brian Taylors library and it showed that for every 1000 cycles the pin interrupt fired 1000 times and the update rates made a heck of a lot more sense to me.

I started the process of porting over the library to the mbed side but getting an error that I just can't figure out and can't seem to find anything on in a google search. Error message ==>
Code:
Error: "MPU9250::MPU9250(mbed::I2C &, std::uint8_t)" provides no initializer for in "MPU9250/MPU9250.cpp", Line: 28, Col: 2

In the .h file I have:
Code:
class MPU9250{
  public:
    
    MPU9250(I2C &i2c, uint8_t addr); 
    ~MPU9250();   …...

on the .cpp file:
Code:
MPU9250::MPU9250(I2C &i2c, uint8_t addr)
{
    _i2c = &i2c;
    _address = addr  << 1;
    _useSPI = false; // set to use I2C
}

If you have an ideas please let me know.

Are you initializing the MBED I2C class that you're passing to the MPU9250 library? We don't need to do it explicitly with Arduino because it's done for us in the core files. If you look at your Teensy core directory, (/teensy/avr/libraries/Wire.cpp), line 322 Wire is instantiated for us, so we don't need to call "TwoWire Wire" in our code. Just a guess...otherwise, if you want to send a link to the MBED I2C library and your source code, that might provide more clues.
 
Err, just thought of something else, how is I2C defined as the private variable in the h file? Is it a reference?
 
Hi Brian.
To answer your questions:
Are you initializing the MBED I2C class that you're passing to the MPU9250 library
Yep. This is the way I have it set up:
Code:
//Set up I2C, (SDA,SCL)
I2C i2c(A4, A5);

//MPU9250 Imu(A4, A5);  // SDA, SCL
MPU9250 Imu(i2c, 0x68);

how is I2C defined as the private variable in the h file?
Code:
    // i2c
    I2C *_i2c; 
    uint8_t _address;

And since I should know better by now, and like you said, here is the whole source code for the lib and main:View attachment MPU-9250-1050.zip
 
Ok spent the day revamping how I converted the library for use with the 1050. Basically just went with a .h file and got rid of the .cpp file and all initialization is done in the convention way. I did get it work with reading the both the raw data and calibrated data from the sensors. I did incorporate the quaternion filters into the library as well if you want to use them. But for the life of me couldn't get the interrupt piece of it work. I implemented on the mBed side the same way as manitou did in the program link he posted and used the libraries method for enabling the interrupt. Still no dice in getting it to fire. Where manitou's program showed it firing 101 times in 500 ms, it only fired 27 times in the same amount of time and neither one does the data update correctly at 1000k i2c freq.

Here it is for reference:View attachment MPU9250AHRS_bfv2_zip_mimxrt1050_evk.zip

EDIT:
Decided to run one more series of tests. Gets interesting now.
1. at 400k i2c rate - mantiou's program fires 101 times, and data is updated correctly
2. at 100k I2C rate - the new lib seems to be firing at about 112 times in the same time period

Not sure what the problem is here - sound to me like some sort of timing issues? Don't know just talking out loud.
 
Last edited:
Max MPU-9250 I2C bus speed is 400kHz.

Is it an issue getting the MPU-9250 interrupt setup to fire at the correct rate or an issue getting the NXP 1050 to recognize the interrupt? Assuming that you're using a breadboard, you could have a Teensy 3.x communicating with the MPU-9250 sensor and bring two jumpers out of the MPU-9250 interrupt pin, one to the Teensy 3.x and one to the NXP 1050. Then you could have the Teensy 3.x do all of the MPU-9250 setup and just have the NXP 1050 looking for the interrupt. Plus if you had both the Teensy 3.x and NXP 1050 counting interrupts, you could compare the two and see if the NXP 1050 missed any.
 
@mjs513 - odd the interrupts are not coming on schedule. Is the _isr() code just setting a flag for loop to read - or is read in the _isr()? What happens with a tight/empty loop() just watching for int pin change ( disable interrupt ) and then read on every change.

Found I hadn't hit POST on this - seeing Brian's idea to run with Teensy and just trap _isr() on 1050.

Seems I was running onehorse's mpu9250 at 1 or 2 MHz?
 
@defragster = on the teesny 3.5 with Onehorse's breakout board I can run Brian's library with interrupts enabled at 1Mhz no issue - I get the number of hits I am expecting. For instance if I count the isr firings per 1000 loop counts I get a 1000 isr hits. This is not what I am seeing with the nxp. That is why I went over to Brian's library for comparison. To be honest it may just be an mBed issue.

@brtaylor -
Is it an issue getting the MPU-9250 interrupt setup to fire at the correct rate or an issue getting the NXP 1050 to recognize the interrupt?
I think I got it set up correctly - going to put a couple of printf's to make sure of it later.

Going to take a look hooking up the Teensy to measure the interrupts as well as the looking at "a tight/empty loop() just watching for int pin change ( disable interrupt ) and then read on every change."

Thanks for the suggestions
Mike
 
Hmm. The difference in the version I am using is this single line when using the interrupt, I am also using d2 instead of D12:
Code:
 mpu9250.readByte(MPU9250_ADDRESS, INT_STATUS);  //? need this with ISR

In my notes from a few years back I noted that I needed the read of INT_STATUS, otherwise the program would eventually stop tracking the IMU. You might try putting that back in the ISR version. From the ref manual, I couldn't see that reading INT_STATUS should make a difference. It does, as you noted, speed things up.

Also, I wouldn't spend too much time on the teensy forum figuring out MBED software problems.
 
@manitou. Doesn't appear that its really is an MBED problem - funny I was just thinking about spending time discussing it here. My apologies for that, but was just trying to ensure myself it wasn't an issue that had to be dealt with in the future.

@brtaylor, @defragster and @manitou. Took Brian's advice and verified that 9250 interrupt was actually firing. It was firing at the SRD that I set - checked 100hz, 200hz and 500hz rates - a match. Comparing that to what I was getting - sometimes strange - I just rewired the break board to the 1050. That seemed to do the trick. Just as a bottom line up front - got it working where no matter what I2C rate you set the data updates correctly and am getting consistent results.

@manitou - you don't need to do the read status byte when using the interrupt pin. Verified that.

Ok now for the results:
Code:
With running the filter 10x per Onehorse's recommendation:
  I2C       SRD     Avg Rate
 100k      200hz     1961hz
 400k      200hz     1967hz
1000k      200hz     1971hz
1000k      500hz     4903hz

Think I beat this to death.
 
In post #2, I noted that mbed core has conditional code for ARM GCC to move the flash into RAM. I didn't see evidence of that when printing out hex addresses of stack variable, external variable, constant, and function().
Code:
                 stack   ext     constant   &fcn
       ARM gcc 81dfe3b0 80000000 60007f18 60007ec1  mbed online
       gnu gcc 2001ffb8 200014ac 600079dc 6000335d  mcuxpresso
       gnu gcc 2001ffb8 200077cc 200059bc 20001351  mcuxpresso with "link to RAM"
       gnu gcc 81dfffec 80000ae0 6000ce48 60002ccd  mbed export  

        ref ch 2
        8000_0000 DFFF_FFFF  1.5GB RAM
        6000_0000 7F7F_FFFF  504MB flexSPI   ch 30  hyperflash boot
        2000_0000 2007_FFFF  512KB DTCM  flexRAM
          TCM tightly coupled memory  fast mode 1 cycle, else 2 cycle ch 29
The MCUXpresso build does use the TCM memory for RAM. EDIT: In MCUXpresso you can configure project to "link to RAM", as the third line in the table above shows. I ported coremark to run under MCUXpresso (GNU GCC 7.2.1, -O3) and got 2444 iterations/sec. Linking coremark to run in RAM didn't make a noticeable difference. Enabling LTO slowed coremark.

Also in post #1, I added comments on errata for ISRs.
 
Last edited:
Just finished testing the external ram that uses the semc controller driver example. As expected the example runs. In order just to get a relative measure I wrapped the calls for 32-16-8 bit writes in a timer that should return CPU cycle counts:
Code:
 SEMC SDRAM 32 bit Data Write and Read Compare Start!

 SEMC SDRAM 32 bit Data Write and Read Compare Succeed!
32 bit Write Clock Cycles == > 14977467

 SEMC SDRAM Memory 16 bit Write Start, Start Address 0x80000000, Data Length 4096 !

 SEMC SDRAM Read 16 bit Data Start, Start Address 0x80000000, Data Length 4096 !

 SEMC SDRAM 16 bit Data Write and Read Compare Start!

 SEMC SDRAM 16 bit Data Write and Read Compare Succeed!
16 bit Write Clock Cycles == > 15011582

 SEMC SDRAM Memory 8 bit Write Start, Start Address 0x80000000, Data Length 4096 !

 SEMC SDRAM Read 8 bit Data Start, Start Address 0x80000000, Data Length 4096 !

 SEMC SDRAM 8 bit Data Write and Read Compare Start!

 SEMC SDRAM 8 bit Data Write and Read Compare Succeed!
8 bit Write Clock Cycles == > 14767014

 SEMC SDRAM Example End.
I found out how to do that from this site: https://www.element14.com/community/roadTestReviews/2713/l/nxp-imx-rt1050-ev-kit-and-display-review. Besides the code block I borrowed for the timer it discusses timings for FFT using the CMSIS-DSP. It also explains how to do it yourself. Makes a good read, highly recommend.
 
FWIW, Teensy will use similar copy code at startup. Supposedly NXP's boot ROM can do the copy to ram, but I can't figure out how to make it work. Looks like the mbed folks couldn't get it either. Not too surprising, since NXP's documentation is light on details and riddled with errors.
 
Back
Top