Pfad: Home =>
AVR-Übersicht =>
Binäres Rechnen =>
Festkommazahl => 10-Bit-Wandlung
Assembler Quelltext der Umwandlung einer 10-Bit-Zahl in eine vierstellige Festkommazahl
; Demonstriert Fließkomma-Umwandlung in
; Assembler, (C)2003 www.avr-asm-tutorial.net
;
; Die Aufgabe: Ein 10-Bit-Analog-Wandler-Signal
; in Binärform wird eingelesen, die Zahlen
; reichen von hex 0000 bis 03FF.
; Diese Zahlen sind umzurechnen in eine
; Fließkommazahl zwischen 0,000 bis 5,000
; Volt.
;
; Der Programmablauf:
; 1. Die Eingabezahl wird geprüft, ob sie
; kleiner als $0400 ist.
; Das vermeidet Überläufe bei der anschlie-
; ßenden Multiplikation.
; 2. Multiplikation mit 320.313 (hex 04E338).
; Dieser Schritt multipliziert die Zahl mit
; den Faktoren 5.000 und 65.536 und divi-
; diert mit 1.023 in einem Schritt.
; 3. Das Ergebnis wird gerundet und die letzten
; beiden Bytes abgeschnitten.
; Dieser Schritt dividiert durch 65.536,
; indem die beiden letzten Bytes des Ergeb-
; nisses ignoriert werden. Davor wird das
; Bit 15 des Ergebnisse abgefragt und zum
; Runden des Ergebnisses verwendet.
; 4. Die erhaltene Zahl wird in ASCII-Zeichen
; umgewandelt und mit einem Dezimalpunkt
; versehen.
; Das Ergebnis, eine Zahl zwischen 0 und
; 5.000 wird in ASCII-Zeichen umgewandelt.
;
; Die verwendeten Register:
; Die Routinen benutzen die Register R10 bis R1,
; ohne diese vorher zu sichern. Zusätzlich wird
; das Vielzweckregister rmp verwendet, das in
; der oberen Registerhälfte liegen muss. Bitte
; beachten, dass diese verwendeten Register
; nicht mit anderen Verwendungen in Konflikt
; kommen können.
;
; Zu Beginn wird die 10-Bit-Zahl im Register-
; paar R2:R1 erwartet. Wenn die Zahl größer
; ist als $03FF, dann kehrt die Prüfroutine
; mit einem gesetzten Carry-Flag zurück und
; der Ergebnistext in R5:R6:R7:R8:R9:R10 wird
; auf den null-terminierten Ausdruck "E.EEEE"
; gesetzt.
; Die Multiplikation verwendet R6:R5:R4:R3 zur
; Speicherung des Multiplikators 320.313 (der
; bei der Berechnung maximal 10 mal links ge-
; schoben wird - Multiplikation mit 2). Das
; Ergebnis der Multiplikation wird in den Re-
; gistern R10:R9:R8:R7 berechnet.
; Das Ergebnis der sogenannten Division durch
; 65.536 durch Ignorieren von R8:R7 des Ergeb-
; nisses ist in R10:R9 gespeichert. Abhängig
; von Bit 7 in R8 wird zum Registerpaar R10:R9
; eine Eins addiert. Das Ergebnis in R10:R9
; wird in das Registerpaar R2:R1 kopiert.
; Die Umwandlung der Binärzahl in R2:R1 in
; eine ASCII-Zeichenfolge verwendet das Paar
; R4:R3 als Divisor für die Umwandlung. Das
; Ergebnis, der ASCII-String, ist in den Re-
; gistern R5:R6:R7:R8:R9:R10 (Null-terminiert)
; abgelegt.
;
; Weitere Bedingungen:
; Die Umwandlung benutzt Unterprogramme, daher
; muss der Stapel funktionieren. Es werden
; maximal drei Ebenen Unterprogrammaufrufe
; verschachtelt (benötigt sechs Byte SRAM).
;
; Umwandlungszeiten:
; Die gesamte Umwandlung benötigt 326 Taktzyklen
; maximal (Umwandlung von $03FF) bzw. 111 Takt-
; zyklen (Umwandlung von $0000). Bei 4 MHz Takt
; entspricht dies 81,25 Mikrosekunden bzw. 27,5
; Mikrosekunden.
;
; Definitionen:
; Register
.DEF rmp = R16 ; als Vielzweckregister verwendet
;
; AVR-Typ
; Getestet für den Typ AT90S8515, die Angabe
; wird nur für den Stapel benötigt. Die Routinen
; arbeiten auf anderen AT90S-Prozessoren genauso
; gut.
.NOLIST
.INCLUDE "8515def.inc"
.LIST
;
; Start des Testprogramms
;
; Schreibt eine Zahl in R2:R1 und startet die Wand-
; lungsroutine, nur für Testzwecke.
;
.CSEG
.ORG $0000
rjmp main
;
main:
ldi rmp,HIGH(RAMEND) ; Richte den Stapel ein
out SPH,rmp
ldi rmp,LOW(RAMEND)
out SPL,rmp
ldi rmp,$03 ; Wandle $03FF um
mov R2,rmp
ldi rmp,$FF
mov R1,rmp
rcall fpconv10 ; Rufe die Umwandlungsroutine
no_end: ; unendliche Schleife, wenn fertig
rjmp no_end
;
; Ablaufssteuerung der Umwandlungsroutine, ruft die
; verschiedenen Teilschritte auf
;
fpconv10:
rcall fpconv10c ; Prüfe die Eingabezahl in R2:R1
brcs fpconv10e ; Wenn Carry, dann Ergebnis="E.EEE"
rcall fpconv10m ; Multipliziere mit 320.313
rcall fpconv10r ; Runden und Division mit 65536
rcall fpconv10a ; Umwandlung in ASCII-String
rjmp fpconv10f ; Setze Dezimalpunkt und Nullabschluss
fpconv10e:
ldi rmp,'E' ; Fehlermeldung in Ergebnistext
mov R5,rmp
mov R7,rmp
mov R8,rmp
mov R9, rmp
fpconv10f:
ldi rmp,'.' ; Setze Dezimalpunkt
mov R6,rmp
clr rmp ; Null-Abschluss setzen
mov R10,rmp
ret ; Alles fertig
;
; Unterprogramm Eingabeprüfung
;
fpconv10c:
ldi rmp,$03 ; Vergleiche MSB mit 03
cp rmp,R2 ; wenn R2>$03, setze carry bei Rückkehr
ret
;
; Unterprogramm Multiplikation mit 320.313
;
; Startbedingung:
; +---+---+
; | R2+ R1| Eingabezahl
; +---+---+
; +---+---+---+---+
; | R6| R5| R4| R3| Multiplikant 320.313 = $00 04 E3 38
; | 00| 04| E3| 38|
; +---+---+---+---+
; +---+---+---+---+
; |R10| R9| R8| R7| Resultat
; | 00| 00| 00| 00|
; +---+---+---+---+
;
fpconv10m:
clr R6 ; Setze den Multiplikant auf 320.313
ldi rmp,$04
mov R5,rmp
ldi rmp,$E3
mov R4,rmp
ldi rmp,$38
mov R3,rmp
clr R10 ; leere Ergebnisregister
clr R9
clr R8
clr R7
fpconv10m1:
mov rmp,R1 ; Prüfe ob noch Bits zu multiplizieren
or rmp,R2 ; Irgendein Bit Eins?
brne fpconv10m2 ; Noch Einsen, mach weiter
ret ; fertig, kehre zurück
fpconv10m2:
lsr R2 ; Schiebe MSB nach rechts (teilen durch 2)
ror R1 ; Rotiere LSB rechts und setze Bit 7
brcc fpconv10m3 ; Wenn das niedrigste Bit eine 0 war,
; dann überspringe den Additionsschritt
add R7,R3 ; Addiere die Zahl in R6:R5:R4:R3 zum Ergebnis
adc R8,R4
adc R9,R5
adc R10,R6
fpconv10m3:
lsl R3 ; Multipliziere R6:R5:R4:R3 mit 2
rol R4
rol R5
rol R6
rjmp fpconv10m1 ; Wiederhole für das nächste Bit
;
; Runde die Zahl in R10:R9 mit dem Wert von Bit 7 von R8
;
fpconv10r:
clr rmp ; Null nach rmp
lsl R8 ; Rotiere Bit 7 ins Carry
adc R9,rmp ; Addiere LSB mit Übertrag
adc R10,rmp ; Addiere MSB mit Übertrag
mov R2,R10 ; Kopiere den Wert nach R2:R1 (durch 65536 teilen)
mov R1,R9
ret
;
; Wandle das Wort in R2:R1 in einen ASCII-Text in R5:R6:R7:R8:R9:R10
;
; +---+---+
; + R2| R1| Eingangswert 0..5.000
; +---+---+
; +---+---+
; | R4| R3| Dezimalteiler
; +---+---+
; +---+---+---+---+---+---+
; | R5| R6| R7| R8| R9|R10| Ergebnistext (für Einmgangswert 5,000)
; |'5'|'.'|'0'|'0'|'0'|$00| mit Null-Abschluss
; +---+---+---+---+---+---+
;
fpconv10a:
ldi rmp,HIGH(1000) ; Setze Dezimalteiler auf 1.000
mov R4,rmp
ldi rmp,LOW(1000)
mov R3,rmp
rcall fpconv10d ; Hole ASCII-Ziffer durch wiederholtes Abziehen
mov R5,rmp ; Setze Tausender Zeichen
clr R4 ; Setze Dezimalteiler auf 100
ldi rmp,100
mov R3,rmp
rcall fpconv10d ; Hole die nächste Ziffer
mov R7,rmp ; Setze Hunderter Zeichen
ldi rmp,10 ; Setze Dezimalteiler auf 10
mov R3,rmp
rcall fpconv10d ; Hole die nächste Ziffer
mov R8,rmp ; Setze Zehner Zeichen
ldi rmp,'0' ; Wandle Rest in ASCII-Ziffer um
add rmp,R1
mov R9,rmp ; Setze Einer Zeichen
ret
;
; Wandle Binärwort in R2:R1 in eine Dezimalziffer durch fortgesetztes
; Abziehen des Dezimalteilers in R4:R3 (1000, 100, 10)
;
fpconv10d:
ldi rmp,'0' ; Beginne mit ASCII-0
fpconv10d1:
cp R1,R3 ; Vergleiche Wort mit Teiler
cpc R2,R4
brcc fpconv10d2 ; Carry nicht gesetzt, subtrahiere Teiler
ret ; fertig
fpconv10d2:
sub R1,R3 ; Subtrahiere Teilerwert
sbc R2,R4
inc rmp ; Ziffer um eins erhöhen
rjmp fpconv10d1 ; und noch einmal von vorne
;
; Ende der Fließkomma-Umwandlungsroutinen
;
;
; Ende des Umwandlungstestprogramms
;
©2003 by http://www.avr-asm-tutorial.net