/****************************************************************************
COMBO2.C
PIC 16F84 Combination Lock
- all combinations are six-digits
- master combination is '083941'
- four transient combinations may be programmed (all keys are allowed)
- press within one-second after master combination:
'A' to enter 1st transient combination
'B' to enter 2nd transient combination
'C' to enter 3rd transient combination
'D' to enter 4th transient combination
'#' to enter two-digit lamp ON seconds
'*' to enter two-digit lock ON seconds
- setting a feature to zeros deactivates it
- resets to first expected key five seconds after last key is pressed
--------- ----------
| 16F84 | | | T
+5 --14-|VCC A0|-17-----|R0 |
+5 ---4-|MCLR A1|-18-----|R1 |
Gnd ---5-| A2|-1------|R2 |
XTAL --16-| A3|-2------|R3 KBD |
XTAL --15-| B0|-6------|C0 |
| B1|-7------|C1 |
| B2|-8------|C2 |
| B3|-9------|C3 |
| | | |
| | ----------
| |
MANUAL SW -----12-|B6 B4|-10------------------- ILLUMINATION RELAY
DISABLE SW ----11-|B5 B7|-13------------------- LOCK SOLENOID
| |
---------
=============
| 1 2 3 A | PB0
| 4 5 6 B | PB1
| 7 8 9 C | PB2
| * 0 # D | PB3
=============
PB4 5 6 7
Keyboard connector is: C0 C1 C2 C3 R0 R1 R2 R3
PA0-3 is 4-row driver
PB0-3 is 4-column receiver
PB4 is lamp output (to illuminate keyboard, high active)
PB5 is lock disable switch input, low-active
PB6 is manual open switch input, low-active
PB7 is lock solenoid output (high active)
Uses 18mS watchdog timer
Oscillator = 4MHz crystal
Cycle time = 1uS
Jon Fick 05/14/08
***************************************************************************/
#include <16f84.h>
/* Set configuration bits in the PIC processor */
#fuses XT, NOPROTECT, WDT, PUT
#define ON 1
#define OFF 0
#define HIGH 1
#define LOW 0
#define UP 1
#define DOWN 0
#define YES 1
#define NO 0
#define ACTIVE 1
#define INACTIVE 0
#define UCHAR char
#define UINT long
#define BIT short
#define POUND 12
#define BLANK 0x20
#define NOKEY 0xFF
#define TRANS_BASE_A 6
#define TRANS_BASE_B 13
#define TRANS_BASE_C 20
#define TRANS_BASE_D 27
#define LAMP_TIME_BASE 34
#define LOCK_TIME_BASE 37
#define COMBO_ACTIVE_FLAG_OFFSET 6
#define LAMP_ACTIVE_FLAG_OFFSET 2
#define LOCK_ACTIVE_FLAG_OFFSET 2
#define WAITKEY_TIMEOUT 75 /* at 15/second */
#define RESET_KBD_COUNT 75 /* at 15/second */
#use delay ( clock = 4000000, restart_wdt ) /* sets appropriate compiler constants */
#use fast_io ( A ) /* don't set TRIS on each I/O statement */
#use fast_io ( B )
#zero_ram
/* initialize EEPROM */
#rom 0x2100 = {
0, 8, 3, 9, 4, 1, /* master combo (6) */
0, 0, 0, 0, 0, 0, INACTIVE, /* transient combo A (6) and ACTIVE flag (1) */
0, 0, 0, 0, 0, 0, INACTIVE, /* transient combo B (6) and ACTIVE flag (1) */
0, 0, 0, 0, 0, 0, INACTIVE, /* transient combo C (6) and ACTIVE flag (1) */
0, 0, 0, 0, 0, 0, INACTIVE, /* transient combo D (6) and ACTIVE flag (1) */
1, 5, ACTIVE, /* lamp time seconds (2) and ACTIVE flag (1) */
0, 1, ACTIVE /* lock time seconds (2) and ACTIVE flag (1) */
}
/* Use #byte to assign variables to a RAM locations */
#byte PORT_A = 5 /* set variable that maps to memory */
#byte PORT_B = 6
#bit MANUAL_SW = PORT_B.6
#bit DISABLE_SW = PORT_B.5
#bit ILL_OUT = PORT_B.4
#bit LOCK_OUT = PORT_B.7
char GetKey ( void ); /* prototypes */
char WaitKey ( void );
void StartOpenLock ( char cCnt );
void StartLampTimer ( char cCnt );
void GetNewData ( char cBaseAddress, char cOffset, char cKeyCount, char cAllKeys );
void UpdateLampSetting ( void );
void UpdateLockSetting ( void );
void ClearFlags ( void );
void Flash ( void );
static long iLampCount, iLampLimit, iLockCount, iLockLimit;
static char cLampSeconds, cLockSeconds;
static char cGettingNewData, cKeyCnt;
static char cMAinvalid, cTAinvalid, cTBinvalid, cTCinvalid, cTDinvalid;
static char cTIinvalid, cTLinvalid;
static char cProgMode, cProgTimeoutCount, cResetKbdCount;
void main ( void )
{
char cX, cK;
set_tris_a ( 0b11110000 ); /* set inputs and outputs */
set_tris_b ( 0b01101111 );
port_b_pullups ( TRUE ); /* enable pullups */
setup_counters ( RTCC_INTERNAL, RTCC_DIV_256 ); /* 65mS roll */
enable_interrupts ( INT_RTCC ); /* turn on timer interrupt */
enable_interrupts ( GLOBAL ); /* enable interrupts */
cKeyCnt = 0;
ILL_OUT = OFF; /* turn off lamp relay */
LOCK_OUT = OFF; /* turn off lock solenoid */
UpdateLampSetting(); /* update lamp timer count */
UpdateLockSetting(); /* update lock timer count */
ClearFlags();
cGettingNewData = NO;
cProgMode = NO;
while ( TRUE )
{
restart_wdt(); /* kick dog */
if ( MANUAL_SW == LOW ) /* if manual open switch pressed */
{
StartLampTimer ( cLampSeconds ); /* turn lamp on */
StartOpenLock( cLockSeconds ); /* energize solenoid */
cKeyCnt = 0;
while ( MANUAL_SW == LOW ) /* wait for switch to be released */
{
restart_wdt();
}
}
else
{
cK = GetKey(); /* check if key pressed */
if ( cK != NOKEY ) /* validate combo as entered, could be aliased */
{
if ( DISABLE_SW == LOW )
{
StartOpenLock( cLockSeconds ); /* any key opens lock */
cKeyCnt = 0;
}
else
{
if ( cK != read_eeprom ( cKeyCnt ) ) /* if not master combo key */
{
cMAinvalid = ON;
}
if ( cK != read_eeprom ( cKeyCnt + TRANS_BASE_A ) ) /* if not transient combo key */
{
cTAinvalid = ON;
}
if ( cK != read_eeprom ( cKeyCnt + TRANS_BASE_B ) ) /* if not transient combo key */
{
cTBinvalid = ON;
}
if ( cK != read_eeprom ( cKeyCnt + TRANS_BASE_C ) ) /* if not transient combo key */
{
cTCinvalid = ON;
}
if ( cK != read_eeprom ( cKeyCnt + TRANS_BASE_D ) ) /* if not transient combo key */
{
cTDinvalid = ON;
}
if ( ( cMAinvalid == OFF ) || ( cTAinvalid == OFF ) || ( cTBinvalid == OFF ) || ( cTCinvalid == OFF ) || ( cTDinvalid == OFF ) ) /* if any combo still good */
{
cKeyCnt++; /* point to next code in combo */
}
else
{
cKeyCnt = 0; /* restart count */
ClearFlags();
}
}
}
}
if ( cKeyCnt == 6 ) /* if one of the combos was correcct */
{
cKeyCnt = 0;
StartOpenLock( cLockSeconds ); /* energize solenoid for programmed time */
if ( cMAinvalid == OFF ) /* if master combo was entered */
{
for ( cX = 0; cX < 50; cX++ ) /* wait one second for a key */
{
cK = GetKey();
if ( cK != NOKEY )
{
cGettingNewData = YES;
break;
}
delay_ms ( 20 );
}
if ( cK == 'A' ) /* if changing transient combo */
{
GetNewData ( TRANS_BASE_A, COMBO_ACTIVE_FLAG_OFFSET, 6, YES );
}
if ( cK == 'B' ) /* if changing transient combo */ {
GetNewData ( TRANS_BASE_B, COMBO_ACTIVE_FLAG_OFFSET, 6, YES );
}
if ( cK == 'C' ) /* if changing transient combo */
{
GetNewData ( TRANS_BASE_C, COMBO_ACTIVE_FLAG_OFFSET, 6, YES );
}
if ( cK == 'D' ) /* if changing transient combo */
{
GetNewData ( TRANS_BASE_D, COMBO_ACTIVE_FLAG_OFFSET, 6, YES );
}
if ( cK == '#' ) /* if changing lamp seconds */
{
GetNewData ( LAMP_TIME_BASE, LAMP_ACTIVE_FLAG_OFFSET, 2, NO );
UpdateLampSetting();
}
if ( cK == '*' ) /* if changing lock solenoid seconds */
{
GetNewData ( LOCK_TIME_BASE, LOCK_ACTIVE_FLAG_OFFSET, 2, NO );
UpdateLockSetting();
}
cGettingNewData = NO;
}
ClearFlags();
}
}
}
/******************************************************************************/
char WaitKey ( void )
{
char cK;
while ( TRUE )
{
cK = getkey();
if ( cK != NOKEY )
{
return ( cK );
}
}
}
char GetKey ( void )
{
char cKey;
cKey = NOKEY; /* default is invalidated key */
PORT_A = 0xF0; /* make all four rows low */
delay_ms ( 5 ); /* wait for row lines to settle */
if ( ( PORT_B & 0x0F ) != 0x0F ) /* if a col is low */
{
delay_ms ( 5 ); /* debounce */
PORT_A = 0b11111110; /* try row 0 */
if ( ( PORT_B & 0x0F ) == 0b00001110 ) /* check columns */
{
cKey = 1;
}
if ( ( PORT_B & 0x0F ) == 0b00001101 )
{
cKey = 2;
}
if ( ( PORT_B & 0x0F ) == 0b00001011 )
{
cKey = 3;
}
if ( ( PORT_B & 0x0F ) == 0b00000111 )
{
cKey = 'A';
}
PORT_A = 0b11111101; /* try row 1 */
if ( ( PORT_B & 0x0F ) == 0b00001110 ) /* check columns */
{
cKey = 4;
}
if ( ( PORT_B & 0x0F ) == 0b00001101 )
{
cKey = 5;
}
if ( ( PORT_B & 0x0F ) == 0b00001011 )
{
cKey = 6;
}
if ( ( PORT_B & 0x0F ) == 0b00000111 )
{
cKey = 'B';
}
PORT_A = 0b11111011; /* try row 2 */
if ( ( PORT_B & 0x0F ) == 0b00001110 ) /* check columns */
{
cKey = 7;
}
if ( ( PORT_B & 0x0F ) == 0b00001101 )
{
cKey = 8;
}
if ( ( PORT_B & 0x0F ) == 0b00001011 )
{
cKey = 9;
}
if ( ( PORT_B & 0x0F ) == 0b00000111 )
{
cKey = 'C';
}
PORT_A = 0b11110111; /* try row 3 */
if ( ( PORT_B & 0x0F ) == 0b00001110 ) /* check columns */
{
cKey = '*';
}
if ( ( PORT_B & 0x0F ) == 0b00001101 )
{
cKey = 0;
}
if ( ( PORT_B & 0x0F ) == 0b00001011 )
{
cKey = '#';
}
if ( ( PORT_B & 0x0F ) == 0b00000111 )
{
cKey = 'D';
}
delay_ms ( 5 );
}
PORT_A = 0xF0; /* make all four rows low */
while ( ( PORT_B & 0x0F ) != 0x0F ) /* wait until all columns are high */
{
delay_ms ( 5 );
}
PORT_A = 0xFF; /* make all four rows high */
if ( ( cKey != NOKEY ) && ( cGettingNewData == NO ) )
{
StartLampTimer ( cLampSeconds ); /* turn lamp on anytime a key is pressed */
}
return ( cKey );
}
void GetNewData ( char cBaseAddress, char cOffset, char cKeyCount, char cAllKeys )
{
char cCnt, cK;
char cActive = NO; /* default = inactive */
cProgMode = YES;
for ( cCnt = 0; cCnt < cKeyCount; cCnt++ )
{
cK = WaitKey(); /* get key */
if ( ( cAllKeys == NO ) && ( cK > 9 ) ) /* if out of range */
{
cK = 0; /* zero it */
}
write_eeprom ( cBaseAddress + cCnt, cK );
if ( cK != 0 ) /* if any key is not zero... */
{
cActive = YES; /* show that this field is active */
}
}
write_eeprom ( cBaseAddress + cOffset, cActive ); /* put in active flag */
cProgMode = NO;
}
void UpdateLampSetting ( void )
{
cLampSeconds = ( 10 * read_eeprom ( LAMP_TIME_BASE ) ) + read_eeprom ( LAMP_TIME_BASE + 1 );
}
void UpdateLockSetting ( void )
{
cLockSeconds = ( 10 * read_eeprom ( LOCK_TIME_BASE ) ) + read_eeprom ( LOCK_TIME_BASE + 1 );
}
void ClearFlags ( void )
{
cMAinvalid = OFF; /* default is off until mismatch found */
if ( read_eeprom ( TRANS_BASE_A + COMBO_ACTIVE_FLAG_OFFSET ) == NO )
{
cTAinvalid = ON;
}
else
{
cTAinvalid = OFF;
}
if ( read_eeprom ( TRANS_BASE_B + COMBO_ACTIVE_FLAG_OFFSET ) == NO )
{
cTBinvalid = ON;
}
else
{
cTBinvalid = OFF;
}
if ( read_eeprom ( TRANS_BASE_C + COMBO_ACTIVE_FLAG_OFFSET ) == NO )
{
cTCinvalid = ON;
}
else
{
cTCinvalid = OFF;
}
if ( read_eeprom ( TRANS_BASE_D + COMBO_ACTIVE_FLAG_OFFSET ) == NO )
{
cTDinvalid = ON;
}
else
{
cTDinvalid = OFF;
}
if ( read_eeprom ( LAMP_TIME_BASE + LAMP_ACTIVE_FLAG_OFFSET ) == NO )
{
cTIinvalid = ON;
}
else
{
cTIinvalid = OFF;
}
if ( read_eeprom ( LOCK_TIME_BASE + LOCK_ACTIVE_FLAG_OFFSET ) == NO )
{
cTLinvalid = ON;
}
else
{
cTLinvalid = OFF;
}
}
void StartOpenLock ( char cCnt )
{
disable_interrupts ( INT_RTCC );
if ( cTLinvalid == NO )
{
LOCK_OUT = ON; /* energize solenoid */
}
iLockLimit = ( long ) cCnt * 15; /* 15 tics per second */
iLockCount = 0; /* zero timer for turning lock solenoid off */
enable_interrupts ( INT_RTCC );
}
void StartLampTimer ( char cCnt )
{
disable_interrupts ( INT_RTCC );
if ( cTIinvalid == NO ) /* if lamp was programmed for other than 0 seconds */
{
ILL_OUT = ON; /* turn on lamp */
}
iLampLimit = ( long ) cCnt * 15; /* 15 tics per second */
ilampCount = 0; /* zero timer for turning lamp off */
cResetKbdCount = 0; /* zero timer for keyboard reset */
enable_interrupts ( INT_RTCC );
}
#int_rtcc
void TimerInterrupt ( void ) /* 65mS tic */
{
if ( cProgMode == YES ) /* if in program mode */
{
if ( cProgTimeoutCount++ >= 8 )
{
ILL_OUT ^= 1; /* toggle lamp state to signal programming mode */
cProgTimeoutCount = 0;
}
}
else /* if in normal mode */
{
if ( iLampCount++ > iLampLimit ) /* if timeout yet */
{
ILL_OUT = OFF; /* turn lamp off */
}
if ( cResetKbdCount++ > RESET_KBD_COUNT )
{
cKeyCnt = 0; /* reset to first key */
}
if ( iLockCount++ > iLockLimit )
{
LOCK_OUT = OFF; /* de-energize lock solenoid */
}
}
}