S32K148 - switching between RUN and VLPS

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

S32K148 - switching between RUN and VLPS

621 Views
tomasfrcek
Contributor III

Hello,

I'm trying to switch the S32K148 to VLPS mode to conserve power and wake it back up to RUN mode when the button is pressed. The SCG module is configured to run in Normal RUN mode with SOSC and SPLL clocks configured. In the initial configuration, the current firmware runs without complications.

I was trying to use reference manual as much as I could, as well as information from here https://www.nxp.com/docs/en/application-note/AN5425.pdf  and here changing State From VLPS to RUN and RUN to VLPS S32K148 .

After I try to enter the VLPS, I see the consumption fall as the peripherals are disabled, but the MCU does not switch to VLPS (consumption stays at ~10mA, SMC_PMCTRL.VLPSA also set, in NVIC, only pending IRQ is RTC, which is not enabled however)

Here is my simplified code:

int main (void)

{

   WDOG_Disable();

   SetupClocks();

   // FlexCAN0, FTM1 and LPSPI1 also used in this project.

   InitPeripherals();

   // Init Button input. PTB15 with internal pull-up and interrupt on falling edge.

   PCC->PCCn[PCC_PORTB_INDEX] = PCC_PCCn_CGC(1U);

   PORTB->PCR[15] = PORT_PCR_PS(1) | PORT_PCR_PE(1) | PORT_PCR_MUX(1) | PORT_PCR_IRQC(10);

   NVIC_SetPriority(LPSPI1_IRQn, 1U);

   NVIC_SetPriority(CAN0_ORed_0_15_MB_IRQn, 2U);
   NVIC_SetPriority(FTM1_Ch0_Ch1_IRQn, 3U);
   NVIC_SetPriority(PORTB_IRQn, 4U);

   NVIC_EnableIRQ(LPSPI1_IRQn);

   NVIC_EnableIRQ(CAN0_ORed_0_15_MB_IRQn);
   NVIC_EnableIRQ(FTM1_Ch0_Ch1_IRQn);
   NVIC_EnableIRQ(PORTB_IRQn);

   while (1)

   {

      if (some conditions are met)

      {

         // Switch to VLPS.

         // 1. Disable peripherals.

         FTM1->SC &= ~FTM_SC_CLKS_MASK;
         PCC->PCCn[PCC_FTM1_INDEX] &= ~PCC_PCCn_CGC_MASK;

         LPSPI1->CR &= ~LPSPI_CR_MEN_MASK;
         PCC->PCCn[PCC_LPSPI1_INDEX] &= ~PCC_PCCn_CGC_MASK;

         CAN0->MCR |= CAN_MCR_MDIS(0x1);

         PCC->PCCn[PCC_FlexCAN0_INDEX] &= ~PCC_PCCn_CGC_MASK;

         

         // 1. Enable SIRC and set it as system clock source.

         while((SCG->SIRCCSR & SCG_SIRCCSR_LK_MASK) != 0x0) { ; }

         SCG->SIRCCSR |= SCG_SIRCCSR_SIRCEN(1U);
         while ((SCG->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK) == 0x0) { ; }

         SCG->RCCR = SCG_RCCR_SCS(2);

         // 2. Deactivate SOSC, SPLL and FIRC.

         SCG->SOSCCSR &= ~SCG_SOSCCSR_SOSCEN_MASK;
         while ((SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) != 0x0) { ; }

         SCG->SPLLCSR &= ~SCG_SPLLCSR_SPLLEN_MASK;
         while ((SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK) != 0x0) { ; }

         SCG->FIRCCSR &= ~SCG_FIRCCSR_FIRCEN_MASK;
         while((SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) != 0x0) { ; }

         SystemCoreClockUpdate();

         // 3. VLPS entry from the Application Note.

         // Enable SLEEPDEEP bit in the Core (Allow deep sleep modes)
         S32_SCB->SCR |= S32_SCB_SCR_SLEEPDEEP_MASK;
         // Allow very low power run mode
         SMC->PMPROT |= SMC_PMPROT_AVLP_MASK;
         // Select VLPS Mode
         SMC->PMCTRL = SMC_PMCTRL_STOPM(0b10);
         PMC->REGSC |= PMC_REGSC_BIASEN_MASK;
         // Check if current mode is RUN mode
         if(SMC->PMSTAT == 0x01)
         {
            // Go to deep sleep mode
            asm("WFI");
         }

      }

   }

}

void PORTB_IRQHandler(void)
{
   if ((PORTB->ISFR & (0x1 << 15) != 0x0)
   {
      if ((SMC->PMSTAT & SMC_PMSTAT_PMSTAT_MASK) >> SMC_PMSTAT_PMSTAT_SHIFT == 0x10)
      {
         // MCU in VLPS mode. Wake-up.
         NormalRUNmode_SPLL_MaxSpeed();
      }  

   } 
    PORTB->ISFR |= 0x1 << 15;   
}

EDIT1:

I think I managed to enter the VLPS mode by adding some code from this example: Example S32K144 RTC VLPS S32DS.R1 . Current falls from 10mA to 6mA (drawn by UJA113 in our PCB I assume, I cannot confirm by debugger). Waking up however does not work.

Labels (1)
11 Replies

37 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hello Tomas,

The FlexCAN can't be directly disabled:

pastedImage_1.png

The debugger must be disconnected and the MCU power cycled, before the MCU can enter VLPS.

Is the pin interrupt working in the RUN mode?

Regards,

Daniel

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

Hello, Dan,

thanks for replying. I created a "mini" version of our project, just to get the mode switching working. I removed the FlexCAN functions completly, so only LPSPI (needed for UJA113 communication) and FTM remain. It didn't help however. But switching FlexCAN to Freeze mode in the "full" version will be necessary, thank you for that information!

I downloaded the current firmware, disconnected the debugger and cycled power. After triggering the "go to sleep" event, the current falls. But after triggering the "wake-up event", nothing happens, the current value stays low.

The interrupt is working well in the RUN mode. I am using the same digital input for both "go to sleep" event (as standard button with debouncing handled) and "wake-up" event. I distinguish between the two events by checking the value of SMC->PMSTAT.PMSTAT in the interrupt handler. I didn't include the "go to sleep" and debouncing procedure in the sample code, to keep it as simple as possible.

0 Kudos
Reply

37 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi Tomas,

Could you attach the mini test project so that I can test it?

Or, you can create a ticket if you don't want to share it here:

https://community.nxp.com/docs/DOC-329745 

BR, Daniel

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

A am attaching the Keil project. All the comments are in Czech, I hope that isn't a problem =)

0 Kudos
Reply

37 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Thanks for the project Tomas, the language is not a problem, but I don't have KEIL, so I'm not able to test it. 

Nevertheless, I think the problem is that although it can wake up, it goes to VLPS again.    

Because sleep_mode_command stays TRUE when it leaves the PORTB_IRQHandler.

You can test it by placing this

if(sleep_mode_command == TRUE) {while(1){}}

at the beginning of PORTB_IRQHandler and measure the consumption or you can attach the debugger (without reset). 

You should see that SMC_PMSTAT == 0x01 because at this point, the MCU is already in RUN not VLPS.

BR, Daniel

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

I am setting the sleep_mode_command back to FALSE in the handler, if at the time the interrupt is set, the value SMC->PMSTAT == 0x10 (VLPS). It's at the line 160 of gpio.c.

However, I will try to set it to FALSE just before entering the VLPS and let you know!

0 Kudos
Reply

37 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hi Tomas,

The handler is setting the sleep_mode_command back to FALSE only if PMSTAT == 0x10 (VLPS) but the MCU is in RUN already PMSTAT = 0x01 (RUN).

BR, Daniel

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

Hello, Daniel

I understand, that at the moment the handler gets fired while the MCU is in VLPS mode, the value of PMSTAT is already 0x1 (RUN)? I tried to modify the code accordingly:

// In the main loop, I switch to the VLPS mode when the conditions are met.
if (sleep_mode_command == TRUE)

{
   // Deactivates peripherals.
   if ((PCC->PCCn[PCC_FTM1_INDEX] & PCC_PCCn_CGC_MASK) != 0x0)
   {
      FTM1->SC &= ~FTM_SC_CLKS_MASK;
      PCC->PCCn[PCC_FTM1_INDEX] &= ~PCC_PCCn_CGC_MASK;
   }
   if ((PCC->PCCn[PCC_LPSPI1_INDEX] & PCC_PCCn_CGC_MASK) != 0x0)
   {
      LPSPI1->CR &= ~LPSPI_CR_MEN_MASK;
      PCC->PCCn[PCC_LPSPI1_INDEX] &= ~PCC_PCCn_CGC_MASK;
   }
   

   // Sets SIRC as system clock in RUN mode.

   SCG->RCCR = (SCG->RCCR & ~SCG_RCCR_SCS_MASK) | SCG_RCCR_SCS(0x2);

   while (((SCG->CSR & SCG_RCCR_SCS_MASK) >> SCG_RCCR_SCS_SHIFT) != 0x2) { ; }

   // Deactivates SOSC and SPLL.
   SOSC_SetEnable(FALSE);
   SPLL_SetEnable(FALSE);

   SystemCoreClockUpdate();

   // Following section from Example S32K144 RTC VLPS S32DS.R1

   if(SMC->PMSTAT == 0x01)

   {

      S32_SCB->SCR |= 0x06;

      PMC->REGSC |= (1 << 1);

      SMC->PMCTRL |= 0x00000002;

      SMC->PMPROT |= (1 << 5);

      (void)SMC->PMPROT;

      if(SMC->PMSTAT == 0x01)

      {

         asm("WFI");

      }

      if (SMC->PMCTRL & 0x8)

      {

         if(SMC->PMSTAT == 0x01)

         {

            asm("WFI");

         }

         if (SMC->PMCTRL & 0x8)

         {

            S32_SCB->AIRCR = 0x05FA0004;

         }

      }

   }
}

void PORTB_IRQHandler(void)

{

   if ((PORTB->ISFR & (0x1 << 15)) != 0x0)

   {

      if (sleep_mode_command == TRUE)

      {

         sleep_mode_command = FALSE;

         // Setting logical 1 to another output pin of PORTB, so that i can confirm, that the handler is fired in the VLPS.

         PORTB->PCR[22] = PORT_PCR_PE(0U) | PORT_PCR_MUX(port_io);
         PTB->PDDR |= 0x1 << 22;
         PTB->PSOR |= 0x1 << 22;

         // Same initialization (except for WDOG disable) that gets called at the start of main()

         SetupClocks();

         IInitGPIOPins();
         SPI_Init_Master(&lpspi1_system_base_chip_config);
         InitRegularTimer();

      }

      PORTB->ISFR |= 0x1 << 15;

   }

}

After I give the command to switch to VLPS mode, I see the current fall from 35mA to 8mA and all peripheral functions stop. I susspect that now the MCU is in VLPS, but I cannot confirm it as I loose the communication with the debugger as well at this point.

When I cause the PORTB_IRQHandler event, voltage at pin PTB22 goes up, which means that the handler gets fired. That probably means, that the problem is with one of the initialization functions. I tried to call the peripheral init functions, then switch them off the same way I do before switching to the VLPS and immediately re-initializing to see, if there is some sort of HardFaulting, but it worked. That means the problem is probably with the SetupClocks() function which works well when called at the beginning of main() however.

 

I am attaching the current clocks.c file, which contains the SetupClocks().

Thank you!

Tomas

0 Kudos
Reply

37 Views
danielmartynek
NXP TechSupport
NXP TechSupport

Hello Tomas,

You can monitor the BUS_CLK or the CORE_CLK on a CLKOUT pin externally, you should see when the MCU stops and wakes up. This example is using it: Example S32K144 RTC VLPS S32DS.R1 

I haven't found anything wrong in the code, but I would recommend stepping the code in the handler once the MCU is back in RUN. 

You can keep the execution in a while loop in PORTB_IRQHandler(),

uint32_t variable = 1;
while(variable){}‍‍

then attach the debugger (without reset), halt the core, clear variable and step the code.

You can also implement a HardFault_handler and debug the exception if there is any.

Regards,

Daniel

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

Hello, Daniel,

I modified the code a little bit and assigned different pins for wake-up and go to sleep events, to mask any potential stupid errors. After powering the MCU, the LED starts to blink (FTM1 function) in regular intervals and current oscilates between ~40mA - ~50mA.

After triggering the go to sleep event, current falls to ~10mA and LED stops to blink. Than I trigger the wake-up event and attach debugger without reset. After stopping the execution in the debug mode, I find myself within the PORT interrupt handler, in the infinite while loop that you suggested. PMSTAT = 1 (RUN mode) and VLPSA = 0, indicating, that the VLPS transition was correct.

I let the execution run after I manually exit the infinite while, the current rises to ~20mA and the LED does not blink. When I put a breakpoint into the FTM1 handler, it gets called as expected, when I step from the handler, I get to the main loop as expected and execute the code. But any breakpoint in the main loop itself does not work. I can see the FTM1 counters work normally, but the code in the main loop apparently does not get called.

A am attaching my current code below:

void PORTB_IRQHandler(void)
{
   if ((PORTB->ISFR & (0x1 << pin_dig_0)) != 0x0)
   {
      // Go to sleep event detected.
      sleep_mode_command = TRUE;

      // Deactivates the Go to sleep interrupt.
      PORTB->PCR[11] &= ~PORT_PCR_IRQC_MASK;

      // Clear interrupt.
      PORTB->ISFR |= 0x1 << 11;
   }
}

if (sleep_mode_command == TRUE)
{
   // Deactivate peripherals.
   if ((PCC->PCCn[PCC_FTM1_INDEX] & PCC_PCCn_CGC_MASK) != 0x0)
   {
      FTM1->SC &= ~FTM_SC_CLKS_MASK;
      FTM1->CONTROLS[0U].CnSC &= ~(FTM_CnSC_CHF_MASK);
      PCC->PCCn[PCC_FTM1_INDEX] &= ~PCC_PCCn_CGC_MASK;
   }
   if ((PCC->PCCn[PCC_LPSPI1_INDEX] & PCC_PCCn_CGC_MASK) != 0x0)
   {
      LPSPI1->CR &= ~LPSPI_CR_MEN_MASK;
      LPSPI1->SR |= 0x1 << 1 /* RDF */;
      PCC->PCCn[PCC_LPSPI1_INDEX] &= ~PCC_PCCn_CGC_MASK;
   }

   // Activate WakeUp interrupt on digital pin.
   PORTC->PCR[15] |= PORT_PCR_IRQC(falling_edge);

   // Switch to VLPS.
   VLPS_Mode();
}

void VLPS_Mode(void)
{

   // Set SIRC as system clock.
   uint32_t rccr = SCG->RCCR;
   rccr &= ~SCG_RCCR_SCS_MASK;
   rccr |= SCG_RCCR_SCS(0x2);
   SCG->RCCR = rccr;

   // Wait for SIRC to be set as system clock source.
   while (((SCG->CSR & (0xF << 24) /* SCS */) >> 24) != 0x2 /* SIRC_CLK */) { ; }

   SOSC_SetEnable(FALSE);
   SPLL_SetEnable(FALSE);
   SystemCoreClockUpdate();

   if(SMC->PMSTAT == 0x01)
   {
      S32_SCB->SCR |= 0x06;
      PMC->REGSC |= (1 << 1);
      SMC->PMCTRL |= 0x00000002;
     SMC->PMPROT |= (1 << 5);
      (void)SMC->PMPROT;

      if(SMC->PMSTAT == 0x01)
      {
         asm("WFI");
      }

   }

}

void PORTC_IRQHandler(void)
{
   if ((PORTC->ISFR & (0x1 << pin_dig_1)) != 0x0)
   {
      // Wakeup event detected.
      if (sleep_mode_command == TRUE)
      {
         // Ensures that the wake up interrupt gets called only in VLPS mode - this is only secondary control as the interrupt          // is disabled before switching to VLPS.
         while (sleep_mode_command == TRUE) { ; }   // Infinite while loop for debugger entry.
         sleep_mode_command = FALSE;

         // Re-initializes clocks. 
         WDOG_disable();
         SOSC_SetEnable(TRUE);
         SPLL_SetEnable(TRUE);

         // SPLL as system clock source.
         SCG->RCCR = (SCG->RCCR & ~SCG_RCCR_SCS_MASK) | SCG_RCCR_SCS(0x6);
         while (((SCG->CSR & (0xF << 24) /* SCS */) >> 24) != 0x6 /* SPLL_CLK */) { ; }
         // Deactivates FIRC if it somehow gets activated after exiting the VLPS.
         if ((SCG->FIRCCSR & SCG_FIRCCSR_FIRCEN_MASK) != 0x0)
         {
            while((SCG->FIRCCSR & SCG_FIRCCSR_LK_MASK) != 0x0) { ; }
            SCG->FIRCCSR &= ~SCG_FIRCCSR_FIRCEN_MASK;
            while((SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) != 0x0) { ; }
          }
         SystemCoreClockUpdate();

         // Re-initializes peripherals.
         InitGPIOPins();
         SPI_Init_Master(&lpspi1_system_base_chip_config);
         InitRegularTimer();
      }

   // Clear interrupt.
    PORTC->ISFR |= 0x1 << 11;
   }
}

0 Kudos
Reply

37 Views
tomasfrcek
Contributor III

Hello, Daniel,

thank you, MCU now wakes up as expected! I also managed a work-around, calling

S32_SCB->AIRCR = S32_SCB_AIRCR_VECTKEY(0x5FA) | S32_SCB_AIRCR_SYSRESETREQ(1);

in the wake up interrupt, causing the processor to be reset. I think it's simpler when you don't need to retain any variables and the application is not time-critical.

Thank you!