PIC sleep mode
PIC sleep mode
To save power I want to put my 16F886 IC into sleep mode (or other low power mode if available) overnight and wake it up in morning. Problem is I need to keep counting time during the night. Is there a way with say a timer (or whatever the PIC has that is not completely turned off) of counting time so when I wake the PIC up I can correct the time clock that was running using the internal oscillator in daytime.
The datsheet says 'Timer1 Oscillator
A low-power 32.768 kHz crystal oscillator is built-in between pins T1OSI (input) and T1OSO (amplifier output). The oscillator is enabled by setting the T1OSCEN control bit of the T1CON register. The oscillator will continue to run during Sleep.' and also 'Timer1 can only operate during Sleep when setup in Asynchronous Counter mode. In this mode, an external
crystal or clock source can be used to increment the counter.'
Not clear though how much time it will count. I need about one hour.
So code is something like this:-
At midnight put PIC to sleep
One hour later wake it up to check light level with light meter connected to A/D, to see if it’s light yet.
If dark still, repeat cycle from first line
If light, read the number of hours elapsed in sleep mode and correct the clock.
Does not have to be very accurate at measuring time, +/- 5% is OK. If it can't do as much as one hour it can simply be set to whatever max it can do but means wake up is more frequent so power saving is less.
And can all this be done with Flowcode or will it be lots of lines of C?
The datsheet says 'Timer1 Oscillator
A low-power 32.768 kHz crystal oscillator is built-in between pins T1OSI (input) and T1OSO (amplifier output). The oscillator is enabled by setting the T1OSCEN control bit of the T1CON register. The oscillator will continue to run during Sleep.' and also 'Timer1 can only operate during Sleep when setup in Asynchronous Counter mode. In this mode, an external
crystal or clock source can be used to increment the counter.'
Not clear though how much time it will count. I need about one hour.
So code is something like this:-
At midnight put PIC to sleep
One hour later wake it up to check light level with light meter connected to A/D, to see if it’s light yet.
If dark still, repeat cycle from first line
If light, read the number of hours elapsed in sleep mode and correct the clock.
Does not have to be very accurate at measuring time, +/- 5% is OK. If it can't do as much as one hour it can simply be set to whatever max it can do but means wake up is more frequent so power saving is less.
And can all this be done with Flowcode or will it be lots of lines of C?
So is that a β€yes’ to Timer 1 being a suitable way to implement this?
In Flowcode when the delay function is used is it using a PIC Timer to generate this delay? Which one and how accurate is it? Is it as accurate as the PIC clock? I ask because my main time clock is based on the delay function, but if not that accurate maybe I should use a Timer 0 interrupt instead, as per http://matrixmultimedia.com/mmforums/vi ... php?t=4005
Or are you doing the delay by running around a dummy loop of instruction cycles?
In Flowcode when the delay function is used is it using a PIC Timer to generate this delay? Which one and how accurate is it? Is it as accurate as the PIC clock? I ask because my main time clock is based on the delay function, but if not that accurate maybe I should use a Timer 0 interrupt instead, as per http://matrixmultimedia.com/mmforums/vi ... php?t=4005
Or are you doing the delay by running around a dummy loop of instruction cycles?
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
I would not suggest using the "delay" functions for accurate clock timing. Instead, use one of the in-built timers. The "delay" function does not use these.
I would suggest using an interrupt on Timer1 as the mechanism for incrementing your clock value. Your main routine would then be responsible for displaying the time and deciding when to go to sleep. Also, the time would be maintained even when the unit was asleep.
If you wake the PICmicro up periodically (say, every 10Hz) and keep the interrupt routine to a minimum, that should have a massive power saving.
Also, rather than use an A/D to determine light level, why not use a comparator. Or even use a transistor circuit to create a digital signal for the light threshold which turns the clock on.
I would suggest using an interrupt on Timer1 as the mechanism for incrementing your clock value. Your main routine would then be responsible for displaying the time and deciding when to go to sleep. Also, the time would be maintained even when the unit was asleep.
If you wake the PICmicro up periodically (say, every 10Hz) and keep the interrupt routine to a minimum, that should have a massive power saving.
Also, rather than use an A/D to determine light level, why not use a comparator. Or even use a transistor circuit to create a digital signal for the light threshold which turns the clock on.
Thanks, that explains why I have had trouble getting it to keep to any consistent timekeeping. I shall experiment with the Timers, although I have avoided them so far as I have never really understood them.
Assuming I use Timer 1 how in Flowcode do I implement the second line of this routine?
Display a temperature reading on LCD
Exactly 6 seconds after last loop display the new value
Go to macro that measures the temperatures
Loop for ever counting up time
I was putting a 5.xx second delay for the second line where 5.xx plus the time to process the other instructions = β€exactly’ 6 seconds.
Assuming I use Timer 1 how in Flowcode do I implement the second line of this routine?
Display a temperature reading on LCD
Exactly 6 seconds after last loop display the new value
Go to macro that measures the temperatures
Loop for ever counting up time
I was putting a 5.xx second delay for the second line where 5.xx plus the time to process the other instructions = β€exactly’ 6 seconds.
Last edited by echase on Fri Sep 14, 2007 10:06 am, edited 2 times in total.
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
Assuming your Timer1 interrupt is handling the updating of the clock, you will have some global variables that contain the hour, minute and second values.
Choice 1: Use another variable that increments every six seconds within this interrupt routine. Then re-read the temperature in the main routine each time this variable changes value:
Choice 2: Use the global second variable to make a decision in your main routine:
Choice 1: Use another variable that increments every six seconds within this interrupt routine. Then re-read the temperature in the main routine each time this variable changes value:
Code: Select all
if (SIX_SEC_TMR <> OLD_SIX_SEC_TMR)
read temperature
end if
OLD_SIX_SEC_TMR = SIX_SEC_TMR
Code: Select all
if (SECONDS = 0) || (SECONDS = 6) || (SECONDS = 12) || (SECONDS = 18) || (SECONDS = 24) || (SECONDS = 30) || (SECONDS = 36) || (SECONDS = 42) || (SECONDS = 48) || (SECONDS = 54)
read temperature
end if
Excellent. 4 questions
1) I managed to get timer 0 working but you suggested timer 1 as it runs in sleep mode. Problem is that Flowcode does not seem to implement Timer 1 for a 16F886. Do I have to use the custom interrupt function and set it to use Timer 1? Think that will be beyond me, but I don’t want to waste your time on giving me the C code for it now as I have not yet decided to go down that route
2) I will though use something like your Choice 2. To be clear, does the PIC execute line 1 and then sit at line 2 constantly looping some code until the 6 secs interrupt is reached? If that’s true then the small time that the other lines take won’t slow down the loop from 6 secs to 6 and a bit secs. I will use something similar on the minute, hour and day and need to make sure that any other code executed does not slow it down slightly.
3) Is your C code an expanded version of what the Flowcode β€If’ diamond does?
4) Does β€<>’ mean β€either greater or less than but not equal to’ ?
Errata: the above routine should have said
Display a temperature reading on LCD
Go to macro that measures the temperatures
Exactly 6 seconds after last loop display a new value
Loop for ever counting up time
But this error does not really affect the discussion.
1) I managed to get timer 0 working but you suggested timer 1 as it runs in sleep mode. Problem is that Flowcode does not seem to implement Timer 1 for a 16F886. Do I have to use the custom interrupt function and set it to use Timer 1? Think that will be beyond me, but I don’t want to waste your time on giving me the C code for it now as I have not yet decided to go down that route
2) I will though use something like your Choice 2. To be clear, does the PIC execute line 1 and then sit at line 2 constantly looping some code until the 6 secs interrupt is reached? If that’s true then the small time that the other lines take won’t slow down the loop from 6 secs to 6 and a bit secs. I will use something similar on the minute, hour and day and need to make sure that any other code executed does not slow it down slightly.
3) Is your C code an expanded version of what the Flowcode β€If’ diamond does?
4) Does β€<>’ mean β€either greater or less than but not equal to’ ?
Errata: the above routine should have said
Display a temperature reading on LCD
Go to macro that measures the temperatures
Exactly 6 seconds after last loop display a new value
Loop for ever counting up time
But this error does not really affect the discussion.
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
The FCD file for the 16F877a implements timer1 - you should be able to modify your 886 file accordingly (make sure you retain a backup just in case).echase wrote:1) I managed to get timer 0 working but you suggested timer 1 as it runs in sleep mode. Problem is that Flowcode does not seem to implement Timer 1 for a 16F886. Do I have to use the custom interrupt function and set it to use Timer 1? Think that will be beyond me, but I don’t want to waste your time on giving me the C code for it now as I have not yet decided to go down that route
Because the timer interrupt is being used, the processing within the main loop should not affect the timing. This is because the interrupt is pretty much guaranteed to be fired every 20ms (or whatever period you set it to). As long as you kepp the processing quick within the interrupt, things should go smoothly.echase wrote:2) I will though use something like your Choice 2. To be clear, does the PIC execute line 1 and then sit at line 2 constantly looping some code until the 6 secs interrupt is reached? If that’s true then the small time that the other lines take won’t slow down the loop from 6 secs to 6 and a bit secs. I will use something similar on the minute, hour and day and need to make sure that any other code executed does not slow it down slightly.
Yes - this is my test-based version of Flowcode icons. Not ideal I know, but it should be fairly readable. I hope to update the forum soon to allow inline images and attachments.echase wrote:3) Is your C code an expanded version of what the Flowcode β€If’ diamond does?
It means "not equal to". You can use the C equivalent "!=" if you prefer.echase wrote:4) Does β€<>’ mean β€either greater or less than but not equal to’ ?
You should not need to wait 6 seconds within your main loop. The timer interrupot, once set up properly, will keep your clock variables up-to-date. If you are simply displaying the temperature every 6 seconds, your main routine should just wait until the time has changed by 6 seconds, then read the temperature value and then display it (and then loop back so it waits again).echase wrote:Errata: the above routine should have said
Display a temperature reading on LCD
Go to macro that measures the temperatures
Exactly 6 seconds after last loop display a new value
Loop for ever counting up time
But this error does not really affect the discussion.
Sorry to be thick but presumably you are saying that this code block for choice 2 is not true lines of C that I need to enter as C, as I thought, but is implemented with Flowcode 'commands/symbols'.
In which case presumably it has to be implemented with 10 diamonds as each one can only do one variable comparison?
You said "your main routine should just wait until the time has changed by 6 seconds". The code has to complete this loop 10 times (exactly 60Secs) with each time taking 6 secs
What is the Flowcode for that? Is it:
a) Loop about every 6 seconds (using delay function) around the 'read sensors and display results' LOOP UNTIL a flag is detected as set, that flag being set at 60 secs in the interrupt macro by the 6 seconds passing 10 times. So it’s done using Flowcode’s standard loop command.
Or
b) Loop around the 'read sensors and display results' loop and have some command in there that is waiting for the 6 secs to pass which will skip to the next of the 10 times. What is this command?
Or
c) Loop about every 6 seconds around the 'read sensors and display results' loop and when the timer gets to 60 secs have a standard Flowcode β€jump to new place’ command in the interrupt macro that terminates the loop by jumping beyond it to the next code block (the minute loop).
Or d) Ditch the looping command and just have
if time =0 seconds read temp
If temp =6 read temp
.
.
.
If temp =54 read temp
Call macro that does other stuff that needs to be done every minute(completes in <1 second)
Jump back to if time = 0 seconds AND increment the minute count by one.
In which case presumably it has to be implemented with 10 diamonds as each one can only do one variable comparison?
You said "your main routine should just wait until the time has changed by 6 seconds". The code has to complete this loop 10 times (exactly 60Secs) with each time taking 6 secs
What is the Flowcode for that? Is it:
a) Loop about every 6 seconds (using delay function) around the 'read sensors and display results' LOOP UNTIL a flag is detected as set, that flag being set at 60 secs in the interrupt macro by the 6 seconds passing 10 times. So it’s done using Flowcode’s standard loop command.
Or
b) Loop around the 'read sensors and display results' loop and have some command in there that is waiting for the 6 secs to pass which will skip to the next of the 10 times. What is this command?
Or
c) Loop about every 6 seconds around the 'read sensors and display results' loop and when the timer gets to 60 secs have a standard Flowcode β€jump to new place’ command in the interrupt macro that terminates the loop by jumping beyond it to the next code block (the minute loop).
Or d) Ditch the looping command and just have
if time =0 seconds read temp
If temp =6 read temp
.
.
.
If temp =54 read temp
Call macro that does other stuff that needs to be done every minute(completes in <1 second)
Jump back to if time = 0 seconds AND increment the minute count by one.
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
The "choice 2" code can be implemented with a single decision icon with the following in its "if" box:
The main routine will be something like this:
The use of "OLD_SECONDS" will prevent the ADC reading being taken continually for each second that is a multiple of 6.
Obviously you will need a mechanism for sending the PICmicro to sleep and waking it up - I'm not sure how you'd do that.
The Timer 1 interrupt would be something like this:
Code: Select all
(SECONDS = 0) || (SECONDS = 6) || (SECONDS = 12) || (SECONDS = 18) || (SECONDS = 24) || (SECONDS = 30) || (SECONDS = 36) || (SECONDS = 42) || (SECONDS = 48) || (SECONDS = 54)
Code: Select all
loop forever
if SECONDS is a factor of 6 (i.e. the decision above)
if SECONDS <> OLD_SECONDS
read the temperature and update the display
OLD_SECONDS = SECONDS
end if
end if
end loop
Obviously you will need a mechanism for sending the PICmicro to sleep and waking it up - I'm not sure how you'd do that.
The Timer 1 interrupt would be something like this:
Code: Select all
if FRACTION = 20 (or whatever is appropriate to get seconds from the interrupt frequency)
SECONDS = SECONDS + 1
If SECONDS >= 60
MINUTE = MINUTES + 1
If MINUTES >= 60
HOURS = HOURS + 1
If HOURS >= 24
HOURS = 0
End if
End if
End if
End if
That quote is a composite from 2 different threads. I am using Timer 0. I have implemented all this in ONE Timer 0 interrupt macro driven at 61Hz and it calculates days, hours, min, secs (in 6 sec blocks) fine. 6secs is a count of 61x6=366. Clock speed 125kHz and prescaler 1:2.steve wrote:
Use the Timer0 interrupt set to say 75Hz. In the interrupt routine, increment a variable. If this variable reaches 75 (or whatever the interrupt frequemcy is), then you know that 1 second has elapsed and you will be able to increment the variables that represent the seconds, minutes and hours
The Timer 1 interrupt would be something like this:
Code: Select all
if FRACTION = 20 (or whatever is appropriate to get seconds from the interrupt frequency) SECONDS = SECONDS + 1 If SECONDS >= 60 MINUTE = MINUTES + 1 If MINUTES >= 60 HOURS = HOURS + 1 If HOURS >= 24 HOURS = 0 End if End if End if End if
I have not implemented sleep mode yet. Need to get the code fully working before adding that complexity.
1) BUT several times a day the LCD goes blank or freezes up (whereas before I implemented this Timer 0 my code was stable). PIC still reacts to button presses when LCD frozen, albeit incorrectly, so the PIC is still running something. I suspect there is a timing issue in that this macro takes too long to run and so upsets the main code and other macros that tell the LCD what to display. Do you think that may be the case? I could put the minute/hour/day calcs in the main code instead to speed macro up.
2) What happens if it's in the middle of this macro and a button is pressed calling a Port B interrupt? Can the PIC react to 2 interrupts at same time or will PIC β€crash’?
3) In the Flowcode simulation time sticks at 00:00:00 and no time variables increment. Why? It may be OK if I slow the simulation down to >100 simulated secs = 1 real seconds, but it’s incrementing so slowly then that I have not had time to run it long enough to really check it. Real PIC is fine.
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
(1)
I think you are right. A prescaler of 1:2 for the timer interrupt might not be long enough to get the calculations done (the PIC will essentially interrupt every 512 instructions).
Can you use a faster clock? This would give more time for calculations and if you chose an appropriate clock you would get an accurate divide for the second count.
(2)
The interrupts should both occur.
(3)
The simulation of the timer interrupt is not very good at the moment. I am working on improving this for v4.
I think you are right. A prescaler of 1:2 for the timer interrupt might not be long enough to get the calculations done (the PIC will essentially interrupt every 512 instructions).
Can you use a faster clock? This would give more time for calculations and if you chose an appropriate clock you would get an accurate divide for the second count.
(2)
The interrupts should both occur.
(3)
The simulation of the timer interrupt is not very good at the moment. I am working on improving this for v4.
1) When you talk about the calculations do you mean the ones that do minutes, hours etc within this interrupt macro? Or the main code?
Don't want to increase clock frequency as uses more power. But could change the prescaler to say 1:4 or 1:1; would that help? Which way does it need to go?
3) Any chance of adding a Timer 1 to 16F886 whilst you are doing v4?
Don't want to increase clock frequency as uses more power. But could change the prescaler to say 1:4 or 1:1; would that help? Which way does it need to go?
3) Any chance of adding a Timer 1 to 16F886 whilst you are doing v4?
- Steve
- Matrix Staff
- Posts: 3433
- Joined: Tue Jan 03, 2006 3:59 pm
- Has thanked: 114 times
- Been thanked: 422 times
I mean the main code. Changing to 1:4 would be better (you would get twice as much time between interrupts).
I've not thoroughly tested the timer1 yet, so I did not roll it out to all FCD files. But I will. For now, you can simply copy the [TMR1] section from the 16F877a to the 16F886 FCD file. You also need to change the [Interrupts] section so it is the same as the one in the 877a FCD file.
I've not thoroughly tested the timer1 yet, so I did not roll it out to all FCD files. But I will. For now, you can simply copy the [TMR1] section from the 16F877a to the 16F886 FCD file. You also need to change the [Interrupts] section so it is the same as the one in the 877a FCD file.
[quote="steve(3)
The simulation of the timer interrupt is not very good at the moment. I am working on improving this for v4.[/quote]
If the simulation is not very good how are the guys in this thread getting it to work?
http://matrixmultimedia.com/mmforums/vi ... php?t=4005
When I try it no variables increment at all so my clock is always at 00:00:00. Without incrementing (even if erratic or inaccurate) my whole software can't run in simulation mode as it does not get past the first temperature reading at the beginning of the programme. So it’s very difficult to use Flowcode to write any software using Timer 0 interrupts. Any thoughts for a quick fix to get it going?
Time is OK on PIC though apart from the LCD freezing problem, which is probably fixable.
The simulation of the timer interrupt is not very good at the moment. I am working on improving this for v4.[/quote]
If the simulation is not very good how are the guys in this thread getting it to work?
http://matrixmultimedia.com/mmforums/vi ... php?t=4005
When I try it no variables increment at all so my clock is always at 00:00:00. Without incrementing (even if erratic or inaccurate) my whole software can't run in simulation mode as it does not get past the first temperature reading at the beginning of the programme. So it’s very difficult to use Flowcode to write any software using Timer 0 interrupts. Any thoughts for a quick fix to get it going?
Time is OK on PIC though apart from the LCD freezing problem, which is probably fixable.
Actually I now find it is incrementing but 25 times too slowly which is not useful. I can temporarily change the interrupt flag to 25 times less to aprox simulate the timing, but have to remember to reset it again before programming the PIC.echase wrote:[quote="steve(3)
When I try it no variables increment at all so my clock is always at 00:00:00.
Time is OK on PIC though .
Is there a line in the 886.FCD file that I can change to correct out this timing error?
I am running the simulation 'as fast as possible' and the delays are the correct length but not this interrupt.