Pfad: Home => AVR-Overview => Applications => Signal Generator => Source Main
Signal Generator small

Signal generator with ATmega8 - Source code Main program


;
; ***************************************************
; * Adjustable Rectangle Generator with an ATmega8  *
; * Frequency (0.25Hz..8MHz) and Pulse Width (0.00..*
; * 100.00%) variable, LCD display (selectable sing-*
; * le/dual line, 16..40 characters per line), dis- *
; * plays frequency or rounds per minute and pulse  *
; * width in %                                      *
; * Requires include files "rectgen_m8_table.inc"   *
; * and "Lcd8_02WO_rec.inc"                         *
; * Version 1 as of April 21, 2006                  *
; * (C)2006 by info!at!avr-asm-tutorial.net         *
; ***************************************************
;
.NOLIST
.INCLUDE "m8def.inc"
.LIST
;
; Debug infos
;
.EQU dbgOn = 0 ; debugging on or off
.IF dbgOn
	.EQU cAdc0 = 1000 ; ADC0 value
	.EQU cAdc1 = 511 ; ADC1 value
	.EQU cFlg = 0
	;.EQU cFlg = (1<<bTime)
	;.EQU cFlg = (1<<bRpm)
	;.EQU cFlg = (1<<bPw)
	.ENDIF
.EQU dbghx = 0 ; debug hex output on LCD
;
; Hardware
;              ___________
;       __ 10k/           |      _
;+5V O-|__|--|RESET    PC5|--O--O O--| Display Pulsewidth
;            |            |      _  (Single line LCD only!)
;  LCD D0 O--|PD0      PC4|--O--O O--| Inverse signal
;            |            |      _
;  LCD D1 O--|PD1      PC3|--O--O O--| Display RPM
;            |            |      _
;  LCD D2 O--|PD2      PC2|--O--O O--| Display Time
;            |            |
;  LCD D3 O--|PD3     ADC1|--O 0..5V Pulse Width adjust
;            |    A T     |
;  LCD D4 O--|PD4     ADC0|--O 0..5V Freq adjust
;            |   mega     |
;     +5V O--|VCC      GND|--O GND
;            |     8      |    10nF
;     GND O--|GND     AREF|--O--||--| AREF 
;            |            |      ___ 22H/10nF
;   XTAL1 O--|XTAL1   AVCC|--O--|___|--O +5V
;            |            |
;   XTAL2 O--|XTAL2 PB5SCK|--O LCD R/W, SCK
;            |            |
;  LCD D5 O--|PD5  PB4MISO|--O LCD RS, MISO
;            |            |
;  LCD D6 O--|PD6  PB3MOSI|--O MOSI
;            |            |
;  LCD D7 O--|PD7  PB2OC1B|--O Output B
;            |            |
;   LCD E O--|PB0  PB1OC1A|--O Output A
;            |____________|
;
;
; Timings
; -------
;
; TC0: prescaler = 1024, overflow interrupt, @16MHz:
;      16.384 ms, counts down from 30 to 0, if zero
;      (491.52 ms) starts ADC conversion cycle
;
; ADC channels 1 and 2: conversions startet by TC0,
;      clock prescaler = 128, @16MHz, first complete
;      interrupt after 200 us, second interrupt after
;      104 us, ADC is diabled and complete flag is set
;      after two complete conversions
;
; ADC complete: reads value for setting ICR1-CTC from
;      table entry for ADC0, calculates COMPA/COMPB
;      value from CTC-value and ADC1, sets TC1-ICR1
;      and COMPA/COMPB values, updates TC1-prescaler
;      and TC1-output-polarity-modes
;
; TC1: Fast PWM mode/WGM=14, prescaler = 1..1024 (de-
;      pending from selected frequency), ICR1 deter-
;      mines TOP, COMPA/B determines phase modulated
;      inversion of output signals
;
; **************************************************
;             D E F I N I T I O N S
; **************************************************
;
; Constants
;
.EQU clock = 16000000 ; clock frequency
.EQU cDivF5 = $00 ; = clock * 100 (5 Bytes)
.EQU cDivF4 = $5F
.EQU cDivF3 = $5E
.EQU cDivF2 = $10
.EQU cDivF1 = $00
.EQU cDivU5 = $16 ; = clock * 100 * 60 (5 Bytes)
.EQU cDivU4 = $5A
.EQU cDivU3 = $0B
.EQU cDivU2 = $C0
.EQU cDivU1 = $00
.EQU cLcdLw = 24 ; Line width of the LCD
.EQU cLcdLn = 2 ; number of lines of the LCD
;.EQU cLcdMicro = $E4 ; micro character on LCD, doesn't work on L2432
.EQU cLcdMicro = 'u'
.EQU cEn = 1 ; english or german version
;
; Dependant constants
;
.EQU cTMult = (256000000/clock)*100
.IF cEn
.EQU c1000s = $2C ; komma
.EQU cDecs = $2E ; dot
.ELSE
.EQU c1000s = $2E ; dot
.EQU cDecs = $2C ; komma
.ENDIF
;
; Registers
;
; used: R0..R13 for calculations
; free: R14
.DEF rSreg = R15 ; Status save register
.DEF rmp = R16 ; Multipurpose register outside Ints
.DEF rimp = R17 ; Multipurpose register inside Ints
.DEF rFlg = R18 ; flag register
	.EQU bTime = 0 ; display time instead of frequency
	.EQU bRpm = 1 ; display RPM instead of frequency
	.EQU bInv = 2 ; invert the output signal
	.EQU bAdc0 = 3 ; ADC channel 0 complete flag
	.EQU bAdc1 = 4 ; ADC channel 1 complete flag
	.IF cLcdLn==1
		.EQU bPw = 7 ; display Pulsewidth on line 1 of single line LCD
		.ENDIF
.DEF rAdc0L = R19 ; LSB last ADC0 result
.DEF rAdc0H = R20 ; dto., MSB
.DEF rAdc1L = R21 ; LSB last ADC1 result
.DEF rAdc1H = R22 ; dto., MSB
.DEF rAdcC = R23 ; counter for delaying ADC start cycle
; free: R24..R25
; used: X (R27:R26) for calculations
; free: Y (R29:R28)
; used: Z (R31:R30) for calculations
;
; Ports
;
; LCD-Ports
.EQU pLcdData = PORTD
.EQU pLcdCtrl = PORTB
.EQU pbLcdE = 0
.EQU pbLcdRs = 4
.EQU pbLcdRw = 5
; Key Ports
.EQU pSwtchOut = PORTC
.EQU pSwtchIn = PINC
.EQU pbTime = 2
.EQU pbRpm = 3
.EQU pbInv = 4
.EQU pbPwm = 5
; Signal outputs
.EQU pSignOut = PORTB
.EQU pSignDdr = DDRB
.EQU pbSignA = 1
.EQU pbSignB = 2
;
; SRAM positions
;
.DSEG
.ORG $0060
sCtc: ; CTC divider TC1
.BYTE 2
sCmp: ; COMPA/B pulse width TC1
.BYTE 2
sPre: ; Prescaler TC1
.BYTE 1
sMode: ; mode TC1
.BYTE 1
sLcdL1: ; Line 1 of the LCD
.BYTE cLcdLw
.IF cLcdLn>1
sLcdL2: ; Line 2 of the LCD
.BYTE cLcdLw
.ENDIF
;
; **************************************************
;       R E S E T - and I N T - V E C T O R S
; **************************************************
;
; Code segment
.CSEG
.ORG $0000
;
	rjmp main ; Reset vector, jump to main loop
	reti ; INT0, not used
	reti ; INT1, not used
	reti ; TIMER2COMP, not used
	reti ; TIMER2OVF, not used
	reti ; TIMER1CAPT, not used
	reti ; TIMER1COMPA, not used
	reti ; TIMER1COMPB, not used
	reti ; TIMER1OVF
	rjmp TC0OvflwInt ; TIMER0OVF
	reti ; SPI, STC, not used
	reti ; USART, RXC, not used
	reti ; USART, UDRE, not used
	reti ; USART, TXC, not used
	rjmp AdcInt ; ADC
	reti ; EE_RDY, not used
	reti ; ANA_COMP, not used
	reti ; TWI, not used
	reti ; SPM_RDY, not used
;
; **************************************************
;      I N T - V E C T O R - R O U T I N E S
; **************************************************
;
; TC0 Overflow Interrupt, starts ADC update cycle
;
TC0OvflwInt:
	in rSreg,SREG ; save status
	dec rAdcC ; downcount start cycle counter
	brne TC0OvflwInt1
	ldi rAdcC,30 ; restart counter
	ldi rimp,(1<<REFS0) ; set ADMUX to channel 0
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; start conversion channel 0
	cbr rFlg,1<<bAdc0 ; clear channel 0 complete flag
TC0OvflwInt1:
	out SREG,rSreg ; restore status
	reti
;
; ADC Ready Interrupt, reads ADC value
;
AdcInt:
	in rSreg,SREG ; save status
	sbrc rFlg,bAdc0 ; channel 0 completed?
	rjmp AdcInt1 ; yes, start next channel
	in rAdc0L,ADCL ; read channel 0
	in rAdc0H,ADCH
	ldi rimp,(1<<REFS0)|(1<<MUX0) ; set channel 1
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; start conversion channel 1
	sbr rFlg,1<<bAdc0 ; set channel 0 complete flag
	out SREG,rSreg ; restore status
	reti
AdcInt1:
	in rAdc1L,ADCL ; read channel 1
	in rAdc1H,ADCH
	ldi rimp,(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; disable ADC
	out ADCSRA,rimp
	sbr rFlg,1<<bAdc1 ; set channel 1 complete flag
	out SREG,rSreg ; restore status
	reti
;
; **************************************************
;             S U B R O U T I N E S
; **************************************************
;
; Reads the current CTC and prescaler value and calculates
; the rate in R7:R6:R5:R4
;
GetRate:
	lds rmp,sPre ; read prescaler to rmp
	andi rmp,(1<<CS12)|(1<<CS11)|(1<<CS10)
	breq GetRate2 ; Prescaler is zero, set carry flag
	lds R4,sCtc ; read divider to R7:R6:R5:R4
	lds R5,sCtc+1
	clr R6
	clr R7
	dec rmp
	breq GetRate1 ; prescaler = 1
	lsl R4 ; divider * 2
	rol R5
	rol R6
	rol R7
	lsl R4 ; divider * 4
	rol R5
	rol R6
	rol R7
	lsl R4 ; divier * 8
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; prescaler = 8
	lsl R4 ; divier * 16
	rol R5
	rol R6
	rol R7
	lsl R4 ; divier * 32
	rol R5
	rol R6
	rol R7
	lsl R4 ; divier * 64
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; prescaler = 64
	lsl R4 ; divier * 128
	rol R5
	rol R6
	rol R7
	lsl R4 ; divier * 256
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; prescaler = 256
	lsl R4 ; divier * 512
	rol R5
	rol R6
	rol R7
	lsl R4 ; divier * 1024
	rol R5
	rol R6
	rol R7
GetRate1: ; Timer is active, clear carry
	clc
	ret
GetRate2: ; Timer is inactive, set carry
	sec
	ret
;
; Calculate display text from current timer params
;
Calc:
	sbrc rFlg,bTime ; display time?
	rjmp CalcTime ; yes, move there
	clr R8 ; clear division result in R11:R10:R9:R8
	inc R8 ; set last bit as counter
	clr R9
	clr R10
	clr R11
	rcall GetRate
	brcs L1Lcd ; Timer is off, frequency = 0
	clr R3 ; set divisor in R3:R2:R1:R0:ZH:ZL:XH:XL
	clr R2
	clr R1
	sbrc rFlg,bRpm ; display RPM?
	rjmp Calc1 ; yes
	ldi rmp,cDivF5 ; display frequency
	mov R0,rmp
	ldi ZH,cDivF4
	ldi ZL,cDivF3
	ldi XH,cDivF2
	ldi XL,cDivF1
	rjmp Calc2
Calc1:
	ldi rmp,cDivU5 ; display RPM
	mov R0,rmp
	ldi ZH,cDivU4
	ldi ZL,cDivU3
	ldi XH,cDivU2
	ldi XL,cDivU1
Calc2:
	lsl XL ; * 2
	rol XH
	rol ZL
	rol ZH
	rol R0
	rol R1
	rol R2
	rol R3
	cp R0,R4 ; compare
	cpc R1,R5
	cpc R2,R6
	cpc R3,R7
	brcs Calc3
	sub R0,R4 ; subtract
	sbc R1,R5
	sbc R2,R6
	sbc R3,R7
	sec
	rjmp Calc4
Calc3:
	clc
Calc4:
	rol R8 ; roll result bit in
	rol R9
	rol R10
	rol R11
	brcc Calc2 ; repeat 32 times
	lsl ZH ; round up?
	rol R0
	rol R1
	rol R2
	rol R3
	cp R0,R4
	cpc R1,R5
	cpc R2,R6
	cpc R3,R7
	brcs Calc5
	ldi rmp,1
	add R8,rmp
	ldi rmp,0
	adc R9,rmp
	adc R10,rmp
	adc R11,rmp
Calc5:
;
; Display the result in R11:R10:R9:R8 on line 1
;
L1Lcd:
	clr R0 ; R0 is leading blank flag
	ldi ZH,HIGH(2*DecTab32) ; point to decimal table
	ldi ZL,LOW(2*DecTab32)
	ldi XH,HIGH(sLcdL1) ; X points to LCD line
	ldi XL,LOW(sLcdL1)
	ldi rmp,'F' ; display frequency
	sbrc rFlg,bRpm
	ldi rmp,'R' ; display RPM
	sbrc rFlg,bTime
	ldi rmp,'T' ; display Time
	st X+,rmp ; set first char
.IF cLcdLw>16
	ldi rmp,' ' ; add a blank
	st X+,rmp
	ldi rmp,'=' ; add =
	st X+,rmp
	rcall Dec32 ; write 10 millions
.ELSE
	rcall Dec32 ; write ten millions
	ld rmp,-X ; read char last position
	cpi rmp,' ' ; is it a blank?
	brne L1Lcd00
	ldi rmp,'=' ; add =
	st X,rmp
L1Lcd00:
	adiw XL,1
.ENDIF
	rcall Dec32 ; write millions
	ldi rmp,c1000s
	tst R0
	brne L1Lcd0a
	ldi rmp,' '
L1Lcd0a:
	st X+,rmp
	rcall Dec32 ; write 100,000's
	rcall Dec32 ; write 10,000's
	rcall Dec32 ; write 1,000's
	ldi rmp,c1000s
	tst R0
	brne L1Lcd0b
	ldi rmp,' '
L1Lcd0b:
	st X+,rmp
	rcall Dec32 ; write 100's
	rcall Dec32 ; write 10's
	inc R0 ; set leading blanks off
	rcall Dec32 ; write 1's
	tst R8 ; display decimals?
	breq L1Lcd1
	ldi rmp,cDecs ; set decimal char
	st X+,rmp
	rcall Dec32 ; write 0.1's
	ldi rmp,'0'
	add rmp,R8 ; write 0.01's
	st X+,rmp
	rjmp L1Lcd2
L1Lcd1:
	ldi rmp,' ' ; set decimals blank
	st X+,rmp
	st X+,rmp
	st X+,rmp
L1Lcd2:
.IF cLcdLw>16
	ldi rmp,' ' ; add another blank
	st X+,rmp
.ENDIF
	sbrc rFlg,bTime ; display time?
	rjmp L1Lcd4 ; yes
	sbrc rFlg,bRpm ; diplay RPM?
	rjmp L1Lcd3 ; yes
	ldi rmp,'H' ; display frequency
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	rjmp L1Lcd5
L1Lcd3:
	ldi rmp,'p' ; display rpm
	st X+,rmp
	ldi rmp,'m'
	st X+,rmp
	rjmp L1Lcd5
L1Lcd4:
	ldi rmp,cLcdMicro
	st X+,rmp
	ldi rmp,'s'
	st X+,rmp
L1Lcd5:
.IF cLcdLw>16
	ldi rmp,' '
	mov R0,rmp
	ldi rmp,cLcdLw-19
L1Lcd6:
	st X+,R0
	dec rmp
	brne L1Lcd6
.ENDIF
	ret
;
; Calculate next decimal digit of a 32-bit number
;
Dec32:
	lpm R4,Z+ ; read next decimal number
	lpm R5,Z+
	lpm R6,Z+
	lpm R7,Z+
	clr rmp
Dec32a:
	cp R8,R4 ; compare the number with the decimal digit
	cpc R9,R5
	cpc R10,R6
	cpc R11,R7
	brcs Dec32b
	sub R8,R4
	sbc R9,R5
	sbc R10,R6
	sbc R11,R7
	inc rmp ; inc result
	rjmp Dec32a ; repeat
Dec32b:
	add R0,rmp ; add to leading blank flag
	subi rmp,-'0' ; add ASCII zero
	tst R0 ; still leading blanks?
	brne Dec32c
	ldi rmp,' ' ; set leading blank
Dec32c:
	st X+,rmp ; store char in SAM buffer
	ret
;
; 32 bit decimal table for conversion
;
DecTab32:
.DB $00,$CA,$9A,$3B ; 1000 mrd
.DB	$00,$E1,$F5,$05 ; 100 mio
.DB	$80,$96,$98,$00 ; 10 mio
.DB	$40,$42,$0F,$00 ; 1 mio
.DB	$A0,$86,$01,$00 ; 100,000
DecTab16:
.DB	$10,$27,$00,$00 ; 10,000
.DB	$E8,$03,$00,$00 ; 1,000
.DB	$64,$00,$00,$00 ; 100
.DB	$0A,$00,$00,$00 ; 10
;
; Calculate and display time
;
CalcTime:
	rcall GetRate ; read rate to R7:R6:R5:R4
	brcc CalcTime1 ; timer is active, calc time
	rjmp L1Lcd ; timer is inactive, display 0
CalcTime1:
	mov R2,R4 ; move multiplicator to R7:R:R5:R4:R3:R2
	mov R3,R5
	mov R4,R6
	mov R5,R7
	clr R6
	clr R7
	clr R8 ; clear result in R13:R12:R11:R10:R9:R8
	clr R9
	clr R10
	clr R11
	clr R12
	clr R13
	ldi rmp,HIGH(cTMult) ; load multiplier to R1:R0
	mov R1,rmp
	ldi rmp,LOW(cTMult)
	mov R0,rmp
CalcTime2:
	lsr R1 ; shift next bit of multiplyer to carry
	ror R0
	brcc CalcTime3
	add R8,R2 ; add the multiplicator
	adc R9,R3
	adc R10,R4
	adc R11,R5
	adc R12,R6
	adc R13,R7
CalcTime3:
	lsl R2 ; multiply multiplicator by 2
	rol R3
	rol R4
	rol R5
	rol R6
	rol R7
	tst R0 ; check LSB for end of multiplication
	brne CalcTime2
	tst R1 ; check MSB for end of multiplication
	brne CalcTime2
	mov rmp,R8 ; keep lowest byte for rounding
	mov R8,R9 ; shift result right = divide by 256
	mov R9,R10
	mov R10,R11
	mov R11,R12
	tst R13 ; check for overflow
	breq CalcTime5
CalcTime4:
	ldi rmp,0xFF ; overflow, set to highest number
	mov R8,rmp
	mov R9,rmp
	mov R10,rmp
	mov R11,rmp
	rjmp L1Lcd ; and display
CalcTime5:
	cpi rmp,0x80
	brcs CalcTime6
	ldi rmp,0x01 ; round up
	add R8,rmp
	ldi rmp,0x00
	adc R9,rmp
	adc R10,rmp
	adc R11,rmp
	brcs CalcTime4
CalcTime6:
	rjmp L1Lcd
;
; Calculate pulse width, first multiply pulse duration by 10000,
; then divide the result by the total puse duration
;
CalcPw:
	lds R7,sCmp ; Read active pulse duration to R10:R9:R8:R7
	lds R8,sCmp+1
	clr R9
	clr R10
	clr R0 ; clear multiplication result in R6:R5:R4:R2:R1:R0
	clr R1
	clr R2
	clr R3
	clr R4
	clr R5
	clr R6
	ldi rmp,HIGH(10000) ; set R12:R11 to 10000
	mov R12,rmp
	ldi rmp,LOW(10000)
	mov R11,rmp
CalcPw1:
	lsr R12 ; shift next bit right to carry
	ror R11
	brcc CalcPw2 ; zero shifted out
	add R0,R7 ; add the multiplicator
	adc R1,R8
	adc R2,R9
	adc R3,R10
CalcPw2:
	lsl R7 ; multiply by 2
	rol R8
	rol R9
	rol R10
	tst R11 ; check end of multiplication
	brne CalcPw1 ; go on
	tst R12
	brne CalcPw1 ; go on
	lds R7,sCtc ; read CTC value to R9:R8:R7
	lds R8,sCtc+1
	clr R9
	clr R10 ; clear result of division
	inc R10
	clr R11
	clr R12
	clr R13
CalcPw3:
	lsl R0 ; shift left
	rol R1
	rol R2
	rol R3
	rol R4
	rol R5
	rol R6
	cp R4,R7 ; compare by divider
	cpc R5,R8
	cpc R6,R9
	brcs CalcPw4 ; don't sutract
	sub R4,R7
	sbc R5,R8
	sbc R6,R9
	sec
	rjmp CalcPw5 ; shift a 1 in
CalcPw4:
	clc ; shift a 0 in
CalcPw5:
	rol R10 ; shift result in
	rol R11
	rol R12
	rol R13
	brcc CalcPw3
	lsl R3 ; round result
	rol R4
	rol R5
	rol R6
	cp R4,R7
	cpc R5,R8
	cpc R6,R9
	brcs L2Lcd
	ldi rmp,1
	add R10,rmp
	ldi rmp,0
	adc R11,rmp
	adc R12,rmp
	adc R13,rmp
L2Lcd:
	mov R8,R10
	mov R9,R11
	mov R10,R12
	mov R11,R13
	ldi ZH,HIGH(2*DecTab16)
	ldi ZL,LOW(2*DecTab16)
	clr R0
.IF cLcdLn==1
	ldi XH,HIGH(sLcdL1)
	ldi XL,LOW(sLcdL1)
.ELSE
	ldi XH,HIGH(sLcdL2)
	ldi XL,LOW(sLcdL2)
.ENDIF
	ldi rmp,'P'
	st X+,rmp
	ldi rmp,' '
	st X+,rmp
	ldi rmp,'='
	st X+,rmp
	rcall Dec32 ; write 100's
	rcall Dec32 ; write 10's
	inc R0
	rcall Dec32 ; write 1's
	ldi rmp,cDecs ; write decimal separator
	st X+,rmp
	rcall Dec32 ; write 0.1's
	ldi rmp,'0' ; write 0.01's
	add rmp,R8
	st X+,rmp
	ldi rmp,'%'
	st X+,rmp
	ldi rmp,' '
	mov R0,rmp
	ldi rmp,cLcdLw-9
L2Lcd1:
	st X+,R0
	dec rmp
	brne L2Lcd1
	ret
;
; **************************************************
;     U P D A T E   T I M E R   V A L U E S
; **************************************************
;
; Convert ADC values and set timer
;
Convert:
	ldi ZH,HIGH(2*Datatable)
	ldi ZL,LOW(2*Datatable)
	add ZL,rAdc0L ; add ADC0 value
	adc ZH,rAdc0H
	add ZL,rAdc0L ; add ADC0 value again
	adc ZH,rAdc0H
	lpm R0,Z+ ; read table value
	lpm R1,Z
	sts sCtc,R0 ; copy to SRAM
	sts sCtc+1,R1
	clr R2 ; clear for multiplication in R3:R2:R1:R0
	clr R3
	mov R4,rAdc1L ; copy ADC1 result to R5:R4
	mov R5,rAdc1H
	clr R9 ; clear result in R9:R8:R7:R6
	clr R8
	clr R7
	clr R6
Convert1:
	lsr R5 ; shift lowest bit to carry
	ror R4
	brcc Convert2 ; bit is zero, don't add
	add R6,R0
	adc R7,R1
	adc R8,R2
	adc R9,R3
Convert2:
	lsl R0 ; shift muliplicator one left
	rol R1
	rol R2
	rol R3
	tst R4 ; test if multiplicator is zero
	brne Convert1
	tst R5
	brne Convert1
	lsr R9 ; divide result by 2
	ror R8
	ror R7
	lsr R9 ; divide result by 4
	ror R8
	ror R7
	brcc Convert3
	ldi rmp,1 ; round up
	add R7,rmp
	ldi rmp,0
	adc R8,rmp
Convert3:
	sts sCmp,R7 ; store in SRAM
	sts sCmp+1,R8
	mov ZL,rAdc0L ; copy ADC0 to Z
	mov ZH,rAdc0H
	ldi XL,LOW(392)
	ldi XH,HIGH(392)
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10)
	cp ZL,XL
	cpc ZH,XH
	brcc Convert4
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS11)
	ldi XL,LOW(225)
	ldi XH,HIGH(225)
	cp ZL,XL
	cpc ZH,XH
	brcc Convert4
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS10)|(1<<CS11)
	ldi XL,LOW(60)
	ldi XH,HIGH(60)
	cp ZL,XL
	cpc ZH,XH
	brcc Convert4
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS12)
	cpi ZL,3
	brcc Convert4
	ldi rmp,(1<<WGM13)|(1<<WGM12)|(1<<CS12)|(1<<CS10)
Convert4:
	sts sPre,rmp ; save to prescaler control byte storage
	ldi rmp,(1<<COM1A1)|(1<<COM1B1)|(1<<WGM11)
	sbis pSwtchIn,pbInv ; ask inverted
	ldi rmp,(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)|(1<<WGM11)
	sts sMode,rmp
	ret
;
; Write the new values to TC1
;
UpdateTc1:
	lds rmp,sCmp+1 ; set compare match value A
	out OCR1AH,rmp
	lds rmp,sCmp
	out OCR1AL,rmp
	lds rmp,sCmp+1 ; set compare match value B
	out OCR1BH,rmp
	lds rmp,sCmp
	out OCR1BL,rmp
	lds rmp,sCtc+1 ; set CTC value
	out ICR1H,rmp
	lds rmp,sCtc
	out ICR1L,rmp
	lds rmp,sMode ; set mode TC1
	out TCCR1A,rmp
	lds rmp,sPre
	out TCCR1B,rmp
	ret
;
; **************************************************
;          L C D - R O U T I N E S
; **************************************************
;
; Init the LCD
;
InitLcd:
	ldi ZH,HIGH(2*LcdInitText)
	ldi ZL,LOW(2*LcdInitText)
	rjmp LcdInit
;
; Display line 1 of the LCD
;
LcdL1:
	ldi ZH,HIGH(sLcdL1) ; Z to line 1 in SRAM
	ldi ZL,LOW(sLcdL1)
	rjmp LcdLine1
;
; Display line 2 of the LCD
;
.IF cLcdLn>1
	LcdL2:
		ldi ZH,HIGH(sLcdL2) ; Z to line 2 in SRAM
		ldi ZL,LOW(sLcdL2)
		rjmp LcdLine2
	.ENDIF
;
; Display TC1 parameters in hex on display line 1
;
.IF dbghx
LcdHexPar:
	rcall LcdHome
	lds ZH,sCtc+1
	lds ZL,sCtc
	rcall Displ4Hex
	ldi rmp,' '
	mov R0,rmp
	rcall LcdChar
	lds ZH,sCmp+1
	lds ZL,sCmp
	rcall Displ4Hex
	ldi rmp,' '
	mov R0,rmp
	rcall LcdChar
	lds rmp,sMode
	rcall Displ2Hex
	ldi rmp,' '
	mov R0,rmp
	rcall LcdChar
	lds rmp,sPre
	rcall Displ2Hex
	ldi rmp,' '
	mov R0,rmp
	rjmp LcdChar
Displ4Hex:
	mov rmp,ZH
	rcall Displ2Hex
	mov rmp,ZL
Displ2Hex:
	push rmp
	swap rmp
	rcall Displ1Hex
	pop rmp
Displ1Hex:
	andi rmp,0x0F
	subi rmp,-'0'
	cpi rmp,'9'+1
	brcs Displ1Hex1
	subi rmp,-7
Displ1Hex1:
	mov R0,rmp
	rjmp LcdChar
	.ENDIF
;
; **************************************************
;     M A I N   P R O G R A M   I N I T
; **************************************************
;
Main:
	ldi rmp,HIGH(RAMEND) ; init stack
	out SPH,rmp
	ldi rmp,LOW(RAMEND)
	out SPL,rmp
	; init key input port
	ldi rmp,(1<<pbTime)|(1<<pbRpm)|(1<<pbInv)|(1<<pbPwm)
	out pSwtchOut,rmp
	; Init the signal outputs
	sbi pSignDdr,pbSignA
	sbi pSignDdr,pbSignB
	cbi pSignOut,pbSignA
	sbi pSignOut,pbSignB
	; init flags
	clr rFlg
.IF dbgOn ; debugging math
	ldi rmp,HIGH(cAdc0)
	mov rAdc0H,rmp
	ldi rmp,LOW(cAdc0)
	mov rAdc0L,rmp
	ldi rmp,HIGH(cAdc1)
	mov rAdc1H,rmp
	ldi rmp,LOW(cAdc1)
	mov rAdc1L,rmp
	ldi rFlg,cFlg
	rcall Convert
	rcall UpdateTc1
	.IF cLcdLn>1
		rcall Calc
		rcall CalcPw
		.ELSE
		sbrc rFlg,bPw
		rjmp dbgPw
		rcall Calc
		rjmp dbgloop
		dbgPw:
			rcall CalcPw
		.ENDIF
	dbgloop:
		rjmp dbgloop
	.ENDIF
	; init the LCD
	rcall InitLcd
	; init the ADC conversion
	ldi rmp,(1<<REFS0) ; set ADMUX to channel 0
	out ADMUX,rmp
	ldi rmp,(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; start conversion channel 0
	ldi rAdcC,1 ; set start value for ADC delay
	ldi rmp,(1<<CS02)|(1<<CS00) ; prescaler 1024
	out TCCR0,rmp
	ldi rmp,1<<TOIE0 ; enable TC0 overflow int
	out TIMSK,rmp
	sei
;
; **************************************************
;     M A I N   P R O G R A M   L O O P
; **************************************************
;
Loop:
	sleep
	nop
	sbrs rFlg,bAdc1 ; ADC ready flag set?
	rjmp Loop ; no, sleep on
	cbr rFlg,1<<bAdc1 ; clear ADC ready flag
	.IF cLcdLn == 1
		cbr rFlg,(1<<bTime)|(1<<bRpm)|(1<<bPw)|(1<<bInv)
		sbis pSwtchIn,pbPwm
		sbr rFlg,1<<bPw
		.ELSE
		cbr rFlg,(1<<bTime)|(1<<bRpm)|(1<<bInv)
		.ENDIF
	sbis pSwtchIn,pbInv ; inverted signals?
	sbr rFlg,1<<bInv
	sbis pSwtchIn,pbTime
	sbr rFlg,1<<bTime
	sbis pSwtchIn,pbRpm
	sbr rFlg,1<<bRpm
	rcall Convert ; convert ADC to TC1 values
	rcall UpdateTc1
	.IF cLcdLn>1
		rcall Calc ; calculate frequency
		rcall CalcPw ; calculate pulse width
		rcall LcdL2 ; display line 2 
		.ELSE
		sbrc rFlg,bPw ; check if pulse width displayed
		rjmp CPw ; yes, calculate pulse width
		rcall Calc ; calculate frequency
		rjmp Loop1 ; and display frequency
		CPw:
			rcall CalcPw ; calculalate pulse width
		.ENDIF
Loop1:
	rcall LcdL1 ; display line 1
.IF dbghx
	rcall LcdHexPar
	.ENDIF
	rjmp Loop ; go to sleep again
;
; **************************************************
;   I N C L U D E   T H E   L C D   R O U T I N E S
; **************************************************
;
.INCLUDE "Lcd8_02WO_rect.inc"
;
; Init-text of the LCD
;
LcdInitText:
.IF cLcdLn == 1
	.IF cLcdLw == 40
		.DB "Rectangle Generator DG4FAC ATmega8 V1.0 "
		.ELSE
		.IF cLcdLw == 24
			.DB "Rect Generator M8 V1.0  "
			.ELSE
			.IF cLcdLw == 20
				.DB "Rect Generator V1.0 "
				.ELSE
				.IF cLcdLw == 16
					.DB "Rect-Gen M8 V1.0"
					.ELSE
					.DB "RG M8 V1"
					.ENDIF
				.ENDIF
			.ENDIF
		.ENDIF
	.ELSE
	.IF cLcdLw == 40
		.DB "Rectangle Generator DG4FAC ATmega8 V1.0",0x0D
		.DB "(C)2006 by info!at!avr-asm-tutorial.net "
		.ELSE
		.IF cLcdLw == 24
			.DB "Rect Generator M8 V1.0 ",0x0D
			.DB "www.avr-asm-tutorial.net"
			.ELSE
			.IF cLcdLw == 20
				.DB "Rect Generator V1.0",0x0D
				.DB "avr-asm-tutorial.net"
				.ELSE
				.IF cLcdLw == 16
					.DB "Rect-Gen M8 V1.0",0x0D,"avr-asm-tutorial",0x00
					.ELSE
					.DB "RG M8 V1",0x0D,"avr-asm"
					.ENDIF
				.ENDIF
			.ENDIF
		.ENDIF
	.ENDIF
.DB 0,0
;
; **************************************************
;         C O N V E R S I O N - T A B L E
; **************************************************
;
.INCLUDE "rectgen_m8_table.inc"
;
©2006 by http://www.avr-asm-tutorial.net