; security.asm ; Doug Ricket ; Microcontroller Project Laboratory ; 7-19-01 ; ********* PIN DEFINITIONS ********* RS equ p3.7 ; Pin 4 RW equ p3.6 ; Pin 5 EN equ p3.5 ; Pin 6 LCDATA equ p2 ; Pins 7-14 BUSY equ p2.7 ; Pin 14 TRIGGER equ p3.4 SPEAKER equ p3.3 LIGHT equ p3.2 ; ********** MEMORY BYTE DEFINITIONS ************* pauseH equ 70h ; Memory locations for pause delays, high pauseM equ 71h ; medium, pauseL equ 72h ; and low timeTicks equ 73h ; 1/256th second timeSec equ 74h ; Memory locations for storing time prev_key equ 75h ; stores the previous key pressed usercode1 equ 76h ; stores user # entered usercode2 equ 77h usercode3 equ 78h usercode4 equ 79h lockcode1 equ 7Ah ; these store the security code to turn off the alarm lockcode2 equ 7Bh lockcode3 equ 7Ch lockcode4 equ 7Dh alarmcounter equ 7Eh ; tells alarm when to change ; ************ MEMORY BIT DEFINITIONS *************** newtime equ 0h ; BIT address triggered equ 1h ; BIT address alarm equ 2h ; BIT address armed equ 3h ; indicates alarm is armed alarm_light equ 4h ; true if alarm light is on org 0h ljmp main org 002Bh T2ISR: push acc jnb alarm, skip_alarm acall do_alarm skip_alarm: inc timeTicks mov a, timeTicks jnz t2isr_done ; on overflow, decrement seconds dec timeSec setb newtime t2isr_done: pop acc clr TF2 clr EXF2 reti main: mov sp, #30h clr alarm clr triggered clr newtime clr armed mov usercode1, #' ' mov usercode2, #' ' mov usercode3, #' ' mov usercode4, #' ' mov lockcode1, #'4' mov lockcode2, #'3' mov lockcode3, #'2' mov lockcode4, #'1' acall serialinit acall clock_init acall lcd_init acall lcd_clear acall switch_armed mainloop: acall test_key acall check_trigger acall check_time sjmp mainloop test_key: acall check_new_key jz test_key_done acall key_to_ascii cjne a, #'#', notpound acall switch_armed sjmp test_key_done notpound: cjne a, #'*', notstar acall set_code sjmp test_key_done notstar mov usercode4, usercode3 mov usercode3, usercode2 mov usercode2, usercode1 mov usercode1, a push acc mov a, #40h ; GOTO 1,0 acall lcd_set_cursor_pos pop acc ; ** PRINT THE CURRENT FOUR DIGIT CODE ENTERED mov a, usercode4 acall lcd_print_chr mov a, usercode3 acall lcd_print_chr mov a, usercode2 acall lcd_print_chr mov a, usercode1 acall lcd_print_chr test_key_done: ret switch_armed: mov a, #00h ; GOTO 0,0 acall lcd_set_cursor_pos jnb armed, arm_on arm_off: acall check_code jz bad_code clr armed clr triggered ; turn off the trigger and alarm clr alarm clr LIGHT ; turn off the light acall lcd_print db "Security OFF", 0h ret arm_on: setb armed acall lcd_clear acall lcd_print db "Security ON ", 0h acall clear_user_code ret bad_code: acall lcd_print db "Wrong Code ", 0h acall clear_user_code ret clear_user_code: mov usercode1, #' ' mov usercode2, #' ' mov usercode3, #' ' mov usercode4, #' ' ret ; returns 0 if user code != lockcode check_code: mov a, usercode1 cjne a, lockcode1, check_code_fails mov a, usercode2 cjne a, lockcode2, check_code_fails mov a, usercode3 cjne a, lockcode3, check_code_fails mov a, usercode4 cjne a, lockcode4, check_code_fails check_code_passes: mov a, #01h ret check_code_fails: mov a, #00h ret set_code: jb armed, no_set_when_armed mov lockcode1, usercode1 mov lockcode2, usercode2 mov lockcode3, usercode3 mov lockcode4, usercode4 mov a, #00h ; goto 0,0 acall lcd_set_cursor_pos acall lcd_print db "Code Set ", 0h ret: no_set_when_armed: mov a, #00h ; goto 0,0 acall lcd_set_cursor_pos acall lcd_print db "Not set ", 0h ret check_trigger: jb triggered, check_trigger_done jb TRIGGER, check_trigger_done jnb armed, check_trigger_done setb triggered ; TRIGGERED! 10 sec delay to alarm mov timeSec, #10 setb newtime mov a, #00h ; GOTO 0,0 acall lcd_set_cursor_pos acall lcd_print db "Triggered ", 0h check_trigger_done: ret check_time: jnb triggered, check_time_done jb alarm, check_time_done jnb newtime, check_time_done mov a, #4Ch ; GOTO 1,12 acall lcd_set_cursor_pos mov a, timeSec acall lcd_print_dec clr newtime ; indicate that we've dealt with this time mov a, timeSec jnz check_time_done setb alarm mov a, #00h ; GOTO 0,0 acall lcd_set_cursor_pos check_time_done: ret do_alarm: cpl SPEAKER ; generate a tone inc alarmcounter ; 2 incs gives twice per second inc alarmcounter mov a, alarmcounter jnz alarm_done ; on wraparound (twice per second) flash lights cpl alarm_light mov a, #00h acall lcd_set_cursor_pos jb alarm_light, light_on light_off: clr LIGHT acall lcd_print db "ALARM! ", 0h sjmp alarm_done light_on: setb LIGHT acall lcd_print db " ", 0h alarm_done: ret ; ----------------------------- KEYPAD FUNCTIONS -------------- ; KEYPAY PIN DEFINITIONS keyport equ p1 col1 equ p1.7 col2 equ p1.6 col3 equ p1.5 col4 equ p1.4 row1 equ p1.3 row2 equ p1.2 row3 equ p1.1 row4 equ p1.0 ; ************************* ; Function: check_key ; ; Checks for a key from a 4x4 matrix keypad ; Turns off each row, tests to see if any columns go off ; If so, a key is found, and the key number 1-16 is stored in A ; Otherwise, 0 is stored in A ; ************************* check_key: mov keyport, #0FFh ; ready for input clr row1 ; Row 1 mov a, #01h jnb col1, keyfound ; If col1 went low, then it connects to row1 mov a, #02h jnb col2, keyfound mov a, #03h jnb col3, keyfound mov a, #04h jnb col4, keyfound setb row1 clr row2 ; Row 2 mov a, #05h jnb col1, keyfound mov a, #06h jnb col2, keyfound mov a, #07h jnb col3, keyfound mov a, #08h jnb col4, keyfound setb row2 clr row3 ; Row 3 mov a, #09h jnb col1, keyfound mov a, #0Ah jnb col2, keyfound mov a, #0Bh jnb col3, keyfound mov a, #0Ch jnb col4, keyfound setb row3 clr row4 ; Row 4 mov a, #0Dh jnb col1, keyfound mov a, #0Eh jnb col2, keyfound mov a, #0Fh jnb col3, keyfound mov a, #10h jnb col4, keyfound setb row4 keynotfound: mov a, #00h ; zero = no key pressed ret keyfound: ; a holds 1-16 ret ; check_new_key ; if check_key != prev_key, return acc; else return zero check_new_key: acall check_key cjne a, prev_key, new_key_found mov a, #0 ret new_key_found: mov prev_key, a ret ; ************************ ; Function: key_to_ascii ; ; Takes a key index 1-16 in A ; Returns the ascii code corresponding to that key in A ; ************************ key_to_ascii: ; Convert key code to ascii char movc a, @a+pc ; Lookup from the following table ret db '1', '2', '3', 'A' db '4', '5', '6', 'B' db '7', '8', '9', 'C' db '*', '0', '#', 'D' ; ************************* ; wait_for_key_done ; ; Loops until no key is pressed ; ************************* wait_for_key_done: push acc ; don't disturb the value in A wait_for_key_done_loop: acall check_key jnz wait_for_key_done_loop pop acc ret ; ******************** ; get_key ; ; Waits for a key to be pressed, ; Gets the key into A, ; Waits for the key to be released ; ******************** get_key: acall check_key jz get_key ; key == 0 means no key, so try again acall wait_for_key_done ret ; ************** ; get_char ; ; Waits for key press and release ; Returns ascii character for key ; ************** get_char: acall get_key acall key_to_ascii ret ; -------------------- END OF KEYPAD FUNCTIONS ; ************* TIME ************* clock_init: mov timeSec, #0h ;mov timeMin, #0h ;mov timeHour, #0h mov timeTicks, #0h ; 7200 (1c20) gives 1/256th second mov t2con, #00h mov RCAP2H, #0E3h ; 65536 - 7200 mov RCAP2L, #0E0h mov th2, #0E3h mov tl2, #0E0h setb TR2 ; start timer setb EA ; enable interrupts setb IE.5 ; ET2: enable timer 2 interrupt ret lcd_print_dec: ; prints a 2-digit # in ascii push acc push b mov b, #0Ah ; /div 10 div ab acall lcd_print_digit ; print 10s mov a, b acall lcd_print_digit ; print 1s (remainder) pop b pop acc ret lcd_print_digit: push acc add a, #30h ; convert to decimal acall lcd_print_chr pop acc ret ; ********* LCD High Level Commands ******** lcd_init: setb EN ; wait > 30 ms mov pauseH, #005h mov pauseM, #0E0h mov pauseL, #001h acall pause mov LCDATA, #38h ; 8-bit bus, 2 lines, 5x7 chars acall lcd_command_byte ; wait > 39 us mov pauseH, #05h mov pauseM, #01h mov pauseL, #50h acall pause mov LCDATA, #0Ch ; Display on, Cursor off. Blink off. acall lcd_command_byte ; wait > 39 us mov pauseH, #05h mov pauseM, #01h mov pauseL, #50h acall pause acall lcd_clear ; wait > 1.53 ms mov pauseH, #05h mov pauseM, #0Dh mov pauseL, #01h acall pause mov LCDATA, #06h ; Cursor auto-advance acall lcd_command_byte ret pause: ; #1c2000 ticks = 1 sec loop1: loop2: loop3: djnz pauseL, loop3 djnz pauseM, loop2 djnz pauseH, loop1 ret lcd_goto_line1: push acc mov a, #00h ; GOTO 0,0 acall lcd_set_cursor_pos pop acc ret lcd_goto_line2: push acc mov a, #40h ; GOTO 1,0 acall lcd_set_cursor_pos pop acc ret lcd_set_cursor_pos: push acc add a, #80h mov LCDATA, a acall lcd_command_byte pop acc acall crlf ; print a carriage return to the serial port ret lcd_clear: mov LCDATA, #01h ; Clear display acall lcd_command_byte ret lcd_print_chr: mov LCDATA, a acall sndchr ; send the data out the serial port too acall lcd_data_byte ret lcd_print_hex: push acc acall binasc ; convert acc to ascii acall lcd_print_chr ; print first ascii hex digit mov a, r2 ; get second ascii hex digit acall lcd_print_chr ; print it pop acc ret lcd_get_cursor_pos: acall lcd_read_status mov a, LCDATA anl a, #7Fh ; mask off the busy bit ret lcd_print: pop dph ; put return address in dptr pop dpl lcdprtstr: clr a ; set offset = 0 movc a, @a+dptr ; get chr from code memory cjne a, #0h, lcdmchrok ; if termination chr, then return sjmp lcdprtdone lcdmchrok: acall lcd_print_chr ; send character inc dptr ; point at next character sjmp lcdprtstr ; loop till end of string lcdprtdone: mov a, #1h ; point to instruction after string jmp @a+dptr ; return ; **************************** ; LCD Write Commands ; **************************** lcd_data_byte: setb RS ; RS high for data byte clr RW ; RW low for write mode mov pauseH, #01h mov pauseM, #01h mov pauseL, #20h acall pause clr EN nop setb EN nop acall lcd_wait_busy ret lcd_command_byte: clr RS ; RS low for command byte clr RW ; RW low for write mode clr EN mov pauseH, #01h mov pauseM, #01h mov pauseL, #70h acall pause nop setb EN acall lcd_wait_busy ret lcd_wait_busy: acall lcd_read_status lcd_wait_loop: jb BUSY, lcd_wait_loop ret lcd_read_status: clr RS ; read status setb RW ; read operation mov LCDATA, #0FFh ; prepare port for input clr EN nop setb EN nop ret ; *************************************************************** ; ****** SERIAL COMMANDS ************ ; *************************************************************** serialinit: ; set up serial port with a 11.0592 MHz crystal, ; use timer 1 for 9600 baud serial communications (19200 for RD2) mov tmod, #20h ; set timer 1 for auto reload - mode 2 mov tcon, #41h ; run counter 1 and set edge trig ints mov th1, #0fdh ; set 9600 baud with xtal=11.059mhz mov scon, #50h ; set serial control reg for 8 bit data ; and mode 1 ret ;=============================================================== ; subroutine sndchr ; this routine takes the chr in the acc and sends it out the ; serial port. ;=============================================================== sndchr: clr scon.1 ; clear the tx buffer full flag. mov sbuf,a ; put chr in sbuf txloop: jnb scon.1, txloop ; wait till chr is sent ret ;=============================================================== ; subroutine getchr ; this routine reads in a chr from the serial port and saves it ; in the accumulator. ;=============================================================== getchr: jnb ri, getchr ; wait till character received mov a, sbuf ; get character anl a, #7fh ; mask off 8th bit clr ri ; clear serial status bit ret ;=============================================================== ; subroutine print ; print takes the string immediately following the call and ; sends it out the serial port. the string must be terminated ; with a null. this routine will ret to the instruction ; immediately following the string. ;=============================================================== print: pop dph ; put return address in dptr pop dpl prtstr: clr a ; set offset = 0 movc a, @a+dptr ; get chr from code memory cjne a, #0h, mchrok ; if termination chr, then return sjmp prtdone mchrok: acall sndchr ; send character inc dptr ; point at next character sjmp prtstr ; loop till end of string prtdone: mov a, #1h ; point to instruction after string jmp @a+dptr ; return ;=============================================================== ; subroutine crlf ; crlf sends a carriage return line feed out the serial port ;=============================================================== crlf: push acc mov a, #0ah ; print lf acall sndchr pop acc cret: push acc mov a, #0dh ; print cr acall sndchr pop acc ret ;=============================================================== ; subroutine prthex ; this routine takes the contents of the acc and prints it out ; as a 2 digit ascii hex number. ;=============================================================== prthex: push acc acall binasc ; convert acc to ascii acall sndchr ; print first ascii hex digit mov a, r2 ; get second ascii hex digit acall sndchr ; print it pop acc ret ;=============================================================== ; subroutine binasc ; binasc takes the contents of the accumulator and converts it ; into two ascii hex numbers. the result is returned in the ; accumulator and r2. ;=============================================================== binasc: mov r2, a ; save in r2 anl a, #0fh ; convert least sig digit. add a, #0f6h ; adjust it jnc noadj1 ; if a-f then readjust add a, #07h noadj1: add a, #3ah ; make ascii xch a, r2 ; put result in reg 2 swap a ; convert most sig digit anl a, #0fh ; look at least sig half of acc add a, #0f6h ; adjust it jnc noadj2 ; if a-f then re-adjust add a, #07h noadj2: add a, #3ah ; make ascii ret ; end of program