Hallo,
ich habe ein Codebeispiel geschrieben, mit dem man unter Linux den
SPI-Bus durch das Setzen von Registern steuert.
Den Weg über den Linux-Treiber:
1
system("modprobe spi-sun7i");
2
spiDevice=open("/dev/spidev0.0",O_RDWR));
3
...
wollte ich nicht nehmen, weil ich bestimmte Timings nicht einhalten
konnte.
Im konkreten Fall habe ich einen MCP3208 an SPI0 gehängt (an SS0).
Ich finde, aus dem Datenblatt des Allwinner A20 lässt sich die
Funktionweise, wann welches Bit geschrieben wird, nicht erkennen.
Ein Beispiel habe ich im Internet auch nicht gefunden. Deshalb
veröffentliche ich hier meinen Code.
Da werden sicherlich noch irgendwo Fehler sein.
(Datenblatt: https://linux-sunxi.org/A20 -> User Manual)
Derzeit läuft der SPI auf 2.5MHz (wenn ich richtig gemessen habe).
Mit der Methode ist die Framelänge maximal 64 Byte, danach ist der
TX-Fifo voll.
LG Andreas
Hi,
ich habe vor einiger einmal versucht, ein TFT-Display über SPI mit dem
BananaPro zu verbinden. Das hat auch funktioniert, war aber viel zu
langsam, weil der SPI-Controller zwischen den einzelnen Bytes eine sehr
lange Pause eingebaut hat, die sich nicht verkürzen ließ. Die Länge der
Pause blieb immer gleich unabhängig vom SPI-Clock. Ich habe da viel Zeit
reingesteckt aber das Problem nicht gelöst.
Hast Du eine ähnliche Erfahrung? Beim Raspberry tritt das Problem nicht
auf.
Was meinst du mit Pause, dass das Clock-Signal stehen geblieben ist
zwischen den Bytes?
Bei mir sind die Bytes alle direkt nacheinander übertragen wurden.
Das Problem bei Benutzung des Linux-Treibers war, dass nachdem das CS0
gesetzt wurde ca. 10µs vergangen sind, bis er angefangen hat zu senden.
Nach dem Senden sind nochmal ca. 15µs vergangen (siehe Bild).
Wie lang ist bei dir die Pause zwischen 2 Bytes und wie viele Bytes
verschickst du mit einem Burst?
Beim Oszi-Bild, wie es bei mir vorher (mit Linux-Treiber) aussah:
Die obere Kurve ist Channel-Select (0-Aktiv). Die untere ist das
Clock-Signal, leider durch Aliasing nicht so gut erkennbar. Aber man
sieht, wann die Clock aktiv ist und wann nicht.
Den Cursor kannst du ignorieren.
Hi,
> Das Problem bei Benutzung des Linux-Treibers war, dass nachdem das CS0> gesetzt wurde ca. 10µs vergangen sind, bis er angefangen hat zu senden.> Nach dem Senden sind nochmal ca. 15µs vergangen (siehe Bild).
Genau das war das Problem, ist schon eine Weile her, deswegen hatte ich
es nicht mehr genau in Erinnerung. Ich habe das Problem hier bschrieben:
Beitrag "SPI Chipselect-Delay auf Banana PI" (unter einem anderen
Nickname).
Das ganze hat dazu geführt, dass ich das Display am BananaPro nicht
nutzen konnte, weil viel zu langsam. Die Übertragung eines kompletten
Framebuffers war eine Angelegenheit von einigen Sekunden.
Verstehe ich das richtig, dass diese Verzögerung bei Deiner eignen
Implementierung nicht auftritt?
Ich habe das Betriebsystem Bananian.
Bei meinem Code tritt das Problem nicht auf.
Die Verzögerung zwischen zwei Übertragungen ist bei mir ca. 4µs. Ich
nehme an, die entsteht durch meinen Code (Schreiben in TX-FIFO, lesen
vom RX-FIFO, usw).
Wenn du diesen Code einsetzt, vergleich mal mit dem Datenblatt, ob die
Bits im Config-Register sinvoll gesetzt sind. :p
Zum DMA:
In dieser Implementierung des Treibers wird der DMA-Modus eingesetzt,
wenn mehr als 64-Bytes übertragen werden:
http://dl.cubieboard.org/parteners/waveshare/Source_Code/a20-cubieboard-dvk/driver%20source/spi-sun7i.c
Funktion: sun7i_spi_xfer
Ich weiß nicht, ob das der tatsächlich eingesetzte Treiber ist. Ich bin
auch kein Treiberexperte.
Da das Programm beim Aufruf von ioctl(...) unterbrochen wird, bis die
Übertragung fertig ist, finde ich, dass man sich den DMA dann auch
sparen kann. Schließlich dauert das Übertragen viel länger, als Bytes
durch den Prozessor zu schleifen.
Wenn man mehr als 64 Bytes übertragen möchte, könnte man während der
Übertragung noch Bytes in den TX-FIFO schreiben und aus dem RX-FIFO
lesen (damit er nicht überläuft).
Im verlinkten Treiber wird erst das Start-Burst-Bit gesetzt und danach
die Bytes in den TX-FIFO geschoben.
Ist meine Meinung dazu.
Ok, danke für Deine Hinweise. Ich werde Deinen Sourcecode mal anpassen
und testen. Wäre ja cool, wenn das doch noch funktionieren würde.
Ich bin mittlerweile zu der Einsicht gekommen, dass DMA in meinem Fall
ohnehin nicht sinnvoll ist. Das TFT-Display hat ein Parallel-interface,
das über ein Schieberegister mit dem SPI-Bus verbunden ist. Man schreibt
dann erst das Schieberegister über SPI (16bit) und dann wird über einen
Impuls auf einem GPIO der Wert in den Displaycontroller übertragen.
Danach muss man ohnehin einen neuen SPI-Transfer aufsetzen für die
nächsten beiden Bytes.
Mit dem gleichen SPI-Takt läft das auf einem Raspberry so schnell, dass
man sogar Videos abspielen kann (mit geringer Framerate), aber auf dem
BananaPro war das wie gesagt schnarchlangsam, und das nur, weil die
Verzögerung nach dem CS0 um ein Vielfaches länger war als die
eigentliche Übertragung.
Anfangs dachte ich, das geht so langsam, weil kein DMA verwendet wird,
aber der eigentliche Grund ist das CS0-Delay.
Also danke nochmal.
Ok, ich habe Deinen Code mal getestet. Saubere Sache, ich messe zwischen
CS->low und dem ersten Clock jetzt ca. 700ns, die ganze Übertragung
eines 16Bit-Wertes dauert jetzt 7us gegenüber 45us vorher. Wobei diese
Zeit jetzt hauptsächlich vom SPI-Clock bestimmt wird und nicht mehr von
dieser unsinning großen Delay am Anfang. Mit einem höheren SPI-Clock
sollte das also noch schneller werden. Das sieht alles sehr gut aus.
Ich frage mich nur, wieso sich der Linux-Treiber so unsinnig verhält.
Vielleicht liegt irgendwo im spi_sun7i-Modul ein Hund begraben, denn
dort werden anscheinend die Register-Settings im A20 gemacht. Das müsste
ich jetzt mal mit Deinen Settings vergleichen. Also jetzt erstmal
Datenblatt inhalieren.