;:ts=8 ; ; EXAMPLE PROGRAM #1 ; ; This program counts a TIL311 hex display through all 16 digits at one ; digit per second. Each digit is faded in gradually. The timer ; interrupt system is used as a software pulse width modulator generating ; 100 pulses/sec with a duty cycle variable in 36 steps. This can be ; done with reasonable overhead (i.e. not too much time is spent in the ; interrupt handler). ; ; The hex display's latch control must be connected to the $B000 write ; decode and its data inputs to bits 0-3 of the I/O data bus. ; ; Internal memory allocation ; 00-07: Register bank 0 -- used for non-interrupt code ; 08-0F: Register bank 1 -- used for interrupt code ; 10-17: Register bank 2 \ ; 18-1F: Register bank 3 > misc. variables go here ; 20-2F: Bit addressable space / ; 30-7F: Generic memory -- used for stack .equ ireg0, 8 .org 0x10 tickcnt1: .skip 1 tickcnt2: .skip 1 tickcnt3: .skip 1 brightlevel: .skip 1 digitcount: .skip 1 .org 0x2100 ljmp tick_isr .org 0x2800 ; CPU initialization ; ------------------ ; Initialize CPU registers. start: mov SP,#0x2f ; Stack at 30H mov PSW,#0 ; Registers at 0H mov TCON,#0 ; Disable timers mov IE,#0 ; Disable all interrupts mov IP,#0 mov PCON,#0 ; Initialize other registers. mov P1,#0xff mov P3,#0xff ; Interrupt and serial port initialization ; ---------------------------------------- ; Set both timers to free running, 8 bit auto reload. mov TMOD,#0x22 ; Set up timer 0 to generate tick interrupts at the ; slowest possible rate (3600/sec). mov TH0,#0 setb IP.1 setb IE.1 setb IE.7 setb TCON.4 mov tickcnt1,#1 mov tickcnt2,#0 mov tickcnt3,#99 mov digitcount,#0x0f ; That's it. Loop forever while the interrupt ; routine does its thing. idle: sjmp idle ;----------------------------------------------------------- ; Interrupt service routine. This is entered 3600 times per ; second, so speed is important. ; ; tickcnt1 counts from 1-36 down to 0. tick_isr: djnz tickcnt1,isr_exit ; If tickcnt2 is set then turn the LED off and reload ; tickcnt1 with that value. push PSW push ACC mov a,tickcnt2 jz tcdone mov tickcnt1,a mov tickcnt2,#0 setb P1.2 pop ACC pop PSW sjmp isr_exit ; Both tickcnt1 and tickcnt2 have run down. Reload them. tcdone: mov a,brightlevel ; 0 to 36 clr C jnz noton mov a,#36 setb C noton: mov P1.2,C mov tickcnt1,a ; 1 to 36 mov a,#36 clr C subb a,tickcnt1 ; 0 to 35 mov tickcnt2,a ; Interrupts occur every 256 instruction cyles. While ; the preceding code was running interrupts were masked. ; It's high time to get them unmasked so we don't miss ; the next interrupt. do this by executing a "reti" ; instruction but returning to our 1/100 second interrupt ; handler instead of the real interrupted program. push DPL push DPH mov dptr,#scan_entry push DPL push DPH isr_exit: reti ; This is executed 100 times per second. Select register ; bank 1 (bank 0 will be restored by the "pop psw" at exit). scan_entry: mov PSW,#08h ; tickcnt3 counts from 0 to 99. Compute the intensity ; value for the LED from that. push B mov B,#5 mov a,tickcnt3 div ab pop B mov dptr,#brighttbl movc a,@a+dptr mov brightlevel,a ; Increment tickcnt3. If it goes over 99, go to the ; once-per-second interrupt stuff. inc tickcnt3 mov a,#99 clr c subb a,tickcnt3 jc do1sec int_exit: pop DPH pop DPL pop ACC pop PSW ret ; Table of brightness levels. brighttbl: .byte 0,0,1,1,2,2,3,3,4,5,7,9,11,14,18,22,28,36,36,36 ; The following is executed once per second. First, reload ; the counter. do1sec: mov tickcnt3,#0 mov brightlevel,#0 setb P1.2 ; Increment the digit and display it. inc digitcount mov a,digitcount mov dptr,#0xB000 movx @dptr,a sjmp int_exit