Forum: Mikrocontroller und Digitale Elektronik Schieberegister ohne SPi


von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Hey Leute,

Ich probiere mich heute zum ersten mal an Schieberegistern und wollte 
dieses ohne SPI ansteuern.

Ich habe dazu ein Programm geschrieben, aber es funktioniert nicht.

Kann mit jmd. helfen?
1
#include <avr/io.h>
2
#ifndef F_CPU
3
#define F_CPU 8000000UL
4
#endif
5
#include <util/delay.h>
6
#define LATCHCLOCK 1
7
#define OUTPUTENABLE 2
8
#define SHIFTCLOCK 4
9
#define A 8
10
#define RESET 16
11
#define SQH 64
12
13
void byteOut (uint8_t);              //Funktion zur Ausgabe über das Schieberegister
14
15
int main(void)
16
{
17
  DDRC = 0xFF;                //Port C auf Ausgang
18
  PORTC = (1<<RESET);              //Reset auf High
19
  byteOut(0x0F);                //Funktionsaufruf: Alle Schieberegister Ausgänge auf Low
20
  while(1)                  //Endlosschleife
21
  {
22
  }              
23
}
24
25
26
void byteOut (uint8_t word)
27
{
28
  PORTC &= ~(1<<OUTPUTENABLE);        //Rückstellung des Outputs
29
  for (uint8_t i = 0;i < 8;i++)        
30
  {
31
    if (word & (1<<0))            //Wenn LSB=1 dann:
32
    {
33
      PORTC |= (1<<A);          //stelle High am Datenbus bereit
34
      PORTC |= (1<<SHIFTCLOCK);      //und gib eine Positive Flanke auf Shiftclock (Bit rutscht in den ersten Slot des Schieberegisters)
35
      PORTC &= ~(1<<SHIFTCLOCK);      //Shiftclock auf Ausgangsposition
36
      PORTC &= ~(1<<A);          //A auf Ausgangsposition
37
    }
38
    else
39
    {
40
      PORTC &= ~(1<<A);          //stelle (zur Sicherheit) Datenbus auf Low
41
      PORTC |= (1<<SHIFTCLOCK);      //und gibt eine Positive Flanke auf Shiftclock (Bit rutscht in den ersten Slot des Schieberegisters)
42
      PORTC &= ~(1<<SHIFTCLOCK);      //Shiftclock auf Ausgangsposition
43
    }
44
    word-=word;                //Bitverschiebung nach rechts
45
  }
46
  PORTC |= (1<<LATCHCLOCK);          //Positive Flanke auf Latchclock übergibt die Werte aus dem Schieberegister ins Speicherregister
47
  PORTC &= ~(1<<LATCHCLOCK);          //Latchclock auf Ausgangsposition
48
  PORTC |= (1<<OUTPUTENABLE);          //Output 'öffnen'
49
}

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:

>     word-=word;                //Bitverschiebung nach rechts

ÄH: Nein.
5 - 5 ergibt eine glatte 0 und nicht einen um 1 Bit verschobenen Wert

Zum Code.
Lass den Output Enable in Ruhe. Den stellst du am Anfang auf 'AUsgang 
durchschalten' und dann lässt du ihn dort. Genau das ist ja der Sinn der 
Latch-Bufferung im 595: Du taktest die Daten seriell ins Schieberegister 
und auf einen Puls am 'LATCHCLOCK' Pin hin werden die neuen Daten auf 
die Ausgänge durchgeschaltet.

Vereinfache den Code!
1
void byteOut (uint8_t word)
2
{
3
  for (uint8_t i = 0;i < 8;i++)        
4
  {
5
    if (word & (1<<0))
6
      PORTC |= (1<<A);
7
    else
8
      PORTC &= ~(1<<A);
9
10
    PORTC |= (1<<SHIFTCLOCK);
11
    PORTC &= ~(1<<SHIFTCLOCK);
12
13
    word >>= 1;
14
  }
15
16
  PORTC |= (1<<LATCHCLOCK);
17
  PORTC &= ~(1<<LATCHCLOCK);
18
}

: Bearbeitet durch User
von Felix A. (madifaxle)


Lesenswert?

Port C ist nur 8 Bit breit, du schiebst die 1 (Reset) aber 16 mal. Kommt 
wohl 0 auf Port C an. Du wolltest wohl PORTC = RESET schreiben...

Bei den anderen Signalen derselbe Fehler.

Dann muss es

if (word & (1<<i)) heißen...

Das hier

word-=word;

ergibt 0.

von Karl H. (kbuchegg)


Lesenswert?

> Port C ist nur 8 Bit breit, du schiebst die 1 (Reset) aber 16 mal. Kommt
> wohl 0 auf Port C an. Du wolltest wohl PORTC = RESET schreiben...

Das hier
1
#define A 8

kann auch nicht stimmen:
1
      PORTC |= (1<<A);

wir fangen bei 0 zu zählen an! Ein Port am AVR hat 8 Bit. Die sind daher 
durchnummeriert von 0 bis 7.
0, 1, 2, 3, 4, 5, 6, 7. Zähl nach, sind genau 8 Stück.

> Ich probiere mich heute zum ersten mal an Schieberegistern
Da frage ich mich doch, welche grundlegenden Übungen du übersprungen 
hast. Die Ansteuerung von Pins an einem Port sollte da eigentlich keine 
Schwierigkeiten mehr machen!

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

PS: Die Ansteuerung zb eines 595 hat den grossen Charme, dass man die 
Ansteuerung so langsam machen kann, wie es einem beliebt. Es ist ist 
also überhaupt kein Problem, da einige _delay_ms im Code zu verteilen.
1
void byteOut (uint8_t word)
2
{
3
  for (uint8_t i = 0;i < 8;i++)        
4
  {
5
    if (word & (1<<0))
6
      PORTC |= (1<<A);
7
    else
8
      PORTC &= ~(1<<A);
9
    _delay_ms(1000);
10
11
    PORTC |= (1<<SHIFTCLOCK);
12
    _delay_ms(1000);
13
    PORTC &= ~(1<<SHIFTCLOCK);
14
    _delay_ms(1000);
15
16
    word >>= 1;
17
  }
18
19
  PORTC |= (1<<LATCHCLOCK);
20
    _delay_ms(1000);
21
  PORTC &= ~(1<<LATCHCLOCK);
22
    _delay_ms(1000);
23
}

und zum Beispiel ein paar LED direkt an die Portpins zu klemmen um zu 
beobachten in welcher Reihenfolge welcher Pin auf High bzw. Low geht. 
Und wenn die 1 Sekunde nicht reichen, dann nimm eben 2 Sekunden oder 
noch länger.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Hey Leute,

Danke für die vielen Antworten.

Karl H. schrieb:
> Thomas schrieb:
>
>>     word-=word;                //Bitverschiebung nach rechts
>
> ÄH: Nein.
> 5 - 5 ergibt eine glatte 0 und nicht einen um 1 Bit verschobenen Wert

ich glaube ich wollte word halbieren.

Karl H. schrieb:
> Das hier#define A 8
>
> kann auch nicht stimmen:      PORTC |= (1<<A);

Ich hatte den Code umgeschrieben von PORTC |= 0x08; zu PORTC |= (1<<A) 
und hab dabei die PIN des Schieberegisters vordefiniert.

Thomas schrieb:
> #define LATCHCLOCK 1
> #define OUTPUTENABLE 2
> #define SHIFTCLOCK 4
> #define A 8
> #define RESET 16
> #define SQH 64

...habe ich nun umgeschrieben:

#define LATCHCLOCK 0
#define OUTPUTENABLE 1
#define SHIFTCLOCK 2
#define A 3
#define RESET 4
#define SQH 5

Das Programm sieht nun folgendermaßen aus:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#ifndef F_CPU
4
#define F_CPU 8000000UL
5
#endif
6
#define LATCHCLOCK 0
7
#define OUTPUTENABLE 1
8
#define SHIFTCLOCK 2
9
#define A 3
10
#define RESET 4
11
#define SQH 5
12
13
void byteOut (uint8_t);              //Funktion zur Ausgabe über das Schieberegister
14
15
int main(void)
16
{
17
  DDRC = 0xFF;                //Port C auf Ausgang
18
  PORTC |= (1<<RESET);            //Reset auf High
19
  PORTC |= (1<<OUTPUTENABLE);          //Output 'öffnen'
20
  byteOut(0x0F);                //Funktionsaufruf
21
  while(1)                  //Endlosschleife
22
  {
23
  }              
24
}
25
26
27
void byteOut (uint8_t word)
28
{
29
  for (uint8_t i = 0;i < 8;i++)        
30
  {
31
    if (word & (1<<0))            //Wenn LSB=1 dann:
32
    {
33
      PORTC |= (1<<A);          //stelle High am Datenbus bereit
34
    }
35
    else
36
    {
37
      PORTC &= ~(1<<A);          //stelle Datenbus auf Low
38
    }
39
    PORTC |= (1<<SHIFTCLOCK);        //und gibt eine Positive Flanke auf Shiftclock (Bit rutscht in den ersten Slot des Schieberegisters)
40
    PORTC &= ~(1<<SHIFTCLOCK);        //Shiftclock auf Ausgangsposition
41
    word>>=1;                //Bitverschiebung nach rechts
42
  }
43
  PORTC |= (1<<LATCHCLOCK);          //Positive Flanke auf Latchclock übergibt die Werte aus dem Schieberegister ins Speicherregister
44
  PORTC &= ~(1<<LATCHCLOCK);          //Latchclock auf Ausgangsposition
45
}

Die 8 LEDs bleiben aber aus. (Auch mit den eingebauten Wartezeiten)

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:


> Die 8 LEDs bleiben aber aus. (Auch mit den eingebauten Wartezeiten)

Die Wartezeiten sollen auch dafür dienen, dass du dir an den bewussten 
Portpins ansehen kannst, ob sich dort überhaupt etwas tut. Ich meine 
damit nicht die Ausgänge vom 595 sondern den PORTC vom Prozessor. 
Welcher ist das überhaupt? Manche AVR haben am PORTC den JTAG Anschluss, 
der per Default aktiviert ist.

Wenn du Programme entwickeln willst, wirst du nicht umhin kommen, dir 
auch Fehlersuchstrategien anzueignen. Eine der einfachsten und 
grundlegensten besteht darin, dass wenn man einen Portpin auf 1 
schaltet, man mit einem Multimeter oder mit einer LED am Pin nachmisst, 
ob das auch wirklich passiert!

: Bearbeitet durch User
von Felix A. (madifaxle)


Lesenswert?

Ersetze mal

word>>=1;

durch

word = word >> 1;

Oder streiche diese Zeile und ändere
1
for (uint8_t i = 8;i > 0;i--)        
2
  {
3
    if (word & (1<<i)) {

von Felix P. (fixxl)


Lesenswert?

Thomas schrieb:
> PORTC |= (1<<OUTPUTENABLE);          //Output 'öffnen'

Damit deaktivierst du die Ausgänge des Schieberegisters. Der Pin heißt 
/OE, also bedeutet 0, dass die Ausgänge aktiv treiben, und 1, dass sie 
tristate sind.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen ist der Output Enable von einem 74595 ein 'active low' 
Eingang. Erkennbar im Datenblatt des IC an einem Querstrich entweder 
über der Pinbezeichnung oder einem Schrägstrich vor der Pinbezeichnung. 
Um den also zu aktivieren, wird er auf 0 geschaltet.
1
  PORTC &= ~(1<<OUTPUTENABLE);          //Output 'öffnen'

Wenn du natürlich keinen 74595 da drann hängen hast (war ja bisher nur 
eine Vermutung von mir), dann musst du im Datenblatt des von dir 
verwendeten IC nachsehen, ob der Eingang auf 0 oder auf 1 sein muss, um 
die entsprechende Funktion zu erhalten.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Felix A. schrieb:
> Ersetze mal
>
> word>>=1;
>
> durch
>
> word = word >> 1;
>
> Oder streiche diese Zeile und ändere
>
>
1
> for (uint8_t i = 8;i > 0;i--)
2
>   {
3
>     if (word & (1<<i)) {
4
>

Das ist alles in Ordnung, so wie er es jetzt hat.
Nichts für ungut Felix, aber das ist mir schon weiter oben aufgefallen, 
dass du ihm Dinge einreden willst, die einfach nur Unsinn sind. So wie 
er das vor hat, ist das alles in Ordnung und ganz im Gegenteil ist seine 
Lösung wesentlich besser als deine (letzte).

: Bearbeitet durch User
von Felix A. (madifaxle)


Lesenswert?

Unsinn denke ich nicht, nur eine weitere Option. Ich fände es so halt 
übersichtlicher.

Doch Unsinn. Der Index würde um 1 versetzt laufen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Felix A. schrieb:
> Unsinn denke ich nicht, nur eine weitere Option. Ich fände es so halt
> übersichtlicher.
>
> Doch Unsinn. Der Index würde um 1 versetzt laufen.

Abgesehen davon.
Du magst das übersichtlicher finden. Dein AVR wird sich allerdings nicht 
gerade über den unnötigen Mehraufwand freuen. Er hat keinen 
Barrelshifter. Eine Operation wie ( 1 << i ) muss er mit einer eigenen 
Schleife abarbeiten. Dein
1
     if (word & (1<<i)) {
wird vom Compiler mangels Alternativen so umgesetzt
1
    {
2
      uint8_t tmp = 0x01;
3
      for( uint8_t tmp_loop = 0; tmp_loop < i; tmp_loop++ )
4
        tmp <<= 1;
5
6
      if( word & tmp ) {
7
...

Immer noch überzeugt, dass das eine gute Lösung im Sinne der Laufzeit 
ist?
Gewöhn dich daran. Die Technik "wir generieren nicht eine variable 
Maske, sondern schieben das Byte unter einer fixen Maske durch" ist eine 
allgegenwärtige Methode. Da ist nichts anstössiges daran.

: Bearbeitet durch User
von Felix A. (madifaxle)


Lesenswert?

Als optimale Lösung nicht gedacht. Nur als einfacher zu lesende.

Da der TE schließlich erst mit dem Thema mehr oder weniger beginnt, sah 
ich Leserlichkeit als vorteilhafter im Vergleich zu optimiert an.

: Bearbeitet durch User
von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Hey Leute,

es hat nun endlich geklappt. Danke dafür.

Der OutputEnable Eingang war tatsächlich active-low.

Hab auch nun mit den Warteschleifen etwas rumprobiert.

Falls es jemanden Interessiert, hab ich mal ein Bild vom Aufbau des 
Schieberegisters gemacht.

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:
> Hey Leute,
>
> es hat nun endlich geklappt. Danke dafür.
>
> Der OutputEnable Eingang war tatsächlich active-low.

2 alte Programmiererweisheiten:

"Kaum macht man es richtig, funktioniert es auch"
und
"Wenn etwas auf Anhieb funktioniert, stimmt etwas nicht."

von Dietrich L. (dietrichl)


Lesenswert?

Thomas schrieb:
> Falls es jemanden Interessiert, hab ich mal ein Bild vom Aufbau des
> Schieberegisters gemacht.

Da fehlt noch ein Stützkondensator am Schieberegister; aber vielleicht 
ist er ja auf der Rückseite.
Auch falls es jetzt ohne geht: stabiler läuft es mit z.B. 100nF.

Gruß Dietrich

von Thomas (Gast)


Lesenswert?

Ne Kondensator is keiner dran.

Stützkondensator sagt mir i.M. nix, aber ich denke mal zur 
Spannungsglättung?
also einfach 100nF zwischen VCC und GND Löten?

von Felix A. (madifaxle)


Lesenswert?

Genau das. Sollte man immer machen an Halbleitern... es sei denn, das 
Datenblatt schreibt was anderes vor.

von Karl H. (kbuchegg)


Lesenswert?

Thomas schrieb:
> Ne Kondensator is keiner dran.

Sollte aber, wenn dir ein stabiler Betrieb wichtig ist.

> Stützkondensator sagt mir i.M. nix, aber ich denke mal zur
> Spannungsglättung?

Im Prinzip ja. Wobei es hier weniger um Glättung geht so wie das in 
einem Netzteil gemacht wird um aus einer pulsierenden Gleichspannung 
eine echte Gleichspannung zu machen, sondern mehr darum, dass digitale 
IC schon mal kurzfristig (im Nanosekundenbereich) sehr viel Strom 
benötigen. Strom, der irgendwo her kommen muss. Kann dieser erhöhte 
Stromverbrauch nicht über die Versorungsleitung gedeckt werden, dann 
bricht die Spannung kurzfristig ein und die IC spielen verrückt. Ein 
BLockkondensator vermeidet das, indem er wie ein kleiner Akku fungiert, 
aus dem sich das IC direkt an seinen Anschlusspins den erhöhten Strom 
holen kann.

> also einfach 100nF zwischen VCC und GND Löten?

Genau. Aus dem genannten Grund folgt: Jedem digital IC verpasst man so 
einen Kondensator, wenn einem zuverlässiger Betrieb wichtig ist. Auch 
wenn es im Testaufbau auch so geht.

von Fuzzy (Gast)


Lesenswert?

Felix A. schrieb:
> Genau das. Sollte man immer machen an Halbleitern


DAS mit SICHERHEIT nicht !!!!

von Felix A. (madifaxle)


Lesenswert?

Hast du auch den teil mit dem Datenblatt gelesen?

von Frickelfritze (Gast)


Lesenswert?

Karl H. schrieb:
> Jedem digital IC verpasst man so einen Kondensator,

Ich möchte diese Aussage "verpäpstlichen":

Jedem Versorgungspin eines digitalen ICc verpasst man einen 
Kondensator.

(siehe Entfernung der mehrfachen Versorgungspins von manchen 28/32-
und 40/44-poligen AVRs.

von Frickelfritze (Gast)


Lesenswert?

Frickelfritze schrieb:
> Jedem Versorgungspin eines digitalen ICc verpasst man einen
> Kondensator.

... und zwar ist das Ziel nicht das IC sondern der jeweilige Pin.

von Thomas (Gast)


Lesenswert?

So Kondensator is dran. Damit kann das Teil nun in Serie gehn :)

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.