17.13 Bitfelder
 
Bitfelder sind Strukturelemente, die mit weniger als 1 Byte in eine Struktur gepackt werden können. Laut ANSI C müssen die einzelnen Elemente von Bitfeldern vom Datentyp int oder unsigned int sein.
Als Beispiel soll hier ein Roboter für eine Fließbandproduktion programmiert werden. Der Roboter muss ein bestimmtes Produkt von Position A nach Position B transportieren. Hier die einzelnen Arbeitsabläufe, die dazu erforderlich sind:
1. |
Produkt zum Befördern vorhanden (Sensor 1 = aktiv). Wenn ja … |
|
|
2. |
Produkt erfassen und hochheben und auf das Fließband legen. Wenn Produkt auf Fließband liegt (Sensor 2 = aktiv), dann ... |
|
|
3. |
Fließband bewegen (Schalter 1) und warten, bis Produkt beim Sensor 3 ankommt. Dann wieder prüfen, ob ein Produkt auf Sensor 1 vorhanden ist. Falls an Sensor 1 ein Produkt vorhanden ist, alles wieder von vorn. |
|
|
Dieser Vorgang soll auch überprüft werden, und bei einem Fehler wird ein Fehlercode ausgegeben, für den es eine entsprechende Stringtabelle gibt. Außerdem wird dazu eine Anzeige benötigt. Es wird davon ausgegangen, dass der Roboter bereits mit dem PC verbunden ist. Hier die Struktur des Roboters:
struct robo {
unsigned char sensor1;
unsigned char sensor2;
unsigned char sensor3;
unsigned char schalter;
int Ausgabe;
} Roboter1;
Abgesehen davon, dass dies in ANSI C nicht erlaubt ist, benötigt der Roboter mit dieser Struktur 48 Bits (6 Bytes). Wenn jetzt noch mehrere Roboter hinzukommen, ist das eine Speicherplatzverschwendung. Häufig ist bei solchen Automatisierungs-Robotern nicht unbegrenzt Speicherplatz vorhanden. Bei den Sensoren und Schaltern benötigen Sie in der Regel nur zwei Schaltstellungen: 1 für betätigt und 0 für unbetätigt. In C ist es auch möglich, einzelne Bits einer Struktur anzusprechen. Hier die entsprechende Struktur:
struct robo {
unsigned sensor1:1;
unsigned sensor2:1;
unsigned sensor3:1;
unsigned schalter:1;
unsigned Ausgabe:4;
} Roboter1;
Jetzt benötigt die Struktur nur noch acht Bits (ein Byte). Der Trick ist eigentlich ganz einfach: Es wurde hier das Schlüsselwort unsigned benutzt, das intern im Prozessor auf 0 gesetzt ist. Das Schlüsselwort unsigned benötigt ein Bit, und mit einem Bit kann man den Wert 1 oder 0 darstellen. Die Variablen-Ausgabe enthält vier Bits, womit eine Zahl bis 16 dargestellt werden kann (2*2*2*2).
Hier das Beispiel dazu:
/* bitfields.c */
#include <stdio.h>
#include <stdlib.h>
enum{ OFF, ON };
struct robo {
unsigned Sensor1:1;
unsigned Sensor2:1;
unsigned Sensor3:1;
unsigned Schalter:1;
unsigned Ausgabe:4;
} Roboter1;
char *msg[7] = {
"Kein Signal an Sensor 1!\n",
"Kein Signal an Sensor 2!\n",
"Kein Signal an Sensor 3!\n",
"Schalter 1 nicht betätigt!\n",
"Notaus betätigt!\n",
"Kein Strom!\n"
};
int main(void) {
int anzahl;
do {
printf("Wie viele Produkte von Pos.A nach Pos.B : ");
do{ scanf("%d",&anzahl); } while(getchar() != '\n');
while((anzahl>0) && (anzahl--)) {
/* Sollte durch echte Schnittstelle ersetzt werden */
Roboter1.Sensor1=ON;
printf("Sensor 1 ist aktiv\n");
if(Roboter1.Sensor1 == ON)
printf("Produkt wird aufgenommen und "
"zum Fließband transportiert\n");
else {
/* Fehler Sensor 1 nicht aktiv
* Fehlermeldung ausgeben */
Roboter1.Ausgabe = 0;
printf("%s\n", msg[Roboter1.Ausgabe]);
}
/* Sollte durch echte Schnittstelle ersetzt werden */
Roboter1.Sensor2=ON;
printf("Sensor 2 ist aktiv\n");
if(Roboter1.Sensor2 == ON) {
printf("Produkt ist auf dem Fließband\n");
printf("Bitte >>ENTER<< drücken"
" für den Schalter\n");
getchar();
printf("Schalter ist eingeschaltet!\n");
/* Sollte durch echte Schnittstelle
* ersetzt werden */
Roboter1.Schalter=ON;
}
else {
Roboter1.Ausgabe=1;
printf("%s\n",msg[Roboter1.Ausgabe]);
Roboter1.Ausgabe=3;
printf("%s\n", msg[Roboter1.Ausgabe]);
}
/* Sollte durch echte Schnittstelle
* ersetzt werden */
Roboter1.Sensor3=ON;
printf("Sensor 3 aktiv\n");
if(Roboter1.Sensor3 == ON) {
printf("Produkt am Ziel angekommen!\n");
printf("Schalter für Fließband auf OFF\n");
printf("Roboter wieder betriebsbereit\n");
printf("Weiter mit >>ENTER<<\n");
getchar();
Roboter1.Schalter=OFF;
}
else {
Roboter1.Ausgabe = 2;
printf("%s\n", msg[Roboter1.Ausgabe]);
}
}
} while(anzahl > 0);
Roboter1.Sensor1=OFF;
Roboter1.Sensor2=OFF;
Roboter1.Sensor3=OFF;
Roboter1.Ausgabe=0;
printf("%s\n",msg[Roboter1.Ausgabe]);
return EXIT_SUCCESS;
}
 Hier klicken, um das Bild zu Vergrößern
Abbildung 17.11
Der Roboter im Einsatz
Dass die Struktur robo hier vier Bytes anstatt einem Byte groß ist, liegt am Alignment des Betriebssystems. Hier kann der Speicherplatz mit dem Keyword attribute oder dem Pragma pack auf ein Byte zusammengepackt werden. Vorausgesetzt, der Compiler unterstützt dies:
//#pragma pack(1)
struct robo {
unsigned Sensor1:1;
unsigned Sensor2:1;
unsigned Sensor3:1;
unsigned Schalter:1;
unsigned Ausgabe:4;
} __attribute__ ((packed)) Roboter1;
Diese Struktur nochmals bildlich:
 Hier klicken, um das Bild zu Vergrößern
Abbildung 17.12
Bitbelegung der einzelnen Bitfelder der Struktur robo
Das Dumme an diesem Beispiel ist, dass es zu gar nichts taugt, da keine Verbindung mit einem Roboter besteht. Zeilen wie
Roboter1.Sensor1=ON;
Roboter1.Sensor2=ON;
müssen selbst eingegeben werden.
Daher folgt ein ausführbares Beispiel, welches zeigt, was eine Schnittstelle bzw. eine Adresse zum PC genau ist. Es wird der Druckerstatus am Port LPT1 überprüft. Das Listing ist nur unter MS-DOS ausführbar und nicht ANSI C-konform:
/* check_printer.c */
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
/* 0x378 ist die Adresse der Schnittstelle von LPT1 */
#define LPT1_PORT 0x378
struct status {
unsigned :3; /* Bit 0–2 nicht verwendet */
unsigned fehler :1; /* 0=Druckerfehler */
unsigned online :1; /* 1=Drucker online */
unsigned papier :1; /* 1=kein Papier */
unsigned empfang:1; /* Emfangsbestätigung */
unsigned busy :1; /* Drucker bereit */
} LPT1_status;
/* Status am LPT1-Port auslesen */
void druckerstatus(char *statuszeiger) {
*statuszeiger = inp( LPT1_PORT+1 ) & 0xF8;
}
int main(void) {
druckerstatus( (char *) &LPT1_status);
if(LPT1_status.busy && LPT1_status.online) {
printf("Drucker ist bereit!\n");
return EXIT_SUCCESS;
}
else if(!LPT1_status.online)
printf("Drucker nicht online!\n");
else if(LPT1_status.papier)
printf("Kein Papier vorhanden!\n");
else
printf("Drucker ist nicht bereit!\n");
return EXIT_SUCCESS;
}
Die Adresse 0x378 stellt die Adresse des Ports LPT1 dar. Das Statusregister, das hier überprüft wird, sieht intern aus, wie in Abbildung 17.13 beschrieben.
Diese Struktur ähnelt der Struktur, die oben bei den Robotern verwendet wurde. Die Bits 0–2 werden nicht verwendet. Die Bits 3–7 geben anschließend den Status des Druckers zurück. Je nachdem, welche Bits gesetzt sind und welche nicht. Die Funktion druckerstatus() liefert den Status zurück. Neu ist bei diesem Programm:
unsigned:3;
Hiermit werden drei Bits ohne Namen definiert. Im Beispiel sind es die ersten drei Bits, die ungenutzt bleiben. Sie können im Programm nicht verwendet werden und werden als anonyme Bitfelder bezeichnet.
 Hier klicken, um das Bild zu Vergrößern
Abbildung 17.13
Bitbelegung eines Druckers am LPT1-Port unter MS-DOS
|