Board Support Packages¶
lbuild module: modm:board
modm provides pre-configured BSPs for several commercial off-the-shelf development boards. There are two main components to the BPSs:
- An inheritable configuration
board.xml
containing the HAL target, options and the board module. This pre-defined configuration is aliased as a repo configuration so that yourproject.xml
simply<extends>modm:{board-name}</extends>
. - A module
modm:board:{board-name}
that pulls in required dependencies, configures the modm library and provides the code to initialize the board. You can then#include <modm/board.hpp>
in your project.
The BSPs all use a common interface within a top-level namespace Board
:
Board::initialize()
: Initializes the targets clock system, logger, LEDs and Buttons.Board::initialize{subsystem}()
: Initializes optional board subsystems.Board::SystemClock
: Provides the clock configuration for use inPeripheral::initialize<Board::SystemClock, ...>()
.Board::Led{name}
: Board-specific LEDs are initialized as outputs and off.Board::Leds
: Amodm::platform::SoftwareGpioPort
containing all board LEDs.Board::Button
: Board-specific input buttons are initialized as input with pull-up/down as required.Board::{pin-name}
: All board-specific pins are aliased to their respectivemodm::platform::Gpio{port}{pin}
.
If the board supports a dedicated serial logging output the BSP redirects the
modm:debug
module debug stream MODM_LOG_INFO
as well as the output of the
standalone printf
function.
Please note that YOU must explicitly call the Board
functions to initialize
your hardware, just including the board module is not enough.
Here is an example using the modm:disco-f469ni
BSP:
#include <modm/board.hpp>
int main()
{
// ALWAYS initialize the board first!
Board::initialize();
// Then initialize the subsystems you want to use
Board::initializeDisplay();
// Set LEDs via the GPIO port
Board::Leds::write(0b1011);
// Use the Arduino pin names
Board::D0::setOutput(modm::Gpio::High);
Board::D1::setInput();
// Use the boards serial logging
MODM_LOG_INFO << "REBOOT!" << modm::endl;
while (true) {
// Link the LED to the button
Board::LedBlue::set(Board::Button:read());
}
return 0;
}
Only select one BSP module
Even though some targets have multiple BSPs modules available (for example: Blue Pill and Black Pill), you can only use one module, since all define the same functions resulting in naming conflicts.
Customization¶
The BSPs contain an opinionated set of pre-defined functionality and settings. This is great for just quickly prototyping something, however, when you want to use custom hardware, or even just change a few settings, it's better to use your own BSP:
- Generate the BSP closest to your custom hardware, then copy the files from
modm/src/modm/board/{name}
to your own project and modify them. - In your
project.xml
remove the board config inheritance (<extends>
) and instead copy the pre-defined options into your own config. - Check what modm modules you need to depend on and add them to your own project
(check for
module.depends(...)
in the BSPsmodule.lb
). - You may need to manually add the pre-defined collector values to your project
configuration (check for
env.collect(...)
in the BSPsmodule.lb
).
Create the SystemClock struct¶
The easiest way using ST's CubeMX tool.
1. CubeMX Clock Graph¶
First we create a project in CubeMX with the desired microcontroller using the largest (pin-count, flash) variant. CubeMX displays something like this in the "Clock configuration" tab:
Then configure all clocks, muxes, multipliers and dividers to the highest allowed clock speeds (*).
(*) exceptions: E.g. USB usually requires exactly 48 MHz.
This settings are reflected in the constants static constexpr uint32_t Frequency
,
Apb1
and Apb2
as well as in const Rcc::PllFactors pllFactors{...}
and the
following lines. The PllFactors
struct should be fairly self-explanatory.
2. Peripheral Mapping¶
As we can see in the graphic above, there are different clock ranges. Each peripheral is connected to a clock domain. Some peripherals have an upstream clock mux, this is currently ignored in modm and the default setting for the clock mux is assumed.
The figure shows the block diagram of the controller, which can be found at the beginning of the data sheet (not in the reference manual):
For each peripheral we create a static constexpr uint32_t
member in
the struct SystemClock
and assign the value of the clock domain to which
the peripheral is connected.