Example program demonstrating proper powering down of Telit radio and putting the processor into STOP mode.

Dependencies:   WakeUp mbed mtsas SpiFlash25

Fork of Dragonfly_Cellular_Ping_Example by MultiTech

Revision:
3:90cf39b6ed0d
Parent:
2:0288328818cb
Child:
4:91a322a2ec86
--- a/main.cpp	Fri Feb 26 16:47:54 2016 +0000
+++ b/main.cpp	Mon Feb 29 14:53:33 2016 +0000
@@ -1,18 +1,60 @@
-/** Dragonfly Cellular Ping Example
- * Configures the cellular radio, brings up the cellular link, and pings Google.
+/** Dragonfly Cellular Low Power Example
+ * This program will run forever doing the following
+ *      bring up the cellular link
+ *      ping Google's DNS server
+ *      properly power off the radio
+ *      go to sleep
+ *
+ * There are two methods for properly powering off the cellular radio
+ *  1) Software shutdown
+ *      - use Cellular::sendBasicCommand() to issue AT#SH to the radio and make sure it is successful
+ *      - poll vdd1_8_mon - when the radio powers off this line will go to 0 (at least 10s after AT#SH)
+ *      - drop the radio_reset line to 0 (pin PC_13) so the radio control chip doesn't attempt to restart the radio
+ *      - (optional) drop the radio_pwr line to 0
+ *          + this eliminates all current draw from the radio when it is powered off but increases the time it takes to reconnect to the network
+ *  2) Hardware shutdown
+ *      - drop the radio_reset line to 0 (pin PC_13) so the radio control chip doesn't attempt to restart the radio
+ *      - poll vdd1_8_mon - when the radio powers off this line will go to 0 (at least 10s after radio_reset was dropped)
+ *      - (optional) drop the radio_pwr line to 0
+ *          + this eliminates all current draw from the radio when it is powered off but increases the time it takes to reconnect to the network
  *
  * NOTE: This example changes the baud rate of the debug port to 115200 baud!
  */
 
 #include "mbed.h"
 #include "mtsas.h"
+#include "WakeUp.h"
 
 // This line controls the regulator's battery charger.
 // BC_NCE = 0 enables the battery charger
 // BC_NCE = 1 disables the battery charger
 DigitalOut bc_nce(PB_2);
 
+// These lines are used to control the maximum current draw of the regulator
+//        | 100mA mode | 500mA mode| 1A mode  |
+// --------------------------------------------
+// BC_EN1 |      0     |     1     |    0     |
+// BC_EN2 |      0     |     0     |    1     |
+// --------------------------------------------
+// Default is 1A mode
+DigitalOut bc_en1(PC_14, 0);
+DigitalOut bc_en2(PC_15, 1);
+
+// This line is used to reset or perform a HW shutdown the radio
+// Set it low and keep it low to perform a HW shutdown
+// Toggle it low and then high again to reset the radio
+DigitalOut radio_reset(PC_13, 1);
+
+// This line controls power to the radio
+// It should only be dropped after the radio has been powered off via the radio_reset line or AT#SHDN command to reduce all current consumption from the radio
+DigitalOut radio_pwr(PC_3, 1);
+
+// This line is 1 when the radio has sufficient power
+// It goes to 0 after the radio has successfully shut down
+DigitalIn vdd1_8_mon(PC_5);
+
 bool init_mtsas();
+void enterStopMode(const uint32_t interval);
 
 // The MTSSerialFlowControl object represents the physical serial link between the processor and the cellular radio.
 mts::MTSSerialFlowControl* io;
@@ -28,6 +70,9 @@
     // Disable the battery charger unless a battery is attached.
     bc_nce = 1;
     
+    uint32_t sleep_time = 10;
+    Timer tmr;
+    
     // Change the baud rate of the debug port from the default 9600 to 115200.
     Serial debug(USBTX, USBRX);
     debug.baud(115200);
@@ -35,8 +80,6 @@
     //Sets the log level to INFO, higher log levels produce more log output.
     //Possible levels: NONE, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
     mts::MTSLog::setLogLevel(mts::MTSLog::INFO_LEVEL);
-    
-    logInfo("initializing cellular radio");
     radio_ok = init_mtsas();
     if (! radio_ok) {
         while (true) {
@@ -45,26 +88,64 @@
         }
     }
     
-    logInfo("setting APN");
-    if (radio->setApn(apn) != MTS_SUCCESS)
-        logError("failed to set APN to \"%s\"", apn);
+    while (true) {
+        logInfo("changing regulator to 1A mode");
+        bc_en1 = 0;
+        bc_en2 = 1;
+        logInfo("powering on radio");
+        radio_pwr = 1;
+        radio_reset = 1;
+        int tries = 0;
+        
+        while (radio->sendBasicCommand("AT", 1000) != MTS_SUCCESS) {
+            tries++;
+            if (tries % 25 == 0) {
+                logWarning("no response from radio after 25 tries (total tries %d) - resetting radio", tries);
+                radio_reset = 0;
+                wait_ms(10);
+                radio_reset = 1;
+            }
+            wait(1);
+        }
         
-    logInfo("bringing up the link");
-    if (! radio->connect()) {
-        logError("failed to bring up the link");
-    } else {
-        for (int i = 0; i < 10; i++) {
-            logInfo("pinging");
-            if (! radio->ping("www.google.com"))
-                logError("failed to ping");
-            else
-                logInfo("ping succeeded");
+        logInfo("setting APN");
+        if (radio->setApn(apn) != MTS_SUCCESS)
+            logError("failed to set APN to \"%s\"", apn);
+        logInfo("bringing up the link");
+        if (! radio->connect()) {
+            logError("failed to bring up the link");
+        } else {
+            for (int i = 0; i < 10; i++) {
+                logInfo("pinging");
+                if (! radio->ping("www.google.com"))
+                    logError("failed to ping");
+                else
+                    logInfo("ping succeeded");
+            }
+            logInfo("finished - bringing down link");
+            radio->disconnect();
         }
+        
+        // power down the radio
+        logInfo("powering down radio");
+        radio_reset = 0;
+        logInfo("waiting for vdd1_8_mon to go low");
+        tmr.reset();
+        tmr.start();
+        // wait up to 30s for the radio to shut down
+        while (vdd1_8_mon != 0 && tmr.read() < 30)
+            wait_ms(500);
+        tmr.stop();
+        logInfo("radio powered off after %fs", tmr.read());
+        radio_pwr = 0;
+        logInfo("putting regulator in 100mA mode");
+        bc_en1 = 0;
+        bc_en2 = 0;
+        
+        logInfo("going to sleep for %lu seconds", sleep_time);
+        enterStopMode(sleep_time);
     }
     
-    logInfo("finished - bringing down link");
-    radio->disconnect();
-    
     return 0;
 }
 
@@ -84,3 +165,60 @@
     
     return true;
 }
+
+#define RTC_ALARMA_DISABLE() do { \
+    RTC->ISR &= ~RTC_ISR_ALRAF; \
+    RTC->WPR = 0xCA; \
+    RTC->WPR = 0x53; \
+    RTC->CR &= ~(RTC_CR_ALRAE | RTC_CR_ALRAIE); \
+    RTC->WPR = 0xFF; \
+} while (0)
+
+void enterStopMode(const uint32_t interval) {
+    // disable ADC - it can consume power in stop mode
+    ADC1->CR2 &= ~ADC_CR2_ADON;
+
+    // enable debugging during stop mode
+    HAL_EnableDBGStopMode();
+
+    // put regulators into low-power mode
+    HAL_PWREx_EnableMainRegulatorLowVoltage();
+    HAL_PWREx_EnableLowRegulatorLowVoltage();
+
+    // power down flash
+    HAL_PWREx_EnableFlashPowerDown();
+    
+    // configure RTC Alarm A to wake the device up
+    WakeUp::set(interval);
+
+    // make sure wakeup flag is cleared
+    PWR->CR |= PWR_CR_CWUF;
+
+    logInfo("entering stop mode %08x", RTC->ISR);
+    fflush(stdout);
+
+    // enter stop mode - don't execute past here until woken up
+    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
+
+    // HSI (High Speed Internal) oscillator is selected when coming out of stop mode - set up clocking
+    SetSysClock();
+
+    // disable RTC alarm A and RTC alarm A interrupt
+    RTC_ALARMA_DISABLE();
+
+    // clear wakeup flag in power control register
+    PWR->CR |= PWR_CR_CWUF;
+
+    // put regulators back into to full-power mode
+    HAL_PWREx_DisableMainRegulatorLowVoltage();
+    HAL_PWREx_DisableLowRegulatorLowVoltage();
+
+    // turn the flash back on
+    HAL_PWREx_DisableFlashPowerDown();
+
+    // enable the ADC
+    ADC1->CR2 |= ADC_CR2_ADON;
+
+    // disable debugging during stop mode
+    HAL_DisableDBGStopMode();
+}