I2C Sensor Not Working After Deep Sleep

I’m trying to expand on the Artemis Example6_LowPower_Alarm to read a Keller pressure sensor every time the micro wakes up. It is essentially working, however when I disable the power to the IOM4 peripheral before sleep (and then re-enable it once woken up) it doesn’t work. The first read is correct, but then all subsequent reads are a fixed value. Any advice would be appreciated.

/*
  Author: Adam Garbo and Nathan Seidle
  Created: June 3rd, 2020

  This example demonstrates how to set an RTC alarm and enter deep sleep.

  The code is configured to set an RTC alarm every minute and enter
  deep sleep between interrupts. The RTC interrupt service routine will
  wake the board and print the date and time upon each alarm interrupt.

  Tested with a SparkFun Edge 2. Confirmed sleep current of 2.5 uA.
*/

/* 
// This file is subject to the terms and conditions defined in
// file 'LICENSE.md', which is part of this source code package.
*/

#include "RTC.h"
#include <Wire.h>
#include "KellerLD.h"

KellerLD sensor;
uint counter = 0;

void setup()
{
  Serial.begin(115200);
  Serial.println("SparkFun RTC Low-power Alarm Example");

  Wire.begin();

  // // Easily set RTC using the system __DATE__ and __TIME__ macros from compiler
  // RTC.setToCompilerTime();

  // Manually set RTC date and time
  rtc.setTime(0, 50, 59, 12, 3, 6, 20); // 12:59:50.000, June 3rd, 2020 (hund, ss, mm, hh, dd, mm, yy)

  // Set the RTC's alarm
  rtc.setAlarm(0, 0, 0, 13, 3, 6); // 13:00:00.000, June 3rd (hund, ss, mm, hh, dd, mm). Note: No year alarm register

  // Set the RTC alarm mode
  /*
    0: Alarm interrupt disabled
    1: Alarm match every year   (hundredths, seconds, minutes, hour, day, month)
    2: Alarm match every month  (hundredths, seconds, minutes, hours, day)
    3: Alarm match every week   (hundredths, seconds, minutes, hours, weekday)
    4: Alarm match every day    (hundredths, seconds, minute, hours)
    5: Alarm match every hour   (hundredths, seconds, minutes)
    6: Alarm match every minute (hundredths, seconds)
    7: Alarm match every second (hundredths)
  */
  rtc.setAlarmMode(6); // Set the RTC alarm to match on seconds rollover
  rtc.attachInterrupt(); // Attach RTC alarm interrupt

  sensor.init();
  sensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)

  if (sensor.isInitialized()) {
    Serial.println("Keller Pressure Sensor Connected.");
  } else {
    Serial.println("Keller Pressure Sensor Not Connected.");
    sensor.init();
    Serial.println("Keller Pressure Sensor not responding. Check wiring.");
    while (1);
  }
}

void loop()
{
  uint32_t enabled = false;

  //Loop to play with sampling x wakeups
  if (counter++ >= 0)
  {
    sensor.init();
    sensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)

    sensor.read();
    sensor.read();
    Serial.print("Pressure: "); 
    Serial.print(sensor.pressure());
    Serial.println(" mbar");
    counter = 0;
  }
  
  // Print date and time of RTC alarm trigger
  Serial.print("Alarm interrupt: "); printDateTime();

  // Enter deep sleep and await RTC alarm interrupt
  goToSleep();
}

// Print the RTC's current date and time
void printDateTime()
{
  rtc.getTime();

  Serial.printf("20%02d-%02d-%02d %02d:%02d:%02d.%03d\n",
          rtc.year, rtc.month, rtc.dayOfMonth,
          rtc.hour, rtc.minute, rtc.seconds, rtc.hundredths);
  Serial.println();
}

// Power down gracefully
void goToSleep()
{
  // Disable UART
  Serial.end();

  // Disable Wire
  Wire.end();

  // Disable ADC
  powerControlADC(false);

  // Force the peripherals off
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM0);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM1);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM2);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM3);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM4);    //Disabling this screws up the I2C
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_IOM5);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_ADC);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART0);
  am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_UART1);

  // Disable all pads (except UART TX/RX)
  for (int x = 0 ; x < 50 ; x++)
    am_hal_gpio_pinconfig(x, g_AM_HAL_GPIO_DISABLE);

  //Power down CACHE, flashand SRAM
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_ALL); // Turn off CACHE and flash
  am_hal_pwrctrl_memory_deepsleep_retain(AM_HAL_PWRCTRL_MEM_SRAM_384K); // Retain all SRAM (0.6 uA)
  
  // Keep the 32kHz clock running for RTC
  am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
  am_hal_stimer_config(AM_HAL_STIMER_XTAL_32KHZ);

  am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP); // Sleep forever

  // And we're back!
  wakeUp();
}

// Power up gracefully
void wakeUp()
{
  // Go back to using the main clock
  am_hal_stimer_config(AM_HAL_STIMER_CFG_CLEAR | AM_HAL_STIMER_CFG_FREEZE);
  am_hal_stimer_config(AM_HAL_STIMER_HFRC_3MHZ);

  // Power up SRAM, turn on entire Flash
  am_hal_pwrctrl_memory_deepsleep_powerdown(AM_HAL_PWRCTRL_MEM_MAX);

  // Renable UART0 pins
  am_hal_gpio_pinconfig(48, g_AM_BSP_GPIO_COM_UART_TX);
  am_hal_gpio_pinconfig(49, g_AM_BSP_GPIO_COM_UART_RX);

  // Renable power to UART0
  am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_UART0);

    // Renable I2C pins
  am_hal_gpio_pinconfig(39, g_AM_BSP_GPIO_IOM4_SCL);
  am_hal_gpio_pinconfig(40, g_AM_BSP_GPIO_IOM4_SDA);

  // Renable power to I2C (IOMaster4) peripheral
  am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_IOM4);

  sensor.init();
  sensor.setFluidDensity(997); // kg/m^3 (freshwater, 1029 for seawater)
  
  // Enable ADC
 //initializeADC();

  //Enable Wire
  Wire.begin();

  // Enable Serial
  Serial.begin(115200);
}

// Interrupt handler for the RTC
extern "C" void am_rtc_isr(void)
{
  // Clear the RTC alarm interrupt
  rtc.clearInterrupt();
}

Hi @CamS_884 ,

You may find some useful clues in the OpenLog Artemis firmware. Link below.

While I was testing that part of the code, I wrote this note:

  //With v2.1 of the core, very bad things happen if the IOMs are disabled.
  //We must leave them enabled: https://github.com/sparkfun/Arduino_Apollo3/issues/412

I hope this helps,
Paul

2 Likes

In Wakeup(), put Wire.begin() BEFORE sensor.init() as it tries to read from the device. (which now fails)

Not even sure that you need to repeat the sensor.init() and sensor.setFluidDensity() in Wakeup() as sleep does not make any change to the library.

2 Likes

Thanks for pointing me to that. Bit disappointing as leaving IOM4 enabled adds about 40uA to my sleep current. This seems a lot, so might still chase this up.

Good pickup thanks. However I’ve tried this and it doesn’t fix the issue. Yes I wouldn’t have expected to have to do those function calls after wakeup either. Just trying random stuff at this point.

Which Artemis board do you have ? Depending on the I2C pads the board is using, the driver library selects the right IOM. Maybe it is not IOM4 in your case.

Artemis Redboard

I’ve also tried just turning them all back on.

I’ve been trying to find the code that gets run to setup the peripheral, clocks, pins etc on initial startup to see if there’s any difference between what happens when the device runs setup() vs my wakeup(), but I’m new to Arduino environment and struggling to find my way around a bit.

I remember now, I solved this issue for me long time ago.
In Wire.begin () it will check that a link (master) to MBED is zero, if so it will create a new link. The problem is related to Wire.end() where it deletes the link / master BUT does NOT set it to zero. Next time around Wire.begin() will not create a new link. The fastest way around is in the Apollo3 packages library Apollo3/2.2.1/libraries /Wire/src/Wire.cpp change the IF function:

void arduino::MbedI2C::end() {
if (master != NULL) {
delete master;
}

...

to

void arduino::MbedI2C::end() {

  if (master != NULL) {
	master->deinit();
	delete master;
	master = NULL;
  }
...

You should have to reset the SDA and SCL either in this case.

Alternative to try : do NOT perform Wire.end() before sleep.

1 Like

I gave it a go removing wire.end(). This didn’t seem to work. So I gave it a go modifying that library code and get a build error:

C:\Users\sem023\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.2.1\libraries\Wire\src\Wire.cpp: In member function 'virtual void arduino::MbedI2C::end()':
C:\Users\sem023\AppData\Local\Arduino15\packages\SparkFun\hardware\apollo3\2.2.1\libraries\Wire\src\Wire.cpp:37:11: error: 'class mbed::I2C' has no member named 'deinit'
   master->deinit();

I took some time today to perform testing.

I have not worked for a long time on the Artemis and actually found that I have solved this issue before.

The root cause and solution is documented on [Observation] Disabling an IOM causes much badness on v2.1 · Issue #412 · sparkfun/Arduino_Apollo3 · GitHub

In short : in Wire.end() do NOTHING with Master, comment out the lines

void arduino::MbedI2C::end() {
//if (master != NULL) {
// delete master;
}

In Wire.begin() change

	if(!master){
		master = new mbed::I2C((PinName)_sda, (PinName)_scl);
		setClock(100000); //Default to 100kHz
	}

to

	if(!master){
		master = new mbed::I2C((PinName)_sda, (PinName)_scl);
	}
	setClock(100000); //Default to 100kHz	

The reasons are explained in detail in the github

The deinit() function is a function I had added to my version of the library, which includes other bug fixes and changes for my own use.