View previous topic :: View next topic |
Author |
Message |
armondo_522
Joined: 20 Mar 2010 Posts: 9
|
Question on waking up from sleep (ULPWU) |
Posted: Sat Mar 20, 2010 9:51 am |
|
|
Hi,
I am using a PIC16F886. I want to use the ULPWU function. I would like the pic to wake up periodically or by an external interrupt. The ULPWU interrupt works but when it wakes up it goes to the ULPWU isr. I would like any interrupt to just wake up processor and perform code after the sleep statement without going to a ISR. Is this possible?
I am using CCS compiler the latest version.
Code: |
main()
{
setup_adc_ports(sAN1|sAN2|sAN3|VSS_VDD);
setup_adc(ADC_CLOCK_DIV_8);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
setup_timer_2(T2_DISABLED,0,1);
setup_ccp1(CCP_OFF);
setup_comparator(NC_NC_NC_NC);// This device COMP currently not supported by the PICWizard
/* these interrupts are enabled after wake up from ulpwu
// enable_interrupts(INT_EXT);
// enable_interrupts(INT_EXT|INT_EXT_H2L);
// enable_interrupts(INT_EXT_H2L);
//enable_interrupts(INT_TIMER1);
// enable_interrupts(INT_TIMER0);
//
*/
enable_interrupts(INT_ULPWU); // must be enabled, works but always goes to isr I want to goto next statement after sleep / nop
// if not enabled does not wake up on time out
enable_interrupts(GLOBAL); // global interrupt enable
setup_oscillator(OSC_4mHZ);
port_b_pullups(0x01);
while(1) // main sleep loop will stay in here
{
output_high(pyro_enbl);
output_high(gain_enbl);
output_high(light_enbl);
sleep_ulpwu(400); // this puts out 400us pulse to charge cap and put to sleep
//delay_cycles(1);
// I would like to wake up and go here regardless of the interrupt cause then return to main sleep loop I will determine cause of interrupt
// below is test code only.
do
{
/*
now we have to go and see why we woke up.
check status of radio int if low, then get status and word
next check to see light level and put in buffer
next check pyro and get level to store
if was normal ulpwu then go back to sleep
else
turn off ulpwu int
turn on light if warranted. ie radio or change or pyro
*/
if (!input(radio_int))
{
/*
go read radio status
*/
}
set_adc_channel (1);
light_in=get_voltage();
if(light_in > 700)
{
shift_left(upper_level,1,1);
}
if(light_in < 300)
{
shift_left(lower_level,1,1);
}
if ((upper_level==7 & Lower_level==1) & (light_in < 200)) //check result of filter
{
// light was on and went off
// turn on light
lite_on=true;
}
if (light_in<200)
{
set_adc_channel (3); //get pyro reading
pyro_in=get_voltage();
if (pyro_in>last_pyro_in)
{
lite_on=true; /// have to add some filtering here
}
last_pyro_in=pyro_in;
}
lite_on=true;
if (lite_on)
{
full_power();
while(lite_on & (sec_run< 10))
{
}
two_thirds_power();
while(lite_on & (sec_run< 20))
{
}
one_third_power();
while(lite_on & (sec_run< 30))
{
}
output_low(led_cntl);
} /// end of if lite on
}while(1);
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Mar 21, 2010 3:48 pm |
|
|
I think there are problems with the CCS sleep_ulpwu() function.
Rather than try and fix it right now, I've posted a work-around routine
and a test program below.
I have a 100 nF (0.1 uF) capacitor connected between pin A0 and ground.
The code program will charge up the capacitor, go to sleep, then let the
cap bleed off it's voltage to ground, and then wake-up. Two LEDs show
the progress of the program. I timed it with a stopwatch, and it takes
about 1.6 seconds for the 2nd LED to turn on. The charge time of 1ms
is overkill. It could probably be reduced to 100us for this cap size.
Code: |
#include <16F886.h>
#fuses INTRC_IO, NOWDT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)
#byte PCON = 0x8E // Register address for 16F886
#bit ULPWUE = PCON.5
#define INT_PEIE 0x0B40
#define my_sleep_ulpwu(x) \
output_high(PIN_A0); \
if(x / 1000) \
delay_ms(x / 1000); \
if(x % 1000) \
delay_us(x % 1000); \
clear_interrupt(INT_ULPWU); \
ULPWUE = 1; \
output_float(PIN_A0); \
enable_interrupts(INT_PEIE); \
enable_interrupts(INT_ULPWU); \
sleep(); \
disable_interrupts(INT_ULPWU)
//=======================================
void main()
{
output_high(PIN_B0); // Turn on LED0 to show program start
my_sleep_ulpwu(1000); // 1000us charge time for 100 nF cap
output_high(PIN_B1); // Turn on LED1 to show ULP wake-up.
while(1);
} |
|
|
|
armondo_522
Joined: 20 Mar 2010 Posts: 9
|
thanks |
Posted: Sun Mar 21, 2010 3:56 pm |
|
|
Thank you, I think my biggest problem was that I enabled the global interupt and that brought me to the isr instead of the statement after sleep, but I will try your method to give me a work around in case the ccs version does not work.
Thanks again, your responses are always right on the money.
Regards, |
|
|
Pulsartomi
Joined: 26 May 2010 Posts: 17
|
|
Posted: Wed May 26, 2010 1:17 pm |
|
|
Thank You for providing this code, solved my problem with wake up with ulwp pin. CCS not seems to be working with sleep_ulwp(); |
|
|
Pulsartomi
Joined: 26 May 2010 Posts: 17
|
|
Posted: Thu May 27, 2010 7:11 am |
|
|
VERY BIG ATTENTION:
disable other interrupts if you enabled before (for eg TIMER1), or it will wake up before it would be desired (the ulpwu timing) even if global interrupts is disabled!
If you are not sure what is the wake up timing, use a voltmeter on the capacitor. Although it will discharge the capacitor faster because of the impedance of the voltmeter. |
|
|
Pulsartomi
Joined: 26 May 2010 Posts: 17
|
|
Posted: Sun Jun 06, 2010 1:50 pm |
|
|
New problem
I would like to use this routine with 18f46j11, but in compiler there is no INT_ULPWU defined for this device (although it has ulpwu).
Before i wanted to use sleep_ulpwu(); but compiler returned an error: undefined identifier (use delay is ok, because delay_ms(); is working)
Does anybody know why is it undefined?With other device it is "defined".
My question would be if i can use somewhat sleep_ulwpu function.
And the other: where can I find int_uplwu?
Thanks
Here are the defined int addresses:
Code: |
////////////////////////////////////////////////////////////////// INT
// Interrupt Functions: ENABLE_INTERRUPTS(), DISABLE_INTERRUPTS(),
// CLEAR_INTERRUPT(), INTERRUPT_ACTIVE(),
// EXT_INT_EDGE()
//
// Constants used in EXT_INT_EDGE() are:
#define L_TO_H 0x40
#define H_TO_L 0
// Constants used in ENABLE/DISABLE_INTERRUPTS() are:
#define GLOBAL 0xF2C0
#define INT_RTCC 0x00F220
#define INT_TIMER0 0x00F220
#define INT_TIMER1 0x009D01
#define INT_TIMER2 0x009D02
#define INT_TIMER3 0x00A002
#define INT_EXT_L2H 0x5000F210
#define INT_EXT_H2L 0x6000F210
#define INT_EXT 0x00F210
#define INT_EXT1_L2H 0x5001F008
#define INT_EXT1_H2L 0x6001F008
#define INT_EXT1 0x00F008
#define INT_EXT2_L2H 0x5002F010
#define INT_EXT2_H2L 0x6002F010
#define INT_EXT2 0x00F010
#define INT_RB 0x00FFF208
#define INT_AD 0x009D40
#define INT_RDA 0x009D20
#define INT_TBE 0x009D10
#define INT_SSP 0x009D08
#define INT_CCP1 0x009D04
#define INT_CCP2 0x00A001
#define INT_BUSCOL 0x00A008
#define INT_COMP 0x00A040
#define INT_RDA2 0x00A320
#define INT_TBE2 0x00A310
#define INT_TIMER4 0x00A308
#define INT_OSCF 0x00A080
#define INT_BUSCOL2 0x00A340
#define INT_EXT3_L2H 0x5003F020
#define INT_EXT3_H2L 0x6003F020
#define INT_EXT3 0x00F020
#define INT_PMP 0x009D80
#define INT_SSP2 0x00A380
#define INT_RTC 0x00A301 |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 06, 2010 3:16 pm |
|
|
Look in the 18F46J11 data sheet. It doesn't mention a ULPWU interrupt.
It says to use an INTx interrupt:
Quote: |
3.7 Ultra Low-Power Wake-up
The Ultra Low-Power Wake-up (ULPWU) on RA0 allows
a slow falling voltage to generate an interrupt without
excess current consumption.
Follow these steps to use this feature:
1. Configure a remappable output pin to output the
ULPOUT signal.
2. Map an INTx interrupt-on-change input function
to the same pin as used for the ULPOUT output
function. Alternatively, in step 1, configure
ULPOUT to output onto a PORTB
interrupt-on-change pin.
|
In CCS, the pin mapping is done with the #pin_select directive.
The 18F46J11 data sheet has sample code here:
Quote: |
EXAMPLE 3-1: ULTRA LOW-POWER WAKE-UP INITIALIZATION
|
That code can be translated to CCS, by using #byte and #bit statements
to allow direct access to register bits. CCS delay_us() or delay_ms()
can be used to do the required delay time. Etc.
For example 3-1, the Pin Select statements for CCS that do the same
thing as the top two lines in the example code, would look like this:
Code: |
#include <18F46J11.h>
#fuses HS,NOWDT
#use delay(clock=20000000)
#pin_select ULPOUT=PIN_D4
#pin_select INT1=PIN_D4
//=====================================
void main()
{
while(1);
}
|
You should attempt to write the code by yourself and debug it. |
|
|
Pulsartomi
Joined: 26 May 2010 Posts: 17
|
|
Posted: Sun Jun 06, 2010 3:21 pm |
|
|
At the same time You replied, I've read that in the datasheet. I've just started to write the code and didn't know this mapping command.
Thank You very much for your help
Best Regards, Tomi |
|
|
Pulsartomi
Joined: 26 May 2010 Posts: 17
|
|
Posted: Sun Jun 06, 2010 5:36 pm |
|
|
bit_set(INTCON,6); //don't forget to enable peripherial interrupts before sleep(); |
|
|
|