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