Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 17 of 17

Thread: Teensy 4.x Battery backed Non volitile memory

  1. #1
    Junior Member
    Join Date
    Jul 2017
    Posts
    4

    Teensy 4.x Battery backed Non volitile memory

    Howdy, all! This is as much of a check your work post as any. I was reading a previous post: https://forum.pjrc.com/threads/45854...-backed-NV-RAM
    regarding using the extra 28 free bytes of the 32 bytes of non-volatile battery-backed memory for run data I would like to keep after a restart.
    I would like to implement a solution like this on the 4.x boards but looking at the memory map at: https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf page: 1242 it appears that the battery-backed memory only allows for 4 bytes starting at address 0x400D4100. Firstly, am I even reading this correctly or even looking at the correct section of memory? I could not find in the teensy 4core where the RTC is even storing the time. I'm a little out of my pay grade here but trying to fake it as best I can.

    Aside: This data I wish to keep is only 12 bytes but is very volatile so EPROM isn't really suitable.

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    There is NVRAM on the 1062 in the T_4.x's. There are 4 32 bit words.

    Below is code that shows it in use from Feb 2021 - discussed in a post somewhere ...
    > It did some things to show function including a 'r' Reset that picks up with the values intact.
    > Added a 't' command it seems that isn't in the startup printing: Looks to give some idea of the timing of doing multiple writes

    Key thing was finding this :: SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
    Frank B found that poking bits that were not documented in the manual IIRC.
    Code:
    // TWO ways to access as 4 32 bit DWORDS of NVRAM backed data
    // #define NVRAM_UINT32 ((uint32_t *)0x400D4100)
    uint32_t *NVRAM_UINT32 ((uint32_t *)0x400D4100);
    uint32_t tRam[4];
    
    elapsedMillis tSome;
    void setup() {
    	SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
    	Serial.begin(115200);
    	while (!Serial && millis() < 4000 );
    	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
    	tSome = 0;
    	Serial.print(" 'r':RESTART 'z':ZERO '?':INC 100 to each\n");
    	for ( int ii = 0; ii < 4; ii++ ) {
    		tRam[ii] = 0;
    	}
    }
    
    #define SNVS_LPGPR0      (IMXRT_SNVS.offset100)
    #define SNVS_LPGPR1      (IMXRT_SNVS.offset104)
    #define SNVS_LPGPR2      (IMXRT_SNVS.offset108)
    #define SNVS_LPGPR3      (IMXRT_SNVS.offset10C)
    
    void loop() {
    	if ( tSome >= 1000 ) {
    		tSome -= 1000;
    		for ( int ii = 0; ii < 4; ii++ ) {
    			Serial.print( '\t' );
    			Serial.print( NVRAM_UINT32[ii] );
    		}
    		Serial.println();
    		for ( int ii = 0; ii < 4; ii++ ) {
    			NVRAM_UINT32[ii]++;
    		}
    	}
    	while ( Serial.available() ) {
    		char cc = Serial.read();
    		if ( 'r' == cc ) {
    			SCB_AIRCR = 0x5FA0004;
    			while (true);
    		}
    		else if ( 'z' == cc ) {
    			Serial.print( "_ZERO_ NVRAM\n" );
    			for ( int ii = 0; ii < 4; ii++ ) {
    				NVRAM_UINT32[ii] = ii * 1000;
    			}
    		}
    		else if ( 't' == cc ) {
    			Serial.print( "_Add_1K_ NVRAM\n" );
    			uint32_t tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < 4; ii++ ) {
    					NVRAM_UINT32[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 RTC DWORDS\n", tt );
    
    			tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				SNVS_LPGPR0 += 1;
    				SNVS_LPGPR1 += 2;
    				SNVS_LPGPR2 += 3;
    				SNVS_LPGPR3 += 4;
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 RTC #define DWORDS\n", tt );
    
    			tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < 4; ii++ ) {
    					tRam[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t\t for 1K increments of 4 RAM DWORDS\n", tt );
    		}
    		else {
    			Serial.print( "_10_INC_ NVRAM\n" );
    			tSome += 10000;
    		}
    	}
    }

  3. #3
    Junior Member
    Join Date
    Jul 2017
    Posts
    4
    This is great! Sorry for the redundancy if I missed a previous post. I will experiment with your code this evening. Cheers!

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Quote Originally Posted by Mupeg View Post
    This is great! Sorry for the redundancy if I missed a previous post. I will experiment with your code this evening. Cheers!
    Hope it helps - post didn't pop up for me either so resorted to finding the sample sketch. One sketch does link to April 2019 - but that was before the Enable was found to make it usable.

  5. #5
    Junior Member
    Join Date
    Jul 2017
    Posts
    4
    Quote Originally Posted by defragster View Post
    Hope it helps - post didn't pop up for me either so resorted to finding the sample sketch. One sketch does link to April 2019 - but that was before the Enable was found to make it usable.
    I tested out your code and it works brilliantly! I'm pretty pleased I was on the right track finding the starting address for the general-purpose registers but I doubt I would have ever found the secret sauce of the enable code. Or even realized I needed to enable it for that matter.
    Thank you kindly, defragster and Frank B!

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Awesome. It is cool to have those 4 dwords on hand to track and save EEPROM and have safe workspace. They do take measurable time to write as they pass through the low speed barrier to the other half of the chip.

  7. #7
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Not that anyone asked - but for another purpose - the post#2 code for T_4.x NVRAM was updated to do the same for T_3.x's.

    Tested this to work on :: T_4.1 and T_3.6 and T_3.0 { should work on anything but a T_LC }::
    Code:
    // TWO ways to access as 4 32 bit DWORDS of NVRAM backed data
    // For T_3.x :: https://forum.pjrc.com/threads/45854-Battery-backed-NV-RAM
    // #define NVRAM_UINT32 ((uint32_t *)0x400D4100)
    
    #if defined(__IMXRT1062__)
    uint32_t *NVRAM_UINT32 ((uint32_t *)0x400D4100);
    #define DWORDS 4
    #else
    uint32_t *NVRAM_UINT32 ((uint32_t *)0x4003E000);
    #define DWORDS 7
    #endif
    
    uint32_t tRam[DWORDS]; // just for speed compare of writes
    
    elapsedMillis tSome;
    void setup() {
    #if defined(__IMXRT1062__)
    	SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
    #endif
    	Serial.begin(115200);
    	while (!Serial && millis() < 4000 );
    	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
    	tSome = 0;
    	showHelp();
    	for ( int ii = 0; ii < DWORDS; ii++ ) {
    		tRam[ii] = 0;
    	}
    }
    
    void loop() {
    	if ( tSome >= 1000 ) {
    		tSome -= 1000;
    		for ( int ii = 0; ii < DWORDS; ii++ ) {
    			Serial.print( '\t' );
    			Serial.print( NVRAM_UINT32[ii] );
    		}
    		Serial.println();
    		for ( int ii = 0; ii < DWORDS; ii++ ) {
    			NVRAM_UINT32[ii]++;
    		}
    	}
    	while ( Serial.available() ) {
    		char cc = Serial.read();
    		if ( 'r' == cc ) {
    			SCB_AIRCR = 0x5FA0004;
    			while (true);
    		}
    		else if ( 'z' == cc ) {
    			Serial.print( "_ZERO_ NVRAM\n" );
    			for ( int ii = 0; ii < DWORDS; ii++ ) {
    				NVRAM_UINT32[ii] = ii * 1000;
    			}
    		}
    		else if ( 't' == cc ) {
    			Serial.print( "_Add_1K_ NVRAM\n" );
    			uint32_t tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					NVRAM_UINT32[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 (7) RTC DWORDS\n", tt );
    			tt = micros();
    #if defined(__IMXRT1062__)
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				SNVS_LPGPR0 += 1;
    				SNVS_LPGPR1 += 2;
    				SNVS_LPGPR2 += 3;
    				SNVS_LPGPR3 += 4;
    			}
    #else
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					NVRAM_UINT32[ii] += 1+ii ;
    				}
    			}
    #endif
    
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 (7) RTC #define DWORDS\n", tt );
    
    			tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					tRam[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t\t for 1K increments of 4 (7) RAM DWORDS\n", tt );
    		}
    		else if ( '?' == cc ) {
    			showHelp();
    		}
    		else {
    			Serial.print( "_10_INC_ NVRAM\n" );
    			tSome += 10000;
    		}
    	}
    }
    
    void showHelp() {
    	Serial.print(" 'r':RESTART 'z':ZERO '?':Help t:INC 1K else:INC 10 to each\n");
    }

  8. #8
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    219
    @defragster
    Copied the sketch above (#7) and it seems to work fine on a battery backed T3.2 when running or reprogramming it starts where it left off. But when I unplug the USB and replug it starts with 0s. The battery is good as the RTC is running on this board.

    Any ideas?

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Quote Originally Posted by bicycleguy View Post
    @defragster
    Copied the sketch above (#7) and it seems to work fine on a battery backed T3.2 when running or reprogramming it starts where it left off. But when I unplug the USB and replug it starts with 0s. The battery is good as the RTC is running on this board.

    Any ideas?
    I'd have to try it, those values were understood to be battery backed.

    Was RTC cell proper polarity and well connected?

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    T_3.2 working here ... had to solder a battery.

    > Confirm good cell near 3V
    > + on vBat
    > - on GND
    > Crystal soldered to T_3.2

    Run Sketch p#7 - edited below to show TIME HH : MM : SS

    >> Restart Teensy with 'r'
    >> Unpower T_3.2 on hub switch
    >> Unplug T_3.2

    On restart all three of those show count continuing
    Did a quick mod with code from earlier today to show the TIME - unplugged over 30 seconds and all is well:
    Code:
    T:\tCode\NVRAM_T\NVRAM_3_4\NVRAM_3_4.ino Jul  6 2021 19:21:41
    RTC has set system time
    hour:19	minute:22	second:16
     'r':RESTART 'z':ZERO '?':Help t:INC 1K else:INC 10 to each
    	2611	3611	4611	5611	6611	7611	8611
    	2612	3612	4612	5612	6612	7612	8612
    	2613	3613	4613	5613	6613	7613	8613
    	2614	3614	4614	5614	6614	7614	8614
    T:\tCode\NVRAM_T\NVRAM_3_4\NVRAM_3_4.ino Jul  6 2021 19:21:41
    RTC has set system time
    hour:19	minute:22	second:57
     'r':RESTART 'z':ZERO '?':Help t:INC 1K else:INC 10 to each
    	2615	3615	4615	5615	6615	7615	8615
    	2616	3616	4616	5616	6616	7616	8616
    New code to confirm RTC is up and running:
    Code:
    // TWO ways to access as 4 32 bit DWORDS of NVRAM backed data
    // For T_3.x :: https://forum.pjrc.com/threads/45854-Battery-backed-NV-RAM
    // #define NVRAM_UINT32 ((uint32_t *)0x400D4100)
    
    #include <TimeLib.h>
    #if defined(__IMXRT1062__)
    uint32_t *NVRAM_UINT32 ((uint32_t *)0x400D4100);
    #define DWORDS 4
    #else
    uint32_t *NVRAM_UINT32 ((uint32_t *)0x4003E000);
    #define DWORDS 7
    #endif
    
    uint32_t tRam[DWORDS]; // just for speed compare of writes
    
    elapsedMillis tSome;
    
    void showTime(void)
    {
    	setSyncProvider(getTeensy3Time);                                // use the Teensy 4.0 RTC timer
    
    	if (timeStatus() != timeSet)
    		Serial.println(F("Unable to sync with RTC"));
    	else
    		Serial.println(F("RTC has set system time"));
    
    	time_t tm = Teensy3Clock.get();
    	tm = now();
    	Teensy3Clock.set(tm);
    	Serial.print("hour:");
    	Serial.print(hour());
    	Serial.print("\tminute:");
    	Serial.print(minute());
    	Serial.print("\tsecond:");
    	Serial.println(second());
    }
    
    
    void setup() {
    #if defined(__IMXRT1062__)
    	SNVS_LPCR |= (1 << 24); //Enable NVRAM - documented in SDK
    #endif
    	Serial.begin(115200);
    	while (!Serial && millis() < 4000 );
    	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
    	tSome = 0;
    	showTime();
    	showHelp();
    	for ( int ii = 0; ii < DWORDS; ii++ ) {
    		tRam[ii] = 0;
    	}
    }
    
    void loop() {
    	if ( tSome >= 1000 ) {
    		tSome -= 1000;
    		for ( int ii = 0; ii < DWORDS; ii++ ) {
    			Serial.print( '\t' );
    			Serial.print( NVRAM_UINT32[ii] );
    		}
    		Serial.println();
    		for ( int ii = 0; ii < DWORDS; ii++ ) {
    			NVRAM_UINT32[ii]++;
    		}
    	}
    	while ( Serial.available() ) {
    		char cc = Serial.read();
    		if ( 'r' == cc ) {
    			SCB_AIRCR = 0x5FA0004;
    			while (true);
    		}
    		else if ( 'z' == cc ) {
    			Serial.print( "_ZERO_ NVRAM\n" );
    			for ( int ii = 0; ii < DWORDS; ii++ ) {
    				NVRAM_UINT32[ii] = ii * 1000;
    			}
    		}
    		else if ( 't' == cc ) {
    			Serial.print( "_Add_1K_ NVRAM\n" );
    			uint32_t tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					NVRAM_UINT32[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 (7) RTC DWORDS\n", tt );
    			tt = micros();
    #if defined(__IMXRT1062__)
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				SNVS_LPGPR0 += 1;
    				SNVS_LPGPR1 += 2;
    				SNVS_LPGPR2 += 3;
    				SNVS_LPGPR3 += 4;
    			}
    #else
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					NVRAM_UINT32[ii] += 1 + ii ;
    				}
    			}
    #endif
    
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t for 1K increments of 4 (7) RTC #define DWORDS\n", tt );
    
    			tt = micros();
    			for ( int jj = 0; jj < 1000; jj++ ) {
    				for ( int ii = 0; ii < DWORDS; ii++ ) {
    					tRam[ii]++;
    				}
    			}
    			tt = micros() - tt;
    			Serial.printf("\t%lu us\t\t for 1K increments of 4 (7) RAM DWORDS\n", tt );
    		}
    		else if ( '?' == cc ) {
    			showHelp();
    		}
    		else {
    			Serial.print( "_10_INC_ NVRAM\n" );
    			tSome += 10000;
    		}
    	}
    }
    
    void showHelp() {
    	Serial.print(" 'r':RESTART 'z':ZERO '?':Help t:INC 1K else:INC 10 to each\n");
    }
    
    time_t getTeensy3Time()
    {
    	return Teensy3Clock.get();
    }

  11. #11
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    219
    After posting, over dinner it dawned on me that maybe the battery was bad and sure enough checking, it is 1.22V. Apparently this is enough to keep the RTC going accurately but not enough for the NVRAM !
    A fresh battery fixed the problem. Maybe I'll see how low it can go.

    thanks

  12. #12
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    219
    see you posted some more info,

    thanks

  13. #13
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Quote Originally Posted by bicycleguy View Post
    After posting, over dinner it dawned on me that maybe the battery was bad and sure enough checking, it is 1.22V. Apparently this is enough to keep the RTC going accurately but not enough for the NVRAM !
    A fresh battery fixed the problem. Maybe I'll see how low it can go.

    thanks
    Glad it is working! After soldering had to use DMM to confirm 2032 here was good ...

    RTC will get set on restart to COMPILE TIME if found unset.

    Not sure how the RTC running was confirmed?

    The Date and Time printed in setup() is time of compile as well - no RTC ref in that.

  14. #14
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    219
    I can't be sure the clock was running because I didn't check the time and re-programming resets a dead clock as you know. I'll reload the original program and see if and how long the clock keeps running on the old battery.

    If there is a difference in the voltage between when the clock dies and when the memory doesn't stick I could build in a feature to check a NVRAM variable agains a EEPROM variable or may just zero to determine when the clock battery needs replaced without actually measuring it!

  15. #15
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Reprogramming provides a NEW time - stored in the sketch at the time of the build.

    As noted on each restart when the RTC memory is seen as 'unset' - it uses the 8th DWORD maintained by T_3.x's after those 7 shown above to determine that IIRC.

    When it is seen as unset - it put the time of last compile as stored in the sketch.

    So when using the low V battery - check the time shown against a running clock. On each repower it may come up running the time of last build.

  16. #16
    Senior Member
    Join Date
    Dec 2015
    Location
    LA
    Posts
    219
    You are correct, whether the RTC was running or not, can't tell because as you said it uses the cleared memory to reset the clock. On mine its at about 1.238V that it dies. Oh well, fun thought.

  17. #17
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    14,421
    Quote Originally Posted by bicycleguy View Post
    You are correct, whether the RTC was running or not, can't tell because as you said it uses the cleared memory to reset the clock. On mine its at about 1.238V that it dies. Oh well, fun thought.
    Hard to see how the RTC could be active when it isn't keeping 'static' memory. Paul must have looked into that as the key to overwrite the Build time into the RTC on startp

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •