Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions examples/nucleo_f446ze/random_access_gpio_port/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <modm/board.hpp>
#include <ranges>

using namespace Board;
using namespace std::literals;

int
main()
{
Board::initialize();

using LedPort = RandomAccessPort<LedRed, LedBlue, LedGreen>;
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<LedPort>);

auto reversedPins = port | std::views::reverse;
while (true)
{
for(auto pin : reversedPins) {
pin.toggle();
modm::delay(250ms);
}
}

return 0;
}
9 changes: 9 additions & 0 deletions examples/nucleo_f446ze/random_access_gpio_port/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<library>
<extends>modm:nucleo-f446ze</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_f446ze/random_access_gpio_port</option>
</options>
<modules>
<module>modm:build:scons</module>
</modules>
</library>
119 changes: 119 additions & 0 deletions src/modm/platform/gpio/stm32/gpio_accessor.hpp.in
Original file line number Diff line number Diff line change
@@ -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 <array>

#include "../device.hpp"
#include "base.hpp"

namespace modm::platform
{

/**
* Type-erased Gpio wrapper
*
* Example:
* @code
* GpioAccessor gpio = GpioAccessor::template fromGpio<GpioA1>();
* 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<GPIO_TypeDef *>(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<uintptr_t>(port)}, idBit{uint16_t(1u << position)}
{}

template<class Gpio>
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<typename... Gpios>
constexpr auto makeGpioArray()
{
return std::array{ GpioAccessor::template fromGpio<Gpios>()... };
}

}

#endif // MODM_STM32_GPIO_ACCESSOR_HPP
2 changes: 2 additions & 0 deletions src/modm/platform/gpio/stm32/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
176 changes: 176 additions & 0 deletions src/modm/platform/gpio/stm32/random_access_port.hpp
Original file line number Diff line number Diff line change
@@ -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 <array>
#include <iterator>
#include <algorithm>

#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<GpioA1, GpioB4, GpioC8>;
* 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<typename... Gpios>
class RandomAccessPort : public SoftwareGpioPort<Gpios...>
{
public:
using Index = int_fast8_t;
static constexpr inline size_t Size{sizeof...(Gpios)};

private:
static constexpr inline std::array<GpioAccessor, Size> GpioPins = [](){
auto gpios = makeGpioArray<Gpios...>();
// 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<GpioIterator>);

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