Teensy 4.0 First Beta Test

Status
Not open for further replies.
3.3v Power or SPI issue Question

Lately I have been testing with my version of the T4 breakout board so I can use Arduino style shields I put together or have. Right now I am testing with the Prop Shield on a shield and it working fine - tested with SensorFusion, LED strips and Serial Flash so all that is good. I tested a ILI9341 shield so I don't have to mess with jumpers (my design) and that works fine by itself like the Prop Shield. If I stack them they run no problem on a T3.5 breakout board, sensor data is displayed in SerMon and Compass displayed on ILI9341. Now if I put them on the T4 breakout the SensorFusion data is displayed on the SerMon but the display just flickers, nothing is displayed.

Not sure this is me or there is some limitation. I am using the ILI9341_t3n library for the display.
Maybe need to see what pin is connected to what and program... To try to wire one up...
 
Maybe need to see what pin is connected to what and program... To try to wire one up...

I was afraid you were going to say wire one up - argh.... Anyway, SPI uses standard 11,12,13 pins, no CS pin except for the Display which is on pin 10. Only possible conflict could be the touch pins but not using touch. I already cut pins to T-sck and T-miso, maybe cut the other ones as well to take touch out completely
 
Hook ups...

I am assuming 9 for DC? Reset? LED? direct or resistor or ...

Might take awhile... Other stuff going on as well
 
Hook ups...

I am assuming 9 for DC? Reset? LED? direct or resistor or ...

Might take awhile... Other stuff going on as well

DC=9, RST=8, LED = 3.3v on the real (PJRC) breakout board RST=23.

…… DELETED …...

No rush - take care of what you need to take care of.

EDIT: Ok my bad I forgot to change RST to pin 23. So once I did that it is working no problem. But its with only SDA/SCL hooked up along with 3.3v and ground. So now to try and figure out whats causing the problem when everything else on the prop shield is connected along with the display
 
Last edited:
DC=9, RST=8, LED = 3.3v on the real (PJRC) breakout board RST=23.

…… DELETED …...

No rush - take care of what you need to take care of.

EDIT: Ok my bad I forgot to change RST to pin 23. So once I did that it is working no problem. But its with only SDA/SCL hooked up along with 3.3v and ground. So now to try and figure out whats causing the problem when everything else on the prop shield is connected along with the display

Maybe there is a solder bridge allowing each to work alone and die together?

Run KurtE's Pin test?

What would be the best variant of that to test for bridges when testing pin X as output to measure on Pin 13? Set X-1 and X+1 to INPUT and put ALERT on SerMon? And maybe have a PIN designated 'Button' to advance from Pin_X to Pin_X+1? That's what I do with a DVM. Of course ALL other pins { except TEST Rx and Tx }could be set to INPUT just in case there is a short elsewhere.

Could also set ALL PINS output with a HIGH on then cycle one pin to INPUT at a time and see if it shows any value?
 
@mjs513, ...

Glad you got it party way working... I was in process of hooking up everything on breadboard... But other stuff...

The things I was going to try was to make sure of was to verify the states of pins 6 and 7
That is pin 7 LOW so SPI data not going to LEDS.

Pin 6 HIGH - as to make sure the memory chips were trying to use SPI...
Pin 10 HIGH
Was going to manually add these settings at start of setup to make sure SPI objects were not walking into each other.
 
@mjs513, ...

Glad you got it party way working... I was in process of hooking up everything on breadboard... But other stuff...

The things I was going to try was to make sure of was to verify the states of pins 6 and 7
That is pin 7 LOW so SPI data not going to LEDS.

Pin 6 HIGH - as to make sure the memory chips were trying to use SPI...
Pin 10 HIGH
Was going to manually add these settings at start of setup to make sure SPI objects were not walking into each other.

Since I heard the typical beep of a new message I looked here first. Anyway just had SPI hooked up except for a few pins - was doing it one at a time. So I checked while on the breadboard.

Pin 7 and pin 6 both showed high - not sure why unless its set in startup. Forced both pins in the sketch low and everything ran the way its suppose. Its interesting that on the T3.5 no problem so it has to be the pin settings for pins 6/7. I also cut traces going to the display that use those pins.
 
… but a huge FIREHOSE to drink from :)

.

Indeed, you have found the marble in the oatmeal.

Relative to marbles, what we have here is a warehouse to put them in.

As always someone else asks the questions I have. I'm looking for where to dig in. Yes, I'm reading.

I have my test unit, everything I have done with Teensy boards has been audio and MIDI, so I immediately started testing the audio library and everything I have tried has compiled and run. ( just audio not usb )

So far all I've done is gasp at the memory left over from what took up everything on a 3.6.

I'll keep looking for rocks in the rows, but if anyone knows of a pile that needs moving, please point the new help at it.
 

Did a real quick test of the new EEPROM support by running through the MotionSense examples. Loaded up the calibratesensors.ino and was able to calibrate the magnetometer no problem. Next thing I did was load up the printcalibration.ino and verified it against the MotionCal tool:
Code:
Calibration Data

Magnetic (Hard Iron) Offsets
   10.79 uT
  -12.91 uT
   65.15 uT

Magnetic Soft Iron Mapping
   0.9809  -0.0206  -0.0080
  -0.0206   1.0036  -0.0033
  -0.0080  -0.0033   1.0164

Expected Magnetic Field Strength
   40.74 uT

Accelerometer Offsets
   0.000 g
   0.000 g
   0.000 g

Gyroscope Offsets
   0.000 degrees/sec
   0.000 degrees/sec
   0.000 degrees/sec
The mag data matched exactly what I saw on the screen. Then of course I ran the NXPSensorFusion and it printed out orientation data no problem.

QUESTION: Is there a good test sketch for EEPROM to exercise it to see if there are problems?
 
Last edited:
QUESTION: Is there a good test sketch for EEPROM to exercise it to see if there are problems?

I'm not aware of anything that attempts to be a comprehensive test.

Testing get() and put() of items having unusual sizes, perhaps not ideally aligned to seemingly natural boundaries would be a good start. Maybe look for corner cases where differing data has been written 256, 1024, or 2048 times (likely forcing the wear leveling to fully fill and need to erase flash sectors). Testing writes near 1079 (the last emulated EEPROM address) might be a good idea.

I did a few dozen simple tests here, but didn't manage to check anything like all possible cases.
 
Back when I was hacking on the OpenCM version of EEPROM, I hacked up a simple test:
Code:
#define MAX_INDEX 64
uint8_t inc_value = 0;  // default to just index...
#include <EEPROM.h>
void setup() {
  // put your setup code here, to run once:
  while (!Serial) ;
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  Serial.println("EEPROM Test");

}

void loop() {
  int ch;
  // lets try writing to EEPROM
  uint16_t max_index = EEPROM.length();
  if (max_index > MAX_INDEX) max_index = MAX_INDEX;
  Serial.printf("EEPROM length: %d Max Test: %d Inc: %d\n", EEPROM.length(), max_index, inc_value);
  Serial.flush(); // Make sure the output is out
  uint32_t start_time = micros();
  for (uint16_t i = 0; i < max_index; i++) {
    EEPROM.write(i, (i+inc_value) & 0xff);
  }
  Serial.printf("Write time: %d\n", micros() - start_time);
  Serial.println("Start read back");
  start_time = micros();
  for (uint16_t i = 0; i < max_index; i++) {
    uint8_t b = EEPROM.read(i);
    if (b != ((i+inc_value) & 0xff)) {
      Serial.printf("Read mismatch: %d %x!=%x\n", i, b, (i+inc_value) & 0xff);
    }
  }
  Serial.printf("Read time: %d\n", micros() - start_time);

  uint8_t still_waiting = false;
  do {
    Serial.println("Press anykey to run again");
    still_waiting = false;
    while (!Serial.available()) {
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      delay(250);
    }
    if ((ch = Serial.read()) == 'i') inc_value++;    // setup so we can increment all values...
    if (ch == 'd') {
      dump_eeprom(); 
      still_waiting = true;
    }
    while (Serial.available()) {
      Serial.read();  // discard everything
    }
  } while (still_waiting);
}
#define ADDR_FLASH_PAGE_126   ((uint32_t)0x0801F800) /* Base @ of Page 126, 1 Kbytes */
#define ADDR_FLASH_PAGE_127   ((uint32_t)0x0801FC00) /* Base @ of Page 127, 1 Kbytes */

/* Define the size of the sectors to be used */
#define PAGE_SIZE               (uint32_t)FLASH_PAGE_SIZE  /* Page size */

/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS  ((uint32_t)ADDR_FLASH_PAGE_126) /* EEPROM emulation start address */

/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS    ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS     ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE0_ID               ADDR_FLASH_PAGE_126

#define PAGE1_BASE_ADDRESS    ((uint32_t)(EEPROM_START_ADDRESS + PAGE_SIZE))
#define PAGE1_END_ADDRESS     ((uint32_t)(EEPROM_START_ADDRESS + PAGE_SIZE + PAGE_SIZE - 1))
#define PAGE1_ID               ADDR_FLASH_PAGE_127
extern void  dumpMemory(uint32_t start_address, uint32_t end_address);

void dump_eeprom() {
  // Lets print out all of the Possible EEPROM values.
  uint16_t eeprom_size = EEPROM.length();
  Serial.printf("EEPROM length: %d\nContents:\n", eeprom_size);

  for (uint16_t i=0; i < eeprom_size; i++) {
    Serial.printf("%x:%x ", i, EEPROM.read(i));

    if ((i & 0xf) == 0xf) Serial.println();
  }
  Serial.println("\nRaw Data");

  // Now lets see if we can dump the 
  Serial.print("EEPROM Page 0 ");
  dumpMemory(PAGE0_BASE_ADDRESS, PAGE0_END_ADDRESS);

  Serial.print("EEPROM Page 1 ");
  dumpMemory(PAGE1_BASE_ADDRESS, PAGE1_END_ADDRESS);
}

void  dumpMemory(uint32_t start_address, uint32_t end_address) {
  __IO uint16_t *addr = (__IO uint16_t*)start_address;
  uint8_t count = 0; 
  
  bool dump_4byte_entries = true;  // assume 4 byte entries
  // Lets guess at state of page
  uint16_t page_type = *addr;
  switch (page_type) {
    case 0: // Valid, see if 2 byte or 4 byte
      if (addr[1] == 0x2) {
        Serial.println("Valid 2 byte entries");
        dump_4byte_entries = false;
      } else {
        Serial.println("Valid 4 byte entries");
      }
      break;
    case 0xffff: Serial.println("Empty"); break;
    case 0xeeee: Serial.println("Receive"); break;
    default: Serial.println("Unknown"); break; 
  }      
  if (dump_4byte_entries) {
    while (addr < (__IO uint16_t *)end_address) {
      Serial.printf("%x:%x ", addr[0], addr[1]);
      addr += 2;
      count++;
      if (count == 16) {
        Serial.println();
        count = 0;
      }
    }
  } else {
  uint16_t w;
    while (addr < (__IO uint16_t *)end_address) {
      w = *addr++;
      Serial.printf("%x:%x ", w & 0xff, w >> 8);
      count++;
      if (count == 16) {
        Serial.println();
        count = 0;
      }
    }
  }
  Serial.println();
}

But the dump code in this is very specific to the OpenCM code, wonder if if it makes sense to hack it up to try to do dumping of the raw EEPROM data to your new code base...
 
Re: EEPROM and NXPMotionSense

I compiled and ran example CalibrateSensors with propshield and T4B2 and I2C sensor data is properly streaming to /dev/ttyACM0. I then ran MotionCal visualizer (linux64) and selected port /dev/ttyACM0 and the sphere started filling in as i wiggled propshield. I then selected to write calibration data out, and console summarized data and presumably wrote to EEPROM (should it say it did or didn't write to EEPROM ?). I then ran example PrintCalibration and it just reports 0's, and my eeprom sketch still reports its unmodified data in bytes 0 to 99. So why didn't it write calibration data to EEPROM. (i retested my simple eeprom sketch, changing write values, and that works)

I repeated the sequence on a T3.2 with propshield and it updated the calibration data in the T3.2 EEPROM.
EDIT: i notice that i never get the fast LED flash on T4B2 indicating calibration data has been written, so it may be some problem with Serial.read() or ?? (but not EEPROM). I'm running 1.8.8 with 1.47beta2 plus the two new eeprom files. ??
just cloned latest Teensy cores from github ... T4B2 still not storing calibration data ??
FWIW, made sketch to write calibration bytes from T3.2+propshield to T4B2 EEPROM. PrintCalibration sketch "worked" on T4B2

EDIT2: ran MotionCal on Win10, at < 10% gaps it would let me save calibration data, and got fast LED blink, BUT no new calibration data written to EEPROM ?? Also tested with MACOS MotionCal, no fast LED blink on save and EEPROM data did not change??

I envy @mjs513's success with CalibrateSensors.
 
Last edited:
@PaulStoffregen, @KurtE and @....
Kurt, would be interesting to see what gets dumped in your example.

I just hacked up a sketch from a couple of the existing sketches to try and implement what Paul identified in post #3062, and ran it a few different ways but if anybody has any other changes have at it. The data below represents writing the data starting at 1044 and going to 1079:

Code:
EEPROM Length:1080
CRC32 of EEPROM data: 0xB90A6B13


Done!Iteration 0, CRC B90A6B13, Start Address 1044
Iteration 1, CRC B90A6B13, Start Address 1044
,,,,
Iteration 5093, CRC B90A6B13, Start Address 1044
Iteration 5094, CRC B90A6B13, Start Address 1044
Iteration 5095, CRC B90A6B13, Start Address 1044

If any one's interested and has recommendations to change the data structure let me know:
Code:
/***
    eeprom_put example.

    This shows how to use the EEPROM.put() method.
    Also, this sketch will pre-set the EEPROM data for the
    example sketch eeprom_get.

    Note, unlike the single byte version EEPROM.write(),
    the put method will use update semantics. As in a byte
    will only be written to the EEPROM if the data is actually
    different.

    Written by Christopher Andrews 2015
    Released under MIT licence.
***/

#include <EEPROM.h>

struct MyObject{
  float field1;
  byte field2;
  char name[10];
};

void setup(){

  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  for ( unsigned int i = 0 ; i < EEPROM.length() ; i++ )
    EEPROM.write(i, 0);

  Serial.print("EEPROM Length:");
  Serial.println(EEPROM.length());

  //Print the result of calling eeprom_crc()
  Serial.print( "CRC32 of EEPROM data: 0x" );
  Serial.println( eeprom_crc(), HEX );
  Serial.print( "\n\nDone!" );

  for(int i = 0; i < 5096; i++){
    float f = 123.456f;  //Variable to store in EEPROM.

    /** Put is designed for use with custom structures also. **/
    //Data to store.
    MyObject customVar = {
      3.14f,
      65,
      "Working!"
    };
    unsigned int startAddress =EEPROM.length() - sizeof(float)-sizeof(customVar)*2;
    unsigned int eeAddress = startAddress;   //Location we want the data to be put.
  
    //One simple call, with the address first and the object second.
    EEPROM.put( eeAddress, f );
  
    //Serial.println("\nWritten float data type!");
     
    //Serial.print("\nSize of Float: ");
    //Serial.println(sizeof(float));
    //Serial.print("\nSize of CustomVar: ");
    //Serial.println(sizeof(customVar));
    //Serial.println();
    eeAddress += sizeof(float); //Move address to the next byte after float 'f'.
  
    EEPROM.put( eeAddress, customVar );
    //Serial.print( "Written custom data type! \n\n" );
    //Serial.print( "Data data type!\n" );
  
    //Serial.println("Second write of Custom Var!");
    eeAddress += sizeof(customVar); //Move address to the next byte after float 'f'.
    EEPROM.put( eeAddress, customVar );
  
    f = 0.00f;   //Variable to store data read from EEPROM.
    eeAddress = 0; //EEPROM address to start reading from
    EEPROM.get( eeAddress, f );
    //Serial.println( EEPROM.get( eeAddress, f ) );
  
    secondTest(); //Run the next test.

    Serial.printf("Iteration %d, CRC %X, Start Address %d\n", i, eeprom_crc(), startAddress);
  }
  exit(0);
}

void loop(){ /* Empty loop */ }

void secondTest(){
  int eeAddress = sizeof(float); //Move address to the next byte after float 'f'.

  MyObject customVar; //Variable to store custom object read from EEPROM.
  EEPROM.get( eeAddress, customVar );

  //Serial.println( "Read custom object from EEPROM: " );
  //Serial.println( customVar.field1 );
  //Serial.println( customVar.field2 );
  //Serial.println( customVar.name );

  EEPROM.get( eeAddress, customVar );

  //Serial.println( "Read custom object from EEPROM: " );
  //Serial.println( customVar.field1 );
  //Serial.println( customVar.field2 );
  //Serial.println( customVar.name );
}

unsigned long eeprom_crc( void ){

  const unsigned long crc_table[16] = {
      0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
      0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
      0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
      0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
  };

  unsigned long crc = ~0L;

  for( unsigned int index = 0 ; index < EEPROM.length()  ; ++index ){
    crc = crc_table[( crc ^ EEPROM[index] ) & 0x0f] ^ (crc >> 4);
    crc = crc_table[( crc ^ ( EEPROM[index] >> 4 )) & 0x0f] ^ (crc >> 4);
    crc = ~crc;
  }
  return crc;
}
 
Good news! I'm just home a few before another quick trip out. My phone shows a shipping label created too :)

Just to confirm the EEPROM Will work the same across all the T4 Beta units? So an abusive test on a T4B1 { with 1052 MCU } would relate directly to the released T4 with 1062 correct?

Since I wrote some Prime# code looking to fill Disk files for testable data - I'll do the same with batches of 256 Prime numbers to write and read in some fashion - perhaps inverting them in each pass knowing all the bits get equally (ab)used ...
 
Paul_ this thread: Counting-backwards-from-100-to-0-stops-at-55

I used T_3.6 - that thread user was with T_3.2:: Has a sketch where too fast printing was breaking - I posted a sketch I thought would fix it using if ( Serial.availableForWrite() > 20 )

And it didn't stop the problem. I'm not sure if this relates to your issue with USB and offers a repro case?

Code:
// https://forum.pjrc.com/threads/56412-Counting-backwards-from-100-to-0-stops-at-55?p=207200&viewfull=1#post207200
void setup() {
	pinMode(LED_BUILTIN, OUTPUT);
	while (!Serial);
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	int index = 0;
	while ( index < 1000001)
	{
		if ( Serial.availableForWrite() > 20 ) {
			if ( !(index % 20) ) Serial.print (" X >>>>");
			Serial.print ("index: ");
			Serial.println (index);
			index++;
			if ( !(index % 1000) ) digitalWriteFast( LED_BUILTIN, !digitalReadFast( LED_BUILTIN ) );
		}
		else {
			delayMicroseconds(20); yield(); // Also dies with (200) perhaps 100K, and (2000) was still  going at 500K
		}
	}

	while (1)
		digitalWriteFast( LED_BUILTIN, !digitalReadFast( LED_BUILTIN ) );
}

void loop() {
}
 
Just to confirm the EEPROM Will work the same across all the T4 Beta units?

I tested only on the latest 1062 (with brown out restart fix). It should work the same on all the 1062 boards.

It isn't supposed to ever attempt writing to the top 4K, where the restore image is located. I also tested on the latest 1062 (with brown out restart fix) an intentional change to attempt erasing the top 4K. The flash chip's hardware lock does prevent erase of the restore image. I did not test with the older boards not having the brown out issue, but I suspect the restore image isn't protected on those. They were written before I had the text fixture built.

I did not do any testing on 1052. I'm not planning to test anything on 1052. Only 19 of those boards exist "in the wild". No more will ever be made.

Paul_ this thread: .....

Replied on that thread, and added to my list of issues to investigate.

Ran it on T4. Doesn't print anything, because we don't have Serial.availableForWrite() yet. But if I change that line to always evaluate true, it does print all 1 million lines in just a couple seconds.
 
I tested only on the latest 1062 (with brown out restart fix). It should work the same on all the 1062 boards.

It isn't supposed to ever attempt writing to the top 4K, where the restore image is located. I also tested on the latest 1062 (with brown out restart fix) an intentional change to attempt erasing the top 4K. The flash chip's hardware lock does prevent erase of the restore image. I did not test with the older boards not having the brown out issue, but I suspect the restore image isn't protected on those. They were written before I had the text fixture built.

I did not do any testing on 1052. I'm not planning to test anything on 1052. Only 19 of those boards exist "in the wild". No more will ever be made.



Replied on that thread, and added to my list of issues to investigate.

Ran it on T4. Doesn't print anything, because we don't have Serial.availableForWrite() yet. But if I change that line to always evaluate true, it does print all 1 million lines in just a couple seconds.

Just wondered about expectation for it to work on 1052 beta's as a valid test - in case that accidentally ran to destruction I wouldn't lose anything supported going forward.

I just saw your post on that thread - I see my code above is indeed missing this code line in my editor version :: if ( 1) { // Serial.availableForWrite() > 20 ) {
When I ran that it didn't always finish to completion the last lines from last run shows this - and similar on last 3 restarts:
Code:
…
index: 863279
 X >>>>index: 863280
index: 863281
index: 863282
index: 863283
index: 863284
index: 8
But that may be a computer/SerMon variance.

Note : I put that 'X >>' in there to aid in watching the scroll speed on T_3.6 - and it doesn't help on T4 'firehose' USB with page draws.
 
manitou said:
EDIT2: ran MotionCal on Win10, at < 10% gaps it would let me save calibration data, and got fast LED blink, BUT no new calibration data written to EEPROM ??
To be honest I tested with the T4 with the white wire on top of the board. I will give it a shot with the other one.

Ok. Ran it on the T4b2m. It did say the data to the EEPROM but this time when I printed the data back I noticed some differences in the data from the cal to the eeprom, Admittedly its in the 3rd/4th decimal but enough that I don't think its round off error:
T42mCal.JPG
 
I then ran MotionCal visualizer (linux64) and selected port /dev/ttyACM0 and the sphere started filling in as i wiggled propshield. I then selected to write calibration data out, and console summarized data and presumably wrote to EEPROM (should it say it did or didn't write to EEPROM ?

Yes. The "Status" empty circle is supposed to turn into a green check mark when the calibration software hears a confirmation that the cal data was stored correctly.

I ran it just now with a T4. Here's a screenshot with the green check mark.

sc.png

Any chance you might have edits in your copy of NXPMotionSense? Maybe previously deleting the EEPROM stuff so it would compile for T4?
 
PING LIBRARY

Ok. I picked up a new Parallax PING sensor (3 pin) to test the PING library. It compiled and ran without issue. Seemed to give me decent results - I did run it off 3.3v so probably a little degradation.
Code:
Microseconds: 280 | Inches 1.89 | Centimeters: 4.83
Microseconds: 0 | Inches 0.00 | Centimeters: 0.00
Microseconds: 280 | Inches 1.89 | Centimeters: 4.83
Microseconds: 0 | Inches 0.00 | Centimeters: 0.00
Microseconds: 280 | Inches 1.89 | Centimeters: 4.83

Which is about the same as I get if I ran it on a T3.5
Code:
Microseconds: 166 | Inches 1.12 | Centimeters: 2.86
Microseconds: 0 | Inches 0.00 | Centimeters: 0.00
Microseconds: 178 | Inches 1.20 | Centimeters: 3.07
Microseconds: 0 | Inches 0.00 | Centimeters: 0.00
Microseconds: 190 | Inches 1.28 | Centimeters: 3.28

EDIT: I updated Post #4 for NXPMotionSense and PING Libraries
 
Yes. The "Status" empty circle is supposed to turn into a green check mark when the calibration software hears a confirmation that the cal data was stored correctly.

Any chance you might have edits in your copy of NXPMotionSense? Maybe previously deleting the EEPROM stuff so it would compile for T4?

I did pick up new MotionCal executables yesterday, and those provided the green check when I tested prop shield with T3.2 or T3.6. But T4B2 when sending calibration will do a fast flash of the LED but does not turn on the green check icon (and the EEPROM has not been updated). I don't know of any mods I made to NXPMotionSense lib but I just now re-cloned NXPMotionSense, but T4B2 still won't update EEPROM. As noted above, I can run a stand-alone sketch that writes calibration values into the T4B2 EEPROM
 
I did pick up new MotionCal executables yesterday, and those provided the green check when I tested prop shield with T3.2 or T3.6. But T4B2 when sending calibration will do a fast flash of the LED but does not turn on the green check icon (and the EEPROM has not been updated). I don't know of any mods I made to NXPMotionSense lib but I just now re-cloned NXPMotionSense, but T4B2 still won't update EEPROM. As noted above, I can run a stand-alone sketch that writes calibration values into the T4B2 EEPROM

That's strange. When I downloaded the EEPROM updates I updated the entire core at the same time. I am running Arduino 1.8.9 with Teensyduino 1.47 beta2 and the latest Teensy 4 core on a Win10x64 PC just for reference. The latest core has all the USB updates as well. Maybe that is whats needed?
 
@mjrc - looks good, I probably should go through my parts bins (for some more parts like this to test ;) :D)

@Paul... I played around some with the EEPROM test program I did to help debug the EEPROM on OpenCM9.04 board... Made it compatible with the new T4 stuff (I think) and then ran it a few times to see how the 15 pages are used and fill...
Code:
#define MAX_INDEX 256
uint8_t inc_value = 0;  // default to just index...
#include <EEPROM.h>
void setup() {
  // put your setup code here, to run once:
  while (!Serial) ;
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  Serial.println("EEPROM Test");

}

void loop() {
  int ch;
  // lets try writing to EEPROM
  uint16_t max_index = EEPROM.length();
  if (max_index > MAX_INDEX) max_index = MAX_INDEX;
  Serial.printf("EEPROM length: %d Max Test: %d Inc: %d\n", EEPROM.length(), max_index, inc_value);
  Serial.flush(); // Make sure the output is out
  uint32_t start_time = micros();
  for (uint16_t i = 0; i < max_index; i++) {
    EEPROM.write(i, (i + inc_value) & 0xff);
  }
  Serial.printf("Write time: %d\n", micros() - start_time);
  Serial.println("Start read back");
  start_time = micros();
  for (uint16_t i = 0; i < max_index; i++) {
    uint8_t b = EEPROM.read(i);
    if (b != ((i + inc_value) & 0xff)) {
      Serial.printf("Read mismatch: %d %x!=%x\n", i, b, (i + inc_value) & 0xff);
    }
  }
  Serial.printf("Read time: %d\n", micros() - start_time);

  uint8_t still_waiting = false;
  do {
    Serial.println("Press anykey to run again");
    still_waiting = false;
    while (!Serial.available()) {
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      delay(250);
    }
    if ((ch = Serial.read()) == 'i') inc_value++;    // setup so we can increment all values...
    if (ch == 'd') {
      dump_eeprom();
      still_waiting = true;
    }
    while (Serial.available()) {
      Serial.read();  // discard everything
    }
  } while (still_waiting);
}
/* EEPROM start address in Flash */

#define FLASH_BASEADDR 0x601F0000
#define FLASH_SECTORS  15

void dump_eeprom() {
  // Lets print out all of the Possible EEPROM values.
  uint16_t eeprom_size = EEPROM.length();
  Serial.printf("EEPROM length: %d\nContents:\n", eeprom_size);

  for (uint16_t i = 0; i < eeprom_size; i++) {
    Serial.printf("%x:%x ", i, EEPROM.read(i));

    if ((i & 0xf) == 0xf) Serial.println();
  }
  Serial.println("\nRaw Data");

  // Now lets see if we can dump the flash sectors:
  uint32_t sector;
  for (sector = 0; sector < FLASH_SECTORS; sector++) {
    const uint16_t *p_start = (uint16_t *)(FLASH_BASEADDR + sector * 4096);
    const uint16_t *p_end = (uint16_t *)(FLASH_BASEADDR + (sector + 1) * 4096);
    uint16_t *p;
    for (p = p_start; p < p_end; p++) {
      if (*p++ == 0xFFFF) break;
    }
    Serial.printf("EEprom Page:%d addresses(%lx - %lx): first free: %x\n", sector, p_start, p_end, p);

    uint8_t count = 0;
    for (p = p_start; p < p_end; p++) {
      if (*p == 0xFFFF) break;
      uint8_t value_offset = *p & 0xff;
      uint8_t value = *p >> 8;
      uint16_t eeprom_index = (((value_offset >> 2)*FLASH_SECTORS) << 2) + (sector << 2) + (value_offset & 0x3);
      Serial.printf(" %03d(%02x)=%02x", eeprom_index, value_offset, value);
      count++;
      if (count == 16) {
        Serial.println();
        count = 0;
      }
    }
    Serial.println();
  }
}
Some of the output:
Code:
EEPROM Test
EEPROM length: 1080 Max Test: 256 Inc: 0
Write time: 317
Start read back
Read time: 309
Press anykey to run again
EEPROM length: 1080
Contents:
0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 8:8 9:9 a:a b:b c:c d:d e:e f:f 
10:10 11:11 12:12 13:13 14:14 15:15 16:16 17:17 18:18 19:19 1a:1a 1b:1b 1c:1c 1d:1d 1e:1e 1f:1f 
20:20 21:21 22:22 23:23 24:24 25:25 26:26 27:27 28:28 29:29 2a:2a 2b:2b 2c:2c 2d:2d 2e:2e 2f:2f 
30:30 31:31 32:32 33:33 34:34 35:35 36:36 37:37 38:38 39:39 3a:3a 3b:3b 3c:3c 3d:3d 3e:3e 3f:3f 
40:40 41:41 42:42 43:43 44:44 45:45 46:46 47:47 48:48 49:49 4a:4a 4b:4b 4c:4c 4d:4d 4e:4e 4f:4f 
50:50 51:51 52:52 53:53 54:54 55:55 56:56 57:57 58:58 59:59 5a:5a 5b:5b 5c:5c 5d:5d 5e:5e 5f:5f 
60:60 61:61 62:62 63:63 64:64 65:65 66:66 67:67 68:68 69:69 6a:6a 6b:6b 6c:6c 6d:6d 6e:6e 6f:6f 
70:70 71:71 72:72 73:73 74:74 75:75 76:76 77:77 78:78 79:79 7a:7a 7b:7b 7c:7c 7d:7d 7e:7e 7f:7f 
80:80 81:81 82:82 83:83 84:84 85:85 86:86 87:87 88:88 89:89 8a:8a 8b:8b 8c:8c 8d:8d 8e:8e 8f:8f 
90:90 91:91 92:92 93:93 94:94 95:95 96:96 97:97 98:98 99:99 9a:9a 9b:9b 9c:9c 9d:9d 9e:9e 9f:9f 
a0:a0 a1:a1 a2:a2 a3:a3 a4:a4 a5:a5 a6:a6 a7:a7 a8:a8 a9:a9 aa:aa ab:ab ac:ac ad:ad ae:ae af:af 
b0:b0 b1:b1 b2:b2 b3:b3 b4:b4 b5:b5 b6:b6 b7:b7 b8:b8 b9:b9 ba:ba bb:bb bc:bc bd:bd be:be bf:bf 
c0:c0 c1:c1 c2:c2 c3:c3 c4:c4 c5:c5 c6:c6 c7:c7 c8:c8 c9:c9 ca:ca cb:cb cc:cc cd:cd ce:ce cf:cf 
d0:d0 d1:d1 d2:d2 d3:d3 d4:d4 d5:d5 d6:d6 d7:d7 d8:d8 d9:d9 da:da db:db dc:dc dd:dd de:de df:df 
e0:e0 e1:e1 e2:e2 e3:e3 e4:e4 e5:e5 e6:e6 e7:e7 e8:e8 e9:e9 ea:ea eb:eb ec:ec ed:ed ee:ee ef:ef 
f0:f0 f1:f1 f2:f2 f3:f3 f4:f4 f5:f5 f6:f6 f7:f7 f8:f8 f9:f9 fa:fa fb:fb fc:fc fd:fd fe:fe ff:ff 
100:ff 101:ff 102:ff 103:ff 104:ff 105:ff 106:ff 107:ff 108:ff 109:ff 10a:ff 10b:ff 10c:ff 10d:ff 10e:ff 10f:ff 
110:ff 111:ff 112:ff 113:ff 114:ff 115:ff 116:ff 117:ff 118:ff 119:ff 11a:ff 11b:ff 11c:ff 11d:ff 11e:ff 11f:ff 
120:ff 121:ff 122:ff 123:ff 124:ff 125:ff 126:ff 127:ff 128:ff 129:ff 12a:ff 12b:ff 12c:ff 12d:ff 12e:ff 12f:ff 
130:ff 131:ff 132:ff 133:ff 134:ff 135:ff 136:ff 137:ff 138:ff 139:ff 13a:ff 13b:ff 13c:ff 13d:ff 13e:ff 13f:ff 
140:ff 141:ff 142:ff 143:ff 144:ff 145:ff 146:ff 147:ff 148:ff 149:ff 14a:ff 14b:ff 14c:ff 14d:ff 14e:ff 14f:ff 
150:ff 151:ff 152:ff 153:ff 154:ff 155:ff 156:ff 157:ff 158:ff 159:ff 15a:ff 15b:ff 15c:ff 15d:ff 15e:ff 15f:ff 
160:ff 161:ff 162:ff 163:ff 164:ff 165:ff 166:ff 167:ff 168:ff 169:ff 16a:ff 16b:ff 16c:ff 16d:ff 16e:ff 16f:ff 
170:ff 171:ff 172:ff 173:ff 174:ff 175:ff 176:ff 177:ff 178:ff 179:ff 17a:ff 17b:ff 17c:ff 17d:ff 17e:ff 17f:ff 
180:ff 181:ff 182:ff 183:ff 184:ff 185:ff 186:ff 187:ff 188:ff 189:ff 18a:ff 18b:ff 18c:ff 18d:ff 18e:ff 18f:ff 
190:ff 191:ff 192:ff 193:ff 194:ff 195:ff 196:ff 197:ff 198:ff 199:ff 19a:ff 19b:ff 19c:ff 19d:ff 19e:ff 19f:ff 
1a0:ff 1a1:ff 1a2:ff 1a3:ff 1a4:ff 1a5:ff 1a6:ff 1a7:ff 1a8:ff 1a9:ff 1aa:ff 1ab:ff 1ac:ff 1ad:ff 1ae:ff 1af:ff 
1b0:ff 1b1:ff 1b2:ff 1b3:ff 1b4:ff 1b5:ff 1b6:ff 1b7:ff 1b8:ff 1b9:ff 1ba:ff 1bb:ff 1bc:ff 1bd:ff 1be:ff 1bf:ff 
1c0:ff 1c1:ff 1c2:ff 1c3:ff 1c4:ff 1c5:ff 1c6:ff 1c7:ff 1c8:ff 1c9:ff 1ca:ff 1cb:ff 1cc:ff 1cd:ff 1ce:ff 1cf:ff 
1d0:ff 1d1:ff 1d2:ff 1d3:ff 1d4:ff 1d5:ff 1d6:ff 1d7:ff 1d8:ff 1d9:ff 1da:ff 1db:ff 1dc:ff 1dd:ff 1de:ff 1df:ff 
1e0:ff 1e1:ff 1e2:ff 1e3:ff 1e4:ff 1e5:ff 1e6:ff 1e7:ff 1e8:ff 1e9:ff 1ea:ff 1eb:ff 1ec:ff 1ed:ff 1ee:ff 1ef:ff 
1f0:ff 1f1:ff 1f2:ff 1f3:ff 1f4:ff 1f5:ff 1f6:ff 1f7:ff 1f8:ff 1f9:ff 1fa:ff 1fb:ff 1fc:ff 1fd:ff 1fe:ff 1ff:ff 
200:ff 201:ff 202:ff 203:ff 204:ff 205:ff 206:ff 207:ff 208:ff 209:ff 20a:ff 20b:ff 20c:ff 20d:ff 20e:ff 20f:ff 
210:ff 211:ff 212:ff 213:ff 214:ff 215:ff 216:ff 217:ff 218:ff 219:ff 21a:ff 21b:ff 21c:ff 21d:ff 21e:ff 21f:ff 
220:ff 221:ff 222:ff 223:ff 224:ff 225:ff 226:ff 227:ff 228:ff 229:ff 22a:ff 22b:ff 22c:ff 22d:ff 22e:ff 22f:ff 
230:ff 231:ff 232:ff 233:ff 234:ff 235:ff 236:ff 237:ff 238:ff 239:ff 23a:ff 23b:ff 23c:ff 23d:ff 23e:ff 23f:ff 
240:ff 241:ff 242:ff 243:ff 244:ff 245:ff 246:ff 247:ff 248:ff 249:ff 24a:ff 24b:ff 24c:ff 24d:ff 24e:ff 24f:ff 
250:ff 251:ff 252:ff 253:ff 254:ff 255:ff 256:ff 257:ff 258:ff 259:ff 25a:ff 25b:ff 25c:ff 25d:ff 25e:ff 25f:ff 
260:ff 261:ff 262:ff 263:ff 264:ff 265:ff 266:ff 267:ff 268:ff 269:ff 26a:ff 26b:ff 26c:ff 26d:ff 26e:ff 26f:ff 
270:ff 271:ff 272:ff 273:ff 274:ff 275:ff 276:ff 277:ff 278:ff 279:ff 27a:ff 27b:ff 27c:ff 27d:ff 27e:ff 27f:ff 
280:ff 281:ff 282:ff 283:ff 284:ff 285:ff 286:ff 287:ff 288:ff 289:ff 28a:ff 28b:ff 28c:ff 28d:ff 28e:ff 28f:ff 
290:ff 291:ff 292:ff 293:ff 294:ff 295:ff 296:ff 297:ff 298:ff 299:ff 29a:ff 29b:ff 29c:ff 29d:ff 29e:ff 29f:ff 
2a0:ff 2a1:ff 2a2:ff 2a3:ff 2a4:ff 2a5:ff 2a6:ff 2a7:ff 2a8:ff 2a9:ff 2aa:ff 2ab:ff 2ac:ff 2ad:ff 2ae:ff 2af:ff 
2b0:ff 2b1:ff 2b2:ff 2b3:ff 2b4:ff 2b5:ff 2b6:ff 2b7:ff 2b8:ff 2b9:ff 2ba:ff 2bb:ff 2bc:ff 2bd:ff 2be:ff 2bf:ff 
2c0:ff 2c1:ff 2c2:ff 2c3:ff 2c4:ff 2c5:ff 2c6:ff 2c7:ff 2c8:ff 2c9:ff 2ca:ff 2cb:ff 2cc:ff 2cd:ff 2ce:ff 2cf:ff 
2d0:ff 2d1:ff 2d2:ff 2d3:ff 2d4:ff 2d5:ff 2d6:ff 2d7:ff 2d8:ff 2d9:ff 2da:ff 2db:ff 2dc:ff 2dd:ff 2de:ff 2df:ff 
2e0:ff 2e1:ff 2e2:ff 2e3:ff 2e4:ff 2e5:ff 2e6:ff 2e7:ff 2e8:ff 2e9:ff 2ea:ff 2eb:ff 2ec:ff 2ed:ff 2ee:ff 2ef:ff 
2f0:ff 2f1:ff 2f2:ff 2f3:ff 2f4:ff 2f5:ff 2f6:ff 2f7:ff 2f8:ff 2f9:ff 2fa:ff 2fb:ff 2fc:ff 2fd:ff 2fe:ff 2ff:ff 
300:ff 301:ff 302:ff 303:ff 304:ff 305:ff 306:ff 307:ff 308:ff 309:ff 30a:ff 30b:ff 30c:ff 30d:ff 30e:ff 30f:ff 
310:ff 311:ff 312:ff 313:ff 314:ff 315:ff 316:ff 317:ff 318:ff 319:ff 31a:ff 31b:ff 31c:ff 31d:ff 31e:ff 31f:ff 
320:ff 321:ff 322:ff 323:ff 324:ff 325:ff 326:ff 327:ff 328:ff 329:ff 32a:ff 32b:ff 32c:ff 32d:ff 32e:ff 32f:ff 
330:ff 331:ff 332:ff 333:ff 334:ff 335:ff 336:ff 337:ff 338:ff 339:ff 33a:ff 33b:ff 33c:ff 33d:ff 33e:ff 33f:ff 
340:ff 341:ff 342:ff 343:ff 344:ff 345:ff 346:ff 347:ff 348:ff 349:ff 34a:ff 34b:ff 34c:ff 34d:ff 34e:ff 34f:ff 
350:ff 351:ff 352:ff 353:ff 354:ff 355:ff 356:ff 357:ff 358:ff 359:ff 35a:ff 35b:ff 35c:ff 35d:ff 35e:ff 35f:ff 
360:ff 361:ff 362:ff 363:ff 364:ff 365:ff 366:ff 367:ff 368:ff 369:ff 36a:ff 36b:ff 36c:ff 36d:ff 36e:ff 36f:ff 
370:ff 371:ff 372:ff 373:ff 374:ff 375:ff 376:ff 377:ff 378:ff 379:ff 37a:ff 37b:ff 37c:ff 37d:ff 37e:ff 37f:ff 
380:ff 381:ff 382:ff 383:ff 384:ff 385:ff 386:ff 387:ff 388:ff 389:ff 38a:ff 38b:ff 38c:ff 38d:ff 38e:ff 38f:ff 
390:ff 391:ff 392:ff 393:ff 394:ff 395:ff 396:ff 397:ff 398:ff 399:ff 39a:ff 39b:ff 39c:ff 39d:ff 39e:ff 39f:ff 
3a0:ff 3a1:ff 3a2:ff 3a3:ff 3a4:ff 3a5:ff 3a6:ff 3a7:ff 3a8:ff 3a9:ff 3aa:ff 3ab:ff 3ac:ff 3ad:ff 3ae:ff 3af:ff 
3b0:ff 3b1:ff 3b2:ff 3b3:ff 3b4:ff 3b5:ff 3b6:ff 3b7:ff 3b8:ff 3b9:ff 3ba:ff 3bb:ff 3bc:ff 3bd:ff 3be:ff 3bf:ff 
3c0:ff 3c1:ff 3c2:ff 3c3:ff 3c4:ff 3c5:ff 3c6:ff 3c7:ff 3c8:ff 3c9:ff 3ca:ff 3cb:ff 3cc:ff 3cd:ff 3ce:ff 3cf:ff 
3d0:ff 3d1:ff 3d2:ff 3d3:ff 3d4:ff 3d5:ff 3d6:ff 3d7:ff 3d8:ff 3d9:ff 3da:ff 3db:ff 3dc:ff 3dd:ff 3de:ff 3df:ff 
3e0:ff 3e1:ff 3e2:ff 3e3:ff 3e4:ff 3e5:ff 3e6:ff 3e7:ff 3e8:ff 3e9:ff 3ea:ff 3eb:ff 3ec:ff 3ed:ff 3ee:ff 3ef:ff 
3f0:ff 3f1:ff 3f2:ff 3f3:ff 3f4:ff 3f5:ff 3f6:ff 3f7:ff 3f8:ff 3f9:ff 3fa:ff 3fb:ff 3fc:ff 3fd:ff 3fe:ff 3ff:ff 
400:ff 401:ff 402:ff 403:ff 404:ff 405:ff 406:ff 407:ff 408:ff 409:ff 40a:ff 40b:ff 40c:ff 40d:ff 40e:ff 40f:ff 
410:ff 411:ff 412:ff 413:ff 414:ff 415:ff 416:ff 417:ff 418:ff 419:ff 41a:ff 41b:ff 41c:ff 41d:ff 41e:ff 41f:ff 
420:ff 421:ff 422:ff 423:ff 424:ff 425:ff 426:ff 427:ff 428:ff 429:ff 42a:ff 42b:ff 42c:ff 42d:ff 42e:ff 42f:ff 
430:ff 431:ff 432:ff 433:ff 434:ff 435:ff 436:ff 437:ff 
Raw Data
EEprom Page:0 addresses(601f0000 - 601f1000): first free: 601f00ca
 000(00)=00 001(01)=01 002(02)=02 003(03)=03 060(04)=3c 061(05)=3d 062(06)=3e 063(07)=3f 120(08)=78 121(09)=79 122(0a)=7a 123(0b)=7b 180(0c)=b4 181(0d)=b5 182(0e)=b6 183(0f)=b7
 240(10)=f0 241(11)=f1 242(12)=f2 243(13)=f3 000(00)=01 001(01)=02 002(02)=03 003(03)=04 060(04)=3d 061(05)=3e 062(06)=3f 063(07)=40 120(08)=79 121(09)=7a 122(0a)=7b 123(0b)=7c
 180(0c)=b5 181(0d)=b6 182(0e)=b7 183(0f)=b8 240(10)=f1 241(11)=f2 242(12)=f3 243(13)=f4 000(00)=00 001(01)=01 002(02)=02 003(03)=03 060(04)=3c 061(05)=3d 062(06)=3e 063(07)=3f
 120(08)=78 121(09)=79 122(0a)=7a 123(0b)=7b 180(0c)=b4 181(0d)=b5 182(0e)=b6 183(0f)=b7 240(10)=f0 241(11)=f1 242(12)=f2 243(13)=f3 000(00)=01 001(01)=02 002(02)=03 003(03)=04
 060(04)=3d 061(05)=3e 062(06)=3f 063(07)=40 120(08)=79 121(09)=7a 122(0a)=7b 123(0b)=7c 180(0c)=b5 181(0d)=b6 182(0e)=b7 183(0f)=b8 240(10)=f1 241(11)=f2 242(12)=f3 243(13)=f4
 000(00)=00 001(01)=01 002(02)=02 003(03)=03 060(04)=3c 061(05)=3d 062(06)=3e 063(07)=3f 120(08)=78 121(09)=79 122(0a)=7a 123(0b)=7b 180(0c)=b4 181(0d)=b5 182(0e)=b6 183(0f)=b7
 240(10)=f0 241(11)=f1 242(12)=f2 243(13)=f3
EEprom Page:1 addresses(601f1000 - 601f2000): first free: 601f10ca
 004(00)=04 005(01)=05 006(02)=06 007(03)=07 064(04)=40 065(05)=41 066(06)=42 067(07)=43 124(08)=7c 125(09)=7d 126(0a)=7e 127(0b)=7f 184(0c)=b8 185(0d)=b9 186(0e)=ba 187(0f)=bb
 244(10)=f4 245(11)=f5 246(12)=f6 247(13)=f7 004(00)=05 005(01)=06 006(02)=07 007(03)=08 064(04)=41 065(05)=42 066(06)=43 067(07)=44 124(08)=7d 125(09)=7e 126(0a)=7f 127(0b)=80
 184(0c)=b9 185(0d)=ba 186(0e)=bb 187(0f)=bc 244(10)=f5 245(11)=f6 246(12)=f7 247(13)=f8 004(00)=04 005(01)=05 006(02)=06 007(03)=07 064(04)=40 065(05)=41 066(06)=42 067(07)=43
 124(08)=7c 125(09)=7d 126(0a)=7e 127(0b)=7f 184(0c)=b8 185(0d)=b9 186(0e)=ba 187(0f)=bb 244(10)=f4 245(11)=f5 246(12)=f6 247(13)=f7 004(00)=05 005(01)=06 006(02)=07 007(03)=08
 064(04)=41 065(05)=42 066(06)=43 067(07)=44 124(08)=7d 125(09)=7e 126(0a)=7f 127(0b)=80 184(0c)=b9 185(0d)=ba 186(0e)=bb 187(0f)=bc 244(10)=f5 245(11)=f6 246(12)=f7 247(13)=f8
 004(00)=04 005(01)=05 006(02)=06 007(03)=07 064(04)=40 065(05)=41 066(06)=42 067(07)=43 124(08)=7c 125(09)=7d 126(0a)=7e 127(0b)=7f 184(0c)=b8 185(0d)=b9 186(0e)=ba 187(0f)=bb
 244(10)=f4 245(11)=f5 246(12)=f6 247(13)=f7
...

The code is real basic. It simply counts up and sets each memory
Code:
for (uint16_t i = 0; i < max_index; i++) {
    EEPROM.write(i, (i + inc_value) & 0xff);
  }
where at startup inc_value = 0

if you type in i<cr>
It will increment the value by 1 and run again.
if you type in d<cr> it does the dump which I show part of here...

I may play some with another command that maybe writes many values to a group of 4 areas, like 0-3, to fill the page and see it then compress out the data...

Again not sure how useful this is!
 
Status
Not open for further replies.
Back
Top