;
;    ****************************
;    *  80m Foxoring TX 'Fixle' *
;    ****************************
;
; Code für Nicks Foxoring-Sender-Steuerung, läuft auf ATtiny25
;
; Created  9. 7.26  Reuses code from several old projects 
;         13. 7.26  All functions coded and basically running
;	  14. 7.26  Key pressed resets LED OFF
;	  17. 7.26  Dot Length in R18
;	  20. 7.26  'International' Mode
;	  22. 7.26  Key handling Clean-Ups
;         29. 7.26  Turn A/D off after use
;	  31. 7.26  TX stays off during battery voltage indication
;
;					 
;***** Function Overview
; Power-Up Sequence
; - Turn on TX and LED
; - After 2 s measure Ubatt
; - Turn both off, send Morse Code as follows 3x, depending on battery voltage
;         >2,7 V     'O' ---  ---  ---
;         2,55-2,7 V 'M' --  --  --
;         2,3-2,55 V 'T' -  -  -
;         <2,3 V     Flicker .........
; - Get identity from EEPROM (Initially 'A')
; Main Loop
; - Send Ident
; - If Key pressed: Increment Ident
; - After 1 Minute: LED Off to save battery
;
; If Keypressed at Poweron: Switch National/International Mode 
;   National: A..Z, 0..9, all 50 BpM (36 in total)
;   International: MO..MO5 50 BpM, MO..MO5 75 BpM, (12 in total)
;
;***** I/O-Pin Assignment Program / Operate
;  PB0: MOSI / Unused
;  PB1: MISO / Key to Gnd 'Select Ident'
;  PB2: SCK  / Measurment Ubatt/5 (Voltagedivider 39k/10k)
;  PB3: -    / Open Drain -TX On
;  PB4: -    / Open Drain -LED On  
;
;***** Clock-Frequency is 128 kHz (Requires low-speed ISP!)
;
;***** Device + Fuses
.device ATtiny25 	
;
; Fuses ATtiny25 are programmed as follows:
;	Check only SPIEN and EESAVE
;	BODLEVEL: Brown-out detection at VCC=1,8V
; 	SUT_CKSEL = 'WD Osc. 128 kHz', bottom option = longest Startup
;	Fuse-Pattern X-H-L FF-D6-E4, reads back as E4-D6-FF  
;
;***** I/O Register Definitions
.equ    SREG    =$3f
.equ    TIMSK   =$39
.equ    TCCR0   =$33
.equ    TCNT0   =$32
.equ    EEARH   =$1f 	
.equ    EEAR    =$1e
.equ    EEDR    =$1d
.equ    EECR    =$1c
.equ    PORTB   =$18
.equ    DDRB    =$17
.equ    PINB    =$16
.equ	ADMUX	=$07
.equ	ADCSRA	=$06
.equ	ADCH	=$05
.equ	ADCL	=$04
;
.equ	RAMBEG	=$60
.equ	RAMEND	=$DF    ; RAM-size 128 B (used for Stack and some Variables)
.equ    E2END   =$7F    ; Last EEPROM Location, 128 B
.equ    FLASHEND=$3FF   ; Last FLASH Location, 2 kB
;
;**** Register Useage 
;		  R0	  used by LPM instruction
.DEF	STRTUP	= R1	; 1 until Battery Test complete
.DEF	WRK	= R16	; General work register
.DEF	WRK2	= R17	; "
.DEF	DOTLEN	= R18	; Dot length in msec, 100 for 60 BPM, DOTLEN = 6000/BPM
.DEF	LEDOFF	= R19	; 1 = LED remains dark (turned on 1 min after Power-On) 
.DEF	VOLTS	= R20	; Battery Voltage, 255 = 4,73 V, 18 mV per step
.DEF	DEBNCE	= R21	; Debounce Counter for Key, if pressed count to 64 msec
			;  .. on 64 MAIN handles key event and resets 
.DEF	WRKM	= R22   ; Used by MORSE
.DEF	IDENT	= R23  	; Identity, 0..35 for A..Z, 0..9 / 128..139 for MO..MOE slow/fast
.DEF	DMS	= R24	; Used by Delay Routines
.DEF	DDOT	= R25	; "
.def	XL	= R26	; Timer for LED Off after 64 s = 64.0000 ms = $FF00
.def	XH	= R27	; "
.def	YL	= R28	;
.def	YH	= R29	;
.def    ZL      = R30	; Used by LPM and as temp register
.def    ZH      = R31	; "
;
;**** RAM Useage
	.DSEG
	.ORG	RAMBEG
	; RAM used as Cache only!
	; Stack Pointer intialized to RAMEND by hardware 
	;
;**** Macros
	; ++++ Delay @0 ms
        .MACRO DXMS
        ldi     DMS,@0	
	call	DELMS
        .ENDMACRO
	;
	; ++++ Delay @0 dot length (120 ms @ 50 Bpm)
	.MACRO DXDOT
        ldi     DDOT,@0	
	call	DELDOT
        .ENDMACRO
	;
	; ++++ Turn on TX and LED
	.MACRO TXON
	sbrs	LEDOFF,0	; LED stays dark after 1 minute
 	sbi	DDRB,3
	sbrs	STRTUP,0	; No TX during Battery Status Indication
	sbi	DDRB,4	 
        .ENDMACRO
	;
	; ++++ Turn off TX and LED
	.MACRO TXOFF
 	cbi	DDRB,3
	cbi	DDRB,4	 
        .ENDMACRO
	;
	;
	;**** Code starts here *****************************************************
	;
	.CSEG
        .ORG 0
        ;
	;++++ Interrupt Vectors
        RJMP    START    ;  0 Reset   
        ;
	;**** Power-On Sequence *********************************************************
	;
START:	;
	;
 	; ++++ Initialize Port B Bit 4 as O.D. Output -LED On
	;		 	 Bit 3 as O.D. Output -TX On
	;			 Bit 2 as Analog Input UBat
	;			 Bit 1 as Input w. Pull-Up Key
	;			 Bit 0 as Input w Pull-Up Unused
	ldi     WRK,$00
        out     DDRB,WRK
        ldi	WRK,$03	
        out	PORTB,WRK
        ;
	ldi	DEBNCE,0  	; Debounce Counter start value
	;
	clr	XH		; Reset LED Timer
	clr 	LEDOFF		; Reset LED Off Flag
	;
	ldi	DOTLEN,120	; Set Dotlength to 120 msec = 50 BPM
	;
	clr	STRTUP		; Start-Up Flag
	inc	STRTUP		; .. 1 until Bat Test complete
	;
	rcall	GETEEP		; Read IDENT from EEPROM
	cpi	IDENT,$FF	; If EEPROM empty
	brne	STA2
	clr	IDENT		; Set ident to 'A'
	;
	; ++++ Battery test
STA2:	;
 	sbi	DDRB,3		; LED On
	sbi	DDRB,4	 	; TX On
	DXDOT	16		; Wait 2 Seconds
	sbis	PINB,1		; Then, if Key pressed
	rcall	TOGIDE		; .. toggle ident (National/International)
	rcall	ANACON		; Measure Battery voltage
	TXOFF			; Turn TX off
	DXDOT	3		; Wait a little
	rcall	BATSTA		; Morse Battery-Status 3 Times
	DXDOT 	7
	;
	clr 	STRTUP		; Activate Key Debouncing
	clr	DEBNCE
	;
	;
	;**** Main Loop ****************************************************
	;
	; ++++ Send CW IDENT
MAIN:	rcall	CWID		; Send designated ID
	;
	; ++++ Handle key, if pressed increment IDENT
M11:	sbrs	DEBNCE,6
	rjmp	MAIN2		; Key not pressed (long enough)
M12:	sbis	PINB,1		; Key pressed: Wait for key Off
	rjmp	M12
	clr 	DEBNCE
	DXDOT	3		; Wait a little before sending new ident
	;
	inc 	IDENT		; Increment Ident
	cpi	IDENT,36	; Wrap 36 to 0
	brne 	M13
	clr	IDENT
M13:	cpi	IDENT,140	; Wrap $80+12 to $80
 	brne	M14
	ldi	IDENT,128
M14:	rcall	PUTEEP		; Save new IDENT in EEPROM
MAIN2:	;
	;
MAIN9:	rjmp	MAIN		; Repeat forever
	;
	;
	;       ***********************************
	; ******* Subroutines                     ********************************** 
	;       ***********************************
	;
	;
	; **** Delay x msec, X in DMS 
	;      DMS * 40 * 3 Cycle / 128 kHz
	;      Also: every msec Debounce Key + LED Off Timer
	;
DELMS:	push 	WRK
	sbrc	DEBNCE,6	; IF Key pressed
	rjmp	D9MS		; .. hurry back to main  
	;
D0MS:	ldi	WRK,40
D1MS:	dec	WRK		; 3 cycle inner loop
	brne	D1MS
	rcall	DEB1MS		; Debounce called every 1 ms
	dec	DMS		 
	brne	D0MS
	;
D9MS:	pop	WRK
	ret
	;
	; 1 msec Debounce service
DEB1MS:	tst	STRTUP		; During Start-up
	brne	DEB6MS		; .. ignore key
	;
	sbis	PINB,1		; If Key not pressed
	rjmp	DEB5MS
	clr	DEBNCE		; .. clear Debounce Counter
	rjmp	DEB6MS
DEB5MS:	clr	LEDOFF		; Kex pressed: Reset LED Off Timer
	clr	XH
	sbrs	DEBNCE,6	; If Debounce Timer < 64 ms
	inc	DEBNCE		; .. increment
	; 
	; 1 msec LED Timer
DEB6MS:	adiw	XL,1		; 
	brne	DEB9MS		; On Overflow (64.000 ms)
	ldi 	LEDOFF,1	; .. turn off LED
	;
DEB9MS: ret
	;
	;
	; **** Delay x dot-length, X in DDOT 
DELDOT:	mov	DMS,DOTLEN	; Dotlength in msec, 120 = 50 Bpm
	rcall	DELMS
DELD3:	dec 	DDOT
	brne	DELDOT
	;
DELD9:	ret
	;
	;
	;**** Send Morsecode for # in WRK (0=A .. 35=9)
	; Morseformat MMMM MLLL
	; LLL = Zeichenlänge 1..5 Pu+Stri
	; MMMMM = 0:Punkt, 1:Strich, erstes Element in Bit 7
	;
MORSE:	push	WRK
	ldi	ZH,$07		; Pointer to morsecode table
	mov	ZL,WRK
	lpm			; Get Byte
	mov	WRK,R0  
	mov	WRKM,WRK	; BitZähler separieren
	andi	WRKM,$07
MO1:	rol	WRK		; Ein Element holen
	brcc	MO2	
	rcall	PIPON		; Strich bzw. Punkt ausgeben
	rcall	PIPON
MO2:	rcall	PIPON		; Danach ein Punkt Pause
	rcall	PIPOFF
	dec	WRKM		; Wenn BitZähler noch nicht auf 0
	brne	MO1		; .. nächstes Bit
M09:	rcall	PIPOF3		; Letzte Pause = Strich
	pop	WRK
	ret
	;
PIPON:	TXON			; 1 Dotlength on
	DXDOT	1
	ret
	;
PIPOFF:	TXOFF			; 1 Dotlength off
	DXDOT	1
	ret
	;	
PIPOF3:	TXOFF			; 3 Dotlengths off
	DXDOT	3
	ret
	;
	;
	; **** Measure Voltage on PB2 and store in VOLTS
	;      Reference is 1,1V, VOLTS = 255 @ 1,1 V
	; Uses
ANACON:	ldi	WRK,$83         ; Enable ADC, Clock Divider 8
        out     ADCSRA,WRK	;     
        ldi	WRK,$A1		; Input 1, Reference 1,1V, ADLAR = 1 
	out     ADMUX,WRK	; ADMUX einstellen
	;
	clr	WRK		; Wait a little for I/O to settle
AC1:	dec	WRK
	brne	AC1
	;
	sbi     ADCSRA,6        ; Start ADC
AC2:	sbis	ADCSRA,4	; Wait for ADC complete 
	rjmp	AC2
	sbi	ADCSRA,4	; Clear ADIF
 	in	VOLTS,ADCH	; Get Conversion Results
	;
	clr 	WRK		; Turn ADC off to save current
        out     ADCSRA,WRK	;     
AC3:	ret
	;
	;
	;**** Send Morsecode for Battery Status
	; For VOLTS >=145 O, >=137 M, >=123 T, <123 5
BATSTA:	clr	WRK  
	cpi	VOLTS,122
	brcs	BAT2
	ldi	WRK,19		; 'T'
	cpi	VOLTS,136
	brcs	BAT2
	ldi	WRK,12		; 'M'
	cpi	VOLTS,144
	brcs	BAT2
	ldi	WRK,14		; 'O'
	;
BAT2: 	tst	WRK		; Send T/M/O 3 times
	breq	BAT3
	rcall	MORSE
	DXDOT	4
	rcall	MORSE
	DXDOT	4
	rcall	MORSE
	DXDOT 	4
	rjmp	BAT9
	;
BAT3:	ldi	WRK,40 		; If undervoltage
BAT4:	TXON			; Flicker LED+TX for 3 sec
	DXMS	40
	TXOFF
	DXMS	40
	dec	WRK
	brne	BAT4
	;
BAT9:	ret
	;
	;**** Toggle Ident International/National
TOGIDE:	mov 	WRK,IDENT	; Toggle Bit7
	andi	WRK,$80		; Result is $00 (Nat) or $80 (Int) 
	ldi	WRK2,$80
	eor	WRK,WRK2
	mov	IDENT,WRK
	rcall	PUTEEP		; Store new Ident in EEP
	ret
	;
	;
	;**** Send Morsecode for ID
CWID:	mov 	WRK,IDENT
	sbrc	WRK,7		; ID national Mode
	rjmp	CWID1
	; National Mode
	rcall	MORSE
	rjmp	CWID9
	; International Mode		
CWID1:	andi	WRK,$0F		; Strip Bit 7
	ldi	DOTLEN,120	; Default 50 BpM
	cpi	WRK,6
	brcs	CWID2
	ldi	DOTLEN,80	; Fast Foxes: 75 BpM
	subi	WRK,6		; For Code Limit Ident to 0..5
	; Send International ID
CWID2:	push	WRK
	ldi	WRK,12		
	rcall 	MORSE		; M
	ldi	WRK,14
	rcall	MORSE		; O
	pop 	WRK
	;
CWID3:	tst	WRK		
	breq	CWID4
	rcall	PIPON		; 0..5 Dots
	rcall	PIPOFF
	dec	WRK
	rjmp	CWID3
	;
CWID4:	DXDOT	6		; Total 7 Dots between MOx 
	rcall	PIPOFF
	;
CWID9:	ret
	;
	;
	;**** Write one Byte in IDENT to EEPROM Address 0
PUTEEP:	clr	ZL
	out	EEAR,ZL
	out	EEARH,ZL	
	out 	EEDR,IDENT
	;
PEX:	ldi	ZL,$04		; Schreiben
	ldi	ZH,$02
	out	EECR,ZL 	; Set EEPROM Master Write Enable
	out	EECR,ZH 	; Set EEPROM Write	
PUTEE1:	sbic	EECR,1		; Wait for Write complete
	rjmp	PUTEE1
	ret
	;
	;++++ Used for Debug only: Write WRK to EEPROM Address 1
PUTTST:	clr	ZL		
	out	EEARH,ZL
	inc	ZL
	out	EEAR,ZL
	out	EEDR,WRK
	rjmp	PEX
	;
	;
	;**** Get 1 Byte from EEPROM Adress 0 and return in IDENT
GETEEP:	clr	WRK
	out	EEAR,WRK
	out	EEARH,WRK	
	ldi	WRK,$01
	out	EECR,WRK	; Set EEPROM Read Cmd
GETEE1:	in	WRK,EECR	; Wait for EEPROM Read complete
	andi	WRK,$01
	brne	GETEE1
	in	IDENT,EEDR	; Read fox ID 
	ret
	;
	;
	;**** Constants stored in Flash *********************************************
	;
	.ORG	$0380		; Reserve 256 B (2 x $80) at end of Flash
	;
	; ++++ Morsecode-Table for A..Z, 0..9
	;      Morseformat MMMM MLLL
	;	 LLL = Length 1..5 Dots + Dashes
	;        MMMMM = 0: Dot, 1: Dash, first Element in Bit 7
	;
ASC2MO:	.DB	$42,$84,$A4,$83 ; A B C D	0
	.DB	$01,$24,$C3,$04 ; E F G H	4	
	.DB	$02,$74,$A3,$44 ; I J K L	8
	.DB	$C2,$82,$E3,$64 ; M N O P	12
	.DB	$D4,$43,$03,$81 ; Q R S T	16
	.DB	$23,$14,$63,$94 ; U V W X	20
	.DB	$B4,$C4		; Y Z		24
	.DB	$FD,$7D,$3D,$1D ; 0 1 2 3	26
	.DB	$0D,$05,$85,$C5 ; 4 5 6 7	30
	.DB	$E5,$F5         ; 8 9 		34
	;
	; The End	
