View previous topic :: View next topic |
Author |
Message |
plinder13
Joined: 16 Jul 2007 Posts: 2
|
Sleep and Interrupts |
Posted: Fri Jul 20, 2007 8:13 am |
|
|
I have had a difficult time getting sleep to work with interrupts as expected on various PICs
I want to put a PIC to sleep using the sleep() function. But, before I put it to sleep, I want to enable global interrupts. As soon as it wakes up, I want to execute the ISR for any interrupts that occured and then immediately disable global interrupts so that background tasks can run. I have much experience with the MSP430 for which the following code works great:
while(1)
{
if (flags.sleep)
{
LPM0_EINT(); //(enter low power mode and enable global interrupts)
DINT(); //(disable low power mode and disable global interrupts
}
// Do background tasks and check flags
}
The reason I need global interrupts enabled immediately before sleeping is that I set various flags in ISRs that determine whether the MCU should sleep. I don't want those ISRs to run between the time I check the flags and the time I place the MCU into sleep (if an interrupt occured between the checking of the flags and the sleep instruction, the MCU could get "stuck" in sleep mode).
Here is the code I have tried which has not worked for me:
if (flags.sleep)
{
enable_interrupts(GLOBAL);
sleep();
delay_cycles(1);
disable_interrupts(GLOBAL);
}
In my specific project, the comparator does not seem to wake up the MCU. However, if I comment out the sleep() instruction the application works fine. But, I have had a hard time understanding how to do this in other applications as well. I also have tried inserting another delay_cycles(1); before the sleep instruction.
Thanks for any help. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jul 20, 2007 12:10 pm |
|
|
Quote: |
I have had a difficult time getting sleep to work with interrupts as
expected on various PICs |
1. Post the PIC that you're most concerned about using.
Don't post a list of all of them -- just the most important one.
2. Also post your CCS compiler version. |
|
|
Guest
|
|
Posted: Fri Jul 20, 2007 12:16 pm |
|
|
PIC16F883
CCS Version 4.044
Specifically, I am trying to use the comparator to wake up from sleep mode. |
|
|
Bill Boucher
Joined: 04 Feb 2005 Posts: 34 Location: Chatham,ON,CA
|
|
Posted: Fri Jul 20, 2007 12:46 pm |
|
|
Try copying the ISR controlled flags variable into a local variable in your main(). The value of this local copy will not be changed if an interrupt happens. Check the value of the copy and do your sleep thing based on that.
Code: |
int8 flags_copy; //you might have to define the bit names as you did with flags.
disable_interrupts(GLOBAL);
flags_copy = flags;
if (flags_copy.sleep)
{
enable_interrupts(GLOBAL);
sleep();
delay_cycles(1);
disable_interrupts(GLOBAL);
}
else
{
enable_interrupts(GLOBAL);
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Jul 20, 2007 12:48 pm |
|
|
Before you execute the Sleep() function, you also need to do
these three things:
1. Clear the "mis-match" condition that causes the interrupt
by reading the CMxCON0 register for the comparator that
you're using. Use the #byte directive to declare the address
of the register, and then read it into a variable with a line of code.
2. Clear the comparator interrupt flag by calling the CCS
clear_interrupt() function, with INT_COMP as the parameter.
Doing items #1 and #2 will clear any pre-existing comparator
interrupt condition, prior to going to sleep.
3. Enable comparator interrupts by using enable_interrupts()
with INT_COMP as the parameter.
Then you can enable Global interrupts and go to sleep.
That's three additional lines of code that you need to add.
Also, inside the comparator isr, you should do item #1. This is in
the data sheet. In addition to that, if you don't want to get any more
comparator interrupts (for the time being), you should call
disable_interrupts(INT_COMP) inside the isr.
Last edited by PCM programmer on Fri Jul 20, 2007 12:49 pm; edited 1 time in total |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Fri Jul 20, 2007 12:49 pm |
|
|
If you want to execute the code after the sleep() when an interrupt occurs then you DONT want to set the GIE bit - just set the interrupt enable flag(s) for the interrupts that you want to happen. When the interrupt occurs the PIC will continue execution after the sleep instruction - if you have the GIE flag set it will jump to the interrupt handler.
Code: | if (flags.sleep)
{
// PIC will halt here until an interrupt
sleep();
// clear interrupt flag here
// do background tasks
} |
|
|
|
plinder13
Joined: 16 Jul 2007 Posts: 2
|
|
Posted: Fri Jul 20, 2007 3:12 pm |
|
|
Let me ask my question a different way.
How do I force my ISRs to execute only at the sleep instruction? I have more than one interrupt that may occur (comparator, port b change, etc.). |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Fri Jul 20, 2007 4:42 pm |
|
|
Let me see if I understand - you want the PIC to be brought out of sleep by an interrupt (which could be from a number of sources), execute some code depending on the interrupt that happened and then go back to sleep? If so, then that is what my code fragment will do - just enable each relevant interrupt, and in the code following the sleep() test the flags to see which interrupt it was.
Code: | // enable interrupts here
if (flags.sleep)
{
// PIC will halt here until an interrupt
sleep();
// test for interrupt - comparator, port b change etc..
// clear interrupt flag here
// do background tasks
} |
Setting the GIE flag will do the same thing but in that case the PIC will execute the interrupt handler(s) first before carrying on from the sleep(). The compiler makes this appear to be a number of ISR's, but in fact there is just one ISR (or two if high priority is used) and the compiler puts in code to work out which interrupt happened.
As long as your interrupts are enabled before calling sleep() you wont get stuck in sleep mode. Even if an interrupting condition happened between you setting the enable and sleep, all that would happen is that the PIC would come out of sleep mode straight away. You wouldnt lose the interrupt, since you have to clear them explicitly. |
|
|
Guest
|
|
Posted: Fri Jul 20, 2007 6:11 pm |
|
|
Quote: | Setting the GIE flag will do the same thing but in that case the PIC will execute the interrupt handler(s) first before carrying on from the sleep(). The compiler makes this appear to be a number of ISR's, but in fact there is just one ISR (or two if high priority is used) and the compiler puts in code to work out which interrupt happened. |
In this scenario, if there are any instructions between the GIE flag being set, and the sleep instruction being executed, and the ISR executes between the GIE being set and the sleep instruction, and clears the interrupt condition, the processor will not exit sleep mode, correct? |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Mon Jul 30, 2007 8:57 am |
|
|
Sorry for delay in replying, back from weeks holiday..
In theory that might happen - the PIC really should have a single instruction that enables global interrupts and puts it to sleep atomically. In reality it probably wouldnt happen, the time difference between setting the GIE bit and then executing sleep would be 4 clocks, surely not enough for an interrupt to be recognised? Maybe.. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Mon Jul 30, 2007 12:10 pm |
|
|
Anonymous wrote: | ...if there are any instructions between the GIE flag being set, and the sleep instruction being executed, and the ISR executes between the GIE being set and the sleep instruction, and clears the interrupt condition, the processor will not exit sleep mode, correct? |
No, you should not set the GIE flag before entering sleep mode. The first posting from SET gives the perfect solution. Leave GIE cleared upon calling sleep, then set the GIE bit _after_ waking up from sleep. If you don’t want to leave GIE enabled all the time, then just leave GIE set for a few instructions after the sleep and then clear it again. To cause a wake-up from sleep, all you need is to have an active interrupt enabled all the way up to, but not including, GIE.
Robert Scott
Real-Time Specialties
Ypsilanti, Michigan |
|
|
Ttelmah Guest
|
|
Posted: Mon Jul 30, 2007 12:14 pm |
|
|
SET wrote: | Sorry for delay in replying, back from weeks holiday..
In theory that might happen - the PIC really should have a single instruction that enables global interrupts and puts it to sleep atomically. In reality it probably wouldnt happen, the time difference between setting the GIE bit and then executing sleep would be 4 clocks, surely not enough for an interrupt to be recognised? Maybe.. |
Not necessary.
If you read the data-sheets, you will find that the manufacturers have thought of this. If the interrupt bit is set at the start of the sleep instruction, the instruction completes as a NOP. If it becomes set after this point, the behaviour is to reset things as if a sleep occured, but not actually sleep.
Best Wishes |
|
|
SET
Joined: 15 Nov 2005 Posts: 161 Location: Glasgow, UK
|
|
Posted: Mon Jul 30, 2007 12:32 pm |
|
|
Thanks for that Tlemah - I assumed that Microchip would have taken it into account but hadn't looked at the exact mechanism.
Yes RLScott, I agree - this is exactly what we do in a number of low-power designs and it works well. |
|
|
|