-
Notifications
You must be signed in to change notification settings - Fork 4
[docs] Add a Getting started doc. #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,271 @@ | ||
| # Getting Started | ||
|
|
||
| This guide walks through integrating wolfHAL into a bare-metal embedded project. | ||
|
|
||
| ## Project Layout | ||
|
|
||
| A typical project using wolfHAL looks like this: | ||
|
|
||
| ``` | ||
| my_project/ | ||
| wolfHAL/ wolfHAL repository (submodule, copy, etc.) | ||
| boards/ | ||
| <board_name>/ | ||
| board.h Peripheral externs and board constants | ||
| board.c Device instances, configuration, and Board_Init | ||
| ivt.c Interrupt vector table and Reset_Handler | ||
| linker.ld Linker script for your MCU | ||
| Makefile.inc Toolchain and source list | ||
| src/ | ||
| main.c Application entry point | ||
| ... Additional application sources | ||
| Makefile | ||
| ``` | ||
|
|
||
| The key idea is that your project provides the board-level glue (device | ||
| instances, pin assignments, clock config, startup code) and wolfHAL provides | ||
| the driver implementations and API. | ||
|
|
||
| ## Adding wolfHAL to Your Project | ||
|
|
||
| wolfHAL is a source-level library with no external dependencies beyond a C | ||
| compiler and standard headers (`stdint.h`, `stddef.h`). To use it: | ||
|
|
||
| 1. Add the wolfHAL repository root to your include path (e.g., `-I/path/to/wolfHAL`) | ||
| 2. Compile the generic dispatch sources for the modules you need: | ||
|
|
||
| ``` | ||
| src/clock/clock.c | ||
| src/gpio/gpio.c | ||
| src/uart/uart.c | ||
| src/flash/flash.c | ||
| src/spi/spi.c | ||
| src/timer/timer.c | ||
| src/rng/rng.c | ||
| src/supply/supply.c | ||
| ``` | ||
|
|
||
| 3. Compile the platform-specific driver sources for your target: | ||
|
|
||
| ``` | ||
| src/clock/<platform>_clock.c | ||
| src/gpio/<platform>_gpio.c | ||
| src/uart/<platform>_uart.c | ||
| ... | ||
| ``` | ||
|
|
||
| You only need to include the modules and platform drivers your project actually | ||
| uses. | ||
|
|
||
| ## The Device Model | ||
|
|
||
| Every peripheral in wolfHAL is represented by a device struct with three fields: | ||
|
|
||
| ```c | ||
| struct whal_Gpio { | ||
| const whal_Regmap regmap; /* base address and size */ | ||
| const whal_GpioDriver *driver; /* vtable of function pointers */ | ||
| const void *cfg; /* platform-specific configuration */ | ||
| }; | ||
| ``` | ||
|
|
||
| - **regmap** — identifies the peripheral's memory-mapped register block | ||
| - **driver** — points to the platform driver implementation (the vtable) | ||
| - **cfg** — points to a platform-specific configuration struct that the driver | ||
| reads during Init | ||
|
|
||
| Platform headers provide device macros that fill in the `regmap` and `driver` | ||
| fields, so you only need to provide the `cfg`: | ||
|
|
||
| ```c | ||
| #include <wolfHAL/platform/st/stm32wb55xx.h> | ||
|
|
||
| whal_Gpio g_whalGpio = { | ||
| WHAL_STM32WB55_GPIO_DEVICE, | ||
| .cfg = &gpioConfig, | ||
| }; | ||
| ``` | ||
|
|
||
| ## Configuring Peripherals | ||
|
|
||
| Each platform driver defines its own configuration struct with the parameters | ||
| it needs. For example, a GPIO driver might need a clock controller reference | ||
| and a pin configuration table: | ||
|
|
||
| ```c | ||
| whal_Gpio g_whalGpio = { | ||
| WHAL_STM32WB55_GPIO_DEVICE, | ||
|
|
||
| .cfg = &(whal_Stm32wbGpio_Cfg) { | ||
| .clkCtrl = &g_whalClock, | ||
| .clk = (const void *[1]) { | ||
| &(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_GPIOB_CLOCK}, | ||
| }, | ||
| .clkCount = 1, | ||
|
|
||
| .pinCfg = (whal_Stm32wbGpio_PinCfg[]) { | ||
| { /* LED */ | ||
| .port = WHAL_STM32WB_GPIO_PORT_B, | ||
| .pin = 5, | ||
| .mode = WHAL_STM32WB_GPIO_MODE_OUT, | ||
| .outType = WHAL_STM32WB_GPIO_OUTTYPE_PUSHPULL, | ||
| .speed = WHAL_STM32WB_GPIO_SPEED_LOW, | ||
| }, | ||
| }, | ||
| .pinCount = 1, | ||
| }, | ||
| }; | ||
| ``` | ||
|
|
||
| A UART driver might need just a clock reference and baud rate: | ||
|
|
||
| ```c | ||
| whal_Uart g_whalUart = { | ||
| WHAL_STM32WB55_UART1_DEVICE, | ||
|
|
||
| .cfg = &(whal_Stm32wbUart_Cfg) { | ||
| .clkCtrl = &g_whalClock, | ||
| .clk = &(whal_Stm32wbRcc_Clk){WHAL_STM32WB55_UART1_CLOCK}, | ||
| .baud = 115200, | ||
| }, | ||
| }; | ||
| ``` | ||
|
|
||
| See the platform-specific headers in `wolfHAL/<device_type>/` for the full set | ||
| of configuration options for each driver. | ||
AlexLanzano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## Initialization | ||
|
|
||
| Peripherals must be initialized in dependency order. The clock controller comes | ||
| first since other peripherals need it to enable their clocks. A typical | ||
| initialization sequence: | ||
|
|
||
| ```c | ||
| #include <wolfHAL/wolfHAL.h> | ||
|
|
||
| whal_Error Board_Init(void) | ||
| { | ||
| whal_Error err; | ||
|
|
||
| err = whal_Clock_Init(&g_whalClock); | ||
| if (err != WHAL_SUCCESS) return err; | ||
|
|
||
| err = whal_Gpio_Init(&g_whalGpio); | ||
| if (err != WHAL_SUCCESS) return err; | ||
|
|
||
| err = whal_Uart_Init(&g_whalUart); | ||
| if (err != WHAL_SUCCESS) return err; | ||
|
|
||
| err = whal_Timer_Init(&g_whalTimer); | ||
| if (err != WHAL_SUCCESS) return err; | ||
|
|
||
| err = whal_Timer_Start(&g_whalTimer); | ||
| if (err != WHAL_SUCCESS) return err; | ||
|
|
||
| return WHAL_SUCCESS; | ||
| } | ||
| ``` | ||
|
|
||
| On platforms with a supply controller (e.g., PIC32CZ), the supply must be | ||
| initialized before the clock if the PLL depends on it. | ||
|
|
||
| ## Using the API | ||
|
|
||
| After initialization, use the wolfHAL API to interact with peripherals: | ||
|
|
||
| ```c | ||
| #include <wolfHAL/wolfHAL.h> | ||
| #include "board.h" | ||
|
|
||
| void main(void) | ||
| { | ||
| if (Board_Init() != WHAL_SUCCESS) | ||
| while (1); | ||
|
|
||
| while (1) { | ||
| whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); | ||
| whal_Uart_Send(&g_whalUart, "Hello!\r\n", 8); | ||
| Board_WaitMs(1000); | ||
|
|
||
| whal_Gpio_Set(&g_whalGpio, BOARD_LED_PIN, 0); | ||
| Board_WaitMs(1000); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| All API functions return `whal_Error`. Check for `WHAL_SUCCESS` to confirm the | ||
| operation completed. The error codes are: | ||
|
|
||
| | Code | Meaning | | ||
| |------|---------| | ||
| | `WHAL_SUCCESS` | Operation completed successfully | | ||
| | `WHAL_EINVAL` | Invalid argument or unsupported operation | | ||
| | `WHAL_ENOTREADY` | Resource is busy or not yet available | | ||
| | `WHAL_EHARDWARE` | Hardware error (e.g., RNG entropy failure) | | ||
|
|
||
| ## Optimizing for Size | ||
|
|
||
| wolfHAL gives you several ways to reduce code size depending on how much | ||
| control you want. | ||
|
|
||
| ### Direct Callbacks | ||
|
|
||
| Define `WHAL_CFG_DIRECT_CALLBACKS` to bypass the generic dispatch layer. API | ||
| calls compile down to direct function pointer calls with no input validation, | ||
| eliminating the dispatch source files entirely. | ||
|
|
||
| ### Custom Vtables | ||
|
|
||
| The platform drivers provide a pre-built vtable with all operations populated. | ||
| If you only use a subset of a driver's functionality, you can define your own | ||
| vtable that only includes the functions you need: | ||
|
|
||
| ```c | ||
| static const whal_GpioDriver myGpioDriver = { | ||
| .Init = whal_Stm32wbGpio_Init, | ||
| .Deinit = whal_Stm32wbGpio_Deinit, | ||
| .Set = whal_Stm32wbGpio_Set, | ||
| /* Get left as NULL — not needed, saves pulling in that code */ | ||
| }; | ||
|
|
||
| whal_Gpio g_whalGpio = { | ||
| .regmap = { .base = 0x48000000, .size = 0x2000 }, | ||
| .driver = &myGpioDriver, | ||
| .cfg = &gpioConfig, | ||
| }; | ||
| ``` | ||
|
|
||
| With link-time optimization (`-flto`) or garbage collection (`-ffunction-sections` | ||
| + `-Wl,--gc-sections`), any driver functions not referenced through the vtable | ||
| will be stripped from the final binary. | ||
|
|
||
| ### Calling Driver Functions Directly | ||
|
|
||
| For maximum control, you can skip the vtable entirely and call the underlying | ||
| platform driver functions directly: | ||
|
|
||
| ```c | ||
| #include <wolfHAL/gpio/stm32wb_gpio.h> | ||
|
|
||
| whal_Stm32wbGpio_Init(&g_whalGpio); | ||
| whal_Stm32wbGpio_Set(&g_whalGpio, BOARD_LED_PIN, 1); | ||
| ``` | ||
|
|
||
| This eliminates the vtable indirection and lets the compiler inline or optimize | ||
| the calls more aggressively. | ||
|
|
||
| **Be careful with this approach:** some drivers call other drivers internally | ||
| through the wolfHAL API. For example, a GPIO driver's Init may call | ||
| `whal_Clock_Enable()` to gate the peripheral clock. If you bypass the vtable | ||
| for the clock driver, those internal calls will still go through the vtable | ||
| unless you also modify the driver source. Make sure the drivers your code | ||
| depends on still have a working dispatch path for any functions they call | ||
| internally. | ||
|
|
||
| ## Next Steps | ||
|
|
||
| - See `boards/` for complete board configuration examples | ||
| - See [Writing a Driver](writing_a_driver.md) for how to add support for a new | ||
| platform | ||
| - See [Adding a Board](adding_a_board.md) for how to create a board | ||
| configuration for your hardware | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.