{"id":16169196,"url":"https://github.com/rkalnins/rmkernel","last_synced_at":"2025-03-18T23:31:01.197Z","repository":{"id":255796720,"uuid":"424458456","full_name":"rkalnins/rmkernel","owner":"rkalnins","description":"Realtime Micro Kernel -- Event-driven Run-to-Completion RTOS with Active Objects, Timed Events, Memory Pools, and Message Queues","archived":false,"fork":false,"pushed_at":"2022-01-09T20:04:30.000Z","size":98,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T13:18:03.166Z","etag":null,"topics":["active-object","arm","cortex-m","embedded","event-driven","rtos","state-machine","stm32"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rkalnins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-04T03:19:33.000Z","updated_at":"2024-08-26T11:42:20.000Z","dependencies_parsed_at":"2024-09-07T06:08:48.729Z","dependency_job_id":null,"html_url":"https://github.com/rkalnins/rmkernel","commit_stats":null,"previous_names":["rkalnins/rmkernel"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkalnins%2Frmkernel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkalnins%2Frmkernel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkalnins%2Frmkernel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rkalnins%2Frmkernel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rkalnins","download_url":"https://codeload.github.com/rkalnins/rmkernel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243955737,"owners_count":20374373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["active-object","arm","cortex-m","embedded","event-driven","rtos","state-machine","stm32"],"created_at":"2024-10-10T03:14:18.610Z","updated_at":"2025-03-18T23:31:00.913Z","avatar_url":"https://github.com/rkalnins.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Realtime Micro Kernel\r\n\r\nSee the University of Michigan Fall 2021 EECS 373 final project [WarehouseProject-EECS373/zumo-controller](https://github.com/WarehouseProject-EECS373/zumo-controller) for usage example.\r\n\r\nIn the example project, a bit of a different source structure is used, specifically\r\n\r\n- See `src/os_port_arm_m4.c` for borrowed and slightly modified code from Quantum Leap's QP-nano (GPLv3)\r\n- `src/app_defs.h` contains message definitions\r\n- `src/main.c` contains active object declarations\r\n\r\nCurrently, `os_port_arm_m4.c` in the example project is `ports/arm-cortex-m4/port.c` in `rmkernel`.\r\n\r\n## Features\r\n\r\n- Active Objects\r\n  - Message queues\r\n  - Variable sized, custom messages\r\n- Periodic and single timed events\r\n- Memory pools\r\n- Command-based hierarchical state machine framework\r\n  - Commands\r\n  - Instant commands\r\n\r\n## Usage\r\n\r\n### Active Objects\r\n\r\n```cpp\r\n// app_definitions.h: global definitions\r\n#include \u003cos.h\u003e\r\n\r\n#define OBJECT_QUEUE_SIZE 16\r\n#define OBJECT_ID 0\r\n#define OBJECT_PRIORITY 1\r\n\r\nACTIVE_OBJECT_EXTERN(example_object, OBJECT_QUEUE_SIZE)\r\n```\r\n\r\n```cpp\r\n// main.c\r\n#include \"app_definitions.h\"\r\n\r\nACTIVE_OBJECT_DECL(example_object, OBJECT_QUEUE_SIZE)\r\n\r\nvoid ObjectEventHandler(Message_t *msg)\r\n{  \r\n}\r\n\r\nint main()\r\n{\r\n    AO_INIT(example_object, OBJECT_PRIORITY, ObjectEventHandler, OBJECT_QUEUE_SIZE, OBJECT_ID);\r\n}\r\n```\r\n\r\n### Custom Messages and Message Queues\r\n\r\n```cpp\r\n// app_definitions.h: global definitions\r\n\r\n#define EXAMPLE_MSG_ID  0x100\r\n\r\ntypedef struct ExampleMessage_s\r\n{\r\n    Messasge_t base;\r\n    uint32_t data;\r\n} ExampleMessage_t;\r\n\r\n```\r\n\r\n```cpp\r\n// isr_example.c\r\n#include \"app_definitions.h\"\r\n#include \u003cos.h\u003e\r\n#include \u003cos_msg.h\u003e\r\n\r\n\r\nvoid ExampleISR()\r\n{\r\n    STATIC_ASSERT(sizeof(ExampleMessage_t) \u003c= OS_MESSAGE_MAX_SIZE);\r\n\r\n    // send a message to example_object\r\n    ExampleMessage_t msg;\r\n    msg.base.id = EXAMPLE_MSG_ID;\r\n    msg.base.msg_size = sizeof(ExampleMessage_t);\r\n    msg.data = 543210;\r\n\r\n    MsgQueuePut(\u0026example_object, \u0026msg);\r\n}\r\n```\r\n\r\n```cpp\r\n// example_object.c\r\n#include \"app_definitions.h\"\r\n\r\nvoid ObjectEventHandler(Message_t *msg)\r\n{\r\n    if ( EXAMPLE_MSG_ID == msg-\u003eid)\r\n    {\r\n        ExampleMessage_t *ex_msg = (ExampleMessage_t*)msg;\r\n        // handle\r\n    }\r\n}\r\n```\r\n\r\n### Timed and Periodic Events\r\n\r\n```cpp\r\n#define DELAY_OR_PERIOD    500 // ms\r\n#define EVENT_TYPE         TIMED_EVENT_SINGLE_TYPE // or TIMED_EVENT_PERIODIC_TYPE\r\n\r\nTimedEventSimple_t event;\r\nMessage_t timed_event_msg = {.id = 0x101, .msg_size = sizeof(Message_t)};\r\n\r\nTimedEventSimpleCreate(\u0026event, \u0026state_ctl_ao, \u0026timed_event_msg, DELAY_OR_PERIOD, EVENT_TYPE);\r\nSchedulerAddTimedEvent(\u0026event);\r\n```\r\n\r\n### Memory Pools\r\n\r\nCan be accessed using a 16-bit key\r\n\r\n```cpp\r\n// getting a memory block pointer\r\nOSStatus_t status;\r\nuint16_t key;\r\n\r\nuint8_t* block_ptr = OSMemoryBlockNew(\u0026key, MEMORY_BLOCK_32, \u0026status); // _64, _128, _512 sizes available as well\r\n```\r\n\r\n```cpp\r\n// getting block\r\nuint8_t* buffer = OSMemoryBlockGet(key);\r\n\r\n// use, make sure to free when done\r\nOSMemoryFreeBlock(key);\r\n\r\n```\r\n\r\n### State Machine Framework\r\n\r\nWe create three commands: A, B, and C. A, B, and C are chained together in that order.\r\nCommand A's implementation is expanded. To create nested hierarchies, create `StateMachine_t`\r\nin the `CommandX_t` struct. Initialize it and then start the state machine the command in the command's `on_Start`\r\nfunction. `on_Message` should pass the given message down into the nested state machine (i.e. treat `on_Message`\r\nlike an event handler as seen in `state_controller.c`).\r\n\r\n```cpp\r\n// app_definitions.h\r\n#define DONE_MSG_ID 0x102\r\n```\r\n\r\n```cpp\r\n// cmd_a.h\r\n#include \"app_definitions.h\"\r\n#include \u003cstate_machine.h\u003e\r\n\r\ntypedef struct CommandA_s\r\n{\r\n    Command_t base;\r\n    // add state machine instance here to create nested hierarchies\r\n    // instance data\r\n} CommandA_t;\r\n\r\nextern void CmdAInit(CommandA_t *cmd, /* init data */, Command_t *next);\r\n\r\n// instance data here is optional, if anything needs to be passed down to the Command instances\r\nextern void CmdA_OnStart(CommandA_t *cmd, void *instance_data);\r\nextern bool CmdA_OnMessage(CommandA_t *cmd, Message_t *msg, void *instance_data);\r\nextern void CmdA_OnEnd(CommandA_t *cmd, void *instance_data);\r\n```\r\n\r\n```cpp\r\n// cmd_a.c\r\n\r\n#include \"cmd_a.h\"\r\n\r\nextern void CmdA_Init(CommandA_t *cmd, /* init data */, Command_t *next)\r\n{\r\n    cmd-\u003ebase.on_Start = CmdA_OnStart;\r\n    cmd-\u003ebase.on_Message = CmdA_OnMessage;\r\n    cmd-\u003ebase.on_End = CmdA_OnEnd;\r\n\r\n    // waits until OnMessage returns true, COMMAND_ON_END_INSTANT immediately goes to next state\r\n    // after running OnStart\r\n    cmd-\u003ebase.end_behavior = COMMAND_ON_END_WAIT_FOR_END;\r\n\r\n    // chain next command to this one, NULL will be end of chain\r\n    cmd-\u003ebase.next = next;\r\n\r\n    /* set init/instance data */\r\n}\r\n\r\nextern void CmdA_OnStart(CommandA_t *cmd, void *instance_data)\r\n{\r\n    // runs when command starts    \r\n}\r\n\r\nextern bool CmdA_OnMessage(CommandA_t *cmd, Message_t *msg, void *instance_data)\r\n{\r\n    // return true if done so state machine can advance to next state\r\n    return DONE_MSG_ID == msg-\u003eid;\r\n}\r\n\r\nextern void CmdA_OnEnd(CommandA_t *cmd, void *instance_data)\r\n{\r\n    // runs when command is done (after OnMessage returns true)\r\n}\r\n\r\n```\r\n\r\n```cpp\r\n// state_controller.c\r\n#include \"app_definitions.h\"\r\n#include \u003cstate_machine.h\u003e\r\n\r\n#include \"cmd_a.h\"\r\n#include \"cmd_b.h\"\r\n#include \"cmd_c.h\"\r\n\r\nstatic StateMachine_t sm;\r\n\r\nstatic CommandA_t cmd_a;\r\nstatic CommandB_t cmd_b;\r\nstatic CommandC_t cmd_c;\r\n\r\nvoid Init()\r\n{\r\n    CmdA_Init(\u0026cmd_a, /* init data */, (Command_t*) cmd_b); // b follows a\r\n    CmdB_Init(\u0026cmd_b, /* init data */, (Command_t*) cmd_c); // c follows b\r\n    CmdC_Init(\u0026cmd_c, /* init data */, (Command_t*) NULL); // NULL pointer ends sequence\r\n\r\n    StateMachineInit(\u0026sm, (Command_t*) cmd_a); // starting with cmd_a\r\n    StateMachineStart(\u0026sm, NULL);\r\n}\r\n\r\nvoid EventHandler(Message_t *msg)\r\n{\r\n    StateMachineStep(\u0026sm, msg, NULL);\r\n}\r\n```\r\n\r\n## Supported Platforms\r\n\r\nTested and developed on STM32 platforms using [`ObKo/stm32-cmake`](https://github.com/ObKo/stm32-cmake)\r\n\r\n- ARM Cortex-M4 (STM32L4R5ZI, STM32F401RE)\r\n\r\n## STM32 Board Notes\r\n\r\n### UART\r\n\r\n- For STM32L4R5ZI `VddIO2` must be enabled for LPUART to work on STM32L4R5. Enable `PWR` clock beforehand.\r\n\r\n### Clocks, Timing\r\n\r\n- `SysTick_Handler` runs at lower interrupt priority for `rmkernel`. However, STM32 HAL expects to hook into the 1ms tick. Therefore, we need an alternative to `SysTick_Handler` for the STM32 HAL. The solution was to sacrifice `TIM2` to the HAL and configure it as a 1ms clock to drive the millisecond-precision OSTime and HAL time. Can hook onto the `__weak`ly defined `HAL_IncTick`, `HAL_InitTick`, and `HAL_GetTick` to make this happen. See [`WarehouseProject-EECS373/zumo-controller/src/rmk_hal_clock_cfg.c/h`](https://github.com/WarehouseProject-EECS373/zumo-controller/blob/main/src/rmk_hal_clock_cfg.c) for an implementation of this.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkalnins%2Frmkernel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frkalnins%2Frmkernel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frkalnins%2Frmkernel/lists"}