Quick Start

Create your first chip driver

Install CLI, then make a C99 header in under ten minutes.

Installation

Install embedd via npm, the recommended method, or download a platform binary.

npm (recommended)

$ npm install -g @embedd/cli
+ @embedd/[email protected]
added 3 packages in 2.4s

$ embedd --version
0.12.0

Binary download

Get macOS, Linux, and Windows binaries from the GitHub releases page. Unpack them, then add them to your PATH.

Authenticate

Once installed, sign in using your API key. Create that key in account settings after sign-up.

$ embedd auth login --key embx_sk_your_api_key_here
Authenticated as [email protected]

Upload a datasheet

Send a PDF to Embedd's extraction engine with embedd upload; it returns a chip identifier.

$ embedd upload stm32l476rg-reference-manual.pdf
Uploading stm32l476rg-reference-manual.pdf (4.2 MB)... done
Extracting register model... done
Chip ID: embx_chip_stm32l476rg_a1b2c3
Peripherals found: GPIO, USART, SPI, I2C, TIM, ADC, DMA, RCC, FLASH

Keep the stable Chip ID. Commit it to version control for reproducible builds.

If the chip is in Embedd's library, skip uploading. See Supported Chips.

Generate a driver

Run embedd generate with a Chip ID to create the driver output.

$ embedd generate embx_chip_stm32l476rg_a1b2c3 \
    --lang c99 \
    --peripherals GPIO,USART \
    --output ./drivers/

Generating C99 driver for STM32L476RG...
  GPIO: 18 registers, 127 bitfield accessors
  USART: 9 registers, 64 bitfield accessors
Output written to ./drivers/stm32l476rg_gpio.h
Output written to ./drivers/stm32l476rg_usart.h
GCC cross-compile smoke test: PASS

Supported output languages

FlagLanguagePlan required
--lang c99C99 (all compilers)Explorer, Engineer, Team
--lang cpp17C++17 with constexprEngineer, Team
--lang rustRust (beta)Team

Understanding output

A header file has three output layers:

/* Base address */
#define GPIOA_BASE  0x48000000U

/* Register access macros */
#define GPIOA_MODER  (*(volatile uint32_t *)(GPIOA_BASE + 0x00))

/* Bitfield masks */
#define GPIOA_MODER_MODER0_Pos   (0U)
#define GPIOA_MODER_MODER0_Msk   (0x3U << GPIOA_MODER_MODER0_Pos)

/* Bitfield accessor functions */
static inline uint32_t GPIOA_MODER_MODER0_get(void) {
    return (GPIOA_MODER & GPIOA_MODER_MODER0_Msk) >> GPIOA_MODER_MODER0_Pos;
}
static inline void GPIOA_MODER_MODER0_set(uint32_t val) {
    GPIOA_MODER = (GPIOA_MODER & ~GPIOA_MODER_MODER0_Msk) |
                  ((val << GPIOA_MODER_MODER0_Pos) & GPIOA_MODER_MODER0_Msk);
}

Each bitfield accessor points to its datasheet row. Inspect the register model JSON through the API.

CMake integration

The CLI ships a cmake/EmbeddGenerate.cmake module that adds a embedd_generate_driver() function.

# CMakeLists.txt
include("cmake/EmbeddGenerate.cmake")

embedd_generate_driver(
    CHIP_ID    "embx_chip_stm32l476rg_a1b2c3"
    LANG       "c99"
    PERIPHERALS "GPIO;USART;SPI"
    OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated"
)

target_include_directories(my_firmware PRIVATE
    "${CMAKE_CURRENT_BINARY_DIR}/generated"
)

Output is cached using the chip hash. Re-running unchanged chips does nothing, keeping CI quick.

GitHub Actions integration

# .github/workflows/build.yml
name: Build firmware

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Embedd CLI
        run: npm install -g @embedd/cli

      - name: Generate drivers
        env:
          EMBEDD_API_KEY: ${{ secrets.EMBEDD_API_KEY }}
        run: |
          embedd auth login --key $EMBEDD_API_KEY
          embedd generate embx_chip_stm32l476rg_a1b2c3 \
            --lang c99 \
            --output ./drivers/

      - name: Build
        run: cmake -B build && cmake --build build

Add the key as a GitHub Actions secret named EMBEDD_API_KEY. Do not commit it.