title "Nixie Clock with latched 8-bit display" #define Debug ; ; This Application uses TMR0 to set a "seconds" flag in the interrupt ; handler. The context saving is handled correctly, finally. And the ; mainline code checks if the seconds flag is set. If so, it calls the ; Digit_A chain of subroutines to increment the seconds and display. ; Otherwise, it simply continues in the loop, looking for pressed buttons. ; Unfortunately, in 2.0, the button debouncing routine sucks. And it is ; possible to screw something up. So, I may make the button push call the ; Digit_A routine. That would work, but not the way I want it to. I really ; want it to just add minutes separate from hours, so that if I hit the ; minutes 59 times, it doesn't roll over hours. This may just be a nit-pick ; that I decide to live with anyway. We'll see. Next version will have ; proper button debouncing (or at least non-interrupt based button ; debouncing). ; ; Hardware Notes: ; This application runs on a 16F84A executing at 4 MHz ; _MCLR is tied through a 10K Resistor to Vcc and PWRT is Enabled ; Port B is hooked to a bus 8 bits wide with 3 '573s on it ; the latch signal is controlled by port A ; I have set port A to have <4,3> be minutes and hours ; advance, and <2,1,0> be the '573 latch signals ; Good old port B is the 8-bit data bus. ; ; Nixie tube clock Version 2.0 ; copyright ; Fred Niell ; 07/07/04 ; ; MPLAB crap here ; absolute listing tabs=5, lines=97, trim long lines=ON LIST R=DEC, P=16F84A INCLUDE "p16f84a.inc" ; Registers CBLOCK 0x020 _w, _status ; Context Register Save Values digitA ; digitB ; digitC ; digitD ; digitE ; digitF ; scratch; keys; PMregister; temp_w; second_flag; bres_hi; bres_mid; bres_lo; ;adopted from the roman black 1-second routine ENDC PAGE __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC ; Mainline of Timer org 0 movlw 2 ; Setup the Count goto Mainline org 4 Int ; Interrupt Handler movwf _w ; Save Context Registers movf STATUS, w ; - Assume TMR0 is the only enabled Interrupt movwf _status ;Roman Black's 1-sec interrupter tstf bres_mid; test if mid = 0 skpnz ;nz = no underflow needed decf bres_hi,f; z, no underflow so dec msb decfsz bres_mid,f; dec mid byte = subtract 256 goto IntEnd; tstf bres_hi; test hi byte skpz ; z = high and mid one second! goto IntEnd; ;from here to IntEnd is code for when you reach 1 second movlw 0x0F movwf bres_hi movlw 0x42 movwf bres_mid movlw 0x40 addwf bres_lo,f skpnc ;no overflow incf bres_mid,f ;add 256 (absorbing error!) movlw 0x01; movwf second_flag; IntEnd bcf INTCON, T0IF ; Reset the Interrupt Flag bcf STATUS, RP0 movf _status, w ; Restore the Context Registers movwf STATUS swapf _w, f swapf _w, w retfie ;return from interrupt Digit_A incf digitA, f movlw 0xA subwf digitA, w btfss STATUS, C ;A overflow? goto Display Digit_B movlw 0x00 movwf digitA ;clear A incf digitB, f movlw 0x6 subwf digitB, w btfss STATUS, C goto Display Clear_seconds movlw 0x00 ; movwf digitA ; movwf digitB ; Digit_C incf digitC, f movlw 0xA subwf digitC, w btfss STATUS, C goto Display Digit_D movlw 0x00 movwf digitC incf digitD, f movlw 0x6 subwf digitD, w btfss STATUS, C goto Display Clear_minutes movlw 0x00 movwf digitC movwf digitD; Digit_E incf digitE, f swapf digitF, w iorwf digitE, w movwf PMregister; movlw 0x13 subwf PMregister, w btfsc STATUS, C goto Clear_hours movlw 0xA subwf digitE, w btfss STATUS, C goto Display Digit_F movlw 0x00 movwf digitE incf digitF, f goto Display Clear_hours movlw 0x01; movwf digitE; movlw 0x00; movwf digitF; Display swapf digitB, w ;seconds first iorwf digitA, w ;load a into w movwf PORTB ;swap nibbles bsf PORTA,0 ;or with b movlw 0x02 ;put on port call Delay ;enable latch bit 0 bcf PORTA,0 ;delay swapf digitD, w ;minutes next iorwf digitC, w movwf PORTB bsf PORTA,1 ;latch bit 1 movlw 0x02 call Delay bcf PORTA,1 swapf digitF, w ;hours next iorwf digitE, w movwf PORTB bsf PORTA,2 ;latch bit 1 movlw 0x02 call Delay bcf PORTA,2 movlw 0xFF movwf PORTB ;tristates the bus movlw 0x00 movwf PORTA movlw 0x00 movwf second_flag; return Delay ;delays by amount in w movwf temp_w ; subroutine, not goto! Delay_loop decfsz temp_w,f goto Delay_loop return Mainline ; Setup the PWM And then ;Monitor it, Updating "PWMOn" movlw 0x00 movwf second_flag; movlw 0x08 ; all digits are 0 movwf digitA movlw 0x05 movwf digitB movlw 0x09 ; movwf digitC movlw 0x05 movwf digitD movlw 0x02 ; Except the hours movwf digitE movlw 0x01 movwf digitF ; So it starts at midnight movlw 0x0F movwf bres_hi movlw 0x42 +1 movwf bres_mid movlw 0x40 movwf bres_lo ;loads 1 million plus 1 256 into ;the 24 bit register movlw 0x00 movwf PMregister movlw 0x00 movwf temp_w bcf STATUS, RP1 bsf STATUS, RP0 ; Goto Bank 1 to set Port Direction movlw 0x00; movwf TRISB ; PORTB is Output movlw 0x18; movwf TRISA ; PORTA is output too movlw 0x0C0 ; Debug - Minimum Timer Delay movwf OPTION_REG bcf STATUS, RP0 ; Go back to Bank 0 clrf TMR0 ; Start the Timer from Scratch ;movlw 0xC8 ;movwf OPTION_REG movlw 0x00; movwf PORTA; movlw 0xFF; movwf PORTB; movlw b'10100000' ;movlw (1 << GIE) + (1 << T0IE) movwf INTCON ; Enable Interrupts Loop ; Loop Here ; if button 1 and 2 are on for 1 second, then ; blink the display and show the time ; remember that the 74141 is display blanking ; for invalid BCD codes. Button 1 increments ; hours, button 2 increments seconds ; if none is selected for 5 seconds it goes back to ; displaying the time. Use WDT for this one. Just ; remember to check that the context register for option ; and status go back the way they should. btfsc second_flag, 0; second flag set call Digit_A; movf PORTA, w xorwf keys, w andlw 0x18 ;only check the button pins btfsc STATUS,Z ;test, if Z=0, no buttons goto Loop ;no, loop again xorwf keys,f Debounce movlw 0x80 call Delay movf PORTA, w movwf keys btfss keys, 3 ;minutes down goto add_minutes ; btfss keys, 4 ;hours down goto add_hours goto Loop ; Let the Interrupt Handler Work in the Background add_hours incf digitE, f swapf digitF, w iorwf digitE, w movwf PMregister; movlw 0x13 subwf PMregister, w btfsc STATUS, C goto reset_hours movlw 0xA subwf digitE, w btfss STATUS, C goto Loop movlw 0x00 movwf digitE incf digitF, f goto Loop reset_hours movlw 0x01; movwf digitE; movlw 0x00; movwf digitF; goto Loop add_minutes incf digitC, f movlw 0xA subwf digitC, w btfss STATUS, C goto Loop movlw 0x00 movwf digitC incf digitD, f movlw 0x6 subwf digitD, w btfss STATUS, C goto Loop movlw 0x00 movwf digitC movwf digitD; goto Loop end