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