Hallo, ich hab eine kurze Frage zu SPI (embedded). Ich hab das bisher mit einem ioctl gemacht:
struct spi_ioc_transfer transfer ={ .tx_buf = (unsigned long)(wr_tmp), .rx_buf = (unsigned long)(rd_tmp), .len = (width>>3)+1, // 8->2, 16->3, 24->4, .speed_hz = SPI_SPEED, .bits_per_word = 8, };
ret = ioctl(file_spi0, SPI_IOC_MESSAGE(1), &transfer); if( ret < 1 ){ printf("could not transmit data\n"); }
Jetzt habe ich aber den seltsamen Fall, dass das Gerät eine 9 (!) Bit Adresse hat und 16 bit Daten hat. Mir ist gar nicht klar wie das gehen soll.
Und während ich das schreibe fällt mir was ein: Vielleicht so: Statt einer message mit Adresse und Daten dann zwei ioctls mit unterschiedlichen bits/word ? Hat das schon mal jemand so gemacht? Andere Ideen?
Gruß Arno
On 05/08/2017 03:41 PM, Arno Steffens wrote:
Hallo, ich hab eine kurze Frage zu SPI (embedded). Ich hab das bisher mit einem ioctl gemacht:
struct spi_ioc_transfer transfer ={ .tx_buf = (unsigned long)(wr_tmp), .rx_buf = (unsigned long)(rd_tmp), .len = (width>>3)+1, // 8->2, 16->3, 24->4, .speed_hz = SPI_SPEED, .bits_per_word = 8, };
ret = ioctl(file_spi0, SPI_IOC_MESSAGE(1), &transfer); if( ret < 1 ){ printf("could not transmit data\n"); }
Jetzt habe ich aber den seltsamen Fall, dass das Gerät eine 9 (!) Bit Adresse hat und 16 bit Daten hat. Mir ist gar nicht klar wie das gehen soll.
Deine Frage ist nicht verständlich. Was heißt, dass Gerät hat eine 9 Bit Adresse? Da es bei generischem SPI im Protokoll gar keine Adressen gibt (anders als z.B. bei i2c), sprichst Du sicher über das Geräte-spezifische Protokoll. Beschreibe mal eine Nachricht, die Du schicken willst oder zeige das Datenblatt.
Liebe Grüße Uwe
Hallo Uwe, du hast natürlich recht. Das mit den Protokoll (9bit Adresse + rw-bit + Daten) habe ich gelöst.
Für .bits_per_word werden andere Werte als 8 nicht akzeptiert, da bekomme ich eine Fehlermeldung.
Ich pussele halt die Adresse, RW-Bit und Daten so in einen Buffer, dass es passt. Kommen halt am Ende ein paar Takte mehr. Sende 4x8bit für den Inhalt von 9+1+16. Aber das scheint kein Problem zu sein.
Sehe ich am Oszi so.
Was ich nicht hinbekommen habe ist, dass ich zum Lesen eine andere Flanke samplen muss!! (siehe Bild).
Zwar gibt es
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
aber ich sehe nur eine Reaktion auf die Änderung des Write modes ?!? Scheint so, als würde ich die nicht getrennt einstellen können.
Im Devicetree gib es wohl auch ein mode, das verwende ich bisher nicht - es gibt aber auch in dem Beispiel nichts für read/write getrennt.
Gruß Arno
Gesendet: Dienstag, 09. Mai 2017 um 11:03 Uhr Von: "Uwe Kleine-König" uwe@kleine-koenig.org An: flug@lug-freiburg.de Betreff: Re: [Flug] SPI 9bit/16bit
On 05/08/2017 03:41 PM, Arno Steffens wrote:
Hallo, ich hab eine kurze Frage zu SPI (embedded). Ich hab das bisher mit einem ioctl gemacht:
struct spi_ioc_transfer transfer ={ .tx_buf = (unsigned long)(wr_tmp), .rx_buf = (unsigned long)(rd_tmp), .len = (width>>3)+1, // 8->2, 16->3, 24->4, .speed_hz = SPI_SPEED, .bits_per_word = 8, };
ret = ioctl(file_spi0, SPI_IOC_MESSAGE(1), &transfer); if( ret < 1 ){ printf("could not transmit data\n"); }
Jetzt habe ich aber den seltsamen Fall, dass das Gerät eine 9 (!) Bit Adresse hat und 16 bit Daten hat. Mir ist gar nicht klar wie das gehen soll.
Deine Frage ist nicht verständlich. Was heißt, dass Gerät hat eine 9 Bit Adresse? Da es bei generischem SPI im Protokoll gar keine Adressen gibt (anders als z.B. bei i2c), sprichst Du sicher über das Geräte-spezifische Protokoll. Beschreibe mal eine Nachricht, die Du schicken willst oder zeige das Datenblatt.
Liebe Grüße Uwe
Freiburger Linux User Group Mail an die Liste: flug@lug-freiburg.de Mailingliste verwalten (u.a. abbestellen): https://lug-freiburg.de/mailman/listinfo/flug
Hallo Arno,
On 05/09/2017 11:23 AM, star@gmx.li wrote:
du hast natürlich recht. Das mit den Protokoll (9bit Adresse + rw-bit + Daten) habe ich gelöst.
Für .bits_per_word werden andere Werte als 8 nicht akzeptiert, da bekomme ich eine Fehlermeldung.
Dann ist der spi-Treiber doof (oder die Hardware kann das nicht).
Ich pussele halt die Adresse, RW-Bit und Daten so in einen Buffer, dass es passt. Kommen halt am Ende ein paar Takte mehr. Sende 4x8bit für den Inhalt von 9+1+16. Aber das scheint kein Problem zu sein.
Sehe ich am Oszi so.
Was ich nicht hinbekommen habe ist, dass ich zum Lesen eine andere Flanke samplen muss!! (siehe Bild).
uah, ich weiß, Gewalt ist keine Lösung, aber der Hardware-Entwickler ...
Zwar gibt es
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
aber ich sehe nur eine Reaktion auf die Änderung des Write modes ?!?
Wenn wir mal einen Hardware-Bug ausschließen: Was schreibst Du denn in mode rein? Für die erste Sequenz muss da CPOL=0 + CPHA=0 (also mode=0) und für die zweite Sequenz CPOL=0 + CPHA=1 (also mode=1) einstellt sein.
Das kriegst Du nur hin, wenn Dein Treiber die Verwendung von struct spi_ioc_transfer::cs_change unterstützt und selbst dann ist das hacky.
Liebe Grüße Uwe
Gesendet: Dienstag, 09. Mai 2017 um 11:35 Uhr Von: "Uwe Kleine-König" uwe@kleine-koenig.org Betreff: Re: [Flug] SPI 9bit/16bit
Hallo Arno,
du hast natürlich recht. Das mit den Protokoll (9bit Adresse + rw-bit + Daten) habe ich gelöst.
Für .bits_per_word werden andere Werte als 8 nicht akzeptiert, da bekomme ich eine Fehlermeldung.
Dann ist der spi-Treiber doof (oder die Hardware kann das nicht).
Mh, hab noch gesehen, es gibt unabhängig von bits/word (was ja eh nicht hilft, wenn Adresse und Daten unterschiedlich sind) noch __u8 tx_nbits; __u8 rx_nbits; Hab damit aber nicht weiter rumgespielt.
Ich pussele halt die Adresse, RW-Bit und Daten so in einen Buffer, dass es passt. Kommen halt am Ende ein paar Takte mehr. Sende 4x8bit für den Inhalt von 9+1+16. Aber das scheint kein Problem zu sein.
Sehe ich am Oszi so.
Was ich nicht hinbekommen habe ist, dass ich zum Lesen eine andere Flanke samplen muss!! (siehe Bild).
uah, ich weiß, Gewalt ist keine Lösung, aber der Hardware-Entwickler ...
Ja, das war auch mein Gedanke, wtf....
Zwar gibt es
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
aber ich sehe nur eine Reaktion auf die Änderung des Write modes ?!?
Wenn wir mal einen Hardware-Bug ausschließen: Was schreibst Du denn in mode rein? Für die erste Sequenz muss da CPOL=0 + CPHA=0 (also mode=0) und für die zweite Sequenz CPOL=0 + CPHA=1 (also mode=1) einstellt sein.
Zunächst hatte ich gar keinen Mode gesetzt, aber 0 scheint default. Den Effekt am Oszi kann ich natürlich nur für den Write-Teil sehen. Und da ist "0" richtig. Mit eins bekomme ich die korrekten Daten zurück. (Es gibt ein Register mit Adresse 0, da ist es egal ob mit Mode 0 oder 1 geschrieben wird. Daher weiß ich das) Bei allen anderen Adressen werden diese natürlich falsch übermittelt, so das ich die richtige Daten zur falschen Adresse bekomme :(
Das kriegst Du nur hin, wenn Dein Treiber die Verwendung von struct spi_ioc_transfer::cs_change unterstützt und selbst dann ist das hacky.
Ich hatte da diesen kurzen Hoffnungsschimmer und hab im spi_ioc_transfer struct auch die .cs_change = 1 gesetzt, aber leider ohne Effekt. SPI_IOC_RD_MODE wird ignoriert. Ist es das was du meinst?
Jetzt fallen mir nur noch zwei Sachen ein: Clock oder Daten per Hardware verzögern, damit ich nicht direkt an der Flanke lesen muss oder den Read in zwei Protokolle aufteilen und zwischendrin den Mode wechseln. Ich schätze dazu müsste ich ausserdem noch das CS low halten, damit das Device nicht meint, es wäre ein neues Kommando.
Gruß Arno
Hallo Arno,
On 05/09/2017 12:52 PM, Arno Steffens wrote:
Mh, hab noch gesehen, es gibt unabhängig von bits/word (was ja eh nicht hilft, wenn Adresse und Daten unterschiedlich sind) noch __u8 tx_nbits; __u8 rx_nbits; Hab damit aber nicht weiter rumgespielt.
Weiss aus dem Stehgreif nicht, was das macht.
Ich pussele halt die Adresse, RW-Bit und Daten so in einen Buffer, dass es passt. Kommen halt am Ende ein paar Takte mehr. Sende 4x8bit für den Inhalt von 9+1+16. Aber das scheint kein Problem zu sein.
Ja, solche Probleme werden in der Regel immer erst relevant, wenn die ersten 1000 Geräte im Feld sind :-)
uah, ich weiß, Gewalt ist keine Lösung, aber der Hardware-Entwickler ...
Ja, das war auch mein Gedanke, wtf....
Wie alt ist denn das Gerät, über das wir da reden? Ich finde, wenn es jünger als 20 Jahre ist, gibt es keine Entschuldigung für so ein Design.
Zwar gibt es
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
aber ich sehe nur eine Reaktion auf die Änderung des Write modes ?!?
Wenn wir mal einen Hardware-Bug ausschließen: Was schreibst Du denn in mode rein? Für die erste Sequenz muss da CPOL=0 + CPHA=0 (also mode=0) und für die zweite Sequenz CPOL=0 + CPHA=1 (also mode=1) einstellt sein.
Zunächst hatte ich gar keinen Mode gesetzt, aber 0 scheint default. Den Effekt am Oszi kann ich natürlich nur für den Write-Teil sehen. Und da ist "0" richtig. Mit eins bekomme ich die korrekten Daten zurück. (Es gibt ein Register mit Adresse 0, da ist es egal ob mit Mode 0 oder 1 geschrieben wird. Daher weiß ich das) Bei allen anderen Adressen werden diese natürlich falsch übermittelt, so das ich die richtige Daten zur falschen Adresse bekomme :(
Das kriegst Du nur hin, wenn Dein Treiber die Verwendung von struct spi_ioc_transfer::cs_change unterstützt und selbst dann ist das hacky.
Ich hatte da diesen kurzen Hoffnungsschimmer und hab im spi_ioc_transfer struct auch die .cs_change = 1 gesetzt, aber leider ohne Effekt. SPI_IOC_RD_MODE wird ignoriert. Ist es das was du meinst?
Die Fehlerbehandlung im spi-Framework ist bescheiden, deswegen wird das cs_change vielleicht ignoriert. Lesen ginge ungefähr so:
/* transfer address, keeping CS active at the end */ transfer.cs_change = 1; transfer.bits_per_word = 10; transfer.len = 1; ioctl(..., &transfer);
/* switch mode */ mode = 1 ioctl(fd, SPI_IOC_WR_MODE, &mode);
/* read datum */ transfer.cs_change = 0; transfer.bits_per_word = 16; transfer.len = 1; ioctl(..., &transfer);
Vermutlich ist es einfacher das Protokoll mit gpio-bitbanging zu implementiern als einen spi-Treiber dazuzubekommen, das so zu machen, wie es muss.
Uwe
Gesendet: Dienstag, 09. Mai 2017 um 13:57 Uhr Von: "Uwe Kleine-König" uwe@kleine-koenig.org An: "Arno Steffens" epsi@gmx.de Cc: flug@lug-freiburg.de Betreff: Re: Aw: Re: [Flug] SPI 9bit/16bit
Hallo Arno,
On 05/09/2017 12:52 PM, Arno Steffens wrote:
Mh, hab noch gesehen, es gibt unabhängig von bits/word (was ja eh nicht hilft, wenn Adresse und Daten unterschiedlich sind) noch __u8 tx_nbits; __u8 rx_nbits; Hab damit aber nicht weiter rumgespielt.
Weiss aus dem Stehgreif nicht, was das macht.
Ich pussele halt die Adresse, RW-Bit und Daten so in einen Buffer, dass es passt. Kommen halt am Ende ein paar Takte mehr. Sende 4x8bit für den Inhalt von 9+1+16. Aber das scheint kein Problem zu sein.
Ja, solche Probleme werden in der Regel immer erst relevant, wenn die ersten 1000 Geräte im Feld sind :-)
uah, ich weiß, Gewalt ist keine Lösung, aber der Hardware-Entwickler ...
Ja, das war auch mein Gedanke, wtf....
Wie alt ist denn das Gerät, über das wir da reden? Ich finde, wenn es jünger als 20 Jahre ist, gibt es keine Entschuldigung für so ein Design.
Zwar gibt es
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
aber ich sehe nur eine Reaktion auf die Änderung des Write modes ?!?
Wenn wir mal einen Hardware-Bug ausschließen: Was schreibst Du denn in mode rein? Für die erste Sequenz muss da CPOL=0 + CPHA=0 (also mode=0) und für die zweite Sequenz CPOL=0 + CPHA=1 (also mode=1) einstellt sein.
Zunächst hatte ich gar keinen Mode gesetzt, aber 0 scheint default. Den Effekt am Oszi kann ich natürlich nur für den Write-Teil sehen. Und da ist "0" richtig. Mit eins bekomme ich die korrekten Daten zurück. (Es gibt ein Register mit Adresse 0, da ist es egal ob mit Mode 0 oder 1 geschrieben wird. Daher weiß ich das) Bei allen anderen Adressen werden diese natürlich falsch übermittelt, so das ich die richtige Daten zur falschen Adresse bekomme :(
Das kriegst Du nur hin, wenn Dein Treiber die Verwendung von struct spi_ioc_transfer::cs_change unterstützt und selbst dann ist das hacky.
Ich hatte da diesen kurzen Hoffnungsschimmer und hab im spi_ioc_transfer struct auch die .cs_change = 1 gesetzt, aber leider ohne Effekt. SPI_IOC_RD_MODE wird ignoriert. Ist es das was du meinst?
Die Fehlerbehandlung im spi-Framework ist bescheiden, deswegen wird das cs_change vielleicht ignoriert. Lesen ginge ungefähr so:
/* transfer address, keeping CS active at the end */ transfer.cs_change = 1; transfer.bits_per_word = 10; transfer.len = 1; ioctl(..., &transfer);
/* switch mode */ mode = 1 ioctl(fd, SPI_IOC_WR_MODE, &mode);
/* read datum */ transfer.cs_change = 0; transfer.bits_per_word = 16; transfer.len = 1; ioctl(..., &transfer);
Vermutlich ist es einfacher das Protokoll mit gpio-bitbanging zu implementiern als einen spi-Treiber dazuzubekommen, das so zu machen, wie es muss.
Ja, so hab ich mir das auch gedacht. Das cs_change halt das CS auch schön low zwischen den Transfers. War schon mal ein guter Tip! Hilft aber nur dann richtig, wenn ich tatsächlich die bit_per_word auf 10 setzen kann. Derzeit wird jeder andere Wert ausser 8 mit "could not transmit data -1" returniert.
Auf bitbanging hab ich wenig Lust. Geht das unter Linux überhaupt schnell genug? Das klingt nach ganz viel Arbeit. Im Moment lese ich auf "edge" trotz Flankenwechsel einigermassen stabil. Klar, bei anderen Temp, Mondphase .... Zur ersten Inbetriebnahme mag das reichen, da muss ich nichts lesen. Vielleicht finde ich im Treiber noch was bzgl word-length.
Danke Uwe. Arno
Hallo Arno,
On 05/10/2017 11:42 AM, Arno Steffens wrote:
Die Fehlerbehandlung im spi-Framework ist bescheiden, deswegen wird das cs_change vielleicht ignoriert. Lesen ginge ungefähr so:
/* transfer address, keeping CS active at the end */ transfer.cs_change = 1; transfer.bits_per_word = 10; transfer.len = 1; ioctl(..., &transfer);
/* switch mode */ mode = 1 ioctl(fd, SPI_IOC_WR_MODE, &mode);
/* read datum */ transfer.cs_change = 0; transfer.bits_per_word = 16; transfer.len = 1; ioctl(..., &transfer);
Vermutlich ist es einfacher das Protokoll mit gpio-bitbanging zu implementiern als einen spi-Treiber dazuzubekommen, das so zu machen, wie es muss.
Ja, so hab ich mir das auch gedacht. Das cs_change halt das CS auch schön low zwischen den Transfers. War schon mal ein guter Tip! Hilft aber nur dann richtig, wenn ich tatsächlich die bit_per_word auf 10 setzen kann.
Oder einen Teiler von 10. 5 könnte (von Seite der Hardware) unterstützt werden.
Auf bitbanging hab ich wenig Lust. Geht das unter Linux überhaupt schnell genug? Das klingt nach ganz viel Arbeit.
Kommt drauf an, was "schnell genug" heißt. Und wie wichtig es ist, es richtig zu machen. Einen Userspace-Treiber zu schreiben, der per gpioctl Lese- und Schreibzugriffe macht, klingt nach einem Arbeitstag für einen Prototypen und vielleicht noch einen Tag um die Fehlerbehandlung sauber zu machen und ausgiebig zu testen.
Vielleicht ist es am zielführendsten mal den Gerätehersteller anzurufen und sich (hoffentlich) bestätigen zu lassen, dass man mit mode0 auch vorgehen darf und dass die zusätzlichen clks am Ende nicht schaden.
Liebe Grüße Uwe