Rmkernel
Realtime Micro Kernel -- Event-driven Run-to-Completion RTOS with Active Objects, Timed Events, Memory Pools, and Message Queues
Install / Use
/learn @rkalnins/RmkernelREADME
Realtime Micro Kernel
See the University of Michigan Fall 2021 EECS 373 final project WarehouseProject-EECS373/zumo-controller for usage example.
In the example project, a bit of a different source structure is used, specifically
- See
src/os_port_arm_m4.cfor borrowed and slightly modified code from Quantum Leap's QP-nano (GPLv3) src/app_defs.hcontains message definitionssrc/main.ccontains active object declarations
Currently, os_port_arm_m4.c in the example project is ports/arm-cortex-m4/port.c in rmkernel.
Features
- Active Objects
- Message queues
- Variable sized, custom messages
- Periodic and single timed events
- Memory pools
- Command-based hierarchical state machine framework
- Commands
- Instant commands
Usage
Active Objects
// app_definitions.h: global definitions
#include <os.h>
#define OBJECT_QUEUE_SIZE 16
#define OBJECT_ID 0
#define OBJECT_PRIORITY 1
ACTIVE_OBJECT_EXTERN(example_object, OBJECT_QUEUE_SIZE)
// main.c
#include "app_definitions.h"
ACTIVE_OBJECT_DECL(example_object, OBJECT_QUEUE_SIZE)
void ObjectEventHandler(Message_t *msg)
{
}
int main()
{
AO_INIT(example_object, OBJECT_PRIORITY, ObjectEventHandler, OBJECT_QUEUE_SIZE, OBJECT_ID);
}
Custom Messages and Message Queues
// app_definitions.h: global definitions
#define EXAMPLE_MSG_ID 0x100
typedef struct ExampleMessage_s
{
Messasge_t base;
uint32_t data;
} ExampleMessage_t;
// isr_example.c
#include "app_definitions.h"
#include <os.h>
#include <os_msg.h>
void ExampleISR()
{
STATIC_ASSERT(sizeof(ExampleMessage_t) <= OS_MESSAGE_MAX_SIZE);
// send a message to example_object
ExampleMessage_t msg;
msg.base.id = EXAMPLE_MSG_ID;
msg.base.msg_size = sizeof(ExampleMessage_t);
msg.data = 543210;
MsgQueuePut(&example_object, &msg);
}
// example_object.c
#include "app_definitions.h"
void ObjectEventHandler(Message_t *msg)
{
if ( EXAMPLE_MSG_ID == msg->id)
{
ExampleMessage_t *ex_msg = (ExampleMessage_t*)msg;
// handle
}
}
Timed and Periodic Events
#define DELAY_OR_PERIOD 500 // ms
#define EVENT_TYPE TIMED_EVENT_SINGLE_TYPE // or TIMED_EVENT_PERIODIC_TYPE
TimedEventSimple_t event;
Message_t timed_event_msg = {.id = 0x101, .msg_size = sizeof(Message_t)};
TimedEventSimpleCreate(&event, &state_ctl_ao, &timed_event_msg, DELAY_OR_PERIOD, EVENT_TYPE);
SchedulerAddTimedEvent(&event);
Memory Pools
Can be accessed using a 16-bit key
// getting a memory block pointer
OSStatus_t status;
uint16_t key;
uint8_t* block_ptr = OSMemoryBlockNew(&key, MEMORY_BLOCK_32, &status); // _64, _128, _512 sizes available as well
// getting block
uint8_t* buffer = OSMemoryBlockGet(key);
// use, make sure to free when done
OSMemoryFreeBlock(key);
State Machine Framework
We create three commands: A, B, and C. A, B, and C are chained together in that order.
Command A's implementation is expanded. To create nested hierarchies, create StateMachine_t
in the CommandX_t struct. Initialize it and then start the state machine the command in the command's on_Start
function. on_Message should pass the given message down into the nested state machine (i.e. treat on_Message
like an event handler as seen in state_controller.c).
// app_definitions.h
#define DONE_MSG_ID 0x102
// cmd_a.h
#include "app_definitions.h"
#include <state_machine.h>
typedef struct CommandA_s
{
Command_t base;
// add state machine instance here to create nested hierarchies
// instance data
} CommandA_t;
extern void CmdAInit(CommandA_t *cmd, /* init data */, Command_t *next);
// instance data here is optional, if anything needs to be passed down to the Command instances
extern void CmdA_OnStart(CommandA_t *cmd, void *instance_data);
extern bool CmdA_OnMessage(CommandA_t *cmd, Message_t *msg, void *instance_data);
extern void CmdA_OnEnd(CommandA_t *cmd, void *instance_data);
// cmd_a.c
#include "cmd_a.h"
extern void CmdA_Init(CommandA_t *cmd, /* init data */, Command_t *next)
{
cmd->base.on_Start = CmdA_OnStart;
cmd->base.on_Message = CmdA_OnMessage;
cmd->base.on_End = CmdA_OnEnd;
// waits until OnMessage returns true, COMMAND_ON_END_INSTANT immediately goes to next state
// after running OnStart
cmd->base.end_behavior = COMMAND_ON_END_WAIT_FOR_END;
// chain next command to this one, NULL will be end of chain
cmd->base.next = next;
/* set init/instance data */
}
extern void CmdA_OnStart(CommandA_t *cmd, void *instance_data)
{
// runs when command starts
}
extern bool CmdA_OnMessage(CommandA_t *cmd, Message_t *msg, void *instance_data)
{
// return true if done so state machine can advance to next state
return DONE_MSG_ID == msg->id;
}
extern void CmdA_OnEnd(CommandA_t *cmd, void *instance_data)
{
// runs when command is done (after OnMessage returns true)
}
// state_controller.c
#include "app_definitions.h"
#include <state_machine.h>
#include "cmd_a.h"
#include "cmd_b.h"
#include "cmd_c.h"
static StateMachine_t sm;
static CommandA_t cmd_a;
static CommandB_t cmd_b;
static CommandC_t cmd_c;
void Init()
{
CmdA_Init(&cmd_a, /* init data */, (Command_t*) cmd_b); // b follows a
CmdB_Init(&cmd_b, /* init data */, (Command_t*) cmd_c); // c follows b
CmdC_Init(&cmd_c, /* init data */, (Command_t*) NULL); // NULL pointer ends sequence
StateMachineInit(&sm, (Command_t*) cmd_a); // starting with cmd_a
StateMachineStart(&sm, NULL);
}
void EventHandler(Message_t *msg)
{
StateMachineStep(&sm, msg, NULL);
}
Supported Platforms
Tested and developed on STM32 platforms using ObKo/stm32-cmake
- ARM Cortex-M4 (STM32L4R5ZI, STM32F401RE)
STM32 Board Notes
UART
- For STM32L4R5ZI
VddIO2must be enabled for LPUART to work on STM32L4R5. EnablePWRclock beforehand.
Clocks, Timing
SysTick_Handlerruns at lower interrupt priority forrmkernel. However, STM32 HAL expects to hook into the 1ms tick. Therefore, we need an alternative toSysTick_Handlerfor the STM32 HAL. The solution was to sacrificeTIM2to the HAL and configure it as a 1ms clock to drive the millisecond-precision OSTime and HAL time. Can hook onto the__weakly definedHAL_IncTick,HAL_InitTick, andHAL_GetTickto make this happen. SeeWarehouseProject-EECS373/zumo-controller/src/rmk_hal_clock_cfg.c/hfor an implementation of this.
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
