This class provides an API to assist with low power behaviour on an STM32F437 micro, as used on the u-blox C030 board. If you need to operate from battery for any significant period, or are mains powered and don't want to take the planet down with you, you should design your code with this in mind. This library uses the https://developer.mbed.org/users/Sissors/code/WakeUp/ library and so could be extended to support all of the MCUs that library supports.
Dependencies: WakeUp
Dependents: example-low-power-sleep aconnoCellularGnss
TESTS/unit_tests/default/main.cpp
- Committer:
- RobMeades
- Date:
- 2017-06-05
- Revision:
- 4:691e6b38fc54
- Parent:
- 1:4f2c412dc013
File content as of revision 4:691e6b38fc54:
#include "mbed.h" #include "greentea-client/test_env.h" #include "unity.h" #include "utest.h" #include "low_power.h" using namespace utest::v1; // ---------------------------------------------------------------- // COMPILE-TIME MACROS // ---------------------------------------------------------------- #ifndef NUM_RAND_ITERATIONS // The number of iterations of random input values in various tests #define NUM_RAND_ITERATIONS 1000 #endif // The duration to sleep for during a test #define SLEEP_DURATION_SECONDS 3 // Ticker period, should be set such that it will occur // within SLEEP_DURATION_SECONDS #define TICKER_PERIOD_US 1000000 // The number of Standby mode iterations to run #define NUM_STANDBY_ITERATIONS 2 // ---------------------------------------------------------------- // PRIVATE VARIABLES // ---------------------------------------------------------------- // An instance of low power static LowPower *gpLowPower = new LowPower(); // A ticker static Ticker gTicker; // A count of ticks static uint32_t gTickCount = 0; // The time stored in Backup SRAM // This has to be the first item in Backup SRAM as we check // its location below BACKUP_SRAM static time_t gStandbyTime; // The number of iterations of the Standby time test BACKUP_SRAM static uint32_t gNumStandby; // An item to take up the rest of Backup SRAM BACKUP_SRAM static char gBackupSram[4096 - sizeof (gStandbyTime) - sizeof (gNumStandby)]; // ---------------------------------------------------------------- // PRIVATE FUNCTIONS // ---------------------------------------------------------------- // A ticker function that increments gTickCount static void incTickCount(void) { gTickCount++; } // Check if the ticker has gone off static bool tickerExpired(void) { return gTickCount > 0; } // Set off the ticker and timer static void startTicker (void) { // Reset the counter gTickCount = 0; // Start the ticker gTicker.attach_us (&incTickCount, TICKER_PERIOD_US); // Make sure that it is running wait_ms ((TICKER_PERIOD_US / 1000) + 1); TEST_ASSERT_EQUAL_UINT32(1, gTickCount); // Reset the counter again gTickCount = 0; } // Stop the ticker static void stopTicker (void) { // Stop the ticker gTicker.detach(); // Reset counters gTickCount = 0; // Make sure that it has really stopped wait_ms ((TICKER_PERIOD_US / 1000) + 1); TEST_ASSERT_EQUAL_UINT32(0, gTickCount); } // ---------------------------------------------------------------- // TESTS // ---------------------------------------------------------------- // Test Stop mode void test_stop_mode() { time_t startTime; // Test a short stop startTime = time(NULL); startTicker(); gpLowPower->enterStop(SLEEP_DURATION_SECONDS * 1000); TEST_ASSERT_FALSE(tickerExpired()); TEST_ASSERT(time(NULL) - startTime >= SLEEP_DURATION_SECONDS - 1); // -1 for tolerance stopTicker(); // Do it again startTime = time(NULL); startTicker(); gpLowPower->enterStop(SLEEP_DURATION_SECONDS * 1000); TEST_ASSERT_FALSE(tickerExpired()); TEST_ASSERT(time(NULL) - startTime >= SLEEP_DURATION_SECONDS - 1); // -1 for tolerance stopTicker(); } // Test the number of user interrupts that have been enabled void test_interrupts_enabled() { int32_t userInterruptsEnabled; uint8_t list[NVIC_NUM_VECTORS - NVIC_USER_IRQ_OFFSET]; // Fill with a known value memset(&(list[0]), 0xff, sizeof (list)); // Check that we can just get the number back without any parameters userInterruptsEnabled = gpLowPower->numUserInterruptsEnabled(); #ifdef TARGET_STM TEST_ASSERT_EQUAL_INT32(2, userInterruptsEnabled); #endif // Now ask for a list, but only with one entry userInterruptsEnabled = gpLowPower->numUserInterruptsEnabled(&(list[0]), 1); // Check that the second entry is untouched TEST_ASSERT_EQUAL_UINT8(0xff, list[1]); #ifdef TARGET_STM // Check that two interrupts are enabled TEST_ASSERT_EQUAL_INT32(2, userInterruptsEnabled); // Check that the first entry is the RTC Alarm interrupt (used by enableStop()) TEST_ASSERT_EQUAL_UINT8(RTC_Alarm_IRQn, list[0]); #endif // Now ask for the full list userInterruptsEnabled = gpLowPower->numUserInterruptsEnabled(&(list[0]), sizeof (list)); #ifdef TARGET_STM // Check that two interrupts are enabled TEST_ASSERT_EQUAL_INT32(2, userInterruptsEnabled); // Check that the third entry is untouched TEST_ASSERT_EQUAL_UINT8(0xff, list[2]); // Check that the first entry is the RTC Alarm interrupt TEST_ASSERT_EQUAL_UINT8(RTC_Alarm_IRQn, list[0]); // Check that the second entry is the TIM5 interrupt (used for RTOS tick) TEST_ASSERT_EQUAL_UINT8(TIM5_IRQn, list[1]); #endif } // Test Standby mode void test_standby_mode() { #ifdef TARGET_STM // Check that the Backup SRAM array has been placed correctly TEST_ASSERT_EQUAL_UINT32 (0x40024000, &gStandbyTime); #endif // Fill backup SRAM with 0x42 memset (&(gBackupSram[0]), 0x42, sizeof (gBackupSram)); // Test that we succeeded in doing so for (uint32_t x = 0; x < sizeof (gBackupSram); x++) { TEST_ASSERT_EQUAL_INT8(0x42, gBackupSram[x]); } startTicker(); // Store the current time and the number of iterations in Backup SRAM also gNumStandby = 0; gStandbyTime = time(NULL); // Go into Standby mode gpLowPower->enterStandby(SLEEP_DURATION_SECONDS * 1000); // At the end of Standby mode the processor will reset and we will // come back again at main(). There we can check if (a) Backup SRAM // is still as we left it and (b) the right amount of time has expired // We should never get here TEST_ASSERT(false); } // ---------------------------------------------------------------- // TEST ENVIRONMENT // ---------------------------------------------------------------- // Setup the test environment utest::v1::status_t test_setup(const size_t number_of_cases) { // Setup Greentea with a timeout GREENTEA_SETUP(120, "default_auto"); return verbose_test_setup_handler(number_of_cases); } // Test cases // NOTE: the ability to enter low power states can be influenced by the debug chip // on the mbed board. It may not be possible to run these tests simply // with mbed test as usual as the process of downloading to the board causes the // debug chip to put the target MCU into the wrong state. A way around this is // to download the build, power off the board entirely, power it up again and // then run the tests without the download step, using: // // mbedhtrun --skip-flashing -p COMx:9600 // where x is replaced by the COM port where the board is attached. Case cases[] = { Case("Stop mode", test_stop_mode), // This must be run second as test_stop_mode is expected to enable some interrupts Case("Num user interrupts enabled", test_interrupts_enabled), #ifdef TARGET_STM // Standby mode doesn't work while debugging, it's just the way the HW is # ifdef DEBUG # error If you want to run the test suite in debug mode, comment out this line. # else // Standby mode is only implemented for ST micro cores // This must be the last test as it resets us back to main() Case("Standby mode", test_standby_mode) # endif #endif }; Specification specification(test_setup, cases); // ---------------------------------------------------------------- // MAIN // ---------------------------------------------------------------- int main() { bool success = true; #ifndef DEBUG gpLowPower->exitDebugMode(); #endif // If the RTC is running then we must have been // in the Standby mode test and have just come back // from reset, so check that Backup SRAM has the // expected contents and that we've slept for the // expected period if (time(NULL) > 0) { // Check that we are at least SLEEP_DURATION_SECONDS past the time // we entered Standby mode, which is stored in Backup SRAM printf ("Time: %d, recorded time %d.\n", (int) time(NULL), (int) gStandbyTime); if (time(NULL) - gStandbyTime < SLEEP_DURATION_SECONDS - 1) { // -1 for tolerance success = false; TEST_ASSERT(false); } // The rest should be the fill value for (uint32_t x = 0; (x < sizeof (gBackupSram)) && success; x++) { if (gBackupSram[x] != 0x42) { success = false; } } TEST_ASSERT(success); gNumStandby++; if (success && (gNumStandby < NUM_STANDBY_ITERATIONS)) { // Let printf leave the building wait_ms(100); // Go into Standby mode again gStandbyTime = time(NULL); gpLowPower->enterStandby(SLEEP_DURATION_SECONDS * 1000); // Again, we will come back from reset so should never get here TEST_ASSERT(false); } else { // Now we have to implement the end of the suite of tests manually printf ("{{__testcase_finish;Standby mode;%01d;%01d}}\n", success, !success); printf ("{{__testcase_summary;3;%01d}}\n", !success); if (success) { printf("{{end;success}}\n"); } else { printf("{{end;failure}}\n"); } printf ("{{__exit;%01d}}\n", !success); } } else { // Otherwise, we can just run the test suite success = !Harness::run(specification); } return success; } // End Of File