First, What Is It
- Device model: the SDK treats every peripheral as a "device" obtained by name (
bflb_device_get_by_name("uart0")), then drives it through that handle — the same pattern for every peripheral. - Driver layering: the
bflb_xxx_*APIs you call are the driver layer; internally they manipulate registers (LL layer) to control the hardware. - Three data paths: polling (program reads actively), interrupt (hardware notifies), DMA (hardware moves data directly) — understanding these is understanding peripheral implementation.
Breaking Down the Principle
1. Unified Device Model
The SDK keeps a "device name → device handle" table; get the handle before initializing:
struct bflb_device_s *uart0 = bflb_device_get_by_name("uart0");
struct bflb_device_s *gpio = bflb_device_get_by_name("gpio");bflb_device_s holds the base address, IRQ number, and driver operation table. Every bflb_uart_*/bflb_gpio_* API takes it as the first argument — the universal pattern of all SDK examples.
2. Driver Layering
- API layer: application-facing, e.g.,
bflb_xxx_init,bflb_xxx_send/recv/set/read. - HAL/LL layer: reads/writes registers directly (e.g., clock gating
GLB_CGEN_*, data registers); implementations differ per chip while the API layer stays stable.
3. The Standard Init and Operation Flow
Almost every peripheral example follows:
Example: UART is get_by_name("uart0") → bflb_uart_init(baud/data/parity/stop) → receive interrupt or poll bflb_uart_getchar.
4. Polling vs Interrupt vs DMA
| Method | Principle | Pros | Cons | Use case |
|---|---|---|---|---|
| Polling | Loop checking status register | Simple, easy to debug | Occupies CPU | Low frequency, simple I/O |
| Interrupt | Hardware triggers a callback | Non-blocking, responsive | No heavy work in ISR | Buttons, UART receive |
| DMA | Hardware moves data into memory | CPU almost free, fast | Complex config, cache coherency | Bulk data (Flash, audio, display) |
How the SDK Implements It
- Related pages: GPIO Output, UART, DMA Transfer, System & Software
GPIO as an example — "device model + init + operation":
struct bflb_device_s *gpio = bflb_device_get_by_name("gpio");
bflb_gpio_init(gpio, GPIO_PIN_12, GPIO_OUTPUT); /* init mode */
bflb_gpio_set(gpio, GPIO_PIN_12); /* operate: output high */All basic-peripheral pages (GPIO/UART/SPI/I2C/ADC/PWM/Timer...) share this skeleton — learn one, generalize to all.
Common Exam & Interview Questions
Why bflb_device_get_by_name instead of direct register access?
The unified device model decouples driver APIs from the concrete chip: changing chip/instance only changes the device name, and the application style stays the same; it also centralizes IRQ/clock resource management.
Difference between interrupt and DMA?
Interrupt: hardware calls the CPU to handle data. DMA: hardware moves data by itself and notifies the CPU when done. DMA frees the CPU from copying, ideal for large transfers.
Why get the device handle before peripheral init?
The handle encapsulates the address, IRQ number, and operation table; driver APIs access the specific peripheral through it — also the basis for driving multiple instances (e.g., two UARTs) with the same code.
Relation between the LL layer and the API layer?
The API layer is application-facing and stable across chips; the LL/HAL layer touches registers and changes with the chip. Platform changes usually only swap the LL implementation, leaving app code untouched.
When is polling unacceptable?
When events are sparse but the program is busy (receiving UART, scanning keys, running a UI), polling wastes CPU and adds latency — switch to interrupt or DMA.
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

