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
| Flag | Language | Plan required |
|---|---|---|
--lang c99 | C99 (all compilers) | Explorer, Engineer, Team |
--lang cpp17 | C++17 with constexpr | Engineer, Team |
--lang rust | Rust (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.