Routinensammlung und Gewusst-wie für Mikrocontroller

Zahlenverarbeitung bei Mikrocontrollern

Hier geht es um die Umwandlung von der internen (maschinenlesbaren) Binärdarstellung in die menschenlesbare ASCII-Darstellung und umgekehrt.

Siehe auch Zahlendarstellung.

Grundlagen

Beachten Sie bei Ihren Mikrocontroller-Projekten, dass diese Umwandlungen relativ zeitintensiv sind. Benutzen Sie daher zur massiven Datenübertragung besser das interne Binärformat. Für Displays und bei numerischen Eingaben kommt man aber um diese Routinen nicht umhin.

Betrachtet werden hier nur Ganzzahlen. Festkommazahlen erreicht man durch "Denken" und Ausgeben eines Kommas an der richtigen Position. Exponential-Darstellungen wie 1E9 werden nicht betrachtet.

Die folgenden Ausführungen beziehen sich nur auf Assembler, weil es diese Konvertierungsroutinen in jeder C-Bibliothek gibt.

itoa - Integer-zu-Ascii

Ein Wort vorweg: Benutzen Sie nach Möglichkeit die Bezeichnung itoa und utoa in Ihrem Assembler-Programm. Da weiß jeder halbwegs erfahrene Leser, was Sie wollen.

Diese Funktion ist deutlich schwieriger als die gegenteilige Routine atoi, basiert auf dem Hornerschema und benötigt eine Divisionsroutine.

Als Bonbon kann man mit jeder Zahlenbasis (Standard: 10) arbeiten; für Hexadezimalzahlen (16) fester Länge existiert ein stark vereinfachter und schnellerer Algorithmus.

Das Hornerschema besagt, die Zahl durch 10 zu teilen, den Rest von rechts nach links auszugeben und mit dem Quotienten so oft zu wiederholen, bis er Null ist. Weil Zahlen besser von links nach rechts ausgegeben werden (das tun semitische Schriften übrigens auch!!), muss man die in verkehrter Reihenfolge anfallenden Ziffern in einem Kellerspeicher zwischenlagern, am einfachsten auf dem Stack.

8051PIC
ATmegaC166
itoa:	;Parameter wie utoa
	adi	r17,0
	brns	utoa
	mov	r18,'-'
	st	x+,r18
	neg	r16
	ldi	r18,0
	subb	r18,r17
	mov	r17,r18
utoa:	;Unsigned-zu-Ascii
;PE: R17:R16=16-bit-Zahl vorzeichenlos
;    R22=Zahlenbasis, R21=Mindest-Ziffernzahl-1
;    R23=Kommastellen, ???=Feldbreite
;    X=Speicher-Zeiger
;PA: X=Zeiger hinter Ziffernfolge
;    R21=vermindert um ausgegebene Zeichen (negativ)
;VR: R16..R21	!UNGETESTET!
	ldi	r20,0	;Ziffern-Push-Zähler
utoa1:	ldi	r18,0	;High-Teil Divisor
	ldi	r19,8	;Rundenzähler für Division 24/8=16R8
utoa2:	add	r16,r16	;R16..R18 linksschieben
	adc	r17,r17
	adc	r18,r18	;hier kommt kein ausgeschobenes Bit vor
	cp	r18,r22	;Wenn größer -> Carry=0
	brcs	utoa3	;springe wenn kleiner
	sub	r18,r22
	ori	r16,1
utoa3:	DJNZ	r19,itoa2
	adi	r18,'0'	;auf Rest ASCII-Null
	;cpi	r18,10
	;sbc	r18,69h
	;das	r18	;hexadezimale Variante mit DAA
	push	r18
	inc	r20	;Push-Zähler
	dec	r23
	brnz	utoa4
	mov	r18,','
	push	r18
	inc	r20
utoa4:	dec	r21
	brns	utoa1	;solange positiv, Ziffern ausgeben
	mov	r18,r16
	or	r18,r17	;Ergebnis Null?
	brnz	utoa1	;weitere Ziffern ausgeben
utoa5:	pop	r18
	st	x+,r18
	DJNZ	r20,utoa5
	ret
Beim PIC macht sich der fehlende Stack störend bemerkbar.

Datensenke: itoa schreibt normalerweise in den Speicher. Dies ist für Mikrocontroller oft ein unpraktischer Zwischenschritt, daher sieht man gern ein Umschaltbit vor, welches die anfallenden Zeichen auf die serielle Schnittstelle schreibt.

Vorzeichen: Vorzeichenbehaftete Zahlen werden vor der Ausgabe positiv gemacht (also bedingt negiert) und ggf. ein Minuszeichen vorher ausgegeben.

Führende Nullen: Für führende Nullen wird einfach eine Mindest- Schleifendurchlaufzahl festgelegt.

Rechtsbündige Ausgabe: Dazu sind Leerzeichen am Anfang auszugeben. Oder man gibt die ganze Zahl von rechts nach links aus (nur bei Displays möglich), darf aber das Löschen alter Ziffern nicht vergessen.

Hexadezimalzahlen: Für diesen Fall wird üblicherweise tetradenweise vorgegangen und der sonst so sinnlose DAA-Befehl genial missbraucht:

8051PIC
ATmegaC166

Komma-Ausgabe: Sie müssen nicht nur beachten, an der richtigen Stelle ein Komma (oder Punkt) auszugeben, sondern auch den Algorithmus verändern, dass die Schleife mindestens [Nachkommastellen+1] Durchläufe macht, um alle erforderlichen Nullen auszugeben. Genauso wie beim Punkt für "führende Nullen".

Typografie: In Deutschland und vielen europäischen Ländern wird das Komma als korrektes Dezimaltrennzeichen angesehen. Auch an Gruppierungszeichen sollte man bei langen Zahlen denken.

Nebenergebnis: itoa liefert zusätzlich:

Man benötigt diese für die Funktion printf.

Eine geeignete Include-Datei für AVR...

2. atoi - Ascii-zu-Integer

Ein Wort vorweg: Benutzen Sie nach Möglichkeit die Bezeichnung atoi in Ihrem Assembler-Programm. Da weiß jeder halbwegs erfahrene Leser, was Sie wollen.

Algorithmus: Zunächst wird ein Akkumulator definiert und auf Null gesetzt. Zweckmäßigerweise parst man von der gegebenen Zeichenkette führende Leerzeichen und Tabulatoren weg, im einfachsten Fall alle Zeichen <21h. Dann arbeitet man sich von links nach rechts durch die Ziffern-Zeichenkette und addiert jede Ziffer auf den Akkumulator, der vorher mit der Zahlenbasis (10) multipliziert wird. Mit der ersten Nicht-Ziffer bricht die Schleife ab. Bei Überlauf des Akkumulators sollte die Schleife nicht abbrechen, statt dessen ein Fehler-Bit setzen. Ein weiteres Bit sollte gelöscht werden, wenn eine Ziffer vorgefunden wurde. Je nach erwartetem Zahlenformat sollten Gruppierungszeichen (Punkt oder einzelnes Leerzeichen im deutschen, Komma im englischen) überlesen werden.

8051PIC
ATmegaC166

Für Hexadezimalzahlen müssen große und kleine Buchstaben in entsprechende Ziffern gewandelt werden. Abgesehen vom 4fachen Linksschieben an Stelle der Multiplikation gibt es keine weiteren Besonderheiten.

Automatische Zahlenbasis: Normalerweise nimmt man dezimale Zahlen an. Erst recht bei Festkomma. Die Vorgabe-Zahlenbasis ist jedoch beliebig. Folgende Präfixe dienen üblicherweise zur Umschaltung:

Suffixe wie in Assembler üblich (h=hex, d=dez, o=oktal, b=binär) sind schwer zu parsen!! Daher meidet man diese Variante gerne.

Datenquelle: atoi liest normalerweise vom Speicher. Dies ist beim Mikrocontroller oft unpraktisch; daher benutzt man gern ein Umschaltbit, welches die Ziffern (meist) von der seriellen Schnittstelle liest.

Vorzeichen: Ein vorangehendes Vorzeichen merkt man sich in einem Bit-Register und negiert am Schleifenende den Akkumulator.

Festkomma: Nach dem Einlesen des Kommas (oder Punktes, oder an der ersten sonstigen Nicht-Ziffer) setzt man die Schleife für [Nachkommastellen] Durchläufe zwangsweise fort und nimmt die Null als gelesene Ziffer an, falls keine weiteren folgen. Überschüssige Nachkommastellen müssen anschließend "weggeparst" werden.

Nebenergebnis: atoi liefert weitere wichtige Nebenergebnisse:

Eine geeignete Include-Datei für AVR...