Forum: Mikrocontroller und Digitale Elektronik HC595 Shift Register Output


von Sebastian S. (sesc360)


Lesenswert?

Hallo zusammen,

ich versuche momentan das Zusammenspiel zwischen einem 595 und dem 
ATMEGA328 zu verstehen und habe ein Testsetup aufgesetzt.

8 LEDs connected zu Q0-Q7 des 595ers, DS an PB0, SHCP and PB1 und STCP 
an PB2 des AVR.

der 595 Code:

**MAIN**

    // AVR
    #include <avr/io.h>
    #include <util/delay.h>

    // CUSTOM
    #include <systemDefinitions.h>
    #include <pinDefinitions.h>
    #include <HC595.h>


    int main(void)
    {
      while (1) {
      }

      return (0);
    }


**HC595.h**

    #define HC595_PORT PORTB
    #define HC595_DDR DDRB
    #define HC595_DS_POS PB0
    #define HC595_SH_CP_POS PB1
    #define HC595_ST_CP_POS PB2

    #define HC595DataHigh() (HC595_PORT |= (1 << HC595_DS_POS))
    #define HC595DataLow() (HC595_PORT &= (~(1<<HC595_DS_POS)))

    void HC595Init() {
      // Make DS, SHCP and STCP output
      HC595_DDR |= ((1 << HC595_SH_CP_POS) | (1 << HC595_ST_CP_POS) | (1 
<< HC595_DS_POS));
    }

    // Sends a clock pulse on SHCP Line
    void HC595Pulse() {
      // Pulse shift clock
      HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
      HC595_PORT &= (~(1<<HC595_SH_CP_POS)); // LOW
    }

    // Sends a clock pulse on STCP Line
    void HC595Latch() {
      // Pulse the store clock
      HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
      _delay_loop_1(1);

      HC595_PORT &= (~(1<<HC595_ST_CP_POS)); // LOW
      _delay_loop_1(1);
    }

    /*

    Main High level function to write a single byte to
    Output shift register 74HC595.

    Arguments:
       single byte to write to the 74HC595 IC

    Returns:
       NONE

    Description:
       The byte is serially transfered to 74HC595
       and then latched. The byte is then available on
       output line Q0 to Q7 of the HC595 IC.

    */

    void HC595Write(uint8_t data) {
      // Send each 8 bits serially
      // Order is MSB first
      for (uint8_t i = 0; i < 8; i++) {
        // Output the data on DS line according to the value of MSB
        if (data & 0b10000000) {
          // MSB is 1 so output HIGH
          HC595DataHigh();
        } else {
          // MSB is 0 so output LOW
          HC595DataLow();
        }
        HC595Pulse(); // Pulse the clock line
        data = data << 1; // Now bring the next bit at MSB Position
      }

      // Now all 8 bits have been transferred to shift register and move 
them to output latch at ones
      HC595Latch();
    }


Der Code den ich ausführe auf dem AVR ist:

    int main(void)
    {
      uint8_t led = 0b11111111;
      HC595Init();

      HC595Write(led);

      return (0);
    }

Meine Erwartung wäre, das jetzt alle 8 LEDs aufleuchten. Aber statt 
11111111 erhalte ich 10101010 auf den LEDs. Wie kann das sein? Habe ich 
was vergessen?

von Teo D. (teoderix)


Lesenswert?

Tja... Beim Aufbau Mist gebaut, was nicht beachtet zB. 
Abblockkondensator, Vorwiderstände...?

von Sebastian S. (sesc360)


Angehängte Dateien:

Lesenswert?

Ich habe mich an diesem Beispiel orientiert.

von Wolfgang (Gast)


Lesenswert?

Sebastian S. schrieb:
> Ich habe mich an diesem Beispiel orientiert.

Und wo sind da die Abblockkondensatoren für den 74hc595 und den ATmega?

von Roland E. (roland0815)


Lesenswert?

Zeiten zwischen Daten anlegen und takten nicht eingehalten?

von Sebastian S. (sesc360)


Lesenswert?

Die habe ich nicht. Welche Wirkung haben die?

von Teo D. (teoderix)


Lesenswert?


: Bearbeitet durch User
von Sebastian S. (sesc360)


Lesenswert?

Ich hab den Cap mit eingefügt, hat aber leider auch nichts verändert.

von Wolfgang (Gast)


Lesenswert?

Sebastian S. schrieb:
> Ich hab den Cap mit eingefügt, hat aber leider auch nichts verändert.

Und hast du dem Schieberegister versuchsweise mal etwas mehr Zeit 
zwischen dem Anlegen der Daten und der aktiver Clock-Flanke gegeben?

von Sebastian S. (sesc360)


Lesenswert?

ich bin jetzt einen Schritt näher gekommen. Tatsächlich habe ich den Cap 
zunächst zuweit weg platziert. Jetzt direkt an das Schieberegister ran 
und es leuchtet schon mal nicht mehr zufällig. Jedoch ist alles um ein 
bit verschoben. Wenn ich 11110000 eingebe, dann kommt 11100000 raus.

Es scheint also halb zu funktionieren. Wenn ich 01111000 nehme dann sehe 
ich 11110000. Woran kann das denn noch liegen? das muss doch jetzt meine 
Write Funktion sein, oder?

: Bearbeitet durch User
von Sebastian S. (sesc360)


Lesenswert?

Teo D. schrieb:
> https://www.mikrocontroller.net/articles/Kondensator#Entkoppelkondensator
> (Abblock == Entkoppel)

Danke. Soweit hat das tatsächlich geklappt (ich bin neu mit der ganzen 
Thematik und lerne noch). Jedoch wie unten beschrieben, scheint alles 
ein Bit versetzt zu sein, da ich mit dem Input 01111000 auf den LEDs 
sehe: 11110000; Aber das Random Leuchten ist weg. Ist meine Write 
Funktion falsch?

von Wolfgang (Gast)


Lesenswert?

Sebastian S. schrieb:
> Ist meine Write Funktion falsch?

Guck dir mit dem LA einfach mal deine Signale an und vergleiche sie mit 
dem im Datenblatt vom 74HC595 gezeigten Ablauf, insbesondere unter 
Berücksichtigung der Zeiten im Abschnitt 11. Dynamic characteristics

von Sebastian S. (sesc360)


Lesenswert?

Es ist sogar noch etwas seltsamer. Wenn ich 00001111 reingebe, kommt 
00011111 raus. Also irgendwie wird ein Bit mehr geschalten. Ein anderes 
komisches Phänomen habe ich, wenn ich den Code auf meinen AVR lade. Die 
Anzeige ist nur bei jedem 2. Mal richtig.

Aus 11111111 wird einmal 11111111 und einmal 11111110, um dann wieder 
11111111 auf den LEDs auszugeben.

von Sebastian S. (sesc360)


Lesenswert?

Wolfgang schrieb:
> Sebastian S. schrieb:
>> Ist meine Write Funktion falsch?
>
> Guck dir mit dem LA einfach mal deine Signale an und vergleiche sie mit
> dem im Datenblatt vom 74HC595 gezeigten Ablauf, insbesondere unter
> Berücksichtigung der Zeiten im Abschnitt 11. Dynamic characteristics

Jetzt hab ich natürlich das Problem als Anfänger, das ich keine LA 
habe... ich habe mir bisher armselig mit einer Serial Port Ausgabe 
beholfen :)

von Wolfgang (Gast)


Lesenswert?

Sebastian S. schrieb:
> Jetzt hab ich natürlich das Problem als Anfänger, das ich keine LA
> habe... ich habe mir bisher armselig mit einer Serial Port Ausgabe
> beholfen :)

Dann investiere mal gut 6€ in dein Hobby. Die sind gut angelegt.
z.B. ebay 201541710029

von Sebastian S. (sesc360)


Lesenswert?

Wolfgang schrieb:
> Sebastian S. schrieb:
>> Jetzt hab ich natürlich das Problem als Anfänger, das ich keine LA
>> habe... ich habe mir bisher armselig mit einer Serial Port Ausgabe
>> beholfen :)
>
> Dann investiere mal gut 6€ in dein Hobby. Die sind gut angelegt.
> z.B. ebay 201541710029

Oh.... das wusste ich nicht. Den letzten den ich gesehen habe, hat 
gleich 180 EUR gekostet, was ich mir aber auch schon überlegt habe.

Hast Du eine Idee warum ich das byte so versetzt sehe? Tatsächlich waren 
die Caps der Grund für das Zufällige aufleuchten. Aber ich verstehe 
nicht, warum sich meine Eingabe um eins verschiebt.

von Arduinoquäler (Gast)


Lesenswert?

Ich hab dir ein einfaches Rätsel gepostet:
1
    // Sends a clock pulse on STCP Line
2
    void HC595Latch()
3
    {
4
      // Pulse the store clock
5
      HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
6
//------------------------^^^^^^^^^^^^^^^^------------
7
      _delay_loop_1(1);
8
9
      HC595_PORT &= (~(1<<HC595_ST_CP_POS)); // LOW
10
      _delay_loop_1(1);
11
    }

Den Rest musst du selbst leisten ;-)

von Arduinoquäler (Gast)


Lesenswert?

Noch ein einfaches Rätsel:

Warum kann meinen C-Code besser lesen als deinen?

Des Rätsels Lösung: Ich habe die

"Wichtige Regeln - erst lesen, dann posten!"

beachtet.

von Peter D. (peda)


Lesenswert?

Namen sollten sich in wenigstens 2 Buchstaben unterscheiden.
Delay braucht man nicht, der HC595 schafft 25MHz. Mit den Bitbefehlen 
kommt der Mega328 @20MHz nur auf max 5MHz.
In *.h Files gehört kein Code.
Man kann die Lesbarkeit mit Bitmacros nochmal deutlich erhöhen:
1
#include <avr/io.h>
2
3
struct bits {
4
  uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
5
} __attribute__((__packed__));
6
7
#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
8
9
#define SPI_CLK         SBIT( PORTB, 0 )        // clock
10
#define SPI_CLK_oe      SBIT( DDRB,  0 )
11
#define SPI_MOSI        SBIT( PORTB, 1 )        // data out
12
#define SPI_MOSI_oe     SBIT( DDRB,  1 )
13
#define SPI_MISO_in     SBIT( PINB,  2 )        // data in
14
15
uint8_t shift_io( uint8_t b )           // send / receive byte
16
{
17
  SPI_CLK_oe = 1;                       // set as output
18
  SPI_MOSI_oe = 1;
19
20
  for( uint8_t i = 8; i; i-- ){         // 8 bits
21
    SPI_MOSI = 0;
22
    if( b & 0x80 )                      // high bit first
23
      SPI_MOSI = 1;
24
    b <<= 1;
25
    SPI_CLK = 1;
26
    if( SPI_MISO_in )
27
      b++;
28
    SPI_CLK = 0;
29
  }
30
  return b;
31
}

von Sebastian S. (sesc360)


Lesenswert?

Arduinoquäler schrieb:
> Ich hab dir ein einfaches Rätsel gepostet:
>
>
1
> 
2
>     // Sends a clock pulse on STCP Line
3
>     void HC595Latch()
4
>     {
5
>       // Pulse the store clock
6
>       HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
7
> //------------------------^^^^^^^^^^^^^^^^------------
8
>       _delay_loop_1(1);
9
> 
10
>       HC595_PORT &= (~(1<<HC595_ST_CP_POS)); // LOW
11
>       _delay_loop_1(1);
12
>     }
13
> 
14
>
>
> Den Rest musst du selbst leisten ;-)

Hmmm... guter Punkt! :)
In Vorfreude hab ich das gleich mal ausprobiert, aber interessanterweise 
keinerlei Auswirkung auf das Ergebnis?!

der Output den ich auf den LEDs sehe, ist immer noch um 1 verschoben, 
nur das ich mir das nicht erklären kann, da der Code dies nicht 
wiederspiegelt meiner Meinung nach.

von Sebastian S. (sesc360)


Lesenswert?

Ah... wieder einen Schritt weiter. ich habe die Funktion nochmals 
angepasst:

void HC595Pulse() {

  // Pulse shift clock
  HC595_PORT |= (1 << HC595_SH_CP_POS); // HIGH
  HC595_PORT &= (~(1<<HC595_SH_CP_POS)); // LOW

}

PS: Ich werde die Variablen noch umbenennen.

Jetzt funktionert alles wie geplant, bis auf ein Detail: Wenn ich den 
Code auf den ATMEGA lade, blitzen die ersten 3 LEDS (11100000) kurz auf 
bevor das richtige Ergebnis erscheint. Dabei habe ich hier ja noch keine 
Daten übertragen?

von Georg (Gast)


Lesenswert?

Sebastian S. schrieb:
> Wenn ich den
> Code auf den ATMEGA lade, blitzen die ersten 3 LEDS (11100000) kurz auf
> bevor das richtige Ergebnis erscheint

Nach dem Einschalten sind die Ausgänge des 595 erstmal grundsätzlich 
unbestimmt und zufällig. Dagegen gibt es verschiedene Massnahmen, z.B. 
kannst du mit /OE des 595 die Ausgänge abschalten, bis sie richtig 
programmiert sind. Dazu musst du dafür sorgen, dass /OE nach einem Reset 
nicht Lo ist, z.B. kannst du einen Port des Prozessors nehmen, der nach 
Reset hochohmig ist, und einen Pullup.

Ob sich das lohnt, musst du wissen. Wegen der LEDs nicht unbedingt, aber 
wenn Motore oder Ventile angesteuert werden, muss auch der 
Initialisierungsvorgeng durchdacht werden, sowohl in Hardware als auch 
in Software.

Zu beachten: der 595 hat einen schweren Designfehler: der Reseteingang 
heisst zwar Master Reset, schaltet aber keineswegs die Ausgänge auf 0, 
erst nach einem zusätzlichen Clock-Impuls. Es gibt daher keine sichere 
Methode, den 595 zu resetten, nur wie oben beschrieben die Ausgänge 
abzuschalten und später freizugeben.

Georg

von Sebastian S. (sesc360)


Lesenswert?

Super! Danke Georg! Das hilft mir definitiv schon weiter!

von Teo D. (teoderix)


Lesenswert?

Georg schrieb:
> Master Reset, schaltet aber keineswegs die Ausgänge auf 0,
> erst nach einem zusätzlichen Clock-Impuls.
> ...
> Es gibt daher keine sichere
> Methode

Obiges ist keine sichere Methode?
(hab den Reset nie gebraucht)

von Bernhard S. (b_spitzer)


Lesenswert?

Sebastian S. schrieb:

>     int main(void)
>     {
>       while (1) {
>       }
>
>       return (0);
>     }
Irgendwie vermisse ich hier was... Nur weil unten dran der Quellcode der 
H-Datei steht, ruft sich da noch keine Funktion selbst auf.

Davon abgesehen - kleines Rätsel:
a) wie lange braucht der Controller für while(1)
b) wann wird in dem Programm return(0) ausgeführt
c) wieso int main(), wer holt sich den zurückgegebenen Wert ab (und 
wann)

von Georg (Gast)


Lesenswert?

Teo D. schrieb:
> Obiges ist keine sichere Methode?

Wenn der Clk vom Prozessor kommt (wie üblich), nein - Reset sollte ja 
wirksam sein, bevor der Prozessor anläuft. Also z.B. während die 
Versorgungspannung noch nicht auf Sollwert ist, und auch gerade dann 
wenn der Prozessor garnicht anläuft, um Schäden zu verhindern.

Teo D. schrieb:
> (hab den Reset nie gebraucht)

Mit dem Designfehler ist er auch nicht zu gebrauchen.

Georg

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.