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 Bogdan Lazarescu

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).

hibernus.cpp

Committer:
BogdanL
Date:
2017-09-08
Revision:
0:f9a13d4b41f3

File content as of revision 0:f9a13d4b41f3:

/**    Hibernus Library
 *	   University of Southampton 2017
 *
 *		Open-source liberary that enable any of your Mbed project work with transient enegy sources.
 *		In order to use this library include the "hibernus.h" header file, and use the "Hibernus()" method at the beginning of you main funtion.
 *		For more details and example see the "main.cpp" exampe file, and the attached documnetation
 *
 *
 *     Released under the MIT License: http://mbed.org/license/mit
 */
#include "hibernus.h"
#include "mbed.h"
#include "config.h"
#ifdef LPC11U24
		#include "Driver_LPC11U24.h"
#endif
#ifdef KL05Z
		#include "Driver_KL05Z.h"
#endif

//11 variables stored at fixed addresses at the beginning of the RAM
#if SaveFlagsInFlash == 0
volatile unsigned int FLAG_1 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset)));// = Flash_Flags_Sector_Start;
volatile unsigned int FLAG_2 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0x4)));// = Flash_Flags_Sector_Start + 0x4;
volatile unsigned int FLAG_3 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0x8)));// = Flash_Flags_Sector_Start + 0x8;
volatile unsigned int FLAG_4 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0xC)));// = Flash_Flags_Sector_Start + 0xC;
#else
volatile unsigned int FLAG_1 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset))) = Flash_Flags_Sector_Start;
volatile unsigned int FLAG_2 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0x4))) = Flash_Flags_Sector_Start + 0x4;
volatile unsigned int FLAG_3 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0x8))) = Flash_Flags_Sector_Start + 0x8;
volatile unsigned int FLAG_4 __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+ 0xC))) = Flash_Flags_Sector_Start + 0xC;
#endif

volatile unsigned int CoreReg_SP __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x10)));
volatile unsigned int CoreReg_LR __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x14)));
volatile unsigned int CoreReg_PC __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x18)));
	
volatile unsigned int  n __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x1C)));   // Variable used for counting in loops (4 bytes) stored at RAM address (RamStart+0x54)
volatile unsigned char i __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x20)));   // Variable used for counting in loops (1 byte) stored at RAM address (RamStart+0x58)

volatile unsigned int *FLASH_ptr_4B __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x24)));    // Pointer that points to flash (4 bytes) stored at RAM address (RamStart+0x5C)
volatile unsigned int *RAM_ptr      __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x28)));    // Pointer that points to RAM (4 bytes) stored at RAM address (RamStart+0x60)
// Arrays used to store the contents of the peripheral registers
volatile unsigned int dum   __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x2C))) = 0x1234FFFF;
volatile unsigned int REG_Array_4B[No_Of_4B_Peripheral_Reg] 	__attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x30)));;			
volatile unsigned char REG_Array_1B[No_Of_1B_Peripheral_Reg] __attribute__((at(RAM_Start+Fixed_Add_Vars_Offset+0x30+4*No_Of_4B_Peripheral_Reg)));;

volatile unsigned int dummyread;      // Variable used for serialization

void Hibernus(){
	//pc1.printf("flags %x %x ",((unsigned int*)(RAM_Start +Fixed_Add_Vars_Offset+ Fixed_Add_Vars_Size))+(int)((RAM_Size-Fixed_Add_Vars_Offset-Fixed_Add_Vars_Size)/4),&dum);
	#if SaveFlagsInFlash == 0
		restore_flags();
	#endif
	
	if( isFlagSet(&FLAG_2) && isFlagSet(&FLAG_3) && isFlagSet(&FLAG_4))  // Enter this only after complete shut down
  {	
		erase_flags_memory();	
		setFlag(&FLAG_1);
		setFlag(&FLAG_4);
   }
		
	if(isFlagSet(&FLAG_1)||isFlagSet(&FLAG_2)||isFlagSet(&FLAG_3)||isFlagSet(&FLAG_4))
	{	
    __enable_irq();     // Enable interrupts
    Enter_LLS();        // Enter LLS mode
  }
		
	if(!isFlagSet(&FLAG_1)) // If *FLAG_1 is not already set (first time waking up)
  {
		erase_flags_memory();
		setFlag(&FLAG_2);
			
		#if HasInternalComparator == 1
				configure_VH_comparator_interrupt();
		#else
				configure_VH_gpio_interrupt();
		#endif
    __enable_irq();     // Enable interrupts
  }
  else{
		if(isFlagSet(&FLAG_4))
		{ 
		#if HasInternalComparator == 1
				configure_VH_comparator_interrupt();
		#else
				configure_VH_gpio_interrupt();
		#endif
			
			erase_flags_memory();
			setFlag(&FLAG_2);
			restore();
     }
  }
}

void Save_RAM_Regs(){  
	//Copy the peripheral registers to RAM
	for(n=0; n<No_Of_4B_Peripheral_Reg;n++){
		REG_Array_4B[n] = *(unsigned int*)REG_Addresses_4B[n];
	}
	for(n=0; n<No_Of_1B_Peripheral_Reg;n++){
		REG_Array_1B[n] = *(unsigned int*)REG_Addresses_1B[n];
	}
	
	//copy all the ram to flash
	copyRamToFlash();
}

void Restore_Regs(){ 
    //Restore peripheral registers from RAM, After the ram content was copied back from flash, after a restore
    for(n=0; n<No_Of_4B_Peripheral_Reg; n++){
        *(unsigned int*) REG_Addresses_4B[n] = REG_Array_4B[n];
    }
    
    for(i=0; i<No_Of_1B_Peripheral_Reg; i++){
        *(unsigned char*) REG_Addresses_1B[i] = REG_Array_1B[i];
    }
}

void hibernate(volatile unsigned int Rsp, volatile unsigned int Rlr, volatile unsigned int Rpc){
	
	if(isFlagSet(&FLAG_2)&& isFlagSet(&FLAG_3) && isFlagSet(&FLAG_4))  // Enter this only after complete shut down
	{
      erase_flags_memory(); 													// Erase flash sector used to save the flags 
			setFlag(&FLAG_1);
			setFlag(&FLAG_4);
   }
    
		if(isFlagSet(&FLAG_2))
    { 
			// Hibernate procedure           
      __disable_irq();     // Disable interrupts
    
			//save the core registers
			CoreReg_SP = Rsp;      // 40 for hibernate function 38 otherwsie
			CoreReg_LR = Rlr;      // 34 for hibernate function 2C otherwise
			CoreReg_PC = Rpc;      // 38 for hibernate function 30 otherwise
        
      
			#if HasInternalComparator == 1
				Save_RAM_Regs();

				setFlag(&FLAG_3);
				setFlag(&FLAG_4);
			
				Comparator_Setup();
				configure_VH_comparator_interrupt();
			#else
			setFlag(getFlag_3());
			setFlag(getFlag_4());
			
      Save_RAM_Regs();
				configure_VH_gpio_interrupt();
			#endif
                                        
      __enable_irq();     // Enable interrupts
                                
      Enter_LLS();        // Enter LLS mode
		}
}

void restore(){
	// Restore procedure
	__disable_irq();     // Disable interrupts
 
	erase_flags_memory();
	setFlag(&FLAG_2); 

	//Restore peripheral registers	
	Restore_Regs();
  
	//Restore RAM
  FLASH_ptr_4B = (unsigned int*) (flash_ramSection_start);       
  RAM_ptr      = (unsigned int*) (RAM_Start);
	
	//Copy RAM until where the pointers and loop variables are stored
	//divide it by 4 because the copy is done word by word but byte by byte
  for(n=0; n<(Fixed_Add_Vars_Offset/4); n++){                                
		*(RAM_ptr + n) = *(FLASH_ptr_4B + n);
	}	
	
  //skip the Core reg and fixed address variables
	FLASH_ptr_4B = (unsigned int*)(flash_ramSection_start +Fixed_Add_Vars_Offset+ Fixed_Add_Vars_Size);
	RAM_ptr = (unsigned int*)(RAM_Start +Fixed_Add_Vars_Offset+ Fixed_Add_Vars_Size); 
	
  //copy the rest of the RAM
	//(RAM_Size-Fixed_Add_Vars_Offset-Fixed_Add_Vars_Size) is the siz eof the rest of the ram that have to be copied
	//divide it by 4 because the copy is done word by word but byte by byte
	 for(n=1; n<(RAM_Size-Fixed_Add_Vars_Offset-Fixed_Add_Vars_Size)/4; n++){                                
		*(RAM_ptr + n) = *(FLASH_ptr_4B + n);
  }
	 
  //copy the 3 core registers from flash o ram
	CoreReg_SP=*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset+0x10);
	CoreReg_LR=*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset+0x14);
	CoreReg_PC=*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset+0x18);
	
	#if SaveFlagsInFlash == 1			//if the flags are stored in their own flash sector, their default value(the flags address in their flash sector) have to be restored
																//otherwise, their value has to be unchanged, there is no need for restore
		FLAG_1 =*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset);
		FLAG_2 =*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset +0x4);
		FLAG_3 =*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset +0x8);
		FLAG_4 =*(unsigned int*)(flash_ramSection_start+Fixed_Add_Vars_Offset +0xC);
	#else
		configure_VH_gpio_interrupt();
	#endif	
                    
  __enable_irq();     // Enable interrupts 
                        
  asm_restore();     	 // Restore the SP                        
  _LR = CoreReg_LR;    // Restore the LR                       
  _PC = CoreReg_PC;    // Restore the PC
}

//use to check if you recover from a sleep without power loss
//is so, don't restore, and just set the VH interrupt
void recovery_no_power_loss(){
	
		if(isFlagSet(getFlag_3())){   // Enter after hibernation with no power loss                        
			#if HasInternalComparator == 1
				configure_VH_comparator_interrupt();
			#else
				configure_VH_gpio_interrupt();
			#endif
			
			erase_flags_memory();  // Erase flash sector used to save the flags 
			setFlag(getFlag_2());
			
      __enable_irq();     // Enable interrupts
    }
}
extern "C" volatile unsigned int* getFlag_1(){
	return &FLAG_1;
}

extern "C" volatile unsigned int* getFlag_2(){
	return &FLAG_2;
}

extern "C" volatile unsigned int* getFlag_3(){
	return &FLAG_3;
}

extern "C" volatile unsigned int* getFlag_4(){
	return &FLAG_4;
}