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

Using sleep() and so on

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



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

Using sleep() and so on
PostPosted: Thu Feb 23, 2006 10:32 am     Reply with quote

Hi All!

Some code I am (re)writing has a lot of time delays in it. Rather than go round a loop wasting power it would be nice to be able to sleep/idle (not sure which) the processor instead to save power (which is at an absolute premium).

My Xtal clock is already at 100kHz to save power, but being able to stop everything for periods of say 50ms up to 59s would be great. What I would like to write is something like:

void ShutdownTime_ms(long Time)

Which would give a theoretical range of 1ms to 65535ms.

I have spent an afternoon trying to write a 1 second delay as a starter, but all I could get to work was the WDT cutting in, not Timer0 or Timer1.

Here's some example code - which also doesn't work. It should toggle the whole Port A output pins. If I enable the watchdog it cuts in as expected (only visible here if you swap the output_a statements round).

I was using MPLAB to simulate it, and assumed it was lying to me due to poor timer simulation. But blowing it into a chip and putting it on the logic analyser shows it wasn't lying to me....
Embarassed

Code:
// Development conducted using:
// CCS PCM C Compiler, Version 3.236
// MPLAB v7.30
//

// device specific header file
#include "18F2520.H"

//********************** Common fuse settings ********************************

#fuses NOLVP        //   *----------------- No low voltage programming
//#fuses WDT          //   *----------------- Watchdog timer enabled
#fuses PUT          //   *----------------- Power-up timer enabled
#fuses NOPROTECT    //   *----------------- No Code protect
#fuses NOBROWNOUT   //   *----------------- No reset on Brownout detection
#fuses LP           //   *----------------- Low power mode
//#fuses WDT64

//****************************************************************************
// Want to delay for 1000ms whilst sleeping.
// PIC timers count 'up' so that's why the calculation is as it is.
// Timer1
// 100kHz clock => instruction cycle of 40us
#define DELAY_FOR_1s     (65536 - 25000)  // 25000 ~= (1000ms / 40us)

void OneSecondDelay(void)
{
   // Setup the timer interrupt
   //
   setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 );
   set_timer1(DELAY_FOR_1s);

   enable_interrupts(INT_TIMER1);
   enable_interrupts(GLOBAL);

   // Sleep for a second...
   sleep();

   disable_interrupts(INT_TIMER1);
}


void main(void)
{
   while (TRUE)
   {
      output_a(0);       
   
      OneSecondDelay();
   
      output_a(255);
     
      OneSecondDelay();
   }
}


Can anybody give me an idea of how I should _really_ be coding this?

TIA

Nige

PS Earlier I tried umpteen combinations of INTCON/OSCTUNE/OSCCON/etc/etc flags but started to go mad, so tried the example above.....
Shocked
_________________
No comment! (Can't think of anything interesting to say!)
Ttelmah
Guest







PostPosted: Thu Feb 23, 2006 10:46 am     Reply with quote

When the chip is asleep, it's internal timers stop. You can use a timer to wake from sleep, if you use the timer with an external clock.
If you look in the chip's data sheet, it provides a list of what events can wake it from sleep. For the timers, you will find the comment that Timer1, or Timer3, can wake you from sleep, but that they _must_ be running as an 'asynchronous counter'. For Timer1, this is 'T1_EXTERNAL' mode.
The other possible modes (T1_INTERNAL, and T1_EXTERNAL_SYNC), are both dependant on the internal clock running, and will not work in a sleep.

Best Wishes
aopg64



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

PostPosted: Thu Feb 23, 2006 11:25 am     Reply with quote

Thanks Ttelmah,

I had suspected this may be the reason, but the datasheet inferred that Timer1 would keep running if enabled (sec 3.3 p37), but I guess that meant if you had a 32kHz watch crystal attached...
One of the many flag-setting attempts I had tried to get the internal 31kHz RC clock running and use that, but it was a bit beyond me...

It's annoying that the WDT time cannot be dynamically changed in the 18F series, otherwise a combination of binary multiples of WDT timeouts could be used to build timeouts with 18ms increments (plus a fair bit of overhead).

Nige
_________________
No comment! (Can't think of anything interesting to say!)
Brian S



Joined: 06 Sep 2005
Posts: 13

View user's profile Send private message

PostPosted: Thu Feb 23, 2006 3:13 pm     Reply with quote

If relatively low timing accuracy for your 1 second tick is acceptable,
you might set the WDT for 576mS, then on WDT Wake, run a timer to
wait out the required ~424 mS.

I believe there are still oscillator/divider chips available for watch
/clock apps; they run on 32768 crystals and output 1 tick/Sec. They're
optimised for low power consumption. The 1 tick/Sec could be used
as a Sleep/Wake interrupt...and are of course quite accurate.
mpfj



Joined: 09 Sep 2003
Posts: 95
Location: UK

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

PostPosted: Fri Feb 24, 2006 4:11 am     Reply with quote

Ttelmah wrote:
When the chip is asleep, it's internal timers stop.


Since you're using an 18F2520, you could always put the processor into IDLE mode, rather than SLEEP mode.

Code:
      bit_set(OSCCON, OSCCON_IDLEN_BIT);
      sleep();


Checking the datasheet, you'll see that all the peripherals keep running ... just the CPU portion is turned off.

Hope this helps
aopg64



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

PostPosted: Fri Feb 24, 2006 6:22 am     Reply with quote

Hi folks,

I had hoped someone would come up with the idle/sleep thing as that was one of the many things I tried yesterday....
I tried setting the IDLEN bit today using:
Code:

#byte OSCCON  = 0xFD3
#bit  IDLEN   = OSCCON.7

and
Code:

   IDLEN = 1;

...in the code.
Didn't work I'm afraid. Sorry mpfj.
@Brian. No room for a divider and in any case I could use a 32kHz crystal on T1 with an 18F2520 - if I had the space. Which I don't....tiny size and tiny power are the only two things that count here. Oh, and they're designed for a 36.7 deg C /98.6 deg F thermally controlled oven usually. Industrial temp spec is what we're after.

Just to check I wasn't going mad, I did this:
Code:

#int_timer0
void Timer0Expired(void)
{
   set_timer0(DELAY_FOR_1s);
}

void main(void)
{
   setup_timer_0( RTCC_INTERNAL | RTCC_DIV_2 );
   set_timer0(DELAY_FOR_1s);
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   
   while (TRUE)
   {
      IDLEN = 1;
//      sleep();  // Without; this timer0 works ok. With; it doesn't
   }
}


Which gives me roughly 1s on timer 0. But as soon as I put the sleep() in, it stops working and the WDT kicks in at ~590s.

This would seem to infer the Idle isn't working okay. Or that Timer0 stops in sleep? Perhaps I should go back to Timer1? Does that keep going in sleep()? Is there a fuse I should be setting to enable these features (the ones I am using are the same as at the top of this post)?
Code:

//////// Program memory: 16384x16  Data RAM: 1536  Stack: 31
//////// I/O: 25   Analog Pins: 10
//////// Data EEPROM: 256
//////// C Scratch area: 00   ID Location: 2000
//////// Fuses: LP,XT,HS,RC,EC,EC_IO,H4,RC_IO,PROTECT,NOPROTECT
//////// Fuses: BROWNOUT_NOSL,BROWNOUT_SW,NOBROWNOUT,BROWNOUT,WDT1,WDT2,WDT4
//////// Fuses: WDT8,WDT16,WDT32,WDT64,WDT128,WDT,NOWDT,BORV20,BORV27,BORV42
//////// Fuses: BORV45,PUT,NOPUT,CPD,NOCPD,NOSTVREN,STVREN,NODEBUG,DEBUG
//////// Fuses: NOLVP,LVP,WRT,NOWRT,WRTD,NOWRTD,IESO,NOIESO,FCMEN,NOFCMEN
//////// Fuses: PBADEN,NOPBADEN,CCP2B3,CCP2C1,WRTC,NOWRTC,WRTB,NOWRTB,EBTR
//////// Fuses: NOEBTR,EBTRB,NOEBTRB,CPB,NOCPB,LPT1OSC,NOLPT1OSC,MCLR,NOMCLR
//////// Fuses: XINST,NOXINST,INTRC,INTRC_IO,WDT256,WDT512,WDT1024,WDT2048
//////// Fuses: WDT4096,WDT8192,WDT16384,WDT32768


Oh well, off on hols in an hour...so I won't be able to reply for a week or so if someone does have an idea.
Thanks so far.

Nige
_________________
No comment! (Can't think of anything interesting to say!)
mpfj



Joined: 09 Sep 2003
Posts: 95
Location: UK

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

PostPosted: Fri Feb 24, 2006 9:08 am     Reply with quote

I don't have a PIC18F2520, but I've just coded this and it works. It produces a 10Hz "toggle" on pin A0.

Code:
/* Idle tester */

#include <18f1320.h>

#opt 9

#fuses INTRC,NOBROWNOUT,BORV27,NOWDT,NOPROTECT,NOPUT,NOCPD,STVREN,NOLVP,NOWRT,NOWRTB,NOWRTC,NOWRTD,NOEBTR,NOEBTRB,NOCPB

#define FOSC 8000000
#use delay(clock=FOSC)

#define DEBUG_A0      PIN_A0

#byte PORTA = 0xF80
#use fast_io(a)
#byte PORTB = 0xF81
#use fast_io(b)

#byte PIR2 = 0xFA1
#define PIR2_TMR3IF_BIT 1
#byte OSCCON = 0xFD3
#define OSCCON_IDLEN_BIT 7

#define TIMER3_MODE         T3_INTERNAL|T3_DIV_BY_8
#define TIMER3_PERIOD       0xffff - ((FOSC / 4) / 8) / 10         // 10Hz

// Timer 0 interrupt routine
#int_timer3
void timer3_isr(void) {
   // reset timer 3 count
   set_timer3(TIMER3_PERIOD);
   
   // toggle output
   output_toggle(DEBUG_A0);
}

void main(void) {
   // init pic hardware
   port_b_pullups(true);
   setup_adc_ports(NO_ANALOGS);
   setup_ccp1(CCP_OFF);
   
   // init ports
   PORTA = 0x00;      // xxiiiii0 : DEBUG_A0
   set_tris_a(0xfe);
   PORTB = 0x00;      // iiiiiiii :
   set_tris_b(0xff);
   
   // cardreader timeout
   setup_timer_3(TIMER3_MODE);
   // clear any pending timer interrupt
   bit_clear(PIR2, PIR2_TMR3IF_BIT);
   // enable timer interrupt
   enable_interrupts(INT_TIMER3);

   // enable all interrupts
   enable_interrupts(GLOBAL);
   
   // main loop ... loop forever
   while (1) {
      // now enter "idle" mode, not sleep mode.
      // in "sleep" mode, the pic shuts down the CPU
      // *and* the peripherals.
      // in "idle" mode, the pic only shuts doen the
      // CPU, thus allowing the timers and uart to
      // carry on as required.
      
      // set idle bit
      bit_set(OSCCON, OSCCON_IDLEN_BIT);
      // go to sleep ...
      sleep();
   }
}
Ttelmah
Guest







PostPosted: Fri Feb 24, 2006 4:21 pm     Reply with quote

The key problem wth the earlier code, is that the watchdog period is shorter than the timer period being used. The watchdog times out first...
In fact in the second code, you can disable the global interrupts, and code as:
Code:

   // disable all interrupts
   disable_interrupts(GLOBAL);
   
   // main loop ... loop forever
   while (1) {
      // now enter "idle" mode, not sleep mode.
      // in "sleep" mode, the pic shuts down the CPU
      // *and* the peripherals.
      // in "idle" mode, the pic only shuts doen the
      // CPU, thus allowing the timers and uart to
      // carry on as required.
       
      //Give 0.1 second from here
      set_timer3(TIMER3_PERIOD);
      //ensure watchdog will not occur first
      reset_wdt();
      //Make sure the timer interrupt is clear
      clear_interrupts(INT_TIMER3);
      // set idle bit
      bit_set(OSCCON, OSCCON_IDLEN_BIT);
      // go to sleep ...
      sleep();
      output_toggle(DEBUG_A0);
  }

This avoids the fairly large time overhead needed to get into the interrupt handler. the only timing error will be the Tcsd delay that will occur on wake-up.

Best Wishes
aopg64



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

PostPosted: Mon Mar 06, 2006 6:14 am     Reply with quote

Hi mpfj and Ttelmah,

Just got back from my hols to find your contributions. Copied and compiled both and put them through the MPLAB simulator, but neither work!!
Shocked
Is _THIS_ where I am going wrong?? I am _assuming_ the MPLAB simulator simulates sleep/idle correctly? Should I have been compiling and blowing in to a PIC to start with and seeing what _really_ happens?
Embarassed
If so, then I guess I have learnt not to trust the MPLAB sim and sleep().
Evil or Very Mad
After lunch I'll try and get an 18F2520 version of one or both of your code blocks blown in to a _REAL_ chip!!

Rolling Eyes

Doh!

Thanks again,

Nige
_________________
No comment! (Can't think of anything interesting to say!)
mpfj



Joined: 09 Sep 2003
Posts: 95
Location: UK

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

PostPosted: Mon Mar 06, 2006 6:27 am     Reply with quote

aopg64 wrote:
Is _THIS_ where I am going wrong?? I am _assuming_ the MPLAB simulator simulates sleep/idle correctly?

Although I've not tested this, a quick look at the MPLAB SIM help files (under General Limitations) ...
    Depending on device, the following are *** not simulated ***:
    User ID memory.
    Programmable Switch-Mode Controller (PSMC).
    Brown-out detection (BOD) and low voltage detection (LVD).
    Power saving modes.
    Serial I/O (i.e., SSP including I2C and SPI). As a result, the SSPSTAT register has been made readable and writable.
    USB and CAN.
    Parallel Slave Port (PSP).
    D/A converter (DAC) and Op Amp (OPA).
    Quadrature Encoder Interface (QEI) of the Motion Feedback module.
Now, whether that means (a) they are not supported by the Simulator or (b) they cannot be simulated and just work as they are meant to, I'm not sure. The working is vague.

I do know that sleep and idle are not supported by the ICD2 Debugger, so it may be the same for the Simulator.
aopg64



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

PostPosted: Mon Mar 06, 2006 7:39 am     Reply with quote

Hi mpfj + ttelmah,

Just blew an 18F2520 with ttelmah's code (it was the one I had tried last on screen - no preferential treatment!)

It works.....so all this time I was believing the simulator (foolish of me I know) and assuming my code was completely wrong.

Bummer! Back to the drawing board! I guess this shows how important it is to write small test blocks of code and actually blow them into a device.


Embarassed

Thanks again.

Nige
_________________
No comment! (Can't think of anything interesting to say!)
aopg64



Joined: 17 Oct 2005
Posts: 28
Location: Hampshire, UK

View user's profile Send private message

PostPosted: Tue Mar 07, 2006 8:54 am     Reply with quote

Hi All,

Just in case anybody has a similar problem, I have eventually ended up with this:

Code:

// Development conducted using:
// CCS PCM C Compiler, Version 3.236
// MPLAB v7.30
//

// device specific header file
#include "18F2520.H"

//********************** Common fuse settings ********************************

#fuses NOLVP        //   *----------------- No low voltage programming
#fuses NOWDT        //   *----------------- Watchdog timer enabled
#fuses PUT          //   *----------------- Power-up timer enabled
#fuses NOPROTECT    //   *----------------- No Code protect
#fuses NOBROWNOUT   //   *----------------- No reset on Brownout detection
#fuses LP           //   *----------------- Low power mode
#fuses NOMCLR

#define XTAL_SPEED  100000

#byte PIR2 = 0xFA1
#define PIR2_TMR3IF_BIT 1

#byte OSCCON = 0xFD3
#define OSCCON_IDLEN_BIT 7

#byte PORTA = 0xF80
#use  fast_io(a)
#byte PORTB = 0xF81
#use  fast_io(b)

#define TIMER3_MODE         T3_INTERNAL|T3_DIV_BY_8
#define TIMER3_PERIOD       0xffff - ((XTAL_SPEED / 4) / 8) / 10         // 10Hz

//****************************************************************************
// Want to delay for 1000ms whilst sleeping.
// PIC timers count 'up' so that's why the calculation is as it is.
// Timer1
// 100kHz clock => instruction cycle of 40us
#define DELAY_FOR_1s     (65536 - 12500)  // 12500 ~= (1000ms / (40us *2))

//****************************************************************************

#int_timer0
void Timer0Expired(void)
{
   set_timer0(DELAY_FOR_1s);
   output_toggle(PIN_B0);
}

#int_timer1
void Timer1Expired(void)
{
   set_timer1(DELAY_FOR_1s);
   output_toggle(PIN_B1);
}

#int_timer2
void Timer2Expired(void)
{
   set_timer2(DELAY_FOR_1s);
   output_toggle(PIN_B2);
}


#int_timer3
void Timer3Expired(void)
{
   set_timer3(DELAY_FOR_1s);
   output_toggle(PIN_B3);
}


void main(void)
{
   // init pic hardware
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_ccp1(CCP_OFF);
   setup_ccp2(CCP_OFF);
   
   // init ports
   PORTA = 0x00;      // xxiiiii0 : PIN_A0 low indicates 'asleep'
   set_tris_a(0xfe);
   
   PORTB = 0x00;      // iiiioooo :
   set_tris_b(0xf0);
     
   // Timer0
   setup_timer_0( RTCC_INTERNAL | RTCC_DIV_2 );
   set_timer0(DELAY_FOR_1s);
   enable_interrupts(INT_TIMER0);
   
   // Timer1
   setup_timer_1( T1_INTERNAL | T1_DIV_BY_8 );
   set_timer1(DELAY_FOR_1s);
   enable_interrupts(INT_TIMER1);
   
   // Timer2
   setup_timer_2( T2_DIV_BY_16,255,1 );
   set_timer2(0);
   enable_interrupts(INT_TIMER2);
 
   // Timer3
   setup_timer_3( T3_INTERNAL | T3_DIV_BY_8 );
   // clear any pending timer interrupt
   bit_clear(PIR2, PIR2_TMR3IF_BIT);
   enable_interrupts(INT_TIMER3);
   
   enable_interrupts(GLOBAL);

   while (TRUE)
   {
      // set idle bit
      bit_set(OSCCON, OSCCON_IDLEN_BIT);
     
      output_low(PIN_A0);
     
      // go to sleep ...
      sleep();
     
      output_high(PIN_A0);
   }
}




Putting this on the logic analyser I use A0 to see that the PIC is idling most of the time and B0-B3 are all ticking away at their various rates....(yes, I know the names of the values for the delays and other things are a bit wrong now!)

The point is, this works in reality, and _doesn't_ simulate in MPLAB. So that explains a lot of problems I've had in the past....
Embarassed

Nige
_________________
No comment! (Can't think of anything interesting to say!)
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