Forum: Mikrocontroller und Digitale Elektronik RP2040 core1 nutzen


von J. S. (jojos)


Lesenswert?

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_t counter;
2
3
void core1_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
void setup() {
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
void loop() {
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?

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

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!

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

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.

von J. S. (jojos)


Angehängte Dateien:

Lesenswert?

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?

von Mi N. (msx)


Lesenswert?

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
1
void multicore_launch_core1_with_stack   (   void(*)(void)    entry,
2
    uint32_t *    stack_bottom,
3
    size_t    stack_size_bytes 
4
  )
verwenden, damit man weiß, was Sache ist?

von J. S. (jojos)


Lesenswert?

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.

von Mi N. (msx)


Lesenswert?

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?

von Εrnst B. (ernst)


Angehängte Dateien:

Lesenswert?

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
extern void setup1() __attribute__((weak));
4
extern void loop1() __attribute__((weak));
5
extern "C" void main1() {
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" int main() {
18
//...
19
        if (setup1 || loop1) {
20
            delay(1); // Needed to make Picoprobe upload start 2nd core
21
            multicore_launch_core1(main1);
22
        }
23
//...
24
}

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

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.

von Chris (Gast)


Lesenswert?

Welche Board Definition nutzt du zum kompilieren? Die Arduino Mbed oder 
die von Earlphil? Beide ausprobiert oder tritt das Problem nur bei einer 
auf?

von J. S. (jojos)


Lesenswert?

Den Arduino Mbed Core weil ich eben das Mbed OS nutzen möchte.
Der Phil Hower macht nur 1 ms Delay, der hat aber auch eine andere USB 
Implementierung.

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Chris K. schrieb:
> Earlphil
Zu der rate ich!

Dann geht dieses:
1
int32_t counter;
2
3
4
void loop1()
5
{
6
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
7
  delay(1000);
8
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
9
  delay(1000);
10
11
  counter++;
12
}
13
14
void setup1()
15
{
16
  pinMode(LED_BUILTIN, OUTPUT);
17
}
18
19
20
void setup()
21
{
22
  Serial.begin(9600);
23
  Serial.println(F("Hello from Pico"));
24
}
25
26
void loop()
27
{
28
  delay(2000);
29
  Serial.println(counter);
30
}

Wenn es komplexer werden soll, ist auch FreeRTOS mit im Boot.

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

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.

von N. M. (mani)


Lesenswert?

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

: Bearbeitet durch User
von Mi N. (msx)


Lesenswert?

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?

von N. M. (mani)


Lesenswert?

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.

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

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.

von J. S. (jojos)


Lesenswert?

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

von Stefan F. (Gast)


Lesenswert?

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.
1
volatile int32_t counter;
2
3
void loop1()
4
{
5
  counter++;
6
}
7
8
void loop()
9
{
10
  Serial.println(counter);
11
}

von N. M. (mani)


Lesenswert?

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.

von J. S. (jojos)


Lesenswert?

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.

: Bearbeitet durch User
von Michi S. (mista_s)


Lesenswert?

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?

von J. S. (jojos)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

von Mi N. (msx)


Lesenswert?

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
1
  PSM_SET->FRCE_OFF = PSM_FRCE_OFF_proc1_Msk;       // core1 passiv
2
  while(!(PSM->FRCE_OFF & PSM_FRCE_OFF_proc1_Msk)); // abwarten
3
  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.

von J. S. (jojos)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.