CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Wakeup from Sleep - Multi-Button IOC - SOLVED and improved

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
jgschmidt



Joined: 03 Dec 2008
Posts: 184
Location: Gresham, OR USA

View user's profile Send private message Send e-mail Visit poster's website

Wakeup from Sleep - Multi-Button IOC - SOLVED and improved
PostPosted: Fri Aug 26, 2016 9:50 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Aug 26, 2016 10:34 pm     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Sat Aug 27, 2016 8:18 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

This is for a multi-button remote project...
PostPosted: Sun Aug 28, 2016 10:57 am     Reply with quote

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

View user's profile Send private message

PostPosted: Sun Aug 28, 2016 11:07 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Sun Aug 28, 2016 11:57 am     Reply with quote

Much appreciated! From brute force to elegant.

Thanks.
_________________
Jürgen
www.jgscraft.com
jgschmidt



Joined: 03 Dec 2008
Posts: 184
Location: Gresham, OR USA

View user's profile Send private message Send e-mail Visit poster's website

8-Button Remote with IOC - version 2.0
PostPosted: Sun Aug 28, 2016 9:41 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Aug 29, 2016 12:22 am     Reply with quote

Well done.

Hope it does everything you want. Smile
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Mon Aug 29, 2016 4:57 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Mon Aug 29, 2016 7:45 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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