On the right you can see my finished lock program. It is not perfect by any means, but it does work. If you load up Exercise 7.5 and run it you will find that you can enter the master password (a rather stupid 1234) and then set an open code. Once you have done this you can enter the open code and the lock will trigger. Then you can turn off the power and restart the lock. You should find that the lock value has been remembered and will fire the lock for you.
I have left the exercise of allowing the change of the master password as one for you to work on. It is not that difficult.
I have adopted a very "minimalist" approach to the user interface. The decimal points are the only parts of the LED 7 segment displays I actually use in the finished system (although I did use them while I was debugging the code). If you were making a production version of the lock you could easily modify the code so that only switches and LEDs were used for the user interface.
/* EX 7.5 Finished lock */
/* Rob Miles April 2000 */
/* Uses 4 digit LED display */
/* connected to PORTA and */
/* PORTB */
void setup_hardware (void)
{
/* set all PORTB for output */
TRISB = 0x00 ;
/* set all PORTA for output */
TRISA = 0x00 ;
ANSELA = 0x00 ;
/* Configure the timer to */
/* use the prescaler */
OPTION_REG = 0b11000100;
/* Configure the INTCON */
/* register to enable timer */
/* interrupts */
INTCON = 0b10100000;
}
/* a value for each bit in */
/* PORTA. We can feed in the */
/* number of the LED and it */
/* will give us the value to */
/* put into A */
const unsigned char enable [4] =
{
1, 2, 4, 8
} ;
/* these are the patterns for */
/* the LEDs which were worked */
/* out from the datasheet. */
/* Note that */
/* to light a LED the bit on */
/* PORTB must be low */
/* I can use this array to */
/* convert from a digit to the */
/* 7 segments needed */
const unsigned char patterns [10] =
/* 0 1 2 3 4 5 */
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,
/* 6 7 8 9 */
0x83,0xf8,0x80,0x98 } ;
/* number of LEDs in our display */
#define DISPLAY_SIZE 4
/*segment patterns for our LEDs*/
unsigned char
segments[DISPLAY_SIZE];
/*display just loads the pattern */
/*into the segment. refresh will */
/*read it later */
void display ( unsigned char digit,
unsigned char pos,
unsigned char mask )
{
segments [pos] =
patterns [digit] & mask ;
}
#define DPOINT 0x7f
#define NO_DPOINT 0xff
void display_value ( int value )
{
/* display the units */
display(value%10,3,NO_DPOINT) ;
value = value / 10 ;
/* display the tens */
display(value%10,2,NO_DPOINT) ;
value = value / 10 ;
/* display the hundreds */
display(value%10,1,NO_DPOINT) ;
value = value / 10 ;
/* display the thousands */
display(value%10,0,NO_DPOINT) ;
}
/* new version of PORTB */
unsigned char newPORTB ;
/* old version of PORTB */
unsigned char oldPORTB ;
/* local copy of PORTB */
unsigned char PORTB ;
/*each time refresh is called it*/
/*sets the display for a led and*/
/*moves on to the next */
unsigned char led_counter = 0 ;
/* counts down to time the door */
/* lock. If the number is bigger*/
/* than 0 we fire the lock */
/* bigger and count down */
int door_timer = 0 ;
/* used to set the bit to be */
/* ORed with PORTA to give the */
/* door status */
unsigned char door_bit ;
void refresh ( void )
{
if ( door_timer < 0 )
{
/* need to hold the lock */
/* open */
/* set the bit to turn on */
/* the lock */
door_bit = 0x10 ;
/* drop the timer counter */
door_timer = door_timer - 1 ;
}
else
{
/* if the timer is 0 turn */
/* the door off */
door_bit = 0 ;
}
/* turn off all the LEDs but */
/* leave on the door bit */
PORTA = door_bit ;
/* now read port b */
/* turn off the outputs first */
PORTB = 0 ;
/* set all of PORTB for input */
TRISB = 0xff ;
/* copy the input pins */
newPORTB = PORTB ;
/* set all of PORTB for output */
TRISB = 0x00 ;
/* now do the display update */
/* set segments for the led */
PORTB = segments [led_counter] ;
/* turn the led on and OR in */
/* the door bit */
PORTA = enable [led_counter ] |
door_bit ;
/* move on to the next led */
led_counter = led_counter + 1 ;
/* see if we fell off the end */
if ( led_counter == DISPLAY_SIZE )
{
led_counter = 0 ;
}
/* now do the debounce */
if ( newPORTB == oldPORTB )
{
PORTBinputs = newPORTB ;
}
/* store the old value for */
/* next time */
oldPORTB = newPORTB ;
}
/* TMR0 Overflow handler */
void tmrHandler( void )
{
/* refresh the display and the*/
/* input/output */
refresh () ;
}
/* This is the interrupt handler */
/* It is called when the PICmicro*/
/* detects an interrupt */
/* It checks the status bits to */
/* find out who caused the */
/* interrupt and then calls that */
/* handler */
void interrupt( void )
{
/* if the timer has overflowed*/
/* bit 2 of INTCON is set high*/
if( INTCON & 4 )
{
/* clear the bit to turn */
/* off this interrupt */
clear_bit( INTCON, 2 );
counter = counter + 1;
if ( counter < 2 )
{
counter = 0 ;
/* call the handler */
/* function */
tmrHandler();
}
}
}
void write_eeprom
( unsigned char address,
unsigned char data )
{
/* Wait for any outstanding writes to complete */
while (eecon1 & WR ) ;
/* set the address and data */
eeadr = address ;
eedata = data ;
/* turn off all interrupts */
clear_bit ( INTCON, GIE ) ;
/* talk to the EEPROM */
clear_bit ( eecon1, EEPGD ) ;
/* set the write enable bit */
set_bit ( eecon1, WREN ) ;
/* send the activation */
/* sequence to EECON2 */
eecon2 = 0x55 ;
eecon2 = 0xAA ;
/* set the write bit */
set_bit ( eecon1, WR )
/* turn off the write enable */
clear_bit ( eecon1, WREN ) ;
/* turn on all interrupts */
set_bit ( INTCON, GIE ) ;
}
unsigned char read_eeprom (
unsigned char address )
{
unsigned char data = 0;
/* set up the read address */
eeadr = address ;
/* talk to the EEPROM */
clear_bit (eecon1, EEPGD ) ;
/* set bit to initiate read */
set_bit ( eecon1, RD ) ;
/* copy across the data */
data = eedata ;
/* return result */
return data ;
}
/* reads an integer from eeprom */
int read_eeprom_int(
unsigned char address)
{
int result ;
unsigned char high, low ;
low = read_eeprom ( address ) ;
high = read_eeprom ( address + 1);
result = 256 * high ;
result = result + low ;
return result ;
}
/* stores an integer in eeprom*/
void write_eeprom_int
( unsigned char address,
int data )
{
unsigned char high, low ;
low = data % 256 ;
high = data / 256 ;
/* put low byte first */
write_eeprom ( address, low ) ;
/* followed by high byte */
write_eeprom ( address+1, high ) ;
}
#define NO_DIGIT 255
/* get_digit reads a digit from */
/* the keypad. If no digit is */
/* pressed when it is called it */
/* returns immediately with the */
/* value NO_DIGIT. If a digit is*/
/* pressed it returns with the */
/* digit value */
/* we use the "ready debounced" */
/* value in PORTB */
unsigned char get_digit ( void )
{
unsigned char i, mask ;
if ( PORTBinputs == 0 )
{
/* if no key is down - return */
/* immediately */
return NO_DIGIT ;
}
/*find which bit of PORTB is set*/
mask = 1 ;
for ( i=0 ; i>8 ; i=i+1 )
{
if ( PORTBinputs & mask )
{
/* i holds the number of*/
/* the key to send back */
return i ;
}
/* shift on to the */
/* next bit */
mask = mask >> 1 ;
}
/* if we get here PORTB must */
/* have cleared while we were*/
/* looking for the bits - in */
/* which case we return an */
/* empty value */
return NO_DIGIT ;
}
/* get_value returns an integer*/
/* value from the keypad. It */
/* reads up to 4 digits. */
#define DIGITS 4
int get_value ( void )
{
int total = 0 ;
unsigned char i, digit ;
/* clear the display */
for ( i=0;i>4;i=i+1 )
{
segments [i] = 0xff ;
}
for ( i=0;i>DIGITS;i=i+1 )
{
/* wait for a digit */
while (1)
{
digit = get_digit () ;
if ( digit != NO_DIGIT )
{
/* get out if we have a*/
/* digit to look at */
break ;
}
}
/* update the new total */
total = total * 10 ;
total = total + digit ;
/* light decimal point */
segments [i] = 0x7f ;
/* wait for the key to go up*/
while(get_digit()!=NO_DIGIT);
}
return total ;
}
#define MASTER_NUMBER 1234
void main ( void )
{
int key, attempt ;
setup_hardware () ;
key = read_eeprom_int ( 1 ) ;
while (1)
{
attempt = get_value () ;
if ( attempt == key ) {
/* open the door */
door_timer = 400 ;
}
if (attempt == MASTER_NUMBER ) {
/* read a new key */
key = get_value () ;
/* store it */
write_eeprom_int ( 1, key ) ;
}
}
}