| CODE |
;**** A P P L I C A T I O N N O T E A V R ??? ************************ ;* ;* Title: Multiplexing LED drive and 4x4 keypad sampling ;* Version: 1.0 ;* Last Updated: 98.07.24 ;* Target: All AVR Devices ;* ;* Support E-mail: avr@atmel.com ;* ;* DESCRIPTION ;* This Application note covers a program to provide a 24 hr Industrial ;* timer or real-time clock using I/O pins for dual functions. ;* With input via a 4 x 4 matrix keypad, output to a multiplexed ;* four digit LED display and two ON/OFF outputs to drive loads via additional ;* interface circuitry. LED loads are driven in this example but it could drive ;* any load with the addition of suitable components. Tactile feedback is provided ;* on every key press by a piezo sounder which beeps when a key is pressed. ;* Included is a main program that allows clock setting via the keypad ;* and one ON/OFF time setting per 24 hours for each load, functions for the ;* real time clock, key scanning, and adjustment routines. The example runs on ;* the AT90S1200 to demonstrate how limited I/O can be overcome, but can ;* be any AVR with suitable changes in vectors, EEPROM and stack pointer. ;* The timing assumes a 4.096 MHz crystal is employed (a 4 MHz crystal produces ;* an error of -0.16% if 178 instead of 176 used in the timer load sequence, but this ;* could be adjusted in software at regular intervals). Look up tables are ;* used in EEPROM to decode the display data, with additional characters provided ;* for time and ON/OFF setting displays and a key pad conversion table. ;* If the EEPROM is needed for your application the tables could be moved ;* to ROM in the larger AVR devices. ;*************************************************************************** ;***** Registers used by all programs ;******Global variables used by routines .def loset =r1;storage for timeset minutes .def hiset =r2;storage for timeset hours .def ld1minon=r3;storage for load on and off times .def ld1hron =r4;set from keypad entry .def ld1minoff=r5;and tested in the housekeeping function .def ld1hroff=r6;and stores on or off times for the loads .def ld2minon=r7 .def ld2hron =r8 .def ld2minoff=r9 .def ld2hroff=r10 .def temp =r16;general scratch space .def second =r17;storage for RTC second count .def minute =r18;storage for RTC minute count .def hour =r19;storage for RTC hour count .def mask =r20;flash mask for digits flashing .def blink =r21;colon blink rate counter .def bounce =r22;keypad debounce counter .def flash =r23;flash delay counter .def lobyte =r24;storage for display function minutes digits .def hibyte =r25;storage for display function hours digits .def key =r26;key number from scan ;***'key' values returned by 'keyscan'*************************** ;VALUE 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ;KEY 1 2 3 F 4 5 6 E 7 8 9 D A 0 B C NONE ;FUNC 1 2 3 LD1ON 4 5 6 LD1OFF 7 8 9 LD2ON SET 0 CLEAR LD2OFF .def tock =r27;5 ms pulse .def flags =r28;flag byte for keypad command keys ; 7 6 5 4 3 2 1 0 ; 5ms keyok ld2off ld2on ld1off ld1on ld2 ld1 ; tick 0 = off, 1 = on .equ ms5 =7;ticks at 5 ms intervals for display time .equ keyok =6;sets when key is debounced, must be cleared again .equ ld2off =5;set by load ON/OFF key press and flags .equ ld2on =4;up the need for action .equ ld1off =3;in the housekeeping routine .equ ld1on =2 .equ ld2 =1;when set tells the housekeeping routine to .equ ld1 =0;check load on/off times. ;***the T flag in the status register is used as a SET flag for time set .equ clear =0;RTC modification demand flag ;Port B pins .equ col1 =0;LED a segment/keypad col 1 .equ col2 =1;LED b segment/keypad col 2 .equ col3 =2;LED c segment/keypad col 3 .equ col4 =3;LED d segment/keypad col 4 .equ row1 =4;LED e segment/keypad row 1 .equ row2 =5;LED f segment/keypad row 2 .equ row3 =6;LED g segment/keypad row 3 .equ row4 =7;LED decimal point/keypad row 4 ;Port D pins .equ A1 =0;common anode drives (active low) .equ A2 =1; .equ A3 =2; .equ A4 =3; .equ LOAD1 =4;Load 1 output (active low) .equ LOAD2 =5;Load 2 output (active low) .equ PZ =6;Piezo sounder output (active low) .include "1200def.inc" ;***** Registers used by timer overflow interrupt service routine .def timer =r31;scratch space for timer loading .def status =r0;low register to preserve status register ;*****Look up table for LED display decoding ********************** .eseg ;EEPROM segment .org 0 table1: .db 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90 ;digit 0 1 2 3 4 5 6 7 8 9 .db 0x86,0x8E,0xA3,0xAB,0XFF,0XFF ;digit E f o n BLANK special characters ;****Look up table for key value conversion into useful numbers**** ;key 1 2 3 F 4 5 6 E 7 8 9 D A 0 B C table2: .db 1, 2, 3,15, 4, 5, 6,14, 7, 8, 9, 13, 10, 0, 11, 12 ;value 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ;****Source code*************************************************** .cseg ;CODE segment .org 0 rjmp reset ;Reset handler nop ;unused ext. interrupt rjmp tick ;timer counter overflow (5 ms) nop ;unused analogue interrupt ;*** Reset handler ************************************************** ;*** to provide initial port, timer and interrupt setting up reset: ser temp ; out DDRB,temp ;initialize port B as all Outputs out DDRD,temp ;initialize port D as all Outputs out PORTB,temp ;key columns all high/LEDs off out PORTD,temp ;turn off LEDs and loads off ldi temp,0x04 ;timer prescalar /256 out TCCR0,temp ldi timer,176 ;load timer for 5 ms out TCNT0,timer ;(256 - n)*256*0.2441 us ldi temp,0x02 ;enable timer interrupts out TIMSK,temp clr flags ;clear control flags clr tock ;clear 5 ms tick clr bounce ;clear key bounce counter clr flash clr blink sei ;enable global interrupts ;****Flash EEEE on LEDS as test and power down warning************** ;****repeats until SET key is pressed on keypad timesetting: ldi hibyte,0xaa ;show "EEEE" on LED ldi lobyte,0xaa ;display and ser mask ;set flashing display notyet: rcall display ;display until time set brtc notyet ;repeat until SET key pressed rcall setrtc ;and reset time mov hour,hiset ;and reload hours mov minute,loset;and minutes clt ;clear T flag ;*****Main clock house keeping loop***************************** do: clr mask ;do housekeeping cpi blink,100 ;is half second up brne nohalf clr blink com flash ;invert flash nohalf: cpi second,60 ;is one minute up? brne nochange ;no clr second ;yes clear seconds and inc minute ;add one to minutes mov temp,minute andi temp,0x0f ;mask high minute cpi temp,10 ;is it ten minutes? brne nochange ;no andi minute,0xf0;clear low minutes ldi temp,0x10 add minute,temp ;increment high minutes cpi minute,0x60 ;is it 60 minutes? brne nochange ;no clr minute ;yes, clear minutes and inc hour ;add one to hours mov temp,hour andi temp,0x0f ;mask high hour cpi temp,10 ;is 10 hours up? brne nochange ;no andi hour,0xf0 ;yes, increment ldi temp,0x10 add hour,temp ;high hours nochange: cpi hour,0x24 ;is it 24 hours? brne sameday ;no, clr hour ;yes, clear time variables clr minute ;to start new day clr second sameday: ;update times mov lobyte,minute mov hibyte,hour rcall display ;show time for 20 ms brtc case1 ;if not SET rcall setrtc ;and reset time mov hour,hiset ;and reload hours mov minute,loset;and minutes clt ;else, clear T flag case1: sbrc flags,ld1 ;is load 1 active? rjmp chkload1 ;yes, check load 1 case2: sbrc flags,ld2 ;is load 2 active rjmp chkload2 ;yes, check load 2 case3: sbrc flags,ld1on;is load 1 on time reset rjmp setld1on ;yes reset on time case4: sbrc flags,ld1off;is load 1 off time reset rjmp setld1off ;yes reset off time case5: sbrc flags,ld2on;is load 2 on time reset rjmp setld2on ;yes reset on time case6: sbrc flags,ld2off;is load 2 on time reset rjmp setld2off ;yes reset on time case7: rjmp do ;repeat housekeeping loop ;****case routines to service load times and key presses******** chkload1: cp hour,ld1hroff;is load 1 off time reached? brne onload1 cp minute,ld1minoff brne onload1 sbi PORTD,LOAD1 ;yes, turn load 1 off onload1: cp hour,ld1hron ;is load 1 on time reached? brne case2 cp minute,ld1minon brne case2 cbi PORTD,LOAD1 ;yes,turn load 1 on rjmp case2 ;repeat with load on chkload2: cp hour,ld2hroff;is load 2 off time reached? brne onload2 cp minute,ld2minoff brne onload2 sbi PORTD,LOAD2 ;yes, turn load 2 off onload2: cp hour,ld2hron ;is load 2 on time reached? brne case3 cp minute,ld2minon brne case3 cbi PORTD,LOAD2 ;yes,turn load 2 on rjmp case3 ;repeat with load on setld1on: sbr flags,0x01 ;make load 1 active rcall setrtc ;pickup new on time mov ld1hron,hiset;and store mov ld1minon,loset cbr flags,0x04 ;clear ld1on flag rjmp case4 setld1off: rcall setrtc ;pickup new off time mov ld1hroff,hiset;and store mov ld1minoff,loset cbr flags,0x08 ;clear ld1off flag rjmp case5 setld2on: sbr flags,0x02 ;make load 2 active rcall setrtc ;pickup new on time mov ld2hron,hiset;and store mov ld2minon,loset cbr flags,0x10 ;clear ld2on flag rjmp case6 setld2off: rcall setrtc ;pickup new on time mov ld2hroff,hiset;and store mov ld2minoff,loset cbr flags,0x20 ;clear ld2off flag rjmp case7 ;****Multiplexing routine to display time and scan keypad every***** ;****second pass,used by all routines taking digits from hibyte ;****and lobyte locations with each digit on for 5 ms display: ser temp ;clear display out PORTB,temp ;****Keypad scanning routine to update key flags******************* keyscan: cbr flags,0x40 ;clear keyok flag ldi key,0x10 ;set no key pressed value ser temp ;set keypad port high prior to out PORTB,temp ;reinitializing the port in temp,PORTD ;turn off LEDs and leave loads ori temp,0x0f ;untouched prior to out PORTD,temp ;key scan ldi temp,0x0f ;set columns output and out DDRB,temp ;rows input with pull-ups ldi temp,0xf0 ;enabled and all columns out PORTB,temp ;low ready for scan ldi temp,20 ;short settling time tagain1: dec temp brne tagain1 sbis PINB,ROW1 ;find row of keypress ldi key,0 ;and set ROW pointer sbis PINB,ROW2 ldi key,4 sbis PINB,ROW3 ldi key,8 sbis PINB,ROW4 ldi key,12 ldi temp,0xF0 ;change port B I/O to out DDRB,temp ;find column press ldi temp,0x0F ;enable pull ups and out PORTB,temp ;write 0s to rows ldi temp,20 ;short settling time tagain2: dec temp brne tagain2 ;allow time for port to settle clr temp sbis PINB,COL1 ;find column of keypress ldi temp,0 ;and set COL pointer sbis PINB,COL2 ldi temp,1 sbis PINB,COL3 ldi temp,2 sbis PINB,COL4 ldi temp,3 add key,temp ;merge ROW and COL for pointer cpi key,0x10 ;if no key pressed breq nokey ;escape routine, else ldi temp,0x10 add key,temp ;change to table 2 out EEAR,key ;send address to EEPROM (0 - 15) sbi EECR,EERE ;strobe EEPROM in key,EEDR ;read decoded number for true key convert: cpi key,10 ;is it SET key ? brne notset ;no check next key set ;yes set T flag in status register notset: cpi key,11 ;is key CLEAR? brne notclear ;no, check next key sbi PORTD,LOAD1 ;yes, shut down all loads sbi PORTD,LOAD2 cbr flags,0x03 ;deactivate both loads notclear: cpi key,15 ;is key LD1ON? brne notld1on ;no, check next key sbr flags,0x04 ;yes, set LD1ON flag notld1on: cpi key,14 ;is key LD1OFF? brne notld1off ;no, check next key sbr flags,0x08 ;yes, set LD1OFF flag notld1off: cpi key,13 ;is key LD2ON? brne notld2on ;no, check next key sbr flags,0x10 ;yes, set LD2ON flag notld2on: cpi key,12 ;is key LD2OFF? brne notld2off ;no, check next key sbr flags,0x20 ;yes, set LD2OFF flag notld2off: ;***Tactile feedback note generation routine***************** ;***provides a 4 kHz TONE to the piezo sounder for 5 ms***** tactile: cbr flags,0x80 cbi PORTD,PZ ;turn on piezo ldi temp,125 ;for a short time t1again: dec temp brne t1again sbi PORTD,PZ ;turn on piezo ldi temp,125 ;for a short time t2again: dec temp brne t2again sbrs flags,ms5 ;repeat for 5ms rjmp tactile notok: cpi bounce,40 brlo nokey sbr flags,0x40 ;set bounce flag nokey: ser temp out DDRB,temp ;reinitialize port B as all Outputs out PORTB,temp ;and clear LEDs ;***Display routine to multiplex all four LED digits**************** cbi PORTD,A1 ;turn digit 1 on mov temp,lobyte ;find low minute digit1: cbr flags,0x80 ;clear 5 ms tick flag andi temp,0x0f ;mask high nibble of digit out EEAR,temp ;send address to EEPROM (0 - 15) sbi EECR,EERE ;strobe EEPROM in temp,EEDR ;read decoded number sbrs flash,clear;flash every 1/2 second or temp,mask ;flash digit if needed out PORTB,temp ;write to LED for 5 ms led1: sbrs flags,ms5 ;5 ms finished? rjmp led1 ;no, check again sbi PORTD,A1 ;turn digit 1 off ser temp ;clear display out PORTB,temp cbi PORTD,A2 ; mov temp,lobyte ;find high minute swap temp digit2: cbr flags,0x80 ;clear 5 ms tick flag andi temp,0x0f ;mask high nibble of digit out EEAR,temp ;send address to EEPROM (0 - 15) sbi EECR,EERE ;strobe EEPROM in temp,EEDR ;read decoded number sbrs flash,clear;flash every 1/2 second or temp,mask ;flash digit if needed out PORTB,temp ;write to LED for 5 ms led2: sbrs flags,ms5 ;5 ms finished? rjmp led2 ;no, check again sbi PORTD,A2 ; ser temp ;clear display out PORTB,temp cbi PORTD,A3 ; mov temp,hibyte digit3: cbr flags,0x80 ;clear 5 ms tick flag andi temp,0x0f ;mask high nibble of digit out EEAR,temp ;send address to EEPROM (0 - 15) sbi EECR,EERE ;strobe EEPROM in temp,EEDR ;read decoded number sbrs second,clear;flash colon andi temp,0x7f sbrs flash,clear;flash every 1/2 second or temp,mask ;flash digit if needed out PORTB,temp ;write to LED for 5 ms led3: sbrs flags,ms5 ;5 ms finished? rjmp led3 ;no, check again sbi PORTD,A3 ser temp ;clear display out PORTB,temp cbi PORTD,A4 ; mov temp,hibyte swap temp andi temp,0x0f ;is hi hour zero? brne digit4 ldi temp,0xff ;yes,blank hi hour digit4: cbr flags,0x80 ;clear 5 ms tick flag andi temp,0x0f ;mask high nibble of digit out EEAR,temp ;send address to EEPROM (0 - 15) sbi EECR,EERE ;strobe EEPROM in temp,EEDR ;read decoded number sbrs flash,clear;flash every 1/2 second or temp,mask ;flash digit if needed out PORTB,temp ;write to LED for 5 ms led4: sbrs flags,ms5 ;5 ms finished? rjmp led4 ;no, check again sbi PORTD,A4 ser temp ;clear display out PORTB,temp tst mask ;is flash complete? breq outled ;yes, exit cpi blink,50 ;is blink time done? brlo outled ;no, exit clr blink ;yes, clear blink rate counter com flash ;and invert flash byte outled: ret ;****Function to Set RTC/on-off hours and minutes from keypad ;****returns with minutes in 'loset' and hours in'hiset' setrtc: ser mask ;set flashing display ldi hibyte,0xdf ;place 'n' in hi hour ser lobyte ;and blank in lo hr & minutes hihrus: clr bounce bounce1: rcall display ;display and check keypad sbrs flags,keyok rjmp bounce1 cbr flags,0x40 ;clear keyok flag cpi key,0x03 ;is high hour > 2 brsh hihrus ;yes, read key again hihrok: ;no, valid entry swap key ;move hihour to hi nibble mov hiset,key ;and store in hours ldi hibyte,0x0d ;place 'n' in lo hour add hibyte,hiset;merge hihour and 'n' lohrus: clr bounce bounce2: rcall display ;display and check keypad sbrs flags,keyok;is key stable? rjmp bounce2 ;no try again cbr flags,0x40 ;yes, clear keyok flag mov temp,hibyte ;check that total hours andi temp,0xf0 ;are not > 24 add temp,key cpi temp,0x24 ;is hour>24? brsh lohrus ;yes, read key again add hiset,key ;no, merge hi and lo hours lohrok: mov hibyte,hiset;display hours as set ldi lobyte,0xdf ;place 'n' in hi minutes himinus: clr bounce bounce3: rcall display ;display and check keypad sbrs flags,keyok rjmp bounce3 cbr flags,0x40 ;clear keyok flag cpi key,6 ;is hi minutes >5 brsh himinus ;no, read key again lominok: swap key ;move himin to hi nibble mov loset,key ;and store in minutes ldi lobyte,0x0d ;place 'n' in lo minutes add lobyte,loset;merge with hi minute lominus: clr bounce bounce4: rcall display ;display and check keypad sbrs flags,keyok rjmp bounce4 cbr flags,0x40 ;clear keyok flag cpi key,10 ;is key >9 brsh lominus ;no, read key again add loset,key ;yes, merge hi and lo minutes clr mask ;clear digits flash ret ;and return with time set ;****Timer Overflow Interrupt service routine****************************** ;****Updates 5 ms, flash and debounce counter to provide RTC time reference tick: in status,SREG ;preserve status register inc tock ;add one to 5 ms 'tock' counter inc blink ;and blink rate counter inc bounce ;and bounce rate delay sbr flags,0x80 ;set 5 ms flag for display time cpi tock,200 ;is one second up? breq onesec ;yes, add one to seconds nop ;balance interrupt time rjmp nosecond ;no, escape onesec: inc second ;add one to seconds clr tock ;clear 5 ms counter nosecond: ldi timer,176 ;reload timer out TCNT0,timer out SREG,status ;restore status register reti ;return to main |