; *********************************************************************** ; Scrolling LED Clock Display - 7x10 Matrix LEDS ; - 60Hz AC interrupts ; ; Revision: 5/08/1999 ; Copyright 1999, LNS Technologies - All rights reserved ; EMAIL: lnstech@techkits.com ; INTERNET: http://www.techkits.com ; *********************************************************************** $mod2051 ; load 2051 definitions file ACFREQ EQU 60 ; 60 interrupts per second RAMSIZE EQU 32 ; 32 columns of 8 bit row data SCRATE EQU 30 ; default scroll rate PTR_R0 EQU R0 ; data pointer PTR_R1 EQU R1 ; data pointer INDEX EQU R3 ; index pointer ONEPASS EQU R4 ; one scroll pass counter SELRB0 EQU 000H ; Register bank 0 SELRB1 EQU 008H ; Register bank 1 SELRB2 EQU 010H ; Register bank 2 SELRB3 EQU 018H ; Register bank 3 ; Microcontroller input pins ACINT0 BIT P3.2 ; AC-60Hz interrupt signal ADVANCE BIT P3.3 ; time increment pushbutton MINSET BIT P3.4 ; slide switch in minute set position HRSET BIT P3.5 ; slide switch in hour set position ; Microcontroller output pins RST BIT P3.7 ; CD4017 reset to column 0 CLK BIT P1.7 ; CD4017 clock to increment column DSEG AT 0020H ; RAM space ORG 030H ; ; 8x22 RAM hidden buffer bufend: DS 4 bufdig4: DS 1 ; 4th digit in hidden buffer DS 4 bufdig3: DS 1 ; 3th digit in hidden buffer DS 1 bufcoln: DS 1 ; colon in hidden buffer DS 4 bufdig2: DS 1 ; 2th digit in hidden buffer DS 4 bufdig1: DS 1 ; 1st digit in hidden buffer ; 8x10 RAM display buffer dispend: DS 4 disdig2: DS 1 ; 2nd digit in display buffer DS 4 disdig1: DS 1 ; 1st digit in display buffer dummy1: DS 2 ; RAM variables ac60hz: DS 1 ; counts # of 60 Hz interrupts seconds: DS 1 ; seconds tensecs: DS 1 ; ten seconds minutes: DS 1 ; minutes tenmins: DS 1 ; ten minutes hours: DS 1 ; hours tenhours: DS 1 ; ten hours twelvehr: DS 1 ; twelve hour rollover rate: DS 1 ; scroll rate in RAM dummy2: DS 2 ; Stack in RAM for subroutine & interrupt processing stack: DS 020H ; stack depth stackend: DS 1 ; end of stack CSEG ; PROGRAM space ORG 0000H ; power on/reset vector ajmp on_reset ; jump to power-on reset routine ORG 0003H ; external interrupt 0 vector clr EA ; disable interrupts ajmp ac_intr ; jump to AC-60Hz interrupt routine ORG 000BH ; timer 0 overflow vector reti ; not used ORG 0013H ; external interrupt 1 vector reti ; not used ORG 001BH ; timer 1 overflow vector reti ; not used ORG 0023H ; serial I/O interrupt vector reti ; not used ORG 0040H ; begin constant data space ; *********************************************************************** ; Lookup table to convert time digits to display dot patterns. ; Bit 7 is used to clock the 4017 so it is always a 0 in the data table ; Data is inverted: 0 => LED is ON & 1 => LED is OFF ; *********************************************************************** X0: DB 01000001B DB 00111110B DB 00111110B DB 01000001B DB 07FH ; blank column between digits X1: DB 01111111B DB 00111101B DB 00000000B DB 00111111B DB 07FH ; blank column between digits X2: DB 00011101B DB 00101110B DB 00110110B DB 00111001B DB 07FH ; blank column between digits X3: DB 01011101B DB 00111110B DB 00110110B DB 01001001B DB 07FH ; blank column between digits X4: DB 01110000B DB 01110111B DB 00000001B DB 01110111B DB 07FH ; blank column between digits X5: DB 01011000B DB 00111010B DB 00111010B DB 01000110B DB 07FH ; blank column between digits X6: DB 01000001B DB 00110110B DB 00110110B DB 01001101B DB 07FH ; blank column between digits X7: DB 01111100B DB 01111110B DB 00000110B DB 01111000B DB 07FH ; blank column between digits X8: DB 01001001B DB 00110110B DB 00110110B DB 01001001B DB 07FH ; blank column between digits X9: DB 01011001B DB 00110110B DB 00110110B DB 01000001B DB 07FH ; blank column between digits XBLANK: DB 01111111B DB 01111111B DB 01111111B DB 01111111B DB 07FH ; blank column between digits ; Perform initializations on power-up. ; *********************************************************************** on_reset: mov IE, #0 ; deactivate all interrupts mov TCON, #0 ; timers off mov TMOD, #0 ; timers off mov SCON, #0 ; no serial communications mov SP, #(stack-1) ; set stack pointer mov PSW, #SELRB0 ; set active register bank ramclear: ; clear all ram locations mov A, #0 ; including stack space mov PTR_R0, #07FH mov PTR_R1, #07EH ramloop: mov @PTR_R0, A dec PTR_R0 djnz PTR_R1, ramloop acall dispclr ; clear display & hidden buffer mov P1, #01111111B ; make sure row LEDs are off mov P3, #0 ; set port 3 pins low setb MINSET ; set pin high to use as input setb HRSET ; set pin high to use as input setb ADVANCE ; set pin high to use as input setb ACINT0 ; set pin high to use as input setb RST ; reset CD4017 to column 0 mov A, #1 acall delay_ms ; delay 1 msec clr RST ; allow CD4017 to count default: ; reset to a default time on power-up mov tenhours, #1 ; default to 12:00 mov hours, #2 ; mov tenmins, #0 ; mov minutes, #0 ; mov seconds, #0 ; mov twelvehr, #12 ; set twelvehr for rollover acall newdigits ; build digit patterns in hidden buffer mov A, hours ; display hours digit mov PTR_R1, #disdig2 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM mov A, tenhours ; display ten hours digit mov PTR_R1, #disdig1 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM pwr_fail: ; flash 12 to show power fail acall refresh ; refresh display mov A, #250 ; blink display acall delay_ms ; delay 250 msec mov A, #250 ; blink display acall delay_ms ; delay 250 msec jnb minset, ac_init ; is minute set switch on? jnb hrset, ac_init ; is hour set switch on? ajmp pwr_fail ; flash 12 until time gets set ac_init: ; initialization for external interrupts mov ac60hz, #ACFREQ ; load interrupt repeat value setb IT0 ; make interrupt 0 edge activated setb EX0 ; enable external 0 interrupts setb EA ; enable interrupts ajmp fullscroll ; start scrolling the current time ; The main loop refreshes the display, scrolls & checks the switches. ; *********************************************************************** scrolltime: mov onepass, #RAMSIZE ; scroll all hidden buffer thru display scrollcol: acall refresh ; refresh display for 1 scroll period acall shiftram ; scroll left 1 column - shift RAM data jnb minset, setmins ; is minute set switch on? jnb hrset, sethrs ; is hour set switch on? djnz onepass, scrollcol ; has entire hidden buffer scrolled by? fullscroll: ; at end of full scroll - test switches acall dispclr ; clear display & hidden buffer acall newdigits ; update the time digits ajmp scrolltime ; do full time scroll again setmins: acall incmins ; set the minutes ajmp fullscroll ; do full scroll again sethrs: acall inchours ; set the hours ajmp fullscroll ; do full scroll again ; This loops one scroll period to refresh the led display. ; *********************************************************************** refresh: mov rate, #SCRATE ; reset scroll rate restart: mov B, #10 ; number of columns in display buffer mov PTR_R1, #dispend ; point to ram display buffer refloop: mov A, @PTR_R1 ; get row data from RAM buffer mov P1, A ; display row data in LEDs mov A, #1 ; acall delay_ms ; display row data for 1 ms mov P1, #07FH ; turn the LEDs off nop ; delay for visual effect mov P1, #0FFH ; increment 4017 to next column nop ; delay to allow 4017 to clock inc PTR_R1 ; advance to next row in RAM buffer djnz B, refloop ; loop to display all 10 columns djnz rate, restart ; if not time to scroll, continue refresh ret ; time to scroll - return ; Shift the data in the ram buffer to make the time digits scroll ; *********************************************************************** shiftram: mov B, #RAMSIZE-1 ; number of ram buffer locations - 1 mov PTR_R0, #disdig1 ; start with digit 1 mov PTR_R1, #disdig1-1 ; adjacent buffer location shiftloop: mov A, @PTR_R1 ; read ram buffer value mov @PTR_R0, A ; write to ram buffer dec PTR_R0 ; advance to next location dec PTR_R1 ; advance to next location djnz B, shiftloop ; shift next byte ret ; Update the time digits in the hidden buffer area. ; *********************************************************************** newdigits: mov A, minutes ; minutes digit mov PTR_R1, #bufdig4 ; point to ram buffer acall decode ; Yes - do update mov A, tenmins ; ten minutes digit mov PTR_R1, #bufdig3 ; point to ram buffer acall decode ; Yes - do update mov bufcoln, #01101011B ; store colon in buffer mov A, hours ; hours digit mov PTR_R1, #bufdig2 ; point to ram buffer acall decode ; Yes - do update mov A, tenhours ; ten hours digit mov PTR_R1, #bufdig1 ; point to ram buffer acall decode ; Yes - do update ret ; Increment the minutes digits while the button is pressed. ; *********************************************************************** incmins: mov A, minutes ; minutes digit mov PTR_R1, #disdig2 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM mov A, tenmins ; ten minutes digit mov PTR_R1, #disdig1 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM acall refresh ; refresh display mov A, #150 ; blink display acall delay_ms ; delay 150 msec jb advance, minskip ; is pushbutton pressed? inc minutes ; yes - increment the minutes mov A, minutes ; cjne A, #10, minskip ; 10 minutes? mov minutes, #0 ; yes, zero minutes inc tenmins ; increment tenmins mov A, tenmins ; cjne A, #6, minskip ; 6 tenmins? mov tenmins, #0 ; yes, zero tenmins minskip: jnb minset, incmins ; test switch ret ; Increment the hours digits while the pushbutton is pressed. ; *********************************************************************** inchours: mov A, hours ; hours digit mov PTR_R1, #disdig2 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM mov A, tenhours ; ten hours digit mov PTR_R1, #disdig1 ; point to ram buffer acall decode ; decode & write the LED patterns to RAM acall refresh ; refresh display mov A, #150 ; blink display acall delay_ms ; delay 150 msec jb advance, hrskip ; is pushbutton pressed? inc hours ; increment hours inc twelvehr ; increment twelvehr mov A, hours ; cjne A, #10, hrsroll ; 10 hours? mov hours, #0 ; yes, zero hours mov tenhours, #1 ; yes, set tenhours to 1 hrsroll: mov A, twelvehr ; cjne A, #13, hrskip ; 13 twelvehr? mov twelvehr, #1 ; yes, reset twelvehr mov hours, #1 ; yes, set 1 o'clock mov tenhours, #99 ; yes, blank tenhours hrskip: jnb hrset, inchours ; test switch ret ; External interrupt routine - happens every 16.67 msec (60 Hz). ; *********************************************************************** ac_intr: push PSW ; save current registers push ACC ; save accumulator djnz ac60hz, ac_retn ; decrement interrupt counter mov ac60hz, #ACFREQ ; reset counter after 60 interrupts inc seconds ; increment seconds mov A, seconds ; cjne A, #10, ac_retn ; 10 seconds? mov seconds, #0 ; yes, zero seconds inc tensecs ; increment tensecs mov A, tensecs ; cjne A, #6, ac_retn ; 6 tensecs? mov tensecs, #0 ; yes, zero tensecs inc minutes ; increment minutes mov A, minutes ; cjne A, #10, ac_retn ; 10 minutes? mov minutes, #0 ; yes, zero minutes inc tenmins ; increment tenmins mov A, tenmins ; cjne A, #6, ac_retn ; 6 tenmins? mov tenmins, #0 ; yes, zero tenmins inc hours ; increment hours inc twelvehr ; increment twelvehr mov A, hours ; cjne A, #10, rollovr ; 10 hours? mov hours, #0 ; yes, zero hours mov tenhours, #1 ; yes, set tenhours to 1 ajmp ac_retn rollovr: mov A, twelvehr ; cjne A, #13, ac_retn ; 13 twelvehr? mov twelvehr, #1 ; yes, reset twelvehr mov hours, #1 ; yes, set 1 o'clock mov tenhours, #99 ; yes, blank tenhours ac_retn: pop ACC ; restore accumulator pop PSW ; restore program status word clr IE0 ; clear interrupt flag setb EA ; re-enable interrupts reti ; return from interrupt ; Convert time digit to dot pattern & store in the ram buffer. ; A = TIMEDIGIT ; PTR_R1 = RAM BUFFER LOCATION ; *********************************************************************** decode: cjne A, #00, tst1 ; 0? mov dptr, #X0 ; point to table for 0 ajmp wrchar ; write the LED patterns to RAM tst1: cjne A, #01, tst2 ; 1? mov dptr, #X1 ; point to table for 1 ajmp wrchar ; write the LED patterns to RAM tst2: cjne A, #02, tst3 ; 2? mov dptr, #X2 ; point to table for 2 ajmp wrchar ; write the LED patterns to RAM tst3: cjne A, #03, tst4 ; 3? mov dptr, #X3 ; point to table for 3 ajmp wrchar ; write the LED patterns to RAM tst4: cjne A, #04, tst5 ; 4? mov dptr, #X4 ; point to table for 4 ajmp wrchar ; write the LED patterns to RAM tst5: cjne A, #05, tst6 ; 5? mov dptr, #X5 ; point to table for 5 ajmp wrchar ; write the LED patterns to RAM tst6: cjne A, #06, tst7 ; 6? mov dptr, #X6 ; point to table for 6 ajmp wrchar ; write the LED patterns to RAM tst7: cjne A, #07, tst8 ; 7? mov dptr, #X7 ; point to table for 7 ajmp wrchar ; write the LED patterns to RAM tst8: cjne A, #08, tst9 ; 8? mov dptr, #X8 ; point to table for 8 ajmp wrchar ; write the LED patterns to RAM tst9: cjne A, #09, blank ; 9? mov dptr, #X9 ; point to table for 9 ajmp wrchar ; write the LED patterns to RAM blank: cjne A, #99, wrdone ; use 99 to display blank digit mov dptr, #XBLANK ; point to table for blank ajmp wrchar ; write the LED patterns to RAM wrchar: mov B, #5 ; 5 dot patterns columns per digit mov index, #0 ; index to first dot pattern column wrloop: mov A, index ; use column index into pattern table movc A, @A+dptr ; get LED pattern from table mov @PTR_R1, A ; write LED pattern to display buffer dec PTR_R1 ; advance to next buffer location inc index ; advance to next dot pattern column djnz B, wrloop ; process next column wrdone: ; done with this character ret ; *********************************************************************** ; UTILITY Routines ; *********************************************************************** ; Even though the RAM has been zeroed, the buffer data must be inverted ; to turn the LEDS off. ; *********************************************************************** dispclr: ; clear display & hidden buffer mov A, #07FH ; inverted data to turn LEDs off mov PTR_R0, #disdig1 mov PTR_R1, #ramsize clrloop: mov @PTR_R0, A dec PTR_R0 djnz PTR_R1, clrloop ret ; Delay for approximately one millisecond times the value in A. ; All registers preserved, including flags. ; *********************************************************************** delay_ms: push PSW ; save program status word push ACC ; save accumulator contents push B ; save register B contents dly_loop: mov B, #204 djnz B, $ ; 445 uS @ 11.0592 MHz djnz B, $ ; 555 uS @ 11.0592 MHz djnz ACC, dly_loop ; Repeat # of times in A pop B ; restore register B contents pop ACC ; restore accumulator contents pop PSW ; restore program status word ret ; *********************************************************************** end