Exercise: Explore the uFerris BSP
~15-20 min
Your Task
Step 1: Read the Source
Open the uFerris BSP crate source code. Don't use it yet — just read it.
Answer these questions:
- How does the BSP map pin numbers to named functions?
- What configuration choices has the BSP author made? (Drive strength? Pull resistors? I2C speed?)
- Can you find the Create → Configure → Control pattern inside the BSP code?
The BSP calls `Output::new()`, which calls into the HAL, which writes to PAC registers, which toggle actual hardware. Every layer you've learned is present — the BSP just wraps them up.
Step 2: Refactor Your Code
Take one of your earlier exercises — blinky or I2C scan — and refactor it to use the BSP:
Before (raw HAL):
#![allow(unused)] fn main() { let mut led = Output::new( peripherals.GPIO5, Level::Low, OutputConfig::default() ); }
After (BSP):
#![allow(unused)] fn main() { let mut led = board.led(); }
How much simpler is the code? What did you lose (if anything)?
Step 3: Look Beyond
What other convenience functions does the BSP provide? Can you find:
- I2C setup?
- Button input?
- Sensor initialization?
Try using one you haven't tried before.
Reflection
You've now seen every layer of the embedded Rust stack:
| Layer | What You Did |
|---|---|
| HAL | Configured GPIO pins, I2C buses manually |
| embedded-hal | Used traits that make driver crates portable |
| Driver crates | Read sensor data with hardware-agnostic drivers |
| BSP | Used board-specific convenience functions |
And for every layer, the same pattern: Create → Configure → Control.
You know how to read the docs for any of them. You know how to adapt examples. You know where to look when you need something new.
That's the skill that lasts.