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
voidbyteOut(uint8_t);//Funktion zur Ausgabe über das Schieberegister
14
15
intmain(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
voidbyteOut(uint8_tword)
27
{
28
PORTC&=~(1<<OUTPUTENABLE);//Rückstellung des Outputs
29
for(uint8_ti=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
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!
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.
> 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!
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
voidbyteOut(uint8_tword)
2
{
3
for(uint8_ti=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.
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
voidbyteOut(uint8_t);//Funktion zur Ausgabe über das Schieberegister
14
15
intmain(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
voidbyteOut(uint8_tword)
28
{
29
for(uint8_ti=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)
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!
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.
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.
Felix A. schrieb:> Ersetze mal>> word>>=1;>> durch>> word = word >> 1;>> Oder streiche diese Zeile und ändere>>
1
>for(uint8_ti=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).
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_ttmp=0x01;
3
for(uint8_ttmp_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.
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.
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.
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."
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
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?
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.
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.
Frickelfritze schrieb:> Jedem Versorgungspin eines digitalen ICc verpasst man einen> Kondensator.
... und zwar ist das Ziel nicht das IC sondern der jeweilige Pin.