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