Forum: Compiler & IDEs SPI Probleme beim Auslesen


von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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:
1
while (1) {
2
    
3
    char byte_tmp = 0x00;
4
    char c_tmp[1];
5
    int i;
6
7
    for(i=1; i<9; i++) {
8
      
9
      waitms(100);
10
11
      byte_tmp = field_read_row(i);
12
      
13
      // first received bit is PINH/D7 == COL H
14
      if(byte_tmp) {
15
        
16
        itoa(i,c_tmp,10);
17
        switch(byte_tmp)
18
        {
19
          case 0x80: send_disp(c_tmp[0], 'H');  break; // 10000000 0x80
20
          case 0x40: send_disp(c_tmp[0], 'G');  break; // 01000000 0x40
21
          case 0x20: send_disp(c_tmp[0], 'F');  break; // 00100000 0x20
22
          case 0x10: send_disp(c_tmp[0], 'E');  break; // 00010000 0x10
23
          case 0x08: send_disp(c_tmp[0], 'D');  break; // 00001000 0x08
24
          case 0x04: send_disp(c_tmp[0], 'C');  break; // 00000100 0x04
25
          case 0x02: send_disp(c_tmp[0], 'B');  break; // 00000010 0x02
26
          case 0x01: send_disp(c_tmp[0], 'A');  break; // 00000001 0x01
27
        }
28
      }  
29
    }    
30
  }

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
char field_read_row(char row) {
2
3
  char byte_tmp = 0x01;
4
  
5
  switch(row)
6
  {
7
    case 8: byte_tmp <<= 0;  break; // 00000001 0x01
8
    case 7: byte_tmp <<= 1;  break; // 00000010 0x02
9
    case 6: byte_tmp <<= 2;  break; // 00000100 0x04
10
    case 5: byte_tmp <<= 3;  break; // 00001000 0x08
11
    case 4: byte_tmp <<= 4;  break; // 00010000 0x10
12
    case 3: byte_tmp <<= 5;  break; // 00100000 0x20
13
    case 2: byte_tmp <<= 6;  break; // 01000000 0x40
14
    case 1: 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
void SPI_MasterTransmit(char cData) {
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
char SPI_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
  return SPDR;
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

von Stefan E. (sternst)


Lesenswert?

itoa erzeugt Strings, also mit einer Null-Terminierung. 1 ist als Größe 
für c_tmp daher zu wenig.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.)

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

Stimmt! Der Null-Term gehört natürlich dazu... aber wie gesagt, ich hab 
es bereits ersetzt, Fehler bleibt.

von Karl H. (kbuchegg)


Lesenswert?

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?

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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 ?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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
  char byte_tmp = 0x00;
4
  int i;
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.

von Karl H. (kbuchegg)


Lesenswert?

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
>   char byte_tmp = 0x00;
4
>   int i;
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)

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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 ;-))

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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)

von holger (Gast)


Lesenswert?

>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.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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):
1
char tmp_ascii[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
2
  
3
decode_char(tmp_ascii[(bits >> 4)], 1, &LS0, &LS1);
4
decode_char(tmp_ascii[(bits & 0xF)], 0, &LS2, &LS3);

ergibt beim Test, wie schon erahnt, eine Bitverschiebung.

Spalte  erwartet  ausgelesen
1.      0x01    0x02
2.      0x02    0x04
3.      0x04    0x08
4.      0x08    0x10
5.      0x10    0x20
6.      0x20    0x40
7.      0x40    0x80
8.      0x80    0x10

...und das jeweils für zwei auffeinanderfolgende Zeilen.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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...

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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.

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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

von Michael U. (amiga)


Lesenswert?

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

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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.

von Michael U. (amiga)


Lesenswert?

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

von Andi B. (g_r_i_z_z_l_y)


Lesenswert?

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
18
PORTB |= (PORT_PL);
19
// activate Clock Enable input
20
PORTB &= ~(PORT_CE);
21
// receive data
22
byte_tmp = SPI_MasterReceive();
23
// deactivate Clock Enable input
24
PORTB |= (PORT_CE);

Ergebnis: Funktioniert auch... ;)

Trotzdem nochmals Dank an alle!

Gruss,
Andi

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.