Wunderschönen Guten Abend!
...bzw. wohl besser Nacht, denn ich zerbreche mir hier schon seit
Stunden den Kopf und kann den Fehler nicht finden.
Zu meinem Problem: Ich habe hier eine 8x8 Tastaturmatrix angeschlossen
(Reihen) an das zweite von vier 74HC595. Der Status (Spalten) wird über
ein 74HC165 ausgelesen.
Für einen Test habe ich folgende Zeilen Code geschrieben, die alle
Zeilen durchgeht und mir die aktiven Spalten an ein 7-Segment-Display
ausgibt:
Die aktiven Spalten werden mir auch korrekt ausgegeben, nur leider immer
für zwei übereinanderliegende Reihen. Er springt also immer zwischen
korrekter und +1 hin&her. Hardwareseitig hab ich die Sache schon
überprüft und konnte keinen Fehler entdecken.
Der Code für die Abfrage lautet:
1
charfield_read_row(charrow){
2
3
charbyte_tmp=0x01;
4
5
switch(row)
6
{
7
case8:byte_tmp<<=0;break;// 00000001 0x01
8
case7:byte_tmp<<=1;break;// 00000010 0x02
9
case6:byte_tmp<<=2;break;// 00000100 0x04
10
case5:byte_tmp<<=3;break;// 00001000 0x08
11
case4:byte_tmp<<=4;break;// 00010000 0x10
12
case3:byte_tmp<<=5;break;// 00100000 0x20
13
case2:byte_tmp<<=6;break;// 01000000 0x40
14
case1:byte_tmp<<=7;break;// 10000000 0x80
15
}
16
17
byte_reed=byte_tmp;
18
19
// RCK -> low
20
PORTB&=~(PORT_SS);
21
// PL -> low
22
PORTB&=~(PORT_PL);
23
// send bytewise in corresponding order of shift registers
24
// µC -> RED -> REED -> GREEN -> GND
25
SPI_MasterTransmit(byte_gnd);
26
SPI_MasterTransmit(byte_grn);
27
SPI_MasterTransmit(byte_reed);
28
SPI_MasterTransmit(byte_red);
29
// RCK -> high will set shift register > storage register
30
PORTB|=(PORT_SS);
31
// set clock pulse high and... (CLK & PL are internally wired together)
32
//PORTB |= (PORT_SCK); // due to CPOL=1, SCK is high when idle
33
// PL -> high will load pin state to shift register
34
PORTB|=(PORT_PL);
35
// activate Clock Enable input
36
PORTB&=~(PORT_CE);
37
// receive data
38
byte_tmp=SPI_MasterReceive();
39
// deactivate Clock Enable input
40
PORTB|=(PORT_CE);
41
42
// byte_tmp = COL HGFEDCBA
43
// bit 7......0
44
return(byte_tmp);
45
}
Die SPI Sende-/Empfangsroutinen:
1
voidSPI_MasterTransmit(charcData){
2
3
// SPI Config: CPOL=1, CPHA=1 (we use SPI Mode 3 on HC595)
4
SPCR|=(1<<CPOL)|(1<<CPHA);
5
// Start transmission
6
SPDR=cData;
7
// Wait for transmission complete
8
while(!(SPSR&(1<<SPIF)));
9
}
10
11
charSPI_MasterReceive(void){
12
13
//SPI Config: CPOL=1, CPHA=0 (we use SPI Mode 2 on HC165)
14
SPCR&=~(1<<CPHA);
15
// set dummy byte to SPDR for receiving data
16
SPDR=0x00;
17
// Wait for reception complete
18
while(!(SPSR&(1<<SPIF)));
19
// Return Data Register
20
returnSPDR;
21
}
Ich habe bereits versucht die Taktrate zu senken, verschiedene
Warteschleifen eingebaut und auch die Simulation hat mir leider nicht
weitergeholfen.
Besten Dank für Eure Hilfe!
Gruss,
Andi
Es passt aber...da 'i' ja nie grösser als 9 wird, sollte in 'itoa'
eigentlich nicht mehr passieren als: c_tmp[0] = '0' + i % 10; ,oder ?
Ich hab's aber jetzt trotzdem durch ('0' + i) ersetzt, spart ein wenig
Platz...
Das Problem bleibt leider immer noch dasselbe. Ist Spalte A in Zeile 1
aktiv, dann pendelt die Anzeige zwischen '1A' und '2A'.
Ich frage nie diesselbe Zeile zweimal ab und die Übertragung zum Display
ist auch korrekt. Aber irgendwo muss noch ein '+1' versteckt sein...
Andi B. schrieb:
> Es passt aber...da 'i' ja nie grösser als 9 wird, sollte in 'itoa'> eigentlich nicht mehr passieren als: c_tmp[0] = '0' + i % 10; ,oder ?
Doch, c_tmp[1] wird mit '\0' beschrieben -- und den Platz dafür
hast du ihm nicht eingeräumt.
Ich würde vorschlagen, das Programm noch mal abzuspecken. Vergiss den
genzen Schnickschnack mit den Buchstaben. Gib den Wert den du von der
read Routine erhältst direkt aus, ohne Umwege.
Ein char Array gross genug dimensionieren, den Wert mittels itoa wandeln
lassen, davor und dahinter noch ein Leerzeichen und raus damit aufs LCD.
(Solche Buffer, die für itoa oder sprintf benutzt werden, dimensioniert
man NIE auf Knirsch.)
Andi B. schrieb:
> Ich frage nie diesselbe Zeile zweimal ab und die Übertragung zum Display> ist auch korrekt. Aber irgendwo muss noch ein '+1' versteckt sein...
+1 eher nicht.
Da du alles in 2-er Potenzen kodiert hast, taucht da irgendwo ein
Bit-Versatz um 1 Bit auf. Sicher dass die SPI_MasterReceive so korrekt
ist? Hast du die einzeln getestet?
Ja ich hatte auch schon überlegt, ob ich mir in SPDR alte Werte
einfange. Aber zwischen den einzelnen Ausleseroutinen liegen 4
Senderoutinen (0x00, 0x00, 0x01[<- Reihen], 0x00) und das Dummybyte beim
Auslesen sollte keine Probleme machen.
Displayroutine ist auch in Ordnung und kodiert die anzuzeigenden Zeichen
pro Aufruf jeweils neu.
Bleibt nur, dass bei zwei aufeinanderfolgenden Abfragen der gleiche Wert
von SPDR zurückgegeben wird.
Edit: Kann es Probleme mit dem Umschalten des SPI Modes während der
Laufzeit geben ?
Andi B. schrieb:
> Edit: Kann es Probleme mit dem Umschalten des SPI Modes während der> Laufzeit geben ?
Daran hab ich auch schon gedacht.
Daher die Frage, ob du die Receive einzeln getestet hast?
Matrix vom 595 abhängen. Nur einlesen und anzeigen. Und dann händisch
eine Zeile mit einem Stück Draht aktivieren.
Ok...war ein wenig umständlicher als gedacht, da Matrixein/-ausgang auf
demselben Pfostenstecker enden. Wie dem auch sei, ich habe jetzt die
vorherigen 595-Senderoutinen entfernt, eine einzelne Zeile an Vcc
gehängt und lasse mir den ausgelesenen Wert anzeigen.
1
while(1){
2
3
charbyte_tmp=0x00;
4
inti;
5
6
for(i=1;i<9;i++){
7
8
waitms(100);
9
10
byte_tmp=field_read_row(i);
11
send_disp(('0'+i),(byte_tmp));
12
}
13
}
Ist keine Spalte aktiv, wirft er mir sporadisch die Werte ' '(0x20) &
'.'(0x2E) aus. Ist eine Spalte dagegen aktiv, kommt gar nix.
Andi B. schrieb:
> Ok...war ein wenig umständlicher als gedacht, da Matrixein/-ausgang auf> demselben Pfostenstecker enden. Wie dem auch sei, ich habe jetzt die> vorherigen 595-Senderoutinen entfernt, eine einzelne Zeile an Vcc> gehängt und lasse mir den ausgelesenen Wert anzeigen.>>
1
>while(1){
2
>
3
>charbyte_tmp=0x00;
4
>inti;
5
>
6
>for(i=1;i<9;i++){
7
>
8
>waitms(100);
9
>
10
>byte_tmp=field_read_row(i);
11
>send_disp(('0'+i),(byte_tmp));
12
>}
13
>}
>> Ist keine Spalte aktiv, wirft er mir sporadisch die Werte ' '(0x20) &> '.'(0x2E) aus. Ist eine Spalte dagegen aktiv, kommt gar nix.
Irgendwas muss kommen.
Gar nix ist in deinem Programm nicht vorgesehen :-)
Machst du dir das Leben absichtlich schwer?
> send_disp(('0'+i), (byte_tmp));
und jetzt aus 0x20 zurückrechnen, welche Bits gesetzt sein müssen, damit
bei einer Addition mit '0' genau 0x20 rauskommt? Du wirst doch wohl eine
Funktion haben, die einen String aufs LCD pinseln kann
while (1) {
char byte_tmp = 0x00;
char buffer[20];
int i;
for(i=1; i<9; i++) {
waitms(100);
byte_tmp = field_read_row(i);
format( byte_tmp, buffer );
send_string( buffer );
}
}
void format( char* buffer, unsigned char byte )
{
char tmp[20];
char zeros[] = "00000000";
utoa( byte, tmp, 2 );
strcpy( buffer, &zeros[ strlen( tmp ) ];
strcat( buffer, tmp );
}
und dann kann man sich auch mal die Bits in 'Aktion' ansehen.
Welche stehen wie eine 1, welche kippen. Wandern sie vielleicht durch
das Byte durch etc.
Um das gleich klarzustellen:
Offensichtlichen Fehler gibt es in deinem Programm keinen mehr. Daher
musst du dir selber helfen, wir hier auf der anderen Seite des Schirms
können nur noch raten (es sei denn jemand hat noch eine göttliche
Eingebung). Dazu musst du dir aber eine Visualisierung der Daten
schaffen, mit der du etwas anfangen kannst. Bei dir spielt sich da
irgendwas auf Bitebene ab, also ist es sinnvoll, sich die
Einelese-Ergebnisse auch auf Bitebene anzusehen (dann braucht man nicht
dauernd umrechnen)
Oh, da hat ich wohl noch einen Denkfehler drin. Muss natürlich:
1
send_disp(('0'+i),('0'+byte_tmp));
heissen und sollte mir dir aktiven Spalten bis A bis D (entsprechend '1'
bis '8') anzeigen. Tut's natürlich nicht, sondern spuckt willkürliche
(?) Werte ohne erkennbares Muster aus.
Karl heinz Buchegger schrieb:
> Machst du dir das Leben absichtlich schwer?
Ja, das Gefühl hab ich schonmal...aber ohne wär ja langweilig ;)
LCD hängt keins dran, nur ein doppeltes 7-Segment-Display am
I2C-Controller. Aber Du hast Recht, ich werd das Ganze mal so umbauen,
dass ich die ausgelesenen Bits direkt angezeigt bekomme. Wärn's doch
8-Segmenter ;-))
Andi B. schrieb:
> LCD hängt keins dran, nur ein doppeltes 7-Segment-Display am> I2C-Controller.
Tschuldigung. Mein Fehler.
Da hab ich wohl die Informationen aus ein paar anderen Threads
durcheinander gewürfel. Hst du ja ganz am Anfang schon gesagt, dass da
ein 7_Seg drann hängt.
> Aber Du hast Recht, ich werd das Ganze mal so umbauen,> dass ich die ausgelesenen Bits direkt angezeigt bekomme. Wärn's doch> 8-Segmenter ;-))
Du hast ja 2 davon :-)
Und ein Byte hat 2 Nibbles, die man wunderbar als jeweils eine
HEX-Ziffer auf einem 7-Seg anzeigen kann :-)
Danke für Deine Hilfe Karl Heinz!
Mir scheint ich hab hier ein weitaus schwerwiegenderes Problem, da ich
je nach Handauflegen ein anderes Bitmuster erzeugen kann... (und das hab
ich jetzt grad noch gebraucht)
>Mir scheint ich hab hier ein weitaus schwerwiegenderes Problem, da ich>je nach Handauflegen ein anderes Bitmuster erzeugen kann... (und das hab>ich jetzt grad noch gebraucht)
Das riecht geradezu nach floatenden Pins oder nach
Haarrissen und dadurch nur kapazitive Kopplung.
Oder kalte Lötstelle.
Kalte Lötstellen und Haarrisse kann ich ausschließen, der Aufbau
befindet sich auf einem Sperrholzbrettchen. Dann wohl eher das
"spassige" Kabelgewirr auf der Unterseite.
Nochmal zur Idee von Karl-Heinz, die Anzeige des Bitmusters (LS0-3 sind
die Register des I2C-Controller):
Ok, die o.a. Verschiebung hat sich wieder erledigt...mein Fehler. Ich
hatte zwischenzeitlich den SPI Mode beim Senden von 3 auf 0 geändert,
was natürlich genau das hervorruft.
> ...und das jeweils für zwei auffeinanderfolgende Zeilen.
Aber daran verzweifel ich grad...
Mahlzeit und ein kurzes Update:
Nachdem ich nochmals an den einzelnen Pins des HC165 nachgemessen habe
und dort auch wirklich nur die aktiven Spalten der Matrix wiedergefunden
habe, scheint es wohl doch das SPDR zu sein welches bei einmaliger SPI
Abfrage die falschen Werte (sprich bei 2 aufeinanderfolgenden Zeilen die
gleichen aktiven Spalten) ausgibt.
Ich habe das Auslesen dementsprechend auf eine mehrmalige Abfrage
geändert:
1
for(i=1;i<9;i++){
2
3
j=0;
4
while(j<200){
5
6
byte_tmp=field_read_row(i);
7
send_disp_bits(byte_tmp,i);
8
waitms(100);
9
j++;}
10
}
...und überlege nun, ob ich das Ganze in die o.a. 'SPI_MasterReceive()'
(vorheriges Neuladen des HC165 vorrausgesetzt) oder in die Routine zum
Spalten auslesen 'field_read_row(char row)' packe.
btw: Zusätzliche hochohmige Pulldown-Widerstände an den Eingängen des
HC165 scheinen wohl auch nicht verkehrt.
Update 2: Und wenn man vor dem Crimpen des Pfostensteckers das Ende des
Flachbandkabels ordentlich abschneidet, so dass keine Kupferadern als
kleine Antennen herausschauen, verschwinden auch die Probleme des
"Handauflegens".
Und wie bereits gesagt, mit mehrfachem Auslesen funktioniert nun alles
bestens!
(Nur das angesprochene SPDR Problem bleibt mir immer noch ein Rätsel...)
Gruss,
Andi
Hallo,
Andi B. schrieb:
>btw: Zusätzliche hochohmige Pulldown-Widerstände an den Eingängen des>HC165 scheinen wohl auch nicht verkehrt.
Falls da Tasten an den Eingängen sind, wo soll wohl bei offener Taste
ein definierter Pegel herkommen? Das ist CMOS, das lädt sich je nach
Sonnenstand, Luftfeuchte, Stürungen im Umfeld auf irgendwas auf...
Selbstverständlich müssen da PullDown/PullUp-Widerstände ran, je nach
dem wie der Ruhepegel sein soll.
PS: Mit Schaltplan hätten es die Forumsmitglieder merklich einfacher
gehabt.
Gruß aus Berlin
Michael
Michael U. schrieb:
> Selbstverständlich müssen da PullDown/PullUp-Widerstände ran, je nach> dem wie der Ruhepegel sein soll.
Daran zweifelt ja keiner. Aber das war ja nicht das ursprüngliche
Problem, welches nach wie vor eigentlich vorhanden ist und bei dem der
Schaltplan nicht weiterhilft.
Hallo,
Andi B. schrieb:
> Michael U. schrieb:>> Selbstverständlich müssen da PullDown/PullUp-Widerstände ran, je nach>> dem wie der Ruhepegel sein soll.>> Daran zweifelt ja keiner. Aber das war ja nicht das ursprüngliche> Problem, welches nach wie vor eigentlich vorhanden ist und bei dem der> Schaltplan nicht weiterhilft.
ich habe nicht alles verfolgt, aber Du bist sicher, daß Du einen
Programmfehler suchst und nicht einfach nur die offenen Leitungen des
HC165 viel zu lange brauchen, un einen definierten Pegel anzunehmen?
Wer sagt Dir, daß nicht schon das kapazitive Übersprechen zwischn den
benachbarten Pins und Leitungen für den Fehler sorgt?
Ich habe jedenfalls noch keine Abweichung im Verhalten des SPI gefunden,
der hat bisher immer genau das eingelesen, was ihm von außen angeboten
wurde.
Dein "mehrmals einlesen" deutet zumindest darauf hin, daß die Pegel an
den Eingängen des 165 ewig brauchen, bis sie mal zu Deinen Erwartungen
passen.
Gruß aus Berlin
Michael
Michael U. schrieb:
> ich habe nicht alles verfolgt, aber Du bist sicher, daß Du einen> Programmfehler suchst und nicht einfach nur die offenen Leitungen des> HC165 viel zu lange brauchen, un einen definierten Pegel anzunehmen?
Die offenen Leitungen haben ja nun durch die Pulldown Ihren definierten
Pegel (keine Ahnung was mich da geritten hat, die zu vergessen) und an
den Pins liegt auch definitiv nur die aktive Spalte an. Zumindest im
Messgeschwindigkeitsbereich eines Multimeters soweit nachgeprüft (mit
Oszi kann ich leider nicht dienen).
Michael U. schrieb:
> Dein "mehrmals einlesen" deutet zumindest darauf hin, daß die Pegel an> den Eingängen des 165 ewig brauchen, bis sie mal zu Deinen Erwartungen> passen.
Genau das habe ich auch weiterverfolgt und eine kurze Wartezeit zwischen
Setzen der aktiven Zeile und Übernahme der aktiven Spalten in den HC165
eingefügt:
1
// RCK -> low
2
PORTB&=~(PORT_SS);
3
// PL -> low
4
PORTB&=~(PORT_PL);
5
// send bytewise in corresponding order of shift registers
6
// µC -> RED -> REED -> GREEN -> GND
7
SPI_MasterTransmit(byte_gnd);
8
SPI_MasterTransmit(byte_grn);
9
SPI_MasterTransmit(byte_reed);
10
SPI_MasterTransmit(byte_red);
11
// RCK -> high will set shift register > storage register
12
PORTB|=(PORT_SS);
13
// give some time for setting shift register > storage register
14
waitms(1);
15
// set clock pulse high and... (CLK & PL are internally wired together)
16
//PORTB |= (PORT_SCK); // due to CPOL=1, SCK is high when idle
17
// PL -> high will load pin state to shift register