Forum: Mikrocontroller und Digitale Elektronik Problem mit SPI / Linux


von Roland K. (rolandk)


Lesenswert?

Hallo zusammen,

ich habe bereits das ganze Forum nach einer Lösung für mein Problem 
abgesucht, bin aber trotzdem noch nicht viel weitergekommen.

Und zwar habe ich folgendes Problem:
Ich bin unter Linux <> SPI ein absoluter Newbie, alles ist für mich fast 
völliges Neuland, deshalb Erklärungen bitte so angeben, dass sie auch 
ein Doofy versteht!

Mein Problem ist: Ich muss unter Linux und einem i.MX6-Prozesssor(= 
Master) einen AD-Wandler (MAX11209 = Slave) ca. 1x pro Sekunde über die 
SPI-Schnittstelle auslesen (Poll-Betrieb).

Softwaremäßig habe ich mich an das im Web erhältliche Beispiel gehalten 
(spidev_test.c). Wenn ich das Programm starte, MOSI mit MISO verbinde, 
CS auf Low lege funktioniert alles prima.

Wenn ich allerdings den AD-Wandler abfragen will, geht alles auf 'nogo'. 
Die Initialisierung des Wandlers sowie die Abfrageroutine scheinen in 
Ordnung zu sein. Dies habe ich an Hand eines SPI-Programmes für den NXP 
LPC2368 überprüft. Hier funktioniert alles mit den gleichen Parametern 
einwandfrei.

Meine Vorgehensweise ist folgende:
Ich initialisiere den ADC, starte den Wandler und warte auf das 
Ready-Bit des Wandlers. Wenn dieses 'High' ist, frage ich den aus 3 
Bytes bestehenden AD-Wert ab.
Und hier beginnt mein Problem: ich erhalte vom ADC keine vernünftige 
Antwort. Alle Befehle und Werte werden m. E. einwandfrei und richtig 
über die Schnittstelle gesendet (habe ich mehrfach kontrolliert), aber 
das Ready-Flag bzw. Daten kommen trotzdem nicht bzw. nur sporadisch ein 
High-Signal mit sehr unterschiedlicher LÄnge. Der Wandler wurde bereits 
mehrfach getauscht und funktioniert  einwandfrei.
Da ich senden kann und auch auf dem Scope etwas sehe, scheint der 
SPI-Treiber auch richtig in Linux eingebunden zu sein.
Ich vermute, dass es mit der 'ioctl'-Routine zu tun hat. Deren 
Funktionsweise ist mir schleierhaft, wie muss z. B. die Initialisierung 
sein, wenn ich 1 Byte senden, aber 3 Byte empfangen will? Habe im Web 
bisher noch keine vernünftige Erklärung gefunden. Die Anzahl der zu 
sendenden Byte kann ich an die ioctl übergeben, aber wie mache ich die 
Größe des Empfangsbuffers variabel?
Wartet die ioctl so lange, bis Daten agekommen sind, und wie lange 
wartet sie (Anzahl der Bytes oder eine Zeitraum)? Sie muss doch 
mindestens so lange warten, bis die Wandlung fertig ist und die Daten 
übertragen sind? Oder muss ich(!) so lange warten, bis die Daten im 
Eingangsbuffer sind und diesen dann explizit abfragen? Wenn ja, wie?
Wie ich auf dem Scope sehe, bedient die ioctl den CS ebenfalls. Stimmt 
das Default-Timing (Delay = 0) oder muss ich über das 'Delay' einen 
anderen Wert einstellen? (Bei einem Clock von 2 MHz geht der CS ca. 10 
µs vor Sendebeginn auf Low, und ca. 15-20 µs nach dem Senden auf High. 
Ich habe bereits einen GPIO zur Erzeugung des CS verwendet, leider 
erfolglos.
Die Einstellungen der SPI für den ADC (Phase, Polarität, MSB-First usw. 
stimmen m. E. alle).

Ich daddle jetzt schon über 2 Wochen mit diesem Problem herum, komme 
aber nicht weiter und bin kurz vor der Verzweiflung.

Am liebsten wäre mir, wenn mir jemand von Euch eine (annähernd) fertige 
Routine, auch gegen Kostenerstattung, zur Verfügung stellen könnte.

Wenn mir also jemand helfen kann oder will und evtl. nähere Infos 
braucht, bitte bei mir melden.
Mein Problem ist für viele von Euch wahrscheinlich ein 'Problemchen', 
aber für mich (noch) eine echte Herausforderung.

Für Eure Hinweise und Tips schon mal im Voraus vielen Dank!


Gruß


Roland

von Christian K. (the_kirsch)


Lesenswert?

Roland K. schrieb:
> Ich vermute, dass es mit der 'ioctl'-Routine zu tun hat. Deren
> Funktionsweise ist mir schleierhaft, wie muss z. B. die Initialisierung
> sein, wenn ich 1 Byte senden, aber 3 Byte empfangen will?

Du kannst nur genauso viel Bits empfangen wie du sendest!

Für jedes Bit das du sendest, Empfängst du gleichzeitig ein Bit. Das ist 
elementares verhalten von SPI.

Must du mal im Datenblatt des AD-Wandlers nachschauen, meistens ist das 
erste Byte was der Slave sendet ein Statusregister, denn der Slave muss 
ja schon was senden, noch bevor er weiß was der Master sendet, ist ja 
gleichzeitig!

von Hannes J. (pnuebergang)


Lesenswert?

Roland K. schrieb:
> wie muss z. B. die Initialisierung
> sein, wenn ich 1 Byte senden, aber 3 Byte empfangen will?

Wer über SPI drei Bytes empfangen will muss drei Bytes senden.

> oder muss ich über das 'Delay' einen
> anderen Wert einstellen? (Bei einem Clock von 2 MHz geht der CS ca. 10
> µs vor Sendebeginn auf Low, und ca. 15-20 µs nach dem Senden auf High.

Datenblatt lesen. Seite 4 und ab Seite 14 die Bilder 5 bis 7.

> Am liebsten wäre mir, wenn mir jemand von Euch eine (annähernd) fertige
> Routine, auch gegen Kostenerstattung, zur Verfügung stellen könnte.

Ah, Hausarbeit. Na dann viel Spaß noch.

von rolandk (Gast)


Lesenswert?

Bin nochmal den Quellcode für den NXP-Prozessor durchgegangen. Habe hier 
völlig übersehen, dass ich für jedes zu empfangende Byte ein sog. 
'Dummy-Byte' rauschicke.
Manchmal ist man halt völlig hirnvernagelt, besonders dann, wenn man zu 
lange über ein Problem brütet.

Ich nehme mal an, dass ich das mit dem ioctl genauso machen muss, 
ungefähr so:


void spi_send (char test)    // test = z.B. der Lesebefehl, 1 Byte
  {
  int ret;

    struct spi_ioc_transfer tr =
  {
  .tx_buf = (unsigned long)test,
  .rx_buf = (unsigned long)rx,
  .len = ARRAY_SIZE(tx),
  .delay_usecs = delay,
  .speed_hz = speed,
  .bits_per_word = bits,
  };

  ret = ioctl(spi0_fd, SPI_IOC_MESSAGE(1), &tr);
  spi_receive(3);        // 3 Bytes abfragen
  return;
  }


void spi_receive(int count)     // Count = Anzahl der zu empfangenden 
Bytes

  {
  int ret;
  int i;
  char spi_read_data[count];
  char dummy;

  dummy = 0x00;      // Dummy-Byte

  struct spi_ioc_transfer tr =
  {
  .tx_buf = (unsigned long)dummy,
  .rx_buf = (unsigned long)rx,
  .len = ARRAY_SIZE(tx),
  .delay_usecs = delay,
  .speed_hz = speed,
  .bits_per_word = bits,
  };


  for(i = 0; i < count;i++)
  {
  ret = ioctl(spi0_fd, SPI_IOC_MESSAGE(1), &tr);

  spi_read_data(i) = rx;
  return;

  }


Liege ich so richtig?

von rolandk (Gast)


Lesenswert?

Danke für Deine Antwort.
Die Zeiten für die Steuerung des CS habe ich bestimmt schon 100x 
durchgelesen, aber anscheinend nicht mehr registriert. Bin schon ganz 
wirr im Kopf.

von rolandk (Gast)


Lesenswert?

Christian K. schrieb:
> Roland K. schrieb:
>> Ich vermute, dass es mit der 'ioctl'-Routine zu tun hat. Deren
>> Funktionsweise ist mir schleierhaft, wie muss z. B. die Initialisierung
>> sein, wenn ich 1 Byte senden, aber 3 Byte empfangen will?
>
> Du kannst nur genauso viel Bits empfangen wie du sendest!
>
> Für jedes Bit das du sendest, Empfängst du gleichzeitig ein Bit. Das ist
> elementares verhalten von SPI.
>
> Must du mal im Datenblatt des AD-Wandlers nachschauen, meistens ist das
> erste Byte was der Slave sendet ein Statusregister, denn der Slave muss
> ja schon was senden, noch bevor er weiß was der Master sendet, ist ja
> gleichzeitig!


Hallo Christian,
hatte meinen Text flasch gepostet. Bin halt selten hier im Forum.

Bin nochmal den Quellcode für den NXP-Prozessor durchgegangen. Habe hier
völlig übersehen, dass ich für jedes zu empfangende Byte ein sog.
'Dummy-Byte' rauschicke.
Manchmal ist man halt völlig hirnvernagelt, besonders dann, wenn man zu
lange über ein Problem brütet.

Ich nehme mal an, dass ich das mit dem ioctl genauso machen muss,
ungefähr so:


void spi_send (char test)    // test = z.B. der Lesebefehl, 1 Byte
  {
  int ret;

    struct spi_ioc_transfer tr =
  {
  .tx_buf = (unsigned long)test,
  .rx_buf = (unsigned long)rx,
  .len = ARRAY_SIZE(tx),
  .delay_usecs = delay,
  .speed_hz = speed,
  .bits_per_word = bits,
  };

  ret = ioctl(spi0_fd, SPI_IOC_MESSAGE(1), &tr);
  spi_receive(3);        // 3 Bytes abfragen
  return;
  }


void spi_receive(int count)     // Count = Anzahl der zu empfangenden
Bytes

  {
  int ret;
  int i;
  char spi_read_data[count];
  char dummy;

  dummy = 0x00;      // Dummy-Byte

  struct spi_ioc_transfer tr =
  {
  .tx_buf = (unsigned long)dummy,
  .rx_buf = (unsigned long)rx,
  .len = ARRAY_SIZE(tx),
  .delay_usecs = delay,
  .speed_hz = speed,
  .bits_per_word = bits,
  };


  for(i = 0; i < count;i++)
  {
  ret = ioctl(spi0_fd, SPI_IOC_MESSAGE(1), &tr);

  spi_read_data(i) = rx;
  return;

  }


Liege ich so richtig?

von Christian K. (the_kirsch)


Lesenswert?

bezüglich "struct spi_ioc_transfer"

tx_buf und rx_buf sind pointier auf Arrays
und len gibt an wie lang die Arrays sind.


#include<stdint.h>


uint8_t tx[3] = 0;
uint8_t rx[3] = 0;

struct spi_ioc_transfer tr = {
  .tx_buf = tx,
  .rx_buf = rx,
  .len = 3,
  .delay_usecs = delay,
  .speed_hz = speed,
  .bits_per_word = 8,
  };

von Florian (Gast)


Lesenswert?

wenn mich nicht alles täuscht musst du die Adresse des Empfangs bzw. 
Sendebuffers in rx_buf und tx_buf ablegen, gecastet nach unsigned long,
also:

int foo, bar;

tx_buf= (unsigned long)&foo;
rx_buf= (unsigned long)&foo;

bei arrays ist die Verwendung ohne index bereits der pointer auf das 
erste element, also:

uint8_t foo[3];
tx_buf= (unsigned long)foo;

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.