Skip to content

Commit 5fed5cd

Browse files
committed
Complete microtimer professional documentation
1 parent 6bb89b2 commit 5fed5cd

9 files changed

Lines changed: 417 additions & 85 deletions

File tree

CONTRIBUTING.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
11
# Contributing
2-
In scope: bug fixes, docs, tests. Out of scope: dynamic allocation, HW timer abstraction.
3-
By contributing, you agree to the MIT License.
2+
3+
## Project Rules
4+
5+
- Keep the public C API compatible with C99.
6+
- Preserve fixed-capacity, caller-owned storage semantics.
7+
- Do not add heap allocation, OS or RTOS dependencies, hidden locks, or generated code systems.
8+
- Keep access serialized per manager unless callers provide external synchronization.
9+
- Do not weaken runtime tests or remove compile-fail tests.
10+
- Do not create tags or releases unless explicitly requested.
11+
12+
## Development Notes
13+
14+
- Prefer CMake for package and consumer verification.
15+
- The checked-in `include/mtimer_config.h` is the source-tree default; build and install trees use a generated resolved configuration header.
16+
- Timer callbacks return `void`; error propagation must happen through caller-managed state.
17+
- `longjmp` out of a callback is unsupported and may leave the active-tick guard set.

README.md

Lines changed: 79 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,107 @@
1-
# microtimer
1+
# microtimer
22

33
[![CI](https://github.com/Vanderhell/microtimer/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Vanderhell/microtimer/actions/workflows/ci.yml)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
55
[![C Standard](https://img.shields.io/badge/C-C99-blue.svg)](https://en.wikipedia.org/wiki/C99)
66

7-
Software timer manager for embedded systems.
7+
`microtimer` is a fixed-capacity software timer manager for C99 projects.
88

9-
C99 | Zero dependencies | Zero allocations | Oneshot + Periodic | Portable
9+
It targets serialized access to one manager from one execution context by default. It does not provide heap allocation, hidden locks, persistence, or general ISR/thread safety.
1010

11-
## Why microtimer?
11+
## Support Scope
1212

13-
Embedded main loops often contain repeated timing checks:
13+
- C99 and C11 consumers
14+
- C++ header consumption
15+
- GCC, Clang, and MSVC builds
16+
- Main-loop usage
17+
- ISR-owned usage with strict limits
18+
- External synchronization around shared access
19+
- ARM Cortex-M compile-only verification
20+
- CMake `find_package()` and `add_subdirectory()` consumers
21+
22+
## Quick Start
1423

1524
```c
16-
if (now - last_blink > 500) { toggle_led(); last_blink = now; }
17-
if (now - last_send > 5000) { send_telemetry(); last_send = now; }
18-
if (now - last_check > 1000) { check_sensors(); last_check = now; }
19-
```
25+
#include "mtimer.h"
2026

21-
`microtimer` replaces this with registered timers and a single tick path:
27+
static uint32_t app_clock_ms(void) { return platform_millis(); }
2228

23-
```c
24-
mtimer_create(&tm, "blink", 500, MTIMER_PERIODIC, on_blink, NULL);
25-
mtimer_create(&tm, "send", 5000, MTIMER_PERIODIC, on_send, NULL);
26-
mtimer_create(&tm, "timeout", 3000, MTIMER_ONESHOT, on_timeout, NULL);
29+
static void blink_cb(uint8_t id, void *ctx)
30+
{
31+
(void)id;
32+
*(volatile int *)ctx = 1;
33+
}
2734

28-
while (1) {
29-
mtimer_tick(&tm);
35+
int main(void)
36+
{
37+
mtimer_t tm;
38+
volatile int blink_due = 0;
39+
int timer_id;
40+
41+
if (mtimer_init(&tm, app_clock_ms) != MTIMER_OK) {
42+
return 1;
43+
}
44+
45+
timer_id = mtimer_create(&tm, "blink", 500u, MTIMER_PERIODIC, blink_cb, (void *)&blink_due);
46+
if (timer_id < 0) {
47+
return 1;
48+
}
49+
50+
if (mtimer_start(&tm, (uint8_t)timer_id) != MTIMER_OK) {
51+
return 1;
52+
}
53+
54+
for (;;) {
55+
int tick_rc = mtimer_tick(&tm);
56+
if (tick_rc < 0) {
57+
return 1;
58+
}
59+
if (blink_due) {
60+
blink_due = 0;
61+
toggle_led();
62+
}
63+
}
3064
}
3165
```
3266
33-
## Features
67+
## Key Contracts
3468
35-
- Oneshot timers that auto-stop after firing.
36-
- Periodic timers with drift correction.
37-
- Pause/resume with remaining-time preservation.
38-
- Dynamic interval changes at runtime.
39-
- Slot reuse through destroy/create.
40-
- Named timer lookup for diagnostics and shell commands.
41-
- Per-timer and global fire counters.
69+
- Clock units are milliseconds on an unsigned 32-bit modulo counter.
70+
- Natural `uint32_t` wraparound is supported if the clock is not reset while timers are active.
71+
- `mtimer_tick()` fires each running timer at most once per successful call.
72+
- Missed periodic intervals are skipped without callback bursts; phase is retained when only one interval is due.
73+
- Timer names are optional and caller-owned. Non-`NULL` names must remain valid, immutable, and unique per manager.
74+
- Timer IDs are slot indexes. Destroying a timer invalidates its ID, and later creates may reuse that slot.
75+
- Same-manager mutation during callbacks returns `MTIMER_ERR_BUSY`.
4276
43-
## Build and Test
77+
## Build
4478
45-
Requirements:
46-
- C99 compiler (`gcc` or `clang`)
47-
- `make`
48-
49-
Run tests:
79+
### CMake
5080
5181
```bash
52-
# clone microtest next to this repository root
53-
# expected path: ../microtest/include
54-
make -C tests
82+
cmake -S . -B build -DMICROTIMER_BUILD_TESTS=ON
83+
cmake --build build
84+
ctest --test-dir build --output-on-failure
5585
```
5686

57-
## Public API
58-
59-
Key functions:
60-
- `mtimer_init`
61-
- `mtimer_create`, `mtimer_destroy`
62-
- `mtimer_start`, `mtimer_stop`, `mtimer_pause`, `mtimer_resume`
63-
- `mtimer_set_interval`
64-
- `mtimer_tick`
65-
- `mtimer_count`, `mtimer_find`, `mtimer_remaining`
66-
67-
See [`include/mtimer.h`](include/mtimer.h) for full API details.
68-
69-
## Repository Layout
87+
### Make
7088

71-
- `include/mtimer.h` - public API
72-
- `src/mtimer.c` - implementation
73-
- `tests/test_all.c` - unit tests
74-
- `docs/DESIGN.md` - design rationale
75-
76-
## Ecosystem
77-
78-
- [microhealth](https://github.com/Vanderhell/microhealth)
79-
- [microwdt](https://github.com/Vanderhell/microwdt)
80-
- [microres](https://github.com/Vanderhell/microres)
81-
- [microsh](https://github.com/Vanderhell/microsh)
82-
83-
## Configuration
84-
85-
- `MTIMER_MAX_TIMERS` (default: `8`)
86-
87-
## Contributing
88-
89-
See [CONTRIBUTING.md](CONTRIBUTING.md).
89+
```bash
90+
make
91+
```
9092

91-
## Changelog
93+
Caller-provided `CC`, `CPPFLAGS`, `CFLAGS`, and `LDFLAGS` are honored by the Makefiles.
9294

93-
See [CHANGELOG.md](CHANGELOG.md).
95+
## Documentation
9496

95-
## License
97+
- [API reference](docs/API_REFERENCE.md)
98+
- [Cookbook](docs/COOKBOOK.md)
99+
- [Design notes](docs/DESIGN.md)
100+
- [Issues and troubleshooting](docs/ISSUES.md)
101+
- [Porting guide](docs/PORTING_GUIDE.md)
102+
- [Verification status](docs/VERIFICATION.md)
103+
- [Contributing](CONTRIBUTING.md)
104+
- [Security reporting](SECURITY.md)
105+
- [Changelog](CHANGELOG.md)
96106

97-
MIT - see [LICENSE](LICENSE).
107+
Releases are tag-driven through `.github/workflows/release.yml` and are only published from pushed `v*` tags.

SECURITY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Security Policy
2+
3+
Report suspected vulnerabilities privately through the repository's security contact path or a private maintainer channel before public disclosure.
4+
5+
This project has no built-in persistence, privilege boundaries, or sandboxing. Security-sensitive integrations must validate their own callback, clock, and synchronization assumptions.

docs/API_REFERENCE.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# API Reference
2+
3+
## Initialization
4+
5+
- `mtimer_init(mtimer_t *tm, mtimer_clock_fn clock)`
6+
Initializes a manager using the caller-visible `sizeof(*tm)`.
7+
- `mtimer_init_sized(mtimer_t *tm, size_t tm_size, mtimer_clock_fn clock)`
8+
Returns `MTIMER_ERR_ABI` when the caller and library disagree on manager size or resolved configuration.
9+
10+
## Creation and Lifetime
11+
12+
- `mtimer_create(...)`
13+
Returns a non-negative slot index on success or a negative `mtimer_err_t` on failure.
14+
- `mtimer_destroy(mtimer_t *tm, uint8_t id)`
15+
Invalidates the slot index immediately. Later creates may reuse the same slot.
16+
17+
`name == NULL` creates an unnamed timer. Non-`NULL` names are caller-owned, must remain valid and immutable for the timer lifetime, and must be unique within the manager.
18+
19+
## Control
20+
21+
- `mtimer_start()`
22+
Fully restarts a timer from the current clock value.
23+
- `mtimer_stop()`
24+
Stops the timer and clears paused bookkeeping relevance.
25+
- `mtimer_pause()`
26+
Valid only from `MTIMER_RUNNING`. Already-due timers store `remaining_ms == 0`.
27+
- `mtimer_resume()`
28+
Valid only from `MTIMER_PAUSED`. Zero remaining time becomes eligible on the next tick.
29+
- `mtimer_set_interval()`
30+
Rejects zero. Running timers restart from the current clock. Paused timers remain paused and adopt the full new interval as remaining time. Stopped timers keep the new interval and remain stopped.
31+
32+
## Tick
33+
34+
- `mtimer_tick(mtimer_t *tm)`
35+
Returns the number of timers fired on success, or a negative `mtimer_err_t` such as `MTIMER_ERR_NULL`, `MTIMER_ERR_INVALID`, or `MTIMER_ERR_BUSY`.
36+
37+
Each successful tick captures a single `now` value, processes timers in ascending slot order, and fires each timer at most once.
38+
39+
## Queries
40+
41+
- `mtimer_get_count()`
42+
- `mtimer_at()`
43+
- `mtimer_find()`
44+
- `mtimer_get_state()`
45+
- `mtimer_get_remaining()`
46+
- `mtimer_get_fire_count()`
47+
- `mtimer_get_total_fires()`
48+
- `mtimer_get_total_ticks()`
49+
50+
All query functions report failure explicitly instead of returning values that could be mistaken for valid state.
51+
52+
## Error Codes
53+
54+
- `MTIMER_OK`
55+
- `MTIMER_ERR_NULL`
56+
- `MTIMER_ERR_FULL`
57+
- `MTIMER_ERR_NOT_FOUND`
58+
- `MTIMER_ERR_INVALID`
59+
- `MTIMER_ERR_BUSY`
60+
- `MTIMER_ERR_ABI`

docs/COOKBOOK.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Cookbook
2+
3+
## 1. Minimal initialization
4+
5+
```c
6+
mtimer_t tm;
7+
if (mtimer_init(&tm, platform_millis) != MTIMER_OK) {
8+
return;
9+
}
10+
```
11+
12+
## 2. Complete quick start
13+
14+
```c
15+
static volatile int timeout_due = 0;
16+
17+
static void timeout_cb(uint8_t id, void *ctx)
18+
{
19+
(void)id;
20+
*(volatile int *)ctx = 1;
21+
}
22+
23+
void app_loop(void)
24+
{
25+
mtimer_t tm;
26+
int timer_id;
27+
28+
if (mtimer_init(&tm, platform_millis) != MTIMER_OK) {
29+
return;
30+
}
31+
timer_id = mtimer_create(&tm, "timeout", 3000u, MTIMER_ONESHOT, timeout_cb, (void *)&timeout_due);
32+
if (timer_id < 0) {
33+
return;
34+
}
35+
if (mtimer_start(&tm, (uint8_t)timer_id) != MTIMER_OK) {
36+
return;
37+
}
38+
for (;;) {
39+
int tick_rc = mtimer_tick(&tm);
40+
if (tick_rc < 0) {
41+
return;
42+
}
43+
if (timeout_due) {
44+
timeout_due = 0;
45+
handle_timeout();
46+
}
47+
}
48+
}
49+
```
50+
51+
## 3. Oneshot timer
52+
53+
Create with `MTIMER_ONESHOT`, then call `mtimer_start()`. It stops itself before the callback runs.
54+
55+
## 4. Periodic timer
56+
57+
Create with `MTIMER_PERIODIC`. Missed periods are skipped without callback bursts.
58+
59+
## 5. Pause and resume
60+
61+
Pause only from `MTIMER_RUNNING`, resume only from `MTIMER_PAUSED`. If a timer was already due when paused, resuming makes it eligible on the next tick.
62+
63+
## 6. Change interval
64+
65+
Running timers restart from the current clock. Paused timers keep the paused state and reset remaining time to the full new interval.
66+
67+
## 7. Named timer lookup
68+
69+
Use `mtimer_find(&tm, "name")`. Duplicate non-`NULL` names are rejected.
70+
71+
## 8. Multiple managers
72+
73+
Independent managers may be ticked independently because the active-tick guard is per manager.
74+
75+
## 9. C++ consumer
76+
77+
The public header is valid in C++ translation units. See `tests/consumers/cpp_consumer.cpp`.
78+
79+
## 10. Installed CMake package consumer
80+
81+
See `tests/find_package_consumer/` for a `find_package(microtimer CONFIG REQUIRED)` fixture that links `microtimer::microtimer`.
82+
83+
## 11. add_subdirectory consumer
84+
85+
See `tests/add_subdirectory_consumer/` for an `add_subdirectory()` fixture.
86+
87+
## 12. Main-loop integration
88+
89+
Use `mtimer_tick()` from one serialized loop or task that owns the manager.
90+
91+
## 13. ISR-limited usage
92+
93+
Only use from an ISR when the manager is ISR-owned or externally protected, the clock function is ISR-safe, and callbacks stay bounded and non-blocking.
94+
95+
## 14. External locking
96+
97+
Protect every API call with the same mutex or critical section when sharing one manager across execution contexts.
98+
99+
## 15. Counter policy
100+
101+
Per-timer and global counters use unsigned modulo wrap at `UINT32_MAX`.
102+
103+
## 16. Clock wraparound
104+
105+
Unsigned `uint32_t` wraparound is supported, but elapsed time longer than one complete counter cycle is not distinguishable.
106+
107+
## 17. Context and name lifetime
108+
109+
Callback context pointers and non-`NULL` timer names are caller-owned and must remain valid for the timer lifetime.
110+
111+
## 18. Set-flag callback pattern
112+
113+
Prefer callbacks that set flags or enqueue lightweight work, then act after `mtimer_tick()` returns.

0 commit comments

Comments
 (0)