Pfad: Home => AVR-Übersicht => Anwendungen => Rechteckgenerator = > Quellcode Hauptprogramm
Rechteckgenerator klein

Rechteckgenerator mit ATmega8 - Quellcode Hauptprogramm


;
; ***************************************************
; * Einstellbarer Rechteckgenerator mit ATmega8     *
; * Frequenz (0.25Hz..8MHz) und PulsWeite (0.00..   *
; * 100.00%) variabel, LCD-Anzeige (waehlbar: ein-  *
; * zeilige/mehrzeilige Anzeige, 16..40 Zeichen pro *
; * Zeile), zeigt Frequenz oder Umdrehungsgeschwin- *
; * digkeit sowie Pulsweite in % an                 *
; * Benoetigt die Dateien "rectgen_m8_table.inc"    *
; * und "Lcd8_02WO_rec.inc"                         *
; * Version 1 vom 21. April 2006                    *
; * (C)2006 by info!at!avr-asm-tutorial.net         *
; ***************************************************
;
.NOLIST
.INCLUDE "m8def.inc"
.LIST
;
; Debug Informationen
;
.EQU dbgOn = 0 ; Debug ein oder aus
.IF dbgOn
	.EQU cAdc0 = 1000 ; ADC0-Wert
	.EQU cAdc1 = 511 ; ADC1-Wert
	.EQU cFlg = 0
	;.EQU cFlg = (1<<bTime)
	;.EQU cFlg = (1<<bRpm)
	;.EQU cFlg = (1<<bPw)
	.ENDIF
.EQU dbghx = 0 ; Debug Hex-Ausgabe auf LCD
;
; Hardware
;              ___________
;       __ 10k/           |      _
;+5V O-|__|--|RESET    PC5|--O--O O--| Pulseweite anzeigen
;            |            |      _  (nur einzeilige LCD!)
;  LCD D0 O--|PD0      PC4|--O--O O--| Signale invertieren
;            |            |      _
;  LCD D1 O--|PD1      PC3|--O--O O--| UPM anzeigen
;            |            |      _
;  LCD D2 O--|PD2      PC2|--O--O O--| Zeit anzeigen
;            |            |
;  LCD D3 O--|PD3     ADC1|--O 0..5V Pulsweiteneinstellung
;            |    A T     |
;  LCD D4 O--|PD4     ADC0|--O 0..5V Frequenzeinstellung
;            |   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 Ausgang B
;            |            |
;   LCD E O--|PB0  PB1OC1A|--O Ausgang A
;            |____________|
;
;
; Timing
; ------
;
; TC0: Vorteiler = 1024, Ueberlauf Interrupt, @16MHz:
;      16.384 ms, abwaertszaehler von 30 auf 0, bei 0:
;      (491.52 ms) startet ADC-Wandlerzyklus
;
; ADC-Kanaele 1 und 2: Wandlung gestartet mit TC0,
;      ADC-Teiler = 128, @16MHz, erster ADC-Complete
;      Interrupt nach 200 us, zweiter Interrupt nach
;      104 us, ADC wird abgeschaltet und Komplett-Flagge
;      gesetzt nach zwei vollstaendigen Wandlungen
;
; ADC-Komplett-Flagge: liest Wert fuer die Einstellung
;      des TC1-CTC (ICR1-CTC) aus Tabelleneintrag von
;      ADC0, berechnet COMPA/COMPB-Wert aus dem CTC-Wert
;      und ADC1, setzt TC1-ICR1 und COMPA/COMPB-Werte,
;      TC1-Vorteiler und TC1-Ausgangspoaritaet
;
; TC1: Schneller PWM mode/WGM=14, Vorteiler = 1..1024 (ab-
;      haengig von gewaehlter Frequenz), ICR1 bestimmt den
;      TOP-Wert, COMPA/B bestimmt phasenmodulierte Umkehr
;      des Ausgangssignals
;
; **************************************************
;             D E F I N I T I O N E N
; **************************************************
;
; Konstanten
;
.EQU clock = 16000000 ; Taktfrequenz
.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 ; Anzahl Zeichen pro Zeile der LCD
.EQU cLcdLn = 2 ; Anzahl Zeilen der LCD
;.EQU cLcdMicro = $E4 ; Mikro-Zeichen der LCD, nicht ok bei L2432
.EQU cLcdMicro = 'u' ; anstelle des Mikro-Zeichens
.EQU cEn = 0 ; Englische oder deutsche Version
;
; Abhaengige Konstanten
;
.EQU cTMult = (256000000/clock)*100
.IF cEn
	.EQU c1000s = $2C ; Komma
	.EQU cDecs = $2E ; Punkt
	.ELSE
	.EQU c1000s = $2E ; Punkt
	.EQU cDecs = $2C ; Komma
	.ENDIF
;
; Register
;
; benutzt: R0..R13 fuer Berechnungen
; frei: R14
.DEF rSreg = R15 ; Status-Sicherungs-Register
.DEF rmp = R16 ; Multipurpose Register ausserhalb Ints
.DEF rimp = R17 ; Multipurpose Register innerhalb Ints
.DEF rFlg = R18 ; Flaggenregister
	.EQU bTime = 0 ; Zeit anstelle Frequenz anzeigen
	.EQU bRpm = 1 ; UPM anstelle Frequenz anzeigen
	.EQU bInv = 2 ; Invertieren des Ausgangssignals
	.EQU bAdc0 = 3 ; ADC-Kanal 0 Komplett-Flagge
	.EQU bAdc1 = 4 ; ADC-Kanal 1 Komplett-Flagge
	.IF cLcdLn==1
		.EQU bPw = 7 ; Anzeige Pulsweite auf Zeile 1 (einzeilige LCD)
		.ENDIF
.DEF rAdc0L = R19 ; LSB letztes ADC0-Ergebnis
.DEF rAdc0H = R20 ; dto., MSB
.DEF rAdc1L = R21 ; LSB letztes ADC1-Ergebnis
.DEF rAdc1H = R22 ; dto., MSB
.DEF rAdcC = R23 ; Zaehler fuer verzoegerten ADC Startzyklus
; frei: R24..R25
; benutzt: X (R27:R26) fuer Berechnungen
; frei: Y (R29:R28)
; benutzt: Z (R31:R30) fuer Berechnungen
;
; Ports
;
; LCD-Port-Anschluesse
.EQU pLcdData = PORTD
.EQU pLcdCtrl = PORTB
.EQU pbLcdE = 0
.EQU pbLcdRs = 4
.EQU pbLcdRw = 5
; Schalter Port-Anschluesse
.EQU pSwtchOut = PORTC
.EQU pSwtchIn = PINC
.EQU pbTime = 2
.EQU pbRpm = 3
.EQU pbInv = 4
.EQU pbPwm = 5
; Signalausgaenge
.EQU pSignOut = PORTB
.EQU pSignDdr = DDRB
.EQU pbSignA = 1
.EQU pbSignB = 2
;
; SRAM Positionen
;
.DSEG
.ORG $0060
sCtc: ; CTC-Teiler fuer TC1
.BYTE 2
sCmp: ; COMPA/B Pulsweite TC1
.BYTE 2
sPre: ; Vorteiler TC1
.BYTE 1
sMode: ; Modus TC1
.BYTE 1
sLcdL1: ; Zeile 1 der LCD
.BYTE cLcdLw
.IF cLcdLn>1
sLcdL2: ; Zeile 2 der LCD
.BYTE cLcdLw
.ENDIF
;
; **************************************************
;       R E S E T - und  I N T - V E C T O R E N
; **************************************************
;
; Code-Segment
.CSEG
.ORG $0000
;
	rjmp main ; Reset Vektor, Sprung zum Beginn
	reti ; INT0, nicht verwendet
	reti ; INT1, nicht verwendet
	reti ; TIMER2COMP, nicht verwendet
	reti ; TIMER2OVF, nicht verwendet
	reti ; TIMER1CAPT, nicht verwendet
	reti ; TIMER1COMPA, nicht verwendet
	reti ; TIMER1COMPB, nicht verwendet
	reti ; TIMER1OVF, nicht verwendet
	rjmp TC0OvflwInt ; TIMER0OVF
	reti ; SPI, STC, nicht verwendet
	reti ; USART, RXC, nicht verwendet
	reti ; USART, UDRE, nicht verwendet
	reti ; USART, TXC, nicht verwendet
	rjmp AdcInt ; ADC
	reti ; EE_RDY, nicht verwendet
	reti ; ANA_COMP, nicht verwendet
	reti ; TWI, nicht verwendet
	reti ; SPM_RDY, nicht verwendet
;
; **************************************************
;      I N T - V E K T O R - R O U T I N E N
; **************************************************
;
; TC0 Ueberlauf Interrupt, startet ADC-Messzyklus
;
TC0OvflwInt:
	in rSreg,SREG ; sichere Status
	dec rAdcC ; zaehle Verzoegerungszaehler abwaerts
	brne TC0OvflwInt1
	ldi rAdcC,30 ; Neustart Zaehler
	ldi rimp,(1<<REFS0) ; ADMUX auf Kanal 0
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; starte Umwandlung Kanal 0
	cbr rFlg,1<<bAdc0 ; setze Kanal-0-Komplett-Flagge
TC0OvflwInt1:
	out SREG,rSreg ; Status wieder herstellen
	reti
;
; ADC-Fertig-Interrupt, lese ADC-Wert
;
AdcInt:
	in rSreg,SREG ; sichere Status
	sbrc rFlg,bAdc0 ; Kanal 0 fertig?
	rjmp AdcInt1 ; ja, starte naechsten Kanal
	in rAdc0L,ADCL ; lese Kanal 0
	in rAdc0H,ADCH
	ldi rimp,(1<<REFS0)|(1<<MUX0) ; setze Kanal 1
	out ADMUX,rimp
	ldi rimp,(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rimp ; starte Umwandlung Kanal 1
	sbr rFlg,1<<bAdc0 ; setze Kanal-0-Komplettflagge
	out SREG,rSreg ; stelle Status wieder her
	reti
AdcInt1:
	in rAdc1L,ADCL ; lese Kanal 1
	in rAdc1H,ADCH
	ldi rimp,(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0) ; ADC ausschalten
	out ADCSRA,rimp
	sbr rFlg,1<<bAdc1 ; setze Kanal-1-Komplettflagge
	out SREG,rSreg ; stelle Status wieder her
	reti
;
; **************************************************
;             S U B R O U T I N E N
; **************************************************
;
; Lese aktuellen CTC- und Vorteiler-Wert und berechnet
; die Rate in R7:R6:R5:R4
;
GetRate:
	lds rmp,sPre ; lese Vorteiler nach rmp
	andi rmp,(1<<CS12)|(1<<CS11)|(1<<CS10)
	breq GetRate2 ; Vorteiler ist Null, setze Carry
	lds R4,sCtc ; lese Teiler nach R7:R6:R5:R4
	lds R5,sCtc+1
	clr R6
	clr R7
	dec rmp
	breq GetRate1 ; Vorteiler = 1
	lsl R4 ; Teiler * 2
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 4
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 8
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; Teiler = 8
	lsl R4 ; Teiler * 16
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 32
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 64
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; Vorteiler = 64
	lsl R4 ; Teiler * 128
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 256
	rol R5
	rol R6
	rol R7
	dec rmp
	breq GetRate1 ; Vorteiler = 256
	lsl R4 ; Teiler * 512
	rol R5
	rol R6
	rol R7
	lsl R4 ; Teiler * 1024
	rol R5
	rol R6
	rol R7
GetRate1: ; Timer ist aktiv, loesche carry
	clc
	ret
GetRate2: ; Timer ist inaktiv, setze carry
	sec
	ret
;
; Berechne Anzeigentext aus aktuellen Timer-Parametern
;
Calc:
	sbrc rFlg,bTime ; Zeit anzeigen?
	rjmp CalcTime ; ja, berechne Zeit
	clr R8 ; loesche Divisionsergebnis in R11:R10:R9:R8
	inc R8 ; setze letztes Bit als Zaehler
	clr R9
	clr R10
	clr R11
	rcall GetRate
	brcs L1Lcd ; Timer ist aus, Frequenz = 0
	clr R3 ; setze Teiler in R3:R2:R1:R0:ZH:ZL:XH:XL
	clr R2
	clr R1
	sbrc rFlg,bRpm ; UPM anzeigen?
	rjmp Calc1 ; ja
	ldi rmp,cDivF5 ; Frequenz anzeigen
	mov R0,rmp
	ldi ZH,cDivF4
	ldi ZL,cDivF3
	ldi XH,cDivF2
	ldi XL,cDivF1
	rjmp Calc2
Calc1:
	ldi rmp,cDivU5 ; zeige UPM an
	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 ; vergleichen
	cpc R1,R5
	cpc R2,R6
	cpc R3,R7
	brcs Calc3
	sub R0,R4 ; subtrahieren
	sbc R1,R5
	sbc R2,R6
	sbc R3,R7
	sec
	rjmp Calc4
Calc3:
	clc
Calc4:
	rol R8 ; rolle Ergebnisbit ein
	rol R9
	rol R10
	rol R11
	brcc Calc2 ; wiederhole 32 mal
	lsl ZH ; aufrunden?
	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:
;
; Ergebnis in R11:R10:R9:R8 auf Zeile 1
;
L1Lcd:
	clr R0 ; R0 ist Flagge fuer fuehrende Nullen
	ldi ZH,HIGH(2*DecTab32) ; Zeiger auf Dezimaltabelle
	ldi ZL,LOW(2*DecTab32)
	ldi XH,HIGH(sLcdL1) ; X zeigt auf LCD-Zeile
	ldi XL,LOW(sLcdL1)
	ldi rmp,'F' ; zeige Frequenz an
	sbrc rFlg,bRpm
	ldi rmp,'R' ; zeige UPM an
	sbrc rFlg,bTime
	ldi rmp,'T' ; zeige Zeit an
	st X+,rmp ; setze erstes Zeichen
.IF cLcdLw>16
	ldi rmp,' ' ; fuege ein Leerzeichen zu
	st X+,rmp
	ldi rmp,'=' ; fuege = zu
	st X+,rmp
	rcall Dec32 ; schreibe 100 Millionen-Stelle
.ELSE
	rcall Dec32 ; schreibe 10 Millionen-Stelle
	ld rmp,-X ; lese letzte Zeichenposition
	cpi rmp,' ' ; Leerzeichen?
	brne L1Lcd00
	ldi rmp,'=' ; fuege = an
	st X,rmp
L1Lcd00:
	adiw XL,1
.ENDIF
	rcall Dec32 ; schreibe Millionen
	ldi rmp,c1000s
	tst R0
	brne L1Lcd0a
	ldi rmp,' '
L1Lcd0a:
	st X+,rmp
	rcall Dec32 ; schreibe 100,000'er
	rcall Dec32 ; schreibe 10,000'er
	rcall Dec32 ; schreibe 1,000'er
	ldi rmp,c1000s
	tst R0
	brne L1Lcd0b
	ldi rmp,' '
L1Lcd0b:
	st X+,rmp
	rcall Dec32 ; schreibe 100'er
	rcall Dec32 ; schreibe 10'er
	inc R0 ; schreibe fuehrende Nullen
	rcall Dec32 ; schreibe 1'er
	tst R8 ; zeige Nachkommastellen an?
	breq L1Lcd1
	ldi rmp,cDecs ; setze Dezimalzeichen
	st X+,rmp
	rcall Dec32 ; schreibe 0.1'er
	ldi rmp,'0'
	add rmp,R8 ; schreibe 0.01'er
	st X+,rmp
	rjmp L1Lcd2
L1Lcd1:
	ldi rmp,' ' ; leere Dezimalstellen
	st X+,rmp
	st X+,rmp
	st X+,rmp
L1Lcd2:
.IF cLcdLw>16
	ldi rmp,' ' ; fuege Leerzeichen hinzu
	st X+,rmp
.ENDIF
	sbrc rFlg,bTime ; Zeit anzeigen?
	rjmp L1Lcd4 ; ja
	sbrc rFlg,bRpm ; UPM anzeigen?
	rjmp L1Lcd3 ; ja
	ldi rmp,'H' ; Frequenz anzeigen
	st X+,rmp
	ldi rmp,'z'
	st X+,rmp
	rjmp L1Lcd5
L1Lcd3:
	ldi rmp,'p' ; zeige Upm an
	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
;
; Berechne naechste Dezimalstelle einer 32-Bit-Zahl
;
Dec32:
	lpm R4,Z+ ; lese naechste Dezimalzahl
	lpm R5,Z+
	lpm R6,Z+
	lpm R7,Z+
	clr rmp
Dec32a:
	cp R8,R4 ; vergleiche die Zahl mit der Dezimalzahl
	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 ; erhoehe Ergebnis
	rjmp Dec32a ; wiederhole
Dec32b:
	add R0,rmp ; addiere zur fuehrenden Nullen-Flagge
	subi rmp,-'0' ; addiere ASCII Null
	tst R0 ; fuehrende Null?
	brne Dec32c
	ldi rmp,' ' ; setze fuehrende Null
Dec32c:
	st X+,rmp ; speichere Zeichen im SRAM
	ret
;
; 32 Bit Dezimaltabelle fuer Zahlumwandlung
;
DecTab32:
.DB	$00,$CA,$9A,$3B ; 1000 mio
.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
;
; Berechne und zeige Zeit an
;
CalcTime:
	rcall GetRate ; Lese Rate nach R7:R6:R5:R4
	brcc CalcTime1 ; Timer ist aktiv, berechne Zeit
	rjmp L1Lcd ; Timer ist inaktiv, zeige 0 an
CalcTime1:
	mov R2,R4 ; kopiere Multiplikator nach R7:R:R5:R4:R3:R2
	mov R3,R5
	mov R4,R6
	mov R5,R7
	clr R6
	clr R7
	clr R8 ; loesche Ergebnis in R13:R12:R11:R10:R9:R8
	clr R9
	clr R10
	clr R11
	clr R12
	clr R13
	ldi rmp,HIGH(cTMult) ; lade Multiplikator in R1:R0
	mov R1,rmp
	ldi rmp,LOW(cTMult)
	mov R0,rmp
CalcTime2:
	lsr R1 ; schiebe naechstes Bit des Multiplikators in Carry
	ror R0
	brcc CalcTime3
	add R8,R2 ; addiere den Multiplikator
	adc R9,R3
	adc R10,R4
	adc R11,R5
	adc R12,R6
	adc R13,R7
CalcTime3:
	lsl R2 ; Multipliziere Multiplikator mit 2
	rol R3
	rol R4
	rol R5
	rol R6
	rol R7
	tst R0 ; pruefe LSB auf Ende der Multiplikation
	brne CalcTime2
	tst R1 ; pruefe MSB auf Ende der Multiplikation
	brne CalcTime2
	mov rmp,R8 ; niedrigstes Byte fuer Runden
	mov R8,R9 ; schiebe Ergebnis rechts = dividiere mit 256
	mov R9,R10
	mov R10,R11
	mov R11,R12
	tst R13 ; pruefe auf Ueberlauf
	breq CalcTime5
CalcTime4:
	ldi rmp,0xFF ; Ueberlauf, setze auf groesste Zahl
	mov R8,rmp
	mov R9,rmp
	mov R10,rmp
	mov R11,rmp
	rjmp L1Lcd ; und zeige an
CalcTime5:
	cpi rmp,0x80
	brcs CalcTime6
	ldi rmp,0x01 ; runde auf
	add R8,rmp
	ldi rmp,0x00
	adc R9,rmp
	adc R10,rmp
	adc R11,rmp
	brcs CalcTime4
CalcTime6:
	rjmp L1Lcd
;
; Berechne Pulsweite, zuerst Pulsdauer mit 10000 multiplizieren,
; dann das Ergebnis durch die gesamte Pulsdauer dividieren
;
CalcPw:
	lds R7,sCmp ; Lese aktive Pulsdauer nach R10:R9:R8:R7
	lds R8,sCmp+1
	clr R9
	clr R10
	clr R0 ; loesche Multiplikationsergebnis in R6:R5:R4:R2:R1:R0
	clr R1
	clr R2
	clr R3
	clr R4
	clr R5
	clr R6
	ldi rmp,HIGH(10000) ; setze R12:R11 auf 10000
	mov R12,rmp
	ldi rmp,LOW(10000)
	mov R11,rmp
CalcPw1:
	lsr R12 ; schiebe naechstes Bit rechts in Carry
	ror R11
	brcc CalcPw2 ; Null herausgeschoben
	add R0,R7 ; addiere den Multiplikator
	adc R1,R8
	adc R2,R9
	adc R3,R10
CalcPw2:
	lsl R7 ; multipliziere mit 2
	rol R8
	rol R9
	rol R10
	tst R11 ; pruefe Ende der Multiplikation
	brne CalcPw1 ; weitermachen
	tst R12
	brne CalcPw1 ; weitermachen
	lds R7,sCtc ; lese CTC-Wert nach R9:R8:R7
	lds R8,sCtc+1
	clr R9
	clr R10 ; loesche Divisionsergebnis
	inc R10
	clr R11
	clr R12
	clr R13
CalcPw3:
	lsl R0 ; schiebe links
	rol R1
	rol R2
	rol R3
	rol R4
	rol R5
	rol R6
	cp R4,R7 ; vergleiche mit Teiler
	cpc R5,R8
	cpc R6,R9
	brcs CalcPw4 ; nicht sutrahieren
	sub R4,R7
	sbc R5,R8
	sbc R6,R9
	sec
	rjmp CalcPw5 ; schiebe eine 1 hinein
CalcPw4:
	clc ; schiebe eine 0 hinein
CalcPw5:
	rol R10 ; schiebe in Ergebnis hinein
	rol R11
	rol R12
	rol R13
	brcc CalcPw3
	lsl R3 ; runde Ergebnis
	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 ; schreibe 100'er
	rcall Dec32 ; schreibe 10'er
	inc R0
	rcall Dec32 ; schreibe 1'er
	ldi rmp,cDecs ; schreibe Dezimaltrenner
	st X+,rmp
	rcall Dec32 ; schreibe 0.1'er
	ldi rmp,'0' ; schreibe 0.01'er
	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
;
; **************************************************
;     E R N E U E R E    T I M E R   W E R T E
; **************************************************
;
; Wandle ADC-Werte um und setze Timer
;
Convert:
	ldi ZH,HIGH(2*Datatable)
	ldi ZL,LOW(2*Datatable)
	add ZL,rAdc0L ; addiere ADC0-Wert
	adc ZH,rAdc0H
	add ZL,rAdc0L ; addiere ADC0-Wert erneut
	adc ZH,rAdc0H
	lpm R0,Z+ ; lese Tabellenwert
	lpm R1,Z
	sts sCtc,R0 ; kopiere ins SRAM
	sts sCtc+1,R1
	clr R2 ; loesche Ergebnis fuer Multiplikation in R3:R2:R1:R0
	clr R3
	mov R4,rAdc1L ; kopiere ADC1-Wert nach R5:R4
	mov R5,rAdc1H
	clr R9 ; loesche Ergebnis in R9:R8:R7:R6
	clr R8
	clr R7
	clr R6
Convert1:
	lsr R5 ; schiebe niedrigstes Bit in Carry
	ror R4
	brcc Convert2 ; Bit ist Null, addiere nicht
	add R6,R0
	adc R7,R1
	adc R8,R2
	adc R9,R3
Convert2:
	lsl R0 ; schiebe Muliplikator eins links
	rol R1
	rol R2
	rol R3
	tst R4 ; teste ob Multiplikator Nul ist
	brne Convert1
	tst R5
	brne Convert1
	lsr R9 ; dividiere Ergebnis durch 2
	ror R8
	ror R7
	lsr R9 ; dividiere Ergebnis durch 4
	ror R8
	ror R7
	brcc Convert3
	ldi rmp,1 ; runde auf
	add R7,rmp
	ldi rmp,0
	adc R8,rmp
Convert3:
	sts sCmp,R7 ; speichere im SRAM
	sts sCmp+1,R8
	mov ZL,rAdc0L ; kopiere ADC0 nach 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 ; sichere Vorteiler-Kontrollbyte im SRAM
	ldi rmp,(1<<COM1A1)|(1<<COM1B1)|(1<<WGM11)
	sbis pSwtchIn,pbInv ; invertiert?
	ldi rmp,(1<<COM1A1)|(1<<COM1A0)|(1<<COM1B1)|(1<<COM1B0)|(1<<WGM11)
	sts sMode,rmp
	ret
;
; Schreibe die neuen Werte in TC1
;
UpdateTc1:
	lds rmp,sCmp+1 ; setze Compare-Match-Wert A
	out OCR1AH,rmp
	lds rmp,sCmp
	out OCR1AL,rmp
	lds rmp,sCmp+1 ; setze Compare-Match-Wert B
	out OCR1BH,rmp
	lds rmp,sCmp
	out OCR1BL,rmp
	lds rmp,sCtc+1 ; setze CTC-Wert
	out ICR1H,rmp
	lds rmp,sCtc
	out ICR1L,rmp
	lds rmp,sMode ; setze Modus TC1
	out TCCR1A,rmp
	lds rmp,sPre
	out TCCR1B,rmp
	ret
;
; **************************************************
;          L C D - R O U T I N E N
; **************************************************
;
; Initiieren der LCD
;
InitLcd:
	ldi ZH,HIGH(2*LcdInitText)
	ldi ZL,LOW(2*LcdInitText)
	rjmp LcdInit
;
; Zeige Zeile 1 der LCD an
;
LcdL1:
	ldi ZH,HIGH(sLcdL1) ; Z auf Zeile 1 im SRAM
	ldi ZL,LOW(sLcdL1)
	rjmp LcdLine1
;
; Zeige Zeile 2 der LCD an
;
.IF cLcdLn>1
	LcdL2:
		ldi ZH,HIGH(sLcdL2) ; Z auf Zeile 2 im SRAM
		ldi ZL,LOW(sLcdL2)
		rjmp LcdLine2
	.ENDIF
;
; Zeige TC1 Parameter in Hex auf LCD-Zeile 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
;
; **************************************************
;     H A U P T P R O G R A M M   I N I T
; **************************************************
;
Main:
	ldi rmp,HIGH(RAMEND) ; Stapel init
	out SPH,rmp
	ldi rmp,LOW(RAMEND)
	out SPL,rmp
	; Schalter-Inputs init
	ldi rmp,(1<<pbTime)|(1<<pbRpm)|(1<<pbInv)|(1<<pbPwm)
	out pSwtchOut,rmp
	; Init der Signalausgabe-Pins
	sbi pSignDdr,pbSignA
	sbi pSignDdr,pbSignB
	cbi pSignOut,pbSignA
	sbi pSignOut,pbSignB
	; Init Flaggen
	clr rFlg
.IF dbgOn ; debug Berechnungen
	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 der LCD
	rcall InitLcd
	; Init des ADC
	ldi rmp,(1<<REFS0) ; setze ADMUX auf Kanal 0
	out ADMUX,rmp
	ldi rmp,(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)
	out ADCSRA,rmp ; starte Umwandlung Kanal 0
	ldi rAdcC,1 ; setze Startwert fuer ADC-Verzoegerung
	ldi rmp,(1<<CS02)|(1<<CS00) ; Vorteiler 1024
	out TCCR0,rmp
	ldi rmp,1<<TOIE0 ; ermoegliche TC0 Ueberlauf Int
	out TIMSK,rmp
	sei
;
; **************************************************
;     H A U P T P R O G R A M M S C H L E I F E
; **************************************************
;
Loop:
	sleep
	nop
	sbrs rFlg,bAdc1 ; ADC fertig Flagge gesetzt?
	rjmp Loop ; nein, schlaf weiter
	cbr rFlg,1<<bAdc1 ; loesche ADC fertig Flagge
	.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 ; Invertiere Signal?
	sbr rFlg,1<<bInv
	sbis pSwtchIn,pbTime
	sbr rFlg,1<<bTime
	sbis pSwtchIn,pbRpm
	sbr rFlg,1<<bRpm
	rcall Convert ; wandle ADC nach TC1-Werte
	rcall UpdateTc1
	.IF cLcdLn>1
		rcall Calc ; berechne Frequenz
		rcall CalcPw ; berechne Pulsweite
		rcall LcdL2 ; zeige in Zeile 2 an
		.ELSE
		sbrc rFlg,bPw ; pruefe of Pulsweite angezeigt
		rjmp CPw ; ja, berechne Pulsweite
		rcall Calc ; berechne Frequenz
		rjmp Loop1 ; und zeige Frequenz an
		CPw:
			rcall CalcPw ; berechne Pulsweite
		.ENDIF
Loop1:
	rcall LcdL1 ; zeige Zeile 1 an
.IF dbghx
	rcall LcdHexPar
	.ENDIF
	rjmp Loop ; weiterschlafen
;
; **************************************************
;   L C D - R O U T I N E N   E I N L E S E N
; **************************************************
;
.INCLUDE "Lcd8_02WO_rect.inc"
;
; Init-Text der LCD
;
LcdInitText:
.IF cLcdLn == 1
	.IF cLcdLw == 40
		.DB "Rechteck- Generator DG4FAC ATmega8 V1.0 "
		.ELSE
		.IF cLcdLw == 24
			.DB "RechtGenerator M8 V1.0  "
			.ELSE
			.IF cLcdLw == 20
				.DB "RechtGenerator V1.0 "
				.ELSE
				.IF cLcdLw == 16
					.DB "RechtGen M8 V1.0"
					.ELSE
					.DB "RG M8 V1"
					.ENDIF
				.ENDIF
			.ENDIF
		.ENDIF
	.ELSE
	.IF cLcdLw == 40
		.DB "Rechteck- Generator DG4FAC ATmega8 V1.0",0x0D
		.DB "(C)2006 by info!at!avr-asm-tutorial.net "
		.ELSE
		.IF cLcdLw == 24
			.DB "RechtGenerator M8 V1.0 ",0x0D
			.DB "www.avr-asm-tutorial.net"
			.ELSE
			.IF cLcdLw == 20
				.DB "RechtGenerator V1.0",0x0D
				.DB "avr-asm-tutorial.net"
				.ELSE
				.IF cLcdLw == 16
					.DB "RechtGen M8 V1.0",0x0D,"avr-asm-tutorial",0x00
					.ELSE
					.DB "RG M8 V1",0x0D,"avr-asm"
					.ENDIF
				.ENDIF
			.ENDIF
		.ENDIF
	.ENDIF
.DB 0,0
;
; **************************************************
;       K O N V E R S I O N S T A B E L L E
; **************************************************
;
.INCLUDE "rectgen_m8_table.inc"
;
©2006 by http://www.avr-asm-tutorial.net