arduino / ArduinoCore-mbed

346 stars 200 forks source link

Raspberry Pi Pico board using multicore lib with mbedOS - two interesting observations #242

Open Gerriko opened 3 years ago

Gerriko commented 3 years ago

I'm using a Raspberry Pi Pico board with the ArduinoCore-mbed 2.1.0.

I'm experimenting with the multicore library, which is part of the pico-SDK and I am using a mix of Arduino commands and mbedOS commands.

Observation 1: CORE1 does not like the standard delay() or the sleep_ms() function within a while loop.

For example this does not work:

void core1_entry() {
  // Note using mbed command mbed::DigitalOut led2(p2, 0); to define my led GPIO
  while (1) {
      sleep_ms(1000);
      led2 = !led2;
  }
}

But if I use millis() as a timer function within Core1 it does work:

void core1_entry() {

  uint32_t t_int = millis();

  while (1) {
      if ((millis() - t_int) > 1000) {
        led2 = !led2;
        t_int = millis();
      }
  }
}

So this code works for me:

#include <mbed.h>
#include "pico/multicore.h"

mbed::DigitalOut led1(LED1, 0);
mbed::DigitalOut led2(p2, 0);

void core1_entry() {

  uint32_t t_int = millis();
  while (1) {
    if ((millis() - t_int) > 1000) {
      led2 = !led2;
      t_int = millis();
    }
  }

}

void setup() {

  multicore_launch_core1(core1_entry);

}

void loop() {
  led1 = !led1;
  sleep_ms(500);
}

Observation 2: multicore does not like the mbedOS EventQueue function.

First I tried using EventQueue in CORE0, and I found that only one core works and this depends on the order you place things. For example, with this code only Core 1 works:

#include <mbed.h>
#include <EventQueue.h>
#include "pico/multicore.h"

using namespace std::chrono_literals;

mbed::DigitalOut led1(LED1, 0);
mbed::DigitalOut led2(p2, 0);

events::EventQueue queue;

void eventTriggerHandler()
{
    led1 = !led1;
}

void core1_entry() {

  uint32_t t_int = millis();

  while (1) {
      if ((millis() - t_int) > 1000) {
        led2 = !led2;
        t_int = millis();
      }
  }
}

void setup() {

  multicore_launch_core1(core1_entry);

  // events are simple callbacks, call every 500 milliseconds
  queue.call_every(500ms, eventTriggerHandler);

  // the dispatch method executes events
  queue.dispatch();

}

void loop() {
}

but if I change the order around so that multicore_launch_core1(core1_entry);is called after queue.dispatch() then only core0 works. As in:

void setup() {

  // events are simple callbacks, call every 2 seconds
  queue.call_every(500ms, eventTriggerHandler);

  // the dispatch method executes events
  queue.dispatch();

  multicore_launch_core1(core1_entry);

}

But if I try and use the EventQueue method in Core 1 it causes a hardware crash. I added a Serial1.begin() in my Core0 setup() and it spits out this info

++ MbedOS Fault Handler ++

FaultType: HardFault

Context:
R0   : FFFFFFDC
R1   : 2000CD40
R2   : 2000CCFC
R3   : 20002A98
R4   : 00000828
R5   : 2000CD84
R6   : 10000343
R7   : 10002221
R8   : FFFFFFFF
R9   : FFFFFFFF
R10  : FFFFFFFF
R11  : FFFFFFFF
R12  : FFFFFFFF
SP   : 20040F88
LR   : FFFFFFF9
PC   : 10001F90
xPSR : A100000B
PSP  : FFFFFFFC
MSP  : 20040F68
CPUID: 410CC601
Mode : Handler
Priv : Privileged
Stack: MSP

-- MbedOS Fault Handler --

++ MbedOS Error Info ++
Error Status: 0x80FF013D Code: 317 Module: 255
Error Message: Fault exception
Location: 0x10001F90
Error Value: 0x2000CB8C
Current Thread: rtx_idle <handler> Id: 0x2000CCFC Entry: 0x10004059 StackSize: 0x200 StackMem: 0x2000B9B8 SP: 0x20040F88 
For more info, visit: https://mbed.com/s/error?error=0x80FF013D&osver=61000&core=0x410CC601&comp=2&ver=100300&tgt=RASPBERRY_PI...

-- MbedOS Error Info --
Gerriko commented 3 years ago

I just noticed that there is some overlap with the issues raised here and some of the solutions given will help some of my issues (re. issue about use of delay): https://github.com/arduino/ArduinoCore-mbed/issues/217

I am reading the Pico documentation again and I see that there are two other methods that can also be used to launch core1.

I wondered if any these two methods have been tested with ArduinoCore-mbed and wondered whether any of these two methods may be better suited with mbed.

void multicore_launch_core1_with_stack (void(*entry)(void), uint32_t *stack_bottom, size_t stack_size_bytes)

void multicore_launch_core1_raw (void(*entry)(void), uint32_t *sp, uint32_t vector_table)

magnusnordlander commented 3 years ago

I'm also working with multicore, and I'd just like to add a couple of observations.

Basically, anything running on core1 (at the time of writing) needs to be pure pico-sdk code.

MNS26 commented 2 years ago

@magnusnordlander i dont quite get what you mean by "pure pico-sdk". Regarding the RTOS issue do you mean core1 stops running after a short period?

magnusnordlander commented 2 years ago

[Caveat: I haven't looked in to this for a few months. Things might have changed, but I'm guessing this is all still true]

@MNS26 There are basically three SDKs you can use when developing for the RP2040. There's the pico-sdk, written in C and supplied by the Raspberry Pi foundation; there's Mbed OS, which internally uses pico-sdk, but which provides a nicer, C++ development environment with an RTOS; and there's the Arduino core, which internally uses Mbed OS, which provide that Arduino ease of use we all know and love.

Mbed OS is not designed for multi-core and is not multi-core aware at all. The RTOS isn't initialized on core 1, and there's no easy way of running two instances of the RTOS (one on each core). Running anything based on the RTOS on core 1 will cause it to freeze. Furthermore, you can't enter into Mbed OS critical sections simultaneously on Core 0 and Core 1, doing so will cause a crash.

A lot of Mbed OS uses either the RTOS or critical sections. As such, it's practically impossible to use any of it on Core 1. Since Arduino Core is built upon Mbed OS, that's also out. Thus you are left with using the pico-sdk.