Teensy 4.0 Sleep with CAN wakeup

I have a CAN bus project (in Arduino) that I need the Teensy to go to sleep, I found the snooze library and believe I have it setup right, I can get the Teensy to sleep but never wakes up, I'm using the RX pins as the wake up interrupts. Can the CAN RX pins be used as wake interrupt pins? I did scope all three CAN RX pins and I'm getting valid CAN data to them while in sleep so they should be triggering the wake up.

Any help is appreciated, here is my sleep testing sketch

Code:
#include <FlexCAN_T4.h>
#include <Snooze.h>

#define CAN1_STB 7      // Transceiver Standby Pin
#define CAN2_STB 12     // Transceiver Standby Pin
#define CAN3_STB 13     // Transceiver Standby Pin
#define CAN1_RX 23      // Transceiver RX Pin
#define CAN2_RX 0       // Transceiver RX Pin
#define CAN3_RX 30      // Transceiver RX Pin

SnoozeDigital digital;
SnoozeBlock config(digital);

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> CAN_1;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> CAN_2;
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> CAN_3;

uint32_t awake;

void setup() {
  pinMode(CAN1_STB, OUTPUT);
  pinMode(CAN2_STB, OUTPUT);
  pinMode(CAN3_STB, OUTPUT);
  digital.pinMode(CAN1_RX, INPUT, FALLING);
  digital.pinMode(CAN2_RX, INPUT, FALLING);
  digital.pinMode(CAN3_RX, INPUT, FALLING);

  // Setup CAN_1
  CAN_1.begin();
  CAN_1.setClock(CLK_60MHz);
  CAN_1.setBaudRate(500000);
  CAN_1.enableFIFO();
  CAN_1.enableFIFOInterrupt();
  CAN_1.onReceive(CAN1_receive);
 
  // Setup CAN_2
  CAN_2.begin();
  CAN_2.setClock(CLK_60MHz);
  CAN_2.setBaudRate(500000);
  CAN_2.enableFIFO();
  CAN_2.enableFIFOInterrupt();
  CAN_2.onReceive(CAN2_receive);
 
  // Setup CAN_3
  CAN_3.begin();
  CAN_3.setClock(CLK_60MHz);
  CAN_3.setBaudRate(500000);
  CAN_3.enableFIFO();
  CAN_3.enableFIFOInterrupt();
  CAN_3.onReceive(CAN3_receive);
}

void CAN1_receive(const CAN_message_t &msg){
  awake = millis();
}

void CAN2_receive(const CAN_message_t &msg){
  awake = millis();
}

void CAN3_receive(const CAN_message_t &msg){
  awake = millis();
}

void loop() {
  if(millis() - awake >= 1000){
    digitalWrite(CAN1_STB, HIGH);
    digitalWrite(CAN2_STB, HIGH);
    digitalWrite(CAN3_STB, HIGH);
    int who = Snooze.deepSleep(config);
    digitalWrite(CAN1_STB, LOW);
    digitalWrite(CAN2_STB, LOW);
    digitalWrite(CAN3_STB, LOW);
  }
CAN_1.events();
CAN_2.events();
CAN_3.events();
}
 
I tried this many times, but could not get it to work.

I think the best thing you can do if find a CAN transceiver that can indicate when the bus is active,. This will usually be a state where it detects a wake up patten on the bus, and will pull RDX low

So what you would do in your app is wait for X seconds for no activity on the bus, put the transceiver into low power/standby mode, set the RDX input pin as a digital pin and set Snooze to wake on on pin interrupt.
Once it's woken up, you need to reinit CAN to set the pin MUX's to CAN mode, and there start your app up
 
The CAN transceivers I’m using are a TJA1442A, they support wake up when in standby mode. I’ve scoped the RX pin (0, 23, 30) and all the transceivers are properly pulling the RX pin low. I’m guessing that because I setup the wake-up pins then initialize the CAN modules, I’m overwriting the wake-up pins and that’s why it’s not waking up?

I also notice that when it goes to sleep the current consumption goes from about ~120mA to ~30mA, is there a way to get this lower? The only thing connected to the teensy is the 3 transceivers and I’m pulling there enable pins high to put them to sleep.
 
On T_3.6 an interrupt could be set on the and triggered when the port was in use as Uart.Serial. Done after GPS message was complete the interrupt was restored for it going LOW, then disabled until the next GPS complete.

This same code did not work on T_4.x after Serial#.begin so it seems the alternate hardware MUX-workings may preclude that.

Before Snooze.deepSleep() repeat the pinModes from setup() - and maybe it will wake?

If that works, it may be needed to then repeat the Can.begin to reset the Mux-workings.
 
Last edited:
@ifrythings

Try this:
When it’s time to sleep, stop all CAN TX’s and disable RX interrupts.
Configure Snooze and then go into the relevant sleep mode.

Regarding the 30mA in sleep mode - I recall a thread where someone observed this in sleep, but in deep sleep and hibernation it dropped even lower.
 
When it’s time to sleep
Yes, See post #4 - T_4.x hardware - at least on UARTs - has isolated 'parallel' interrupts that used to work on T_3.6 & T_3.5

Though need to have CAN alive to wake - whatever that takes. First messages may be missed before wake and reset.
 
On T_3.6 an interrupt could be set on the and triggered when the port was in use as Uart.Serial. Done after GPS message was complete the interrupt was restored for it going LOW, then disabled until the next GPS complete.

This same code did not work on T_4.x after Serial#.begin so it seems the alternate hardware MUX-workings may preclude that.

Before Snooze.deepSleep() repeat the pinModes from setup() - and maybe it will wake?

If that works, it may be needed to then repeat the Can.begin to reset the Mux-workings.
I don't think the UART is causing this as I don't have any Serial commands at all, not even Serial#.begin.

I tried setting the snooze pins just before sleep and putting the transceivers into standby mode before that, all TX was stopped and no RX was received.
@ifrythings

Try this:
When it’s time to sleep, stop all CAN TX’s and disable RX interrupts.
Configure Snooze and then go into the relevant sleep mode.

Regarding the 30mA in sleep mode - I recall a thread where someone observed this in sleep, but in deep sleep and hibernation it dropped even lower.
Tried this, doesn't work, maybe I'm missing something or a typo?

I'm using the Snooze.deepSleep() and still seeing that 30mA draw, now its completely possible I have something set wrong and the processor is hung up and that's why I'm getting the 30mA draw? (wild guess, I have nothing to support this theory besides the Teensy not waking up)

Yes, See post #4 - T_4.x hardware - at least on UARTs - has isolated 'parallel' interrupts that used to work on T_3.6 & T_3.5

Though need to have CAN alive to wake - whatever that takes. First messages may be missed before wake and reset.
I'm not using the UART's but maybe something else needs to be set before going to sleep?
I have done this before with an Atmega64M1 and yes the first message is lost while the micro is waking up but missing the first message isn't a problem for my application, the low power sleep is more important.

Here is my test code
Code:
#include <FlexCAN_T4.h>
#include <Snooze.h>

#define CAN1_STB 7      // Transceiver Standby Pin
#define CAN2_STB 12     // Transceiver Standby Pin
#define CAN3_STB 13     // Transceiver Standby Pin
#define CAN1_RX 23      // Transceiver RX Pin
#define CAN2_RX 0       // Transceiver RX Pin
#define CAN3_RX 30      // Transceiver RX Pin

SnoozeDigital digital;
SnoozeBlock config(digital);

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> CAN_1;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> CAN_2;
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> CAN_3;

uint32_t keep_awake;      // Timer For System Sleep

void CAN_INT(){
  // Setup CAN_1
  CAN_1.begin();
  CAN_1.setClock(CLK_60MHz);
  CAN_1.setBaudRate(500000);
  CAN_1.enableFIFO();
  CAN_1.enableFIFOInterrupt();
  CAN_1.onReceive(CAN1_receive);
 
  // Setup CAN_2
  CAN_2.begin();
  CAN_2.setClock(CLK_60MHz);
  CAN_2.setBaudRate(500000);
  CAN_2.enableFIFO();
  CAN_2.enableFIFOInterrupt();
  CAN_2.onReceive(CAN2_receive);
 
  // Setup CAN_3
  CAN_3.begin();
  CAN_3.setClock(CLK_60MHz);
  CAN_3.setBaudRate(500000);
  CAN_3.enableFIFO();
  CAN_3.enableFIFOInterrupt();
  CAN_3.onReceive(CAN3_receive);
}

void CAN_SLEEP(){
  digitalWrite(CAN1_STB, HIGH);               // Put Transceiver In Standby Mode
  digitalWrite(CAN2_STB, HIGH);               // Put Transceiver In Standby Mode
  digitalWrite(CAN3_STB, HIGH);               // Put Transceiver In Standby Mode
  digital.pinMode(CAN1_RX, INPUT, FALLING);   // Set Wake Up Pin
  digital.pinMode(CAN2_RX, INPUT, FALLING);   // Set Wake Up Pin
  digital.pinMode(CAN3_RX, INPUT, FALLING);   // Set Wake Up Pin
  Snooze.deepSleep(config);                   // Put System To Sleep
  digitalWrite(CAN1_STB, LOW);                // Put Transceiver In Active Mode
  digitalWrite(CAN2_STB, LOW);                // Put Transceiver In Active Mode
  digitalWrite(CAN3_STB, LOW);                // Put Transceiver In Active Mode
  CAN_INT();
}

void setup() {
  pinMode(CAN1_STB, OUTPUT);
  pinMode(CAN2_STB, OUTPUT);
  pinMode(CAN3_STB, OUTPUT);
  CAN_INT();
}

void CAN1_receive(const CAN_message_t &msg){
  keep_awake = millis();      // Keep System Awake If CAN Traffic
}

void CAN2_receive(const CAN_message_t &msg){
  keep_awake = millis();      // Keep System Awake If CAN Traffic
}

void CAN3_receive(const CAN_message_t &msg){
  keep_awake = millis();      // Keep System Awake If CAN Traffic
}

void loop() {
  if(millis() - keep_awake >= 5000) CAN_SLEEP();      // If No CAN Traffic for 5s, Go To Sleep
  CAN_1.events();
  CAN_2.events();
  CAN_3.events();
}
 
Small update, I found in one of the examples that said you have to use SnoozeUSBSerial usb; on all T4.x devices, I added that in along with SnoozeBlock config(usb, digital);. The sleep current now has gone below what my power supply can measure (<10mA) so that's good news. Problem now is it doesn't wake up, it just shoots up to 30mA for about 3-5 seconds and then acts like you hit the reset button and does a full boot.

If I manually toggle the pin (23) it wakes up and acts normal, if I send CAN data the current shoots up to 30mA and it never executes code for 3-5 seconds and then just reboots itself and starts working again till it goes to sleep.

To me it appears that the short pulses from the CAN controller confuse the Teensy somehow and after the 3-5 seconds the Teensy just restarts itself from some error.
 
the UART is causing this
not saying it is the UART - it is CAN - but the same may apply.

What worked perfectly on the T_3.6 was DOA on the T_4.x. Due to a change in the operation of the hardware no doubt.
Maybe it wasn't supposed to work on the T_3.6 - but it did reliably without any interference on the UART to the GPS.

Perhaps try the asm>>"WFI" instead of Snooze? Do the pinMode change and set an interrupt on the pin(s) and see what happens? If no other interrupts are active (the Millis_Tick doesn't count) it will sleep until a true interrupt triggers.
 
Not seeing pinMode( CANx_stb, INPUT) in CAN_SLEEP() along with the interrupt( 'pin', FALLING). The digitalWrite() are possibly 'falling on deaf ears' when the CAN has MUX'd the pins to its use.
 
It is some time since I did any work with the Snooze library. I do recall that the Teensy 4.x hibernate return from sleep results in a Teensy reboot.
Teensy 4.x deepsleep, however, operates as expected.
I ran some tests to determine wakeup times as the T4.x was not waking up as expected. I was missing data. It turned out that the T4.x was taking over 5ms to wakeup. Below are the charts for all the Teensy's I had.

Snooze Wakeup times.png
 
I do now recall that there was a problem with T4.x. See the comment and solution here.
I think this is now included in Teensyduino, but it is worth a check just in case it has slipped through.
 
Small update, I found in one of the examples that said you have to use SnoozeUSBSerial usb; on all T4.x devices, I added that in along with SnoozeBlock config(usb, digital);. The sleep current now has gone below what my power supply can measure (<10mA) so that's good news. Problem now is it doesn't wake up, it just shoots up to 30mA for about 3-5 seconds and then acts like you hit the reset button and does a full boot.

If I manually toggle the pin (23) it wakes up and acts normal, if I send CAN data the current shoots up to 30mA and it never executes code for 3-5 seconds and then just reboots itself and starts working again till it goes to sleep.

To me it appears that the short pulses from the CAN controller confuse the Teensy somehow and after the 3-5 seconds the Teensy just restarts itself from some error.
Just a suggestion. Can you wire OR your CAN Rx lines (using diodes to isolate each other) and connect the resultant wire to a vacant pin. Then use that pin to wakeup from sleep. As I said above it will take the T4.x over 5 ms to wake up so it's more than likely that you will lose some data.

Paul has some T3,2s for sale at the moment. Could you use one of those. The Snooze library works much better on T3.x.
 
not saying it is the UART - it is CAN - but the same may apply.

What worked perfectly on the T_3.6 was DOA on the T_4.x. Due to a change in the operation of the hardware no doubt.
Maybe it wasn't supposed to work on the T_3.6 - but it did reliably without any interference on the UART to the GPS.

Perhaps try the asm>>"WFI" instead of Snooze? Do the pinMode change and set an interrupt on the pin(s) and see what happens? If no other interrupts are active (the Millis_Tick doesn't count) it will sleep until a true interrupt triggers.
I'll take a look at the WFI instruction, I haven't done any assembly language before so that could be interesting to figure out.

It looks like the missing SnoozeUSBSerial usb; was causing the high sleep currents.
Not seeing pinMode( CANx_stb, INPUT) in CAN_SLEEP() along with the interrupt( 'pin', FALLING). The digitalWrite() are possibly 'falling on deaf ears' when the CAN has MUX'd the pins to its use.
I did forget those when I posted the code above, I did have them in my stripped down test, the digitalWrite() before sleep were working, after the sleep command it never ran code just the Teensy rebooted for some reason.
It is some time since I did any work with the Snooze library. I do recall that the Teensy 4.x hibernate return from sleep results in a Teensy reboot.
Teensy 4.x deepsleep, however, operates as expected.
I ran some tests to determine wakeup times as the T4.x was not waking up as expected. I was missing data. It turned out that the T4.x was taking over 5ms to wakeup. Below are the charts for all the Teensy's I had.

View attachment 38395
5ms isn't great but I can work with that, it's the current 3-5s that's an issue.
Just a suggestion. Can you wire OR your CAN Rx lines (using diodes to isolate each other) and connect the resultant wire to a vacant pin. Then use that pin to wakeup from sleep. As I said above it will take the T4.x over 5 ms to wake up so it's more than likely that you will lose some data.

Paul has some T3,2s for sale at the moment. Could you use one of those. The Snooze library works much better on T3.x.
It doesn't appear that the Teensy is having issues using the CAN RX pins as interrupts, the issue appears the quick speed of the CAN frame is causing some issue that ends up rebooting the Teensy. If I manually ground one of the RX pins, the Teensy wakes up and continues to run, if I send a CAN frame it appears to ignore it even though the pin is getting toggled to ground, if I repeatedly send CAN frames eventually the Teensy just reboots and works fine again till it goes to sleep again.

Unfortunately the T3.2 wont work, I need the 3 CAN busses and the speed of the T4.x

I have made an even more stripped down sketch without CAN in it and I'm observing the same issues, it's almost like either the low pulse from CAN frames is too short or there is too many happening at once that's confusing the Teensy (maybe multiple nested interrupts?) until it reboots from some fault.

Code:
#include <Snooze.h>

SnoozeDigital digital;
SnoozeUSBSerial usb;
SnoozeBlock config(usb, digital);

uint32_t keep_awake;      // Timer For System Sleep

void setup() {
  pinMode(23, INPUT_PULLUP);
  digital.pinMode(23, INPUT_PULLUP, FALLING); // Set Wake Up Pin
}

void loop() {
  if(!digitalRead(23)) keep_awake = millis();
  if(millis() - keep_awake >= 5000) Snooze.deepSleep(config); // Put System To Sleep
}

Super simple code, if I manually toggle the pin to ground (doesn't matter how fast I can physically toggle it) the Teensy wakes up and runs normal. If I send a CAN frame it does nothing, if I keep sending frames after 5-10 seconds the Teensy just reboots itself and runs normal till going to sleep again.
 
Have you verified the VIO pin of TJA1442A is connected to 3.3V ?
And, that it's a TJA1442A and not TJA1442B ?
Yes the VIO pin has 3.3V all the time and I’m using the TJA1442A, 5V to the TJA1442A and Teensy 5V input is sold also. The CAN side of things work perfectly, been running a gateway between 3 busses with this for over 2 months now without issues. Now I’m trying to get it to sleep as the last step now.
 
@ifrythings
Idea, you might have tried this and I missed it - bridge the RX pin to another GPIO, and use that GPIO for the wakeup trigger
That way, you don't need to muck around with reconfiguring CAN pins or setting the RX pin to GPIO for the trigger.
 
@ifrythings
Idea, you might have tried this and I missed it - bridge the RX pin to another GPIO, and use that GPIO for the wakeup trigger
That way, you don't need to muck around with reconfiguring CAN pins or setting the RX pin to GPIO for the trigger.
I haven’t tried that but I don’t think that’s going to change anything, with that super basic sketch that doesn’t even have the CAN library in it, the teensy doesn’t wake up properly when a CAN frame is toggling the RX pin.
Have you tried using CrashReport in your setup() function? That may indicate a reason for the reboot.
I didn’t even know about this, I’ll do some research and try it out.
 
I'll take a look at the WFI instruction
Quick forum search suggests this is the usage: asm volatile("wfi");
digitalWrite() before sleep were working,
This indicates indeed that interrupts don't work when those pins are associated with CAN ... just like when they are devoted to UART on the T_4.x. This was the first step.

What happens after that might be indicated with suggested CrashReport(). After the 'wake' that interrupt must be disabled or it will trigger as the data flows on the pin, which might be the source of the problem - as it wasn't noted that perhaps after the pinMode() - it might need to be given an attachInterrupt() to a function that just might do the Detach and exit - but will cause the WAKE.
 
@defragster the WFI instruction will put it into Sleep mode, but not deepSleep or Hibernate
@ifrythings Another thing you can try, but I don't like this solution much, is to use a voltage divider on the CANL line into a GPIO and sense a rise on that signal to wake up.
 
Back
Top