{"id":31781653,"url":"https://github.com/jzimnol/avrtos","last_synced_at":"2026-05-19T14:08:49.542Z","repository":{"id":198909774,"uuid":"701725977","full_name":"JZimnol/avrtos","owner":"JZimnol","description":"AVRTOS - Enable concurrency on your AVR!","archived":false,"fork":false,"pushed_at":"2024-06-29T15:24:18.000Z","size":52,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-10T08:56:19.462Z","etag":null,"topics":["arduino","atmega","avr","rtos"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JZimnol.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}},"created_at":"2023-10-07T11:39:03.000Z","updated_at":"2025-01-06T13:48:36.000Z","dependencies_parsed_at":null,"dependency_job_id":"a5688dcc-6a01-4bfd-add1-56d8924e894e","html_url":"https://github.com/JZimnol/avrtos","commit_stats":null,"previous_names":["jzimnol/avrtos"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/JZimnol/avrtos","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JZimnol%2Favrtos","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JZimnol%2Favrtos/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JZimnol%2Favrtos/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JZimnol%2Favrtos/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JZimnol","download_url":"https://codeload.github.com/JZimnol/avrtos/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JZimnol%2Favrtos/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33219463,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-19T07:54:09.561Z","status":"ssl_error","status_checked_at":"2026-05-19T07:54:08.508Z","response_time":58,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["arduino","atmega","avr","rtos"],"created_at":"2025-10-10T08:56:16.562Z","updated_at":"2026-05-19T14:08:49.511Z","avatar_url":"https://github.com/JZimnol.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AVRTOS - Enable concurrency on your AVR!\r\n\r\n## What is an AVRTOS?\r\n\r\n**AVRTOS** is a fun project undertaken during my free time, with the aim of\r\nenabling concurrency on AVR boards. I haven't read any books about RTOSes or how\r\nto create one. I just wanted to code a little. The primary goal was to keep it\r\nsimple and not resource-intensive. AVRTOS supports bare-C development, for\r\nexample, using Microchip Studio, as well as C++, for instance, with PlatformIO.\r\nIn its most basic version, it does not support the CMake build system.\r\n\r\nOne of the possibilities was to make the API similar to, for example, ZephyrRTOS\r\none. I didn't want to do that, even though it makes the code unportable to\r\nnon-AVR platforms. The main goal was the simplicity and the ability to\r\nunderstand how the basic mechanisms of RTOSes work. AVRTOS has been documented\r\nsufficiently that it can be understood and developed by Embedded Community.\r\n\r\nAVRTOS has been tested using `toolchain-atmelavr v5.4.0` (C compiler) as well as\r\n`toolchain-atmelavr v7.3.0` (C++ compiler). It does a few tricks using `inline\r\nassembly` during scheduling processes. Currently, the only supported board is\r\nthe `ATmega328p`, but support for other boards can be easily added (see [Adding\r\ncustom AVR board](#adding-custom-avr-board)).\r\n\r\n## Compiling\r\n\r\n### Microchip Studio\r\n\r\nTo run and build a sample app using Microchip Studio, follow these steps:\r\n* copy all files from the `src/` directory to your project directory\r\n* add these files to your Microchip Studio project\r\n* create main.(c|cpp) in your project\r\n* compile the project\r\n* use your own programmer to program the board or utilize the built-in app\r\n  simulator\r\n\r\n### PlatformIO\r\n\r\nTo run and build a sample app using PlatformIO, follow these steps:\r\n* clone this repository to the \u003cproject_name\u003e/lib directory\r\n* PlatformIO will compile AVRTOS into a static library and link it into the\r\n  executable file.\r\n\r\n### Using CMake\r\n\r\nAt this time, the AVRTOS project does not support the CMake build system.\r\nCreating a platform-independent CMake build system can be challenging, so this\r\ntask has been postponed. Only basic unit tests are supported.\r\n\r\n## Adding custom AVR board\r\n\r\nFor now, only `ATmega328p` board is supported, which uses `TIMER0_COMPA_vect`\r\n(scheduler), `TIMER2_COMPA_vect` (delays) and `USART_UDRE_vect` (logger)\r\ninterrupts.\r\n\r\nTo support another AVR board, add proper `avrtos_\u003cboard_name\u003e.c` file to\r\n`src/boards/` directory. The whole file should be guarded with `#if\r\ndefined(\u003cboard_name\u003e)` directive (e.g. `#if defined(__AVR_ATmega328P__)` and\r\nshould implement functions declared in `src/boards/avrtos_board_impl.h` file.\r\n\r\nThis is not the most elegant possible solution as interrupt vectors could be\r\nunified using defines, ~~BUT IT WORKS~~ but this can be improved in future\r\nversions of AVRTOS.\r\n\r\n## Examples\r\n\r\nAVRTOS in its basic form supports: `concurrent scheduling, task-specific\r\narguments, non-blocking delays, task GPIO tracing, asynchronous UART logger and\r\nmutexes`. Most of the features can be switched on/off using `avrtos_config.h`\r\nfile. For instance, if you wish to exclude the asynchronous logger from the\r\ncode, simply comment out the `#define AVRTOS_WITH_ASYNCHRONOUS_LOGGER` line.\r\nThis allows you to save valuable FLASH or RAM space.\r\n\r\nSetting the `AVRTOS_CPU_CLOCK_FREQUENCY` value correctly in the\r\n`avrtos_config.h` file is crucial to ensure accurate scheduling algorithms and\r\ndelays\r\n\r\n### Concurrent scheduling example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task3);\r\nAVRTOS_STACK_DEFINE(stack3, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nvoid thread1(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 0 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 0);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nvoid thread2(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 1 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 1);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nvoid thread3(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 2 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 2);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n    (void) avrtos_task_create(\u0026task1, thread1, stack1, sizeof(stack1), NULL);\r\n    (void) avrtos_task_create(\u0026task2, thread2, stack2, sizeof(stack2), NULL);\r\n    (void) avrtos_task_create(\u0026task3, thread3, stack3, sizeof(stack3), NULL);\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n\r\n### Task-specific arguments example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\ntypedef struct {\r\n    uint64_t sleep_ms;\r\n    uint8_t pin;\r\n} example_args_t;\r\n\r\nvoid thread(void *_arg) {\r\n    volatile example_args_t *arg = (example_args_t *) _arg;\r\n    while (1) {\r\n        /* toggle pin defined in passed argument */\r\n        PORTD ^= (1 \u003c\u003c arg-\u003epin);\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n\r\n    example_args_t arg1 = { .pin = 5 };\r\n    example_args_t arg2 = { .pin = 6 };\r\n\r\n    (void) avrtos_task_create(\u0026task1, thread, stack1, sizeof(stack1), \u0026arg1);\r\n    (void) avrtos_task_create(\u0026task2, thread, stack2, sizeof(stack2), \u0026arg2);\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n\r\n### Non-blocking delays example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n#include \"avrtos_delay.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\ntypedef struct {\r\n    uint64_t sleep_ms;\r\n    uint8_t pin;\r\n} example_args_t;\r\n\r\nvoid thread(void *_arg) {\r\n    volatile example_args_t *arg = (example_args_t *) _arg;\r\n    while (1) {\r\n        /* toggle pin defined in passed argument */\r\n        PORTD ^= (1 \u003c\u003c arg-\u003epin);\r\n        /* perform argument-specific non-blocking delay */\r\n        avrtos_delay_ms(arg-\u003esleep_ms);\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n\r\n    example_args_t arg1 = { .sleep_ms = 300, .pin = 5 };\r\n    example_args_t arg2 = { .sleep_ms = 400, .pin = 6 };\r\n\r\n    (void) avrtos_task_create(\u0026task1, thread, stack1, sizeof(stack1), \u0026arg1);\r\n    (void) avrtos_task_create(\u0026task2, thread, stack2, sizeof(stack2), \u0026arg2);\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n\r\nConnecting logic analyzer to the defined pins, we get the following output:\r\n\r\n\u003cimg src=\"./doc/images/avrtos_delay_example.png\" alt=\"Delay example\"/\u003e\r\n\r\n### Task GPIO tracing example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n#include \"avrtos_gpio_trace.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\nAVRTOS_GPIO_TRACE_DEFINE(trace1, D, 5);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\nAVRTOS_GPIO_TRACE_DEFINE(trace2, D, 6);\r\n\r\nAVRTOS_TASK_DEFINE(task3);\r\nAVRTOS_STACK_DEFINE(stack3, AVRTOS_MINIMAL_STACK_SIZE);\r\nAVRTOS_GPIO_TRACE_DEFINE(trace3, D, 7);\r\n\r\n// for Arduino UNO special macros can be used\r\n// example: AVRTOS_GPIO_TRACE_DEFINE_ARDUINO_UNO(trace1, 13);\r\n// example: AVRTOS_GPIO_TRACE_DEFINE_ARDUINO_UNO(trace1, A0);\r\n\r\nvoid thread1(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 0 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 0);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nvoid thread2(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 1 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 1);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nvoid thread3(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* toggle pin 2 of PORTD */\r\n        PORTD ^= (1 \u003c\u003c 2);\r\n        /* do stuff */\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n    (void) avrtos_task_create(\u0026task1, thread1, stack1, sizeof(stack1), NULL);\r\n    (void) avrtos_task_create(\u0026task2, thread2, stack2, sizeof(stack2), NULL);\r\n    (void) avrtos_task_create(\u0026task3, thread3, stack3, sizeof(stack3), NULL);\r\n\r\n    avrtos_gpio_trace_install(\u0026task1, \u0026trace1);\r\n    avrtos_gpio_trace_install(\u0026task2, \u0026trace2);\r\n    avrtos_gpio_trace_install(\u0026task3, \u0026trace3);\r\n\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n\r\nConnecting logic analyzer to the defined pins, we get the following output:\r\n\r\n\u003cimg src=\"./doc/images/avrtos_gpio_trace_example.png\" alt=\"GPIO trace example\"/\u003e\r\n\r\n### Asynchronous UART logger example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n#include \"avrtos_delay.h\"\r\n#include \"avrtos_logger.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\ntypedef struct {\r\n    uint64_t sleep_ms;\r\n    uint8_t pin;\r\n} example_args_t;\r\n\r\nvoid thread(void *_arg) {\r\n    volatile example_args_t *arg = (example_args_t *) _arg;\r\n    while (1) {\r\n        /* toggle pin defined in passed argument */\r\n        PORTD ^= (1 \u003c\u003c arg-\u003epin);\r\n        /* write UART message using interrupts */\r\n        avrtos_log(test, INFO, \"Toggled pin: %d, now delay: %lu ms\", arg-\u003epin, arg-\u003esleep_ms);\r\n        /* perform argument-specific non-blocking delay */\r\n        avrtos_delay_ms(arg-\u003esleep_ms);\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n    example_args_t arg1 = {.sleep_ms = 300, .pin = 5};\r\n    example_args_t arg2 = {.sleep_ms = 400, .pin = 6};\r\n\r\n    (void) avrtos_task_create(\u0026task1, thread, stack1, sizeof(stack1), \u0026arg1);\r\n    (void) avrtos_task_create(\u0026task2, thread, stack2, sizeof(stack2), \u0026arg2);\r\n\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n\r\nConnecting UART pins to e.g. UART-to-USB converter, we get the following output:\r\n\r\n```\r\nINFO [test] Toggled pin: 6, now delay: 400 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\nINFO [test] Toggled pin: 6, now delay: 400 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\nINFO [test] Toggled pin: 6, now delay: 400 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\nINFO [test] Toggled pin: 6, now delay: 400 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\nINFO [test] Toggled pin: 5, now delay: 300 ms\r\n...\r\n```\r\n\r\n### Mutexes example\r\n\r\n```c\r\n#include \u003cavr/io.h\u003e\r\n\r\n#include \"avrtos_init.h\"\r\n#include \"avrtos_mutex.h\"\r\n\r\nAVRTOS_TASK_DEFINE(task1);\r\nAVRTOS_STACK_DEFINE(stack1, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_TASK_DEFINE(task2);\r\nAVRTOS_STACK_DEFINE(stack2, AVRTOS_MINIMAL_STACK_SIZE);\r\n\r\nAVRTOS_MUTEX_DEFINE(example_mutex);\r\n\r\nint some_counter = 0;\r\n\r\nvoid critical_section(void) {\r\n    avrtos_mutex_lock(\u0026example_mutex);\r\n    some_counter++;\r\n    avrtos_mutex_unlock(\u0026example_mutex);\r\n}\r\n\r\nvoid thread1(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* do stuff */\r\n        // ...\r\n        critical_section();\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nvoid thread2(void *_arg) {\r\n    (void) _arg;\r\n    while (1) {\r\n        /* do stuff */\r\n        // ...\r\n        critical_section();\r\n    }\r\n    /* undefined behaviour - the code here should be unreachable */\r\n}\r\n\r\nint main(void) {\r\n    (void) avrtos_task_create(\u0026task1, thread1, stack1, sizeof(stack1), NULL);\r\n    (void) avrtos_task_create(\u0026task2, thread2, stack2, sizeof(stack2), NULL);\r\n    avrtos_scheduler_start();\r\n\r\n    while (1) {\r\n        /* code unreachable */\r\n    }\r\n}\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjzimnol%2Favrtos","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjzimnol%2Favrtos","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjzimnol%2Favrtos/lists"}