Die 1. Nachricht ("Write") sieht perfekt aus, bei der 2. Nachricht
vergisst der ESP8266 aber einfach die restlichen 3 Bytes und schickt nur
das 1. Byte korrekt - warum?
Ich gönne ihm sogar extra 25ms Pause :(
P.S: Wenn ich die 1. Nachricht (also "Write") weglasse, dann wird die
"Read"-Nachricht korrekt ausgegeben.
Erstens
Max M. schrieb:> void send_spi(uint8_t *data, uint8_t length) {> spi_trans_t trans;
Damit legst du das Objekt bzw. die Struktur trans funktionslokal an. Es
liegt auf dem Stack. Wer garantiert dir nach Ende der Funktion, dass es
noch da ist?
Zweitens
Max M. schrieb:> trans.mosi = data;
Der Datenzeiger muss für die gesamte Zeit, bis alle Daten raus gepustet
wurden, valide sein. Ist er das auch?
Drittens
Max M. schrieb:> Ich gönne ihm sogar extra 25ms Pause
Warten ist keine Synchronisationsmethode
mfg mf
minifloat schrieb:> Wer garantiert dir nach Ende der Funktion, dass es> noch da ist?
Wenn ich wüsste, wann das Übertragen abgeschlossen ist, könnte ich dir
das sagen.
minifloat schrieb:> Ist er das auch?
Gleiche Aussage wie oben. Ich hab keine Infos dazu gefunden, wann die
SPI-Daten tatsächlich übertragen werden.
Danke für den Hinweis, ich werde die Variablen mal innerhalb der
Task-Funktion global machen.
Edit: Leider nein, gleiches Verhalten (außer ich hab was übersehen):
minifloat schrieb:> Warten ist keine Synchronisationsmethode
Korrekt. Bei der wenigen Dokumentation, die es zum RTOS gibt, hab ich
leider noch nicht rausgefunden, wie man einen SPI-Receive oder
SPI-Transmit Interrupt implementiert.
Max M. schrieb:> ich werde die Variablen mal innerhalb der Task-Funktion global machen
Mach' die mal in der C-Source überhaupt global. Oderststic. Weil wenn
das Ding auf dem Stack liegt, die Übertragung aber vermutlich
Interrupt-getrieben und nicht synchron zum Tasksystem, muss das Ding
dauerhaft existent sein.
Max M. schrieb:> Wenn ich wüsste, wann das Übertragen abgeschlossen ist,
Du hast dort eine wunderschöne Statemachine. Gibt es für das spi_trans_t
Objekt keine IsReady-Funktion?
Gibt es Doku zu dem SPI-Krempel?
mfg mf
Max M. schrieb:> Nun sind beide Nachrichten falsch
Soll ich ehrlich sein, das hatte ich bereits erwartet...
spi_trans() scheint für den Usercode kein "blockierender"Syscall zu
sein. D.h. der Task wird nicht suspended, wenn SPI noch am Werk ist.
Max M. schrieb:> minifloat schrieb:>> Gibt es Doku zu dem SPI-Krempel?>> https://docs.espressif.com/projects/esp8266-rtos-sdk/en/latest/api-reference/peripherals/spi.html
Schön. Hab' mal kurz durchgesehen. Es gibt offenbar keinen global
abrufbaren Status der beiden SPIs. Spinlocks sind auch nicht das beste
Mittel...
Aaaaaber. Du kannst einen Event-Callback anlegen. Das geht wohl im
spi_config_t-Objekt:
>> spi_event_callback_t event_cb>> SPI interrupt event callback
Und der Typ...
>> typedef void (*spi_event_callback_t)>> (int event, void *arg)
...stellt in Aussicht, bei "int event" z.B. einen dieser hier zu
bekommen...
>> SPI_TRANS_DONE_EVENT>> SPI_TRANS_DONE
Also
a) Thread nach spi_trans() schlafen legen und mit dem Callback bei
Erfolg wieder Aufwecken
b) Eigenen globalen Status mit dem Callback steuern.
mfg mf
minifloat schrieb:> Du kannst einen Event-Callback anlegen. Das geht wohl im> spi_config_t-Objekt:
Danke für den Hinweis, ich habs nun mal versucht zu implementieren:
Vielleicht sollte Espressif lieber mehr Doku schreiben anstatt auf
GitHub jeden Tag Commits rauszuhauen.
Edit: Sobald ich alle *printf()*-Ausgaben aus der spi_callback
entferne, stürzt der ESP8266 nicht mehr ab (öhm alles klar!), allerdings
wird der Task trotzdem nicht wieder aufgenommen (eventuell da das Event
nie aufgerufen wird) - die 1. Nachricht läuft aber richtig über das SPI.
Was machst du denn da?
Max M. schrieb:> const uint8_t resp = read_spi();
Weil das passt irgendwie nicht so recht zu deiner Deklaration...
Max M. schrieb:> void read_spi(spi_trans_t *trans, uint8_t *dest)
...als void-Funktion und dann willst du einen const uint8_t beschreiben.
Würde irgendwie zu...
Max M. schrieb:> Guru Meditation Error: Core 0 panic'ed (StoreProhibited).
...passen.
Ich geh pennen.
mfg mf
Max M. schrieb:> Edit: Sobald ich alle *printf()*-Ausgaben aus der spi_callback entferne,> stürzt der ESP8266 nicht mehr ab (öhm alles klar!),
Aha. Ist printf vielleicht ein blocking system call (Gift im IRQ)
und/oder schreibt irgendwo wo er nicht soll?
Ich hab da eine Frage für Dumme.
Warum heißt es xTaskCreate
Aber vTaskSuspend und vTaskResume?
btw. Ich hab' keine Erfahrung mit FreeRTOS, alles nur höngengebliebenes
Wissen aus dem Studium, was man eben über RTOS so vor 5-7 Jahren gelehrt
hat.
Aus dem Callback heraus sollte es aber möglich sein, Tasks aufzuwecken.
Wäre ja sonst sinnlos.
Letzte Idee des Abends
Dass es ein SPI-Init-Event gibt, sagt mir dass du vielleicht vor dem SPI
losballern erst auf dieses SPI-Init-Event warten solltest. Damit würde
ich dann den Task zum losballern starten.
n8 mfg mf
minifloat schrieb:> Was machst du denn da?
Ups, da hab ich zwei Codestände durcheinander geschmissen.
minifloat schrieb:> Dass es ein SPI-Init-Event gibt, sagt mir dass du vielleicht vor dem SPI> losballern erst auf dieses SPI-Init-Event warten solltest. Damit würde> ich dann den Task zum losballern starten.
Hab ich nun probiert:
Wenn ich anstatt des *Suspend()* und *Resume()* das Mutex verwende,
funktioniert es (sobald ich xSemaphoreGive aus dem Callback entferne,
bleibt der Controller stehen, daher gehe ich davon aus, dass die richtig
funktionieren) im Sinne von "der Controller wartet, bis das SPI-Packet
erfolgreich übertragen wurde". Bei Resume() und Suspend() bleibt der
Controller nach dem 1. Suspend stehen.
Im Sinne des eigentlichen Zieles, dass 2 SPI-Pakete richtig übertragen
werden, ist das Verhalten genauso wie im 1. Beitrag beschrieben.
minifloat schrieb:> Wäre ja sonst sinnlos.
:(
minifloat schrieb:> Ich hab da eine Frage für Dumme.> Warum heißt es xTaskCreate> Aber vTaskSuspend und vTaskResume?>> btw. Ich hab' keine Erfahrung mit FreeRTOS, alles nur höngengebliebenes> Wissen aus dem Studium, was man eben über RTOS so vor 5-7 Jahren gelehrt> hat.
Dann solltest du die aktuelle Doku untersuchen!
Dann stellst du schnell fest, dass die vFunktionen als void definiert
sind, und die xFunktionen Daten zurückgeben.
Gibt och mehr solcher Präfixe
Arduino Fanboy D. schrieb:> Dann solltest du die aktuelle Doku untersuchen!
Ich bin nicht der TO. Werde mir daher nicht die Doku rein ziehen, erst
wenn ich es benötige.
Warum leistest du angesichts deiner FreeRTOS-Allmacht keinen fachlich
wertvollen Beitrag?
Arduino Fanboy D. schrieb:> Gibt och mehr solcher Präfixe
Es gibt Leute die...
1 die Ungarische Notation verstehen und sinnvoll einsetzen
2 die Ungarische Notation für outdated halten, da Dank moderner IDEs
obsolet
3 die Ungarische Notation mit Umgekehrter Polnischer Notation
verwechseln und dich bei Diskussionen dann wie ein Auto anschauen
4 die Ungarische Notation nicht verstehen und sie falsch einsetzen
5 die Ungarische Notation durch etwas anderes ersetzen, was auch nicht
sinnvoll ist
mfg mf
minifloat schrieb:> Ich bin nicht der TO. Werde mir daher nicht die Doku rein ziehen, erst> wenn ich es benötige.> Warum leistest du angesichts deiner FreeRTOS-Allmacht keinen fachlich> wertvollen Beitrag?
Warum fragst du dann?
Nur um hinterher rum maulen zu können?
Ich wünsche dir:
Frohe Weihnachten.
Und ein schönes neues Jahr, gerne auch ohne solche Ausfälle.
Arduino Fanboy D. schrieb:> Nur um hinterher rum maulen zu können?
Was ist mit dir falsch?
Ich sehe das aktuelle Problem nicht bei FreeRTOS sondern eher den
System-Funktionen vom ESP8266 die schlecht / gar nicht dokumentiert
sind.
Max M. schrieb:> Nun sind beide Nachrichten falsch
Doofe Frage am Rande: Welchen Logicanalyzer und Software nutzt du? Ist
ähnlich zu Saleae, aber anderes Design. (Wollte mich noch nachträglich
beschenken.)
Arduino Fanboy D. schrieb:> [...] gerne auch ohne solche Ausfälle.
Danke, die Ausfälle gebe ich gern zurück.
Wieder kein fachlich sinnvoller Beitrag von dir.
BTT.
Max, dein Mutex würde den Thread erst pausieren, wenn der Callback
bereits ausgeführt ist. So rennt der Thread weiter und nimmt sich die
Semaphore.
Ein hässliches Konstrukt mit...
> Thread> ...> SemaTake> SemaTake*> SemaGive> ...> Callback> ...> SemaGive> ...
...kam mir gerade in den Sinn. Der Thread würde beim zweiten SemTake (*)
stoppen, der Callback gibt wieder frei wenn er soweit ist, Thread nimmt
erfolgreich und gibt wieder ab.
Hast du schon andere weitere Erkenntnisse, Max?
minifloat schrieb:> So rennt der Thread weiter und nimmt sich die> Semaphore.
Stimmt, mein Fehler.
Meinst du, das macht so mehr Sinn?
Fragender schrieb:> Welchen Logicanalyzer und Software nutzt du? Ist> ähnlich zu Saleae, aber anderes Design. (Wollte mich noch nachträglich> beschenken.)
Ist ein Saleae-Klon aus China mit offizieller Software.
Max M. schrieb:> Meinst du, das macht so mehr Sinn?
Beim Zweiten Durchlauf des Tasks wird an der Semaphore gehalten. Das ist
u.U. nicht sofort ersichtlich. Insgesamt aber logisch und sauberer als
mein Vorschlag. Die Struktur "trans" und die Ressource
"SPI-Schnittstelle" musste ja geschützt werden. Wenn die SPI noch busy
ist, was du ja im Callback siehst, hat der Task nix dran rum zu fummeln.
mfg mf
Ich hab inzwischen ein wenig weiter entwickelt. Man soll nun per
GET-Aufruf auf die Seite "/set?addr=5423&value=123" einen SPI-Transmit
mit eben den verwendeten Werten absetzen können (ebenso mit
"get?addr=5423").
Da ich nun noch das ganze HTTP-Handler Zeug in der main.c hab, dachte
ich mir der Übersicht halber, das SPI-Handling in eine extra Struktur
auszulagern:
29 StoreProhibited: CPU tried to store memory from a region which is protected against writes
https://github.com/SuperHouse/esp-open-rtos/wiki/Crash-Dumps
Aha...
Ich hab leider keine Ahnung warum, sobald ich den Funktionsaufruf
spi_trans rausnehme, stürzt er nicht mehr ab (aber es passiert
natürlich auch nichts).
Ich weiß nicht, ob das wieder so ein "printf"-Ding ist wie beim
SPI-Callback oder ob die interne WLAN-Routine zu wenig Rechenzeit
bekommt und deswegen der Watchdog den ESP killt (kann ich mir ja fast
nicht vorstellen, so viel passiert nun auch nicht). Den RTOS-Task hab
ich auch rausgeschmissen, so wirklich viel Sinn hatte der nicht wenn das
eh alles asynchron läuft mit dem SPI-Event. War das ein Fehler? Muss so
ein Task existieren damit das RTOS vernünftig läuft?
Allgemein bin ich nicht so zufrieden, selbst wenn die aktuelle Lösung
funktionieren würde, da die Antwort asynchron zum HTTP-GET Aufruf
passiert.
Aktuell liefere ich nach dem Aufruf "Reading..." als HTTP-Response-Body
zurück. Meine Idee wäre dann, sobald das Byte per SPI gelesen ist (krieg
ich ja mit über das spi-event:
)
eine weitere HTTP-Antwort zu schicken. Aber da hab ich dann gar keinen
Aufruf also kein *httpd_req_t*-Pointer den die Funktion
httpd_resp_send zur Antwort benötigt - wie mache ich das denn nun?
Unglücklicherweise ist der *http_req_t*-Pointer "verbraucht" sobald man
eine Antwort gesendet hat (laut einem Kommentar im Espressif-GitHub-Repo
Example). Alternativ könnte ich gar keine Antwort schicken und mir den
Pointer merken bis die Antwort da ist...?
Edit: Ich hab probeweise das
1
spi_trans(HSPI_HOST,&self->spi_t);/* ESP stürzt ab */
mit
1
while(true);
ersetzt, und der ESP stürzt erst nach ein paar Sekunden mit diesmal
einer anderen Fehlermeldung ab:
1
Task watchdog got triggered.
2
3
4
5
Guru Meditation Error: Core 0 panic'ed (unknown). Exception was unhandled.
Max M. schrieb:> Den RTOS-Task hab ich auch rausgeschmissen, so wirklich viel Sinn hatte> der nicht wenn das eh alles asynchron läuft mit dem SPI-Event.
Warum, das hat doch super funktioniert?
Kannst du einen Task erstellen, der zyklisch liest (nicht erst auf
Anfrage) und einen, der sich um HTTP kümmert?
Datenübergabe dann über eine Struktur, die per Mutex geschützt wird.
Oder geht es dir darum, per HTTP-get und -post gezielt SPI-Transfers
auszulösen?
Ich hab mit dem HTTP-gedöns auch schon ein wenig herumgespielt,
allerdings ohne RTOS. Dabei ist es von Vorteil gewesen, Daten für HTTP
(die "Internetseite") dauerhaft bereit zu halten. Datenaustausch erfolgt
über hin-und-her gehende xml-Objekte. Auf der "Internetseite" läuft
Javascript, um zyklisch oder auf Trigger hin Daten auszutauschen.
mfg mf
minifloat schrieb:> Warum, das hat doch super funktioniert?
Ich hatte alles in der main.c und das sah furchtbar aus. Deswegen hab
ich versucht, den SPI-Teil in eben jene extra Struktur auszulagern.
minifloat schrieb:> Oder geht es dir darum, per HTTP-get und -post gezielt SPI-Transfers> auszulösen?
Ja genau, man soll mit dem Aufrufen von "/get?addr=xxxxxx" einen
SPI-Transmit (READ_CMD) auslösen können. Diese Nachricht geht an einen
ATmega, der dann entsprechend eine Antwort zurückliefert (natürlich
leider asynchron, d.h. ich weiß (noch) nicht wie ich das mit der Antwort
dann mache).
Genau das gleiche soll mit "/set?addr=xxxxxx&value=xxx" passieren, nur
eben WRITE_CMD (und es gibt keine Antwort vom ATmega).
minifloat schrieb:> Kannst du einen Task erstellen, der zyklisch liest (nicht erst auf> Anfrage) und einen, der sich um HTTP kümmert?
Sowas fände ich sehr schön, ich hab aus 2017 ein Codebeispiel gefunden
(das hat nicht funktioniert), die Beispiele vom Espressif-GitHub-Repo
nutzen HTTP-Request-Handler.
Man legt so eine Struktur an:
printf("Starting server on port: '%d'\n",config.server_port);
7
if(httpd_start(&server,&config)==ESP_OK){
8
printf("Registering URI handlers\n");
9
httpd_register_uri_handler(server,&get);
10
httpd_register_uri_handler(server,&set);
11
12
returnserver;
13
}
14
15
printf("Error starting server!\n");
16
returnNULL;
17
}
und hat einen Handler wie im letzten Beitrag schon gezeigt. D.h. ich
wüsste nicht, wie ich da einen Task drum rum basteln soll.
Sobald ich die mosi/miso-Arrays, das Anlagen der *spi_trans_t*-Struktur
wieder nach *main.c*-Verschiebe und spi_trans auch nur dort aufrufe,
stürzt der ESP nicht mehr ab. Ich weiß nicht, ob sowas möglich wäre,
aber kann es sein, dass es der ESP einfach nicht mag wenn man in
externen C-Files die Strukturen für Systemfunktionen anlegt
minifloat schrieb:> Dabei ist es von Vorteil gewesen, Daten für HTTP> (die "Internetseite") dauerhaft bereit zu halten.
Ist bestimmt eine gute Idee, aber ich kann nicht vorhersagen, was der
Benutzer für eine Adresse und Wert eingibt.
minifloat schrieb:> Leider bin ich bis morgen Abend außer Haus, sonst hätte ich dir> entsprechende Codeschnipsel Posten können...
Mach dir kein Kopf, reicht schon wenn sich einer damit rumärgern müss :/
minifloat schrieb:> Auf der "Internetseite" läuft> Javascript, um zyklisch oder auf Trigger hin Daten auszutauschen.
So hatte ich mir das auch gedacht aber ich hab keine Ahnung wie die
Kommunikation zwischen JavaScript und ESP abläuft.
Max M. schrieb:> wie ich da einen Task drum rum basteln soll.
Ein Task, der zyklisch die Daten vom SPI in einen Puffer schaufelt. Die
Get-Handler in der httpd_uri_t Struktur sind auch wieder so lustige
Callbacks.
Max M. schrieb:> minifloat schrieb:>> Auf der "Internetseite" läuft>> Javascript, um zyklisch oder auf Trigger hin Daten auszutauschen.>> So hatte ich mir das auch gedacht aber ich hab keine Ahnung wie die> Kommunikation zwischen JavaScript und ESP abläuft.
Da kann ich dir morgen Abend paar Schnipsel Javascript dazu posten. Wie
gesagt ein XML-Objekt je Kommunikationsrichtung.
mfg mf
PS. Ich denke, man kann den HTTP-Get-Callback sicher mit einem Mutex
kurz pausieren. Da scheint die SPI-Mimik bereits nicht mehr kurz genug.
Was hältst du von folgendem:
1) HTTP-get eine Seite mit Bedienoberfläche und Javascript. Dieses
Javascript macht dann:
2) HTTP-post ein XML mit den abzusetzenden SPI-Daten(Mosi).
3) HTTP-get ein XML mit
a) BinNochNichtFertig
b) BinFertig mit empfangenen SPI-Daten(Miso)
Es empfiehlt sich, die Mosi-Daten und die BinNochNichtFertig- bzw.
BinFertig-XMLs mit einer ID zu versehen, sonst ist die Synchronisation
der Benutzeroberfläche Gulasch.
Wenn nochmal Mosi-XML eintrudeln, ohne dass Miso-XML abgeholt wurde,
wird Miso einfach gelöscht und ein neuer Transferzyklus kann beginnen.
mfg mf
minifloat schrieb:> Da kann ich dir morgen Abend paar Schnipsel Javascript dazu posten.
Habe herade gesehen, dass ich das Ding "mit Arduino" erstellt habe.
Leider nur eine unfertige Version. Das Konzept sollte aber klar sein.
Dann muss ich mich bei dir entschuldigen, das hier...
minifloat schrieb:> Wie gesagt ein XML-Objekt je Kommunikationsrichtung.
...stimmt bei meiner Umsetzung nicht.
Ich lade die HTML-Seite, diese lädt sich ein XML. Im XML befinden sich
die Daten, die von den Bedienelementen dargestellt werden
sollen(ESP8266->Webseite). Kommandos (Webseite->ESP8266) werden über den
requeststring des XMLs ausgetauscht.
mfg mf
Vielen Dank für deine Hilfe. Das sieht gut aus, dann weiß ich ungefähr
wie ich das machen muss.
Max M. schrieb:> Das kann doch nicht sein? ???
Hab das Problem inzwischen tatsächlich mit zwei Queues gelöst bekommen,
die sich mit dem FreeRTOS sehr komfortabel erstellen lassen.
Ich hatte da ein paar Pointer / Struktur-Fehler die beim ESP für sehr
seltsames Verhalten gesorgt haben (aber keine Absturzmeldung) weswegen
ich darauf erstmal nicht aufmerksam geworden bin.
Falls es jemanden nützt, so sieht nun der Event-Handler des Webservers
aus:
printf("Substate 'WROTE_READ_CMD' for READ-REQUEST, setting up SPI buffer...");
41
spiHandler.spi_t.bits.mosi=0;
42
spiHandler.spi_t.bits.miso=8;
43
spiHandler.spi_t.miso=recv_data;
44
printf("done. Transmitting...");
45
spi_trans(HSPI_HOST,&spiHandler.spi_t);
46
printf("done\n");
47
spiHandler.state=SENT_READ_CLK;
48
}break;
49
caseSENT_READ_CLK:{
50
printf("Substate 'SENT_READ_CLK' for READ-Request, received response\n");
51
resp.addr=req.addr;
52
resp.data=recv_data[0];
53
spiHandler.state=READY;
54
printf("Push result into queue...");
55
xQueueSend(spiHandler.toWebserverQueue,&resp,0);
56
printf("done\n");
57
xSemaphoreGive(spiHandler.mutex);
58
}break;
59
caseWROTE_WRITE_CMD:{
60
xQueueSend(spiHandler.toWebserverQueue,&resp,0);
61
spiHandler.state=READY;
62
xSemaphoreGive(spiHandler.mutex);
63
}break;
64
default:break;
65
}
66
}
67
}
68
69
vTaskDelay(10/portTICK_RATE_MS);
70
}
71
}
Ursprünglich wollte ich zwei Arrays, die ich in der Struktur
SPI_HANDLER angelegt habe, für MISO/MOSI verwenden. Aber da wurden
dann wieder mehrere Nullen übertragen (wie im ursprünglichen Beitrag).
Mit lokalen Arrays so wie oben dargestellt, die eigentlich außerhalb des
Scopes liegen, funktioniert es dann seltsamerweise.
So ganz verstanden hab ich es noch nicht.
Max M. schrieb:> Vielen Dank für deine Hilfe.
Bitte gerne. Note to self: Lichtsteuerung zuende coden und die seit 3
Jahren laufene noch üblere Bastelversion damit ersetzen...
Schön, dass es jetzt ein bisschen mehr funzt :)
mfg mf