Search This Blog

Mar 14, 2012

SiM3U1xx-power-mode-9



Introduction

  SiM3U1xx and SiM3C1xx devices have several power modes: Normal, Power Mode 1; Power Mode 2, Power Mode 3 Fast Wake, Power Mode 3, Power Mode 9(PM9). We will discuss PM9 in this documents.

Function Description

  In Power Mode 9, the core and all peripherals are halted, all clocks are stopped, and the pins and peripherals are set to a lower power mode. In addition, standard RAM contents are not preserved, though retention RAM contents are still available after exiting the power mode. This mode provides the lowest power consumption for the device, but requires an appropriate reset to exit. The available reset sources to wake from PM9 are controlled by the Power Management Unit (PMU).

To enter this mode, firmware should first set the PMSEL bit in the RSTSRC_CONFIG register to 1, and write the SLEEPDEEP bit in the ARM System Control Register. Firmware must then execute a WFI or WFE instruction. The core will remain in PM9 until an enabled reset source occurs.
The reset wake for PM9 can be sourced from pins (Pin Wake), the Low Power Timer, Comparator 0, RTC0 Alarms (0, 1, or 2), RTC0 Fail, or the Reset Pin (RESET). In most cases, the corresponding interrupt enable must be set in the module in order for an event to be a wakeup source. The Comparator module is the exception and the wakeup event will occur even if the interrupt is disabled. These wakeup sources (except for the reset pin) can also be optionally used to reset RTC0 or the Low Power Timer while the device remains in PM9.

Firmware can check the PM9EF bit during the initialization sequence to determine if the device reset because of a wake from Power Mode 9. If the device did reset because of a wake from PM9, firmware must clear the bits keeping the peripheral and pin interfaces in a lower power state (PERILPEN and PINLPEN), and the WAKESTATUS register provides status flags to indicate the wakeup source. The WAKESTATUS register can be cleared by writing 0 to the WAKECLR bit.

Code Implementation

   We made little modification in “Sleep” example code to realize PM9.
void gPB_enter_default_config()
{
   // START APB CLK AND ENABLE SW PRINF
   SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG0_PB0);
   SI32_PBSTD_A_set_pins_push_pull_output (SI32_PBSTD_1, 0x00000008);
   // ENABLE CROSSBAR 1. WE WILL DRIVE LED's, SAMPLE BUTTONS, AND USE UART ON XBAR1
   SI32_PBCFG_A_enable_crossbar_1(SI32_PBCFG_0);
   // ENABLE LED DRIVERS (P2.10, P2.11) and turn on P2.10
   SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_2, 0x00000C00);
   SI32_PBSTD_A_write_pins_low(SI32_PBSTD_2, 0x400);
   // ENABLE SWITCH SENSING (P2.10, P2.11)
   SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_2, 0x00000300);
   // RTC PORT SETUP
   // RTC PINS TO ANALOG (PB0.9, PB0.10)
   SI32_PBSTD_A_set_pins_analog(SI32_PBSTD_0, 0x00000600);
   // BRING OUT RTC0 (PB2.7) (push-pull, P2.0-P2.6 skiped, RTC0 output enabled in crossbar)
   SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_2, 0x00000080);
   SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_2, 0x0000007F);
   SI32_PBCFG_A_enable_xbar1_peripherals(SI32_PBCFG_0, SI32_PBCFG_A_XBAR1_RTC0EN);
   // UART PINS TO PROPER CONFIG (TX = PB1.12, RX = PB1.13)
   SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_1, 0x0001000);   
   SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_1, 0x00002000);
   SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_0, 0x0000FFFF);
   SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_1, 0x00000FFF);
   // BRING OUT UART
   SI32_PBCFG_A_enable_xbar0h_peripherals(SI32_PBCFG_0, SI32_PBCFG_A_XBAR0H_UART0EN);
}
2.       Added code to enter PM9. And added code to check reset source and wakeup source. Handle with exiting from PM9. (gPMU.c)

  // CHECK FOR POR
  if (SI32_PMU_A_is_power_on_reset_event_flag_set(SI32_PMU_0))
  {
     // IMMEDIATLY CLEAR POR SO ANY UNEXPECTED RESET WILL BE PROPERLY DECODED
     SI32_PMU_A_clear_por_flag(SI32_PMU_0);

     // SET VARIABLES
     reset_source = SI32_POWER_ON_RESET;
     wakeup_source = SI32_NO_WAKEUP;

     // APP BUILDER INSERTS USER FUNCTON OR DEFAULT FUNCTION HERE
     gModes_enter_my_default_mode();

     SI32_PMU_A_clear_pin_level_shifter_hold(SI32_PMU_0);
     return;
  }
  else if (SI32_PMU_A_is_wakeup_event_flag_set(SI32_PMU_0))
  {
    SI32_PMU_A_clear_wakeup_flags(SI32_PMU_0);
    reset_source = SI32_PMU_WAKEUP_RESET;
    wakeup_source = SI32_RESET_WAKEUP;
    gModes_enter_my_default_mode();
    SI32_PMU_A_clear_pin_level_shifter_hold(SI32_PMU_0);
    return;
  }

void pmu_sleep_now(void)
{
  // SAFTY CODE TO BE REMOVED LATER.
  // GROUNDING PB3.0 WILL CAUSE THE PART TO NOT GO TO SLEEP
  SI32_PBCFG_A_enable_crossbar_1(SI32_PBCFG_0);
  SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_3, 0x00000001);
  if (SI32_PBSTD_A_read_pins(SI32_PBSTD_3) & 0x00000001)
  {
     // CLEAR WAKUP SOURCES
     SI32_PMU_A_clear_wakeup_flags(SI32_PMU_0);
     SI32_RSTSRC_A_enable_power_mode_9(SI32_RSTSRC_0);
     // SET DEEPSLEEP in SCR (and service all pending interrutps before sleep
     SCB->SCR = 0x14;
     __set_FAULTMASK(1);
     __WFI();
  }
}// pmu_sleep_now();

//------------------------------------------------------------------------------
void pmu_enter_sleep_till_reset_config(void)
{
   // DISABLE all wakeup sources
   SI32_PMU_A_write_wakeen(SI32_PMU_0, 0x0);

   // ENABLE Reset PIN as wake event
   SI32_PMU_A_enable_reset_pin_wake_event(SI32_PMU_0);
}

//------------------------------------------------------------------------------
void pmu_enter_sleep_till_alarm_config(void)
{
   // DISABLE all wakeup sources
   SI32_PMU_A_write_wakeen(SI32_PMU_0, 0x0);
   // ENABLE RTC_Alarm as wake event
   SI32_PMU_A_enable_rtc0_alarm_wake_event(SI32_PMU_0);
}

3.       Add RTC startup code, which needed by our PM9 reset source. (gRTC.c)
void gRtc0_enter_running_config(void)
{
   // ENABLE RTC CLOCK (+ IVC0 EXTVREG0 LPOSC0 EXTOSC0 LDO VREG0 VMON0)
   SI32_CLKCTRL_A_enable_apb_to_modules_1(SI32_CLKCTRL_0, SI32_CLKCTRL_A_APBCLKG1_MISC0);
   // SETUP RTC
   SI32_RTC_A_enable_module(SI32_RTC_0);
   SI32_RTC_A_enable_crystal_oscillator(SI32_RTC_0);
   SI32_RTC_A_set_clock_source_rtc(SI32_RTC_0);
   SI32_RTC_A_disable_bias_doubler(SI32_RTC_0);
   SI32_RTC_A_enable_autostep(SI32_RTC_0);
   SI32_RTC_A_enable_auto_gain_control(SI32_RTC_0);

   // START RTC AND WAIT FOR LOADCAP TO SETTLE
   SI32_RTC_A_start_timer(SI32_RTC_0);
   while(!SI32_RTC_A_is_load_capacitance_ready(SI32_RTC_0));
   SI32_RTC_A_clear_oscillator_fail_flag(SI32_RTC_0);
}

4.       Add code to check reset source and check retention ram and standard ram value.(main.c)
int main()
{
   // msTicks increments every 1ms (1Khz). Driven by boot osc (myCpu.c)
   // _last variables store the last seen state of variables so we know when they have changed
   uint32_t msTicks_last;
   // Keeps track of when button was depressed
   uint32_t hold_start = 0;
   uint32_t volatile *retention_ram = (uint32_t *)(0x20000000);
   uint32_t volatile *standard_ram = (uint32_t *)(0x20003000);
    // Setup the device in a mode depending on the reset/wakeup source
   setup_device();
   if (SI32_POWER_ON_RESET == get_pmu_reset_source())  {
     signal_POR();
     retention_ram[0] = 0x12345678;
     retention_ram[1] = 0xabcdef01;
   } else if (SI32_PIN_RESET == get_pmu_reset_source())  {
     signal_button_reset();
     while (SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 8));
     if((retention_ram[0] == 0x12345678) && (retention_ram[1] == 0xabcdef01)) {
       blink_3(0x800);
     } else  {
       blink_3(0x400);
     }
     while (SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 8));
     if((standard_ram[0] == 0x12345678) && (standard_ram[1] == 0xabcdef01))  {
       blink_3(0x800);
     } else {
       blink_3(0x400);
     }
   } else if (SI32_RESET_WAKEUP == get_pmu_wakeup_source())  {
     signal_reset_wake();
     while (SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 8));
     if((retention_ram[0] == 0x12345678) && (retention_ram[1] == 0xabcdef01))   {
       blink_3(0x800);
     }   else    {
       blink_3(0x400);
     }
     while (SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 8));
     if((standard_ram[0] == 0x12345678) && (standard_ram[1] == 0xabcdef01))  {
       blink_3(0x800);
     }  else   {
       blink_3(0x400);
     }
   } else  {
     //PANIC (TURN OFF LEDS AND SPIN)
     SI32_PBSTD_A_write_pb(SI32_PBSTD_2, 0xFFFF);
     while(1);
   }
   standard_ram[0] = 0x12345678;
   standard_ram[1] = 0xabcdef01;
   //PERFORM THE FOLLOWING TASKS FOREVER
   hold_start = 0;
   while (1)  {
      // If msTicks has changed
     if (get_msTicks() != msTicks_last)    {
         // IF PB2.9 BUTTON PRESSED
         if (!SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 9))  {
            // if start of button hold not logged then log it
            if(!hold_start)    {
               hold_start = get_msTicks();
            }
         }
         // IF BUTTON PB2.9 NOT PRESSED
         if (SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 9))      {
            // if the button was pressed
            if(hold_start > 0)     {
              sleep_for_ms(get_msTicks() - hold_start);
            }
         }
         // IF PB2.8 BUTTON PRESSED
         if (!SI32_PBSTD_A_read_pin(SI32_PBSTD_2, 8))      {
            // Sleep till reset pressed
            sleep_till_reset();
         }
         // save current msTicks value as last seen
         msTicks_last = get_msTicks();
      }// if msTicks changed
   }// while(1)
}// main()…

Function validation

      If POR occurs, both LEDs (DS3, DS4) will blink, we will store data in retention ram and standard ram. There are two options:When you press SW2(PB2.8), it will enter PM9 mode till reset; When you press SW3(PB2.9), it will enter PM9 mode till alarm, the alarm time is the time you press on SW3, and then wake up. Both option are turn on yellow LED(DS4) when enter PM9 mode.  
  If Reset wake occurs, yellow LED (DS4) will blink, it indicates exit from PM9.
  If button reset occurs, red LED (DS3) will blink, it indicates it is reset.
   Both above reset occur, we need to check retention ram and standard ram value.    First, Press SW2(PB2.8), it read out retention ram data and check whether it equal our preset value. If the value matched, it will blink yellow LED(DS4). If the value doesn't match, it will blink red LED(DS3). And then press SW2(PB2.8) again, it read out standard ram data check whether it equal our pre-set value.   If the value matched, it will blink yellow LED(DS4). If the value doesn't match, it will blink red LED(DS3). After checking ram data, we call choose two options by pressing SW2 or SW3.

References

1.       SiM3U1xx/SiM3C1xx Reference Manual: downloadable from the Silicon Labs web site at http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support Documents/TechnicalDocs/SiM3U1xx_SiM3C1xx_RM.pdf&src=DocumentationWebPart

2.       SiM3U1xx Data Sheet: downloadable from the Silicon Labs web site at http://www.silabs.com/pages/DownloadDoc.aspx?FILEURL=Support Documents/TechnicalDocs/SiM3U1xx.pdf&src=DocumentationWebPart


No comments: