;
; ***************************************************
; * 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"
;