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?
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?
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
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?
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.
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.
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.
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?
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.
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.
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.
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?
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.