;*************************************************************************** ;* Title: AVR mega48 based nixie clock 1.0 ;* Commercial use forbidden without license from author. ;* (c) copyright 2005 Henry Carl Ott N2RVQ, All Rights Reserved. ;* Questions, comments or offers of cash? carlott@rcn.net http://users.rcn.com/carlott/ ;*************************************************************************** ;* Description: ;* A single chip 4 digit nixie clock. ;* PWM used for HV generation. RTC function maintained in power down mode. ;* see seperate schematic for circuit details. ;*************************************************************************** ;* Revision List: ;* 1.0 Initial release ; not optimized, some routine stubbs ;*************************************************************************** ;* Fuses: ;* internal 8mhz osc .nolist .include "m48def.inc" .list .listmac ;*************************************************************************** ;* ;* MACROS ;* ;*************************************************************************** ;--- load z pointer with arg .macro _init_z ldi zh,high(@0) ldi zl,low(@0) .endm ;--- load y pointer with arg .macro _init_y ldi yh,high(@0) ldi yl,low(@0) .endm .macro _init_x ldi xh,high(@0) ldi xl,low(@0) .endm ;--- load eeaddr hi/lo with arg .macro _init_ee ldi xl,low(@0) ldi xh,high(@0) .endm ;--- load z pointer with arg*2 for flash stored data (convert byte to word pointer) .macro _init_zflash ldi zh,high(@0 * 2) ldi zl,low(@0 * 2) ;Init Z-pointer .endm ;--- load x pointer with arg*2 for flash stored data (convert byte to word pointer) .macro _init_xflash ldi xh,high(@0 * 2) ldi xl,low(@0 * 2) ;Init x-pointer .endm ;--- skip next instruction .macro skip rjmp (pc + 2) ; skip, next instruction .endm ;------delay two cycles in one instruction .macro _nop_2 rjmp (pc + 1) .endm ;************************************************************************** ; ATmega48 pin out ; ; ;************************************************************************** ;--- port and pin defines ;PORTB .equ K_7 = 0x00 ; .equ K_PM = 0x01 ; .equ K_4 = 0x02 ; .equ ANODE_HH = 0x03 ; .equ ANODE_H = 0x04 ; .equ K_6 = 0x05 ; .equ PB6_NC = 0x06 .equ PB7_NC = 0x07 ;PORTC .equ K_9 = 0x00 .equ ANODE_M = 0x01 .equ K_8 = 0x02 .equ HV_SENSE = 0x03 .equ K_0 = 0x04 .equ K_1 = 0x05 .equ PC6_NC = 0x06 .equ PC7_NC = 0x07 ;PORTD .equ PB_PORT = PIND .equ K_2 = 0x00 .equ K_3 = 0x01 .equ ANODE_MM = 0x02 .equ PB_2 = 0x03 .equ PB_1 = 0x04 .equ PWM = 0x05 .equ K_5 = 0x06 .equ LV_SENSE = 0x07 ;---- some program equates and constants .equ clock = 8000000 ; cpu clock .equ pwm_start_val = 0x37 ; starting pwm value .equ pwm_max_val = 0xf0 ; max pwm val .equ hv_reg_val = 8 ; hv adc regulation point .equ blank = 11 ; value to blank display .equ secs_display= 8 ; number of seconds to display seconds when pb_2 is pressed .equ fade_rate = 65 ; delay of tics for cross fade .equ blink_bit = 5 ; bit mask used to blink digits .equ t1_reload = 0x1400 ; timer 1 reload values ;------------------------------------- .equ T0_DIV1 =$01 ; .equ T0_DIV8 =$02 ; .equ T0_DIV64 =$03 ; .equ T0_DIV256 =$04 ; .equ T0_DIV1024 =$05 ; .equ T1_DIV1 =$01 ; .equ T1_DIV8 =$02 ; .equ T1_DIV64 =$03 ; .equ T1_DIV256 =$04 ; .equ T1_DIV1024 =$05 ; .equ T2_OFF =$00 .equ T2_DIV1 =$01 ; .equ T2_DIV8 =$02 ; .equ T2_DIV32 =$03 .equ T2_DIV64 =$04 ; .equ T2_DIV128 =$05 .equ T2_DIV256 =$06 ; .equ T2_DIV1024 =$07 ; ;***************************** ;* Global Register Variables * ;***************************** .def tic = r02 .def xfade = r03 ; crossfade .def hv_target = r04 ; value to compare adc value to .def isr_save = r14 ; isr storage for status word .def zero = r15 ; always zero, handy for 16 bit math, .def temp = r16 ; gp working reg .def temp2 = r17 ; another gp reg .def ii = r19 .def sys_flags = r20 ; system bit flags, explained below .def clk_flags = r21 ; clock options, defined below ;************************** ;- system bit flags defined ;************************** .equ blink_hh = 0x00 ; set to blink tens of hours .equ blink_h = 0x01 .equ blink_mm = 0x02 .equ blink_m = 0x03 .equ colon_on = 0x05 ; set to light colon .equ pm_on = 0x06 ; set to light pm indicator .equ debug = 0x07 ; set debug mode ;------------------------- ;************************** ;- clock bit flags defined ;************************** .equ _12hr = 0x01 ; twelve hour display format .equ clk_halt = 0x02 ; set to halt rtc .equ sec = 0x04 ; bits set by rtc routine .equ min = 0x05 .equ hour = 0x06 .equ day = 0x07 .DSEG _seconds: .byte 1 _minutes: .byte 1 _hours: .byte 1 _day: .byte 1 _date: .byte 1 _month: .byte 1 _year: .byte 1 _count_a: .byte 1 ; vertical counter storage for next sample _count_b: .byte 1 _debounced: .byte 1 ; debounced status of inputs _pb: .byte 1 ; push button events, bits set by button press _disp_buff: .byte 4 ; storage for display buffer hh:mm _disp_buffz: .byte 4 ; zbuffer for display cross fading ;------------------------- ;********************* ;* Interrupt Vectors * ;********************* .CSEG .org 0x00 rjmp reset ; Reset Handle .org INT0addr ; external int 0 ; 1hz int from rtc chip rjmp reset .org INT1addr ; external int 1 rjmp reset .org PCI0addr rjmp reset .org PCI1addr rjmp reset .org PCI2addr rjmp reset .org OVF2addr ; Overflow2 Interrupt Vector Address rjmp t2_isr .org oc1aaddr rjmp t1_isr .org OVF1addr ; Overflow1 Interrupt Vector Address rjmp reset .org OVF0addr rjmp reset .org ADCCaddr rjmp adc_isr ;--- hits about 1500 hz, handle display mux, debounce push buttons t1_isr: in isr_save, sreg ; save status reg push temp push temp2 push zl push zh ; and some work registers inc tic mov temp,tic andi temp,0x3f ; mask how often to check adc brne tst_db ; not this time lds temp,ADCSRA ; start a conversion sbr temp,1< date ? brmi rtc_umonth ; no std z+4,temp2 ; save date ret rtc_umonth: ldi temp,1 std z+4,temp ; reset date ldd temp,z+5 ; get month inc temp cpi temp,13 breq rtc_uyear std z+5,temp ret rtc_uyear: ldi temp,1 ; roll over month to jan std z+5,temp ; save it ldd temp,z+6 ; get year inc temp std z+6,temp rtc_exit: ret ;--- return leap year corrected days in month ; return in temp, trash temp2 days_in_month: ldd temp,z+5 ; get month dec temp ; 1-12 to 0-11 push zl ; save our time pointer push zh _init_zflash _month_table add zl,temp ; adjust pointer adc zh,zero lpm temp,z ; get month data pop zh ; restore pointer pop zl cpi temp,28 ; feb? breq test_leap ret ; no, just return test_leap: ldd temp2,z+6 ; get year andi temp2,3 ; see if low two bits are zero (divide by 4) brne pc + 2 ; no, not a leap year inc temp ; 29 days in feb in a leap year days_in_m_end: ret _month_table: .db 31,28,31,30,31,30,31,31,30,31,30,31 ;--- disp temp 0-255 ;--- trash temp,temp2,ii disp_bin_hss: _init_y _disp_buff ldi temp2,blank st y+,temp2 ldi temp2,100 ; hunds rcall sub_it ldi temp2,10 ; tens rcall sub_it mov ii,temp rjmp sub_it_a ; ones sub_it: clr ii ; start at zero sub_it_lp: cp temp,temp2 ; see if we can sub without a carry brlo sub_it_a ; no, done with this loop sub temp,temp2 inc ii ; bump loop counter (in ascii) rjmp sub_it_lp ; loop again sub_it_a: st y+,ii ; save it sub_it_end: ret ;--- display temp; on ss, blank hh disp_bin_ss: _init_y _disp_buff push temp ; save temp ldi temp,blank ; blank hh st y+,temp st y+,temp pop temp ;--- display temp as two bcd digits @ y disp_bin: rcall bin2bcd ; convert temp st y+,temp2 ; temp2 has msd andi temp,0x0f ; mask off any unwanted bits st y+,temp ; save it ret ;--- convert temp into correct output port and pin disp_nixie: cpi temp,10 brsh disp_nix_end ; display nothing _init_zflash _disp_table lsl temp ; mul x2, table is in words add zl,temp ; set pointer adc zh,zero ; handle carry lpm temp,z+ ; get port addr lpm temp2,z ; get bit mask clr zh ; mov zl,temp ; point to port ld temp,z ; get current port value or temp,temp2 ; set bit st z,temp ; back out to pins disp_nix_end: ret ;-- table contais port address and bit mask for each anode _disp_table: .db PORTC + 0x20,1<