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

SAFE_COMBO_02.C

PIC 16F628 Combination Lock
   - resets to first expected digit two seconds after last key is pressed
   - uses PWM to reduce current to coil after initial pulse

                    ---------          ----------                                   
           +5 --14-|VCC    B0|-17-----|C1        |
                   |       B1|-18-----|C2        |
          Gnd ---5-|       B2|-1------|C3        |
    6MHz XTAL --16-|       B4|-2------|R1  KBD   |
         XTAL --15-|       B5|-6------|R2        |
                   |       B6|-7------|R3        |
                   |       B7|-8------|R4        |
                   | 16F628  |         ----------
                   |         |
                   |       B3|-6--- LOCK SOLENOID
                   |       A1|-18-- LED
                    ---------

         =========
        |  1 2 3  |  R0
        |  4 5 6  |  R1
        |  7 8 9  |  R2
        |  * 0 #  |  R3
         =========
          C0 C1 C2

Keyboard connector is (backside):    C2 C1 C0 R3 R2 R1 R0

Oscillator = 6MHz crystal

Jon Fick  03/09/07

***************************************************************************/
#include <16F628A.h>
#include 

// Set configuration bits in the PIC processor
#fuses HS, NOPROTECT, NOWDT, PUT, BROWNOUT, NOMCLR, NOLVP

#define     COMBO_1         '1'
#define     COMBO_2         '2'
#define     COMBO_3         '3'
#define     COMBO_4         '4'
#define     COMBO_5         '5'
#define     COMBO_6         '6'
#define     KBD_C1          PIN_B0
#define     KBD_C2          PIN_B1
#define     KBD_C3          PIN_B2
#define     KBD_R1          PIN_B4
#define     KBD_R2          PIN_B5
#define     KBD_R3          PIN_B6
#define     KBD_R4          PIN_B7
#define     LOCK_OUT        PIN_B3
#define     LED             PIN_A1
#define     NOKEY           0xFF
#define     RESET_KBD_COUNT 46          // at 23 counts/second
#define     LOCK_HIGH       255         // full PWM duty cycle
#define     LOCK_HIGH_TIME  30          // mS at full coil current
#define     LOCK_MED        20          // partial PWM duty cycle
#define     LOCK_MED_TIME   1450        // mS at partial coil current
#define     LOCK_OFF        0           // no PWM current

#use delay ( clock = 6000000 )      // sets appropriate compiler constants
#use standard_io ( A )
#use standard_io ( B )

char GetKey ( void );                           // prototypes

static char cKbdTimeoutFlag, cLedCount;

#int_rtcc
void TimerInterrupt ( void )          // 43mS tic, 23/second
    {
    if ( cKbdTimeoutFlag != 0 )
        {
        cKbdTimeoutFlag--;             // count down to zero
        }
    if ( cLedCount++ > 12 )
        {
        cLedCount = 0;
        }
    if ( cLedCount > 6 )
        {
        output_high ( LED );
        }
    else
        {
        output_low ( LED );
        }
    }

void main ( void )
    {
    delay_ms ( 100 );                           // power up delay
    output_low ( LOCK_OUT );                     // turn off lock solenoid */
    output_low ( LED );
    port_b_pullups ( TRUE );                    /* enable pullups */
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_256 );
    cLedCount = 0;
    // PWM is for relay current control
    setup_ccp1 ( CCP_PWM );                     // set for PWM mode
    //The cycle time will be ( 1 / clock ) * 4  * t2div * ( period + 1 )
    // 1/6000000 * 4 * 1 * 64 = 42uS = 23KHz
    setup_timer_2 ( T2_DIV_BY_1, 64, 1 );      // set PWM period
    // duty cycle = value * ( 1 / clock ) * t2div
    // val * 1/6000000 * 1 = 1.2uS
    set_pwm1_duty ( LOCK_OFF );                  // set output off
    enable_interrupts ( INT_RTCC );             /* turn on timer interrupt */
    enable_interrupts ( GLOBAL );               /* enable interrupts */

    while ( TRUE )
        {
        while ( TRUE )
            {
            if ( GetKey() != COMBO_1 )
                {
                break;
                }
            if ( GetKey() != COMBO_2 )
                {
                break;
                }
            if ( GetKey() != COMBO_3 )
                {
                break;
                }
            if ( GetKey() != COMBO_4 )
                {
                break;
                }
            if ( GetKey() != COMBO_5 )
                {
                break;
                }
            if ( GetKey() != COMBO_6 )
                {
                break;
                }
            set_pwm1_duty ( LOCK_HIGH );                  // set output high current
                delay_ms ( LOCK_HIGH_TIME );
            set_pwm1_duty ( LOCK_MED );                  // set output medium current
                delay_ms ( LOCK_MED_TIME );
                set_pwm1_duty ( LOCK_OFF );                  // set output off
            }
        }
    }

char GetKey ( void )
    {
    char cKey;

    cKbdTimeoutFlag = RESET_KBD_COUNT;
    cKey = NOKEY;                               /* default is invalidated key */
    output_low ( KBD_R1 );                      /* make all four rows low */
    output_low ( KBD_R2 );
    output_low ( KBD_R3 );
    output_low ( KBD_R4 );
    delay_ms ( 5 );                             /* wait for row lines to settle */
    // wait until a button is pressed
    while ( ( input ( KBD_C1 ) == HIGH  ) && ( input ( KBD_C2 ) == HIGH ) && ( input ( KBD_C3 ) == HIGH ) )
        {
        if ( cKbdTimeoutFlag == 0 )        // if counted down to zero
            {
            return ( cKey );            // timed out, return NOKEY
            }
        }
    while ( TRUE )
        {
        output_low ( KBD_R1 );                  // try Row 1
        output_high ( KBD_R2 );
        output_high ( KBD_R3 );
        output_high ( KBD_R4 );
        delay_ms ( 1 );
        if ( input ( KBD_C1 ) == LOW )
            {
            cKey = '1';
            break;
            }
        if ( input ( KBD_C2 ) == LOW )
            {
            cKey = '2';
            break;
            }
        if ( input ( KBD_C3 ) == LOW )
            {
            cKey = '3';
            break;
            }
        output_high ( KBD_R1 );                  // try Row 2
        output_low ( KBD_R2 );
        output_high ( KBD_R3 );
        output_high ( KBD_R4 );
        delay_ms ( 1 );
        if ( input ( KBD_C1 ) == LOW )
            {
            cKey = '4';
            break;
            }
        if ( input ( KBD_C2 ) == LOW )
            {
            cKey = '5';
            break;
            }
        if ( input ( KBD_C3 ) == LOW )
            {
            cKey = '6';
            break;
            }
        output_high ( KBD_R1 );                  // try Row 3
        output_high ( KBD_R2 );
        output_low ( KBD_R3 );
        output_high ( KBD_R4 );
        delay_ms ( 1 );
        if ( input ( KBD_C1 ) == LOW )
            {
            cKey = '7';
            break;
            }
        if ( input ( KBD_C2 ) == LOW )
            {
            cKey = '8';
            break;
            }
        if ( input ( KBD_C3 ) == LOW )
            {
            cKey = '9';
            break;
            }
        output_high ( KBD_R1 );                  // try Row 4
        output_high ( KBD_R2 );
        output_high ( KBD_R3 );
        output_low ( KBD_R4 );
        delay_ms ( 1 );
        if ( input ( KBD_C1 ) == LOW )
            {
            cKey = '*';
            break;
            }
        if ( input ( KBD_C2 ) == LOW )
            {
            cKey = '0';
            break;
            }
        if ( input ( KBD_C3 ) == LOW )
            {
            cKey = '#';
            break;
            }
        break;    // break anyway to ensure no endless loop
        }
    delay_ms ( 5 );
    // wait until all buttons are up
    output_low ( KBD_R1 );                      /* make all four rows low */
    output_low ( KBD_R2 );
    output_low ( KBD_R3 );
    output_low ( KBD_R4 );
    while ( ( input ( KBD_C1 ) == LOW  ) || ( input ( KBD_C2 ) == LOW ) || ( input ( KBD_C3 ) == LOW ) );
    // make all four rows high
    output_high ( KBD_R1 );
    output_high ( KBD_R2 );
    output_high ( KBD_R3 );
    output_high ( KBD_R4 );
    return ( cKey );
    }