Guys, I'm using RPC for communication between the M4 and M7 core on the Protenta H7. Does anyone know if there is an interrupt in the RPC library? Or has anyone the keywords of the RPC.h lib?
Not RPC related, but related to sharing data between cores using threading and SRAM. I am actively pursuing the best method to quickly communicate between cores. I have not yet tried the code in the link:
sram-share-core-memory
This may be of interest as well:
stm32h7-dual-core-inter-cpu-async-communication
Hey Jhaytera, Communication between the cores is not an issue for me. RPC worked fine for me. The only thing Im looking for is a method to let the "receiver" know that the data is ready.
In other words: the M4 core collects a batch of 10 seconds of data. When this is ready, the M7 should receive it (now using an external hardware interrupt). The transfer should be reasonably fast so the M4 core can continue working on the next batch of 10 seconds. However, the M7 core has the time to do all the processing. Link in wire.h you have something like Wire.onReceive, which can be used in I2C communication.
There's talk of interrupts on this Portenta thread:
interrupts on portenta
Someone says you can use the "normal" Arduino function attachInterrupt().
I'm thinking that you can have the M7 core attach to a pin as an interrupt. Then, when the M4 is ready, have the M4 core set the pin to HIGH, thus setting off the M7 interrupt routine? The M7 core interrupt routine can then reset the interrupt pin, and now it knows to go get the data. Just an idea. I'm still learning.
If you attach an LED to the pin, you'll be able to see how busy the cores are
Thats exactly what I'm testing. The only thing I'm not sure of is the set HIGH of a pin using the M4 and the reset of the pin to LOW using the M7. I have to run this test.
Awesome.
Maybe the pin can be one that is not broken out, this way none of the headers pins are affected.
Some pin out info with portenta-breakout-board.
FYI. I've tried SPI, but the SPI.h lib for the Portenta is different and cannot set a core as a slave.
Good to know. Thanks.
Do you know how to tell the linker to not use the SRAM4 in any other allocations (STACK etc) if it is being used for data sharing between the 2 CPUs.
You may find everything you need in the following link. I have that example working. Just got it working last night. Seems very fast:
portenta-pro-community-solutions/blob/main/examples/dot6-portenta-advanced/dot63-sram-share-core-memory/dot63-sram-share-core-memory.ino
There's mention of carving out the memory if you want to use SRAM4 or SRAM3... not sure if that's what you are looking for.
I was using a randomly malloc'ed area of memory for sharing, and I was getting some errors happening (every time I would Serial.print there was a chance the M7 core would hang).
I decided to try your example as above at the start of this thread. I run MPU_config as my first command in setup(). Program works up until it is time to start the Ethernet thread. I have not completely debugged it, but I am wondering if the Ethernet thread makes use of the SRAM4 area.
I will retry the code with SRAM3 as the CPU shared RAM area, and let you know.
Interesting. Please keep us updated! That would be very good to know.
I could not get the SRAM3 or SRAM4 interface to work. When executing the MPU_Config() command (as the first command executed in setup), the program would hang at the Ethernet Configuration stage.
If I eliminated the MPU_Config() command, the Ethernet initialization worked, but then the code would crash shortly thereafter (likely because of STAM3 and/or SRAM4 being used by code, and being overwritten by the manually created shared memory areas).
I changed the code to simply share 4K of space in the SDRAM - I was already using it to share the monitoring of watchdog variable status (i.e. threads in M7 and 1 thread in M4 set the variables in the SDRAM, and a master thread in M7 looks to see that they are set - if all set - it refreshes the watchdog timer, and then resets the variables - this way if any thread crashes - the watchdog will be tripped).
In any case - I simply extended the amount of mem required in SDRAM for sharing to add my new requirements, and used and passed those pointers to the threads in question.
So far that is working.
One of the issues of SDRAM is that it is an 8bit interface, so if you are using it to pass actual data, it ?may be possible to read a partially stored variable. For example - thread M7 starts to write a 4 byte float, gets to only 2 bytes before M4 reads it.
In my case I do not have an issue with this as I am storing only non changing pointers to RAM locations in main memory to pass variables.
If I had to read SDRAM variables directly, I would probably create another flag field for each block of variables - set it to indicate new data to M4, once read and copied, M4 would clear that variable. M7 would not enter new data until that variable is cleared. A little slower, but it would work.
Hi rs77can. I decided to combine a wired ethernet web server sketch (responds to simple web input to change the H7 LED), and the memory sharing routine/shetch (shares memory between cores, uses SRAM4, and has a 1ms M7 memory check). It seems to be running well. I set my router to let the connection through from the outside world to Port 80 to the Portenta H7. All seems good. I'm not passing any chunks of data between cores yet as far as I can tell in the example. The LED lights with the action of the Cores. You can probably just compile this and put your local IP in a browser to test (change MAC# and IP)... if you feel like it. I'll try and get some large chunks of data going back and forth to see if anything breaks.
Here's the mashed sketches working as one:
// Original by Khoi Hoang (Github @khoih-prog ) and Jeremy Ellis (Github @hpssjellis )
// Note: each core can be separated to it's own IDE folder
#define STARTING_M4_VALUE 0
#define STARTING_M7_VALUE 0
#include "mbed.h"
#include "Arduino.h"
#include <vector>
using namespace mbed;
using namespace rtos;
#define USING_SRAM4 true
#define SRAM3_START_ADDRESS ((uint32_t) 0x30040000)
#define SRAM4_START_ADDRESS ((uint32_t) 0x38000000)
#if USING_SRAM4
// Using AHB SRAM4 at 0x38000000
#define SRAM_START_ADDRESS SRAM4_START_ADDRESS
//#define ARRAY_SIZE 16000
const uint16_t ARRAY_SIZE = 16000;
// Max 64K - 8 bytes (1 * uint32_t + 2 * uint8_t)
#if ( ARRAY_SIZE > (65536 - 8) / 4 )
#error ARRAY_SIZE must be < 16382
#endif
#else
// Using AHB SRAM3 at 0x30040000
#define SRAM_START_ADDRESS SRAM3_START_ADDRESS
//#define ARRAY_SIZE 8000
const uint16_t ARRAY_SIZE = 8000;
// Max 32K - 8 bytes (1 * uint32_t + 2 * uint8_t)
#if ( ARRAY_SIZE > (32768 - 8) / 4 )
#error ARRAY_SIZE must be < 8190
#endif
#endif
struct dummy_senor_data
{
long Val;
};
struct shared_data
{
uint32_t M7toM4;
uint16_t M4toM7_Index;
uint16_t dummyM7val;
uint16_t dummyM4val;
uint32_t M4toM7[ARRAY_SIZE];
};
int LED_Blink = 0;
////////////////////////////////////////////////////////////////////
#ifdef CORE_CM7 // Start M7 Core programming
//====================================================================================
#include <Arduino.h> // Only needed by https://platformio.org/
#include <Portenta_Ethernet.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0x00, 0x55, 0xD8, 0x39, 0x00, 0x4F
};
IPAddress ip(192, 168, 1, 77); // what are these for??
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
//====================================================================================
uint32_t localm7m4 = STARTING_M7_VALUE;
uint32_t myStoreFromM4 = STARTING_M4_VALUE;
// Current 1 sec
#define M7_DELAY_US 1000UL //20230223 changed to 25ms was --> 30000UL // microSeconds not milliSeconds (30 ms)
unsigned long delayStart = 0;
static struct shared_data * const xfr_ptr = (struct shared_data *) SRAM_START_ADDRESS;
Thread M7Thread;
////////////////////////////////////////////
void MPU_Config()
{
/*
MPU - The MPU is an optional component for the memory protection. Including the MPU
in the STM32 microcontrollers (MCUs) makes them more robust and reliable.
The MPU must be programmed and enabled before using it. If the MPU is not enabled,
there is no change in the memory system behavior.
HAL - The STM32 Hardware Abstraction Layer (HAL) provides a simple, generic multi-instance
set of APIs (application programming interfaces) to interact with the upper layers
like the user application, libraries and stacks.
*/
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/////////////
/* Configure the MPU attributes as WT for SDRAM */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
// Base address of the region to protect
#if USING_SRAM4
MPU_InitStruct.BaseAddress = SRAM4_START_ADDRESS; // For SRAM4 only
// Size of the region to protect, 64K for SRAM4
MPU_InitStruct.Size = MPU_REGION_SIZE_64KB; // Important to access more memory
#else
MPU_InitStruct.BaseAddress = SRAM3_START_ADDRESS; // For SRAM3 only
// Size of the region to protect, only 32K for SRAM3
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; // Important to access more memory
#endif
// Region access permission type
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
// Shareability status of the protected region
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
/////////////
// Optional
// Bufferable status of the protected region
//MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
// Cacheable status of the protected region
//MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
// Number of the region to protect
//MPU_InitStruct.Number = MPU_REGION_NUMBER7;
// TEX field level
//MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
// number of the subregion protection to disable
//MPU_InitStruct.SubRegionDisable = 0x00;
// instruction access status
//MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
/////////////
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
////////////////////////////////////////////
void setup()
{
MPU_Config();
bootM4();
pinMode(LEDR, OUTPUT); // portenta red LED
Serial.begin(2000000);
//===========================================================================================
pinMode(LEDR, OUTPUT);
pinMode(LEDG, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT); // set the LED Green pin mode
digitalWrite(LEDR, HIGH);
digitalWrite(LEDG, HIGH);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("Ethernet WebServer Example");
// start the Ethernet connection and the server:
Ethernet.begin(mac);
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// start the server
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
digitalWrite(LEDR, LOW);
digitalWrite(LED_BUILTIN, HIGH);
//===========================================================================================
//while (!Serial);
// Following issue solved by starting RPC. Not sure why.
// Following line is needed, not sure why?
//Serial.println(SRAM_START_ADDRESS, HEX); // MUST HAVE
M7Thread.start(callback(M7ThreadFunc));
}
////////////////////////////////////////////
void M7ThreadFunc()
{
static uint16_t localIndex;
localm7m4 = STARTING_M7_VALUE;
while (true)
{
yield();
if ( (micros() - delayStart ) >= M7_DELAY_US ) //every 5ms
{
delayStart = micros(); // reset the delay time
//--20230227 moved above yield();
localm7m4++;
xfr_ptr->M7toM4 = localm7m4;
//--20230223 out Serial.print("M7 to M4: "); Serial.println(xfr_ptr->M7toM4);
//--20230223 out localIndex = xfr_ptr->M4toM7_Index;
//--20230223 out Serial.print("M4 to M7: Index = "); Serial.print(localIndex);
//--20230223 out Serial.print(", value = "); Serial.println(xfr_ptr->M4toM7[localIndex]);
// test if M7 detects struct changed on M4 (xfr_ptr->M4toM7_Index)
if (myStoreFromM4 != xfr_ptr->M4toM7_Index)
{
myStoreFromM4 = xfr_ptr->M4toM7_Index;
if (LED_Blink++ >= 7) {
LED_Blink = 0;
digitalWrite(LEDR, !digitalRead(LEDR)); // flip red LED on and off
}
}
}
}
}
////////////////////////////////////////////
void loop()
{
// listen for incoming clients
EthernetClient client = server.available();
if (client) { // if you get a client,
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
yield();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
//client.println("{answer:42}");
client.println();
// the content of the HTTP response follows the header:
client.print("<input type=button value='LED GREEN Off' onclick='{location=\"/H\"}'>");
client.print("<input type=button value='LED GREEN On' onclick='{location=\"/L\"}'>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
} else { // if you got a newline, then clear currentLine:
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Check to see if the client request was "GET /H" or "GET /L":
if (currentLine.endsWith("GET /H")) {
digitalWrite(LEDG, HIGH); // GET /H turns the LED on
}
if (currentLine.endsWith("GET /L")) {
digitalWrite(LEDG, LOW); // GET /L turns the LED off
}
}
}
// close the connection:
client.stop();
}
yield();
}
#endif
////////////////////////////////////////////////////////////////////
#ifdef CORE_CM4 // Start M4 Core programming
uint32_t localm4m7 = STARTING_M4_VALUE;
static struct shared_data * const xfr_ptr = (struct shared_data *) SRAM_START_ADDRESS;
uint32_t myStoreFromM7 = STARTING_M7_VALUE;
Thread M4Thread;
////////////////////////////////////////////
void setup()
{
pinMode(LEDB, OUTPUT); // portenta blue LED
memset( xfr_ptr, 0, sizeof(*xfr_ptr) );
xfr_ptr->M4toM7_Index = 0;
M4Thread.start(callback(M4ThreadFunc));
}
void M4ThreadFunc()
{
localm4m7 = STARTING_M4_VALUE;
xfr_ptr->M4toM7[0] = 0xDEADBEEF;
while (true)
{
// without this => not increase ???
//delayMicroseconds(0); //--20230223 <--- I removed. Not needed.
// To verify if SRAM change if OK by checking index = array[index]
//xfr_ptr->M4toM7[xfr_ptr->M4toM7_Index] = xfr_ptr->M4toM7_Index;
//xfr_ptr->M4toM7[xfr_ptr->M4toM7_Index] = localm4m7++;
xfr_ptr->M4toM7[xfr_ptr->M4toM7_Index] = analogRead(A0);
xfr_ptr->M4toM7_Index = (xfr_ptr->M4toM7_Index + 1) % ARRAY_SIZE;
// test if M4 detects struct changed on M7 (xfr_ptr->M7toM4)
if (myStoreFromM7 != xfr_ptr->M7toM4)
{
myStoreFromM7 = xfr_ptr->M7toM4;
if (LED_Blink++ >= 10) {
LED_Blink = 0;
digitalWrite(LEDB, !digitalRead(LEDB)); // flip blue LED on and off
}
}
}
}
////////////////////////////////////////////
void loop()
{
}
#endif
I couldn't get the ethernet routine to work on the M4 core like it does on the M7 core, but I may have just botched the attempt.
Arduino IoT cloud only works on the M7
This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.