shiftIn() / shiftOut()
Die beiden Arduino Funktionen shiftOut() und shiftIn() funktionieren ähnlich wie die hardwareseitige SPI Schnittstelle. Nur eben als Softwarelösung. Die beiden Funktionen sind wesentlich langsamer als SPI, können aber im Gegensatz dazu an allen I/O Ports eingesetzt werden.
shiftOut()
Der Funktion shiftOut(Data, Clock, Bitorder, Byte) werden folgende Parameter mitgegeben:
- Data: I/O Port über welchen die Daten ausgegeben werden
- Clock: I/O Port an dem das dazugehörende Clock-Signal ausgegeben wird
- Bitorder: Über die Angabe von MSBFIRST oder LSBFIRST kann man die Bitreihenfolge der Übertragung des Datenbytes angegeben werden.(MSBFIRST = von links nach rechts, LSBFIRST = von rechts nach links)
- Byte: Die 8 Bit (ein Byte) die übertragen werden sollen
Für Empfänger die die Daten hardwareseitig mit einem Schieberegister empfangen wird neben der Daten und CLK Leitung noch eine CS (oder auch SS oder Latch) Leitung zur Übertragung benötigt. Über diese Leitung wird das Eingangsregister des Datenempfängers geschalten. Während der Übertragung wird diese Leitung auf LOW gelegt. Sobald CS am Ende wieder auf HIGH geht werde die Daten im Eingangsregister vom Empfänger übernommen. Die genaue Funktion dieser ChipSelect/Latch Leitung wird Kapitel "Schieberegister" im Detail beschrieben.
Beispiel
byte sendeDaten = B10100110;
shiftOut(Daten_Pin, CLK_Pin, MSBFIRST, sendeDaten);
digitalWrite(CS, HIGH);
digitalWrite(CS, LOW);
Source Code von shiftOut()
Hier ist zur Information der in der Arduino IDE hinterlegte Source Code von shiftOut() abgebildet.
Unit8_t wird hier als Synonym für eine byte Variable verwendet.
Abhängig von welcher Seite die Bits übertragen werden sollen (Bitorder LSBFIRST oder MSBFIRST) wird jedes Bit
überprüft ob dieses eine logische '0' oder '1' enthällt. Dieses einzelne Bit wird dann mit
digitalWrite() auf den Daten Ausgang gelegt. Am Ende jedes Bits wird dann mit einem kurzen Clockpuls
dem Empfängerregister signalisiert, dass der Wert auf der Datenleitung übernommen werden soll.
void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val){
uint8_t i;
for (i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST)
digitalWrite(dataPin, !!(val & (1 << i)));
else
digitalWrite(dataPin, !!(val & (1 << (7 - i))));
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
}
}
Mit 1<<i wird eine "walking 1" erzeugt. Bei i=0 ist dies B00000001, bei i=1 B00000010 und bei
i=7 schließlich B10000000. Mit dem bitweisen UND (&) wird das Übertragungsbyte jeweils verglichen. Ist die i’te
Stelle des Bytes ebenfalls 1 wird als Ergebnis 1 zurückgegeben. Ansonsten 0. (Für weitere Infos siehe Kapitel Bitoperationen).
Das Ergebnis von (val & (1 << i)) ist ein Bytewert der 0 ist (Bsp B0000000) oder an
irgendeiner Stelle i eine 1 hat (Bsp B0010000). Für die digitalWrite() Funktion wird aber eine
boolsche '1' oder '0' bzw. HIGH/LOW benötigt und keine Zahl. Mit der logischen Negierung ! wird das
Byte zu einem boolschen Wert umgewandelt. Da aus einer Zahl (Bsp B0010000) durch die Negierung jetzt aber
false geworden ist und aus einer 0 ein boolsches true wird durch ein zweites ! nochmal
zurück negiert.
shiftIn()
Die Funktion shiftIn(Data, Clock, Bitorder) gibt das empfangene Byte als Wert zurück.
Folgende Parameter müssen angegeben werden:
- Data: I/O Port über welchen die Daten empfangen werden
- Clock: I/O Port an dem das dazugehörende Clock-Signal vom Arduino getaktet wird. (Mit dem Clocksignal wird der Sender vom Arduino aus getaktet!)
- Bitorder: Über die Angabe von MSBFIRST oder LSBFIRST kann man festlegen von welcher Richtung die empfangenen Bits zu einem Byte zusammengesetzt werden.(MSBFIRST = von links nach rechts, LSBFIRST = von rechts nach links)
Beispiel
digitalWrite(CS, LOW);
digitalWrite(CS, HIGH);
byte empfangeneDaten = shiftIn(Daten_Pin, CLK_Pin, LSBFIRST);
Serial.println(empfangeneDaten);
delay(500);
Source Code von shiftOut()
unit8_t ist wieder ein Synonym für byte. Sobald die Clockleitung auf HIGH ist wird der
anliegende Pegel von der Datenleitung übernommen und an die i’te Stelle des Empfangsbytes geschrieben.
Anschließend wird die Clockleitung wieder auf LOW gelegt. Der Sender kann jetzt den Pegel des nächsten Bits auf
die Datenleitung legen. Sobald alle 8 Bits empfangen sind, wird mit return value das
zusammengestellte Empfangsbyte ausgegeben.
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
if (bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i;
else
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW);
}
return value;
}