MSCAN WAKE UP  FROM STOP & NESTED INTERRUPTs

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

MSCAN WAKE UP  FROM STOP & NESTED INTERRUPTs

1,103 Views
aprince
Contributor I

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

0 Kudos
3 Replies

612 Views
RadekS
NXP Employee
NXP Employee

Hi Akimul,
About stuck in SLPAK testing loop)
The time when the MSCAN enters sleep mode depends on a fixed synchronization
delay and its current activity:
• If there are one or more message buffers scheduled for transmission (TXEx = 0), the MSCAN will continue to transmit until all transmit message buffers are empty (TXEx = 1, transmitted successfully or aborted) and then goes into sleep mode.
• If the MSCANis receiving, it continues to receive and goes into sleep mode as soon as the CAN bus next becomes idle.
• If the MSCAN is neither transmitting nor receiving, it immediately goes into sleep mode.

Idea: So, I guess, that there might be some problem with occupied TX buffer. Please check CANTFLG register. If the next node does not acknowledge message from MCU, the transmit buffer cannot be released.

Note: your pseudo code contains “ If (Recevied_Msgs = = wake_condition)” line. I suppose that “= =” is just typo error and it is not part of your real code. In fact, I am not sure how compiler translates space between equal chars.

About STOP instruction)
Yes, that is correct. If S bit is cleared, the STOP instruction puts MCU into (pseudo) stop mode. When MCU wake-ups and finish pending interrupts, it will continue in the instructions after STOP. So, you may keep all housekeeping functions prior stop mode and after wake-up inside a single function.

About wake-up interrupt handler)
I would like to recommend using just one option for MCU wake-up. Either wake-up functionality in MSCAN module or wake-up functionality in CAN transceiver (if available).
You should not handle flags which do not refer appropriate interrupt. So, the clearing CAN flags like WUPIF inside port M interrupt is the obviously wrong approach.
Note: since you used “interrupt” keyword in the function declaration, you don’t need to add “asm RTI;” command at end of interrupt routine. This instruction is used automatically based on “interrupt” keyword.

About CAN wake-up)
The MSCAN sleep mode is confirmed by SLPAK flag. If this never sets, MSCAN doesn’t enter sleep mode and entering stop mode may cause CAN bus error.
So, please focus on the reason why SLPAK never reach log1. The MSCAN module waits for idle state prior enter sleep mode.

Note: When MCU is wake-up by CAN wake-up feature, the SLPRQ bit is cleared automatically.

I hope it helps you.

Have a great day,
Radek

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

612 Views
aprince
Contributor I

I've been sick lately, so I'm not at my work place.Besides I wanted to try few things before I replied you guys back but It came close to the end of the week before I could feel better ; so I decided to reply you guys back anyways before I go back to work and try to solve this problem. 

  •  About stuck in SLPAK testing loop:   

The IDEA you suggested: 

Idea: So, I guess, that there might be some problem with occupied TX buffer. Please check CANTFLG register. If the next node does not acknowledge message from MCU, the transmit buffer cannot be released.

Can you please explain what you mean by " If the next node does not acknowledge message from MCU, the transmit buffer cannot be released"? 

what I understood from this line: If another unit is not connected, the message due for transmission is not successfully sent and message remains in the transmit buffer.

But this is not the case for me though I try to send these messages 9 times to a specific entity/module (when some specific conditions are true) in the car through OBD-II port of the car. 

The code I had for transmitting data:

[ The code style followed from the application node AN3034: Using MSCAN on the HCS12 Family]

void Transmit_CAN_Msgs(  unsigned short id, unsigned char length,
                                             char *Tx_Data, unsigned char priority) {

         unsigned char txbuffer = {0}; 

         while((CAN0TFLG & 0x07)== 0){} ; // Wait for transmit buffer available

         CAN0TBSEL = CAN0TFLG; // Request selection of empty buffer with

                                              // the lowest bit set in the CAN0TBSEL
         txbuffer = CAN0TBSEL; /* Backup selected buffer */

         :

         :

         :

         CAN0TFLG = txbuffer; // flag buffer as ready for transmission

         while ( (CAN0TFLG & txbuffer) != txbuffer);  //Wait for Transmission  completion

}

I was expecting not to have any issue that you are suspecting with CAN0TFLG because of the last line

"while ( (CAN0TFLG & txbuffer) != txbuffer); ". Besides, the code never got stuck in this while loop.

 

NOTE: Again I'm not using CAN0TIER as I don't need to send data periodically/serially with interrupt driven mechanism.  

  • Note: your pseudo code contains “ If (Recevied_Msgs = = wake_condition)” line. I suppose that “= =” is just typo error and it is not part of your real code. In fact, I am not sure how compiler translates space between equal chars

This is used in the pseudo code in order to explain that the flags(bits) of the messages I received through CAN0RIER ISR are required to be checked against some conditions in the code. This is not part of the real code. 

The actual code was the like the following: 

for(;;) {
        Msgs = CAN_Receive();

                                          //fires the ISR function  MSCAN0_Receive_Interrupt_Service() because of CAN0RIER =0x01;

         if (Msgs==1){
             condition_1 = 1;
           } else if (Msgs==11){
              condition_1 = 0;
           } else if (Msgs ==2){
                condition_2 =2;

           } else if (Msgs ==3) {
                condition_3 = 3;

           }else if (Msgs ==4) {
                SLEEP = 1;
            }else if (Msgs ==44) {
                SLEEP = 0;
            } // if ends here

        if (condition_1 ==1 && condition_2 ==2 && condition_3 == 3 && SLEEP ==0){

            ......

             .......

           Transmit_9_Times() ; // a wrapper function for Transmit_CAN_Msgs() 

          }   

         Code continues.......

      >>> Total 4 conditions(4 filters are configured) I'm counting on to be true to transmit what I want to transmit. 

  • About STOP instruction)
    Yes, that is correct. If S bit is cleared, the STOP instruction puts MCU into (pseudo) stop mode. When MCU wake-ups and finish pending interrupts, it will continue in the instructions after STOP. So, you may keep all housekeeping functions prior stop mode and after wake-up inside a single function.

So do you mean that I should be using a function , let's say, the function wake_up() after  the following piece of code:

   

   /*The following code is the continuation of the above code*/

        else if(SLEEP ==1) { // when I read one specific flag of a specific message (through the ISR for CAN0RIER = 0x01)

                                        //  after I turned the car off , the variable SLEEP is set to 1 and then I decided to request the                                             //  sleep mode of the MCU  

                   CAN0CTL0 |= 0x02; //sleep mode request
                   while((CAN0CTL1&0x01)==0){} ;// As you know, it's getting stuck here
                    asm ANDCC #0x7F; //clear S bit
                    asm nop;
                    asm STOP; //STOP mode 

           }

     So, after the asm STOP line: 

          else if (SLEEP==0) {// meaning the car is turned back on & SLEEP condition is checked through the ISR function                                             // because of CAN0RIER = 0x81; RXFIE =1;

                        This is my concern: If the MCU is already in STOP mode, this else if(SLEEP==0) should not execute because this condition relies on the ISR function of the MSCAN0_Receive_Interrupt_Service() 

                       wake_up();  // expecting to call the ISR function of  MSCAN0_WakeUp_Interrupt_Handler() because                                              // of CAN0RIER = 0x81; WUPIE =1;

           }

Note:       I Just can't call the function wake_up() after the asm STOP without checking the condition as I only want to wake up next time I turned the car back on. If I don't put it under any condition, it is always going to call wake_up() function as it's inside for(;;) loop.

  • About wake-up interrupt handler)
    I would like to recommend using just one option for MCU wake-up. Either wake-up functionality in MSCAN module or wake-up functionality in CAN transceiver (if available).
    You should not handle flags which do not refer appropriate interrupt. So, the clearing CAN flags like WUPIF inside port M interrupt is the obviously wrong approach

I just did it when nothing was working. 

Thanks a lot as you replied with some valuable thoughts. I thought no one is ever gonna reply me back as I wrote so much. I would be glad If you could provide some code where you had to go sleep when certain condition is true and wake back up from STOP mode when it is requited.

 Thanks a lot to you again and to @Iama

0 Kudos

612 Views
lama
NXP TechSupport
NXP TechSupport

Hi,

I am going to investigate your issue and try to read entire novel. However, before I start I would like to know whether all

conditions for entering CAN sleep mode are fulfilled.

As a first step I am going to create my own test code....

Have you stopped the code in the while(CAN0CTL1_SLPAK == 0);  ?

...modification....

unsigned long i = 100000UL;   // set suitable value

while(!CAN0CTL1_SLPAK)

{

  if( !(i--) )

    {

       asm nop;                     //  too long waiting loop for sleep mode => set breakpoint here or report error..and check

                                           //  registers and conditions which can cause the SLPAK is not set

    }

}

Best regards,

Ladislav

0 Kudos