diff --git a/examples/nucleo_f446ze/random_access_gpio_port/main.cpp b/examples/nucleo_f446ze/random_access_gpio_port/main.cpp new file mode 100644 index 0000000000..d598adaf8c --- /dev/null +++ b/examples/nucleo_f446ze/random_access_gpio_port/main.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021, Christopher Durand + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include +#include + +using namespace Board; +using namespace std::literals; + +int +main() +{ + Board::initialize(); + + using LedPort = RandomAccessPort; + LedPort::setOutput(); + + // Enable output with index 2 => LedRed + LedPort::set(2); + modm::delay(1000ms); + + // Type-erased pin wrapper + const auto port = LedPort{}; + port[1].set(); + + GpioAccessor pin = port[0]; + pin.set(); + + modm::delay(500ms); + + // Demonstrate iterator/range interface + static_assert(std::ranges::random_access_range); + + auto reversedPins = port | std::views::reverse; + while (true) + { + for(auto pin : reversedPins) { + pin.toggle(); + modm::delay(250ms); + } + } + + return 0; +} diff --git a/examples/nucleo_f446ze/random_access_gpio_port/project.xml b/examples/nucleo_f446ze/random_access_gpio_port/project.xml new file mode 100644 index 0000000000..06d955fcff --- /dev/null +++ b/examples/nucleo_f446ze/random_access_gpio_port/project.xml @@ -0,0 +1,9 @@ + + modm:nucleo-f446ze + + + + + modm:build:scons + + diff --git a/src/modm/platform/gpio/stm32/gpio_accessor.hpp.in b/src/modm/platform/gpio/stm32/gpio_accessor.hpp.in new file mode 100644 index 0000000000..b47ba63449 --- /dev/null +++ b/src/modm/platform/gpio/stm32/gpio_accessor.hpp.in @@ -0,0 +1,119 @@ +/* +* Copyright (c) 2021, Christopher Durand +* +* This file is part of the modm project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_GPIO_ACCESSOR_HPP +#define MODM_STM32_GPIO_ACCESSOR_HPP + +#include + +#include "../device.hpp" +#include "base.hpp" + +namespace modm::platform +{ + +/** +* Type-erased Gpio wrapper +* +* Example: +* @code +* GpioAccessor gpio = GpioAccessor::template fromGpio(); +* gpio.toggle(); +* @endcode +* +* @author Christopher Durand +* @ingroup modm_platform_gpio +*/ +class GpioAccessor : public Gpio +{ +private: + // Store port as uintptr_t instead of GPIO_TypeDef* + // because ST's GPIOx macros have an int-to-pointer cast + // not possible during compile-time evaluation + uintptr_t portAddress; + uint16_t idBit; + + static consteval uintptr_t + getPort(Port id) + { + switch (id) + { +%% for port in ports + case Port::{{ port | upper }}: + return GPIO{{ port | upper }}_BASE; +%% endfor + } + } + + auto port() const { return reinterpret_cast(portAddress); } + + constexpr GpioAccessor(uintptr_t port, uint8_t position) + : portAddress{port}, idBit{uint16_t(1u << position)} + {} + +public: + using PortType = GPIO_TypeDef *; + + GpioAccessor(PortType port, uint8_t position) + : portAddress{reinterpret_cast(port)}, idBit{uint16_t(1u << position)} + {} + + template + static constexpr GpioAccessor fromGpio() + { + static_assert(!Gpio::isInverted, "Inverted Gpios are not supported"); + return GpioAccessor{getPort(Gpio::port), Gpio::pin}; + } + + void set() const + { + port()->BSRR = idBit; + } + + void reset() const + { + port()->BSRR = (idBit << 16); + } + + void set(bool enable) const + { + if(enable) { + set(); + } else { + reset(); + } + } + + bool isSet() const + { + return (port()->ODR & idBit); + } + + void toggle() const + { + set(!isSet()); + } + + bool read() const + { + return (port()->IDR & idBit); + } +}; + +template +constexpr auto makeGpioArray() +{ + return std::array{ GpioAccessor::template fromGpio()... }; +} + +} + +#endif // MODM_STM32_GPIO_ACCESSOR_HPP diff --git a/src/modm/platform/gpio/stm32/module.lb b/src/modm/platform/gpio/stm32/module.lb index 2e4814dcbe..c4abc895b6 100644 --- a/src/modm/platform/gpio/stm32/module.lb +++ b/src/modm/platform/gpio/stm32/module.lb @@ -303,7 +303,9 @@ def build(env): env.template("unused.hpp.in") if len(env["enable_ports"]): env.template("enable.cpp.in") + env.template("gpio_accessor.hpp.in") + env.copy("random_access_port.hpp") env.copy("../common/inverted.hpp", "inverted.hpp") env.copy("../common/connector.hpp", "connector.hpp") env.template("../common/connector_detail.hpp.in", "connector_detail.hpp") diff --git a/src/modm/platform/gpio/stm32/random_access_port.hpp b/src/modm/platform/gpio/stm32/random_access_port.hpp new file mode 100644 index 0000000000..f945215203 --- /dev/null +++ b/src/modm/platform/gpio/stm32/random_access_port.hpp @@ -0,0 +1,176 @@ +/* +* Copyright (c) 2021, Christopher Durand +* +* This file is part of the modm project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ +// ---------------------------------------------------------------------------- + +#ifndef MODM_STM32_RANDOM_ACCESS_PORT_HPP +#define MODM_STM32_RANDOM_ACCESS_PORT_HPP + +#include +#include +#include + +#include "software_port.hpp" +#include "gpio_accessor.hpp" + +namespace modm::platform +{ + +/** +* Extended software Gpio port with single pin random access. It inherits all +* functionality from SoftwareGpioPort with the additional feature to set and +* read single Gpios with a runtime index. +* +* It conforms to the std::ranges::random_access_range concept. +* +* Example: +* @code +* using Port = RandomAccessPort; +* Port::setOutput(); +* Port::set(2); // set pin with index 2 => A1 +* +* Port port; +* // set pin C8 +* GpioAccessor pin = port[0]; +* pin.set(); +* +* auto reversedPins = port | std::views::reverse; +* for(auto pin : reversedPins) { +* pin.toggle(); +* } +* @endcode +* +* @tparam Gpios Up to 32 Gpios, ordered MSB to LSB +* +* @author Christopher Durand +* @ingroup modm_platform_gpio +*/ +template +class RandomAccessPort : public SoftwareGpioPort +{ +public: + using Index = int_fast8_t; + static constexpr inline size_t Size{sizeof...(Gpios)}; + +private: + static constexpr inline std::array GpioPins = [](){ + auto gpios = makeGpioArray(); + // Reverse pin order to match SoftwareGpioPort indexing + std::reverse(gpios.begin(), gpios.end()); + return gpios; + }(); + + friend class GpioIterator; + +public: + class GpioIterator + { + private: + Index index_; + + public: + using iterator_category = std::random_access_iterator_tag; + using difference_type = Index; + using value_type = GpioAccessor; + using reference = const GpioAccessor&; + + constexpr GpioIterator() = default; + constexpr explicit GpioIterator(Index index) : index_{index} + {} + + constexpr inline GpioIterator& + operator+=(difference_type diff) { index_ += diff; return *this; } + + constexpr inline GpioIterator& + operator-=(difference_type diff) { index_ -= diff; return *this; } + + constexpr inline reference + operator*() const { return RandomAccessPort::GpioPins[index_]; } + + constexpr inline reference + operator[](difference_type diff) const { return RandomAccessPort::GpioPins[index_ + diff]; } + + constexpr inline GpioIterator& + operator++() { ++index_; return *this; } + + constexpr inline GpioIterator& + operator--() { --index_; return *this; } + + constexpr inline GpioIterator + operator++(int) { GpioIterator tmp{*this}; ++index_; return tmp; } + + constexpr inline GpioIterator + operator--(int) { GpioIterator tmp{*this}; --index_; return tmp; } + + constexpr inline difference_type + operator-(const GpioIterator& rhs) const { return index_ - rhs.index_; } + + constexpr inline GpioIterator + operator+(difference_type diff) const { return GpioIterator{index_ + diff}; } + + constexpr inline GpioIterator + operator-(difference_type diff) const { return GpioIterator{index_ - diff}; } + + friend constexpr inline GpioIterator + operator+(difference_type lhs, const GpioIterator& rhs) { return GpioIterator{lhs + rhs.index_}; } + + friend constexpr inline GpioIterator + operator-(difference_type lhs, const GpioIterator& rhs) { return GpioIterator{lhs - rhs.index_}; } + + constexpr inline auto + operator<=>(const GpioIterator& rhs) const = default; + }; + static_assert(std::random_access_iterator); + + constexpr GpioIterator + begin() const { return GpioIterator{0}; } + + constexpr GpioIterator + end() const { return GpioIterator{Size}; } + + constexpr const GpioAccessor& + operator[](Index index) const + { + return GpioPins[index]; + } + + static void set(Index index, bool enable) + { + GpioPins[index].set(enable); + } + + static void set(Index index) + { + GpioPins[index].set(); + } + + static void reset(Index index) + { + GpioPins[index].reset(); + } + + static void toggle(Index index) + { + GpioPins[index].toggle(); + } + + static bool read(Index index) + { + return GpioPins[index].read(); + } + + static bool isSet(Index index) + { + return GpioPins[index].isSet(); + } +}; + +} + +#endif // MODM_STM32_RANDOM_ACCESS_PORT_HPP