-
Notifications
You must be signed in to change notification settings - Fork 5
Configuration
Every driver will need to be configured to work with your hardware. For example, you'll need to select the GPIO pins and ports, the peripheral number (SPI1 or SPI2), and associated DMA settings, interrupts numbers, clock speeds, etc. Hopefully the method of configuration will be obvious, but this page describes in detail the ways mdrivlib handlers driver configuration.
There are several ways drivers in mdrivlib are configured:
- Template parameters:
// Initialize ADC1 channel 13 to return ADC readings as uint16_t
AdcChan<AdcPeriphNum::_1, AdcChanNum::_13, uint16_t> myADC;
-
Template struct:
Multiple parameters are wrapped into a struct and passed as a template argument. Since every element of the struct is
constexpr
, the resulting compiled code will be require less RAM look-ups and will generally run faster. Also, this method supports default parameter values through inheritance of configuration classes.
// Inherit from the default SPI configuration, overriding where needed:
struct ExampleSpiAdcConfig : mdrivlib::DefaultSpiConfig {
static constexpr uint16_t PeriphNum = 2; // SPI2
static constexpr uint16_t NumChips = 2; //Two external peripheral chips on this bus
static constexpr IRQType IRQn = SPI2_IRQn;
static constexpr PinNoInit SCLK = {GPIO::A, 9, LL_GPIO_AF_5};
static constexpr PinNoInit COPI = {GPIO::B, 15, LL_GPIO_AF_5};
static constexpr PinNoInit CS0 = {GPIO::B, 9, 0};
static constexpr PinNoInit CS1 = {GPIO::B, 8, 0};
//...
};
// Pass the struct as a template parameter:
AdcSpi_MAX11666<ExampleSpiAdcConfig> myADC;
- Constructor/init params: normal function parameters passed to the constructor or an init function. e.g.:
const uint32_t period = 256;
const uint16_t prescaler = 1;
const uint32_t clock_division = 1;
TimPwmChannel myAnalogOut{TIM2, TimChannelNum::3, period, prescaler, clock_division};
myAnalogOut.set(100);
- Config struct: wraps multiple params into a struct passed as a constructor argument:
I2CPeriph myI2C {
.I2Cx = I2C3,
.SCL = {GPIO::B, 8, LL_GPIO_AF4},
.SDA = {GPIO::B, 9, LL_GPIO_AF4},
// ...
};
myI2C.write(deviceAddr, data, dataSize);
Take a look at the default SPI configuration in drivers/spi_config_struct.hh
:
struct DefaultSpiConf {
static constexpr uint16_t PeriphNum = 1; // 1 ==> SPI1, 2 ==> SPI2, ...
static constexpr uint16_t NumChips = 1;
static constexpr bool is_controller = true; // aka "master"
static constexpr IRQType IRQn = SPI1_IRQn;
static constexpr uint16_t priority1 = 3;
static constexpr uint16_t priority2 = 3;
static constexpr PinNoInit SCLK = {GPIO::A, 0, LL_GPIO_AF_0};
static constexpr PinNoInit COPI = {GPIO::A, 0, LL_GPIO_AF_0};
static constexpr PinNoInit CIPO = {GPIO::A, 0, LL_GPIO_AF_0};
static constexpr PinNoInit CS0 = {GPIO::A, 0, LL_GPIO_AF_0};
//...
};
To use these types of structs, create your own struct type that derives from the DefaultSpiConf type, and override whatever values you don't want to be default.
This type of configuration is used when configuration values are required during the peripheral's normal operation (as opposed to just during the peripheral's setup), and using values stored in RAM effects performance. For instance, a high-speed SPI DAC driver with multiple chips on a single SPI bus requires manually toggling the Chip Select pins in the SPI interrupt, because the STM32 SPI peripheral does not support multiple CS lines. Using constexpr values allows for the GPIO register addresses and values for the CS pins to be hard-coded into the interrupt handler's assembly, saving valuable cycles.
In general, the config structs found in files named *_config_struct.hh
. The I2CPeriph struct from this example is defined in drivers/i2c_config_struct.hh
:
struct I2CConfig {
I2C_TypeDef *I2Cx;
PinNoInit SCL;
PinNoInit SDA;
// etc..
};
Instead of including the configuration inline, you can define all your conf structs in one or more header files like this:
//my_i2c_config.hh file:
const I2CConfig myI2Cconf {
.I2Cx = I2C3,
.SCL = {GPIO::B, 8, LL_GPIO_AF4},
.SDA = {GPIO::B, 9, LL_GPIO_AF4},
// etc..
};
//.cc file:
I2CPeriph myI2C {myI2Cconf};
myI2C.write(deviceAddr, data, dataSize);
- Todo: move all
config_struct.hh
files to aconf_struct/
dir - Todo: create conf structs for adc_builtin, ... (others)