|
|
View previous topic :: View next topic |
Author |
Message |
Liam_85
Joined: 09 Nov 2014 Posts: 26 Location: Ireland
|
Sleep mode, RTC & decreased oscillator speed [SOLVED] |
Posted: Fri Feb 06, 2015 5:35 pm |
|
|
Hi everybody, hope somebody can help with a few problems I am having.
So basically what I am trying to do:
I am running a PIC24HJ at 80MHz with an external crystal at the start of the program. I didn't declare any oscillator fuses but I did use "#use delay(clock=80MHz, crystal=20MHz)" and that sets the fuses and PLL for me. Then I decrease the oscillator speed to 500kHz using code I got from ae6zw, http://www.ccsinfo.com/forum/viewtopic.php?p=155886. The reason I use this code is because setup_oscillator() doesn't seem to work right with PIC24/DSPIC33 uControllers. Then I enter sleep mode and two things wake up the PIC, either a high to low transition on EXT1 or the timer1 RTC rolling over, the RTC timer rolls over and goes back to sleep 10 times before proceeding. When 10 seconds has passed or an ext interrupt has occurred it wakes back up and using the code again from ae6zw, up the speed to internal 8MHz (I will up it to 80MHz with the external crystal eventually but have not looked into it yet). The problem I am having is when I don't alter the uC speed the program works fine, both ext interrupts and RTC counting to 10. But if i do change the speed the program never wakes to update the RTC but an external interrupt will wake the PIC. I have a slimmed down version of the most important parts of the code:
Code: | #fuses NOIOL1WAY // Allows multiple reconfigurations of peripheral pins
#fuses CKSNOFSM // Clock Switching is enabled, fail Safe clock monitor is disabled
#fuses NOIOL1WAY // Allows multiple reconfigurations of peripheral pins
#use delay(clock=80000000, crystal=20000000)
#pin_select U1TX = pin_b7
#pin_select U1RX = pin_b8
#use rs232(baud = 1200, xmit = PIN_b7, rcv = PIN_b8, bits=8, errors, stream = ftdi)
#pin_select INT1 = pin_b8 // External interrupt on pin b8
// Use pin b8 as dual function, int on change and rx pin
// depending on what int is set uses this pin
#define interrupt_enabled(x) !!(*make8(x,1) & make8(x,0))
// Next 2 functions is from ae6zw
void setup_oscillator2( int16 int16_OSCCON_01, int8 int8_CLK_DIV )
{
int8 int8_temp_01, int8_temp_02, int8_temp_03;
int8_temp_01 = ( int8 ) ( int16_OSCCON_01 / 0x0100 ); // higher byte
int8_temp_02 = ( int8 ) ( int16_OSCCON_01 & 0x00ff ); // lower byte
int8_temp_03 = ( int8_CLK_DIV );
#ASM
;OSCCON (high byte) Unlock Sequence
MOV.B ui8_temp_01, W0
MOV #0x743, W1
MOV #0x78, W2
MOV #0x9A, W3
MOV.B W2, [ W1 ]
MOV.B W3, [ W1 ]
MOV.B W0, [ W1 ]
;OSCCON (low byte) Unlock Sequence Start oscillator switch operation
MOV.B ui8_temp_02, W0
MOV #0x742, W1
MOV #0x46, W2
MOV #0x57, W3
MOV.B W2, [ W1 ]
MOV.B W3, [ W1 ]
MOV.B W0, [ W1 ]
//BSET 0x742, #0 ;OSWEN
;CLKDIV ( high byte )
MOV #0x745, W1
MOV.B ui8_temp_03, W0
MOV.B W0, [ W1 ]
#ENDASM
}
void setup_clk( int8 int8_clk )
{
// 0 -- 32K internal // 1 -- 32k secondary OSC // 2 -- 500k Internal //
switch ( int8_clk)
{
case 1:
output_low(LED1);
setup_oscillator2( 0x0601, 0x00 ); // 500 kHz , 258 uA
delay_us(1);
break;
case 2:
output_low(LED3);
setup_oscillator2( 0x0701, 0x00 ); // 8 MHz div 1 = 8 MHz, 2.66 mA
delay_us(1);
break;
}
}
// When EXT1 enabled a transition on this pin it will cause the pic
// to awake from sleep. T1, ext int are two allowed reasons for
// waking from sleep.
// A dummy received char from the pic12 gets this int to run
#int_EXT1 Level = 4
void EXT1_isr(void)
{
i_sleep_timer = 0; // Set i_sleep_timer = to 0 so while(sleep) will break
disable_interrupts(INT_EXT1); // Disable ext int because its not needed anymore
}
// When Timer1 rolls over from 0xFFFF to 0x0000,
// this interrupt routine will be executed.
#int_timer1
void timer1_isr(void)
{
if(i_sleep_timer)
{
i_sleep_timer--;
timer1_stop();
bit_set(TMR1, 15); // Set Timer1 MSB to 0x80, same as the line above
timer1_start();
output_toggle(LED1);
}
}
// Sleep for the specified time of 1 to 255 seconds,
// Note: The PIC will actually wake-up once per second
// briefly, and check the remaining time.
void sleep_for_x_seconds(int8 seconds)
{
int8 global_interrupts_enabled;
i_sleep_timer = seconds; // Load sleep count into global variable
// Preset Timer1 so it will roll over in 1 second.
timer1_stop();
set_timer1(32768);
timer1_start();
if(interrupt_enabled(INTR_GLOBAL)) // If global ints were enabled before this function set a flag
// so that this is known.
global_interrupts_enabled = TRUE;
f_sleep_flag = true; // set sleep flag so rda int knows to
// wake by received char, not used
setup_clk(1); // Run clock down to 500kHz
clear_interrupt(INT_EXT1); // Enable required ints for 2 methods of waking from sleep
clear_interrupt(INT_TIMER1); // EXT1 & t1 overflow wakes the PIC
EXT_INT_EDGE(1, H_TO_L); // Received high to low pulse from PIC12 (a dummy byte)
enable_interrupts(INT_EXT1);
enable_interrupts(INT_TIMER1);
enable_interrupts(INTR_GLOBAL);
// Wait in this loop until the desired delay in seconds is done.
while(i_sleep_timer)
{
sleep();
}
delay_ms(1); //Required for osc to get up and running
output_low(LED2);
setup_clk(2); // Run clock up to 8MHz
clear_interrupt(INT_RDA); // Set RS232 Rx ints active
enable_interrupts(INT_RDA);
// If global interrupts were disabled outside of
// of this routine, then disable them before we exit.
if(global_interrupts_enabled == FALSE)
disable_interrupts(INTR_GLOBAL);
}
//==========================================
void main()
{
setup_timer1(T1_EXTERNAL_RTC | TMR_DIV_BY_1);
disable_interrupts(INT_EXT1);
while(true){
output_high(LED1);
output_high(LED2);
output_high(LED3);
fprintf(ftdi, "Running... \n\n\r");
//delay_ms(1000);
//output_low(LED1); // Turn on LED1 to show start of sleep.
sleep_for_x_seconds(10);
//output_low(LED2); // Turn on LED 2 to show end of sleep.
}
} |
Thanks guys for your help. _________________ Liam Hanmore
Last edited by Liam_85 on Sat Feb 07, 2015 3:06 pm; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Sat Feb 07, 2015 2:05 am |
|
|
Always, always, always _tell us the actual chip_. It'd only be a matter of leaving it's name in the code posted, but it matters.....
However the answer screams at me. Look at what 'sleep' does by default:
(from one data sheet in the family):
"Sleep mode has these features:
• The system clock source is shut down. If an
on-chip oscillator is used, it is turned off"
Note 'oscillator is turned off'.
Then look at what sleep(SLEEP_IDLE); does.
The only clock that stays running in the full sleep mode, is the LPRC clock, and this only if the watchdog is enabled. |
|
|
Liam_85
Joined: 09 Nov 2014 Posts: 26 Location: Ireland
|
|
Posted: Sat Feb 07, 2015 7:25 am |
|
|
It's a PIC24HJ128GP502 Ttelmah. I understand the core is turned off during sleep but an external 32.768kHz crystal still runs timer 1 during that time and when it rolls over it wakes the uC. I'm sorry maybe I didn't make it clear I was using an external crystal to increment timer 1.
I also know that the core is still running during idle but this uses to much power since I need the uC to run off a coin cell.
Anyways, as I said earlier If I don't run the core down to 500 kHz timer 1 works fine. It wakes up every second and goes back to sleep. But if I do run the core down to 500 kHz timer 1 appears to not run and only an external interrupt will wake the uC instead of either interrupt. _________________ Liam Hanmore |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Sat Feb 07, 2015 9:28 am |
|
|
Get document 70308B.pdf
Modify the clock switch code to match this.
Currently it is not correct, and because of the way it is done, results in the LPOSCEN bit getting disabled.... |
|
|
Liam_85
Joined: 09 Nov 2014 Posts: 26 Location: Ireland
|
|
Posted: Sat Feb 07, 2015 3:03 pm |
|
|
Thank you very much Ttelmah, that was a very quick and easy fix. I thought that bit was to do with running the uC core using the secondary oscillator. Instead it just enabled it for me.
I simply changed every setup_oscillator2( ) function to setup_oscillator2( 0xXXX3, 0xXX ); The 3 enables the secondary oscillator and the oscillator switch enable bit.
Another question I have for you if you don't mind regarding #pin select. I am using pin b8 as the RS232 Rx pin and an external interrupt on change pin. I have declared at the top of the code;
#pin_select U1RX = pin_b8
and
#pin_select INT1 = pin_b8.
When the uC is asleep either the timer or a dummy received char on pin 8 from another uC wakes it up. I disable RDA interrupts and enable EXT1 interrupts when I want interrupt on change capability to detect the dummy char then I disable EXT1 interrupts and enable RDA interrupts only when I want RS232 receive interrupts. In doing this it works fine every time.
I know using the fuse NOIOL1WAY allows multiple reconfigurations of peripheral pins but my question is if this is correct programming or is there a better way to do this.
Thanks again for your help Ttelmah _________________ Liam Hanmore |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19498
|
|
Posted: Sat Feb 07, 2015 3:40 pm |
|
|
You are not reconfiguring the pins, just changing which interrupt is enabled. You can do this as often as you want.
However you don't need to do this. The UART on this chip has the ability to trigger a wake on a start bit for you. If you enable the bit 'WAKE' in the U1MODE register, before going to sleep, it'll generate a RX interrupt when the incoming serial line falls, just like the external interrupt. It does this without needing the UART to be clocked. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|