Ich spiele gerade wieder mit dem RPi pico und habe ein kleines
Testprogramm (PlattformIO, Arduino Core).
Im setup() starte ich zum Schluss ein blinky auf dem core1 mit
1
uint32_tcounter;
2
3
voidcore1_worker(){
4
// DigitalOut led(LED1);
5
counter++;
6
7
pinMode(LED_BUILTIN,OUTPUT);
8
while(true){
9
digitalWrite(LED_BUILTIN,HIGH);// turn the LED on (HIGH is the voltage level)
10
sleep_ms(1000);// wait for a second
11
digitalWrite(LED_BUILTIN,LOW);// turn the LED off by making the voltage LOW
12
sleep_ms(1000);
13
14
counter++;
15
dsTemp_1+=1.0f;
16
}
17
}
18
19
voidsetup(){
20
Serial1.begin(115200);
21
Serial1.println(F("Hello from Pico"));
22
23
sleep_ms(500);
24
25
multicore_reset_core1();
26
multicore_launch_core1(core1_worker);
27
}
28
29
voidloop(){
30
31
printf("test %d\r\n",counter);
32
33
delay(2000);
34
}
Mit dem sleep von 500 ms funktioniert es, ohne oder mit 100 ms scheint
der core1 zu hängen, der counter bleibt bei 1 stehen. Warum ist dieses
sleep nötig?
J. S. schrieb:> der counter bleibt bei 1 stehen
Aus Sicht des Core0 loop ändert sich counter nicht.
Der Compiler weis nicht, dass Core1 die Variable stetig verändert.
Stichworte:
Atomic, Memory Barrier, volatile
J. S. schrieb:> Mit dem sleep von 500 ms funktioniert es, ohne oder mit 100 ms scheint> der core1 zu hängen,
Das scheint mir eine Nebelkerze zu sein!
evtl ändert die sleep Größe was am optimierungsverhalten das Compilers.
J. S. schrieb:> scheint der core1 zu hängen
Blinken müsste es trotzdem!
Arduino F. schrieb:> Aus Sicht des Core0 loop ändert sich counter nicht.> Der Compiler weis nicht, dass Core1 die Variable stetig verändert.
beide cores arbeiten auf den gleichen Speicher, daher ist es egal wer
den counter hochzählt. Und mit dem sleep funktioniert es ja,
reproduzierbar.
Arduino F. schrieb:> Das scheint mir eine Nebelkerze zu sein!
was für eine Nebelkerze? Es ist deutlich sichtbar und reproduzierbar.
Die Variable zählt hoch wenn es funktioniert und wird per printf am uart
ausgegeben. Und die onboard LED blinkt im funktionierenden Fall. Ohne
sleep blinkt die LED nicht und die counter Ausgabe bleibt bei 1, nicht
0.
Das sleep vor dem multicore_launch_core1(core1_worker); habe ich auch in
anderen Beispielen gesehen, aber ohne Erklärung warum das nötig ist.
es gibt einen Hardfault in der _usbisr. Die Ursache ist mir aber noch
nicht klar, darf der core1 nicht gestartet werden wenn core0 in einer
isr läuft? Aber das kann ja immer passieren?
J. S. schrieb:> multicore_reset_core1();> multicore_launch_core1(core1_worker);
Das schöne an Arduino ist ja, daß man nicht weiß, was man tut ;-)
Ich würde den 1. Befehl weglassen, denn ein nicht initialisierter core1
kann kaum zurückgesetzt werden.
Vielleicht besser
diese Funktionen sind aus dem pico SDK, die sollten also ok sein.
Es hat mit dem USB zu tun, ich habe noch dieses Issue gefunden:
https://github.com/arduino/ArduinoCore-mbed/issues/378
Bei Versorgung aus einer Powerbank läuft es auch mit ohne sleep.
J. S. schrieb:> Bei Versorgung aus einer Powerbank läuft es auch mit ohne sleep.
Wie sieht es aus, wenn Du in loop() mit while(1) { printf() und delay()
}
in eine Enlosschleife packst, damit loop() selber garnicht mehr
ausgeführt wird?
J. S. schrieb:> diese Funktionen sind aus dem pico SDK, die sollten also ok sein.
Evtl. ist genau das das Problem... der Arduino-Core bringt schon was
eigenes mit um den zweiten Core laufen zu lassen, nimmt es dir also
vielleicht übel, wenn du da mit den Low-Level-SDK-Funktionen
dazwischenpfuschst.
https://github.com/earlephilhower/arduino-pico/blob/master/docs/multicore.rst
aber eigentlich sollte das egal sein, wenn weder setup1 noch loop1
definiert sind:
1
//...
2
// Optional 2nd core setup and loop
3
externvoidsetup1()__attribute__((weak));
4
externvoidloop1()__attribute__((weak));
5
extern"C"voidmain1(){
6
rp2040.fifo.registerCore();
7
if(setup1){
8
setup1();
9
}
10
while(true){
11
if(loop1){
12
loop1();
13
}
14
}
15
}
16
//...
17
extern"C"intmain(){
18
//...
19
if(setup1||loop1){
20
delay(1);// Needed to make Picoprobe upload start 2nd core
Dann klemmt man das USBSerial ab.
Mi N. schrieb:> in eine Enlosschleife packst, damit loop() selber garnicht mehr> ausgeführt wird?
das ändert nichts, auch nicht wenn ich die endlos Schleife gleich in
setup() einfüge.
Εrnst B. schrieb:> Evtl. ist genau das das Problem... der Arduino-Core bringt schon was> eigenes mit um den zweiten Core laufen zu lassen
das ist 'der andere core', nicht der offizielle von Arduino. setup1()
und loop1() werden im Arduino core nicht aufgerufen.
Das weiß ich doch und habe es schon geschrieben das es setup1/loop1 im
anderen Core gibt.
Der Arduino Mbed Core hat ein RTOS eingebaut. Hardwarezugriffe sind
threadsafe. Und es gibt die EventQueue die ich nutzen möchte.
Im Datatsheet (2.8.2) steht dass die Funktion multicore_launch_core1
ausreicht. Ich würde versuchshalber Mal das reset weglassen. In Setup wo
du das aufrufst muss core1 eigentlich sowieso schon im Initialen Sleep
hängen.
Ich glaube aber selbst nicht dass es daran hängt.
Im gleichen Kapitel heißt es aber auch das beim Start des 2.Cores eine
Statusmaschine abgearbeitet wird die auch Core0 betrifft.
Wenn man sich dann etwas durchhangelt sieht man das in
multicore_launch_raw die Interrupts abgeschalteten werden. Solange bis
die Statusmaschine durch ist.
Ich könnte mir vorstellen dass das der USB Teil im Core0 (gerade wenn es
evtl noch viel auszuhandeln gibt) nicht sonderlich mag.
Deshalb evtl auch der Fault im USB Teil den du oben gezeigt hast.
Nur Vermutungen...
N. M. schrieb:> Nur Vermutungen...
Ich habe in den SDK-Routinen nachgesehen. Da wird reset_core1() implizit
in multicore_launch_core1() aufgerufen. Das ist also egal.
J. S. schrieb:> Dann klemmt man das USBSerial ab.>> Mi N. schrieb:>> in eine Enlosschleife packst, damit loop() selber garnicht mehr>> ausgeführt wird?>> das ändert nichts, auch nicht wenn ich die endlos Schleife gleich in> setup() einfüge.
Die Idee dahinter war ja, daß keine USB-Routinen mehr bedient werden.
Wenn es dann immer noch nicht läuft, könnte es ja ein Hardware-Problem
mit der Versorgungsspannung sein, was mich dann generell interessieren
würde.
Εrnst B. schrieb:> J. S. schrieb:>> diese Funktionen sind aus dem pico SDK, die sollten also ok sein.>> Evtl. ist genau das das Problem...
Auch das erscheint mir plausibel zu sein.
Selber programmiere ich auf Registerebene ohne Arduino und Co. Wenn man
den 2. Kern startet, läuft das Programm (idealerweise) wie erwartet.
Aber bei erneutem Download läuft - soweit ich das sehe - der 2. Kern
einfach weiter und bleibt hängen, sobald sein eigener Programmbereich
zerschossen wird. Da hilft dann nur noch ein Trennen der
Versorgungsspannung oder ein Hardware-Reset.
Vielleicht ist das auch hier das Problem. Einmal geht es, danach nicht
mehr?
Mi N. schrieb:> Ich habe in den SDK-Routinen nachgesehen. Da wird reset_core1() implizit> in multicore_launch_core1() aufgerufen. Das ist also egal.
Ja, habe ich mittlerweile auch.
Wie gesagt vermute ich auch eher was in Richtung USB und den Start des
2.Cores.
Ich denke Mal das hier ist ja genau dein Problem:
https://forums.raspberrypi.com/viewtopic.php?t=347097
Scheint also kein Einzelfall zu sein.
Edit:
Wenn ich das richtig lese wird vermutet dass der Timer so früh noch
nicht richtig läuft und deshalb etwas Timeoutet.
Im verlinkten Thread schreiben sie ja dass ein sleep_us(5000) als erste
Zeile reichen würde im das Problem zu lösen.
Edit2:
Hört sich doch eher nach einem Problem in OpenOCD/Picoprobe an wenn man
die Links liest.
Mi N. schrieb:> J. S. schrieb:>> Dann klemmt man das USBSerial ab.
Den Satz wollte ich noch löschen, das war meine Vermutung und ich habe
es vor dem Absenden noch getestet: das USBSerial läuft auch wenn ich in
das setup() eine Endlosschleife einbaue.
Der korrekte Test wäre also im setup den USB Int zu deaktivieren und
erst nach dem Start von core1 zu aktivieren.
Das core1 reset hatte ich eingebaut nachdem ich auf das Problem
gestossen bin, das ist also auch nicht. Der Mechanismus dahinter scheint
ja einfach: core1 startet mit Code aus dem ROM und wartet darauf über
den fifo SP und entry Pointer zu bekommen und rennt los wenn er alles
bekommen hat.
N. M. schrieb:> Edit2:> Hört sich doch eher nach einem Problem in OpenOCD/Picoprobe an wenn man> die Links liest.
viel zu lesen... ich kann auch nicht über den RPI debug probe Code
flashen. Max Gerhardt hat mir darauf geantwortet das der OpenOCD im
Arduino package evtl. zu alt ist und mir einen Link auf sein Repo
gegeben. Muss ich auch noch testen...
https://community.platformio.org/t/pico-upload-with-rpi-debug-probe-and-cmsis-dap-fails/33190
J. S. schrieb:> beide cores arbeiten auf den gleichen Speicher, daher ist es egal wer> den counter hochzählt
Ist es nicht. Innerhalb einer schleife neigt der Compiler dazu,
wiederholte Zugriffe auf RAM zu vermeiden, indem der Wert einmal in ein
CPU Register kopiert wird und dann nur noch das Register gelesen wird.
Das geht nämlich schneller, als RAM-Zugriffe.
Wann genau der Compiler das macht, und wann nicht, ist mehr oder weniger
zufällig. Mit dem Schlüsselwort "volatile" vor der Deklaration der
Variable kannst du den Compiler dazu zwingen, immer aufs RAM
zuzugreifen.
Ah okay.
Kannst ja auch Mal noch den Tipp versuchen den Timer nicht mit dem
Debugger zu stoppen:
timer_hw->dbgpause = 0;
Als erste Zeile.
Hilft dir dann zwar nicht direkt weiter, man will ja oft dass die Timer
stoppen, aber dann weißt du zumindest dass es da her kommt dass der
Debugger scheinbar die Timer beim Startup nicht richtig bedient.
Also der Fix von M. Gerhardt funktioniert, ich kann über die debug probe
den upload machen. Das ist schon mal fein und geht einen Tick schneller.
Das core1 Problem wird dadurch aber nicht behoben.
Ich starte auch nicht immer den Debugger, hatte ich jetzt nur einmal um
sehen wo es hängt.
Volatile hilft nicht, die LED blinkt im Fehlerfall ja auch nicht und der
Code darf nicht wegoptimiert werden.
N. M. schrieb:> Wenn man sich dann etwas durchhangelt sieht man das in> multicore_launch_raw die Interrupts abgeschalteten werden.> Solange bis die Statusmaschine durch ist.> Ich könnte mir vorstellen dass das der USB Teil im Core0> (gerade wenn es> evtl noch viel auszuhandeln gibt) nicht sonderlich mag.
Oder bevor das Serial1.printf durch ist?
Schon mal versucht, das hinter den Start von core1 zu verschieben?
Michi S. schrieb:> Oder bevor das Serial1.printf durch ist?> Schon mal versucht, das hinter den Start von core1 zu verschieben?
probiert und löst es auch nicht.
Das USBSerial wird noch vor dem setup() aktiviert:
https://github.com/arduino/ArduinoCore-mbed/blob/main/cores/arduino/main.cpp
Der core verwendet noch SDK 1.3.0, aktuell ist 1.5.0. Vielleicht ist in
den Changelogs ja was zu finden.
Ansonsten bleibt das sleep erstmal drin.
J. S. schrieb:> Volatile hilft nicht, die LED blinkt im Fehlerfall ja auch nicht
Ja, ich wollte mit meinem obigen Kommentar auch nicht sagen, das
Volatile das Hauptproblem lösen würde. Das hat (wenn überhaupt) nur
einen Einfluss auf die Ausgabe des Counters.
Da ich für den RP2040 einen J-Link EDU mini benutze und damit Probleme
habe, den 2. Kern "zielgerichtet" neu zu starten, hatte ich gehofft,
hier eine Idee zur Lösung des Problems zu erhalten:
Wenn der 2. Kern erst einmal läuft, gibt es Probleme, per SWD eine neue
Programmversion zu starten.
Mit ein paar Wiederholungen kann es dann doch laufen. Laden mit "Connect
during reset" o.ä. scheinen nur wirkungslose Optionen zu sein.
Andere Anwender haben dieses Problem in ähnlicher Form wohl auch.
Angeregt durch diesen Beitrag hier habe ich weitere Dinge probiert und
u.a. gezielt im Debugger das reset_core1() manuell nachgebildet
PSM_CLR->FRCE_OFF=PSM_FRCE_OFF_proc1_Msk;// Bit wieder loeschen
was aber keinen Erfolg bringt.
Erst nach Trennen der Versorgungsspannung ist wieder Ruhe, was aber bei
der Entwicklung extrem lästig ist.
Vielleicht sind delay(), sleep() und Programmumstellungen nur
"Hilfeschreie", um den richtigen Zeitpunkt beim Start zu erwischen, den
es eigentlich garnicht geben dürfte.
ich habe da noch nicht weiterforschen können. Eine Idee war noch einen
BP auf die Übergabe der Argumente für core1 zu setzen, also um zu sehen
ob der Stackpointer richtig ist.
Was im sleep passiert habe ich nicht nachgesehen. Im Mbed sleep wird ein
Wecker gestellt und dann per WFI gewartet, evtl. sogar im deep sleep.
Das ist gut zum Strom sparen, macht aber in einigen Fällen Ärger.
Der Reset müsste ja auch core1 rücksetzen, und der sollte dann wieder
aus dem ROM in seine Initialsierung laufen. Aber so genau habe habe ich
mir das RefMan noch nicht angesehen.