Forum: Mikrocontroller und Digitale Elektronik Porterweiterung über 74HC595 (Ausgang)


von Le H. (beks)


Angehängte Dateien:

Lesenswert?

Hallo,

ich versuche mich gerade mit meinem ersten Schieberegister.

Habe mir dazu den Artikel im Tutorial durchgelesen (Porterweiterung über 
74HC595 (Ausgang)).

Im Anhang habe ich erstmal meine Anschaltung des Schieberegisters 
skizziert. Habe mich weitestgehend am Datenblatt und Tutorial versucht 
zu orientieren.

Als MC kommt ein ATMEGA8 zum Einsatz. Der Quellcode lässt sich ohne 
Schwierigkeiten kompilieren. Ich habe an jeden Ausgang eine LED 
angeschlossen, damit ich ein gesetztes Bit sehen kann.

Da ich erstmal nur Bit 1 setzen möchte habe ich den Code des Main-file 
etwas abgeändert.
1
  com74hc595_init();
2
3
      while(1)
4
      {
5
        com74hc595_setBit(1);
6
7
      }

Leider tut sich bislang überhaupt nichts. Habe ich eventuell schon einen 
Schaltungsfehler in der Ansteuerung des 74HC595?

von Thomas E. (thomase)


Lesenswert?

Le Ha schrieb:
> com74hc595_init();
> com74hc595_setBit(1);

Und was macht das?

Le Ha schrieb:
> Leider tut sich bislang überhaupt nichts. Habe ich eventuell schon einen
> Schaltungsfehler in der Ansteuerung des 74HC595?

Offensichtlich nicht.

Ganz nebenbei: Bei Leitungskreuzungen zeichnet man weder Tunnel noch 
Brücken, sondern kreuzt einfach die Leitungen. Dort, wo Verbindungen 
bestehen sollen, setzt man dicke Punkte.


mfg.

: Bearbeitet durch User
von Le H. (beks)


Lesenswert?

Hallo Thomas,

ich habe den Quellcode aus dem Tutorial entnommen.

https://www.mikrocontroller.net/articles/Porterweiterungen#Porterweiterung_.C3.BCber_74HC595_.28Ausgang.29_2

com74hc595.h
1
 
2
* com74hc595.h
3
 *
4
 *  Created on: 18.05.2015
5
 *      Author: ericjahn
6
 */
7
#ifndef _COM74HC595_H_
8
#define _COM74HC595_H_
9
10
// Anzahl der über Software-SPI anzusteuernde 74HC595 Bausteine
11
#define COM74HC595_SIZE 1
12
13
// Anzahl der Bits über alle Bausteine
14
#define COM74HC595_BYTES COM74HC595_SIZE * 8
15
16
// PORT am Mikrokontroller über den der Software SPI erfolgen soll
17
#define PORT_com74hc595         PORTB
18
#define PORT_SER     PB0    // 74HC595 PIN 14
19
#define PORT_SCK     PB1    // 74HC595 PIN 11
20
#define PORT_RCK     PB2    // 74HC595 PIN 12
21
#define PORT_OE     PB3    // 74HC595 PIN 13
22
23
#define DDR_com74hc595          DDRB
24
#define DDR_SER     DDB0          // 74HC595 PIN 14
25
#define DDR_SCK     DDB1          // 74HC595 PIN 11
26
#define DDR_RCK     DDB2          // 74HC595 PIN 12
27
#define DDR_OE       DDB3          // 74HC595 PIN 13
28
29
/*
30
Falls eine Steuerung über /OE PIN 13 des 74HC595 gewünscht ist,
31
muss PIN 13 per Pullup Widerstand gesetzt werden, dass sorgt
32
dafür, dass der 74HC595 nach einem Reset in einem definierten
33
Zustand gesetzt werden kann.
34
Der 74HC595 wird in der Initialisierungsroutine mit 0x00 initialisiert.
35
Dadurch, dass /OE über den Pullup-Widerstand auf HIGH gezogen wurde,
36
befindet sich der 74HC595 im Tristate-Modus. Wird jetzt /OE auf LOW
37
gezogen, steht der gesetzte Zustand an den Ports an.
38
*/
39
#define WITH_OE
40
41
// globales Array zur Speicherung der Bitmuster
42
extern unsigned char com74hc595[];
43
44
// Initialisierung der Ports im Mikrocontroller
45
extern void com74hc595_init(void);
46
47
// Ausgabe der Bitmuster an die 74HC595
48
extern void com74hc595_out(void);
49
50
// setzen eines einzelnen Bits innerhalb des Arrays
51
extern void com74hc595_setBit( unsigned char BitNumber );
52
53
// zurücksetzen eines einzelnen Bits innerhalb des Arrays
54
extern void com74hc595_unsetBit( unsigned char BitNumber );
55
56
// setzen eines ganzen Ports
57
extern void com74hc595_setPort( unsigned char Port, unsigned char Bits );
58
59
// zurücksetzen eines ganzen Ports
60
extern void com74hc595_unsetPort( unsigned char Port );
61
62
// setzt alle Bits auf logisch 1
63
extern void com74hc595_setall(void);
64
65
// setzt alle Bits auf logisch 0
66
extern void com74hc595_unsetall(void);
67
68
#endif /* _COM74HC595_H_ */

com74hc595.c
1
 
2
#include <avr/io.h>
3
#include <stdlib.h>
4
#include "com74hc595.h"
5
6
// Array fuer die Daten
7
unsigned char com74hc595[ COM74HC595_SIZE ];
8
9
void com74hc595_setBit( unsigned char BitNumber)
10
{
11
  unsigned char Port = BitNumber >> 3;
12
  unsigned char Bit = BitNumber & 7;
13
  com74hc595[ Port ] |= (1 << ( Bit ) );
14
}
15
16
void com74hc595_unsetBit( unsigned char BitNumber)
17
{
18
  unsigned char Port = BitNumber >> 3;
19
  unsigned char Bit = BitNumber & 7;
20
  com74hc595[ Port ] &= ~(1 << ( Bit ) );
21
}
22
23
void Com74hc595_setall(void)
24
{
25
  for ( int i = 0; i < COM74HC595_BYTES; i++ )
26
  {
27
    com74hc595_setBit( i );
28
  }
29
}
30
31
void com74hc595_unsetall(void)
32
{
33
  for ( int i = 0; i < COM74HC595_BYTES; i++ )
34
  {
35
    com74hc595_unsetBit( i );
36
  }
37
}
38
39
void com74hc595_setPort( unsigned char Port, unsigned char Bits )
40
{
41
  com74hc595[ Port ] = Bits;
42
}
43
44
void com74hc595_unsetPort( unsigned char Port )
45
{
46
  com74hc595[ Port ] = 0x00;
47
}
48
49
50
void com74hc595_init()
51
{
52
        /* Verwendete Ports auf OUT */
53
  DDR_com74hc595 |= ( 1 << DDR_SER);
54
  DDR_com74hc595 |= ( 1 << DDR_SCK);
55
  DDR_com74hc595 |= ( 1 << DDR_RCK);
56
57
#ifdef WITH_OE
58
  DDR_com74hc595 |= ( 1 << DDR_OE);
59
#endif
60
61
        /* SER auf definierten Level LOW */
62
  PORT_com74hc595 &= ~(1<<PORT_SER);
63
64
65
66
        /* SCR und RCK auf definierten Level HIGH */
67
  PORT_com74hc595 |= (1<<PORT_SCK);
68
  PORT_com74hc595 |= (1<<PORT_RCK);
69
70
#ifdef WITH_OE
71
  PORT_com74hc595 |= (1<<PORT_OE);
72
#endif
73
74
  com74hc595_unsetall();
75
  com74hc595_out();
76
77
#ifdef WITH_OE
78
  PORT_com74hc595 &= ~(1<<PORT_OE);
79
#endif
80
}
81
82
void com74hc595_out()
83
{
84
    unsigned char anz = COM74HC595_SIZE;
85
    unsigned char* serp = com74hc595 + COM74HC595_SIZE;
86
87
    do
88
    {
89
        unsigned char bits;
90
        unsigned char data = *--serp;
91
92
        /* 8 Bits pro Byte rausschieben */
93
        for (bits = 8; bits > 0; bits--)
94
        {
95
      PORT_com74hc595 &= ~(1<<PORT_SER);
96
            if (data & 0x80)
97
            {
98
    PORT_com74hc595 |= (1<<PORT_SER);
99
            };
100
101
            data <<= 1;
102
            /* Strobe an SCK schiebt Daten im Gaensemarsch */
103
            /* um 1 Position weiter durch alle Schieberegister */
104
      PORT_com74hc595 &= ~(1<<PORT_SCK);
105
      PORT_com74hc595 |= (1<<PORT_SCK);
106
        }
107
    }
108
    while (--anz > 0);
109
110
    /* Strobe an RCK bringt die Daten von den Schieberegistern in die Latches */
111
    PORT_com74hc595 &= ~(1<<PORT_RCK);
112
    PORT_com74hc595 |= (1<<PORT_RCK);
113
}

test.c
1
#include <util/delay.h>
2
#include <avr/io.h>
3
#include "com74hc595.h"
4
int main (void) {
5
6
  com74hc595_init();
7
      while(1)
8
      {
9
        com74hc595_setBit(1);
10
11
      }
12
 return 0;
13
}

von Peter C. (peter_c49)


Lesenswert?

aufruf von com74hc595_out() fehlt.

von Codedurchwühler (Gast)


Lesenswert?

1
void com74hc595_setBit( unsigned char BitNumber)
2
{
3
  unsigned char Port = BitNumber >> 3;   // <------- !!
4
  unsigned char Bit = BitNumber & 7;
5
  com74hc595[ Port ] |= (1 << ( Bit ) );
6
}

In der markierten Zeile gibt es einen unsinnigen Shift.
Zumindest wenn man eine Bitnummer 0..7 an die Funktion übergibt.

Da muss man wohl nochmal drüber nachdenken.

von Le H. (beks)


Lesenswert?

Danke Peter! Das war es und weiterhin hat sich genau bei Bit 1 leider 
eine defekte LED eingeschlichen. Alle anderen LED´s haben sich nämlich 
ansteuern lassen.

Lässt sich das Schieberegister eigentlich auch über SPI des ATMEGA 
ansteuern? Wäre der Quellcode dann einfacher bzw. weniger in Summe?

von Thomas E. (thomase)


Lesenswert?

Le Ha schrieb:
> ich habe den Quellcode aus dem Tutorial entnommen.

Na gut. Aber das Werk tu ich mir jetzt nicht an.
1
#define PORT595 PORTB
2
#define DATA 0
3
#define SHIFT 1
4
#define LATCH 2
5
6
void Set595(unsigned char BitMuster)
7
{
8
   unsigned char mask = 0x80;
9
10
   while(mask)
11
   {
12
      if(BitMuster & mask) PORT595 |= (1 << DATA); else PORT595 &= ~(1 << DATA);
13
      PORT595 ^= (1 << SHIFT);
14
      PORT595 ^= (1 << SHIFT);
15
      mask >>= 1;
16
   }
17
18
   PORT595 ^= (1 << LATCH);
19
   PORT595 ^= (1 << LATCH);
20
}

mfg.

von Le H. (beks)


Lesenswert?

Hallo Thomas,

der Code sieht auf jeden Fall erstmal übersichtlicher aus. Habe gerade 
versucht ihn zu verstehen. So ganz klappt das noch nicht und leider 
läuft es bei mir auch nicht. Kannst du mit kurzen Kommentaren oder einer 
kleinen Erklärung mir etwas helfen.

Warum hast du die mask auf 0x80 gesetzt?

In mask werden solange die bits rechts verschoben bis die if Bedingungen 
erfüllt sind. Dann lege ich an den Dateneingang das High Signal an, 
welches dann in das Schieberegister übernommen werden soll. Dazu muss 
meines Wissen dann aber an SHIFT auch ein High Pegel anliegen.
1
#include <util/delay.h>
2
#include <avr/io.h>
3
4
#define PORT595 PORTB
5
#define DATA 0
6
#define SHIFT 1
7
#define LATCH 2
8
9
void Set595(unsigned char BitMuster)
10
{
11
   unsigned char mask = 0x80;  //0b1000 0000
12
13
   while(mask)
14
   {                      //bitMuster = 0b0000 0001
15
      if(BitMuster & mask)  //0b0000 0000 -> false
16
        PORT595 |= (1 << DATA); 
17
      else 
18
        PORT595 &= ~(1 << DATA); // B0 = Low
19
          PORT595 ^= (1 << SHIFT); 
20
          PORT595 ^= (1 << SHIFT);
21
      mask >>= 1; //0b0100 0000
22
   }
23
24
   PORT595 ^= (1 << LATCH);
25
   PORT595 ^= (1 << LATCH);
26
}
27
28
int main (void) {
29
      while(1)
30
      {
31
        Set595(0b00000001);
32
      }
33
 return 0;
34
}

von Thomas E. (thomase)


Lesenswert?

Le Ha schrieb:
> Warum hast du die mask auf 0x80 gesetzt?

0x80 = 0b1000 0000

Damit wird Bit7 zuerst rausgeschoben. Nach jedem Schieben wird das 
mask-Bit um 1 nach rechts geschoben und fällt zum Schluss "hinten raus". 
Dann ist die Bedingung 'while(mask)' nicht mehr erfüllt und das 
Geschiebe ist beendet. Somit landet das erste rausgeschobene 
Bit(Bit7)dann, nach 8x schieben, auf D7 am Schieberegister und die 
anderen 7 Bits reihen sich entsprechend ein.

Le Ha schrieb:
> Dazu muss
> meines Wissen dann aber an SHIFT auch ein High Pegel anliegen.

Es muss ein Taktimpuls erzeugt werden. Tick-Tack. 0 - 1 oder 1 - 0. je 
nachdem wie dieser Pegel vorher war.

Dazu wird der Portpin 2x invertiert, auf neudeutsch getoggelt:

> PORT595 ^= (1 << SHIFT);
> PORT595 ^= (1 << SHIFT);

Genau wie am Ende der Latch-Up, der die Bits vom Schieberegister auf den 
Ausgang schaufelt.

Le Ha schrieb:
> So ganz klappt das noch nicht und leider
> läuft es bei mir auch nicht.

Hast du die 3 Portpins auf Ausgang gesetzt?

mfg.

: Bearbeitet durch User
von Le H. (beks)


Angehängte Dateien:

Lesenswert?

Thomas Eckmann schrieb:
> Hast du die 3 Portpins auf Ausgang gesetzt?

Das war es. Hatte ich vorher ausersehen mit auskommentiert.

Danke für die sonst gute Erklärung. Habe jetzt mal ein kleines Lauflicht 
damit gebastelt.


1
/*
2
 * test.c
3
 *
4
 *  Created on: 14.05.2015
5
 *      Author: ericjahn
6
 */
7
#include <util/delay.h>
8
#include <avr/io.h>
9
#include <stdint.h>
10
11
#define PORT595 PORTB
12
#define DATA 0
13
#define SHIFT 1
14
#define LATCH 2
15
16
void Set595(unsigned char BitMuster)
17
{
18
   unsigned char mask = 0x80;  //0b1000 0000
19
20
   while(mask)
21
   {  //bitMuster = 0b0000 0001
22
      if(BitMuster & mask)  //Ist in BitMuster das Bit mask gesetzt (mask)?
23
        PORT595 |= (1 << DATA); // das mask Bit in Bitmuster ist gesetzt, dann DATA = true
24
      else
25
        PORT595 &= ~(1 << DATA);//das mask Bit in Bitmuster ist nicht gesetzt, dann DATA = false
26
27
          PORT595 ^= (1 << SHIFT); //SHIFT invertieren; Flanke erzeugen um DATA Bit ins Schieberegister zu schieben
28
          PORT595 ^= (1 << SHIFT); //SHIFT invertieren
29
      mask >>= 1; //mask Bit rechtsschieben
30
   }
31
32
   PORT595 ^= (1 << LATCH); //LATCH invertieren; Flanke erzeugen um Schieberegister an Ausgänge zu geben
33
   PORT595 ^= (1 << LATCH); //LATCH invertieren
34
}
35
36
int main (void) {
37
  unsigned char Bitmask = 0b00000000;
38
39
  DDRB  = 0xff;
40
  while(1)
41
  {
42
    Bitmask = 0b10000000;
43
      for (int i=0; i<14; i++)
44
      {
45
      Set595(Bitmask);
46
      _delay_ms(100);
47
      if (i<7)
48
        Bitmask>>=1;
49
      if (i>=7)
50
        Bitmask<<=1;
51
      }
52
  }
53
 return 0;
54
}

von neeDanke (Gast)


Lesenswert?

Le Ha schrieb:
> Danke für die sonst gute Erklärung. Habe jetzt mal ein kleines Lauflicht
> damit gebastelt.

Ich zB will mir das gar nicht näher ansehen. Ich weiß nicht, wie das 
verschaltet ist, weiß nicht wie das Licht läuft. Der Inhalt und Format 
vom Dateianhang ist auch unbekannt.

Das müßte ich mir alles mühsam aus dem Code zusammereimen. Aus 
Kommentaren geht es nicht hervor. Nein danke. Hättest dir den Post 
sparen können bis auf die Bekanntgabe des entdeckten Fehlers :(

von Le H. (beks)


Lesenswert?

neeDanke schrieb:
> Das müßte ich mir alles mühsam aus dem Code zusammereimen. Aus
> Kommentaren geht es nicht hervor. Nein danke. Hättest dir den Post
> sparen können bis auf die Bekanntgabe des entdeckten Fehlers :(

Danke für deine Bemühungen.
Wie kommst du darauf das ich einen Fehler bekanntgebe in meinem Post?

Es läuft doch bereits schon :)

Die Datei Lauflicht ist eine JPG. Das Dateiende wird nur nicht mit 
angezeigt. Die Kommentare sind nur für mich zum Verständnis. Damit habe 
ich die sehr gute Erklärung von Thomas für seinen Code verständlich 
gemacht für mich.

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.