Hi, I am trying to use a shared variable accessible between 2 cores (M7 and M4) of arduino GIGA but I don't know what I am doing wrong:
#include <SPI.h>
#include <RPC.h>
const int sensor_1_NC = 70; // Output pin of Sensor 1 (normally closed).
int n = 0;
bool S1_detecting = false;
bool DETECT_S1 = false;
String currentCPU() {
if (HAL_GetCurrentCPUID() == CM7_CPUID) {
return "M7";
} else {
return "M4";
}
}
void resetFunc2(void) {
unsigned long *registerAddr;
registerAddr = (unsigned long *)0xE000ED0C; //Writes to the AIRCR register of the stm32h747 to software restet the arduino
//It is a 32 bit register set bit 2 to request a reset and write 0x05FA on the first bytes to perform
// the write
//See Arm® v7-M Architecture Reference Manual for more information
//Serial.println(*registerAddr,HEX); //For debug
//*registerAddr = *registerAddr | (unsigned long) 0x05FA0304;
*registerAddr = (unsigned long) 0x05FA0304;
//Serial.println(*registerAddr,HEX); //For debug
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(3000);
pinMode(sensor_1_NC, INPUT_PULLUP);
RPC.begin(); // boots core M4.
}
void loop() {
// put your main code here, to run repeatedly:
if(currentCPU() == "M7"){ // Code for M7 core.
delay(3000);
Serial.println("Detected " + String(n) + " times.");
if(n > 10){ // If it counts 10 detections, reset the board.
resetFunc2();
}
} // End of M7 code.
if(currentCPU() == "M4"){ // Code for M4 core.
if((digitalRead(sensor_1_NC) == 0) && S1_detecting == false){ // Sensor gives 0 cuando detects y 1 in other case.
DETECT_S1 = true;
S1_detecting = true;
n = n + 1; // Add one more detection to the variable used in Main core.
}else{DETECT_S1 = false;}
if(S1_detecting == true && (digitalRead(sensor_1_NC) == 1)){ // If it has stopped detecting, we can prepare for another detection.
S1_detecting = false;
}
} // End of M4 code.
}
I have read I sould use mutex. Is there a mutex library for arduino? Is this applicable to my problem of shared variables?
/*
* Copyright (c) 2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Copyright (c) 2020 Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*/
#include "mbed.h"
#include "LibPrintf.h"
#include "rtos.h"
using namespace rtos;
#include "mbed.h"
#include "LibPrintf.h"
#include "rtos.h"
#include "DigitalOut.h"
using namespace rtos;
Mutex mutex;
ConditionVariable cond(mutex);
// These variables are protected by locking mutex
uint32_t counter = 0;
bool done = false;
void worker_thread()
{
mutex.lock();
do {
printf("Worker: Count %lu\r\n", counter);
// Wait for a condition to change
cond.wait();
} while (!done);
printf("Worker: Exiting\r\n");
mutex.unlock();
}
int main()
{
Thread thread;
thread.start(worker_thread);
for (int i = 0; i < 5; i++) {
mutex.lock();
// Change count and signal this
counter++;
printf("Main: Set count to %lu\r\n", counter);
cond.notify_all();
mutex.unlock();
ThisThread::sleep_for(1000);
}
mutex.lock();
// Change done and signal this
done = true;
printf("Main: Set done\r\n");
cond.notify_all();
mutex.unlock();
thread.join();
}
void loop() {
// put your main code here, to run repeatedly:
}
The key was to include:
#include "mbed.h"
#include "rtos.h"
using namespace rtos;
How could I share a string variable between 2 cores in arduino GIGA using RPC protocol?
I have tried sharing a string with the following code:
#include <RPC.h>
volatile char arraychar[12]; // common variable between cores.
String message = "";
String emptystr = "";
String currentCPU() {
if (HAL_GetCurrentCPUID() == CM7_CPUID) {
return "M7";
} else {
return "M4";
}
}
void setup() {
if(currentCPU() == "M7") {
Serial.begin(9600);
while(!Serial);
RPC.begin();
message = "helloworld!"; // 11 characters.
RPC.println(message); // Prints the message in the RPC Stream for M4 to receive it.
delay(4000);
}
if(currentCPU() == "M4") {
RPC.begin();
String bufferM4 = "";
while (RPC.available()) { // Read the setream to get the data sent by M7.
bufferM4 += (char)RPC.read();
}
strcpy(arraychar, bufferM4);
delay(4000);
}
}
void loop() {
if(currentCPU() == "M7") { // In loop, M7 prints everything sent by M4 in loop every second.
String buffer = "";
while (RPC.available()) {
buffer += (char)RPC.read();
} // When it has read the stream, M7 deletes the variable.
emptystr.toCharArray(arraychar, 23); // Clears the array of char.
if (buffer.length() > 0) {
Serial.print(buffer);
}
delay(1000);
}
if(currentCPU() == "M4") {
delay(250);
if(String(arraychar) == ""){ // If the array of char has been emptied, it can now send new data.
RPC.println(String(arraychar)); // Prints the array char received by M7 in setup.
arraychar = "anothermssg"; // I will be changing this message in every iteration of loop (this will be sensor reading info).
}
}
}
But I get the following error:
Compilation error: invalid conversion from 'volatile char*' to 'char*' [-fpermissive]
How can I overwrite a shared variable from M4 to M7 and vice versa? I know I should use mutex but I can't seem to make "Mutex.h" library work in my IDE.
Thanks a lot for your help. I am studying the information you have given me to see if I can solve my problem.
Some extra information from my case: I need to send a string from M4 to M7 containing 300 characters every 3 seconds and I need to read this string and copy it to a string variable located in M7. Then I need to erase the message so M4 can send another message.
Think this is probably the easiest way to do it based on the previous posts:
#include <RPC.h>
#include "DigitalOut.h"
#include "SDRAM.h"
struct DATA_FRAME_RETURN {
char debugStringData[300];
} __attribute__((aligned(8)));
struct RETURN_MSG_READY_FLAG {
volatile bool msg_ready = false;
} __attribute__((aligned(8)));
//SDRAM Pointers
const uint32_t SDRAM_START_ADDRESS_4 = ((uint32_t)0x38000000); //USING THE AHB SRAM4 DOMAIN SPACE
volatile uint32_t *sdramMemory = (uint32_t*)0x38000000;
RETURN_MSG_READY_FLAG* return_msg_ready_sdram = (RETURN_MSG_READY_FLAG*)(sdramMemory);
DATA_FRAME_RETURN* data_frame_return_sdram = (DATA_FRAME_RETURN*)(sdramMemory + (sizeof(RETURN_MSG_READY_FLAG)) / sizeof(uint32_t));
#ifdef CORE_CM4 // Start M4 Core programming
#include "elapsedMillis.h"
elapsedMillis sendTime;
String data_values = "This is a test of the emergency broadcasting system. The litte brown fox jumped over the moon";
void setup() {
RPC.begin();
delay(1000);
pinMode(LED_RED, OUTPUT);
for (uint8_t i = 0; i < 5; i++ ) {
digitalWrite(LED_RED, !digitalRead(LED_RED)); // flip blue LED on and off
delay(250);
}
sendMsgToM7("M4 Started.....", true);
delay(100);
}
void loop() {
if(sendTime > 3000) {
sendMsgToM7(data_values, true);
sendTime = 0;
}
receiveMsgFromM7();
}
void sendMsgToM7(String debugStringData, bool blocking) {
do {
if (!return_msg_ready_sdram->msg_ready) { //if 0 can send data
DATA_FRAME_RETURN dataForM7;
debugStringData.toCharArray(dataForM7.debugStringData, sizeof(dataForM7.debugStringData));
*data_frame_return_sdram = dataForM7;
return_msg_ready_sdram->msg_ready = true; //1 lets m4 know it can read data
return; //break out of do->while loop
} else if (blocking) {
delayMicroseconds(5); //hang out and try again, in a little bit
}
} while (blocking);
}
void receiveMsgFromM7() {
if (return_msg_ready_sdram->msg_ready) { //true indicates it can read by M7
DATA_FRAME_RETURN dataFromM7 = *data_frame_return_sdram;
//telemetry.printTelemetry(String(dataFromM4.debugStringData), TELEMETRY_LOW_PRIORITY);
//USERIAL->println(String(dataFromM4.debugStringData));
//for testing
return_msg_ready_sdram->msg_ready = false; //false indicates data has been read by M4
sendMsgToM7(String(dataFromM7.debugStringData), true);
}
}
#endif //end CORE_CM4
#ifdef CORE_CM7 // Start M7 Core programming
bool hasReceivedMsgFromM4 = false;
void setup() {
while (!Serial && millis() < 5000) {}
Serial.begin(115200);
RPC.begin();
delay(100);
SDRAM.begin(SDRAM_START_ADDRESS_4);
delay(100);
pinMode(LED_BLUE, OUTPUT);
for (uint8_t i = 0; i < 5; i++ ) {
digitalWrite(LED_BLUE, !digitalRead(LED_BLUE)); // flip blue LED on and off
delay(250);
}
}
void loop() {
receiveMsgFromM4();
}
void sendMsgToM4(String debugStringData, bool blocking) {
do {
if (!return_msg_ready_sdram->msg_ready) { //if 0 can send data
DATA_FRAME_RETURN dataForM4;
debugStringData.toCharArray(dataForM4.debugStringData, sizeof(dataForM4.debugStringData));
*data_frame_return_sdram = dataForM4;
return_msg_ready_sdram->msg_ready = true; //1 lets m7 know it can read data
return; //break out of do->while loop
} else if (blocking) {
delayMicroseconds(5); //hang out and try again, in a little bit
}
} while (blocking);
}
void receiveMsgFromM4() {
if (return_msg_ready_sdram->msg_ready) { //true indicates it can read by M7
DATA_FRAME_RETURN dataFromM4 = *data_frame_return_sdram;
//telemetry.printTelemetry(String(dataFromM4.debugStringData), TELEMETRY_LOW_PRIORITY);
return_msg_ready_sdram->msg_ready = false; //false indicates data has been read by M7
Serial.println(String(dataFromM4.debugStringData));
// put your copy to local string here.........
}
}
#endif //end CORE_CM7
I think I may have found a simpler way to comunicate a single string from one core to another:
#ifdef CORE_CM4 // Start M4 programming
#include "RPC.h"
String timefromserver="";
void setup() {
RPC.begin();
while(timefromserver == ""){ // Continues in loop until it receives the time from server from M7.
String buffer = "";
while (RPC.available()) {
buffer += (char)RPC.read();
}
if (buffer.length() > 0) { // Prints what it has received + M4 to make Serial print in M7 that M4 has received the message.
RPC.print(buffer + " from M4.");
}
timefromserver = String(buffer);
}
// If it has reached here, it has received the message from M7.
}
void loop(){
RPC.println();
}
#endif
#ifdef CORE_CM7 // Start M7 programming
#include "RPC.h"
String timefromserver="";
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
while(!Serial);
RPC.begin();
while(!RPC);
// Gets time from server.
timefromserver = "20231122 12:49:16.316";
// Prints to RPC stream so M4 can receive it.
RPC.println(timefromserver);
delay(1500); // Waits for M4 to receive, read and extract it.
// Reads RPC stream to confirm that M4 has receive the string 'timefromserver'.
String buffer = "";
while (RPC.available()) {
buffer += (char)RPC.read();
}
if (buffer.length() > 0) {
Serial.println(buffer);
}
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println("Has reached loop");
delay(2000);
}
#endif
but I keep getting this in serial port:
09:45:29.893 -> Has reached loop
09:45:31.945 -> Has reached loop
09:45:34.032 -> Has reached loop
09:45:36.116 -> Has reached loop
09:45:38.199 -> Has reached loop
09:45:40.257 -> Has reached loop
09:45:42.332 -> Has reached loop
Any idea why y can't read RPC stream from M4 and store the string in an M4 global variable?
Why does it only print the last part inside loop() function?
I only want to share this variable once, at the beggining of M4 code. I tried to create a sort of loop in the setup in the code above, but it doesn't work and I can't see anything on the serial monitor.
After a few days of frustration, this is the code I use that works perfectly for my needs. You could redefine the struct that is used to suit whatever you want to transmit from one core to another. In my case I just want to read info on the M7 that is captured by the M4 from a CAN stream.
GigaDisplay_Data_Definition.h
#ifndef GIGADISPLAY_DATA_DEFINITION_H
#define GIGADISPLAY_DATA_DEFINITION_H
struct CanDataFrame {
int rpm = 0;
int speed = 0;
int iat = 0;
int ect = 0;
int egt = 0;
int cont = 0;
} __attribute__((aligned(8)));
#endif //GIGADISPLAY_DATA_DEFINITION_H
M7 Code
// Lets make sure this code is loaded only into to core M7
#ifndef CORE_CM7
#error Expected to run on core M7
#endif
#include <RPC.h>
#include <GigaDisplay_Data_Definition.h>
// Here we will store the pointer to the shared memory
CanDataFrame* sharedCanDataPtr = nullptr;
// Flag set true when the address ir recieved.
bool addressConfigured = false;
// Procedure to be called as RPC
// Recieves an address that points to the value used by the M4 core
void setDataPointer(uint32_t dataAddress) {
// Newline
Serial.println("");
// DEBUG: lets print the memory address
Serial.println("Recieved memory address: " + String(dataAddress, HEX));
// Lets store the value on the global pointer we created.
sharedCanDataPtr = (CanDataFrame*) dataAddress;
// Lets print the value that is stored on the pointer address
//Serial.println("Value stored in address: " + String((int) *rpm));
// Lets set this flag to true so the loop can continue monitoring the recieved address
addressConfigured = true;
return;
}
void setup() {
int startTime = millis();
Serial.begin(115200);
while (!Serial or startTime + 4000 < millis()) ;
// Set the RPC call
RPC.bind("setDataPointer", setDataPointer);
// Lets begin core M4
RPC.begin();
Serial.print("Waiting for Address");
}
void loop() {
// If we already recieved an address from core M4
if(addressConfigured) {
// Lets clean the cache on this address for the M7 core
CanDataFrame newData = *sharedCanDataPtr;
SCB_CleanInvalidateDCache_by_Addr(sharedCanDataPtr, sizeof(newData));
Serial.println("Rpm: " + String(newData.rpm));
Serial.println("Speed: " + String(newData.speed));
Serial.println("Iat: " + String(newData.iat));
Serial.println("Ect: " + String(newData.ect));
Serial.println("Egt: " + String(newData.egt));
Serial.println("Cont: " + String(newData.cont));
Serial.println("");
} else {
Serial.print(".");
}
delay(10);
}
M4 Code
// Lets make sure this code is loaded only into to core M4
#ifndef CORE_CM4
#error Expected to run on core M4
#endif
#include <RPC.h>
#include <GigaDisplay_Data_Definition.h>
// This is the struct that will be shared with the other core.
CanDataFrame sharedCanData;
// Used to blink the led and make sure that the core started and is running.
bool ledStatus = false;
void setup() {
pinMode(LEDG, OUTPUT);
// Lets start RPC (is it needed for RPC.Call)
RPC.begin();
delay(100); //needed or core hangs
RPC.call("setDataPointer", (int32_t)&sharedCanData); // we call the procedure on core M7 and pass the address of my loopCount variable.
//delay(100); //needed or core hangs
}
void loop() {
static int loopNum = 1;
// Lets blink turn on or off the led
if(ledStatus) {
digitalWrite(LEDG, LOW);
} else {
digitalWrite(LEDG, HIGH);
}
ledStatus = !ledStatus;
// Incremente loopCount (should change the value on memory)
CanDataFrame newData;
newData.rpm = (sharedCanData.rpm + 112) % 7200;
newData.speed = (sharedCanData.speed + 3) % 200;
newData.iat = 20 + (loopNum % 7);
newData.ect = 85 + (loopNum % 19);
newData.egt = 800 + (loopNum % 197);
newData.cont = loopNum++;
// Copiamos la nueva info al puntero.
memcpy(&sharedCanData, &newData, sizeof(newData));
// sharedCanData = newData;
// Lets delay the loop a different amounth each time so we can be cetain that loopCount is incrementing.
delay(5);
}
Thanks for your help! I will try this code and update any news or issues I encounter.
By the way, Can we only share int variables? Could we share a string of 23 characters?
thanks for all your contributions to this thread, I have solved my problem of sharing a string variable using two integers as flags (one for each core) which signal when each core is ready to receive/send and the string is printed from M4 using RPC.print, then read and stored in M7.