SPI.transfer übermittelt nur einmal Array Inhalt

OP #7390171
Lesenswert?

Hallo,

ich denke zu folgendem Problem fehlen mir gewisse Grundlagen:

Die fillDisplay Funktion unten überträgt nur beim ersten SPI.transfer den array Inhalt "0xff, 0x00, 0xff" (also bei i=0), danach wird bis i < 102400 immer 3mal 0x00 übertragen.

Wieso das?

1
void fillDisplay(void){
2
    uint8_t array[3]={0xff,0x00,0xff};
3

4
    SPI1.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0));
5
    for(uint32_t i=0; i<102400;i++){
6
        SPI1.transfer(&array,3);
7

8
    }
9
    SPI1.endTransaction();
10
}
#7390182
Lesenswert?

Leider verschweigst du die Definition von SPI1.transfer(). Damit können wir nur raten.

SPI1.transfer(&array,3) ist vermutlich falsch. Das überträgt einen Zeiger auf einen Zeiger auf ein Array.

Entweder SPI1.transfer(&array[0],3) (Zeiger auf das erste Element des Arrays) oder SPI1.transfer(array,3) (Zeiger auf das Array) wäre hier vermutlich korrekt.

Das es beim ersten Aufruf funktioniert ist vermutlich Zufall, da an der Speicherstelle (vermutlich Stack) zufällig noch der korrekte Wert aus der Initialisierung steht.

OP #7390184
Lesenswert?

Michael H. schrieb:

SPI1.transfer(array,3) (Zeiger auf das Array)

das gibt ein Fehler zurück, siehe Anhang.

unten die Klasse... Verwendet wird, oder sollte wohl folgende Funktion:

1
virtual void transfer(void *buf, size_t count);
1
class MbedSPI : public SPIClass
2
{
3
public:
4
    MbedSPI(int miso, int mosi, int sck);
5
    MbedSPI(PinName miso, PinName mosi, PinName sck);
6
    virtual uint8_t transfer(uint8_t data);
7
    virtual uint16_t transfer16(uint16_t data);
8
    virtual void transfer(void *buf, size_t count);
9

10
    // Transaction Functions
11
    virtual void usingInterrupt(int interruptNumber);
12
    virtual void notUsingInterrupt(int interruptNumber);
13
    virtual void beginTransaction(SPISettings settings);
14
    virtual void endTransaction(void);
15

16
    // SPI Configuration methods
17
    virtual void attachInterrupt();
18
    virtual void detachInterrupt();
19

20
    virtual void begin();
21
    virtual void end();
22

23
private:
24
    SPISettings settings = SPISettings(0, MSBFIRST, SPI_MODE0);
25
    _mbed_spi* dev = NULL;
26
    PinName _miso;
27
    PinName _mosi;
28
    PinName _sck;
29
};
Angehängte Dateien:
Moderator (Firma: Titel) Persönliche Seite #7390192
Lesenswert?

Epi K. schrieb:

überträgt nur beim ersten SPI.transfer den array Inhalt

Hast du das mit dem Oszi auf dem Bus gemessen?

SPI.transfer

Du hast dich aber schon informiert, wie SPI prinzipiell funktioniert? Du weißt, dass bei SPI gleichzeitig mit jedem gesendeten Bit auch ein Bit empfangen wird? Was macht also diese Funktion? Gibt sie evtl. im Puffer die gleichzeitig empfangenen SPI-Daten zurück?

Epi K. schrieb:

das gibt ein Fehler zurück, siehe Anhang.

Ist ja eigentlich klar: die Funktion möchte einen void Pointer und du übergibst einen char Pointer.

Probiers mal so: SPI1.transfer((void*)array,3);

Und dann arbeite doch einfach mal irgendein Grundlagenbuch zum Thema C durch. Darin besonders den Abschnitt Pointer und Arrays.

#7390194
Lesenswert?

Epi K. schrieb:

Michael H. schrieb:

SPI1.transfer(array,3) (Zeiger auf das Array)

das gibt ein Fehler zurück, siehe Anhang.

Ja logisch. array IST schon ein Zeiger auf das Array! &array ist Unsinn, das wäre ein Zeiger auf den Zeiger auf das Array. Bestenfalls &array[0] wäre OK, was ein Zeiger auf das erste Element ist, was gleichbedeutend mit array ist. Ja, die Zeiger in C sind manchmal etwas verwirrend.

(Firma: Gast) #7390203
Lesenswert?

1
void fillDisplay(void)
2
{
3
  const uint8_t array[3] = {0xff, 0x00, 0xff};
4
  SPI1.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0));
5
  for(uint32_t i = 0; i < 102400; i++)
6
   for(const uint8_t zelle:array)
7
    SPI1.transfer(zelle);
8
 SPI1.endTransaction();
9
}

ohne Gewähr

OP #7390210
Lesenswert?

J. S. schrieb:

SPI1.transfer(array,3)

das geht nicht

Lothar M. schrieb:

Du weißt, dass bei SPI gleichzeitig mit jedem gesendeten Bit auch ein Bit empfangen wird?

Lothar M. schrieb:

Was macht also diese Funktion? Gibt sie evtl. im Puffer die gleichzeitig empfangenen SPI-Daten zurück?

Wilhelm M. schrieb:

Die Signatur der Funktion hat explizit einen Input/Output-Parameter. Das Array wird wohl mit der Antwort überschrieben.

wird wohl das Hauptproblem sein. Ich denke man erkennt es daran?

1
void arduino::MbedSPI::transfer(void *buf, size_t count) {
2
    dev->obj->write((const char*)buf, count, (char*)buf, count);
3
}

so funktioniert es...

1
      for(uint32_t i=0; i<102400;i++){
2
        uint8_t array[3]={0xff,0x00,0xff};
3
        SPI1.transfer(&array,3);
4
      }

Falk B. schrieb:

Ja logisch. array IST schon ein Zeiger auf das Array! &array ist Unsinn

scheint aber zu funktionieren...

vielen Dank schon mal...

OP #7390211
Lesenswert?

J. S. schrieb:

SPI.transfer() ist hier ungünstig und langsam, Grund wurde ja schon genannt. Besser ist es eine ganze Zeile im RAM zu initialisieren und diese per SPI.write() zu senden, ohne das Rücklesen was die meisten Displays eh nicht unterstützen.

hmm interessant, kann man mit write() auch Buffer / Arrays übertragen? Naja muss ich mich wohl mal schlau machen.

Arduino F. schrieb:

for(const uint8_t zelle:array)

das habe ich noch nie gesehen, und diesen Code verstehe ich auch nicht...

OP #7390240
Lesenswert?

J. S. schrieb:

SPI.transfer() ist hier ungünstig und langsam, Grund wurde ja schon genannt. Besser ist es eine ganze Zeile im RAM zu initialisieren und diese per SPI.write() zu senden, ohne das Rücklesen was die meisten Displays eh nicht unterstützen.

da würde mich schon interessieren wie das gehen soll und ob es schneller ist, mit SPI.write kann man ja nur 1 Byte übertragen, kein Buffer...?

Siehe Anhang Bild, ich verliere aktuell ca. 2 Clocks an Zeit zwischen jedem Byte...

Angehängte Dateien:
#7390277
Lesenswert?

Epi K. schrieb:

diese per SPI.write() zu senden, ohne das Rücklesen was die meisten Displays eh nicht unterstützen.

da würde mich schon interessieren wie das gehen soll und ob es schneller ist, mit SPI.write kann man ja nur 1 Byte übertragen, kein Buffer...?

Doch, die Methode write überträgt auch Arrays. Ein "Experte" hat es geschafft, die überaus sinnvolle Methode zu verkrüppeln, indem die Empfangsdaten die Sendedaten überschreiben. Man hätte schlicht die Methode write DIREKT nutzen sollen! Es leben die 1001 Abstraktionsschichten!

1
void arduino::MbedSPI::transfer(void *buf, size_t count) {
2
    dev->obj->write((const char*)buf, count, (char*)buf, count);
3
}
OP #7390284
Lesenswert?

nun ja, ich verstehe diese transfer Funktion noch nicht richtig (sie greift ja schlussendlich auch auf write zu)...

ich kann eine Variable wie folgt anlegen:

uint32_t a = 0xFF00FF;

und mit SPI.transfer(&a,3), wird mir 0xFF, 0x00, 0xFF ausgegeben mit ca. 1-2 Clock Pause zwischen den Bytes.

Jetzt möchte ich mit SPI.transfer gleich ein Inhalt von 102400 * 3 Bytes aufeinmal übertragen, wie stelle ich das am besten an (also ohne mehrmals SPI.transfer aufrufen zu müssen)?

#7390297
Lesenswert?

Falk B. schrieb:

Epi K. schrieb:

Michael H. schrieb:

SPI1.transfer(array,3) (Zeiger auf das Array)

das gibt ein Fehler zurück, siehe Anhang.

Ja logisch. array IST schon ein Zeiger auf das Array! &array ist Unsinn, das wäre ein Zeiger auf den Zeiger auf das Array.

Nein, das ist falsch.

a ist ein Array-Objekt. In den meisten Kontexten zerfällt a zu einem Zeiger, was ein Zeiger auf das erste Element ist.

&a ist ein Zeigr auf das Array-Objekt, was auch ein Zeiger auf das erste Element ist vom Wert her.

Allerdings zerfällt a dann zu char* während &a den Type char(*)[3] hat.

Dies ist hier zwar egal wegen des void* (Polymorphie für Arme ala C), jedoch merkt man das, wenn man (&a + 1) schreibt. Das ist dann ein Zeiger (&a[0] + 3), da das Array-Objekt 3 Bytes groß ist.

Ja, die Zeiger in C sind manchmal etwas verwirrend.

Ganz genau!

#7390310
Lesenswert?

Wilhelm M. schrieb:

Da es sich um C++ handelt, ist es einfach unverständlich, wieso man so eine vermurkste Signatur schreibt.

Besser wäre

struct MbedSPI { template<auto N> void transfer(char (&a)[N]);

1
template<auto N>
2
void transfer(std::array<std::byte, N>& a);

};

(da fehlte das schließende Tag)

1
struct MbedSPI {
2
    template<auto N> void transfer(char (&a)[N]);
3

4
    template<auto N>
5
    void transfer(std::array<std::byte, N>& a);
6
};
#7390312
Lesenswert?

Wilhelm M. schrieb:

a ist ein Array-Objekt. In den meisten Kontexten zerfällt a zu einem Zeiger, was ein Zeiger auf das erste Element ist.

deshalb verstehe ich nicht warum SPI.transfer(array, 3) einen Fehler werfen soll. Nur wenn es zu uint32_t array = 0xFF00FF; geändert wurde. Oder?

Falk B. schrieb:

Doch, die Methode write überträgt auch Arrays. Ein "Experte" hat es geschafft, die überaus sinnvolle Methode zu verkrüppeln, indem die Empfangsdaten die Sendedaten überschreiben. Man hätte schlicht die Methode write DIREKT nutzen sollen! Es leben die 1001 Abstraktionsschichten! void arduino::MbedSPI::transfer(void buf, size_t count) { dev->obj->write((const char)buf, count, (char*)buf, count); }

ja, das wurde hier von Arduino 'vereinfacht'. Das Mbed write ist sehr komplex weil ja auch unterschiedliche Anzahlen für transmitt/receive count möglich sind. Und das Arduino transfer ist das blockierende Mbed write, in Mbed gibt es noch eine asynchrone Methode wenn es das Target unterstützt, und die heißt da dann transfer. Es sollte aber möglich sein auf das komplette Mbed API zuzugreifen und kann dann für receive buffer nullptr übergeben, dann sollte die Targetabhängige Implementierung das Optimieren.

Wenn es um den RP2040 geht, dann ist in dem Fall der core von EarlePhilPower besser. Und man kann die Bodmer/eTFT_SPI Lib nehmen, die nutzt da SPI mit PIO und DMA. Diese Lib unterstützt den offiziellen core leider nicht, die Krux mit den Arduino cores... Arduino hat hier für den RP2040 aber in zwei Jahren nicht viel gemacht und der andere core ist hier beliebter. Hat halt nur nicht die angenehmen Dinge von Mbed. Wenn das Target ein H7 ist, dann geht das SPI noch durch die STM HAL, da ist die asynchrone (Mbed)Transfer Funktion deutlich schneller. Arduino33BLE habe ich nicht benutzt, mit Nordic MCU, k.A. wie es da Implementiert ist.

Die Anzahl 102400 ist auch krumm, ist das wirklich ein 320*320 Display oder welche Größe?

OP #7390317
Lesenswert?

J. S. schrieb:

Wenn es um den RP2040 geht, dann ist in dem Fall der core von EarlePhilPower besser. Und man kann die Bodmer/eTFT_SPI Lib nehmen, die nutzt da SPI mit PIO und DMA. Diese Lib unterstützt den offiziellen core leider nicht, die Krux mit den Arduino cores... Arduino hat hier für den RP2040 aber in zwei Jahren nicht viel gemacht und der andere core ist hier beliebter. Hat halt nur nicht die angenehmen Dinge von Mbed. Wenn das Target ein H7 ist, dann geht das SPI noch durch die STM HAL, da ist die asynchrone (Mbed)Transfer Funktion deutlich schneller. Arduino33BLE habe ich nicht benutzt, mit Nordic MCU, k.A. wie es da Implementiert ist.

Die Anzahl 102400 ist auch krumm, ist das wirklich ein 320*320 Display oder welche Größe?

RP2040, und 320*320, jap. Mit EarlePhilPower Core (und platformio / VS) kriege ich leider ein Fehler.

Ich versuche gerade folgendes, leider ohne Erfolg. Beim ausgeklammerten funktioniert es, aber beim Zweiten nicht, wieso?? Ziel ist nur alles mit einem SPI.transfer übertragen zu können.

1
void fillDisplay(uint32_t a){
2
/*
3
      for(uint32_t i=0; i<102400;i++){
4
        uint8_t array[3]={a>>16,a>>8,a};
5
        //uint32_t array = a;
6
        SPI1.transfer(array,3);
7
      }
8
*/
9
    uint8_t array[102400*3];
10
    for (uint32_t i = 0; i < (102400*3); i=(i+1)*3) {
11
        array[i] = a>>16;
12
        array[i+1] = a>>8;
13
        array[i+2] = a;
14
    }
15
    SPI1.transfer(array,102400*3);
16
}
#7390320
Lesenswert?

Epi K. schrieb:

Ich versuche gerade folgendes, leider ohne Erfolg. Beim ausgeklammerten funktioniert es, aber beim Zweiten nicht, wieso?? Ziel ist nur alles mit einem SPI.transfer übertragen zu können.

Was soll denn der Unsinn? Ich mein dein Ziel ist zwar richtig, der Weg aber aus Holz!

Deine Funktion benötig ein 300kB lokales Array! Das hat dein RSP2040 nicht! Und für ein konstantes Füllmuster ist das Unfug. Bestenfalls für eine Zeile!

OP #7390324
Lesenswert?

Falk B. schrieb:

Deine Funktion benötig ein 300kB lokales Array! Das hat dein RSP2040 nicht! Und für ein konstantes Füllmuster ist das Unfug. Bestenfalls für eine Zeile!

nur zum Testen, stimmt, dass mit den 300kB ist gerade zuviel. Aber leider geht es auch nicht mit 51200*3 Bytes..., und dass der RAM voll wäre meldet mir der Compiler nicht.

#7390328
Lesenswert?

1
void fillDisplay(uint8_t a){
2
    uint32_t line[240];   // 320*3=480*2=240*4
3
    uint32_t pattern;
4

5
    pattern = a<<24 | a<<16 | a<<8 | 8;
6
    for (uint32_t i = 0; i < 320; i++) {
7
        for (int j=0; j<240; j++) line[j]=pattern;
8
        SPI1.transfer(line, 320*3);
9
    }
10
}

Und ein Array mit den Namen array ist doof.

#7390330
Lesenswert?

Epi K. schrieb:

nur zum Testen, stimmt, dass mit den 300kB ist gerade zuviel. Aber leider geht es auch nicht mit 51200*3 Bytes..., und dass der RAM voll wäre meldet mir der Compiler nicht.

Weil so ein Compiler nicht hellsehen kann und Programmieren nicht idiotensicher ist. Stackverbrauch kann der Compiler im Normalfall nicht anzeigen. Es gibt einige Ausnahmen, die das können.

#7390345
Lesenswert?

Naja, eigentlich will man ja beim Füllen eine konstante Farbe haben und nicht für RGB immer den gleichen Wert, was ja nur Grauwerte ergibt. Also eher so.

1
void fillDisplay(uint32_t color) {  // RGB
2
    uint32_t line[240];   // 320*3=240*4
3
    uint32_t p1, p2, p3;
4

5
    p1 = (color <<  8) | (color >> 16);
6
    p2 = (color << 16) | (color >>  8);
7
    p3 = (color << 24) |  color;
8
    
9
    for (uint32_t i = 0; i < 320; i++) {
10
        for (int j=0; j < 240; j+= 3) {
11
            line[j]=p1
12
            line[j+1]=p2;
13
            line[j+2]=p3;
14
        }
15
        SPI1.transfer(line, 320*3);
16
    }
17
}
OP #7390361
Lesenswert?

Falk B. schrieb:

Naja, eigentlich will man ja beim Füllen eine konstante Farbe haben und nicht für RGB immer den gleichen Wert, was ja nur Grauwerte ergibt. Also eher so.

ich habe es jetzt so gelöst (array global):

1
  for(uint8_t i = 0; i < 2; i++){
2
    for (uint32_t i = 0; i < (51200); i++) {
3
        array[i*3] = a>>16;
4
        array[i*3+1] = a>>8;
5
        array[i*3+2] = a;
6
    }
7
    SPI1.transfer(array,3*51200);
8
  }

Falk B. schrieb:

Was soll denn der Unsinn? Ich mein dein Ziel ist zwar richtig, der Weg aber aus Holz!

Aber ja, zwischen jedem Byte verliert die SPI1.transfer Funktion noch 1-2 Clock an Zeit... deshalb ist es wohl wirklich Unsinn :-( ...

#7390382
Lesenswert?

Falk B. schrieb:

Naja, eigentlich will man ja beim Füllen eine konstante Farbe haben und nicht für RGB immer den gleichen Wert, was ja nur Grauwerte ergibt. Also eher so.

1
> void fillDisplay(uint32_t color) {  // RGB
2
>     uint32_t line[240];   // 320*3=240*4
3
>     uint32_t p1, p2, p3;
4
> 
5
>     p1 = (color <<  8) | (color >> 16);
6
>     p2 = (color << 16) | (color >>  8);
7
>     p3 = (color << 24) |  color;
8
> 
9
>     for (uint32_t i = 0; i < 320; i++) {
10
>         for (int j=0; j < 240; j+= 3) {
11
>             line[j]=p1
12
>             line[j+1]=p2;
13
>             line[j+2]=p3;
14
>         }
15
>         SPI1.transfer(line, 320*3);
16
>     }
17
> }
18
>

Das Array line mit einem repetitiven Pattern vollzuschreiben, macht eigentlich auch keinen Sinn. Da das ganze in C++ realisiert ist, könnte man bis zur Treiber-Ebene mit Containern arbeiten. Für diesen speziellen Fall würde man einen "Folding-Container" benutzen, der ein Wertemuster wiederholt an bestimmten Indexpositionen sichtbar macht. Dann bräuchte man bei intelligenter Auslegung nur 3 Byte RAM. Da das ganze Interface aber eigentlich ein C-Interface ist (s.a. Anmerkungen oben), geht das natürlich nicht so einfach. Zudem ist das RAM ja da, und ungenutzter Speicher bringt kein Geld zurück ;-)

OP #7390384
Lesenswert?

J. S. schrieb:

Das sieht auch nach 20 MHz SPI clock aus, mit der genannten Lib kommt man auf 62,5 MHz und DMA. Das macht sich beim Bildaufbau schon bemerkbar. Allerdings wird da auf das Ende des DMA gewartet, auch da gibt es noch Optimierungsmöglichkeiten.

welche Lib meinst du jetzt? TFT_eSPI ?

Wie kriege ich ohne GUI Library eine SPI.transfer Funktion hin, die kein 1-2 Clock zwischen jedem Byte verliert?

Ich suche quasi nur ein Code (Bare-Metal?) für die SPI denn ich in meinem "Arduino-Code" integrieren könnte... (RP2040)

Zahle gerne was dafür...

#7390392
Lesenswert?

Wilhelm M. schrieb:

Das Array line mit einem repetitiven Pattern vollzuschreiben, macht eigentlich auch keinen Sinn.

Es ist ein Workaraund. Man kann ja auch die dämliche transfer-Methode aufräumen.

Da das ganze in C++ realisiert ist, könnte man bis zur Treiber-Ebene mit Containern arbeiten. Für diesen speziellen Fall würde man einen "Folding-Container" benutzen, der ein Wertemuster wiederholt an bestimmten Indexpositionen sichtbar macht. Dann bräuchte man bei intelligenter Auslegung nur 3 Byte RAM.

Und der ist schneller als die Variante oben? Da habe ich meine Zweifel.

Da das ganze Interface aber eigentlich ein C-Interface ist (s.a. Anmerkungen oben), geht das natürlich nicht so einfach. Zudem ist das RAM ja da, und ungenutzter Speicher bringt kein Geld zurück ;-)

Die 960 BYTE kann ein RP2040 wohl verschmerzen, zumal lokal auf dem Stack.

#7390396
Lesenswert?

Epi K. schrieb:

Wie kriege ich ohne GUI Library eine SPI.transfer Funktion hin, die kein 1-2 Clock zwischen jedem Byte verliert?

Die normale SPI-Lib kann das.

https://www.arduino.cc/reference/en/language/functions/communication/spi/transfer/

Ohhh, dort steck der gleiche Mist dahinter! Wer produziert so einen Käse?

Nimm die Variante mit der einen Zeile von mir, das ist ein sehr guter Kompromiss aus Geschwindigkeit und RAM-Bedarf.

#7390401
Lesenswert?

So einen Buffer (oder zwei) kann man sehr gut auch für die Zeichenfunktionen gebrauchen, siehe lvgl. Das RAM kann die CPU in nullkommanix füllen, jeden SPI Transfer einzeln starten und abwarten dauert ewig. Und wenn man dann noch DMA hat und den nächsten Buffer vorbereiten kann, dann macht es richtig Sinn. Das SPI aus der eTFT Lib ist mit den EarlePhilPower Core verwurschtelt, ich habe lvgl auf dem Pico daher auch erstmal mit diesem Core angefangen.

oder probieren das Mbed SPI direkt zu benutzen:

1
mbed::SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK);
2

3
...
4
   spi.frequency(30'000'000);
5
   spi.write((const char*) array, n, nullptr, 0);

kann es jetzt nur nicht testen.

OP #7390417
Lesenswert?

Falk B. schrieb:

Nimm die Variante mit der einen Zeile von mir, das ist ein sehr guter Kompromiss aus Geschwindigkeit und RAM-Bedarf.

nee, ich weiss nicht ob du mich verstehst,

das Problem ist siehe Anhang, diese Pause (roter Pfeil, 96ns, bzw. 1-2 Clocks), die muss ich weghaben, dann wäre die SPI.transfer perfekt...

Ich weiss jetzt wie ich mit einem einzigen SPI.transfer Aufruf fast den ganzen Displayinhalt beschreiben kann, jedoch ist das ganze eben noch zu langsam, wegen dieser verflixten Pause im SPI.transfer...

Wie löse ich dies - ohne verwurschtelte Bibliotheken wo ich nicht weiss wo jetzt was ist...

Angehängte Dateien:
#7390464
Lesenswert?

Epi K. schrieb:

Falk B. schrieb:

Nimm die Variante mit der einen Zeile von mir, das ist ein sehr guter Kompromiss aus Geschwindigkeit und RAM-Bedarf.

nee, ich weiss nicht ob du mich verstehst,

das Problem ist siehe Anhang, diese Pause (roter Pfeil, 96ns, bzw. 1-2 Clocks), die muss ich weghaben, dann wäre die SPI.transfer perfekt...

Ja und? Gewinnst du dann einen Preis? Ist dann dein Leben lebenswert? Vermutlich entsteht die Pause zwischen den einzelnen Bytes durch das nicht sonderlich gute Nachladen des nächsten Werts. Das wird vermutlich per CPU und einer Warteschleife gemacht, die halt erstmal erkennen muss, daß das aktuelle Byte komplett übertragen wurde und dann erst das nächste in das SPI-Register schreiben kann. Und da das SPI vermutlich keine Doppelpufferung hat, entsteht halt die Lücke. Mein Gott, dadurch läuft dein SPI anstatt mit vollen 20 MHz effektiv nur mit ca. 16MHz. D.h. das Schreiben des LCDs mit 307200 Bytes dauert statt 123ms eben 147ms. Das macht das Kraut nicht fett.

Ich weiss jetzt wie ich mit einem einzigen SPI.transfer Aufruf fast den ganzen Displayinhalt beschreiben kann, jedoch ist das ganze eben noch zu langsam, wegen dieser verflixten Pause im SPI.transfer...

Was ist denn zu langsam? Wieviele Jahre Pause liegen denn dazwischen? Kann es sein, daß du die Verhältnisse LEICHT verkennst?

Wie löse ich dies - ohne verwurschtelte Bibliotheken wo ich nicht weiss wo jetzt was ist...

Das schrieb ich bereits, du verstehst es leider nicht.

#7390523
Lesenswert?

Das ist ja der ArduinoMbed core, da sind beide APIs drin. SPI ist die Klasse aus Mbed, die Arduino Funktionen greifen darauf zu, Mbed hat viel mehr Funktionalität als das Arduino API. Das Arduino API ist einem eigenen Repo wo nur das API drin ist, da ist die SPIClass per define gleich HardwareSPI gesetzt.

https://github.com/arduino/ArduinoCore-API/blob/master/api/HardwareSPI.h

OP #7390789
Lesenswert?

J. S. schrieb:

1
> #include <mbed.h>
2
> 
3
> using namespace mbed;
4
>

Braucht es noch.

spi.write(0xff0f) gibt nur 0x0f raus, und bei spi.write mit array geht gar nichts (kein clock).

1
    for (uint32_t i = 0; i < (51200); i++) {
2
        array[i*3] = a>>16;
3
        array[i*3+1] = a>>8;
4
        array[i*3+2] = a;
5
    }
6
    //SPI1.transfer(array,3*51200);
7
    //spi.unlock();
8
    spi.frequency(30'000'000);
9
    //spi.write((const char*)array,sizeof(array), nullptr, 0);
10
    spi.write(0xff0f);
#7390880
Lesenswert?

Das scheint an der halbherzigen Umsetzung des Mbed API zu liegen. Transfer benutzt spi_write_read_blocking vom Pico SDK, und das erwartet einen gültigen Pointer für den receive Buffer. Transfer müsste das prüfen und entsprechend read oder write only aufrufen, das gibt es im SDK. Also Core patchen und PR einreichen, aber Arduino sieht da träge aus. Oder gucken wie man an das spi Handle kommt und eine Subclass vom SPI bauen. Ich gucke nachher mal, wollte auch mal meine picoProbe ausprobieren.

Und dann wäre da noch die Frage ob man den 2. core nutzen kann.

#7390961
Lesenswert?

Falk B. schrieb:

Waren die Entwickler zu faul oder doof?

Das würde ich denen nicht unterstellen. Ilitek hat viele Controller, dieser hat SPI, aber eben nur 1 oder 6 Bit pro Farbkanal. Dafür kann er noch MIPI, 8/9/16/18/24 Bit parallel, das ist doch schon was. Wenn man kann, dann sucht man sich einen Controller mit passendem Interface aus. Es gibt aber nun mal günstige fertige Kombis von Displays und Controller, da muss man mit den Schnittstellen leben die man angeboten bekommt.

OP #7391010
Lesenswert?

J. S. schrieb:

Über 8 Bit parallel kann der RGB565, das wäre noch schneller wenn es so angeklemmt werden kann.

jäjo, aber mit Arduino-Code, da müsste ich irgendwie direkt die Port / GPIO -Register des RP2040 ansprechen können (und viel im Datenblatt herumstudieren), und ob das so einfach geht wie für ein 8bit MCU a la Attiny oder so (DDRB = 255; PORTB &= ~(1<<PB0);) - wage ich zu bezweifeln. Und die TFT_eSPI Library ist mir zu unübersichtlich um es nur hierfür zu verwenden.

Tja und cool wäre wenn man auch SPI DMA mit paar Register setzen aktivieren könnte :D ..., ich denke da gibt es noch kein Beispiel-Code?

also zahlen würde ich gerne für solchen Code..

#7391226
Lesenswert?

habe das auch mal ausprobiert:

Das spi.write((const char*)array,sizeof(array), nullptr, 0); läuft in assert rx_len != tx_len und stoppt dann mit hardfault. Um das zu sauber zu umgehen muss man die libFrameworkArduino.a neu bauen, aufwändig...

Ein 10 kB Buffer kann mit der vorhandenen transfer Funktion gesendet und gelesen werden (überschreibt den Buffer dann) und das dauert dann 3,705 ms @ 62,5 MHz.

Quick and dirty kann man aber das write aus dem SDK aufrufen mit

1
uint8_t test_buffer[10*1024];
2

3
mbed::SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK);
4

5
spi.frequency(62'500'000);
6
spi_write_blocking(spi0, (const uint8_t*) test_buffer, sizeof(test_buffer)); // Pico SDK

damit werden die 10 kB in 1,562 ms rausgehauen, also mehr also doppelt so schnell, das lohnt sich dann also schon.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren