Pfad: Home => AVR-Übersicht => Anwendungen => Frequenzzähler => Software Logo
Frequenzzaehler

Software für den Frequenzzähler



; *******************************************************
; * Frequenzzaehler, Drehzahlmesser und Voltmeter       *
; * fuer ATmega8 bei 16 MHz Taktfrequenz (Quarzosz.)    *
; * Version 0.2 (C)2005 by info!at!avr-asm-tutorial.net *
; *******************************************************
;
.INCLUDE "m8def.inc"
;
.EQU debug = 0
.EQU debugpulse = 0
;
; Switches for connected hardware
;
.EQU cDisplay = 1 ; LCD display connected
.EQU cDisplay8 = 0 ; displays 8 characters per line instead of 16
.EQU cDisplay2 = 1 ; two line LCD display connected
.EQU cUart = 1 ; Uart active
; attached prescaler on port C
.EQU pPresc = PORTC ; prescaler by 8 output attached to port C
.EQU pPrescD = DDRC ; data direction of prescaler
.EQU bPresc = 5 ; bit 5 enables prescaler by 8
;
; ================================================
;          Other hardware depending stuff
; ================================================
;
.EQU cFreq = 16000000 ; Clock frequency processor in cycles/s
.IF cUart
	.EQU cBaud = 9600 ; If Uart active, define Baudrate
	.ENDIF
.EQU bLcdE = 5 ; LCD E port bit on Port B
.EQU bLcdRs = 4 ; Lcd RS port bit on Port B
;
; ================================================
;       Constants for voltage measurement
; ================================================
;
; Resistor network as pre-divider for the ADC
; --------------------------------------
; R1   R2(k) Meas  Accur.  MaxVoltage
; kOhm kOhm  Volt  mV/dig  Volt
; --------------------------------------
; 1000 1000   5,12    5    10
; 1000  820   5,68    6    11
; 1000  680   6,32    6    12
; 1000  560   7,13    7    14
; 1000  470   8,01    8    15
; 1000  330  10,32   10    20
; 1000  270  12,04   12    23
; 1000  220  14,20   14    27
; 1000  180  16,78   16    32
; 1000  150  19,63   19    38
; 1000  120  23,98   23    46
; 1000  100  28,16   28    55
;
.EQU cR1 = 1000 ; Resistor between ADC input and measured voltage
.EQU cR2 = 1000 ; Resistor between ADC input and ground
.EQU cRin = 8250 ; Input resistance ADC, experimental 
;
; Other sSoft switches
;
.EQU cNMode = 3 ; number o0f measurements before mode changes
.EQU cDecSep = ',' ; decimal separator for numbers displayed
.EQU c1kSep = '.' ; thousands separator
.EQU nMeasm = 4 ; number of measurements per second
.IF (nMeasm<4)||(nMeasm>7)
	.ERROR "Number of measurements outside acceptable range"
	.ENDIF
;
; ================================================
;           Hardware connections
; ================================================
;           ___   ___
;    RESET |1  |_| 28| Prescaler divide by 8 output
;      RXD |2   A  27| 
;      TXD |3   T  26| 
; Time inp |4   M  25| 
;          |5   E  24| Mode select input, 0..2.56 V
; Count in |6   L  23| Voltage input, 0..2.56 V
;      VCC |7      22| GND
;      GND |8   A  21| AREF (2.56 V output)
;    XTAL1 |9   T  20| AVCC input
;    XTAL2 |10  m  19| SCK/LCD-E
;          |11  e  18| MISO/LCD-RS
;          |12  g  17| MOSI/LCD-D7
;          |13  a  16| LCD-D6
;   LCD-D4 |14  8  15| LCD-D5
;          |_________|
;
;
; ================================================
;           Derived constants
; ================================================
;
.EQU cR2c = (cR2 * cRin) / (cR2+cRin)
.EQU cMultiplier = (641 * (cR1+cR2c))/cR2c ; used for voltage multiplication
.EQU cMaxVoltage = 1024*cMultiplier/256 ; in mV
.EQU cSafeVoltage = (cMaxVoltage * 5000) / 2560
.EQU cTDiv = 1000/nMeasm ; interval per measurement update
; calculating the CTC and prescaler values for TC1 (frequency measurement)
.SET cCmp1F = cFreq/32 ; CTC compare value with prescaler = 8
.SET cPre1F = (1<<WGM12)|(1<<CS11) ; CTC and prescaler = 8
.IF cFreq>2097120
	.SET cCmp1F = cFreq/256 ; CTC compare value with prescaler = 64
	.SET cPre1F = (1<<WGM12)|(1<<CS11)|(1<<CS10) ; prescaler = 64
	.ENDIF
.IF cFreq>16776960
	.SET cCmp1F = cFreq/1024 ; CTC compare value with prescaler = 256
	.SET cPre1F = (1<<WGM12)|(1<<CS12) ; prescaler = 256
	.ENDIF
; calculating the CTC and prescaler values for TC2 (LCD/UART update) 
.SET cCmp2 = cFreq/8000
.SET cPre2 = (1<<CS21) ; prescaler = 8
.IF cFreq>2040000
	.SET cCmp2 = cFreq/32000
	.SET cPre2 = (1<<CS21)|(1<<CS20) ; prescaler = 32
	.ENDIF
.IF cFreq>8160000
	.SET cCmp2 = cFreq/64000
	.SET cPre2 = (1<<CS22) ; prescaler = 64
	.ENDIF
.IF cFreq>16320000
	.SET cCmp2 = cFreq/128000 ; prescaler = 128
	.SET cPre2 = (1<<CS22)|(1<<CS20) 
	.ENDIF
;
; Uart constants
;
.IF cUart
	.EQU cNul = $00
	.EQU cClrScr = $0C
	.EQU cCr = $0D
	.EQU cLf = $0A
	.ENDIF
;
; Debug definitions for testing
;
;
; ================================================
;            Register definitons
; ================================================
;
; R0 used for LPM and for calculation purposes
.DEF rRes1 = R1 ; Result byte 1
.DEF rRes2 = R2 ; Result byte 2
.DEF rRes3 = R3 ; Result byte 3
.DEF rRes4 = R4 ; Result byte 4
.DEF rDiv1 = R5 ; Divisor byte 1
.DEF rDiv2 = R6 ; Divisor byte 2
.DEF rDiv3 = R7 ; Divisor byte 3
.DEF rDiv4 = R8 ; Divisor byte 4
.DEF rCpy1 = R9 ; Copy byte 1
.DEF rCpy2 = R10 ; Copy byte 2
.DEF rCpy3 = R11 ; Copy byte 3
.DEF rCpy4 = R12 ; Copy byte 4
.DEF rCtr1 = R13 ; Counter/Timer byte 1
.DEF rCtr2 = R14 ; Counter/Timer byte 2
.DEF rCtr3 = R15 ; Counter/Timer byte 3
.DEF rmp = R16 ; Multipurpose register outside interrupts
.DEF rimp = R17 ; Multipurpose register inside interrupts
.DEF rSreg = R18 ; Save status register inside interrupts
.DEF rTDiv = R19 ; Internal divider for TC2 count down
.DEF rMode = R20 ; Current mode of operation
.DEF rNMode = R21 ; Number of inadequate measurements
; free: R22
.DEF rFlg = R23 ; Flag register
.EQU bCyc = 2 ; measure cycle ended
.EQU bMode = 3 ; measuring mode, 1 = frequency, 0 = time
.EQU bEdge = 4 ; measured edge, 1 = rising, 0 = falling
.EQU bOvf = 5 ; overflow bit
.EQU bAdc = 6 ; ADC conversion complete flag bit
.EQU bUartRxLine = 7 ; Uart line complete flag bit
.DEF rDelL = R24 ; delay counter for LCD, LSB
.DEF rDelH = R25 ; dto., MSB
; X = R26..R27 used for calculation purposes
; Y = R28..R29: free
; Z = R30..R31 used for LPM and calculation purposes
;
; ================================================
;             SRAM definitions
; ================================================
;
.DSEG
.ORG $0060
;
; Result display space in SRAM
;
sResult:
.BYTE 32
;
; Uart receive buffer space in SRAM
;   sUartRxBs is buffer start
;   sUartRxBe is buffer end
;   sUartRxBp is buffer input position
;
.IF cUart
	.EQU UartRxbLen = 38 ; Buffer length in bytes
;
	sUartFlag: ; flag register for Uart
		.BYTE 1
		.EQU bUMonU = 0 ; displays voltage over Uart
		.EQU bUMonF = 1 ; displays frequency over Uart
		; free: bits 2..7
	sUartMonUCnt: ; counter for Monitoring voltage
		.BYTE 1
	sUartMonURpt: ; counter preset for monitoring voltage
		.BYTE 1
	sUartRxBp: ; buffer pointer
		.BYTE 1
	sUartRxBs: ; buffer
		.BYTE UartRxbLen
	sUartRxBe: ; buffer end
	.ENDIF
;
; Main interval timer characteristics
;
sTMeas: ; ms per measuring interval (default: 250)
.BYTE 1
;
; ADC result
;
sAdc0L: ; ADC result, channel 0, LSB
.BYTE 1
sAdc0H: ; ADC result, channel 0, MSB
.BYTE 1
sAdc1L: ; ADC result, channel 1, LSB
.BYTE 1
sAdc1H: ; ADC result, channel 1, MSB
.BYTE 1
;
; Interim storage for counter value during time measurement
;
sCtr:
.BYTE 4
;
; ================================================
;          Selected mode flags
; ================================================
;
;  Mode   Measuring  Prescale  Display
;  ---------------------------------------------
;   0     Frequency    8       Frequency
;   1     Frequency    1       Frequency
;   2     Time HL      1       Frequency
;   3     Time HL      1       Rounds per Minute
;   4     Time HL      1       Time
;   5     Time H       1       Time
;   6     Time L       1       Time
;   7     PW ratio H   1       Pulse width ratio H %
;   8     PW ratio L   1       Pulse width ratio L %
;   9     none         -       Voltage only
;
.EQU cModeFrequency8 = 0
.EQU cModeFrequency = 1
.EQU cModeTimeFreq = 2
.EQU cModeTimeRpm = 3
.EQU cModeTimeTimeHL = 4
.EQU cModeTimeTimeH = 5
.EQU cModeTimeTimeL = 6
.EQU cModeTimePwrH = 7
.EQU cModeTimePwrL = 8
.EQU cModeVoltage = 9
;
sModeSlct: ; Selected mode
.BYTE 1
sModeNext: ; next selected mode
.BYTE 1
;
;
; ==================================================
;   Info on timer and counter interrupt operation
; ==================================================
;
; Clock => Presc2 => TC2 => CTC => rTDiv =>
;       ADC0 conversion => ADC1 conversion => bAdc-flag
;
; Main interval timer TC2
;    - uses TC2 as 8-bit-CTC, with compare interrupt
;    - starts a ADC conversion
;    - on ADC conversion complete:
;      * store ADC result
;      * convert ADC result
;      * if a new counter result: convert this
;      * if Uart connected and monitoring f/U: display on Uart
;      * if LCD connected and display mode: display f/U result  
; 
; Operation at 16 MHz clock:
;   cFreq => Prescaler/128 => CTC(125) => rTDiv(250)
;   16MHz =>   125 kHz     =>  1 kHz   =>   4 Hz
;
; Frequeny counting modes (Mode = 0 and 1)
;    - uses TC0 as 8-bit-counter to count positive edges
;    - uses TC1 as 16-bit-counter to time-out the counter after 250 ms
;
; Timer modes (Mode = 2 to 8)
;    - uses edge detection on external INT0 for timeout
;    - uses TC1 as 16-bit-counter to time-out from edge to edge
;
; Voltage only (Mode = 9)
;    - Timers TC0 and TC1 off
;    - Timer TC2 times interval
;
; ==============================================
;   Reset and Interrupt Vectors starting here
; ==============================================
;
.CSEG
.ORG $0000
;
; Reset/Intvectors
;
	rjmp Main ; Reset
	rjmp Int0Int; Int0
	reti ; Int1
	rjmp TC2CmpInt ; TC2 Comp
	reti ; TC2 Ovf
	reti ; TC1 Capt
	rjmp Tc1CmpAInt ; TC1 Comp A
	reti ; TC1 Comp B
	rjmp Tc1OvfInt ; TC1 Ovf
	rjmp TC0OvfInt ; TC0 Ovf
	reti ; SPI STC
.IFDEF cUart
	rjmp SioRxcIsr ; USART RX
.ELSE
	reti ; USART RX
.ENDIF
	reti ; USART UDRE
	reti ; USART TXC
	rjmp AdcCcInt ; ADC Conv Compl
	reti ; EERDY
	reti ; ANA_COMP
	reti ; TWI
	reti ; SPM_RDY
;
; =============================================
;
;     Interrupt Service Routines
;
; =============================================
;
; TC2 Compare Match Interrupt
;   counts rTDiv down, if zero: starts an AD conversion
;
TC2CmpInt:
	in rSreg,SREG ; save SREG
	dec rTDiv ; count down
	brne TC2CmpInt1 ; not zero, interval not ended
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; start ADC conversion
	lds rTDiv,sTMeas ; restart interval timer
TC2CmpInt1:
	out SREG,rSreg ; restore SREG
	reti
;
; External Interrupt INT0 Service Routine
;   active in modes 2 to 6 (measuring the signal duration),
;   detects positive going edges of the input
;   INT1, TC1 is in free running mode,
;   reads the current counter state of TC1,
;   copies it to the result registers,
;   clears the counter and restarts it
;
Int0Int:
	in rSreg,SREG ; 1, save SREG
	sbrc rFlg,bCyc ; 2/3, check if cycle flag signals ok for copy
	rjmp Int0Int1 ; 4, no, last result hasn't been read
	in rCpy1,TCNT1L ; 4, read timer 1 LSB
	in rCpy2,TCNT1H ; 5, dto., MSB
	mov rCpy3,rCtr2 ; 6, copy the counter bytes
	mov rCpy4,rCtr3 ; 7
	sbr rFlg,1<<bCyc ; 8, set cycle end flag bit
	cbr rFlg,1<<bEdge ; 9, set falling edge
	sbic PIND,2 ; 10/11, check if input = 0
	sbr rFlg,1<<bEdge ; 11, no, set edge flag to rising
Int0Int1: ; 4/11
	ldi rimp,0 ; 5/12, reset the timer
	out TCNT1H,rimp ; 6/13, set TC1 zero to restart
	out TCNT1L,rimp ; 7/14
	mov rCtr1,rimp ; 8/15, clear the upper bytes
	mov rCtr2,rimp ; 9/16
	mov rCtr3,rimp ; 10/17
	out SREG,rSreg ; 11/18, restore SREG
	reti ; 15/22
;
; TC1 Compare Match A Interrupt Service Routine
;   active in modes 0 and 1 (measuring the number of
;   sigals on the T1 input), timeout every 0.25s,
;   reads the counter TC0, copies the count to
;   the result registers and clears TC0
;
Tc1CmpAInt:
	in rSreg,SREG ; 1, save SREG
	sbrc rFlg,bCyc ; 2/3, check if cycle flag signals ok for copy
	rjmp TC1CmpAInt1 ; 4, no, last result hasn't been read
	in rCpy1,TCNT0 ; 4, read counter TC0
	mov rCpy2,rCtr1 ; 5, copy counter bytes to result
	mov rCpy3,rCtr2 ; 6
	mov rCpy4,rCtr3 ; 7
	sbr rFlg,1<<bCyc ; 8, set cycle end flag bit
Tc1CmpAInt1: ; 4/8
	ldi rimp,0 ; 5/9, clear counter
	out TCNT0,rimp ; 6/10
	mov rCtr1,rimp ; 7/11, clear counter bytes
	mov rCtr2,rimp ; 8/12
	mov rCtr3,rimp ; 9/13
	out SREG,rSreg ; 10/14, restore SREG
	reti ; 14/18
;
; TC1 Overflow Interrupt Service Routine
;   active in modes 2 to 6 counting clock cycles to measure time
;   increases the upper bytes and detects overflows
;
Tc1OvfInt:
	in rSreg,SREG ; 1, save SREG
	inc rCtr2 ; 2, increase byte 3 of the counter
	brne Tc1OvfInt1 ; 3/4, no overflow
	inc rCtr3 ; 4, increase byte 4 of the counter
	brne Tc1OvfInt1 ; 5/6, no overflow
	sbr rFlg,(1<<bOvf)|(1<<bCyc) ; 6, set overflow and end of cycle bit
Tc1OvfInt1: ; 4/6
	out SREG,rSreg ; 5/7, restore SREG
	reti ; 9/11
;
; TC0 Overflow Interrupt Service Routine
;   active in modes 0 and 1 counting positive edges on T1
;   increases the upper bytes and detects overflows
;
Tc0OvfInt:
	in rSreg,SREG ; 1, save SREG
	inc rCtr1 ; 2, increase byte 2 of the counter
	brne Tc0OvfInt1 ; 3/4, no overflow
	inc rCtr2 ; 4, increase byte 3 of the counter
	brne Tc0OvfInt1 ; 5/6, no overflow
	inc rCtr3 ; 6, increase byte 4 of the counter
	brne Tc0OvfInt1 ; 7/8, no overflow
	sbr rFlg,(1<<bOvf)|(1<<bCyc) ; 8, set overflow bit
Tc0OvfInt1: ; 4/6/8
	out SREG,rSreg ; 5/7/9, restore SREG
	reti ; 9/11/13
;
; Uart RxC Interrupt Service Routine
;   receives a character, signals errors, echoes it back,
;   puts it into the SRAM line buffer, checks for carriage
;   return characters, if yes echoes additional linefeed
;   and sets line-complete flag
;
.IF cUart
SioRxCIsr:
	in rSreg,SREG ; 1, Save SReg
	in rimp,UCSRA ; 2, Read error flags
	andi rimp,(1<<FE)|(1<<DOR)|(1<<PE) ; 3, isolate error bits
	in rimp,UDR ; 4, read character from UART
	breq SioRxCIsr1 ; 5/6, no errors
	ldi rimp,'*' ; 6, signal an error
	out UDR,rimp ; 7
	rjmp SioRxCIsr4 ; 9, return from int
SioRxCIsr1: ; 6
	out UDR,rimp ; 7, echo the character
	push ZH ; 9, Save Z register
	push ZL ; 11
	ldi ZH,HIGH(sUartRxBs) ; 12, Load Position for next RX char
	lds ZL,sUartRxBp ; 14
	st Z+,rimp ; 16, save char in buffer
	cpi ZL,LOW(sUartRxBe+1) ; 17, End of buffer?
	brcc SioRxCIsr2 ; 18/19, Buffer overflow
	sts sUartRxBp,ZL ; 20, Save next pointer position
SioRxCIsr2: ; 19/20
	cpi rimp,cCr ; 20/21, Carriage Return?
	brne SioRxCIsr3 ; 21/22/23, No, go on
	ldi rimp,cLf ; 22/23, Echo linefeed
	out UDR,rimp ; 23/24
	sbr rFlg,(1<<bUartRxLine) ; 24/25, Set line complete flag
SioRxCIsr3: ; 22/23/24/25
	pop ZL ; 24/25/26/27, restore Z-register
	pop ZH ; 26/27/28/29
SioRxCIsr4: ; 9/26/27/28/29
	out SREG,rSreg ; 10/27/28/29/30, restore SREG
	reti ; 14/31/32/33/34, return from Int
.ENDIF
;
; ADC has completed a conversion
;   used in all modes, ADC has completed a conversion
;   if ADMUX channel is 0 then set ADMUX=1 and start
;     another coversion
;   if ADMUX channel is 1 he set ADMUX=0, disable the ADC
;     and set teh ADC cycle end flag
;
.EQU cStopAdc = (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
.EQU cStartAdc = (1<<ADEN)|(1<<ADSC)|(1<<ADIE)|cStopAdc
AdcCcInt:
	in rSreg,SREG ; 1, save SREG
	in rimp,ADMUX ; 2, read the current channel info
	sbrc rimp,0 ; 3/4, jump if channel = 0
	rjmp AdcCcInt1 ; 5
	sbr rimp,1 ; 5, set channel MUX to one
	out ADMUX,rimp ; 6
	in rimp,ADCL ; 7, read result LSB
	sts sAdc0L,rimp ; 9, store result LSB
	in rimp,ADCH ; 10, read result MSB
	sts sAdc0H,rimp ; 12, store result MSB
	ldi rimp,cStartAdc
	out ADCSRA,rimp ; 14, start next conversion
	out SREG,rSreg ; 15, restore SREG
	reti ; 19
AdcCcInt1: ; 5
	cbr rimp,1 ; 6, set MUX to channel 0
	out ADMUX,rimp ; 7
	in rimp,ADCL ; 8, read result LSB
	sts sAdc1L,rimp ; 10, store result LSB
	in rimp,ADCH ; 11, read result MSB
	sts sAdc1H,rimp ; 13, store result MSB
	sbr rFlg,1<<bAdc ; 14, set flag bit
	ldi rimp,cStopAdc ; 15, ADC off
	out ADCSRA,rimp ; 16, switch ADC off
	out SREG,rSreg ; 17, restore SREG
	reti ; 21
;
; ================================================
;          Common subroutines
; ================================================
;
; Setting timer/counter modes for measuring
;
SetModeNext:
	rcall ClrTc ; clear the timers TC0 and TC1, disable INT0
	lds rmp,sModeNext ; read next mode
	cpi rmp,10 ; check number of modes
	brcs SetModeNext1 ; mode is ok
	ldi rmp,1 ; set mode 1
	sts sModeNext,rmp ; correct next mode
SetModeNext1:
	mov rMode,rmp ; copy to current mode
	ldi ZH,HIGH(SetModeTab)
	ldi ZL,LOW(SetModeTab)
	add ZL,rmp
	ldi rmp,0
	adc ZH,rmp
	ijmp
; Table mode setting
SetModeTab:
	rjmp SetMode0 ; f div 8, f
	rjmp SetModeF ; f, f
	rjmp SetModeT ; t, f
	rjmp SetModeT ; t, u
	rjmp SetModeT ; t, t
	rjmp SetmodeE ; th, t
	rjmp SetModeE ; tl, t
	rjmp SetModeE ; th, p
	rjmp SetModeE ; tl, p
	ret ; U, U
;
; Set counters/timers to mode 0
;   TC0 counts input signals (positive edges)
;   TC1 times the gate at 250 ms
;   INT0 disabled
;   
SetMode0:
	cbi pPresc,bPresc ; enable prescaler
	rjmp SetModeF ; frequency measurement
;
; Set counters/timers to mode 1
;
SetMode1:
	sbi pPresc,bPresc ; disable prescaler
; Set timer/counter mode to frequency measurement
SetModeF:
	ldi rmp,HIGH(cCmp1F) ; set the compare match high value
	out OCR1AH,rmp
	ldi rmp,LOW(cCmp1F) ; set the compare match low value
	out OCR1AL,rmp
	ldi rmp,0xFF ; disable the compare match B
	out OCR1BH,rmp
	out OCR1BL,rmp
	ldi rmp,0 ; CTC mode
	out TCCR1A,rmp
	ldi rmp,cPre1F ; set the prescaler value for TC1
	out TCCR1B,rmp
	ldi rmp,(1<<CS02)|(1<<CS01)|(1<<CS00) ; count rising edges on T0
	out TCCR0,rmp
	ldi rmp,(1<<OCIE2)|(1<<OCIE1A)|(1<<TOIE0) ; enable TC2Cmp, TC1CmpAInt and TC0OverflowInt
	out TIMSK,rmp
	ret
;
; Set timer/counter mode to time measurement
;
SetModeT:
	ldi rmp,0 ; timing mode
	out TCCR1A,rmp
	ldi rmp,1<<CS10 ; count with prescaler = 1
	out TCCR1B,rmp
	ldi rmp,(1<<SE)|(1<<ISC01)|(1<<ISC00) ; sleep enable, positive edges on INT0 interrupt
	out MCUCR,rmp
	ldi rmp,1<<INT0 ; enable INT0 interrupt
	out GICR,rmp
	ldi rmp,(1<<OCIE2)|(1<<TOIE1) ; enable TC2Cmp, TC1Ovflw
	out TIMSK,rmp
	ret
;
; Set timer/counter mode to time measurement, all edges
;
SetModeE:
	ldi rmp,0 ; timing mode
	out TCCR1A,rmp
	ldi rmp,1<<CS10 ; count with prescaler = 1
	out TCCR1B,rmp
	ldi rmp,(1<<SE)|(1<<ISC00) ; sleep enable, any logical change on INT0 interrupts
	out MCUCR,rmp
	ldi rmp,1<<INT0 ; enable INT0 interrupt
	out GICR,rmp
	ldi rmp,(1<<OCIE2)|(1<<TOIE1) ; enable TC2Cmp, TC1Ovflw
	out TIMSK,rmp
	ret
;
;
; clears the timers and resets the upper bytes
;
ClrTc:
	clr rmp ; disable INT0
	out GICR,rmp
	clr rmp ; stop the counters/timers
	out TCCR0,rmp ; stop TC0 counting/timing
	out TCCR1B,rmp ; stop TC1 counting/timing
	out TCNT0,rmp ; clear TC0
	out TCNT1L,rmp ; clear TC1
	out TCNT1H,rmp
	clr rCtr1 ; clear upper bytes
	clr rCtr2
	clr rCtr3
	ldi rmp,1<<OCIE2 ; enable only output compare of TC2 ints
	out TIMSK,rmp ; timer int disable
	ret
;
; =======================================================
;                 Math routines
; =======================================================
;
; Divides cFreq/256 by the timer value in rDiv4:rDiv3:rDiv2:rDiv1
;   yields frequency in R4:R3:R2:(Fract):R1
;
Divide:
	clr rmp ; rmp:R0:ZH:ZL:XH:XL is divisor
	clr R0
	clr ZH
	ldi ZL,BYTE3(cFreq/256) ; set divisor
	ldi XH,BYTE2(cFreq/256)
	ldi XL,BYTE1(cFreq/256)
	clr rRes1 ; set result
	inc rRes1
	clr rRes2
	clr rRes3
	clr rRes4
Divide1:
	lsl XL ; multiply divisor by 2
	rol XH
	rol ZL
	rol ZH
	rol R0
	rol rmp
	cp ZL,rDiv1 ; compare with divident
	cpc ZH,rDiv2
	cpc R0,rDiv3
	cpc rmp,rDiv4
	brcs Divide2
	sub ZL,rDiv1
	sbc ZH,rDiv2
	sbc R0,rDiv3
	sbc rmp,rDiv4
	sec
	rjmp Divide3
Divide2:
	clc
Divide3:
	rol rRes1
	rol rRes2
	rol rRes3
	rol rRes4
	brcc Divide1
	ret
;
; Multiply measured time in rRes4:rRes3:rRes2:rRes1 by 65536 / fq(MHz)
;   rmp:R0 are the upper bytes of the input
;   ZH:ZL:rDiv4:rDiv3:rDiv2:rDiv1 is the interim result
;   XH:XL is the multiplicator
;   result is in rRes4:rRes3:rRes2:rRes1
;
.EQU cMulti = 65536000 / (cFreq/1000)
;
Multiply:
	ldi XH,HIGH(cMulti) ; set multiplicator
	ldi XL,LOW(cMulti)
	clr ZH
	clr ZL
	clr rDiv4
	clr rDiv3
	clr rDiv2
	clr rDiv1
	clr R0
	clr rmp
Multiply1:
	cpi XL,0
	brne Multiply2
	cpi XH,0
	breq Multiply4
Multiply2:
	lsr XH
	ror XL
	brcc Multiply3
	add rDiv1,rRes1
	adc rDiv2,rRes2
	adc rDiv3,rRes3
	adc rDiv4,rRes4
	adc ZL,R0
	adc ZH,rmp
Multiply3:
	lsl rRes1
	rol rRes2
	rol rRes3
	rol rRes4
	rol R0
	rol rmp
	rjmp Multiply1
Multiply4:
	ldi rmp,128 ; round result
	clr R0
	add rDiv2,rmp
	adc rDiv3,R0
	adc rDiv4,R0
	adc ZL,R0
	adc ZH,R0
	mov rRes1,rDiv3 ; move result
	mov rRes2,rDiv4
	mov rRes3,ZL
	mov rRes4,ZH
	ret
;
; Display seconds at buffer end
;
DisplSec:
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'u'
	st X+,rmp
	ldi rmp,'s'
	st X+,rmp
	ldi rmp,' '
	st X,rmp
	.ENDIF
	ret
;
; An overflow has occurred during pulse width calculation
;
PulseOvflw:
	ldi XH,HIGH(sResult)
	ldi XL,LOW(sResult)
	st X+,rmp
.IF cDisplay8
	ldi ZH,HIGH(2*TxtPOvflw8)
	ldi ZL,LOW(2*TxtPOvflw8)
	ldi rmp,7
	.ELSE
	ldi ZH,HIGH(2*TxtPOvflw16)
	ldi ZL,LOW(2*TxtPOvflw16)
	ldi rmp,15
	.ENDIF
PulseOvflw1:
	lpm
	adiw ZL,1
	st X+,R0
	dec rmp
	brne PulseOvflw1
	ret
.IF cDisplay8
	TxtPOvflw8:
	.DB ":error! "
	.ELSE
	TxtPOvflw16:
	.DB ":error calcul.! "
	.ENDIF
;
; ======================================================
;     Pulse width calculations 
; ======================================================
;
; Calculate the pulse width ratio
;   active cycle time is in rDelH:rDelL:R0:rmp
;   total cycle time is in rDiv
;   result will be in rRes
;   overflow: carry flag is set
;
CalcPwO: ; overflow
	sec
	ret
CalcPw:
	mov rRes1,rmp ; copy active cycle time to rRes
	mov rRes2,R0
	mov rRes3,rDelL
	mov rRes4,rDelH
	lsl rRes1 ; * 2
	rol rRes2
	rol rRes3
	rol rRes4
	brcs CalcPwO ; overflow
	lsl rRes1 ; * 4
	rol rRes2
	rol rRes3
	rol rRes4
	brcs CalcPwO ; overflow
	lsl rRes1 ; * 8
	rol rRes2
	rol rRes3
	rol rRes4
	brcs CalcPwO ; overflow
	mov XL,rRes1 ; copy to Z:X
	mov XH,rRes2
	mov ZL,rRes3
	mov ZH,rRes4
	lsl rRes1 ; * 16
	rol rRes2
	rol rRes3
	rol rRes4
	brcs CalcPwO
	add rRes1,XL ; * 24
	adc rRes2,XH
	adc rRes3,ZL
	adc rRes4,ZH
	clr ZH ; clear the four MSBs of divisor
	clr ZL
	clr XH
	mov XL,rDelH ; * 256
	mov rDelH,rDelL
	mov rDelL,R0
	mov R0,rmp
	clr rmp
	lsl R0 ; * 512
	rol rDelL
	rol rDelH
	rol XL
	rol XH
	lsl R0 ; * 1024
	rol rDelL
	rol rDelH
	rol XL
	rol XH
	sub rmp,rRes1 ; * 1000
	sbc R0,rRes2
	sbc rDelL,rRes3
	sbc rDelH,rRes4
	sbc XL,ZH
	sbc XH,ZH
	cp XL,rDiv1 ; overflow?
	cpc XH,rDiv2
	cpc ZL,rDiv3
	cpc ZH,rDiv4
	brcc CalcPwO
	clr rRes1 ; clear result
	inc rRes1
	clr rRes2
	clr rRes3
	clr rRes4
CalcPw1: ; dividing loop
	lsl rmp ; multiply by 2
	rol R0
	rol rDelL
	rol rDelH
	rol XL
	rol XH
	rol ZL
	rol ZH
	cp XL,rDiv1 ; compare with divisor
	cpc XH,rDiv2
	cpc ZL,rDiv3
	cpc ZH,rDiv4
	brcs CalcPw2 ; smaller, roll zero in
	sub XL,rDiv1 ; subtract divisor
	sbc XH,rDiv2
	sbc ZL,rDiv3
	sbc ZH,rDiv4
	sec ; roll one in
	rjmp CalcPw3
CalcPw2:
	clc
CalcPw3: ; roll result
	rol rRes1
	rol rRes2
	rol rRes3
	rol rRes4
	brcc CalcPw1 ; roll on
	lsl rDelL ; round result
	rol XL
	rol XH
	rol ZL
	rol ZH
	cp XL,rDiv1
	cpc XH,rDiv2
	cpc ZL,rDiv3
	cpc ZH,rDiv4
	brcs CalcPw4
	ldi rmp,1 ; round up
	add rRes1,rmp
	ldi rmp,0
	adc rRes2,rmp
	adc rRes3,rmp
	adc rRes4,rmp
CalcPw4:
	tst rRes4 ; check > 1000
	brne CalcPwE
	tst rRes3
	brne CalcPwE
	ldi rmp,LOW(1001)
	cp rRes1,rmp
	ldi rmp,HIGH(1001)
	cpc rRes2,rmp
	brcc CalcPwE
	clc ; no error
	ret
CalcPwE: ; error
	sec
	ret
;
; Display the binary in R2:R1 in the form "  100,0%"
;
DisplPw:
	ldi XH,HIGH(sResult)
	ldi XL,LOW(sResult)
	ldi rmp,' '
	st X+,rmp
	st X+,rmp
	clr R0
	ldi ZH,HIGH(1000)
	ldi ZL,LOW(1000)
	rcall DisplDecX2
	ldi ZH,HIGH(100)
	ldi ZL,LOW(100)
	rcall DisplDecX2
	ldi ZL,10
	inc R0
	rcall DisplDecX2
	ldi rmp,cDecSep
	st X+,rmp
	ldi rmp,'0'
	add rmp,rRes1
	st X+,rmp
	ldi rmp,'%'
	st X+,rmp
.IF ! cDisplay8
	ldi rmp,8
	ldi ZL,' '
DisplPw1:
	st X+,ZL
	dec rmp
	brne DisplPw1
	.ENDIF
	ret
;
; If the first characters in the result buffer are empty,
;   place the character in ZL here and add equal, if possible
;
DisplMode:
	ldi XH,HIGH(sResult+1) ; point to result buffer
	ldi XL,LOW(sResult+1)
	ld rmp,X ; read second char
	cpi rmp,' '
	brne DisplMode1
	ldi rmp,'='
	st X,rmp
DisplMode1:
	sbiw XL,1
	ld rmp,X ; read first char
	cpi rmp,' '
	brne DisplMode2
	st X,ZL
DisplMode2:
	ret
;
;=================================================
;        Display binary numbers as decimal
;=================================================
;
; Converts a binary in R2:R1 to a digit in X
;   binary in Z
;
DecConv:
	clr rmp
DecConv1:
	cp R1,ZL ; smaller than binary digit?
	cpc R2,ZH
	brcs DecConv2 ; ended subtraction
	sub R1,ZL
	sbc R2,ZH
	inc rmp
	rjmp DecConv1
DecConv2:
	tst rmp
	brne DecConv3
	tst R0
	brne DecConv3
	ldi rmp,' ' ; suppress leading zero
	rjmp DecConv4
DecConv3:
	subi rmp,-'0'
DecConv4:
	st X+,rmp
	ret
;
; Display fractional number in R3:R2:(Fract)R1
;
DisplFrac:
	ldi XH,HIGH(sResult)
	ldi XL,LOW(sResult)
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	st X+,rmp
	.ENDIF
	clr R0
	ldi ZH,HIGH(10000)
	ldi ZL,LOW(10000)
	rcall DisplDecY2
	ldi ZH,HIGH(1000)
	ldi ZL,LOW(1000)
	rcall DisplDecY2
.IF ! cDisplay8
	ldi rmp,c1kSep
	tst R0
	brne DisplFrac0
	ldi rmp,' '
	DisplFrac0:
	st X+,rmp
	.ENDIF
	ldi ZL,100
	rcall DisplDecY1
	ldi ZL,10
	rcall DisplDecY1
	ldi rmp,'0'
	add rmp,R2
	st X+,rmp
	tst R1 ; fraction = 0?
	brne DisplFrac1
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'H'
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	st X+,rmp
	st X+,rmp
	st X+,rmp
	.ENDIF
	ret
DisplFrac1:
	ldi rmp,cDecSep
	st X+,rmp
.IF cDisplay8
	ldi ZL,2
	.ELSE
	ldi ZL,3
	.ENDIF
DisplFrac2:
	clr rRes3
	clr rRes2
	mov R0,rRes1 ; * 1
	lsl rRes1 ; * 2
	adc rRes2,rRes3
	lsl rRes1 ; * 4
	rol rRes2
	add rRes1,R0 ; * 5
	adc rRes2,rRes3
	lsl rRes1 ; * 10
	rol rRes2
	ldi rmp,'0'
	add rmp,rRes2
	st X+,rmp
	dec ZL
	brne DisplFrac2
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'H'
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	ldi rmp,' '
	st X+,rmp
	.ENDIF
	ret
;
; convert a decimal in R4:R3:R2, decimal in ZH:ZL
;
DisplDecY2:
	clr rDiv1 ; rDiv1 is counter
	clr rDiv2 ; overflow byte
DisplDecY2a:
	cp rRes2,ZL
	cpc rRes3,ZH
	cpc rRes4,rDiv2
	brcs DisplDecY2b ; ended
	sub rRes2,ZL ; subtract
	sbc rRes3,ZH
	sbc rRes4,rDiv2
	inc rDiv1
	rjmp DisplDecY2a
DisplDecY2b:
	ldi rmp,'0'
	add rmp,rDiv1
	add R0,rDiv1
	tst R0
	brne DisplDecY2c
	ldi rmp,' '
DisplDecY2c:
	st X+,rmp
	ret
;
; convert a decimal decimal in R:R2, decimal in ZL
;
DisplDecY1:
	clr rDiv1 ; rDiv1 is counter
	clr rDiv2 ; overflow byte
DisplDecY1a:
	cp rRes2,ZL
	cpc rRes3,rDiv2
	brcs DisplDecY1b ; ended
	sub rRes2,ZL ; subtract
	sbc rRes3,rDiv2
	inc rDiv1
	rjmp DisplDecY1a
DisplDecY1b:
	ldi rmp,'0'
	add rmp,rDiv1
	add R0,rDiv1
	tst R0
	brne DisplDecY1c
	ldi rmp,' '
DisplDecY1c:
	st X+,rmp
	ret
;
; Display a 4-byte-binary in decimal format on result line 1
;   8-bit-display: "12345678"
;   16-bit-display: "  12.345.678 Hz "
;
Displ4Dec:
	ldi rmp,BYTE1(100000000) ; check overflow
	cp rRes1,rmp
	ldi rmp,BYTE2(100000000)
	cpc rRes2,rmp
	ldi rmp,BYTE3(100000000)
	cpc rRes3,rmp
	ldi rmp,BYTE4(100000000)
	cpc rRes4,rmp
	brcs Displ4Dec1
	rjmp CycleOvf
Displ4Dec1:
	clr R0 ; suppress leading zeroes
	ldi XH,HIGH(sResult) ; X to result buffer
	ldi XL,LOW(sResult)
.IF ! cDisplay8
	ldi rmp,' ' ; clear the first two digits
	st X+,rmp
	st X+,rmp
	.ENDIF
	ldi ZH,BYTE3(10000000) ; 10 mio
	ldi ZL,BYTE2(10000000)
	ldi rmp,BYTE1(10000000)
	rcall DisplDecX3
	ldi ZH,BYTE3(1000000) ; 1 mio
	ldi ZL,BYTE2(1000000)
	ldi rmp,BYTE1(1000000)
	rcall DisplDecX3
.IF ! cDisplay8
	ldi rmp,c1kSep ; set separator
	tst R0
	brne Displ4Dec2
	ldi rmp,' '
	Displ4Dec2:
	st X+,rmp
	.ENDIF
	ldi ZH,BYTE3(100000) ; 100 k
	ldi ZL,BYTE2(100000)
	ldi rmp,BYTE1(100000)
	rcall DisplDecX3
	ldi ZH,HIGH(10000) ; 10 k
	ldi ZL,LOW(10000)
	rcall DisplDecX2
	ldi ZH,HIGH(1000) ; 1 k
	ldi ZL,LOW(1000)
	rcall DisplDecX2
.IF ! cDisplay8
	ldi rmp,c1kSep ; set separator
	tst R0
	brne Displ4Dec3
	ldi rmp,' '
	Displ4Dec3:
	st X+,rmp
	.ENDIF
	ldi ZL,100 ; 100
	rcall DisplDecX1
	ldi ZL,10
	rcall DisplDecX1
	ldi rmp,'0' ; 1
	add rmp,R1
	st X+,rmp
	ret
;
; convert a decimal in R3:R2:R1, decimal in ZH:ZL:rmp
;
DisplDecX3:
	clr rDiv1 ; rDiv1 is counter
	clr rDiv2 ; subtractor for byte 4
DisplDecX3a:
	cp rRes1,rmp ; compare
	cpc rRes2,ZL
	cpc rRes3,ZH
	cpc rRes4,rDiv2
	brcs DisplDecX3b ; ended
	sub rRes1,rmp ; subtract
	sbc rRes2,ZL
	sbc rRes3,ZH
	sbc rRes4,rDiv2
	inc rDiv1
	rjmp DisplDecX3a
DisplDecX3b:
	ldi rmp,'0'
	add rmp,rDiv1
	add R0,rDiv1
	tst R0
	brne DisplDecX3c
	ldi rmp,' '
DisplDecX3c:
	st X+,rmp
	ret
;
; convert a decimal in R3:R2:R1, decimal in ZH:ZL
;
DisplDecX2:
	clr rDiv1 ; rDiv1 is counter
	clr rDiv2 ; next byte overflow
DisplDecX2a:
	cp rRes1,ZL
	cpc rRes2,ZH
	cpc rRes3,rDiv2
	brcs DisplDecX2b ; ended
	sub rRes1,ZL ; subtract
	sbc rRes2,ZH
	sbc rRes3,rDiv2
	inc rDiv1
	rjmp DisplDecX2a
DisplDecX2b:
	ldi rmp,'0'
	add rmp,rDiv1
	add R0,rDiv1
	tst R0
	brne DisplDecX2c
	ldi rmp,' '
DisplDecX2c:
	st X+,rmp
	ret
;
; convert a decimal decimal in R2:R1, decimal in ZL
;
DisplDecX1:
	clr rDiv1 ; rDiv1 is counter
	clr rDiv2 ; next byte overflow
DisplDecX1a:
	cp rRes1,ZL
	cpc rRes2,rDiv2
	brcs DisplDecX1b ; ended
	sub rRes1,ZL ; subtract
	sbc rRes2,rDiv2
	inc rDiv1
	rjmp DisplDecX1a
DisplDecX1b:
	ldi rmp,'0'
	add rmp,rDiv1
	add R0,rDiv1
	tst R0
	brne DisplDecX1c
	ldi rmp,' '
DisplDecX1c:
	st X+,rmp
	ret
;
;=================================================
;             Delay routines
;=================================================
;
Delay10ms:
	ldi rDelH,HIGH(10000)
	ldi rDelL,LOW(10000)
	rjmp DelayZ
Delay15ms:
	ldi rDelH,HIGH(15000)
	ldi rDelL,LOW(15000)
	rjmp DelayZ
Delay4_1ms:
	ldi rDelH,HIGH(4100)
	ldi rDelL,LOW(4100)
	rjmp DelayZ
Delay1_64ms:
	ldi rDelH,HIGH(1640)
	ldi rDelL,LOW(1640)
	rjmp DelayZ
Delay100us:
	clr rDelH
	ldi rDelL,100
	rjmp DelayZ
Delay40us:
	clr rDelH
	ldi rDelL,40
	rjmp DelayZ
;
; Delays execution for Z microseconds
;
DelayZ:
.IF cFreq>18000000
	nop
	nop
	.ENDIF
.IF cFreq>16000000
	nop
	nop
	.ENDIF
.IF cFreq>14000000
	nop
	nop
	.ENDIF
.IF cFreq>12000000
	nop
	nop
	.ENDIF
.IF cFreq>10000000
	nop
	nop
	.ENDIF
.IF cFreq>8000000
	nop
	nop
	.ENDIF
.IF cFreq>6000000
	nop
	nop
	.ENDIF
.IF cFreq>4000000
	nop
	nop
	.ENDIF
	sbiw rDelL,1 ; 2
	brne DelayZ ; 2
	ret
;
; =========================================
; Main Program Start
; =========================================
;
main:
	ldi rmp,HIGH(RAMEND) ; set stack pointer
	out SPH,rmp
	ldi rmp,LOW(RAMEND)
	out SPL,rmp
	clr rFlg ; set flags to default
;
.IF debug
.EQU number = 100000000
	ldi rmp,BYTE4(number)
	mov rRes4,rmp
	mov rDiv4,rmp
	ldi rmp,BYTE3(number)
	mov rRes3,rmp
	mov rDiv3,rmp
	ldi rmp,BYTE2(number)
	mov rRes2,rmp
	mov rDiv2,rmp
	ldi rmp,BYTE1(number)
	mov rRes1,rmp
	mov rDiv1,rmp
	rcall CycleM6
beloop:	rjmp beloop
.ENDIF
.IF debugpulse
	.EQU nhigh = 100000000
	.EQU nlow = 15000
	ldi rmp,BYTE4(nhigh)
	sts sCtr+3,rmp
	ldi rmp,BYTE3(nhigh)
	sts sCtr+2,rmp
	ldi rmp,BYTE2(nhigh)
	sts sCtr+1,rmp
	ldi rmp,BYTE1(nhigh)
	sts sCtr,rmp
	ldi rmp,BYTE4(nlow)
	mov rRes4,rmp
	mov rDiv4,rmp
	ldi rmp,BYTE3(nlow)
	mov rRes3,rmp
	mov rDiv3,rmp
	ldi rmp,BYTE2(nlow)
	mov rRes2,rmp
	mov rDiv2,rmp
	ldi rmp,BYTE1(nlow)
	mov rRes1,rmp
	mov rDiv1,rmp
	sbr rFlg,1<<bEdge
	rcall CycleM7
	bploop: rjmp bploop
	.ENDIF
;
; Clear the output storage
;
	ldi ZH,HIGH(sResult)
	ldi ZL,LOW(sResult)
	ldi rmp,' '
	mov R0,rmp
	ldi rmp,32
main1:
	st Z+,R0
	dec rmp
	brne main1
;
; Init the Uart
;
.IF cUart
	rcall UartInit
	ldi rmp,1<<bUMonU ; monitor U over Uart
	sts sUartFlag,rmp
	ldi rmp,20 ; 5 seconds
	sts sUartMonURpt,rmp ; set repeat default value
	ldi rmp,1
	sts sUartMonUCnt,rmp
	.ENDIF
;
; Init the LCD
;
.IF cDisplay
	rcall LcdInit
.ENDIF
;
; Disable the Analog comparator
;
	ldi rmp,1<<ACD
	out ACSR,rmp
;
; Disable the external prescaler by 8
;
	sbi pPrescD,bPresc ; set prescaler port bit to output
	sbi pPresc,bPresc ; disable the prescaler
;
; Init the ADC
;
	ldi rmp,(1<<REFS1)|(1<<REFS0) ; int.ref, channel 0
	out ADMUX,rmp
	ldi rmp,(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; prescaler = 128
	out ADCSRA,rmp
;
; Start main interval timer
;
	ldi rmp,cCmp2 ; set Compare Match
	out OCR2,rmp
	ldi rmp,cPre2|(1<<WGM21) ; CTC mode and prescaler
	out TCCR2,rmp
;
; Start timer/counter TC2 interrupts
;
	ldi rmp,(1<<OCIE2) ; Interrupt mask
	out TIMSK,rmp
;
; Set initial mode to mode 1
;
	ldi rmp,1 ; initial mode = 1
	sts sModeNext,rmp
	rcall SetModeNext
;
	sei ; enable interrupts
;
loop:
	sleep ; send CPU to sleep
	nop
	sbrc rFlg,bCyc ; check cycle end
	rcall Cycle
	sbrc rFlg,bAdc
	rcall Interval
.IF cUart
	sbrc rFlg,bUartRxLine ; check line complete
	rcall UartRxLine ; call line complete
.ENDIF
	rjmp loop ; go to sleep
;
; Timer interval for calculation and display
;
Interval:
	cbr rFlg,1<<bAdc ; clear flag
	lds ZL,sAdc1L ; read ADC channel 1 value
	lds ZH,sAdc1H
	lsl ZL ; multiply by 2
	rol ZH
	subi ZH,-2
	cpi ZH,cModeTimeRpm ; check if new mode to be set
	brcs Interval1
	sts sModeNext,ZH ; selected mode is not frequency, set
	rjmp Interval2
Interval1: ; selected mode is frequency
	lds rmp,sModeNext ; read next mode
	cpi rmp,cModeTimeRpm
	brcs Interval2 ; frequency already selected
	ldi rmp,cModeFrequency
	sts sModeNext,rmp
Interval2:
	lds rmp,sModeNext ; check if mode has to be changed
	cp rMode,rmp
	breq Interval3 ; continue current mode
	rcall SetModeNext ; start new mode
Interval3:
.IF cUart || cDisplay
	lds R0,sAdc0L ; read ADC value
	lds R1,sAdc0H
	rcall cAdc2U ; convert to text
	.IF cDisplay
		rcall LcdDisplayFT
		rcall LcdDisplayU
		.ENDIF
	.IF cUart
		rcall UartMonU
		.ENDIF
	.ENDIF
	ret
;
; Frequency/Time measuring cycle ended, calculate results
;
Cycle:
	sbrc rFlg,bOvf ; check overflow
	rjmp CycleOvf ; jump to overflow
	mov rRes1,rCpy1 ; copy counter
	mov rRes2,rCpy2
	mov rRes3,rCpy3
	mov rRes4,rCpy4
	cbr rFlg,(1<<bCyc)|(1<<bOvf) ; clear cycle flag and overflow
	mov rDiv1,rRes1 ; copy again
	mov rDiv2,rRes2
	mov rDiv3,rRes3
	mov rDiv4,rRes4
	ldi ZH,HIGH(CycleTab) ; point to mode table
	ldi ZL,LOW(CycleTab)
	add ZL,rMode ; displace table by mode
	brcc Cycle1
	inc ZH
Cycle1:
	ijmp ; call the calculation routine
; overflow occurred
CycleOvf:
	cbr rFlg,(1<<bCyc)|(1<<bOvf) ; clear cycle flag and overflow
	ldi XH,HIGH(sResult) ; point to result buffer
	ldi XL,LOW(sResult)
.IF cDisplay8
	ldi ZH,HIGH(2*TxtOvf8) ; point to short message
	ldi ZL,LOW(2*TxtOvf8)
	ldi rmp,8
	.ELSE
	ldi ZH,HIGH(2*TxtOvf16) ; point to long message
	ldi ZL,LOW(2*TxtOvf16)
	ldi rmp,16
	.ENDIF
CycleOvf1:
	lpm
	adiw ZL,1
	st X+,R0
	dec rmp
	brne CycleOvf1
	ret
;
.IF cDisplay8
	TxtOvf8:
	.DB "  ovflow"
	.ELSE
	TxtOvf16:
	.DB "  overflow      "
	.ENDIF
; Table with routines for the 8 modes
CycleTab:
	rjmp CycleM0
	rjmp CycleM1
	rjmp CycleM2
	rjmp CycleM3
	rjmp CycleM4
	rjmp CycleM5
	rjmp CycleM6
	rjmp CycleM7
	rjmp CycleM8
	ret ; voltage only
;
; Mode 0: Measured prescaled frequency, display frequency
;
CycleM0:
	mov rmp,rRes3 ; check frequency below 2 MHz
	or rmp,rRes4
	brne CycleM0a
	dec rNMode ; count mode counter down
	brne CycleM0a
	ldi rNMode,cNMode ; restart mode counter
	ldi rmp,1 ; switch to mode 1
	sts sModeNext,rmp
CycleM0a:
	clr R5 ; detect overflow in R5
	lsl R1 ; * 2
	rol R2
	rol R3
	rol R4
	rol R5
	lsl R1 ; * 4
	rol R2
	rol R3
	rol R4
	rol R5
	lsl R1 ; * 8
	rol R2
	rol R3
	rol R4
	rol R5
	lsl R1 ; * 16
	rol R2
	rol R3
	rol R4
	rol R5
	lsl R1 ; * 32
	rol R2
	rol R3
	rol R4
	rol R5
	tst R5 ; check overflow
	breq CycleM0b ; no error
	rjmp CycleOvf
CycleM0b:
	rcall Displ4Dec
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'H'
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	ldi rmp,' '
	st X,rmp
	.ENDIF
	ldi ZL,'F'
	rjmp DisplMode
;
; Mode 1: Frequency measured, prescale = 1, display frequency
;
CycleM1:
	ldi rmp,0 ; switch to mode 0?
	tst rRes4
	brne CycleM1a ; switch to mode 0
	mov rmp,rRes3
	cpi rmp,0x26 ; compare > 10 MHz
	ldi rmp,0
	brcc CycleM1a
	tst rRes3 ; check smaller than 1 kHz
	brne CycleM1b
	tst rRes2
	brne CycleM1b
	ldi rmp,2 ; switch to mode 2
CycleM1a:
	dec rNMode ; count modes
	brne CycleM1b
	ldi rNMode,cNMode
	sts sModeNext,rmp
CycleM1b:
	clr rDiv1 ; detect overflow in rDiv1
	lsl rRes1 ; * 2
	rol rRes2
	rol rRes3
	rol rRes4
	rol rDiv1
	lsl rRes1 ; * 4
	rol rRes2
	rol rRes3
	rol rRes4
	rol rDiv1
	tst rDiv1 ; check overflow
	breq CycleM1d ; no error
	rjmp CycleOvf
CycleM1d:
	rcall Displ4Dec
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'H'
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	ldi rmp,' '
	st X,rmp
	.ENDIF
	ldi ZL,'f'
	rjmp DisplMode
;
; Mode 2: Time measured, prescale = 1, display frequency
;
CycleM2:
	mov rmp,rDiv2 ; check too small
	andi rmp,0xC0 ; isolate upper six bits
	or rmp,rDiv3
	or rmp,rDiv4
	brne CycleM2a
	dec rNMode ; count mode change counter down
	brne CycleM2a
	ldi rNMode,cNMode ; restart counter
	ldi rmp,cModeFrequency ; switch to frequency mode
	sts sModeNext,rmp
CycleM2a:
	rcall Divide
	tst rRes4
	brne CycleM2e
	rcall DisplFrac
	ldi ZL,'v'
	rcall DisplMode
	ret
CycleM2e:
	mov rRes1,rRes2 ; number too big, skip fraction
	mov rRes2,rRes3
	mov rRes3,rRes4
	clr rRes4
	rcall Displ4Dec
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'H'
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	ldi rmp,' '
	st X,rmp
	.ENDIF
	ldi ZL,'v'
	rcall DisplMode
	ret
;
; Measure time, display rounds per minute
;
CycleM3:
	rcall Divide
	clr R0 ; overflow detection
	clr rmp
	lsl rRes1 ; * 2
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	lsl rRes1 ; * 4
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	mov rDiv1,rRes1 ; copy
	mov rDiv2,rRes2
	mov rDiv3,rRes3
	mov rDiv4,rRes4
	lsl rRes1 ; * 8
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	lsl rRes1 ; * 16
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	lsl rRes1 ; * 32
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	lsl rRes1 ; * 64
	rol rRes2
	rol rRes3
	rol rRes4
	adc R0,rmp
	tst R0 ; overflow?
	breq CycleM3a
	rjmp CycleOvf
CycleM3a:
	sub rRes1,rDiv1
	sbc rRes2,rDiv2
	sbc rRes3,rDiv3
	sbc rRes4,rDiv4
	mov rRes1,rRes2
	mov rRes2,rRes3
	mov rRes3,rRes4
	clr rRes4
	rcall Displ4Dec
.IF ! cDisplay8
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'r'
	st X+,rmp
	ldi rmp,'p'
	st X+,rmp
	ldi rmp,'m'
	st X+,rmp
	.ENDIF
	ldi ZL,'u'
	rcall DisplMode
	ret
;
; Measure time high+low, display time
;
CycleM4:
	rcall Multiply
	rcall Displ4Dec
	rcall DisplSec
	ldi ZL,'t'
	rcall DisplMode
	ret
;
; Measure time high, display time
;
CycleM5:
	sbrs rFlg,bEdge
	rjmp CycleM5a
	rcall Multiply
	rcall Displ4Dec
	rcall DisplSec
	ldi ZL,'h'
	rcall DisplMode
CycleM5a:
	ret
;
; Measure time low, display time
;
CycleM6:
	sbrc rFlg,bEdge
	rjmp CycleM6a
	rcall Multiply
	rcall Displ4Dec
	rcall DisplSec
	ldi ZL,'l'
	rcall DisplMode
CycleM6a:
	ret
;
; Measure time high and low, display pulse width ratio high in %
;   if the edge was negative, store the measured time, if positive calculate
;   rRes and rDiv hold the active low time, sCtr the last active high time
;   to CalcPw: rDelH:rDelL:R0:rmp = active high time
;
CycleM7:
	sbrs rFlg,bEdge
	rjmp CycleM7a
	ldi ZH,HIGH(sCtr) ; edge is high, calculate
	ldi ZL,LOW(sCtr)
	ld rRes1,Z+ ; copy counter value
	ld rRes2,Z+
	ld rRes3,Z+
	ld rRes4,Z+
	add rDiv1,rRes1 ; add to total time
	adc rDiv2,rRes2
	adc rDiv3,rRes3
	adc rDiv4,rRes4
	brcs CycleM7b
	mov rmp,rRes1 ; copy high value to divisor
	mov R0,rRes2
	mov rDelL,rRes3
	mov rDelH,rRes4
	rcall CalcPw ; calculate the ratio
	brcs CycleM7b ; error
	rcall DisplPw ; display the ratio
	ldi ZL,'P'
	rjmp DisplMode
CycleM7a:
	ldi ZH,HIGH(sCtr)
	ldi ZL,LOW(sCtr)
	st Z+,rRes1 ; copy counter value
	st Z+,rRes2
	st Z+,rRes3
	st Z+,rRes4
	ret
CycleM7b: ; overflow
	ldi rmp,'P'
	rjmp PulseOvFlw
;
; Measure time high and low, display pulse width ratio low in %
;   if the edge was negative, store the measured time, if positive calculate
;   rRes and rDiv hold the active low time, sCtr the last active high time
;   to CalcPw: rDelH:rDelL:R0:rmp = active low time
;
CycleM8:
	sbrs rFlg,bEdge
	rjmp CycleM8a
	ldi ZH,HIGH(sCtr) ; edge is high, calculate
	ldi ZL,LOW(sCtr)
	ld rmp,Z+ ; read high-time
	ld R0,Z+
	ld rDelL,Z+
	ld rDelH,Z
	add rDiv1,rmp ; add to total time
	adc rDiv2,R0
	adc rDiv3,rDelL
	adc rDiv4,rDelH
	mov rmp,rRes1 ; copy the active low time
	mov R0,rRes2
	mov rDelL,rRes3
	mov rDelH,rRes4
	rcall CalcPw ; calculate the ratio
	brcs CycleM8b ; error
	rcall DisplPw ; display the ratio
	ldi ZL,'p'
	rjmp DisplMode
CycleM8a:
	ldi ZH,HIGH(sCtr)
	ldi ZL,LOW(sCtr)
	st Z+,rRes1 ; copy counter value
	st Z+,rRes2
	st Z+,rRes3
	st Z+,rRes4
	ret
CycleM8b: ; overflow
	ldi rmp,'p'
	rjmp PulseOvFlw
;
; Converts an ADC value in R1:R0 to a voltage for display
;   cAdc2U  input: ADC value, output: Voltage in V for display
;
cAdc2U:
	clr R2 ; clear the registers for left shift in R3:R2
	clr R3
	ldi rmp,HIGH(cMultiplier) ; Multiplier to R5:R4
	mov R5,rmp
	ldi rmp,LOW(cMultiplier)
	mov R4,rmp
	clr XL ; clear result in ZH:ZL:XH:XL
	clr XH
	clr ZL
	clr ZH
cAdc2U1:
	lsr R5 ; shift Multiplier right
	ror R4
	brcc cAdc2U2 ; bit is zero, don't add
	add XL,R0 ; add to result
	adc XH,R1
	adc ZL,R2
	adc ZH,R3
cAdc2U2:
	mov rmp,R4 ; check zero
	or rmp,R5
	breq cAdc2U3 ; end of multiplication
	lsl R0 ; multiply by 2
	rol R1
	rol R2
	rol R3
	rjmp cAdc2U1 ; go on multipying
cAdc2U3:
	ldi rmp,$80 ; round up
	add XL,rmp
	ldi rmp,$00
	adc XH,rmp
	adc ZL,rmp
	adc ZH,rmp
	tst ZH ; check overflow
	mov R1,XH ; copy result to R2:R1
	mov R2,ZL
	ldi XH,HIGH(sResult+16) ; point to result
	ldi XL,LOW(sResult+16)
	ldi rmp,'U'
	st X+,rmp
	breq cAdc2U5
	ldi ZH,HIGH(2*AdcErrTxt)
	ldi ZL,LOW(2*AdcErrTxt)
cAdc2U4:
	lpm
	tst R0
	breq cAdc2U6
	sbiw ZL,1
	st X+,R0
	rjmp cAdc2U4
cAdc2U5:
	clr R0
	ldi ZH,HIGH(10000)
	ldi ZL,LOW(10000)
	rcall DecConv
	inc R0
	ldi ZH,HIGH(1000)
	ldi ZL,LOW(1000)
	rcall DecConv
	ldi rmp,cDecSep
	st X+,rmp
	clr ZH
	ldi ZL,100
	rcall DecConv
	ldi ZL,10
	rcall DecConv
	ldi rmp,'0'
	add rmp,R1
	st X+,rmp
	ldi rmp,'V'
	st X,rmp
	lds rmp,sResult+17
	cpi rmp,' '
	brne cAdc2U6
	ldi rmp,'='
	sts sResult+17,rmp
cAdc2U6:
	ret
;
AdcErrTxt:
.DB	"overflw",$00
;
; ===========================================
; Lcd display routines
; ===========================================
;
.IF cDisplay ; if display connected
;
; LcdE pulses the E output for at least 1 us
;
LcdE:
	sbi PORTB,bLcdE
	.IF cFreq>14000000
		nop
		nop
		.ENDIF
	.IF cFreq>12000000
		nop
		nop
		.ENDIF
	.IF cFreq>10000000
		nop
		nop
		.ENDIF
	.IF cFreq>8000000
		nop
		nop
		.ENDIF
	.IF cFreq>6000000
		nop
		nop
		.ENDIF
	.IF cFreq>4000000
		nop
		nop
		.ENDIF
	.IF cFreq>2000000
		nop
		nop
		.ENDIF
	nop
	nop
	cbi PORTB,bLcdE
	ret
;
; outputs the content of rmp (temporary
; 8-Bit-Interface during startup)
;
LcdRs8:
	out PORTB,rmp
	rcall LcdE
	ret
;
; write rmp as 4-bit-command to the LCD
;
LcdRs4:
	mov R0,rmp ; copy rmp
	swap rmp ; upper nibble to lower nibble
	andi rmp,0x0F ; clear upper nibble
	out PORTB,rmp ; write to display interface
	rcall LcdE ; pulse E
	mov rmp,R0 ; copy original back
	andi rmp,0x0F ; clear upper nibble
	out PORTB,rmp ; write to display interface
	rcall LcdE
	mov rmp,R0 ; restore rmp
	ret
;
; write rmp as data over 4-bit-interface to the LCD
;
LcdData4:
	push rmp ; save rmp
	mov rmp,R0 ; copy rmp
	swap rmp ; upper nibble to lower nibble
	andi rmp,0x0F ; clear upper nibble
	sbr rmp,1<<bLcdRs ; set Rs to one
	out PORTB,rmp ; write to display interface
	rcall LcdE ; pulse E
	mov rmp,R0 ; copy original again
	andi rmp,0x0F ; clear upper nibble
	sbr rmp,1<<bLcdRs ; set Rs to one
	out PORTB,rmp ; write to display interface
	rcall LcdE
	rcall Delay40us
	pop rmp ; restore rmp
	ret
;
; writes the text in flash to the LCD, number of
; characters in rmp
;
LcdText:
	lpm ; read character from flash
	adiw ZL,1
	rcall LcdData4 ; write to 
	rcall delay40us
	dec rmp
	brne LcdText
	ret
;
; Inits the LCD with a 4-bit-interface
;
LcdInit:
	ldi rmp,0x0F | (1<<bLcdE) | (1<<bLcdRs)
	out DDRB,rmp
	clr rmp
	out PORTB,rmp
	rcall delay15ms ; wait for complete self-init
	ldi rmp,0x03 ; Function set 8-bit interface
	rcall LcdRs8
	rcall delay4_1ms ; wait for 4.1 ms
	ldi rmp,0x03 ; Function set 8-bit interface
	rcall LcdRs8
	rcall delay100us ; wait for 100 us
	ldi rmp,0x03 ; Function set 8-bit interface
	rcall LcdRs8
	rcall delay40us ; delay 40 us
	ldi rmp,0x02 ; Function set 4-bit-interface
	rcall LcdRs8
	rcall delay40us
	.IF cDisplay2
		ldi rmp,0x28 ; 4-bit-interface, two line display
		.ELSE
		ldi rmp,0x20 ; 4-bit-interface, single line display
		.ENDIF
	rcall LcdRs4
	rcall delay40us ; delay 40 us
	ldi rmp,0x08 ; display off
	rcall LcdRs4
	rcall delay40us ; delay 40 us
	ldi rmp,0x01 ; display clear
	rcall LcdRs4
	rcall delay1_64ms ; delay 1.64 ms
	ldi rmp,0x06 ; increment, don't shift
	rcall LcdRs4
	rcall delay40us ; delay 40 us
	ldi rmp,0x0C ; display on
	rcall LcdRs4
	rcall delay40us
	ldi rmp,0x80 ; position on line 1
	rcall LcdRs4
	rcall delay40us ; delay 40 us
	.IF cDisplay8
		ldi rmp,8
		ldi ZH,HIGH(2*LcdInitTxt8)
		ldi ZL,LOW(2*LcdInitTxt8)
		.ELSE
		ldi rmp,16
		ldi ZH,HIGH(2*LcdInitTxt16)
		ldi ZL,LOW(2*LcdInitTxt16)
		.ENDIF
	rcall LcdText
	.IF cDisplay2
		ldi rmp,0xC0 ; line 2
		rcall LcdRs4
		rcall delay40us ; delay 40 us
		.IF cDisplay8
			ldi rmp,8
			.ELSE
			ldi XH,HIGH(sResult+25)
			ldi XL,LOW(sResult+25)
			ldi ZH,HIGH(2*LcdInitTxtMode)
			ldi ZL,LOW(2*LcdInitTxtMode)
			ldi rmp,6
			LcdInitMode:
				lpm
				adiw ZL,1
				st X+,R0
				dec rmp
				brne LcdInitMode
			ldi rmp,16
			.ENDIF
		rcall LcdText
		.ENDIF
	ret
.IF cDisplay8
	LcdInitTxt8:
	.DB "F-CNT V1"
	.IF cDisplay2
		.DB "-DG4FAC-"
		.ENDIF
	.ELSE
	LcdInitTxt16:
	.DB "Freq-counter V01"
	.IF cDisplay2
		.DB " (C)2005 DG4FAC "
		LcdInitTxtMode:
		.DB " Mode="
		.ENDIF
	.ENDIF
;
; Display frequency/time on Lcd
;
LcdDisplayFT:
.IF ! cDisplay2 ; single line display
	cpi rMode,cModeVoltage ; voltage display selected?
	breq LcdDisplayFT2
	.ENDIF
	ldi rmp,$80 ; set display position to line 1
	rcall LcdRs4
	rcall Delay40us
	ldi ZH,HIGH(sResult) ; point Z to line buffer
	ldi ZL,LOW(sResult)
.IF cDisplay8
	ldi rmp,8
	.ELSE
	ldi rmp,16
	.ENDIF
LcdDisplayFT1:
	ld R0,Z+ ; read a char
	rcall LcdData4 ; display on LCD
	dec rmp
	brne LcdDisplayFT1
LcdDisplayFT2:
	ret
;
; Display voltage on the display
;
LcdDisplayU:
.IF cDisplay2 ; two-line LCD connected
	.IF !cDisplay8
		lds rmp,sModeNext
		subi rmp,-'0'
		sts sResult+31,rmp
		.ENDIF
	ldi rmp,$C0 ; output to line 2
	.ELSE
	cpi rMode,cModeVoltage ; check switch
	brne LcdDisplayU2
	ldi rmp,$80 ; output to line 1
	.ENDIF
	rcall LcdRs4 ; set output position
	rcall Delay40us
	ldi ZH,HIGH(sResult+16) ; point to result
	ldi ZL,LOW(sResult+16)
.IF cDisplay8
	ldi rmp,8
	.ELSE
	ldi rmp,16
	.ENDIF
LcdDisplayU1:
	ld R0,Z+ ; read character
	rcall LcdData4
	dec rmp ; next char
	brne LcdDisplayU1 ; continue with chars
LcdDisplayU2:
	ret
;
.ENDIF ; end LCD routines to be included
;
; ===========================================
;   Uart routines
; ===========================================
;
.IF cUart
UartInit: ; Init the Uart on startup
.EQU cUbrr = (cFreq/cBaud/16)-1 ; calculating UBRR single speed
	ldi rmp,LOW(sUartRxBs) ; set buffer pointer to start
	sts sUartRxBp,rmp
	ldi rmp,HIGH(cUbrr) ; set URSEL to zero, set baudrate msb
	out UBRRH,rmp
	ldi rmp,LOW(cUbrr) ; set baudrate lsb
	out UBRRL,rmp
	ldi rmp,(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0) ; set 8 bit characters
	out UCSRC,rmp
	ldi rmp,(1<<RXCIE)|(1<<RXEN)|(1<<TXEN) ; enable RX/TX and RX-Ints
	out UCSRB,rmp
	rcall delay10ms ; delay for 10 ms duration
	ldi ZH,HIGH(2*txtUartInit)
	ldi ZL,LOW(2*txtUartInit)
	rjmp UartSendTxt
;
UartRxLine:
	cbr rFlg,1<<bUartRxLine ; clear line complete flag
	ldi ZH,HIGH(2*txtUartCursor)
	ldi ZL,LOW(2*txtUartCursor)
	rjmp UartSendTxt
;
; Send character in rmp over Uart
;
UartSendChar:
	sbis UCSRA,UDRE ; wait for empty buffer
	rjmp UartSendChar
	out UDR,rmp
	ret
;
; Monitoring the voltage over the Uart
;
UartMonU:
	lds rmp,sUartFlag ; flag register for Uart
	sbrs rmp,bUMonU ; displays voltage over Uart
	ret
	lds rmp,sUartMonUCnt ; read counter
	dec rmp
	sts sUartMonUCnt,rmp
	brne UartMonU2
	lds rmp,sUartMonURpt
	sts sUartMonUCnt,rmp
	ldi ZH,HIGH(sResult+16)
	ldi ZL,LOW(sResult+16)
	ldi rmp,8
UartMonU1:
	sbis UCSRA,UDRE ; wait for empty buffer
	rjmp UartMonU1
	ld R0,Z+
	out UDR,R0
	dec rmp
	brne UartMonU1
	ldi rmp,cCr
	rcall UartSendChar
	ldi rmp,cLf
	rjmp UartSendChar
UartMonU2:
	ret
;
; Send text from flash to UART, null byte ends transmit
;
UartSendTxt:
	lpm ; read character from flash
	adiw ZL,1
	tst R0 ; check end of text
	breq UartSendTxtRet
UartSendTxtWait:
	sbis UCSRA,UDRE ; wait for empty char
	rjmp UartSendTxtWait
	out UDR,R0 ; send char
	rjmp UartSendTxt
UartSendTxtRet:
	ret
;
; Uart text constants
;
txtUartInit:
.DB " ",cClrScr
.DB "************************************************* ",cCr,cLf
.DB "* Frequency- and voltmeter (C)2005 by g.schmidt * ",cCr,cLf
.DB "************************************************* ",cCr,cLf
txtUartMenue:
.DB cCr,cLf,"Commands: elp",cCr,cLf
txtUartCursor:
.DB cCr,cLf,"i> ",cNul
.ENDIF
;
; End of source code
;


©2006 by http://www.avr-asm-tutorial.net