This library allows any user to use their Mbed project with a transient energy source( e.g. windturbine, solar power).
Dependencies: mbed
Fork of Hibernus by
This library allows any user to use their Mbed project with a transient energy source( e.g. windturbine, solar power). You can do this by simply import the "hibernus.h" header file, and call the "Hibernus()" method at the beginning of you main program. In case of a power loss the state of your programm will be saved to the nonvolatile memory and it will be resumed from the same point as soon as there is enough power for the board to run properly. If the power drops down, the internal capacitance of the system is used to save a snapshot of your program into the flash memory, and the board goes in a low power mode (sleep or deepsleep). In order to detect a power loss, the library uses an analog comparator which can be internal (eg Freescale KL 05Z has an internal comparator which can be used ), or external (on LPC11U24 there is no internal comparator) via a GPIO interrupt. For more details see the code comments and the attached example main.cpp file.
This library use "IAP" library, in order to write the required data to the flash nonvolatile memory.
The library can be easily adapted to work with other boards, from different manufactures, which have support for Mbed. In order to adapt this library and use it on your board, the write to flash methods have to be changed. Some changes listed below are required because of the platform dependent parameters of each board. All required changes have to be applied to the "config.h" and "config.cpp" files.
- The "erase_flags_memory()", "copyRamToFlash()", "restore_flags()", "setFlag()" and " isFlagSet()" methods have to be modified in order to use the right Flash IAP of your board.
- The wake up and hibernate interrupts have to be modified in order to be trigghered when the voltage drops down or rise. If you use an internal comparator, it should trigger and interrupt whenever the power drops(e.g see CMP0_IRQHandler() method writtend for KL05Z at https://developer.mbed.org/users/BogdanL/code/Hibernus-KL05Z/ ) . At that time a snapshot have to be saved and the board sent to sleep. Another interrupt have to be triggered when the power comes back(see "LLW_IRQHandler()" at https://developer.mbed.org/users/BogdanL/code/Hibernus-KL05Z ) . This have to wake up the board and resume de computation. In you use an external comparator two GPIO interrupts are used. One of them (for LPC11U24 see "FLEX_INT1_IRQHandler()" ) is used to save the snapshot when the power drops down, and the other one (for LPC11U24 see "FLEX_INT0_IRQHandler()" ) is used to wake up the board.
- For each board, the right Sleep mode have to be chosen. Also the interrups and the comparator have to be properly set, in order to be triggered as desired. For a good example see "configure_VR_gpio_interrupt()" and "configure_VH_gpio_interrupt()" that are used to set up the Restore and Hibernate interrupts on LPC11U24 board, that uses an external comparator.
- In the "config.h" file, the two arrays, "REG_Addresses_4B[]" and "REG_Addresses_4B[]" have to be populated with the addresses of the 1 Byte and 4Bytes peripheral registers that are used by your project. Different boards have different modules that use different peripheral registers. The addresses of the registers can be found in the Reference Manual of each board, and will be used in order to save and later restore the content of the registers. Also the number of used registers, "No_Of_4B_Peripheral_Reg", No_Of_1B_Peripheral_Reg, have to be updated with the correct number of used registers.
- At the top of "config.h" header file, specific board parameters have to be fixed: RAM start address(RAM_1_Address), Flash start address(FLASH_Start), RAM size(RAM_Size), Flash size(flash_Size) and the flash sector size(sector_Size).
Drivers/Driver_KL05Z.cpp@0:f9a13d4b41f3, 2017-09-08 (annotated)
- Committer:
- BogdanL
- Date:
- Fri Sep 08 18:01:22 2017 +0000
- Revision:
- 0:f9a13d4b41f3
First Version of Hibernus Library;
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
BogdanL | 0:f9a13d4b41f3 | 1 | #include "config.h" |
BogdanL | 0:f9a13d4b41f3 | 2 | #ifdef KL05Z |
BogdanL | 0:f9a13d4b41f3 | 3 | #include "Driver_KL05Z.h" |
BogdanL | 0:f9a13d4b41f3 | 4 | #include "hibernus.h" |
BogdanL | 0:f9a13d4b41f3 | 5 | Serial pr(USBTX, USBRX); |
BogdanL | 0:f9a13d4b41f3 | 6 | volatile unsigned int dummyread1; // Variable used for serialization |
BogdanL | 0:f9a13d4b41f3 | 7 | |
BogdanL | 0:f9a13d4b41f3 | 8 | //inerrupt triggered by comparator whenever the power drops under the treshold value |
BogdanL | 0:f9a13d4b41f3 | 9 | extern "C" void CMP0_IRQHandler() //To make sure the compiler sees handler, need to declare it as extern (https://developer.mbed.org/forum/mbed/topic/419/?page=1#comment-2126) |
BogdanL | 0:f9a13d4b41f3 | 10 | { |
BogdanL | 0:f9a13d4b41f3 | 11 | CMP0->SCR |= CMP_SCR_CFF_MASK | CMP_SCR_CFR_MASK; // Clear CMP0 flags |
BogdanL | 0:f9a13d4b41f3 | 12 | dummyread1 = CMP0->SCR; // Read back for serialization |
BogdanL | 0:f9a13d4b41f3 | 13 | |
BogdanL | 0:f9a13d4b41f3 | 14 | //call hibernate procedure directly with the three parameters (don't store them in a variable and then call the method with them, |
BogdanL | 0:f9a13d4b41f3 | 15 | //the store procedure changes the stackpointer and the program counter) |
BogdanL | 0:f9a13d4b41f3 | 16 | // pram1 = StackPointer = SP, param2 = LinkRegister = LR, param3 = ProgramCounter = PC |
BogdanL | 0:f9a13d4b41f3 | 17 | // call hibernate(param1, param2, param3) then the voltage drops under the low(hibernate) treshold |
BogdanL | 0:f9a13d4b41f3 | 18 | hibernate((_SP+0x28),*((unsigned int*)(_SP+0x1C)),*((unsigned int*)(_SP+0x20))); |
BogdanL | 0:f9a13d4b41f3 | 19 | |
BogdanL | 0:f9a13d4b41f3 | 20 | } |
BogdanL | 0:f9a13d4b41f3 | 21 | |
BogdanL | 0:f9a13d4b41f3 | 22 | extern "C" void LLW_IRQHandler(){ //interrupt triggered to wake up the board from sleep mode |
BogdanL | 0:f9a13d4b41f3 | 23 | |
BogdanL | 0:f9a13d4b41f3 | 24 | CMP0->SCR |= CMP_SCR_CFF_MASK | CMP_SCR_CFR_MASK; // Clear CMP0 flags |
BogdanL | 0:f9a13d4b41f3 | 25 | dummyread1 = CMP0->SCR; // Read back for serialization |
BogdanL | 0:f9a13d4b41f3 | 26 | NVIC_ClearPendingIRQ(CMP0_IRQn); // Clear pending CMP0 interrupt so that the CMP_IRQ is not entered |
BogdanL | 0:f9a13d4b41f3 | 27 | |
BogdanL | 0:f9a13d4b41f3 | 28 | recovery_no_power_loss(); //check if you are recovering after no power loss, and if so, configure VH interrupt |
BogdanL | 0:f9a13d4b41f3 | 29 | } |
BogdanL | 0:f9a13d4b41f3 | 30 | |
BogdanL | 0:f9a13d4b41f3 | 31 | void Comparator_Setup(){ |
BogdanL | 0:f9a13d4b41f3 | 32 | |
BogdanL | 0:f9a13d4b41f3 | 33 | NVIC_SetPriority(CMP0_IRQn, 1); // Lower the CMP0 interrupt priority from 0 to 1 (smaller number = higher priority) |
BogdanL | 0:f9a13d4b41f3 | 34 | NVIC_EnableIRQ(CMP0_IRQn); // Enable CMP0 interrupts in NVIC |
BogdanL | 0:f9a13d4b41f3 | 35 | |
BogdanL | 0:f9a13d4b41f3 | 36 | SIM->SCGC4 |= SIM_SCGC4_CMP_MASK; // Enable comparator module clock |
BogdanL | 0:f9a13d4b41f3 | 37 | LLWU->ME |= LLWU_ME_WUME1_MASK; // Enable CMP0 as a LLWU source |
BogdanL | 0:f9a13d4b41f3 | 38 | PMC->REGSC |= PMC_REGSC_BGEN_MASK | // Allow bangap buffer in low power operation |
BogdanL | 0:f9a13d4b41f3 | 39 | PMC_REGSC_BGBE_MASK; // Enable bandgap buffer for +1V reference (CMP0_IN6) |
BogdanL | 0:f9a13d4b41f3 | 40 | |
BogdanL | 0:f9a13d4b41f3 | 41 | // Comparator in sampled, filtered mode 4B |
BogdanL | 0:f9a13d4b41f3 | 42 | CMP0->CR0 = 0x22; // Hysteresis level 2 (20mV) and 2 consecutive filter samples must agree |
BogdanL | 0:f9a13d4b41f3 | 43 | CMP0->CR1 = 0x00; // Low-speed compare, non-inverted output, use filtered output and disable comparator output pin PTA2 |
BogdanL | 0:f9a13d4b41f3 | 44 | CMP0->FPR = 0x01; // Filter sample period = 1 bus clock cycle |
BogdanL | 0:f9a13d4b41f3 | 45 | CMP0->SCR = 0x06; // Disable all interrupts and clear flags |
BogdanL | 0:f9a13d4b41f3 | 46 | CMP0->DACCR = 0x40; // Disable DAC, Vdd is 6-bit reference, threshold set to zero |
BogdanL | 0:f9a13d4b41f3 | 47 | CMP0->MUXCR = 0x3E; // CMP0_IN7 (DAC) to V+ channel and CMP0_IN6 (+1V bandgap) to V- 3E |
BogdanL | 0:f9a13d4b41f3 | 48 | } |
BogdanL | 0:f9a13d4b41f3 | 49 | |
BogdanL | 0:f9a13d4b41f3 | 50 | void Enter_LLS(){ |
BogdanL | 0:f9a13d4b41f3 | 51 | |
BogdanL | 0:f9a13d4b41f3 | 52 | Comparator_Setup(); |
BogdanL | 0:f9a13d4b41f3 | 53 | CMP0->DACCR = 0xDA; // Enable DAC, Vdd is 6-bit reference, threshold set to P3V3 = +2.37V (V_R) |
BogdanL | 0:f9a13d4b41f3 | 54 | CMP0->SCR = 0x16; // Enable rising edge interrupt and clear flags |
BogdanL | 0:f9a13d4b41f3 | 55 | CMP0->CR1 |= CMP_CR1_EN_MASK; // Enable comparator module |
BogdanL | 0:f9a13d4b41f3 | 56 | |
BogdanL | 0:f9a13d4b41f3 | 57 | NVIC_EnableIRQ(LLW_IRQn); // Enable LLW interrupts in NVIC |
BogdanL | 0:f9a13d4b41f3 | 58 | MCG->C6 &= ~(MCG_C6_CME_MASK); // DIsable all clock monitors |
BogdanL | 0:f9a13d4b41f3 | 59 | SCB->SCR = 1<<SCB_SCR_SLEEPDEEP_Pos; // Set the SLEEPDEEP bit for stop mode |
BogdanL | 0:f9a13d4b41f3 | 60 | |
BogdanL | 0:f9a13d4b41f3 | 61 | SMC->PMPROT = SMC_PMPROT_ALLS_MASK; // Allow LLS power modes |
BogdanL | 0:f9a13d4b41f3 | 62 | SMC->PMCTRL &= ~(SMC_PMCTRL_STOPM_MASK); // Serialisation |
BogdanL | 0:f9a13d4b41f3 | 63 | SMC->PMCTRL = SMC_PMCTRL_STOPM(0x3); // Select LLS as desired power mode |
BogdanL | 0:f9a13d4b41f3 | 64 | dummyread1 = SMC->PMCTRL; // Read back for serialisation |
BogdanL | 0:f9a13d4b41f3 | 65 | |
BogdanL | 0:f9a13d4b41f3 | 66 | __WFI(); // Stop executing instructions and enter LLS (wait for interrupt) |
BogdanL | 0:f9a13d4b41f3 | 67 | } |
BogdanL | 0:f9a13d4b41f3 | 68 | |
BogdanL | 0:f9a13d4b41f3 | 69 | void configure_VH_comparator_interrupt(){ |
BogdanL | 0:f9a13d4b41f3 | 70 | Comparator_Setup(); |
BogdanL | 0:f9a13d4b41f3 | 71 | CMP0->DACCR = 0xDC; // Enable DAC, Vdd is 6-bit reference, threshold set to P3V3 = +2.21V (V_H) |
BogdanL | 0:f9a13d4b41f3 | 72 | CMP0->SCR = 0x0E; // Enable falling edge interrupt and clear flags |
BogdanL | 0:f9a13d4b41f3 | 73 | CMP0->CR1 |= CMP_CR1_EN_MASK; // Enable comparator module |
BogdanL | 0:f9a13d4b41f3 | 74 | } |
BogdanL | 0:f9a13d4b41f3 | 75 | |
BogdanL | 0:f9a13d4b41f3 | 76 | void configure_VR_comparator_interrupt(){ |
BogdanL | 0:f9a13d4b41f3 | 77 | CMP0->DACCR = 0xDA; // Enable DAC, Vdd is 6-bit reference, threshold set to P3V3 = +2.37V (V_R) |
BogdanL | 0:f9a13d4b41f3 | 78 | CMP0->SCR = 0x16; // Enable all interrupts and clear flags |
BogdanL | 0:f9a13d4b41f3 | 79 | CMP0->CR1 |= CMP_CR1_EN_MASK; // Enable comparator module |
BogdanL | 0:f9a13d4b41f3 | 80 | } |
BogdanL | 0:f9a13d4b41f3 | 81 | |
BogdanL | 0:f9a13d4b41f3 | 82 | void setFlag(volatile unsigned int* add){ |
BogdanL | 0:f9a13d4b41f3 | 83 | const unsigned int set = Flag_set; |
BogdanL | 0:f9a13d4b41f3 | 84 | program_flash(*add,(char*)&set,4); //if the flags are stored in flash, use this line |
BogdanL | 0:f9a13d4b41f3 | 85 | } |
BogdanL | 0:f9a13d4b41f3 | 86 | |
BogdanL | 0:f9a13d4b41f3 | 87 | bool isFlagSet(volatile unsigned int* add){ |
BogdanL | 0:f9a13d4b41f3 | 88 | if( *(unsigned int *)(*add) == Flag_set) return true; |
BogdanL | 0:f9a13d4b41f3 | 89 | return false; |
BogdanL | 0:f9a13d4b41f3 | 90 | } |
BogdanL | 0:f9a13d4b41f3 | 91 | |
BogdanL | 0:f9a13d4b41f3 | 92 | void erase_flags_memory(){ |
BogdanL | 0:f9a13d4b41f3 | 93 | if(!SaveFlagsInFlash) { //if the flags are stored in RAM, their value have to be clanged to the "erased" value ("Flag_erase") |
BogdanL | 0:f9a13d4b41f3 | 94 | *getFlag_1() = Flag_erase; |
BogdanL | 0:f9a13d4b41f3 | 95 | *getFlag_2() = Flag_erase; |
BogdanL | 0:f9a13d4b41f3 | 96 | *getFlag_3() = Flag_erase; |
BogdanL | 0:f9a13d4b41f3 | 97 | *getFlag_4() = Flag_erase; |
BogdanL | 0:f9a13d4b41f3 | 98 | }else{ //if the flags are stored in Flash, their secvtor have to be erased |
BogdanL | 0:f9a13d4b41f3 | 99 | erase_sector(Flash_Flags_Sector_Start); |
BogdanL | 0:f9a13d4b41f3 | 100 | } |
BogdanL | 0:f9a13d4b41f3 | 101 | } |
BogdanL | 0:f9a13d4b41f3 | 102 | //erase the flash memory needed to save the whole RAM content |
BogdanL | 0:f9a13d4b41f3 | 103 | void erase_flash_secors_for_RAM(){ |
BogdanL | 0:f9a13d4b41f3 | 104 | int i; |
BogdanL | 0:f9a13d4b41f3 | 105 | for(i = 2; i<= 2+ramToFlash_sectors_number; i++) //start the erase from the second last block, the first is used for flags |
BogdanL | 0:f9a13d4b41f3 | 106 | erase_sector(flash_end-i*sector_Size); |
BogdanL | 0:f9a13d4b41f3 | 107 | } |
BogdanL | 0:f9a13d4b41f3 | 108 | |
BogdanL | 0:f9a13d4b41f3 | 109 | void copyRamToFlash(){ |
BogdanL | 0:f9a13d4b41f3 | 110 | // Erase flash sectors o save the RAM content |
BogdanL | 0:f9a13d4b41f3 | 111 | erase_flash_secors_for_RAM(); |
BogdanL | 0:f9a13d4b41f3 | 112 | program_flash(flash_ramSection_start, (char*) RAM_Start, RAM_Size); // Copy all the RAM to flash |
BogdanL | 0:f9a13d4b41f3 | 113 | } |
BogdanL | 0:f9a13d4b41f3 | 114 | #endif |