;IN1 Nixie Clock - Simple 4-digit clock with 50/60 Hz reference, crystal backup ;ATtiny214 @ 2 MHz (internal osc.) + 32 KiHz backup crystal ;Fuses: FUSE[5] = 0xC0 (PA0 = GPIO) ; ;PORTS ; PA0 = Push button (active low, internal pull-up) / [UPDI] ; PA1 = Segment shift register data [TXD*] ; PA2 = Min10 2^0 ; PA3 = Segment shift register clock [XCK*] ; PA4 = Min10 2^1 (PA4 & PA5 both high ; PA5 = Min10 2^2 for Min10 blanking) ; PA6 = Hour10 2^0 (PA6 & PA7 both high ; PA7 = Hour10 2^1 for Hour10 blanking) ; PB0 = mains frequency input (50 Hz or 60 Hz) ; PB1 = Nixie SMPS enable (active high) / Shift register clear (active low) ; PB2-3 = [32 KiHz XTAL] ; ;SHIFT REGISTER ; Q1 (shifted last) = Hour1 2^0 ; Q2 = Hour1 2^1 ; Q3 = Hour1 2^2 ; Q4 = Hour1 2^3 ; Q5 = Min1 2^0 ; Q6 = Min1 2^1 ; Q7 = Min1 2^2 ; Q8 (shifted first) = Min1 2^3 ; ;VERSIONS ; 2024-08-11 Arne Rossius ; * first version (DG75 clock) ; 2025-02-22 Arne Rossius ; * adapted for IN1 nixie clock .include "../include/tn214def.inc" .undef XL .undef XH .undef YL .undef YH ;R1:R0 = multiplication result (used in interrupt) .def smps_seq = R2 .def ac_restore_cnt = R3 .def last_ac_l = R4 .def last_ac_h = R5 .def zero = R6 .def mux = R7 .def intdiv = R8 .def sreg_backup = R9 .def min_disp = R10 .def hour_disp = R11 .def min_prev = R12 .def hour_prev = R13 .def set_timeoutL = R14 .def set_timeoutH = R15 .def temp = R16 .def temp2 = R17 .def count = R18 .def subsec = R19 .def second = R20 .def minute = R21 .def hour = R22 .def button_cnt = R23 .def inttmp = R24 .def inttmp2 = R25 .def timeout = R26 .def set_digit = R27 .def flash = R28 .def flags = R29 .equ fButtonShort = 0 .equ fButtonLong = 1 .equ fTimeSet = 2 .equ fAcState = 3 .equ fAcFailDetected = 4 .equ fAcEdgesDetected = 5 .equ fAcRestored = 6 .equ fDST = 7 ;R31:R30 used for Z pointer ;user settings .equ F_MAINS = 50 ;mains frequency (time base): 50 Hz or 60 Hz .equ ZERO_BLANKING = 1 ;blank 1st digit zero (0 = no, 1 = yes) .equ FLASH_SPEED = 100 ;Display flashing period (10 ms units), max. 255 .equ BUTTON_SHORT_TIME = 2 ;Short button press (10 ms units), max. 255 .equ BUTTON_LONG_TIME = 100 ;Long button press (10 ms units), max. 255 .equ SET_TIMEOUT = 5*60*100 ;Timeout for set mode (10 ms units), max. 65535 ;other settings .equ F_CPU = 2000000 ;20 MHz / 10 = 2 MHz .equ F_IRQ = 2000 ;interrupt frequency = 2 kHz ;debug options .equ CRYSTAL_CHECK = 0 ;output F_XTAL / 64 on PA2 ;PORTA .equ BUTTON = 0 .equ SR_D = 1 .equ M10_A = 2 .equ SR_CK = 3 .equ M10_B = 4 .equ M10_C = 5 .equ H10_A = 6 .equ H10_B = 7 ;PORTB .equ AC_IN = 0 .equ SMPS_EN = 1 ;=============================================================================== .macro shift_clock sbi VPORTA_OUT, SR_CK cbi VPORTA_OUT, SR_CK .endmacro .macro shift_bit sbrc @0, @1 sbi VPORTA_OUT, SR_D sbrs @0, @1 cbi VPORTA_OUT, SR_D shift_clock .endmacro ;=============================================================================== .cseg .org 0x000 rjmp reset .org PORTB_PORT_vect ;input edge interrupt (mains frequency input) rjmp port_b .org RTC_CNT_vect ;RTC interrupt: AC fail detection & backup clock rjmp rtc .org TCA0_OVF_vect ;TCA0 overflow (display update, button polling, timeouts) rjmp ovf_a ;-------------------- port_b: in sreg_backup, CPU_SREG sbrs flags, fAcFailDetected rjmp port_b_end sbis VPORTB_IN, AC_IN rjmp port_b_end ;AC fail mode, rising edge port_b_cnt: lds inttmp, RTC_STATUS ;wait until CNT register has been updated sbrc inttmp, RTC_CNTBUSY_bp rjmp port_b_cnt movw inttmp2:inttmp, last_ac_h:last_ac_l lds last_ac_l, RTC_CNTL lds last_ac_h, RTC_CNTH ;out16 com inttmp2 ;subtract previous value from new value neg inttmp brne PC+2 inc inttmp2 add inttmp, last_ac_l adc inttmp2, last_ac_h tst inttmp2 brne port_b_fail ;way too much time between edges cpi inttmp, 32768/8 * 5/4 / F_MAINS ;1.25 * T_AC brsh port_b_fail ;too much time between edges cpi inttmp, 32768/8 * 3/4 / F_MAINS ;0.75 * T_AC brlo port_b_fail ;not enough time between edges ;valid time between AC edges sbrs ac_restore_cnt, 4 ;stop counting at 16 inc ac_restore_cnt sbrc ac_restore_cnt, 4 ;consider AC restored after 16 edges ori flags, 1< set fAcState=1 andi flags, ~(1<= 32? rjmp enable_smps ovf_a_end: out CPU_SREG, sreg_backup reti enable_smps: ;set all nixies to blank and turn on SMPS sbic VPORTB_OUT, SMPS_EN rjmp ovf_a_end ;already enabled ldi inttmp, 0xF4 | 1< summer: increment hour ;summer -> winter: decrement hour subi hour, 1 brcs dst_h23 ;hour wrapped around from 0 to 23 brhc PC+2 subi hour, 6 ret dst_h23: ldi hour, 0x23 ret adj_h1: subi hour, -7 brhc PC+2 subi hour, 6 cpi hour, 0x24 brlo PC+2 clr hour rjmp restart_set_timeout adj_m10: clr subsec clr second mov temp, minute andi temp, 0xF0 subi temp, -0x10 cpi temp, 0x60 brlo PC+2 clr temp andi minute, 0x0F or minute, temp rjmp restart_set_timeout adj_m1: clr subsec clr second mov temp, minute andi temp, 0x0F inc temp cpi temp, 0x0A brlo PC+2 clr temp andi minute, 0xF0 or minute, temp rjmp restart_set_timeout ;-------------------- button_long: ;long button press andi flags, ~(1<