Is there a hook to know when the on/off pin on the Teensy 4.0 has been grounded?

MichaelMeissner

Senior Member+
I switched my little neopixel bowtie project to use a Teensy 4.0 (from Teensy LC). One of the things that prompted me to use the Teensy 4.0 was the on/off pin that can be used to turn off most of the Teensy functionality. However, I forgot since I'm powering the neopixels from VIN, that the on/off switch doesn't affect that. That means when I turn off the Teensy 4.0, the LEDs are frozen at their last position (and of course using power).

Now, I likely will overload the button I use to switch patterns to off the lights as a state, but I was wondering if there was a way I can detect when the on/off pin was grounded to turn off the lights, and then another hook when the power is restored. Obviously I need some time to turn off the LEDs, so I could live with just notification of the button press before the 5 second window expires (or a blocking call that waits until the call finishes to turn off power), but I also would need a hook to run when power is restored.
 
If you have a spare GPIO, couldn't you tie that to the ON/OFF pin and set it to input?

Yes, I could do that. At the moment, I needed it for this weekend. Since I don't have to work with any random neopixel strip, I just switched the neopixels to use 3.3v and not VIN. While it is out of spec, many neopixels will work fine at 3.3v as long as you don't strain the Teensy voltage regulator (I keep it down to 30-75 mA for the neopixels, by limiting the output power).

Another thought that I've had is connect a diode to VIN based on 3.3v.
 
Last edited:
Would be nice to have notice of Power OFF start. Of course starting a timer to notify if the Power OFF was cut short and not actualized, or having it be a CHANGE interrupt where the return to HIGH says the OFF was a false alarm, and if OFF stays LOW it would be lights out.

As far as a hook for Power ON : Setup() or these happen when the POWER goes ON in any case:
Code:
void startup_early_hook(void)		__attribute__ ((weak, alias("startup_default_early_hook")));
void startup_late_hook(void)		__attribute__ ((weak, alias("startup_default_late_hook")));
 
I have not played with it nor looked carefully in the reference manual: But maybe the:IRQ_SNVS_ONOFF = 48,

Might be something, but have not looked yet to see when/if this one is actually called, and what control one might have over it.
 
The voltage regulator switches off, too - the 3V3 pin goes low.
You might want to look at the table 20.5.3 Power mode transitions (Reference manual)

Bit 17 of SNVS_LPSR can tell you if there was a power-off before.
Serial.println(SNVS_LPSR, HEX);

Bit 18 is interesting, too:
Set Power Off
The SPO bit is set when the power button is pressed longer than the configured debounce time. Writing
to the SPO bit will clear the set_pwr_off_irq interrupt.
So, we have an interrupt, and I guess there is a way to stop the power-off sequence and do something before we then power-off manually with software
 
Last edited:
Thanks Frank - here is a starter sketch:
Code:
void setup() {
	// put your setup code here, to run once:
	Serial.begin(115200);
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	Serial.println(SNVS_LPSR, HEX);
	pinMode( 13, OUTPUT );
}

uint32_t reg_SNVS_LPSR = 0;
void loop() {
	uint32_t t_reg_SNVS_LPSR;

	t_reg_SNVS_LPSR = SNVS_LPSR;
	if ( t_reg_SNVS_LPSR != reg_SNVS_LPSR) {
		digitalWriteFast( 13, !digitalReadFast(13));
		Serial.println(SNVS_LPSR, HEX);
		reg_SNVS_LPSR = t_reg_SNVS_LPSR;
	}
}

Those bits are STICKY! First press - and repeated press no change:
T:\tCode\T4\PowerOffTest\PowerOffTest.ino Jan 17 2020 14:14:41
40000008
40000008
40040008

Then complete OFF and ON restart - button is ignored as the bit remains:
T:\tCode\T4\PowerOffTest\PowerOffTest.ino Jan 17 2020 14:15:48
40060008
40060008

Then kill USB POWER ( WITH RTC vBat present ) and on start:
T:\tCode\T4\PowerOffTest\PowerOffTest.ino Jan 17 2020 14:15:48
40000008
40000008
 
ok, a bit sticky :)

But there is a way to reset them:

SNVS_HPCOMR |= (1<<4);

Not sure what this helps.. the only reason after the initial power on is a "soft" power-on" with button press.
So, you get always the same value.. even with this reset.

Edit: Reminder (for me and others)
Power off via software with:

delay(15000); //attention
SNVS_LPCR |= (1<<6);
 
SNVS_LPCR: 17-16 BTN_PRESS_TIME

This field configures the button press time out values for the PMIC Logic.
00 : 5 secs
01 : 10 secs
10 : 15 secs
11 : long press disabled (pmic_en_b will not be asserted regardlessof how long BTN is asserted)

So.. to disable the button:

SNVS_LPCR |= (1<<17) | (1<<16);
 
if you do this:

Code:
void setup() {
_VectorsRam[48+16] = &int48;
NVIC_ENABLE_IRQ(48);
}
void int48() {
   Serial.println("int");
}

The interrupt fires as soon the button is pressed, immediately or after the debounce-time (have to check this)
So we can use this to power off - we just have to wait 4.99 seconds via software, and then use SNVS_LPCR |= (1<<6); - or, more simple - just use the interrupt to call the desired callback and hope that it is faster than 5 seconds.
I had to wait 10 seconds before power-on again - looks like the capacitors on board have enough power to keep the "button was pressed" information...

But it's too late now.. bed-time.

I hope one of you guys has this working when it's morning here ;)
All information is here.. or chapter SNVS, in the manual :)
 
Frank - this edits the upper WORD? Not sure to what effect - did not use :: // SNVS_HPCOMR |= (1 << 4);

Also page number 1313 in Rev1 RM would have been a good link :)

Here is a run of code that follows. Tested on LOGLOW T_4 Breakout boards with POWER button. It acts the SAME with and without RTC vBat cell - with USB power continuous.

POWER applied USB (1), then TAP the POWER_OFF and release(2), Then hold POWER_OFF(3), Then press POWER_ON(4):
T:\tCode\T4\PowerOffTest\PowerOffTest.ino Jan 17 2020 21:46:44 (1)
40000008
40000008
40000008
POWER_OFF BUTTON 3685000 40040008 (2)
40000008
40000008
POWER_OFF NEAR 8585274
NO POWER_OFF 9085360
POWER_OFF BUTTON 12699824 40040008 (3)
40000008
40000008

POWER_OFF NEAR 17600091

T:\tCode\T4\PowerOffTest\PowerOffTest.ino Jan 17 2020 21:46:44 (4)
40020008
b17 - SNVS_LPSR :: Back from POWER_OFF
40000008
40000008

from this code:
Code:
volatile uint32_t pOff = 0;
uint32_t pOff_Time;
void setup() {
	// put your setup code here, to run once:
	Serial.begin(115200);
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
	Serial.println(SNVS_LPSR, HEX);
	pinMode( 13, OUTPUT );
	digitalWriteFast( 13, !digitalReadFast(13));

	if ( SNVS_LPSR & (1 << 18) )
	{
		Serial.println(" b18 - SNVS_LPSR :: Power button pressed");
		[COLOR="#FF0000"]SNVS_LPSR |= (1 << 18);  // PG 1313[/COLOR]
		delay(1000);
	}
	if ( SNVS_LPSR & (1 << 17) )
	{
		SNVS_LPSR |= (1 << 17);  // PG 1313 :: Reg is RW so this clears 
		Serial.println(" b17 - SNVS_LPSR :: Back from POWER_OFF");
	}
	Serial.println(SNVS_LPSR, HEX);
	_VectorsRam[48 + 16] = &int48;
	NVIC_ENABLE_IRQ(48);
}

void int48() {
	digitalWriteFast( 13, !digitalReadFast(13));
	Serial.printf("POWER_OFF BUTTON %lu\t", micros());
	Serial.println(SNVS_LPSR, HEX);
	SNVS_LPSR |= (1 << 18);  // PG 1313 : Write SPO bit to clear set_pwr_off_irq
	pOff = 1;
	pOff_Time = micros();
	asm volatile ("dsb"); // otherwise it doubles
}

uint32_t reg_SNVS_LPSR = 0;
void loop() {
	uint32_t t_reg_SNVS_LPSR;

	t_reg_SNVS_LPSR = SNVS_LPSR;
	if ( t_reg_SNVS_LPSR != reg_SNVS_LPSR) {
		Serial.println(SNVS_LPSR, HEX);
		reg_SNVS_LPSR = t_reg_SNVS_LPSR;

		if ( SNVS_LPSR & (1 << 18) )
		{
			Serial.println(" b18 - SNVS_LPSR :: Power button pressed");
			delay(1000);
			digitalWriteFast( 13, !digitalReadFast(13));
		}
	}
	if ( pOff != 0 && (micros() > pOff_Time + 5000000) ) {
		pOff = 0;
		digitalWriteFast( 13, 1);
		Serial.printf("NO POWER_OFF %lu\n", micros());
	}
	else if ( pOff != 0 && (micros() > pOff_Time + 4900000) ) {
		Serial.printf("POWER_OFF NEAR %lu\n", micros());
		digitalWriteFast( 13, 1);
		delay(500);
		digitalWriteFast( 13, 0);
	}
}

Note the _isr() >> int48()::
> the "dsb" is needed to prevent doubling here
> the INTERRUPT fires PERPETUALLY until :: SNVS_LPSR |= (1 << 18); // PG 1313 : Write SPO bit to clear set_pwr_off_irq

<EDIT>:: SPECULATION?? The following BOLD lines in print above are CAUGHT in LOOP and don't appear to make sense?

40000008
40000008

They should not print unless the value of SNVS_LPSR changes - but the values shown are the same. But looking at the code in loop() the saved compare value isn't printed - but the current value of SNVS_LPSR.

Sure enough a edit to printed values shows it was caught before the _isr() fired - then the _isr() fired and cleared before printing - so the _isr() it seems fires AFTER the debounce.
POWER_OFF BUTTON 8160366 40040008
SNVS_LPSR:40000008 and test:40040008
SNVS_LPSR:40000008 and test:40000008
POWER_OFF NEAR 13060641
NO POWER_OFF 13560735

Changed code line:
Code:
		Serial.printf( "SNVS_LPSR:%X and test:%X\n", SNVS_LPSR, t_reg_SNVS_LPSR);
 
Last edited:
@Tim, thank you!

@MichaeMeissner, @PaulStoffregen

So, this is a complete program for the user-callback:
Edt: See library

@Paul: Can we add this to the core?

It shuts down the Teensy immediately after button-press. The "emergency shutdown" still works, even if the user-callback crashes.
.. To detect the soft-power off, we could add another little function (@Tim... ;-) )
 
Last edited:
Frank - I see the _isr() trigger and reset NOT as :: SNVS_LPSR |= (1 << 17); // PG 1313 clear interrupt
But as this coded above p#11:: SNVS_LPSR |= (1 << 18); // PG 1313 : Write SPO bit to clear set_pwr_off_irq

And if the button is released it will not go to power OFF based on bit #18. That _isr() is the start of the 4.9499 second wait.

Time for sleep here - but that looks like it will go POWER_OFF as soon as the debounce period ends and the _isr() is triggered?

Bit 17 is set on power up after POWER_OFF triggered with bit 18 in code posted above. Bit 17 is only set as the MCU is shutdown to OFF.

Alternatively to post #11 the _isr() could disable itself and leave the button indicator pin on [ i.e. no // SNVS_LPSR |= (1 << 18); ]. Button will release and the _isr() re-enabled when detected in loop(), or it will be held to power OFF.
I cleaned up the _isr() taking out the print()'s and abusing LP regs can track the time between _isr() and power off - but those 2 32bit regs I see as working are marked 'RESERVED' ( pg 392-4 @ 0x400A4000 )

@FrankB - can you find the usable address for what could be LP NVRAM regs indicated at pg 1294? SNVS base address: 400D_4000h :: GPR's at 68h or 90h or 100h ???
 
@Tim, SNVS_LPSR |= (1 << 18); does not work too - i've just removed the line in #12 - still works (have you tried the code?)
I see no need to wait 5 seconds. It's a better user-experience when it shuts down asap :)
And - if everything goes wrong, the 5 secs "emergency shut down" in hardware still works.

edit: NVRAM - no, I have to do some shopping in the city now.
 
@Tim, SNVS_LPSR |= (1 << 18); does not work too - i've just removed the line in #12 - stil works.
I see no need to wait 5 seconds. It's a better user-experience when it shuts down asap :)
And - if everything goes wrong, the 5secs "emergency shut down" in hardware still works.

Okay - so I read the code right - though when _isr triggers I never see that bit #17 set with the HEX print of the register - until power on? Would be nice to have a way for the user callback to abort though if the POWER_OFF is released before 5 secs? Leave Bit #18 set - do the optional callback() and if on return the user dropped #18 do not shutdown immediately - though if held 5 secs I don't suppose that can be stopped unless 'disabled?

>> As long as you don't leave the _isr() it won't be called again - so turning that bit off doesn't matter.

Can you find the NVRAM addressing like you did for the T_3.x's?
 
I'm at a convention this weekend, but the way I tend to want to use the feature is quicker on/off. I can understand why you want to have a timeout (to prevent accidental touching of the button), but I would prefer it to be quicker. It doesn't have to use the hardware pin, but some programmatic way to go into sleep state that is easily would be helpful, providing I don't have to bring out two buttons (one for sleep, one for waking up).

I don't need sleep mode (i.e. restore to current ram), just turning off/on would be helpful. I realize Paul packs the PCB completely, but having a small physical switch that cuts the power completely (like most of Adafruit's mid/high boards have) would be nice. But that won't help the current T4.
 
Edit: See library

So, this version above works good. (Maybe a bit more testing from other users might be good)
- It calls the callback, after pressing the button, without timeout
- if callback crashes, the 5-sec "emergency"-power off still works
- it can detect the previous power state

There was a problem with a already pending interrupt. Solution was to just clear the pending bit.
 
Last edited:
should be offset 0x100, I think
Have you got a minute to test ? My efforts were wrong or it doesn't work as expected.



That looks nice Frank - I can't test it just now. One comment - you might #ifdef it to have those functions go NULL on Teensy that doesn't have POWER signal so code that works on T_4 would work on a T_3.x without compiler error or user code edits?

Also maybe add a POWER_DOWN_NOW_function(){SNVS_LPCR |=(1<<6) ; // turn off power} that just does that as @MichaeMeissner or others might want to do.
 
Have you got a minute to test ? My efforts were wrong or it doesn't work as expected.




That looks nice Frank - I can't test it just now. One comment - you might #ifdef it to have those functions go NULL on Teensy that doesn't have POWER signal so code that works on T_4 would work on a T_3.x without compiler error or user code edits?

Also maybe add a POWER_DOWN_NOW_function(){SNVS_LPCR |=(1<<6) ; // turn off power} that just does that as @MichaeMeissner or others might want to do.

I would, but I have not Teensy4 with battery.
and.. how to reset? the arm-core reset seems to power off.

Thank you for the ideas :)
 
The lib was updated and has now three functions:

void arm_reset(void); // reset
void arm_power_down(void); //switch off
void set_arm_power_button_callback(void (*fun_ptr)(void));
 
Last edited:
I would, but I have not Teensy4 with battery.
and.. how to reset? the arm-core reset seems to power off.

Thank you for the ideas :)

Interesting - not sure if I tested with vBat either - though it seemed that when USB stayed connected there was power to maintain low power when I was abusing the reserved dWords I found before ...
 
Interesting - not sure if I tested with vBat either - though it seemed that when USB stayed connected there was power to maintain low power when I was abusing the reserved dWords I found before ...

Maybe it's better to open a new thread.
The SNVS_LP General Purpose Register is a 128-bit read/write register located in
SNVS_LP, which can be used by any application for retaining data during an SoC powerdown
mode. This is a privileged read/write register. The full GPR register is accessed as
4 32-bit registers located in successive word addresses starting at offset 100h. For
backward compatibility with earlier versions of SNVS, LPGPR0..LPGPR3 are aliased at
the earlier offset of 90h and LPGPR0 is aliased at its original offset of 68h. The GPR will
be automatically zeroized when a tamper event occurs, unless GPR zeroization is
disabled via the GPR_Z_DIS bit in the LP Control Register
.
So the remaining question is: where is the GPR_Z_DIS bit. It not documented in the LP Control Register description.

The manual says, it's offset 100 for the "NVRAM".
So it's probably something like IMXRT_SNVS.offset100 = 0xbeef; to write it.
 
Last edited:
Back
Top