|
|
View previous topic :: View next topic |
Author |
Message |
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
Wakeup from Sleep - Multi-Button IOC - SOLVED and improved |
Posted: Fri Aug 26, 2016 9:50 pm |
|
|
Compiler: PCM 5.048
PIC16LF1575 programmed via ICD-U64
Hi,
I'm trying to use the IOC pins to wake up the processor from sleep via one of several buttons. When the system wakes up I need to determine which button was pressed, do my work and go back to sleep.
I've browsed through various posts on how to do this and apparently am still confused since my test code doesn't work as expected. I based my code on this post:
http://www.ccsinfo.com/forum/viewtopic.php?t=54075
What I glean from this and other posts is that when the interrupt occurs I need to determine which interrupt occurred, read the pin, clear the flag and CCS clears the interrupt. In the code below, I appear to go to sleep as expected, as checked with my ammeter. Pushing one of the buttons increases power requirements briefly and then drops again so something is waking up and going back to sleep, but it's not behaving as expected.
As an aside, I tried to turn on LEDs inside the ISRs but apparently with this chip or compiler that gives me a warning and turns off interrupts. I've done short delays in ISRs on other projects so I'm additionally puzzled.
(I haven't written any CCS code in a while so I'm probably overlooking something obvious.)
Any advice will be appreciated. Codes comments include connections to buttons and lights.
Code: | #case
// 1575SleepIOC.c IOC wake from sleep testing
//
// Use any IOC to wake from deep sleep
// Let us know which pin woke us up, can be multiple ports!
//
// Device: Microchip PIC15LF1575
// Compiler: Custom Computer Services, Inc. PCWH 5.x
// Programmer: CCS ICD-U64
//
// + VDD - 1 14 - VSS -
// RA5 - 2 13 - RA0/DAT -> debug output
// RA4 - 3 12 - RA1/CLK -> HBLED
// MCLR/RA3 - 4 11 - RA2 -> PB STAFF1
// RC5 - 5 10 - RC0
// INTLED <- RC4 - 6 9 - RC1
// PB STAFF3 -> RC3 - 7 8 - RC2 -> PB STAFF2
//
// - All ports support internal weak pullups
// - IOC available on all I/O pins
// - 25mA sink/source on I/O pins
// - use PPS to define UART
//
// 2018.08.26 - Jurgen G Schmidt
#include <16LF1575.h>
#fuses INTRC_IO //Internal RC Osc, no output
#fuses NOWDT //No Watch Dog Timer
#fuses NOPUT //No Power Up Timer
#fuses NOMCLR //Plain i/o, no reset
#fuses NOPROTECT
#fuses NOBROWNOUT //No brownout reset
#fuses NOCLKOUT
#fuses NOWRT
#fuses NOSTVREN
#fuses NOLVP
#fuses NODEBUG
//--------------------------- Hardware Definitions ----------------------------
#pin_select TX1 = PIN_A5 // H/W UART
#pin_select RX1 = PIN_A4
#define HBLED PIN_A1 // heartbeat LED
#define INTLED PIN_C5 // we made it to the interrupt !
#define PFET PIN_C4 // pMosfet power switch for external devices
#use delay(internal=32M)
#use rs232( BAUD=38400, XMIT=PIN_A0, INVERT, STREAM=JGS_DBG )
typedef unsigned int8 UINT8;
typedef unsigned int16 UINT16;
typedef int1 BIT;
//--------------------------- CONSTANTS ---------------------------------------
#define STAFF1 PIN_A2 // 98 ID for button which woke us up
#define STAFF2 PIN_C0 // 112
#define STAFF3 PIN_C3 // 115
#byte IOCAF = 0x393 //Map IOC Flag Registers
#byte IOCCF = 0x399
#bit IOC1 = IOCAF.2
#bit IOC2 = IOCCF.0
#bit IOC3 = IOCCF.3
//--------------------------- Global Variables --------------------------------
UINT8 intWakePin;
//--------------------------- Interrupt Handlers ------------------------------
#INT_RA
void int_ioc_A()
{
UINT8 temp;
// find out which of the IOC pins woke us up
if( interrupt_active( INT_RA2 ) ) { // Was it on RA2 ?
intWakePin = STAFF1; // save button name
temp = input( STAFF1 ); // read port pin
IOC1 = 0; // clear flag
return; // exit
}
}
#INT_RC
void int_ioc_C()
{
UINT8 temp;
// find out which of the IOC pins woke us up
if( interrupt_active( INT_RC0 ) ) {
intWakePin = STAFF2;
temp = input( STAFF2 );
IOC2 = 0;
return;
}
if( interrupt_active( INT_RC3 ) ) {
intWakePin = STAFF3;
temp = input( STAFF3 );
IOC3 = 0;
return;
}
}
//--------------------------- Support Functions -------------------------------
void HWSetup( void ); // hardware setup after power-on / reset
void HWSleep( void ); // prepare hardware for low power sleep
//-----------------------------------------------------------------------------
void main()
{
UINT16 i;
// Power-up proof of life and correct speed...
output_high( HBLED );
delay_ms( 1000 );
output_low( HBLED );
fprintf(JGS_DBG,"\r\n\%s %s\r\n", __FILENAME__, __TIME__);
delay_ms( 300 );
while( TRUE ) { //------ BIG loop - we stay here forever, even asleep!
HWSleep(); // go to sleep after power-up, and from a continue at this level
HWSetup();
//------ Start work...
for( i=0; i<5; i++ ) {
output_high( HBLED );
delay_ms( 100 );
output_low( HBLED );
delay_ms( 100 );
}
fprintf(JGS_DBG, "Button %d pressed\r\n", intWakePin);
delay_ms( 200 );
} //------ end of work and BIG loop - go to sleep now
}
//-----------------------------------------------------------------------------
void HWSetup( void )
{
setup_adc_ports( NO_ANALOGS); // ALWAYS, Always, always do this !!!
}//-- end of HWSetup()
//------------------------------------------------------------------------------
void HWSleep( void )
{
//-- power down what we can...
setup_adc_ports( NO_ANALOGS); // Disable all internal devices
setup_vref( VREF_OFF ); // uses 16uA when on
setup_timer_1( T1_DISABLED );
setup_timer_2( 0, 0, 1 );
output_a( 0x00 ); // turn off everything
output_c( 0x00 ); // turn off everything
output_high( PFET ); // power off external perpherals
enable_interrupts( INT_RA ); // enable IOC
enable_interrupts( INT_RA ); // enable IOC
enable_interrupts( GLOBAL ); // don't need GLOBAL if no handler and continue after sleep
sleep(); // go to sleep
disable_interrupts( INT_RA );
disable_interrupts( INT_RC );
disable_interrupts( GLOBAL );
// resume our work loop...
}//-- end of HWSleep()
//-----------------------------------------------------------------------------
//------ end of 1575SleepIOC.c ------ |
_________________ Jürgen
www.jgscraft.com
Last edited by jgschmidt on Sun Aug 28, 2016 9:42 pm; edited 2 times in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Aug 26, 2016 10:34 pm |
|
|
Quote: | void HWSleep( void )
{
//-- power down what we can...
setup_adc_ports( NO_ANALOGS); // Disable all internal devices
setup_vref( VREF_OFF ); // uses 16uA when on
setup_timer_1( T1_DISABLED );
setup_timer_2( 0, 0, 1 );
output_a( 0x00 ); // turn off everything
output_c( 0x00 ); // turn off everything
|
The code above sets all pins on Ports A and C to be low level output pins.
In that case, you can't use them to detect a button press. You need
some of them to be input pins for that. Note that the sample code you
are referring to has enabled #fast_io() for the desired port or ports.
If #fast_io is enabled, the TRIS is not changed for output_a() and output_c().
Also, do you have external pull-up resistors on the interrupt-on-change pins ?
Quote: | output_high( PFET ); // power off external perpherals
enable_interrupts( INT_RA ); // enable IOC
enable_interrupts( INT_RA ); // enable IOC
enable_interrupts( GLOBAL ); // don't need GLOBAL if no handler and continue after sleep
sleep(); // go to sleep
disable_interrupts( INT_RA );
disable_interrupts( INT_RC );
disable_interrupts( GLOBAL );
// resume our work loop...
}//-- end of HWSleep()
|
You have two calls to enable INT_RA in the code above. The 2nd one
must be a typo. You must have intended for it to be INT_RC.
There is actually a lot more that can be written about the design of your
program. You could be using the individual pin constants for IOC
interrupts shown at the end of the 12LF1575.h file. |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
|
Posted: Sat Aug 27, 2016 8:18 am |
|
|
Thanks for the quick reply. I was using pull-down resistors on the IOC pins and using a pushbutton to pull high. Are you suggesting I should do it the other way around? If so couldn't I just use the builtin weak pullups?
I've done many successful projects using a single button for wakeup on the INT pin. This is my first attempt at a multi-button remote, which is why I'm delving into the IOC.
Anyway, you've given me several things to look at... back to the workbench. _________________ Jürgen
www.jgscraft.com |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
This is for a multi-button remote project... |
Posted: Sun Aug 28, 2016 10:57 am |
|
|
After reviewing several posts and re-reading the interrupts sections in the manual, having some tea, and reading them again (thanks bkamen from the sticky post), I came up with this demo program, which works just the way I want:
- use any one of several buttons to wake from sleep
- find which button woke us and do some work
- go back to sleep
The following implementation ended up using less than than 1 microamp while sleeping, which is several years running off a pair of AAA ultra lithiums.
I welcome any observations, suggestions for improvements, etc.
Enjoy!
Code: | #case
// 1575SleepIOC.c Multi-button remote
//
// Wake from sleep on IOC - find out which one woke us
//
// Device: Microchip PIC15LF1575
// Compiler: Custom Computer Services, Inc. PCWH 5.048
// Programmer: CCS ICD-U64
//
// + VDD - 1 14 - VSS -
// RA5 - 2 13 - RA0/DAT -> debug output
// RA4 - 3 12 - RA1/CLK -> HBLED
// MCLR/RA3 - 4 11 - RA2
// RC5 - 5 10 - RC0 -> PB STAFF1
// RC4 - 6 9 - RC1
// PB STAFF3 -> RC3 - 7 8 - RC2 -> PB STAFF2
//
// Use internal pullups and PB pulls to GND
//
// Quick and dirty demo - assumes PortC only used for IOC buttons
//
// 2018.08.26 - Jurgen G Schmidt
// 2018.08.28 - works !!! < 1uA in sleep
#include <16LF1575.h>
#fuses INTRC_IO //Internal RC Osc, no output
#fuses NOMCLR //Plain i/o, no reset
#fuses NOWDT,NOPUT,NOPROTECT,NOBROWNOUT,NOCLKOUT,NOWRT,NOSTVREN,NOLVP,NODEBUG
//--------------------------- Hardware Definitions ----------------------------
#define HBLED PIN_A1 // heartbeat LED
#use delay(internal=32M)
#use rs232( BAUD=38400, XMIT=PIN_A0, INVERT, STREAM=JGS_DBG )
typedef unsigned int8 UINT8;
typedef unsigned int16 UINT16;
typedef int1 BIT;
//--------------------------- CONSTANTS ---------------------------------------
#define STAFF1 PIN_C0 // Push buttons to GND
#define STAFF2 PIN_C2
#define STAFF3 PIN_C3
#byte IOCCF = getenv( "SFR:IOCCF" )
#bit IOC1 = IOCCF.0
#bit IOC2 = IOCCF.2
#bit IOC3 = IOCCF.3
//--------------------------- Macros and Globals ------------------------------
#define nop() #asm nop #endasm
UINT8 intWakePin = 0; // save pin number which woke us
//--------------------------- Interrupt Handlers ------------------------------
#INT_RC
void int_ioc_C()
{
disable_interrupts( INT_RC ); // avoid re-trigger
if( IOC1 ) intWakePin = STAFF1; // check IOCCF bits for triggering pin
if( IOC2 ) intWakePin = STAFF2;
if( IOC3 ) intWakePin = STAFF3;
IOCCF = FALSE; // clear all bits
}
//--------------------------- Support Functions -------------------------------
void HWSleep( void ); // prepare hardware for low power sleep
//-----------------------------------------------------------------------------
void main()
{
UINT16 i; // utility counters
setup_adc_ports( NO_ANALOGS); // ALWAYS, Always, always do this !!!
// Power-up proof of life and correct speed...
output_high( HBLED );
delay_ms( 1000 );
output_low( HBLED );
fprintf(JGS_DBG,"\r\n\%s %s\r\n", __FILENAME__, __TIME__);
delay_ms( 300 );
set_tris_c( 0b00111111 ); // set buttons to inputs
port_c_pullups( 0b00111111 ); // set weak pullups
while( TRUE ) { //------ BIGLOOP Starts ------
HWSleep();
//------ Start work...
for( i=0; i<5; i++ ) {
output_high( HBLED );
delay_ms( 100 );
output_low( HBLED );
delay_ms( 100 );
}
fprintf(JGS_DBG, "Button %d pressed\r\n", intWakePin);
} //------ BIGLOOP Ends ------
}//-- end of main()
//------------------------------------------------------------------------------
void HWSleep( void )
{
//-- power down what we can...
setup_vref( VREF_OFF ); // uses 16uA when on
setup_timer_1( T1_DISABLED );
setup_timer_2( 0, 0, 1 );
output_a( 0x00 ); // turn off everything
enable_interrupts( INT_RC ); // interrupts we want to wake on
enable_interrupts( GLOBAL ); // don't need GLOBAL if no handler and continue after sleep
sleep(); // go to sleep
nop(); // swallowed by prefetch
// Continue with tasks in BIGLOOP...
}//-- end of HWSleep()
//-----------------------------------------------------------------------------
//------ end of 1575SleepIOC.c ------ |
_________________ Jürgen
www.jgscraft.com |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Sun Aug 28, 2016 11:07 am |
|
|
It's possibly just worth explaining what PCM_Programmer was pointing out.
If you don't 'enable_interrupts(GLOBAL)' before you setup the sleep, then the chip will wake (the interrupt flag gets set), but no interrupt handler will be called.
You can then just have the test for the IOC bits in the main code. This is faster, smaller etc..
So:
Code: |
//get rid of INT_RC handler
enable_interrupts( INT_RC ); // interrupts we want to wake on
sleep(); // go to sleep
delay_cycles(1); // NOP swallowed by prefetch
disable_interrupts( INT_RC ); // avoid re-trigger
if( IOC1 ) intWakePin = STAFF1; // check IOCCF bits for triggering pin
if( IOC2 ) intWakePin = STAFF2;
if( IOC3 ) intWakePin = STAFF3;
IOCCF = FALSE; // clear all bits
clear_interrupts (INT_RC);
|
You don't even need the flags, you can just directly execute the code for the pin(s) involved. Note also how to code a NOP in CCS. |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
|
Posted: Sun Aug 28, 2016 11:57 am |
|
|
Much appreciated! From brute force to elegant.
Thanks. _________________ Jürgen
www.jgscraft.com |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
8-Button Remote with IOC - version 2.0 |
Posted: Sun Aug 28, 2016 9:41 pm |
|
|
In case anyone is still watching, my latest 8-button remote template is shown below, thanks to the additional guidance from T.
Code: | #case
// 1575SleepIOC2.c Multi-button remote template 2.0
//
// Thank you CCS Forum Post # 55438 responders
//
// Wake from sleep on IOC - find out which one woke us
//
// Device: Microchip PIC15LF1575
// Compiler: Custom Computer Services, Inc. PCWH 5.048
// Programmer: CCS ICD-U64
//
// + VDD - 1 14 - VSS -
// PPS TX1 <- RA5 - 2 13 - RA0/DAT -> debug output
// PPS RX1 -> RA4 - 3 12 - RA1/CLK -> HBLED
// PB8 -> MCLR/RA3 - 4 11 - RA2 <- PB1
// PB7 -> RC5 - 5 10 - RC0 <- PB2
// PB6 -> RC4 - 6 9 - RC1 <- PB3
// PB5 -> RC3 - 7 8 - RC2 <- PB4
//
// Use internal pullups. PBx pulls to GND
//
// 2016.08.26 - Jurgen G Schmidt
// 2016.08.28 - works !!! < 1uA in sleep
// 2016.08.29 - updated per post responders, use both ports for buttons
#include <16LF1575.h>
#fuses INTRC_IO //Internal RC Osc, no output
#fuses NOMCLR //Plain i/o, no reset
#fuses NOWDT,NOPUT,NOPROTECT,NOBROWNOUT,NOCLKOUT,NOWRT,NOSTVREN,NOLVP,NODEBUG
//--------------------------- Hardware Definitions ----------------------------
#define HBLED PIN_A1 // heartbeat LED
#use delay(internal=32M)
// Serial debug data via SW. INVERT so can connect directly to legacy PC serial port
#use rs232( BAUD=38400, XMIT=PIN_A0, INVERT, STREAM=JGS_DBG )
typedef unsigned int8 UINT8;
typedef unsigned int16 UINT16;
typedef int1 BIT;
//--------------------------- CONSTANTS ---------------------------------------
#byte IOCAF = getenv( "SFR:IOCAF" )
#byte IOCCF = getenv( "SFR:IOCCF" )
// Combine IOCAF and IOCCF into a 16-bit number
#define PB1 4L
#define PB2 256L
#define PB3 512L
#define PB4 1024L
#define PB5 2048L
#define PB6 4096L
#define PB7 8192L
#define PB8 8L
//--------------------------- Macros and Globals ------------------------------
UINT16 intWakePin = 0; // save pin number which woke us
//--------------------------- Support Functions -------------------------------
void HWSleep( void ); // prepare hardware for low power sleep
//-----------------------------------------------------------------------------
void main()
{
UINT16 i; // utility counters
setup_adc_ports( NO_ANALOGS); // ALWAYS, Always, always do this !!!
// Power-up proof of life and correct speed...
output_high( HBLED );
delay_ms( 1000 );
output_low( HBLED );
fprintf(JGS_DBG,"\r\n\%s %s\r\n", __FILENAME__, __TIME__);
delay_ms( 300 );
set_tris_a( 0b00011100 ); // set PORTA buttons to inputs (set A4 as input for RX1
port_a_pullups( 0b00001100 ); // set weak pullups
set_tris_c( 0b00111111 ); // set PORTC buttons to inputs
port_c_pullups( 0b00111111 ); // set weak pullups
while( TRUE ) { //------ BIGLOOP Starts ------
HWSleep();
//------ Start work...
for( i=0; i<6; i++ ) {
output_toggle( HBLED );
delay_ms( 100 );
}
intWakePin = make16( IOCCF,IOCAF ); // combine IOC bits into 16-bit number
switch( intWakePin ) {
case PB1:
//-- assemble the appropriate transmission sequences here...
fprintf( JGS_DBG, "PB1 " );
break;
case PB2:
fprintf( JGS_DBG, "PB2 " );
break;
case PB3:
fprintf( JGS_DBG, "PB3 " );
break;
case PB4:
fprintf( JGS_DBG, "PB4 " );
break;
case PB5:
fprintf( JGS_DBG, "PB5 " );
break;
case PB6:
fprintf( JGS_DBG, "PB6 " );
break;
case PB7:
fprintf( JGS_DBG, "PB7 " );
break;
case PB8:
fprintf( JGS_DBG, "PB8 " );
break;
default:
fprintf( JGS_DBG, "oops! " );
break;
}
//-- prep transmitter and send...
fprintf(JGS_DBG, "Button %lu pressed\r\n", intWakePin);
//-- wait for confirmation, handle errors, etc...
//-- Done.
} //------ BIGLOOP Ends ------
}//-- end of main()
//------------------------------------------------------------------------------
void HWSleep( void )
{
//-- power down what we can...
setup_vref( VREF_OFF ); // uses 16uA when on
setup_timer_1( T1_DISABLED );
setup_timer_2( 0, 0, 1 );
IOCAF = FALSE; // clear all bits
IOCCF = FALSE;
clear_interrupt( INT_RA );
clear_interrupt( INT_RC );
enable_interrupts( INT_RA ); // interrupts we want to wake on
enable_interrupts( INT_RC );
sleep(); // go to sleep
delay_cycles(1); // swallowed by prefetch
disable_interrupts( INT_RA );
disable_interrupts( INT_RC );
// Continue with tasks in BIGLOOP...
}//-- end of HWSleep()
//-----------------------------------------------------------------------------
//------ end of 1575SleepIOC2.c ------ |
_________________ Jürgen
www.jgscraft.com |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Aug 29, 2016 12:22 am |
|
|
Well done.
Hope it does everything you want. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Mon Aug 29, 2016 4:57 am |
|
|
You should add it to the 'working code library forum' here !!!
I'm sure others will appreciate the time and effort you've spent working out the bugs !!
Jay |
|
|
jgschmidt
Joined: 03 Dec 2008 Posts: 184 Location: Gresham, OR USA
|
|
Posted: Mon Aug 29, 2016 7:45 am |
|
|
Thanks, I'll definitely post the code. I'm new to this chip and still need to research all the fuses, and the power-down requirements for maximum power savings. I also need to add in the real-life peripherals and make sure everything plays nice. _________________ Jürgen
www.jgscraft.com |
|
|
|
|
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
|