Allgemeines zur
PIC-ALU
ganze Zahlen
größer
als 255
ganze
negative Zahlen (signed integer)
Fließkomma-Zahlen
(floating)
32-Bit-Ganzzahl-Addition
PICs sind 8-Bit Microcontroller. Sie besitzen eine ALU (arithmetic logical unit), mit der sie einfache mathematische Operationen durchführen können. Das sind die Addition (ADDWF), die Subtraktion (SUBWF) sowie Boolsche Operationen (AND, OR, XOR ...). (PIC18Fxxx können auch die Multiplikation.)
All diese Operationen können nur
mit
ganzzahligen (integer) 8 Bit großen Zahlen durchgeführt
werden.
Der PIC kennt also nur ganze Zahlen von 0 bis 255. Das ist nicht gerade
viel. Auch der gesamte Datenspeicher ist 8-bitig ausgeführt. Somit
benötigt man für eine 8-Bit-Zahl genau eine Speicherzelle
(Register)
des PIC.
; Speicherplatz
für eine
8-Bit-Zahl festlegen ; der Speicherplatz hat die Adresse 0x20 ; und bekommt den Namen f0 f0 EQU 0x20 ; Speicherplatz für eine 8-Bit Zahl |
Die kleinste Zahl ist 0 und wird im
Binärcode
B'00000000' (hexadezimal 0x00) geschrieben,
die größte Zahl ist 255 und
wird im Binärcode B'11111111' (hexadezimal 0xFF) geschrieben.
Größere Zahlen lassen sich
verarbeiten,
wenn man mit 16, 24 oder 32 Bit rechnen kann. Mit 24 Bit langen Zahlen
lassen sich alle ganzzahligen Werte von 0 bis 16777215 darstellen. Mit
32 Bit kommt man bis 4294967295. Nun kann der PIC aber so große
Zahlen
nicht als Ganzes verarbeiten, er kann sie aber in 8 Bit große
Schnipsel
zerlegt verarbeiten. Eine 16-Bit-Zahl wird einfach in zwei
8-Bit-Hälften
zerteilt abgespeichert. Eine 24-Bit-Zahl benötigt 3
Speicherzellen
und eine 32-Bit-Zahl derer 4.
; Speicherplatz
für eine
24-Bit Zahl festlegen ; bestehend aus 3 Speicherplätzen zu je 8 Bit f0 EQU 0x20 ; Speicherplatz für die unteren 8-Bit der Zahl f1 EQU 0x21 ; Speicherplatz für die mittleren 8-Bit der Zahl f2 EQU 0x22 ; Speicherplatz für die oberen 8-Bit der Zahl |
Die größte 32-Bit Zahl ist binär geschrieben B'11111111111111111111111111111111'. Das ist etwas unübersichtlich, deshalb schreibt man größere Zahlen (und oft auch kleine) nicht als binäre Zahlen sondern als Hexadezimalzahlen. Dabei werden immer 4 Stellen einer Binärzahl als eine Ziffer einer Hexadezimalzahl geschreiben. Als Ziffern verwendet man 0,1,2....9,A,B,C,D,E,F, wobei die Buchstaben A..F für Ziffern der Werte 10 bis 15 stehen. Die größte 32-Bit Zahl schreibt man im hexadezimalen System 'FFFFFFFF'. In der PIC-Welt kennzeichnet man hexadezimale Zahlen mit einem vorgestellten 0x. Also ist unsere Zahl 0xFFFFFFFF.
Natürlich kennt der PIC keinen Befehl um z.B. zwei 32-Bit Zahlen zu addieren. Operationen mit großen Zahlen werden schrittweise mit kleinen Unterprogrammen durchgeführt, die man selber schreiben kann, oder aus einer fertigen Bibliothek nimmt.
Bisher habe wir uns nur mit positiven Zahlen beschäftigt, es gibt aber auch negative Zahlen. Im täglichen Leben hat sich für negative Zahlen die Verwendung eines Vorzeichens (-) durchgesetzt, in der Rechentechnik verwendet man stattdessen das sogenannte Zweierkomplement.
Um von einer Zahl das Zweierkomplement zu bilden, schreibt man sie binär auf, invertiert dann jedes Bit und addiert zum Ergebnis 1 dazu. (Das kling übermäßig kompliziert, aber es vereinfacht später das Rechnen.) Da eine 8-Bit Zahl nur 256 verschiedene Werte annehmen kann, sind das die Werte -128 bis +127. Die positiven Zahlen sehen als Zweierkomplementzahlen genauso aus, wie normale Zahlen. Negative Zahlen erkennt man leicht darann, das ihr höchstwertiges Bit (MSB) eine 1 ist. Zur Wandlung einer 8 Bit-Zahl in ihr negatives Zweierkomplement dient der Befehl NEGF.
Beispiel:
Gesucht ist die binäre Schreibweise für die negative Zahl -9
im Zweierkomplement:
Ausgangswert ist +9 | '0000 1001' |
invertiert ergibt das: | '1111 0110' |
um
1 erhöhen, und man ist fertig: |
'1111 0111' |
Eine -9 schreibt man binär also
'1111 0111'. Das ist aber auch die Schreibweise für die positive
Zahl +247. Es ist leider so, dass die Zweierkomplemente der Zahlen 1 ..
127 genauso aussehen wie die Zahlen 128 .. 255. Um diese
Zweideutigkeit zu vermeiden, muss man eine Entscheidung treffen.
Man muss sich entscheiden, ob man
negative Zahlen braucht oder nicht. Wenn nicht, dann verwendet man unsigned
integer, dass heißt, es gibt kein Zweierkomplement, und
mit 8 Bit reicht man von 0 bis 255.
Benötigt man aber auch negative
Zahlen,
verwendet man das Zweierkomplement und spricht vom signed integer
und kann bei 8 Bit mit Zahlen von -128 bis +127 agieren.
Für die beiden unterschiedlichen Zahlenformate benötigt man unterschiedliche Rechenroutinen.
Üblich sind signed integer mit 8, 16, 24 oder 32 Bit.
Wenn Zahlen zu groß oder zu klein werden, schaltet jeder gute Taschenrechner in die Fließkommadarstellung. Dabei wird eine eine normale Zahl (Mantisse) mit Dezimalpunkt gefolgt vom Exponenten einer Zehnerpotenz dargestellt. Da bedeutet dann 4,1234567x21 nichts anderes als 4,1234567 x 1021. Im Klartext bedeutet das, dass man das Komma eigentlich um 21 Stellen nach rechts verschieben müsste, wollte man die Zahl normal aufschreiben.
Dieses Zahlenformat gibt es auch bei Prozessoren, allerdings nimmt man dort keine Zehnerpotenzen, sondern Zweierpotenzen. Außerdem setzte man das Komma immer eine Stelle weiter links als im Taschenrechner. Das entspräche also 0,41234567x22. Auf diese Art und Weise beginnt jede Zahl mit '0,' und man kann es sich eigentlich sparen, das extra mit '0,' am Anfang abzuspeichern. Im Prozessor speichert man nur 41234567x22 und denkt sich davor das '0,'.
Während ich hier noch mit Dezimalzahlen und Zehnerpotenzen agiert habe, passiert das Ganze im PIC natürlich mit Binärzahlen und Zweierpotenzen. Der Exponent wird als 8-Bit Zahl im Zweierkomplement abgespeichert. Damit lassen sich Potenzen von 2-128 (etwa 0,000000000000000000000000000000000000003 im Dezimalsystem) bis 2127 (etwa 340000000000000000000000000000000000000 im Dezimalsystem) darstellen. Das reicht wohl zur Beschreibung aller Größen im Universum. Die Genauigkeit dieser Zahlen hängt davon ab, mit wievielen Bits der eigentliche Zahlenwert (genannt die Mantisse) abgespeichert wird. Hier sind 16 Bit und 24 Bit (jeweils im Zweierkomplement) üblich.
Insgesamt benötigt man für
Exponent
und Mantisse zusammen also 3 Byte (24-Bit Fließkomma) oder 4 Byte
(32-Bit Fließkomma).
Wenn man zwei 8-Bit-Zahlen addieren will, so steht dafür der ADDWF-Befehl zur Verfügung. Die Addition von 16-, 24-, oder 32-Bit-Zahlen ist auch nicht sehr kompliziert. Ich möchte das am Beispiel einer 32-Bit Addition erläutern.
Jede 32-Bit-Zahl besteht aus 4
Schnipseln
a 8 Bit. Das ist ähnlich einer 4-stelligen Dezimalzahl, die auch
aus
4 einzelnen Ziffern besteht. Wenn man z.B. die beiden Dezimalzahlen 1234
und 8695 addieren möchte, so fängt man bekanntlich
mit
den jeweils letzten Stellen beider Zahlen an, und addiert sie: 4+5=9.
Damit hat man die letzte Ziffer des
Ergebnisses.
Dann adiert man die beiden vorletzten Ziffern u.s.w
Das einzige Problem, das dabei
auftreten
kann, ist, dass die Summe zweier Ziffern größer als 9
ist.
In einem solchen Fall wird die Einerstelle der Summe als neue Ziffer
des
Ergebnisses aufgeschrieben, während die Zehnerstelle (eine 1) bei
der
Addition der nächsthöheren Ziffer mit hinzuaddiert wird. Das
ist in unserem Beispiel bei der Zehnerstelle der Fall: 3+9=12
Die 2 (Einerstelle der 12) wird als
Zehnerstelle
des Ergebisses aufgeschrieben, die 1 (Zehnerstelle der 12) wird bei der
Addition der Hunderterstellen hinzuaddiert: 2+6+1=9
Die Addition der 4-Byte langen 32-Bit-Zahlen erfolgt nach dem gleichen Schema. Anfangs werden jeweils die beiden niederwertigsten Bytes addiert. Ist die Summe kleiner als 256, dann passt sie in das niederwerigste Ergebnisbyte. Die Summe kann aber auch einen Wert zwischen 256 und 511 annnehmen. In diesem Fall haben wir ein 9-Bit langes Ergebnis der 8-Bit Addition. Die unteren 8-Bit bilden das Ergebnisbyte, das 9. Bit (eine 1) wird beim nächsten Byte hinzuaddiert.
Im folgenden Programm werden zwei 32-Bit Integer-Zahlen addiert. Die Zahlen befinden sich in den Registern f3, f2, f1, f0 und xw3, xw2, xw1, xw0. Das 32-Bit Ergebnis wird nach f (also f3, f2, f1, f0) geschrieben. Falls das Ergebnis 33-Bit lang wird, dann steht das 33. Bit zu Schluss im C-Flag des STATUS-Registers. Dieses Flag dient also als Überlaufindikator.
Im Programm werden zuerst die
niederwertigsten
Bytes aus f0 und xw0 addiert. Das Ergebnis gelangt nach f0. Trat dabei
ein Überlauf auf (9. Bit), dann wird xw1 um 1 erhöht. Falls
schon
dadurch xw1 überlaufen sollte (falls xw1 also zufälligerweise
255 war) wird f1 als Ergebnisbyte behalten, und der Übertrag zur
nöchsten
Stelle weitergereicht, ansonsten wird f1 und xw1 addiert u.s.w
;32 bit Adition,
C-Flag bei
Überlauf gesetzt ;********************************************************************* ;* ;* addiere xw (32-byte value) mit f (32-byte value). ;* Input variablen: f (32-bit value), xw (32-bit value) ;* Output variablen: f (32-bit value), 33. bit in STATUS,C ;********************************************************************* Add32 ; 32-bit add: f = f + xw movf xw0,W ; low byte addwf f0,F ; low byte add
movf
xw1,W
;
next
byte
movf
xw2,W
;
next
byte
movf
xw3,W
;
high
byte return ; fertig |
Diese Routine lässt sich
natürlich
leicht auf 24 Bit oder 16 Bit kürzen.
;16 bit Adition,
C-Flag bei
Überlauf gesetzt ;********************************************************************* ;* ;* addiere xw (16-byte value) mit f (16-byte value). ;* Input variablen: f (16-bit value), xw (16-bit value) ;* Output variablen: f (16-bit value), 17. bit in STATUS,C ;********************************************************************* Add16 ; 16-bit add: f = f + xw movf xw0,W ; low byte addwf f0,F ; low byte add
movf
xw1,W
;
high
byte return ; fertig |
Alles was über eine einfache 8-Bit-Addition/Subtraktion hinausgeht, muss man mit Hilfe von kleinen Programmen erledigen. Für die 32-BitAddition haben wir uns gerade solch ein Programm selber ausgedacht. Aber solche Programme gibt es auch schon fertig in Bibliotheken zusammengefasst bei Microchip zum Download, und können in eigene Programme eingebunden werden.
AN617 (integer)
In der Application note AN617 sind
Routinen
für das Multiplizieren und Dividieren von 8, 16, 24 und 32 Bit
Zahlen
enthalten. Wenn man das zur AN617 zugehörige ZIP-File entpackt
hat,
findet man mehrere Dateien mit der Endung '.A16' vor. Die
enthalten
Macros für Multiplikationen und Divisionen aller möglicher
Kombinationen
von 8,16,24,32-Bit langen Zahlen. Natürlich werden eine ganze
Reihe
von vorher definierten Registern benötigt, z.B. um die
Ausgangswerte
und das Ergebnis zu speichern. Die Definitionen dafür findet man
in
der Datei math16.inc
Addition und Subtraktion werden nicht
unterstützt, die sind zu einfach, um dafür extra Bibliotheken
zu bemühen. Ein praktisches Beispiel für die Nutzung der
AN617 findet
man
hier.
AN575 (floating point)
Diese Application note enthält
Routinen für die Grundrechenarten (Addition, Subtraktion,
Multiplikation
und Division) mit Fließkommazahlen (32 Bit und 24 Bit).
Außerdem
sind Routinen enthalten, um integer-Zahlen in floating-point-Zahlen
umzuwandeln,
und auch um floating-point-Zahlen wieder in integer-Zahlen wieder
zurückzuverwandeln.
AN660 (floating point)
Hier findet man eine ganze Reihe von
Routinen,
die über die Grundrechenarten hinausgehen
AN670 (floating point to ASCII)
Im täglichen Leben begegnet der
Mensch
eher selten binären Fließkommazahlen, und so ist er den
Umgang
mit ihnen nicht gewohnt. Wenn man eine Fließkommazahl auf einem
Display
ausgeben will, so sollte man sie der Freundlichkeit halber vorher in
ein
lesbares Format umwandeln. Die dafür nötigen Routinen sind in
der AN670 enthalten.
So werden aus binären
Fließkommazahlen
ASCII-Strings (Zeichenketten) im Dezimalformat, die man direkt auf
einem
Display anzeigen, oder via RS232 an ein Terminalprogramm senden kann.
Autor: sprut
erstellt am: 18.10.2003
letzte Änderung: 26.06.2007