T4 SPI broken by Snooze

junknerd

New member
I'm working on creating a logger which I would like to have a low-power version of - Teensy 4.1 with SPI to a MAX31856 thermocouple measurement IC (and many other items that work but stripped out to show my issue below).

Whenever I put the T4.1 to sleep (sleep() or deepSleep()) using the Snooze library (10/10/20 v6.3.9), the SPI completely hangs after wake. If I comment out the sleep lines, everything runs fine. I'm not the first to come across this, though the only PJRC thread I can find on the topic is T4 SPI port register - how to reset it?. The conclusion was to use hibernate() instead, causing a hard-reset of the T4.1, however when i attempt this, my T4.1 never wakes (only attempted with alarm.setRtcTimer(), which is what I need).

That thread also led me to david-res's comment in T4.x SPI and Hibernate #85 in the github Snooze library. I have tried various combinations of SPI.end() and SPI.endTransaction() just before alarm.deepSleep(config) per the suggestions there, with no luck.

After trying many things I'm not sure if this was still relevant, but I have added FLASHMEM to TEENSY40\hal.c (Line 652) per which I believe was required to allow me to get sleep() and deepSleep() to work in the first place.

I have begun digging through the 1060's datasheet, but nothing jumps out as clearly the issue from the LPSPI chapter. The Snooze library is rather extensive, but that is another option, or changing to a different chip. I'd rather stay with the Teensy4.1 since I have a number of pieces working already, and it makes for a blazing fast 3-port CAN to UDP bridge as well (different project, but I hope to merge these eventually).

Any advice on how to get SPI working after waking from sleep/deepSleep (preferably) or possibly correcting the hibernate() would be appreciated.

Logger code, stripped down to just SPI and Snooze, replicates issue:
Code:
#define MYLED 2
#include <stdio.h>

#include <Snooze.h>
SnoozeAlarm  alarm;
SnoozeUSBSerial usb;
SnoozeBlock config_teensy40( usb, alarm);

// ------------- MAX31856 Thermocouple -------------
#include <SPI.h>
const int dataReadyPin = 33;
const int chipSelectPin = 10;

const byte max31856_RD_MASK = 0b01111111;
const byte max31856_WR_MASK = 0b11111111;

uint8_t max31856_read8(uint8_t addr){
  unsigned r; 
  //Serial.printf("Reading 1 byte from reg[%u]\n",addr);
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE1));
    digitalWrite(chipSelectPin, LOW);
      SPI.transfer(addr & max31856_RD_MASK);
      r = SPI.transfer(0x00);
    digitalWrite(chipSelectPin, HIGH);
  SPI.endTransaction();
  return r;
}

void max31856_begin(){
  pinMode(dataReadyPin, INPUT);
  pinMode(chipSelectPin, OUTPUT);
  SPI.begin();
}

void max31856_end(){
  SPI.endTransaction();
  SPI.end();
}

void max31856_setup(){
  unsigned i = 0;

  max31856_begin();

  Serial.printf("max31856_read8() dump:\n");
  for(i=0; i <= 0x0F; i++){
    Serial.printf("[%u] 0x%02X\n", i, max31856_read8(i));
  }
}

void max31856_loop(){
   Serial.printf("max31856_read8(1):");
   Serial.flush();
   Serial.printf("0x%0X\n",max31856_read8(1)); 
}

void setup(){
  pinMode(MYLED, OUTPUT);

  // Open serial communications (removed waiting on Serial to be compact for now)
  Serial.begin(115200);

  // Blink LED to confirm startup
  for (int i=0; i<4; ++i){
    digitalWrite(MYLED, !digitalRead(MYLED));
    delay(80);
  }

  max31856_setup();
}

void try_sleep(){
  static unsigned loopCnt = 0;

  // Wait 5 seconds before attemping sleep after power-up
  if(loopCnt < 5){
    ++loopCnt;
    return;
  }

  int who;
  alarm.setRtcTimer(0, 0, 5);// hour, min, sec
 
  Serial.println("Preparing to sleep");
  max31856_end();

  Serial.println("Going to sleep");
  delay(10);
  Serial.end(); // Serial.end() --> deepSleep() --> Serial.begin() fixed Serial

  // Maybe if it's right next to Snooze? - Didn't work either
  //SPI.endTransaction();
  //SPI.end();
  
  //who = Snooze.sleep(config_teensy40);
  who = Snooze.deepSleep(config_teensy40);
  //who = Snooze.hibernate(config_teensy40);
  
  max31856_begin();
  
  Serial.begin(115200);
  delay(10);
  Serial.println("Awake!");

  switch(who){
    // Who 0-31 is just the pin number that woke us
    //case BTN_PIN: Serial.printf("Woke up by [%d] %s\n", who, "Button"); break;
    case      34: Serial.printf("Woke up by [%d] %s\n", who, "Comparator");break;
    case      35: Serial.printf("Woke up by [%d] %s\n", who, "RTC");break;
    case      36: Serial.printf("Woke up by [%d] %s\n", who, "Low-power timer");break;
    case      37: Serial.printf("Woke up by [%d] %s\n", who, "Touch Interrupt");break;
    default     : Serial.printf("Woke up by [%d] Unknown\n", who);break;
  }
  
}

void loop() {
  max31856_loop();
  try_sleep();
  delay(1000);
}

The output is all correct, it simply hangs at the first read via SPI.

Note: I put some extra debug prints in the SPI.cpp to help track this down, those lines all start with the file path to SPI.cpp.

Code:
/Teensy4.1/arduino-1.8.19/hardware/teensy/avr/libraries/SPI/SPI.cpp:1278: SPIClass::begin()
max31856_read8() dump:
[0] 0x00
[1] 0x03
[2] 0xFF
[3] 0x7F
[4] 0xC0
[5] 0x7F
[6] 0xFF
[7] 0x80
[8] 0x00
[9] 0x00
[10] 0x00
[11] 0x00
[12] 0x00
[13] 0x00
[14] 0x00
[15] 0x00
max31856_read8(1):0x3
max31856_read8(1):0x3
max31856_read8(1):0x3
max31856_read8(1):0x3
max31856_read8(1):0x3
max31856_read8(1):0x3
Preparing to sleep
hardware/teensy/avr/libraries/SPI/SPI.cpp:1819: SPIClass::end() 
hardware/teensy/avr/libraries/SPI/SPI.cpp:1822: Disabling SPI module
Going to sleep

Sleeps appropriate amount of time, then the read never completes:
Code:
Awake!
Woke up by [35] RTC
max31856_read8(1):

Teensy: 4.1 with Ethernet, both PSRAM chips populated, and CR2032 RTC battery installed
Snooze: 10/10/20 v6.3.9
Teensy Loader: 1.57
Arduino: 1.8.19

Thanks for any help and let me know if any other details would be useful.
 
Back
Top