> ## Documentation Index
> Fetch the complete documentation index at: https://docs.embedder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Driver development

> Implement a peripheral driver from verified hardware requirements, then test its behavior in software and on the board.

Develop one driver against the exact fitted part and board revision. Define what the application needs, reuse the project's existing transport where possible, and verify register transactions separately from physical signals.

Start with [Rapid bring-up](/solutions/rapid-bring-up) if the board does not yet build, boot, or provide a working observation path.

## Establish the hardware facts

1. Open the firmware workspace and select the project with `/project`.
2. Add the fitted part with `/peripheral`. Use the [custom-peripheral workflow](/core-concepts/add-peripheral#add-a-custom-peripheral) when available for a part outside the catalog, or attach its datasheet as project documentation.
3. Supply the exact datasheet revision, relevant errata, MCU reference manual, and the complete [schematic set](/core-concepts/schematics) for this board.
4. Run `/init` if the workspace is not configured. Confirm the toolchain, SDK version, build and test commands, flash method, and observation path in [EMBEDDER.md](/core-concepts/embedder-md).

A catalog match does not identify the populated variant or confirm board wiring. Check package, supply and signal voltages, address straps or chip select, reset polarity, clock source, interrupts, and routed MCU pins against the actual design.

Use the exact schematic reference and workspace files for your project in requests such as:

```text theme={"system"}
Check the fitted sensor at @sch:component:U4 against its uploaded datasheet
and @src/board/pins.c. List the bus, address selection, pull-ups, power-up
sequence, reset timing, and interrupt wiring. Cite the relevant document
sections and schematic connections. Separate confirmed facts from conflicts
or missing information before proposing register values.
```

Replace `U4` and the source path. Resolve any conflict that affects wiring, electrical limits, or initialization before proceeding.

## Define the driver contract

Use `/plan` to agree on the driver interface and its limits before changing several modules:

```text theme={"system"}
Plan a driver for the confirmed sensor at U4. The application needs initialize,
read one sample, and report status. Use the project's existing I2C transport.
Define return values, units, maximum wait times, retry limits, and recovery
behavior. Identify required files and tests. Keep DMA and interrupt-driven
sampling out of the first implementation unless the requirements need them.
```

Specify startup state, sampling rate, supported operating modes, caller or interrupt context, and whether multiple tasks can use the driver. Decide who owns bus locking and how the application distinguishes a transport error, invalid device response, not-ready result, and valid sample.

Require a deadline for every wait and a limit for retries. Retry only operations whose device semantics permit it: repeating a command, clearing a status bit, or reading a FIFO can change state. Define what the driver leaves configured after a failed initialization or transfer.

## Choose reuse or a custom implementation

Ask Embedder to inspect the existing SDK, HAL, and drivers before adding code. Prefer a compatible existing driver when it supports the exact device and required behavior. Check its version, license, supported modes, and error handling; a similarly named part may use a different register map.

For a custom driver, separate device behavior from bus access so host tests can supply controlled responses. Keep board pins, bus instances, and interrupt routing in the project's board layer. Respect generated-code boundaries and the existing project style.

For MCU peripherals, check the reference manual and errata for clock enables, reset order, pin multiplexing, interrupt flags, and DMA requirements. For external devices, verify framing, byte order, command sequences, reserved bits, and read/write side effects. Derive constants from the exact reference; do not copy them from a nearby device family.

## Implement and test in stages

Review the plan, then switch to `/act`. Start with initialization and one bounded transaction before adding buffered, interrupt, or DMA operation.

```text theme={"system"}
Implement the reviewed driver using the existing transport interface. Add
host tests for the documented initialization sequence, sample conversion,
wrong device identity if the part exposes one, not-ready status, and transfer
timeout. Use datasheet values for fixtures and explain their origin. Run the
focused tests and then build the selected firmware configuration.
```

Test decoding with boundary values, sign handling, units, and invalid responses. Exercise error paths through a fake transport or the project's existing test framework. Check that a timed-out request terminates, reports the intended error, and allows the documented next operation.

Resolve test, compile, and link failures before flashing. Passing host tests checks the modeled behavior; a target build checks compilation and linking. Neither establishes that the board's wiring, device timing, or electrical signals are correct.

## Validate on the intended board

Confirm the board revision, fitted part, firmware artifact, and probe or port. Configure the chosen [UART](/debug-mode/uart), [logic analyzer](/debug-mode/logic-analyzer), or [oscilloscope](/debug-mode/oscilloscope) using its setup guide, including voltage limits and channel assignments.

```text theme={"system"}
Build the reviewed firmware and flash the confirmed board only if the build
passes. At the project's verified bus rate, run initialization and read three
samples. Capture UART for at most 15 seconds and the corresponding I2C
transactions with the configured logic analyzer. Report startup status,
addresses, acknowledgments, decoded samples, and any timeout separately.
```

Replace the bus and observation method for the actual device. Verify the transmitted sequence against the datasheet and the returned values against known test conditions. An acknowledgment alone does not establish that configuration or measurement is correct.

Use a scope when the question concerns signal levels, rise time, ringing, or setup/hold timing. Set a suitable achieved sample rate and trigger. If the conclusion depends on one exact event appearing in multiple captures, use a common marker or trigger; independent logs do not necessarily share a clock.

Use `/debug` for a hang or unexpected driver state. Preserve existing evidence and follow the [attachment precautions](/debug-mode/gdb#attach-to-a-running-target) before connecting a debugger. Halting can change timing, so reproduce timing-sensitive results while the firmware runs normally.

## Keep the regression repeatable

Save the verified sequence as a [hardware script](/debug-mode/hardware-scripts) under `.embedder/hardware/`, with the bench settings, bounded waits, pass conditions, and cleanup. Hardware scripts can run in Act or Debug mode.

```text theme={"system"}
Create a repeatable driver check for this bench. Verify initialization and
three samples against the agreed limits, report failures distinctly from
missing observations, and stop the captures when finished. Record the board
revision, firmware artifact, wiring, and test conditions with the result.
```

Keep host regressions for each corrected software failure. Add target checks for required reset, repeated initialization, power-state, or recovery behavior using the board's approved test procedure. Run the same checks after SDK, clock, bus, or driver changes, and record any requirement that remains unverified.
