/**************************************************************************** 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 */ } } }