|
| 1 | +Create a new ST-LIB hardware driver for: $ARGUMENTS |
| 2 | + |
| 3 | +Use ADC as the reference implementation: |
| 4 | +- `Inc/HALAL/Services/ADC/ADC.hpp` — service header pattern |
| 5 | +- `Tests/adc_test.cpp` — test pattern with mocked drivers |
| 6 | + |
| 7 | +### Step 1 — Service header `Inc/HALAL/Services/[Module]/[Module].hpp` |
| 8 | + |
| 9 | +- Define `[Module]Domain` struct (wraps everything) |
| 10 | +- Define `Entry` struct: compile-time config per peripheral instance (pin, mode, output pointer, etc.) |
| 11 | +- Define `Config` struct: fully resolved config after `build()` (all AUTO fields resolved) |
| 12 | +- Define enums for peripheral IDs, operating modes, resolutions, etc. |
| 13 | +- Implement `consteval build<N>(std::span<const Entry, N>) -> std::array<Config, N>` |
| 14 | + - Resolve AUTO fields (pin → peripheral/channel lookup tables) |
| 15 | + - Validate constraints at compile time — use `compile_error()` for violations |
| 16 | +- Guard hardware-specific declarations with `#ifdef HAL_[MODULE]_MODULE_ENABLED` |
| 17 | +- Include `hal_wrapper.h` at the top |
| 18 | + |
| 19 | +### Step 2 — Service implementation `Src/HALAL/Services/[Module]/[Module].cpp` |
| 20 | + |
| 21 | +- HAL handle declarations (`extern [MODULE]_HandleTypeDef h[module]N`) |
| 22 | +- `start(const Config& cfg)` function: initialize HAL, configure DMA if needed |
| 23 | +- Runtime operations: `read()`, `write()`, `get_value()`, etc. |
| 24 | +- Use `#ifdef SIM_ON` to call mocked functions instead of real HAL |
| 25 | + |
| 26 | +### Step 3 — MockedDriver `Inc/MockedDrivers/mocked_hal_[module].hpp` |
| 27 | + |
| 28 | +- In-memory state arrays replacing HAL registers |
| 29 | +- Init function called instead of HAL init in `SIM_ON` mode |
| 30 | +- Control functions for tests: `[module]_set_*(idx, value)`, `[module]_get_*(idx)` |
| 31 | +- Time simulation if the peripheral has timing behavior: `[module]_advance_time_ns(ns)` |
| 32 | +- Callback/interrupt simulation if needed |
| 33 | + |
| 34 | +### Step 4 — Tests `Tests/[module]_test.cpp` |
| 35 | + |
| 36 | +```cpp |
| 37 | +#include <gtest/gtest.h> |
| 38 | +#include "HALAL/Services/[Module]/[Module].hpp" |
| 39 | +#include "MockedDrivers/mocked_hal_[module].hpp" |
| 40 | + |
| 41 | +namespace ST_LIB::TestErrorHandler { void reset(); void set_fail_on_error(bool); } |
| 42 | + |
| 43 | +namespace { |
| 44 | +// Declare test configs at namespace scope (constexpr can't be local) |
| 45 | +inline float test_output = 0.0f; |
| 46 | +constexpr std::array<[Module]Domain::Entry, 1> test_entry{{ ... }}; |
| 47 | +constexpr auto test_cfg = [Module]Domain::build<1>(std::span{test_entry}); |
| 48 | +static_assert(test_cfg[0].peripheral == ...); // verify compile-time resolution |
| 49 | +} |
| 50 | + |
| 51 | +TEST(ModuleTest, CompileTimeConfig) { ... } |
| 52 | +TEST(ModuleTest, BasicReadWrite) { ... } |
| 53 | +TEST(ModuleTest, ErrorOnInvalidConfig) { |
| 54 | + ST_LIB::TestErrorHandler::set_fail_on_error(true); |
| 55 | + // trigger invalid config... |
| 56 | + ST_LIB::TestErrorHandler::reset(); |
| 57 | +} |
| 58 | +``` |
| 59 | +
|
| 60 | +Add to `Tests/CMakeLists.txt`: |
| 61 | +```cmake |
| 62 | +add_executable([module]_test [module]_test.cpp) |
| 63 | +target_link_libraries([module]_test PRIVATE stlib GTest::gtest_main) |
| 64 | +add_test(NAME [Module]Test COMMAND [module]_test) |
| 65 | +``` |
| 66 | + |
| 67 | +### Step 5 — Register in HALAL |
| 68 | + |
| 69 | +- Add `#include "HALAL/Services/[Module]/[Module].hpp"` to `Inc/HALAL/HALAL.hpp` |
| 70 | +- Add `[Module]Domain::start()` call in `Src/HALAL/HALAL.cpp` inside `common_start()` |
| 71 | + - Check initialization order: DMA must be started before services that use it |
| 72 | + |
| 73 | +### Verification |
| 74 | + |
| 75 | +```bash |
| 76 | +cmake --preset simulator |
| 77 | +cmake --build --preset simulator |
| 78 | +ctest --preset simulator-all |
| 79 | +``` |
0 commit comments