AnsweredAssumed Answered

MSCAN WAKE UP  FROM STOP & NESTED INTERRUPTs

Question asked by Akimul Prince on Jun 16, 2017

This application is for the automotive industry  and what I'm trying to do  using CAN0 module of the board(The prototyping board from Technologicalarts - Adapt9S12XDP512 ) is:

 

                                  : Receive and  check flags of the different messages, as soon as vehicle is turned on,   So I definitely have to use  Receiver Full Interrupt Enable Flag "RXFIE(CAN0 receive)" of the of the register "CAN0RIER  " so I can receive all the messages serially filtered out by my filter settings. (four 16-bit filters are used)

                                 : When all the required messages are received (meaning conditions are true for the very first time), transmit messages only one time in the entire system, so I don't need  to enable CAN0TIER  register

                                : Once the transmission is done , conditions can be true afterwards but make sure don't transmit again

                                : When vehicle is turned off , Put the  CAN to stop or sleep in order to draw minimum current

                               : As soon as, Vehicle is turned back on , wake up from  STOP /SLEEP mode and everything should work like the above  mentioned  settings, So I definitely  have to use WUPIF (CAN0 wake-up)Flag of the CAN0RIER .

                             ->  Hence the value is written into the CAN0RIER = 0x81; so both the WUPIE and RXFIE are 1

                             ->  Consequently I have to use TWO ISRs for both WUPIF and RXF flags  so Nesting of Interrupts come into the picture

                            ->   By default, The vector address of  CAN0 receive  is smaller than that of CAN0 wake-up, so  CAN0 wake-up has the higher priority  (Section 1.6.1 ,Page 74 of the MC9S12XDP512 datasheet )

                            ->  The ISR of  CAN0  receive(RXF)  is declared first to receive messages and later on the ISR of CAN0 wake-up (WUPIF) is used

 

Using this setting , I can successfully go back to sleep when vehicle is turned off and get back from the sleep with or even without using the second ISR.  So there is a flag that I'm reading when the vehicle is being turned on and off to take the decision when to go to SLEEP or STOP mode.  I will explain a bit here with some pseudo code that I had written for Sleep mode:

 

The following  code goes inside the main() function of the main.c file   :

 

                                         Initialize_CAN(); // This is same for any application for any user where all the registers are set

                                                                     // for Enabling the CAN module, setting the filters , setting the Interrupts

                                                                    // I need Receive Interrupt(RXFIE) and Wake_up Interrupt (WUPIE) bits to be                                                                                  //enabled                                                                                                                                               

                                           EnableInterrupts;

                                           for(;;) {

       

                                                     Recevied_Msgs = CAN_Receive();

                                  //  With this function ,  fires the ISR for Receive Interrupt(RXF) and  gets new messages;

                                  // I’m filtering 4 messages out that I need for all the conditions to be true to transmit  messages

                                 // once

                                                                                                                                                                                   

                                      If (Recevied_Msgs == All_the_conditions_set_to_transmit)

                                                      Transmit_messages_once();

 

                                   Else if (Recevied_Msgs == Sleep_condition)

                                     CAN0CTL0 |= 0x02;   //  wake-up option has to be enabled (WUPE = 1); It is initialized inside                                                                               //Initialize_CAN()    before power down mode 

 

 

Please see below for the  Question for this section  Surrounded  by the "+" sign                                                                  +++++++++++++++++++++++++++++++++++++++++++++++++++++++

 // while((CAN0CTL1 & 0x02)==0){} ;// Not being able to use it here  

                                           or                                                                                                                                            

   //while(CAN0CTL1_SLPAK == 0);  /*     To acknowledge sleep  * /             

                                                                                                                     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                           If (Recevied_Msgs = = wake_condition)

                                 wake_up_status = Wake_Up_once();

                                                                                                                                                                                                     // This Wake_Up_once () function intends to fire the ISR for wake_up Interrupt(WUPIF)   

    // I tried to perform Interrupt nesting mechanism without using software scheduler

   // By clearing the global I mask bit using ‘asm RTI’ or asm CLI inside ISR function  for wake_up Handling

   // This fired the ISR and the CAN module works fine and transmits again as expected.

                                  if (wake_up_status) 

                                     Reset all the conditions;    ( required to check receive messages and transmit again)

                                    } //  for loop  for the pseudo code ends here    

 

 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++   So My Question for the surrounded section is :: Why can't I use either of the lines  "while((CAN0CTL1 & 0x02)==0){} ;"  or  "while(CAN0CTL1_SLPAK == 0); "    here inside main()  as it gets caught up/stuck here in the infinite loop  ?

  I would understand If the memory mapped register CAN0CTL1 was not declared as "volatile "but it is declared as volatile in the derivative header file like :

    extern volatile CAN0CTL1STR _CAN0CTL1 @(REG_BASE + 0x00000141UL);

 

On top of that, I even used  inside my  Initialize_CAN()   function  ; Please  look into my Initialize_CAN() function;

I never had any issue there.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Now Pseudo code explanation for the STOP Mode:

 

 

                                            Initialize_CAN();                                                                                                                        

                                           EnableInterrupts;

 

                                               for(;;) {

       

                                             Recevied_Msgs = CAN_Receive();

                                                                                                                                                                        

                                              If (Recevied_Msgs == All_the_conditions_set_to_transmit)

                                                                                                                                                                                                                                          Transmit_messages_once();

 

                                              Else if (Recevied_Msgs == Sleep_condition)

                                            CAN0CTL0 |= 0x02;   

                                                                                                                                                                                                                 

                                            asm ANDCC #0x7F;  //clear S bit

                                            asm nop;

                                            asm STOP;         //STOP mode

                                        } //  for loop  for the psuedo code ends here

       Question:        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                 

    After the asm STOP , we are not supposed to use any thing  below because it is not going to work, right?

                                                 If (Recevied_Msgs = = wake_conditon)

                                                  wake_up_status = Wake_Up_once();

                                                    if (wake_up_status) 

                                                         Reset all the conditions;    ( required to check receive messages and transmit again)

 

But  from the application note AN2255/D MSCAN Low-Power Applications , page 10 : 

                                        "If the MCU was in Stop or Wait mode, code execution continues with the next instruction after the WAI or STOP instruction "

 So Does it mean we can have something after the asm STOP instruction for the wake up to take place?

 

 

 And page 7 of the previous  application note says:

 

3a. Using the MSCAN to wake-up the MCU.

The WUPE bit in the CANCTL0 register must be set, to enable wake-up

on bus activity. The WUPIE bit in the CANRIER register must be set and

there must be an interrupt routine to handle the wake-up interrupt. The

I-mask bit in the CCR must be cleared. The WUPM bit in the CANCTL1

register may be set to filter out any short spikes from the CAN interface

RX pin.

 

So If you please take a quick look of the ISR I have for waking up from either SLEEP or STOP mode:

 

 #pragma CODE_SEG __NEAR_SEG NON_BANKED

              

 void interrupt MSCAN0_WakeUp_Interrupt_Handler(){

     // asm CLI;

      CAN0RIER &= ~CAN0RIER_WUPIE_MASK;  // Clear WUPIE  

     

     Wakeup_ISR_Check = "W_I_FIRED"; // Just for checking ISR works or not    

      if ((CAN0CTL0_WUPE==1) && (CAN0RFLG_WUPIF == 1)) {

           

                 CAN0CTL0 &= ~0x02; //Clear SLPRQ to leave Sleep Mode     

        }

     

       CAN0RFLG = 0x80; // Clear WUPIF 

      Wakeup_ISR_Check_end = "END"; // Just for checking ISR works or not

      asm RTI;

      }

     

 

 #pragma CODE_SEG DEFAULT

 

 And page 8 of the previous  application note says:

3b. Using an Interrupt pin to wake-up the MCU

The appropriate pin from the CAN interface or system basis chip (e.g.

NERR or INT) must be connected to a port pin with interrupt functionality

e.g. PORTP.

The port pin must be configured as an input, the interrupt enable bit for

the port pin must be set, and the port pin must be configured to generate

an interrupt on a falling edge. There must be an interrupt routine to

handle the port interrupt. The I-mask bit in the CCR must be cleared.

 

So, the quick look of the ISR I have for waking up from  STOP mode as CAN0 is connected to the PORT P:

I tried the  following ISR function declared in the main.c file (off course  outside of  main() function but It behaves quite  weird ).

 

 #pragma CODE_SEG __NEAR_SEG NON_BANKED

              

 void interrupt MSCAN0_WakeUp_Interrupt_Handler(){

         // asm CLI;

          PTM = ~PTM; // Invert the direction of the port

          DDRM = 0x00; // Change the data direction and configure as Input ; It is previously declared as DDRM = 0xFF  output inside the main () function

        

         Wakeup_ISR_Check = "W_I_FIRED";     

         if ((CAN0CTL0_WUPE==1) && (CAN0RFLG_WUPIF == 1)) {

           

                   CAN0CTL0 &= ~0x02; //CAN0CTL0_SLPRQ = 0; SLPRQ=0 to leave Sleep Mode     

          }

         CAN0RIER &= ~CAN0RIER_WUPIE_MASK;    

         CAN0RFLG = 0X80;   // Clear interrupt flag

       //  CAN0CTL1 |= 0x80, 

/*Resetting all the conditions as soon as wakes up */

        sleep_status = 0,

        Transmit_counts= 0,

        retry_var = 1,

        condition_1 = -1 ,

        condition_2 = -2,

        condition_3 = -3

        ;

        Wakeup_ISR_Check_end = "Reachd" ;

      asm RTI;

     

  }

 

 #pragma CODE_SEG DEFAULT

 

 

Now The logic I have using asm RTI or asm CLI inside the ISR is the following:

 

Let F1 and F2 be two interrupt functions, with priority levels equal to 1 and 3 respectively. If  F1 declares before F2, F1 will not be suspended by F2. F2 will only start at the end of F1. However, priority will be given to lower level priorities. Indeed, by default, a maskable interrupt request can not interrupt another maskable interrupt request, whatever its priority level .

In order to suspend the execution of an interrupt function momentarily and to execute a higher priority function , it is necessary to reset the bit I of the CCR register as soon as the first interrupt function . Indeed, when the first interrupt is triggered, the bit I is set to 1 preventing the execution of the program from another interrupt. By resetting this bit, the highest priority interrupt request takes over. The bit I can be reset to 0 by applying the assembly instruction CLI   :

asm (cli); // reset bit I

 

 

Let's go back to the previous example   : IPL = 1 when F1 starts. During execution, after resetting I to 0, the interrupt related to F2 makes a request. IPL then passes to 3 and the execution of F1 is interrupted while that of F2 starts. At the end of F2, IPL returns to 1 and the execution of F1 resumes where it was left. As soon as F1 ends, IPL returns to 0 if no new interrupts have occurred in the meantime and the initial program can resume.

 

Note   : RTI instruction   : asm (RTI)   ;

It is also possible to prematurely suspend the execution of an interrupt function, using the RTI assembly instruction. This allows the contents of the IPL registry to be reset to the previous level. If it is applied to an interrupt function, it is immediately terminated and the execution of the preceding program is resumed. For example, assume that F2 has temporarily interrupted F1. A RTI statement executes in F2. Before executing the RTI instruction, IPL = 3. When RTI is executed, IPL = 1, F2 stops prematurely and F1 resumes where it was left. If an RTI instruction appears in F1, IPL goes to 0 and returns to the initial program.

 

 

And I'm aware of the followings:

 

 

from Page 471 ; section 10.4.5.6 of the data sheet.

 

10.4.5.6 MSCAN Power Down Mode

The MSCAN is in power down mode (Table 10-36) when

  • CPU is in stop mode

 

from Page 467 ; section 10.4.5 of the data sheet.

 

“Table 10-36 summarizes the combinations of MSCAN and CPU modes. A particular combination of

modes is entered by the given settings on the CSWAI and SLPRQ/SLPAK bits.”

 

“For all modes, an MSCAN wake-up interrupt can occur only if the MSCAN is in sleep mode (SLPRQ = 1

and SLPAK = 1), wake-up functionality is enabled (WUPE = 1), and the wake-up interrupt is enabled

(WUPIE = 1).

 

Section :10.4.7.7 Recovery from Stop or Wait

The MSCAN can recover from stop or wait via the wake-up interrupt. This interrupt can only occur if the

MSCAN was in sleep mode (SLPRQ = 1 and SLPAK = 1) before entering power down mode, the wake-up

option is enabled (WUPE = 1), and the wake-up interrupt is enabled (WUPIE = 1).

 

 

Hence the biggest question I have is  : :  Why can't the CAN  module restart/reinitialize the activity  from the STOP mode?

 

 

 

 

So Please help me with some guidance so that I can wake up back from  MCU STOP mode . Files are attached and any guidance or suggestions are highly appreciated. 

 

Thanks

Original Attachment has been moved to: Utilities.c.zip

Original Attachment has been moved to: main.c.zip

Original Attachment has been moved to: Utilities.h.zip

Outcomes