Hier entzaubere ich (wenn ich es denn wirklich richtig verstanden habe) die CodeZeile

pinWrite(DATA, (val & (1 << i)));

aus dem TI Shiftregister Beispielquellcode.

Die Bemühungen Hardwarenah mit C und Assemblerbefehlen zu Programmieren stellen sich als Tortour heraus. Ich habe nun einige Versuche mit dem TI 430 Launchpad gemacht und dabei aus den guten Beispielen auf den TI Seiten funktionierende Aufbauten in Gang gebracht. Viele Zeilen des Codes verstehe ich nur teils oder gar nicht. Ich versuche nun Anhand von Recherche den Programmablauf zu verstehen.

Der Code den ich meine ist hier zu finden:

http://processors.wiki.ti.com/index.php/MSP430_Launchpad_Shift_Register (externer Link)

Damit wird ein Shiftregister angesteuert. Ein Shiftregister funktioniert so, daß durch drei erforderliche Leitungen eine Zahl an das Register übergeben wird die die jeweiligen Pins des Shiftregisters HIGH oder LOW schaltet. Dabei wird bei einem 8-Bit Shiftregister eine Zahl zwischen 0 und 255 übergeben. Das geschieht Binär (Die Zahl wird binär aus acht Stellen dargestellt welche 0 oder 1 sein können).

In dem Codebeispiel gibt es folgenden Absatz:

for (i = 0; i < 8; i++) {
 pinWrite(DATA, (val & (1 << i)));
 pulseClock();
 }

Dies nun Zeile für Zeile erklärt:

for (i = 0; i < 8; i++) {

Es ist der Beginn einer for Schleife welche für die Variable i von Null beginnend den Wert bei jedem Durchlauf um Eins erhöht (i++ = erhöhe i um Eins) solange  i kleiner als Acht ist.

for Schleifen prüfen die nach dem for in runden Klammern gesetzten Bedingungen. Sind diese noch innerhalb der Vorgaben die dort gemacht wurden, wird der Code innerhalb der geschweiften Klammern ausgeführt.

Nun kommt die für mich kryptische Zeile:

pinWrite(DATA, (val & (1 << i)));

Die Zeile führt die Funktion pinWrite() durch welche im Programm auch definiert ist und später von mir erläutert wird. Sie gibt dabei die Werte DATA (Ist im Programmkopf als Port 1.0 definiert) und val & ( 1 << i ) (für mich als Laie vollkommen unverständlich) an die Funktion weiter.

val ist der Wert der in der Funktion shiftOut() mitgegeben wurde. Er bestimmt letztendlich welche Ports 1 und welche 0 sein sollen.

Zur Erinnerung, der Wert liegt bei einem 8-Bit Register zwischen 0 und 255, und wird Binär acht Stellen umfassen.

Ein Beispiel dazu verdeutlicht das ganze. Man stelle sich dazu die Folge von acht Zahlen als die Ports vor. o bedeutet der Port ist aus (also LED aus z.B.) und x bedeutet der Port ist an . So kann man leicht die gewünschten Ports aktivieren indem man die Summe der Bits bildet die leuchten sollen. Es kann dabei sein, dass die Reihenfolge exakt umgekehrt (links<>rechts) ist, je nachdem wie rum das Register arbeitet.

  Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Summe
Wert 128 64 32 16 8 4 2 1 255
x o o o o o o o 128
x o x o o o o o 160
o o o o o x o x 5

Als nächstes findet man das Zeichen welches ein Operator ist der sich UND nennt. Dieser macht folgendes. Er vergleicht den Wert davor und danach und gibt eine 0 aus wenn die Werte beide 0 sind oder einer davon 1. Er gibt eine 1 aus wenn beide 1 sind.

Klingt erstmal seltsam, erschliesst sich aber wenn man es bildlich darstellt. Das mache ich aber erst gleich nachdem ich das (1 << i) erklärt habe.

Das << steht für bitweises Verschieben. Wobei es in Klammern steht damit dies gemacht wird bevor der & Operator es mit val vergleicht. i ist dabei die Variable aus dem Schleifenkopf. Sie wird also mit 0 beginnen und schließlich mit 7 enden, Also genau Acht mal, da es acht Ports sind die wir schalten wollen.

Das Bitschieben lässt sich am besten Binär verstehen. Hier eine kleine Tabelle dazu:

  Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Summe
Wert 128 64 32 16 8 4 2 1 255
o o o o o o o x 1
o o o o o o x o 2
o o o o o x o 0 4
o o o o x o o 0 8
o o o x o o o 0 16
o o x o o o o 0 32
o x o o o o o 0 64
x o o o o o o 0 128
Das x aus der ersten Zeile wird nach links verschoben und steht dann auf Bit 2. Die Werte die dadurch ganz links weiterrücken fallen einfach raus. Was rechts daneben neu reinkommt ist immer 0. Somit wird das eine x um so viele Stellen von Bit1 nach links geschoben wie i angibt. Da es immer von der 1 aus begonnen wird kommen alle 8 Bits im Laufe dieser Schleife an die Reihe.
Jetzt lichtet sich gleich der Nebel.
Wenn nun also angenommen an die acht Pins LEDs angeschlossen sind und wir wollen jeweils die äußeren beiden leuchten lassen stellt es sich so dar:
Bit8 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Summe
Wert 128 64 32 16 8 4 2 1 255
Gewünschte Leuchtreihenfolge x x o o o o x x 195
Schleifendurchlauf
1 o o o o o o o x 1
2 o o o o o o x o 2
3 o o o o o x o 0 4
4 o o o o x o o 0 8
5 o o o x o o o 0 16
6 o o x o o o o 0 32
7 o x o o o o o 0 64
8 x o o o o o o 0 128
Die Durchläufe 1 bis 8 prüfen also nacheinander ob von den Bits der beiden Werte welche beide auf 1 stehen. Da das (1 <<i) immer nur an einer Stelle eine 1 ergibt kann es also nur an einer Stelle vorkommen. Das ist praktisch, da man so jeden Wert der acht Bitpositionen ermitteln kann.
Im ersten Durchlauf steht Bei Bit 1 sowohl beim gewünschten Leuchtwert als auch beim Schleifenwert ein x. Das ergibt 1 und wird im weiteren Programmablauf die Diode anschalten da der Port dann auf High gesetzt wird. Beim zweiten, vorletzen und letzten Durchlauf ist das auch so. Alle anderen ergeben 0, da nie beide Werte ein x sind.
Das ist der Grund warum die Ports so geschaltet werden wie wir es mit der Variable val vorgeben.
Danach kommt noch
pulseClock();

Das startet die Funktion pulseClock(); die die Information des aktuellen Schleifendurchlaufs (also ob 0 oder 1) an das Register durchgibt.

Das erkläre ich sobald ich es verstanden habe.