API guide¶
Look up pins and channels, filter them and turn ADC counts into volts, by task. Every block compiles against both boards.
The shape of the API¶
| Task | Call | Returns |
|---|---|---|
| One pin's row | GetGpioMapping(HfFunctionalGpioPin) |
const HfGpioMapping*, nullptr if out of range |
| One ADC channel's row | GetAdcMapping(HfFunctionalAdcChannel) |
const HfAdcMapping* or nullptr |
| Every row | HF_GPIO_MAPPING[], HF_ADC_MAPPING[] with ..._SIZE |
constexpr arrays |
| Name of a pin or channel | to_string(pin), to_string(channel) |
std::string_view, "UNKNOWN_PIN" if out of range |
| Owner of a pin | get_pin_category(pin), should_register_as_gpio(category) |
HfPinCategory, bool |
| Pins by owner or chip | GetGpioPinsByCategory(), GetGpioPinsByChipType() |
count written to your array |
| Pull resistors | RequiresPullUp(pin), RequiresPullDown(pin) |
bool |
| Availability | IsGpioPinAvailable(pin), IsAdcChannelAvailable(ch) |
bool |
Walk the pins a GPIO layer owns¶
#include "hf_functional_pin_config.hpp" // the build defines one HARDFOC_BOARD_* macro
// Print where every pin that the GPIO layer should own lives on this board.
void list_gpio_pins() {
for (size_t i = 0; i < HF_GPIO_MAPPING_SIZE; ++i) {
const HfGpioMapping& m = HF_GPIO_MAPPING[i];
if (!should_register_as_gpio(static_cast<HfPinCategory>(m.category))) {
continue; // core and comm pins belong to the system and the bus drivers
}
std::printf("%-28.*s chip %u pin %2u %s\n", static_cast<int>(m.name.size()), m.name.data(),
m.chip_type, m.physical_pin, m.is_inverted ? "active-low" : "active-high");
}
}
A board HAL does the same loop and creates a driver for each row on the chip chip_type names
(static_cast<HfGpioChipType>(m.chip_type)).
Look up one pin¶
// Find one pin by function: the only place a pin number comes from.
bool chip_select_is_active_low(HfFunctionalGpioPin pin) {
const HfGpioMapping* m = GetGpioMapping(pin);
if (m == nullptr) {
return false; // not on this board
}
return m->is_inverted;
}
The enum value indexes the arrays directly, so the lookup is a bounds check and an array access.
Filter by chip¶
// Every pin a given chip carries, without a heap.
size_t pins_on_mcu(HfFunctionalGpioPin (&out)[HF_GPIO_MAPPING_SIZE]) {
return GetGpioPinsByChipType(HfGpioChipType::ESP32_INTERNAL, out, HF_GPIO_MAPPING_SIZE);
}
GetGpioPinsByCategory() works the same way with an HfPinCategory. Both write at most max_count entries and
return how many they wrote.
ADC channels¶
// ADC channels carry their scaling: full-scale voltage, resolution and divider.
float counts_to_volts(HfFunctionalAdcChannel ch, uint32_t counts) {
const HfAdcMapping* m = GetAdcMapping(ch);
if (m == nullptr) {
return 0.0F;
}
const float full_scale = static_cast<float>((1U << m->resolution_bits) - 1U);
return static_cast<float>(counts) / full_scale * (static_cast<float>(m->max_voltage_mv) / 1000.0F) *
m->voltage_divider;
}
max_voltage_mv is the full-scale input of the converter and voltage_divider the ratio of the external divider
in front of it (Flux V1 bus voltage: 11.0).
Names and categories¶
// Names for logs and consoles; categories decide who owns a pin.
void describe(HfFunctionalGpioPin pin) {
const std::string_view name = to_string(pin);
const bool owned_by_gpio = should_register_as_gpio(get_pin_category(pin));
std::printf("%.*s: %s\n", static_cast<int>(name.size()), name.data(), owned_by_gpio ? "GPIO" : "reserved");
}
Board-specific constants¶
A board header may export derived constants for code that calls the MCU SDK directly. Flux V1 has
kFluxHwUartFdo2TxGpio and kFluxHwUartFdo2RxGpio, read from the UART_FDO2_TX / UART_FDO2_RX rows, so they
cannot disagree with the map.
Next: Adding a board.