We now have a fully working combination lock. We can even package it up on a really small unit which could be fitted into a door. We would probably make the device battery powered, which leads us to concerns about power consumption.
The PICmicro datasheet puts the power consumption of a PIC16F88 at around 4.5-10 milliamps, depending on the clock speed and what the processor is actually doing. If we use AAA batteries in our lock, which have a capacity of around 800 milliamp hours, we can power the lock (processor only) for around 80-160 hours continuously. Note that these calculations do not include power taken by things like LEDs, each of which would take around 20 milliamps, or the lock solenoid itself. This is only around a week of continuous operation, and so we need to find a way of reducing the power consumption even further.
The PICmicro Sleep Instruction
Fortunately the PICmicro has a "sleep" mode. When the processor is asleep it consumes much less current (around 400 microamps - or half a milliamp). This is because the power is doing nothing more than keeping the variable values alive. The processor clock is stopped and your program is no longer running, hence the reduced power consumption. In sleep mode a PICmicro could run for well over 1000 hours on AAA batteries.
The sleep mode is entered by means of a sleep instruction which is provided in the form of a function called sleep
.
Getting the PICmicro to sleep is the easy bit, the fun comes when you try to wake the PICmicro up. The PICmicro can be woken up in a number of different ways. An external interrupt can be made to restart the PICmicro, as well as a change on some of the input pins on PORTB.
Waking from Sleep
If an interrupt is used to wake the PICmicro it will jump to the interrupt handler for that interrupt (although for hardware reasons it actually performs the instruction after the sleep instruction first). We don't want the PICmicro to go off and perform an interrupt when it restarts our lock, so we have to configure the hardware so that the desired event is noticed, but doesn't cause an interrupt.
I have decided to package up the behaviour in a function called sleeper
. This will set up the hardware so that a change on bits 7-4 of PORTB will wake the processor and cause the function to continue. It is a pity that it is not possible to cause a wakeup on any of the bits of PORTB, since this would mean that pressing any digit key would cause the program to restart.
In real life we would use one of the buttons as a "power on" button, for now I am going to tell the users that all pass numbers must start with digits 7 to 4 (you could think of this as an additional security measure if you like!).
The code on the right implements a sleep function which works. It pauses the execution of the program until a button is pressed on PORTB. Note that I have made sure that PORTB is an input for the duration of the sleep. I also have to do some interesting things with the interrupt enables and the change detection. The code is commented, so that you can work out what is going on if you want to, otherwise you can use the code as is.
If you want to trigger exit from sleep on other events you can use the other bits in the INTCON register to do this.
/* sleeper is called to put the */
/* PICmicro to sleep. */
/* The PICmicro is */
/* woken by a change on bits */
/* 4-7 of PORTB */
void sleeper ( void )
{
unsigned char oldTRISB, dummy ;
/* we need to configure */
/* PORTB as inputs */
/* turn off all the lights */
PORTB = 0 ;
PORTA = 0 ;
/* record the old state of */
/* PORTB */
oldTRISB = TRISB ;
/* set all of PORTB for input */
TRISB = 0xff ;
/* we need to turn off */
/* interrupts but enable the */
/* interrupt on change of */
/* bits 4-7 of PORTB */
/* turn off interrupts */
clear_bit ( INTCON, GIE ) ;
/* enable interrupt on change */
/* of bits 4-7 of PORTB */
set_bit ( INTCON, RBIE ) ;
/* read PORTB to reset the interrupt */
/* condition */
dummy = PORTB;
/* clear the flag because PORTB */
/* has been changing in the */
/* past */
clear_bit ( INTCON, RBIF ) ;
/* now we can sleep.... */
sleep () ;
/* put null instruction here */
/* in case we ever want to */
/* use interrupts with */
/* sleeper */
nop () ;
/* acknowledge receipt of */
/* PORTB change event */
clear_bit ( INTCON, RBIF ) ;
/* turn off interrupts on */
/* PORTB change */
clear_bit ( INTCON, RBIE ) ;
/* turn on interrupt */
set_bit ( INTCON, GIE ) ;
/* put TRISB back */
TRISB = oldTRISB ;
}