ShiftOut vs. digitalWrite

Hallo,

ich habe an einem Nano zwei Schieberegister 74HC595 in Reihe geschaltet, über die ich einen Barcode mit 16 LED ansteuere. An einem analogen Eingang habe ich ein Poti angeschlossen. Ich will nun analog zur Potistellung am Eingang eine entsprechende Anzahl von LED leuchten lassen.

Ich habe dazu ein Sketch geschrieben, zunächst mit digitalWrite-Befehlen, hier der Teil der Datenübergabe ans Register (ledCount ist hier 16):

for (int i = 0; i < ledCount; i++) {
    resetPins(); // Pin-Reset zur Vorbereitung für Takt-Flankensteuerung
    digitalWrite(datenPin, ledArray[i]);
    digitalWrite(taktPin, HIGH);
    Serial.println(ledArray[i]); //Anzeige der Zustände der Array-Ausgänge
  }
  digitalWrite(speicherPin, HIGH); //Aktivierung der Ausgänge des Schieberegisters
  delay(200);
}

//Reset des Takt- und Speicherpins zur Vorbereitung der neuen Datenübergabe:
void resetPins() {
  digitalWrite(taktPin, LOW);
  digitalWrite(speicherPin, LOW);
}

Das funktioniert tadellos, am seriellen Monitor werden die jeweiligen Bits des Array (=Ausgänge Schieberegister) entsprechend dem ausgelesenen Rohwert am Poti-Eingang korrekt angezeigt und die LEDs leuchten auch so.

Wenn ich im oberen Code die beiden Zeilen

    digitalWrite(datenPin, ledArray[i]);
    digitalWrite(taktPin, HIGH);

durch

shiftOut(datenPin, taktPin, MSBFIRST, ledArray[i]);

ersetze, werden mir am seriellen Monitor die selben Werte angezeigt (also eigentlich alles ok), aber es werden nur die jeweils ersten LED an den beiden Schieberegistern bei Maximalwerten am analogen Eingang angesteuert. Sobald der Wert unter 14 (von 16 LED) geht, leuchtet keine mehr.

Nun bin ich etwas ratlos, anscheinend interpretiere ich die Funktionsweise des shiftOut-Befehls nicht richtig. Weiß jemand einen Rat?

fragt:

der Lutze

im Anhang der komplette Sketch.

Poti_ShiftReg_BarGr_mit_map_u_mehr_Reg_ShiftOut.ino (2.33 KB)

digitalWrite() schreibt ein Bit. shiftOut() schreibt nacheinander alle Bits in dem Byte das du übergibst. Also 8 Stück:

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);		
	}
}

Am schnellsten geht es per SPI und transfer(). Ist aber nicht unbedingt nötig.

Hallo Serenifly,

danke - das erklärt, warum nur die ersten beiden LED leuchten. Und shiftOut kann nur 8 LED, muss also verdoppelt werden (bzw. die Anzahl der LED auf 8 begrenzt). Aber wie bekomme ich die Zustände aus dem Array in leuchtende/nicht leuchtende LED übersetzt?

Ich lasse mir die so ermitteln

RawValue = analogRead(potIn); //Auslesen Poti - Eingang
int LEDlevel = map (RawValue, 0, EndWert, 0, ledCount); //Mapping Rohwert auf LEDs
  int ledArray[ledCount] = {}; // Array für die LED-Zustände
  //Wenn der Index des Array kleiner ist als das dem Rohwert entsprechende Led-Level, dann setze Wert auf 1, sonst 0:
  for (int aktLED = 0; aktLED < ledCount; aktLED++) {
    if (aktLED < LEDlevel) {
      ledArray[aktLED] = 1;
    }
    else {
      ledArray[aktLED] = 0;
    }
  }

Dann hab ich nun versucht, die ohne die Schleife zu übergeben:

digitalWrite(speicherPin, LOW); 
shiftOut(datenPin, taktPin, MSBFIRST, ledArray);
digitalWrite(speicherPin, HIGH);
delay(200);

Nun leuchten irgendwelche LED, unabhängig vom Rohwert am analogen Eingang.

Irgendwo hängts - aber wo?

Lutz

hi,

Du brauchst ein

byte LEDArray[2]

Du schickst 16 "werte", also eigentlich 2mal 8 bit, eins für's erste, eins für's zweite schieberegister.

wenn Du mit dem ersten shiftOut LEDArray[0] schickst, also das erste byte, passiert folgendes:

in LEDArray[0] steht zb der bytewert 17, das ist binär 00010001, also leuchten am ersten schieberegister die LEDs 1 und 5.

dann machst Du ein zweites shiftOut fürs zweite schieberegister mit dem entsprechenden wert und gut is.

den speicherpin auf LOW, dann beide shiftOut und dann den speicherpin wieder auf HIGH.

gruß stefan

EDIT:

Hallo Stefan,

das ist komplizierter, als ich dachte. Um es zu vereinfachen, beschränke ich mich auf ein Schieberegister.

Was ich nicht verstehe: ich liefere doch einen ledArray z.B. in Mittelstellung des Potis von {1,1,1,1,0,0,0,0}. Das kann ich mir auch so per seriellem Monitor anzeigen lassen. Ich dachte nun, dass diese 8 Werte an das Schieberegister so übertragen werden und die entsprechenden LEDs leuchten.

Das ist aber nicht der Fall, es leuchten 5 LED und die nicht hintereinander und unbeeinflusst vom Rohwert.

Bzw. wie kann ich mit dem Rohwert ein - wie von Dir vorgeschlagen - byte LEDArray[2] füttern, dass er mir eine entsprechende Reihe von LEDs ansteuert. (Ich will nicht irgendwelche LED, die dem Binärwert des Rohwertes entsprechen, anzeigen, sondern sozusagen eine "Füllstandsanzeige" von bei der ersten (grünen) LED beginnend bis hin zu den letzten (roten) LED.

Oder bin ich hier völlig auf der falschen Fährte und die shiftOut-Funktion ist gar nichts für meinen Anwendungsfall?

fragt:

der Lutze

Du speicherst anscheinend jedes Bit als ein Byte. Nimm statt dessen für jeden Zustand auch wirklich nur ein Bit in einem Byte. Also für 8 LEDs ein Byte. Dann passt es.

Geht z.B. mit bitWrite:
https://www.arduino.cc/en/Reference/BitWrite

Das ist die Lösung!

Zwar ist in diesem Falle eine einfache Lösung über 2mal digitalWrite einfacher als die shiftOut-Funktion, aber ich habe jetzt zumindest eine leise Ahnung, wie das mit den Bits und Bytes beim Shiftregister (und auch sonst) ist. :wink:

Vielen Dank!

Gruss

Lutz

Du solltest dich nicht nur darauf festlegen was in Moment einfacher ist. Jedes Bit als ein Byte zu kodieren kann dich später mal ganz gewaltig stören wenn du z.B. eine Matrix hast und größere Muster abspeichern willst. Und wenn du mal wegen der Geschwindigkeit wirklich SPI brauchst geht es so auch nicht.

Und die einzelnen Bits eines Bits zu setzen und zu lesen ist letztlich auch nicht wirklich komplizierter.