Arduino Portenta H7 Part 2: Dual-Core Computing

Arduino PRO Portenta H7 Development Board
Arduino PRO Portenta H7 Development Board

With its two Arm Cortex cores operating at 480 MHz and 240 MHz, the Arduino Portenta H7 is the most powerful Arduino platform we’ve seen here so far.  The platform was created for use in high-end industrial machinery, lab equipment, computer vision systems, and other professional equipment.

Previously, we installed the latest Arduino development software and used it to blink the on-board multicolor light-emitting diode (LED).  We also noticed that both the M7 core and the M4 core are selectable as targets any time we upload our sketch (application).

This means that our future systems can now benefit from true hardware parallelism.  With previous Arduino platforms, we could achieve concurrency only through interrupts and multi-threading.  This was the case for resources such as the official Scheduler library.

Today, we will take this idea to the next step by truly running two sketches in parallel!  One sketch for the M7 core, and one for M4.  This is thanks to the dual-core STM32H747XI Arm microcontroller unit (MCU).

Notice of Non-Affiliation and Disclaimer: As of the publication date, we are not affiliated, associated, authorized, endorsed by, compensated by, or in any way officially connected with Arduino, Arduino.cc, Arduino LLC, Genuino, Arduino AG, ARM Limited, ARM Holdings, STMicroelectronics, Google Brain Team, or their owners, subsidiaries or affiliates.  The names Arduino, Arduino.cc, Arduino LLC, Genuino, Arduino AG, Portenta H7, Arduino Uno, STMicroelectronics, STM32, ARM Cortex, TensorFlow, as well as related names, marks, emblems, and images are trademarks of their respective owners.

Objectives

  • Test the Dual-Core LED Blink Example
    • Program the M7 Core to blink red
    • Program the M4 Core to blink green
    • Enable the M4 Core
  • Replace the delay() method with the millis() method

Software Requirements

This setup uses the plain Arduino Integrated Development Environment (IDE).  You can also try the web-based IDE and the Arduino Professional IDE; which was in beta as of August 2020.

  • Arduino IDE 1.8.10 or later
    • Version 1.8.13 is featured here
  • Board Manager must have “mbed-enabled Boards” installed
    • Version 1.2.0 is featured here
  • Portenta’s COM port should be visible in Device Manager

Material Requirements

  • Laptop or Desktop computer
  • Arduino Pro Portenta H7 board
  • USB C cable
  • (Recommended) Anti-static workstation

Headers are Optional

As a side note, you’ll notice today my Portenta H7 now has 2.54 mm (0.1 inch) pitch pin headers attached.  This was an optional decision on my part.  If you don’t have an immediate need for these, just ignore them: because they are not required for this procedure.

A circuit board

Description automatically generated
Figure 1: The System with Optional Pins Attached

If you plan on doing this, think first about how you plan to use the high-density connectors on the bottom, and the upcoming Portenta Carrier Board they connect to.  Or consider using sockets to match the Arduino MKR series form factor.

Prepping the System

Go ahead and plug your Portenta H7 into your computer if you have not already.

To recap: last time, we made sure that the Portenta H7 was visible in Windows Device Manager as a USB Serial Device.

Figure 2: The Arduino Appears as a COM Device. Your COM Number may Vary.

You’ll know you are ready for this tutorial if you’re able to double-click the blue reset button on the device (while it’s plugged in), and then see a new USB device: “Envie H7 Bootloader” appear on your system.  This will place the system in bootloader mode, which is indicated by a slow “breathing” green LED pattern.

Figure 3: In Bootloader Mode, a New Device will Appear in the USB Devices Category

If that didn’t work out for you, it’s worth trying the unofficial step of navigating to the directory:

C:\Users\YOUR_USER_NAME_HERE\AppData\Local\Arduino15\packages\arduino-beta\hardware\mbed\1.2.0\

In that directory (or one later than version 1.2.0), you should find a batch file named post_install.bat.  Run it as an administrator.  It won’t give you a response unless there’s an error.  This may improve your driver.

Program the Dual-Core Example

Now we can open the Arduino IDE.  If your Portenta H7 is in bootloader mode, get it back to normal by unplugging it and then plugging it back in.

The official example will blink the on-board LED is a sequence of three different colors:

  • Red using only the M7 core
  • Green using only the M4 core
  • Yellow when red and green are both on

The yellow appearance is caused by additive mixing of red and green, the system has no yellow LED.  But we expect the “yellow” state to also be brighter than the other states.

Next, we will program the red LED to blink with a period of 400 milliseconds (ms).  Specifically: 200 ms on, plus 200 ms off.

The green LED will blink with a period of 1000 ms.  Note that for the on-board LED, a state of “LOW” means the LED is illuminated, and a state of “HIGH” means it is dark.

Programming the M7 Core

In the Arduino IDE, create a new sketch and enter the following code…

void setup() {
  pinMode(LEDR, OUTPUT);      // Set Red LED pin to output mode
}

void loop() {
  digitalWrite(LEDR, LOW);    // LED is active low : 0 = LED on, 1 = LED off
  delay(200);                 // Hold for visibility
  digitalWrite(LEDR, HIGH);   // LED off
  delay(200);                 // Hold 
}

Save the sketch as blink_RedLed_m7.ino.

Figure 4: The Red LED Blink Sketch

To upload, navigate to Tools –> Board:

  • Select “Arduino mBed OS Boards (nRF52840/STM32H747)”
  • Select “Arduino Portenta H7 (M7 core)”
Figure 5: Select the M7 Core using the “Board” Submenu

Now, navigate to Tools –> Port:

  • Select whatever COM port your Arduino has claimed.
Figure 6: Select the Appropriate COM Port for your System

Then click the upload button in the top-left corner.

Within seconds, you should see your Arduino Portenta H7 blinking the red LED.

Programming the M4 Core

Now its time to blink the green LED using the M4 core.

Leave your existing sketch open, and then open a new sketch.

Save the new sketch as “blink_GreenLed_m4.ino,” and then enter the code below.  It’s basically the previous code, but with the following changes…

  • Replace all “delay(200)” with “delay(500).”
  • Replace all “LEDR” with “LEDG.”

By the end, it should look something like this…

void setup() {
  pinMode(LEDG, OUTPUT);      // Set Green LED pin to output mode
}

void loop() {
  digitalWrite(LEDG, LOW);    // LED is active low : 0 = LED on, 1 = LED off
  delay(500);                 // Hold for visibility
  digitalWrite(LEDG, HIGH);   // LED off
  delay(500);                 // Hold 
}
Figure 7: The Green LED Sketch for the M4 Core

Now select the M4 core as the target.  Adjust the COM port if you need to, and then click the upload button.  You should see that the blinking pattern has not changed from before.  That is because the M4 core isn’t configured like the M7 core.  We must program the M7 core to enable the M4 first!

A circuit board

Description automatically generated
Figure 8: The M7 Core will be the only Core Running Until it Enables the M4 Core

Force-Boot the M4 Core

To boot the M4 core into a state that will run its application, we must return to the M7 sketch.

Return to the M7 sketch, and then add the LL_RCC_ForceCM4Boot() function to the non-repeating “setup()” section of the code…

void setup() {
  LL_RCC_ForceCM4Boot();      // M4 boot enable
  pinMode(LEDR, OUTPUT);      // Set Red LED pin to output mode
}

void loop() {
  digitalWrite(LEDR, LOW);    // LED is active low : 0 = LED on, 1 = LED off
  delay(200);                 // Hold for visibility
  digitalWrite(LEDR, HIGH);   // LED off
  delay(200);                 // Hold 
}

In the red LED sketch, re-select the M7 core if you need to, and then upload the new code.

Now we can return to the device to see all three lights appearing as expected.  Notice we didn’t need to re-upload our earlier M4 sketch.

Making the Delays Non-Blocking

To make these sketches practical for use, we’ll want to perform other tasks besides blinking the LED.  This isn’t practical with the simple blink code we just wrote, because the delay() functions will block program flow until their time intervals have elapsed.

One way to avoid this problem is to let the Arduino’s millis() function do the time-keeping for us.

The millis() function (short for “milliseconds”) will return the Arduino’s uptime in milliseconds.  This millis() timing function happens in the background.  The official website provides a tutorial: “BlinkWithoutDelay” with additional detail.

This will free up our future code to do other things.  As long as our code checks the value of millis() often enough, we can use millis() to signal when an LED should change states.

Go back to the M7 core sketch (blink_RedLed_m7.ino).  Then, enter the following …

int ledState = LOW;               // For storing the desired LED state
unsigned long previousMillis = 0; // For storing uptime
const long INTERVAL = 200;        // Toggle this often (unit: ms)

void setup()
{
  //--Enable the M4 core and make the Red LED an output
  LL_RCC_ForceCM4Boot();
  pinMode( LEDR, OUTPUT);
}

void loop()
{
  //--Check the elapsed time  
  unsigned long currentMillis = millis();          

  //<-- YOUR FUTURE CODE GOES HERE -->

  //--Is it time to toggle the LED?
  if ( currentMillis - previousMillis >= INTERVAL) 
  {
    // ..If yes, save current time.  Then update the LED pin and LED state.
    previousMillis = currentMillis;                
    ( ledState == LOW) ? (ledState = HIGH) : (ledState = LOW);
    digitalWrite( LEDR, ledState);
  }
}

…as you can see, the delay() method is no longer present.  Instead, we used new variables: currentMillis and previousMillis to test when the time interval had elapsed.

  • If it has, then we invert the state of ledState, and then we write that state to the LED pin.
  • If it has not, then we do other things until its time for another LED state change

Upload the code again, and then open the M4 sketch.

Reprogram the M4 Core to use the millis() Method, also

Now, let’s get the M4 core to do the same thing.  The code below is identical to the M7 code, except…

  • Don’t use LL_RCC_ForceCM4Boot()
  • Replace all “LEDR” with “LEDG”
  • Replace “INTERVAL = 200” with “INTERVAL = 500”
  • Update the comments if you want to
int ledState = LOW;               // For storing the desired LED state
unsigned long previousMillis = 0; // For storing uptime
const long INTERVAL = 500;        // Toggle this often (unit: ms)

void setup()
{
  //--Make the Green LED an output
  pinMode( LEDG, OUTPUT);
}

void loop()
{
  //--Check the elapsed time  
  unsigned long currentMillis = millis();          

  //<-- YOUR FUTURE CODE GOES HERE -->

  //--Is it time to toggle the LED?
  if ( currentMillis - previousMillis >= INTERVAL) 
  {
    // ..If yes, save current time.  Then update the LED pin and LED state.
    previousMillis = currentMillis;                
    ( ledState == LOW) ? (ledState = HIGH) : (ledState = LOW);
    digitalWrite( LEDG, ledState);
  }
}

Upload the sketch.

Now the LEDs are blinking just like before, but in a non-blocking fashion that frees our Arduino to do other things.

The be certain this isn’t our original delay-based code, consider adjusting the interval values to something different.  Then check the results.

Closing Remarks

Just as before, using the dual-core features of the Portenta H7 was straightforward.  Thanks to the Arduino IDE, having 2 cores was no more challenging than having 2 Arduinos plugged in at once.

The 3-color sequence also gives us an easy way to check whether both cores are running.  If one core is offline, then we will only see one color and a very steady blink rate.  This is excellent visual communication!

Speaking of both cores, this system represents an asymmetric multiprocessing (AMP) system.  The official webpage indicates we can use remote procedure calls (RPC) to make the cores work directly with each other.  And the RPC technique already has examples included in the Arduino IDE.  If RPCs are not your style, we may visit other methods of inter-processor communication in the future.

Finally, it’s worth considering what happens when both cores have a shared resource that one core might overwrite before or after the other.  This is a best practice for concurrent systems.  Concurrent software design is practically its own sub-branch of engineering, so it’s worth considering this if you want to use the Portenta H7 to its full potential!

References

[1]STMicroelectronics, “Dual 32-bit Arm® Cortex®-M7 up to 480MHz and -M4 MCUs, up to 2MB Flash, 1MB RAM, 46 com. and analog interfaces, SMPS, DSI, DS12930 Rev 1,” May 2019. [Online]. Available: https://www.st.com/content/ccc/resource/technical/document/datasheet/group3/99/a2/f9/3c/27/bc/48/a8/DM00602212/files/DM00602212.pdf/jcr:content/translations/en.DM00602212.pdf. [Accessed 3 Aug. 2020].
[2]Arduino, “Arduino – Scheduler,” Arduino, 3 Aug. 2020. [Online]. Available: https://www.arduino.cc/en/Reference/Scheduler. [Accessed 24 Dec. 2019].
[3]Arduino, “Arduino – MultipleBlinks,” Arduino, [Online]. Available: https://www.arduino.cc/en/Tutorial/MultipleBlinks. [Accessed 3 Aug. 2020].
[4]Arduino, “Portenta H7,” 7 Jan. 2020. [Online]. Available: https://content.arduino.cc/assets/Arduino-PortentaH7-schematic-V1.0.pdf. [Accessed 3 Aug. 2020].
[5]Arduino, “Arduino Pro,” Arduino, 23 Apr. 2020. [Online]. Available: https://www.arduino.cc/pro/tutorials/portenta-h7/por-ard-dcp. [Accessed 5 Aug. 2020].
[6]“Arduino – BlinkWithoutDelay,” 28 July 2015. [Online]. Available: https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay. [Accessed 4 Aug. 2020].
[7]Arduino, “millis() – Arduino Reference,” Arduino, [Online]. Available: https://www.arduino.cc/reference/en/language/functions/time/millis/. [Accessed 4 Aug. 2020].

Important Notice: This article and its contents (the “Information”) belong to Unboxing-tomorrow.com and Voxidyne Media LLC. No license is granted for the use of it other than for information purposes. No license of any intellectual property rights is granted.  The Information is subject to change without notice. The Information supplied is believed to be accurate, but Voxidyne Media LLC assumes no responsibility for its accuracy or completeness, any error in or omission from it or for any use made of it.  Liability for loss or damage resulting from any reliance on the Information or use of it (including liability resulting from negligence or where Voxidyne Media LLC was aware of the possibility of such loss or damage arising) is excluded