Schieberegister ohne SPi

Gast #4382701
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
}
Angehängte Dateien:
#4382708
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
}
#4382716
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!
#4382724
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.
Gast #4383217
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)
#4383236
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!
#4383253
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.
#4383256
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).
#4383279
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.
Gast #4383402
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.
Angehängte Dateien:
#4383454
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren