System APIs
APIs to manage system processes.
Inter-processor communication (IPC)
APIs to initiate multicore processing and pass messages between the M7 and M4 cores.
For example, the following code (from examples/multi_core_ipc/
) executes on
the M7. It registers a handler to receive messages from the M4 program
right before it starts the M4. Then it starts a loop to also periodically
send messages to the M4:
namespace coralmicro {
namespace {
void HandleM4Message(const uint8_t data[kIpcMessageBufferDataSize]) {
const auto* msg = reinterpret_cast<const ExampleAppMessage*>(data);
if (msg->type == ExampleMessageType::kAck) {
printf("[M7] ACK received from M4\r\n");
}
}
[[noreturn]] void Main() {
printf("Multicore IPC Example!\r\n");
// Turn on Status LED to show the board is on.
LedSet(Led::kStatus, true);
auto* ipc = IpcM7::GetSingleton();
ipc->RegisterAppMessageHandler(HandleM4Message);
ipc->StartM4();
CHECK(ipc->M4IsAlive(500));
bool led_status = false;
while (true) {
led_status = !led_status;
printf("---\r\n[M7] Sending M4 LED_STATUS: %s\r\n",
led_status ? "ON" : "OFF");
IpcMessage msg{};
msg.type = IpcMessageType::kApp;
auto* app_msg = reinterpret_cast<ExampleAppMessage*>(&msg.message.data);
app_msg->type = ExampleMessageType::kLedStatus;
app_msg->led_status = led_status;
ipc->SendMessage(msg);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
} // namespace
} // namespace coralmicro
extern "C" [[noreturn]] void app_main(void* param) {
(void)param;
coralmicro::Main();
}
And the following is the counterpart code that runs on the M4. It registers a message handler to receive incoming messages from the M7, which simply passes another message back to the M7:
namespace coralmicro {
namespace {
void HandleM7Message(const uint8_t data[kIpcMessageBufferDataSize]) {
const auto* msg = reinterpret_cast<const ExampleAppMessage*>(data);
if (msg->type == ExampleMessageType::kLedStatus) {
printf("[M4] LED_STATUS received: %s\r\n", msg->led_status ? "ON" : "OFF");
LedSet(Led::kUser, msg->led_status);
IpcMessage ack_msg{};
ack_msg.type = IpcMessageType::kApp;
auto* app_msg = reinterpret_cast<ExampleAppMessage*>(&ack_msg.message.data);
app_msg->type = ExampleMessageType::kAck;
IpcM4::GetSingleton()->SendMessage(ack_msg);
}
}
} // namespace
} // namespace coralmicro
extern "C" void app_main(void* param) {
(void)param;
printf("M4 started.\r\n");
coralmicro::IpcM4::GetSingleton()->RegisterAppMessageHandler(
coralmicro::HandleM7Message);
vTaskSuspend(nullptr);
}
Notice that the data for IpcMessage
uses a custom message
format, which is defined in examples/multi_core_ipc/example_message.h
like
this:
enum class ExampleMessageType : uint8_t {
kLedStatus,
kAck,
};
struct ExampleAppMessage {
ExampleMessageType type;
bool led_status;
} __attribute__((packed));
For information about how to get started with multicore processing with the M4, see the guide to create a multicore app.
-
namespace
coralmicro
-
class
Ipc
¶ - #include <ipc.h>
Do not instantiate this class. It provides shared IPC functions for
IpcM7
andIpcM4
.Public Functions
-
void
SendMessage
(const IpcMessage &message)¶ Sends an IPC message to the other core.
- Parameters
message – The message to send.
-
inline void
RegisterAppMessageHandler
(AppMessageHandler handler)¶ Sets a callback function to process incoming IPC messages.
- Parameters
handler – The function to receive incoming messages.
Public Types
-
using
AppMessageHandler
= std::function<void(const uint8_t data[kIpcMessageBufferDataSize])>¶ The function type to handle incoming IPC messages, which must be given to
RegisterAppMessageHandler()
viaIpcM7
orIpcM4
, respective of which core is receiving the messages.The function receives the IPC message as a byte array of size
kIpcMessageBufferDataSize
(127).
-
void
-
class
-
namespace
coralmicro
-
class
IpcM7
: public coralmicro::Ipc¶ - #include <ipc_m7.h>
Singleton object that provides IPC on the M7 core so it can start the M4 core and relay messages with the M4.
The M4 can not operate independent from the M7: the M7 must start the M4 with
StartM4()
, and then the M7 task may suspend itself.The M7 can send messages to the M4 with
SendMessage()
and register a callback to receive messages from the M4 withRegisterAppMessageHandler()
.Public Functions
-
void
StartM4
()¶ Starts the M4 core, invoking the
app_main()
function in the executable declared with the CMakeadd_executable_m4()
command.
-
bool
M4IsAlive
(uint32_t millis)¶ Checks if the M4 core is alive.
- Parameters
millis – The amount of time (in milliseconds) to wait for a response from the M4 core.
- Returns
True if the M4 core signals that it is ready to preform a task or preforming a task within the millis time limit, false otherwise.
Public Static Functions
-
static inline IpcM7 *
GetSingleton
()¶ Gets the
IpcM7
singleton that can start the M4 and perform IPC with the M4.- Returns
A reference to the singleton
IpcM7
object.
-
static bool
HasM4Application
()¶ Checks if the M4 core is running a process.
- Returns
True if the M4 is running a process, false otherwise.
-
void
-
class
-
namespace
coralmicro
-
class
IpcM4
: public coralmicro::Ipc¶ - #include <ipc_m4.h>
Singleton object that provides IPC on the M4 core to relay messages with the M7 core.
The M4 can not operate independent from the M7: the M7 must start the M4 with
IpcM7::StartM4()
, and then the M7 task may suspend itself.The M4 can send messages to the M7 with
SendMessage()
and register a callback to receive messages from the M7 withRegisterAppMessageHandler()
.
-
class
-
namespace
coralmicro
-
struct
IpcMessage
¶ - #include <ipc_message_buffer.h>
A message to be sent with
Ipc::SendMessage()
(using eitherIpcM4
orIpcM7
).The
message
union is designed to support two types of message, but you should always use an “app” message. So you should settype
toIpcMessageType::kApp
and then populatedata
with a custom data format that both processes know how to read/write.For an example, see
examples/multi_core_ipc/
.Public Members
-
IpcMessageType
type
¶ Identifier for the type of message (apps should always use
kApp
).
-
IpcSystemMessage
system
¶ Internal use only.
-
uint8_t
data
[kIpcMessageBufferDataSize
]¶ A byte array, which should be a structured data format that’s defined by the app, but limited to size
kIpcMessageBufferDataSize
(127 bytes).
-
union coralmicro::IpcMessage::[anonymous]
message
¶ The message to be sent (
system
ordata
).
-
IpcMessageType
Functions
-
struct coralmicro::IpcMessage __attribute__ ((packed))
Enums
-
enum class
IpcMessageType
: uint8_t¶ The types of message that may be sent in an
IpcMessage
.Values:
-
enumerator
kSystem
¶ Internal use only.
-
enumerator
kApp
¶ A custom app message with a byte array of size
kIpcMessageBufferDataSize
(127).
-
enumerator
-
struct
Mutex
APIs to ensure mutual-exclusive access to resources.
-
namespace
coralmicro
-
class
MulticoreMutexLock
¶ - #include <mutex.h>
Defines a mutex lock that is unique across MCU cores, ensuring safe handling of any resources that are shared between the M7 and M4 cores.
Public Functions
-
inline explicit
MulticoreMutexLock
(uint8_t gate)¶ Acquires a multi-core mutex lock using a semaphore gate number.
Any code following this request for the mutex lock is blocked until the lock is successfully acquired, and each unique mutex lock can be held by only one task at a time, regardless of which MCU core the task is on. Thus, code-blocks that use the same gate to get a
MulticoreMutexLock
will have thread-safe variables, even across cores.After execution leaves the code block where the lock is acquired, the mutex lock is automatically released.
- Parameters
gate – An integer representing a unique semaphore gate. Must be within range of the maximum number of gates available on the hardware (16).
-
inline explicit
-
class
MutexLock
¶ - #include <mutex.h>
Defines a mutex lock for the active MCU core, ensuring safe handling of any resources that are shared between tasks.
Public Functions
-
inline explicit
MutexLock
(SemaphoreHandle_t sema)¶ Acquires a mutex lock using a semaphore.
Any code following this request for the mutex lock is blocked until the lock is successfully acquired, and each unique mutex lock can be held by only one task at a time (on the same MCU core). Thus, code-blocks that use the same semaphore to get a
MutexLock
(on the same MCU core) will have thread-safe variables between said code-blocks.After execution leaves the code block where the lock is acquired, the mutex lock is automatically released.
- Parameters
sema – The SemaphoreHandle to define a unique mutex lock.
-
inline explicit
-
class
Watchdog
APIs to create watchdog timers that monitor the MCU behavior and reset it when it appears to be malfunctioning.
-
namespace
coralmicro
-
struct
WatchdogConfig
¶ - #include <watchdog.h>
Represents a watchdog config, this config needs to be passed into
WatchdogStart()
.Public Members
-
int
timeout_s
¶ Number of seconds before the watchdog resets the CPU (unless pet).
-
int
pet_rate_s
¶ The rate to refresh the watchdog timer in seconds.
-
bool
enable_irq
¶ Set to true to enable the watchdog interrupt. If watchdog interrupt is enabled, be sure to implement and extern WDOG1_IRQHandler. Otherwise, you will call DefaultISR.
-
int
irq_s_before_timeout
¶ When enable_irq is set, time remaining before watchdog expiration that will trigger an interrupt (e.g. timeout_s = 10, irq_s_before_timeout=2 means the interrupt will fire at 8 seconds).
-
int
Functions
-
void
WatchdogStart
(const WatchdogConfig &config)¶ Enables watchdog and starts SW timer to refresh at a given rate.
- Parameters
config – The watchdog config to enable watchdog.
-
void
WatchdogStop
()¶ Stops SW refresh timer, disables watchdog.
-
struct
Reset
APIs to reset the Dev Board Micro into different states and read reset stats.
-
namespace
coralmicro
-
struct
ResetStats
¶ - #include <reset.h>
Represents reset stats.
Functions
-
void
ResetToBootloader
()¶ Reset the board to bootloader mode.
-
void
ResetToFlash
()¶ Reset the board to flash mode.
-
void
ResetStoreStats
()¶ Stores the current reset stats.
-
ResetStats
ResetGetStats
()¶ Gets the current reset stats.
-
struct
Is this content helpful?