Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SAM software gpio port #952

Merged
Merged
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
21 changes: 1 addition & 20 deletions src/modm/board/samd21_xplained_pro/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,7 @@ struct SystemClock
using Led0 = GpioB30;
using Button = GpioA15;

// No SoftwareGpioPort yet for SAM
struct Leds
{
static constexpr std::size_t width{1};

static void setOutput()
{
Led0::setOutput();
}

static void write(uint32_t value)
{
Led0::set(value & 1);
}

static void toggle()
{
Led0::toggle();
}
};
using Leds = SoftwareGpioPort<Led0>;

struct Debug
{
Expand Down
16 changes: 1 addition & 15 deletions src/modm/board/same54_xplained_pro/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,7 @@ struct SystemClock
using Led0 = GpioC18;
using Button = GpioB31;

// No SoftwareGpioPort yet for SAM
struct Leds
{
static constexpr std::size_t width{1};

static void setOutput()
{
Led0::setOutput();
}

static void write(uint32_t value)
{
Led0::set(value & 1);
}
};
using Leds = SoftwareGpioPort<Led0>;

struct Debug
{
Expand Down
24 changes: 1 addition & 23 deletions src/modm/board/samv71_xplained_ultra/board.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,7 @@ using Led0 = GpioA23;
using Led1 = GpioC9;
using ButtonSW0 = GpioA9;

// No SoftwareGpioPort yet for SAM
struct Leds
{
static constexpr std::size_t width{2};

static void setOutput()
{
Led0::setOutput();
Led1::setOutput();
}

static void setOutput(bool state)
{
Led0::setOutput(state);
Led1::setOutput(state);
}

static void write(uint32_t value)
{
Led0::set(value & 1);
Led1::set(value & 2);
}
};
using Leds = SoftwareGpioPort<Led1, Led0>;

struct Debug
{
Expand Down
1 change: 1 addition & 0 deletions src/modm/platform/gpio/sam/module.lb
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,6 @@ def build(env):
if len(env["enable_ports"]):
env.template("enable.cpp.in")
env.template("pin.hpp.in")
env.template("software_port.hpp.in")
env.copy("unused.hpp")
env.copy("../common/inverted.hpp", "inverted.hpp")
86 changes: 80 additions & 6 deletions src/modm/platform/gpio/sam/pin.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,25 @@ struct OneOfSignals
static constexpr bool value = ((std::is_same_v<typename Signal::signal, Signals>) | ...);
};

// Detect if a pin is inverted
// Used to support both pins config and gpio classes being passed to GpioSet.
// Pin config classes don't define isInverted and are treated as non-inverted.
template<typename T>
struct is_inverted
{
static constexpr bool value = false;
};

template<typename T>
requires (T::isInverted == true)
struct is_inverted<T>
{
static constexpr bool value = true;
};

template<typename T>
constexpr bool is_inverted_v = is_inverted<T>::value;
salkinium marked this conversation as resolved.
Show resolved Hide resolved

%% if target["family"] in ["g5x", "e7x/s7x/v7x"]

template<class... PinConfigs>
Expand Down Expand Up @@ -143,12 +162,24 @@ class GpioSet : protected PinMuxMixin<PinConfigs...>
protected:
using PinMux = PinMuxMixin<PinConfigs...>;

static constexpr uint32_t
static consteval uint32_t
mask(PortName port)
{
return (((PinConfigs::port == port) ? 1u << PinConfigs::pin : 0u) | ...);
}

static consteval uint32_t
invertedMask(PortName port)
{
return (((PinConfigs::port == port) ? (is_inverted_v<PinConfigs> ? 1u : 0u) << PinConfigs::pin : 0u) | ...);
salkinium marked this conversation as resolved.
Show resolved Hide resolved
}

static consteval uint32_t
nonInvertedMask(PortName port)
{
return mask(port) & ~invertedMask(port);
}

template<PortName port>
inline static constexpr volatile uint32_t*
getPortReg(size_t offset)
Expand Down Expand Up @@ -237,7 +268,14 @@ public:
static void
set()
{
setPortReg(PIO_SODR_OFFSET);
%% for port in ports
if constexpr (nonInvertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PIO_SODR_OFFSET) = nonInvertedMask(PortName::{{ port }});
}
if constexpr (invertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PIO_CODR_OFFSET) = invertedMask(PortName::{{ port }});
}
%% endfor
}

static void
Expand All @@ -252,7 +290,14 @@ public:
static void
reset()
{
setPortReg(PIO_CODR_OFFSET);
%% for port in ports
if constexpr (nonInvertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PIO_CODR_OFFSET) = nonInvertedMask(PortName::{{ port }});
}
if constexpr (invertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PIO_SODR_OFFSET) = invertedMask(PortName::{{ port }});
}
%% endfor
}

static void
Expand Down Expand Up @@ -316,12 +361,24 @@ class GpioSet : protected PinCfgMixin<PinConfigs...>
protected:
using PinCfg = PinCfgMixin<PinConfigs...>;

static constexpr uint32_t
static consteval uint32_t
mask(PortName port)
{
return (((PinConfigs::port == port) ? 1u << PinConfigs::pin : 0u) | ...);
}

static consteval uint32_t
invertedMask(PortName port)
{
return (((PinConfigs::port == port) ? (is_inverted_v<PinConfigs> ? 1u : 0u) << PinConfigs::pin : 0u) | ...);
}

static consteval uint32_t
nonInvertedMask(PortName port)
{
return mask(port) & ~invertedMask(port);
}

template<PortName port>
inline static constexpr volatile uint32_t*
getPortReg(size_t offset)
Expand Down Expand Up @@ -396,7 +453,14 @@ public:
static void
set()
{
setPortReg(PORT_OUTSET_OFFSET);
%% for port in ports
if constexpr (nonInvertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PORT_OUTSET_OFFSET) = nonInvertedMask(PortName::{{ port }});
}
if constexpr (invertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PORT_OUTCLR_OFFSET) = invertedMask(PortName::{{ port }});
}
%% endfor
}

static void
Expand All @@ -411,7 +475,14 @@ public:
static void
reset()
{
setPortReg(PORT_OUTCLR_OFFSET);
%% for port in ports
if constexpr (nonInvertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PORT_OUTCLR_OFFSET) = nonInvertedMask(PortName::{{ port }});
}
if constexpr (invertedMask(PortName::{{ port }})) {
*getPortReg<PortName::{{ port }}>(PORT_OUTSET_OFFSET) = invertedMask(PortName::{{ port }});
}
%% endfor
}

static void
Expand Down Expand Up @@ -446,6 +517,9 @@ public:
using Data = PinConfig;
static constexpr bool isInverted = false;

static constexpr auto pin = PinConfig::pin;
static constexpr auto port = PinConfig::port;

template<PeripheralPin peripheral_pin_v>
struct As;

Expand Down
129 changes: 129 additions & 0 deletions src/modm/platform/gpio/sam/software_port.hpp.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2018, Niklas Hauser
* Copyright (c) 2023, 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/.
*/
// ----------------------------------------------------------------------------

#pragma once

#include "pin.hpp"
#include <type_traits>

namespace modm::platform
{

/**
* Create an up to 32-bit port from arbitrary pins.
*
* This class optimizes the data type for the `read()` and `write()` methods.
* Supplying up to 8 Gpios will use `uint8_t`, up to 16 Gpios `uint16_t` and
* up to 32 Gpios `uint32_t`.
*
* @note Since the bit order is explicitly given by the order of template arguments,
* this class only supports `DataOrder::Normal`.
* If you need reverse bit order, reverse the order of `Gpios`!
*
* @tparam Gpios Up to 32 GpioIO classes, ordered MSB to LSB
*
* @author Christopher Durand
* @author Niklas Hauser
* @ingroup modm_platform_gpio
*/
template<class... Gpios>
class SoftwareGpioPort : public ::modm::GpioPort, public GpioSet<Gpios...>
{
using Set = GpioSet<Gpios...>;
public:
static constexpr auto width = sizeof...(Gpios);
static_assert(width <= 32, "Only a maximum of 32 pins are supported by this port!");
using PortType = std::conditional_t< (width > 8),
std::conditional_t< (width > 16),
uint32_t,
uint16_t >,
uint8_t >;
static constexpr DataOrder getDataOrder()
{ return ::modm::GpioPort::DataOrder::Normal; }

protected:
%% for port in ports
static constexpr int8_t shift_masks_{{port}}[] = {
int8_t(Gpios::port == PortName::{{port}} ? Gpios::pin : -1)...
};
static_assert(std::size(shift_masks_{{port}}) == width);
static constexpr int8_t shift_mask_{{port}}(uint8_t pos) { return shift_masks_{{port}}[width - 1 - pos]; };
%% endfor
using Set::mask;
using Set::invertedMask;

public:
static PortType isSet()
{
PortType r{0};
%% for port in ports
if constexpr (mask(PortName::{{port}})) {
%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
const uint32_t p = Set::template readPortReg<PortName::{{port}}>(PIO_ODSR_OFFSET) ^ invertedMask(PortName::{{port}});
%% else
const uint32_t p = Set::template readPortReg<PortName::{{port}}>(PORT_OUT_OFFSET) ^ invertedMask(PortName::{{port}});
%% endif
%% for pos in range(32)
if constexpr ({{pos}} < width) if constexpr (constexpr auto pos = shift_mask_{{port}}({{pos}}); pos >= 0) r |= ((p >> pos) & 1) << {{pos}};
%% endfor
}
%% endfor
return r;
}

static void write(PortType data)
{
%% for port in ports
if constexpr (mask(PortName::{{port}})) {
uint32_t set{0};
uint32_t reset{0};
%% for pos in range(32)
if constexpr ({{pos}} < width) {
if constexpr (constexpr auto shift = shift_mask_{{port}}({{pos}}); shift >= 0) {
if (bool(data & (1ul << {{pos}})) != bool(invertedMask(PortName::{{port}}) & (1 << shift)))
set |= (1ul << shift);
else
reset |= (1ul << shift);
}
}
%% endfor
%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
*(Set::template getPortReg<PortName::{{port}}>(PIO_SODR_OFFSET)) = set;
*(Set::template getPortReg<PortName::{{port}}>(PIO_CODR_OFFSET)) = reset;
%% else
*(Set::template getPortReg<PortName::{{port}}>(PORT_OUTSET_OFFSET)) = set;
*(Set::template getPortReg<PortName::{{port}}>(PORT_OUTCLR_OFFSET)) = reset;
%% endif
}
%% endfor
}

static PortType read()
{
PortType r{0};
%% for port in ports
if constexpr (mask(PortName::{{port}})) {
%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
const uint32_t p = (Set::template readPortReg<PortName::{{port}}>(PIO_PDSR_OFFSET) & mask(PortName::{{port}})) ^ invertedMask(PortName::{{port}});
%% else
const uint32_t p = (Set::template readPortReg<PortName::{{port}}>(PORT_IN_OFFSET) & mask(PortName::{{port}})) ^ invertedMask(PortName::{{port}});
%% endif
%% for pos in range(32)
if constexpr ({{pos}} < width) if constexpr (constexpr auto pos = shift_mask_{{port}}({{pos}}); pos >= 0) r |= ((p >> pos) & 1) << {{pos}};
%% endfor
}
%% endfor
return r;
}
};

} // namespace modm::platform
Loading