diff --git a/README.md b/README.md
index 84afd658ef..a8c758785e 100644
--- a/README.md
+++ b/README.md
@@ -437,7 +437,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
-○ |
+✅ |
○ |
✅ |
✅ |
diff --git a/examples/samg55_xplained_pro/uart/main.cpp b/examples/samg55_xplained_pro/uart/main.cpp
new file mode 100644
index 0000000000..9baa11721b
--- /dev/null
+++ b/examples/samg55_xplained_pro/uart/main.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2021, Jeff McBride
+ *
+ * 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 modm::platform;
+using namespace modm::literals;
+
+// Create IO wrapper for the debug UART, which is connected to the built-in
+// USB debugger virtual COM port
+modm::IODeviceWrapper debugDevice;
+modm::IOStream debugStream(debugDevice);
+
+int
+main()
+{
+ Board::initialize();
+
+ uint32_t cycle = 0;
+ while (true)
+ {
+ modm::delay(1s);
+ debugStream.printf("Cycle: %lu\r\n", cycle);
+ }
+}
\ No newline at end of file
diff --git a/examples/samg55_xplained_pro/uart/project.xml b/examples/samg55_xplained_pro/uart/project.xml
new file mode 100644
index 0000000000..a79d630b84
--- /dev/null
+++ b/examples/samg55_xplained_pro/uart/project.xml
@@ -0,0 +1,9 @@
+
+ modm:samg55-xplained-pro
+
+
+
+
+ modm:build:scons
+
+
\ No newline at end of file
diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp
index 33022b1711..078289d939 100644
--- a/src/modm/board/samg55_xplained_pro/board.hpp
+++ b/src/modm/board/samg55_xplained_pro/board.hpp
@@ -25,8 +25,10 @@ struct SystemClock
// Chosen to achieve 48MHz USB clock
static constexpr uint32_t PllBMult = 1465;
- static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz;
+ static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz; // CPU core frequency
static constexpr uint32_t Usb = PllBMult * SlowClkFreqHz;
+ static constexpr uint32_t Mck = Frequency; // Master clock, used by most peripherals
+
static bool inline
enable()
{
@@ -50,6 +52,9 @@ struct SystemClock
using Led = GpioA6;
using Button = GpioA2;
+using DebugUart = Uart7;
+using TxPin = GpioA28;
+using RxPin = GpioA27;
inline void
initialize()
@@ -60,6 +65,9 @@ initialize()
SystemClock::enable();
SysTickTimer::initialize();
+ DebugUart::initialize();
+ DebugUart::connect();
+
Led::setOutput(modm::Gpio::Low);
Button::setInput();
diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb
index 6b766ea002..b3302f866c 100644
--- a/src/modm/board/samg55_xplained_pro/module.lb
+++ b/src/modm/board/samg55_xplained_pro/module.lb
@@ -18,7 +18,13 @@ def prepare(module, options):
if not options[":target"].partname == "samg55j19a-au":
return False
- module.depends(":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usb");
+ module.depends(
+ ":platform:clockgen",
+ ":platform:uart:7",
+ ":platform:gpio",
+ ":platform:core",
+ ":platform:usb",
+ ":io")
return True
def build(env):
diff --git a/src/modm/platform/uart/samg/module.lb b/src/modm/platform/uart/samg/module.lb
new file mode 100644
index 0000000000..e2a0851920
--- /dev/null
+++ b/src/modm/platform/uart/samg/module.lb
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, Jeff McBride
+#
+# 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/.
+# -----------------------------------------------------------------------------
+
+props = {}
+
+class Instance(Module):
+ def __init__(self, driver, instance):
+ self.driver = driver
+ self.instance = int(instance)
+
+ def init(self, module):
+ module.name = str(self.instance)
+ module.description = "Instance {}".format(self.instance)
+
+ def prepare(self, module, options):
+ module.depends(":platform:uart")
+
+ module.add_option(
+ NumericOption(
+ name="buffer.tx",
+ description="Size of transmit buffer",
+ minimum=1, maximum=2 ** 16 - 2,
+ default=64))
+ module.add_option(
+ NumericOption(
+ name="buffer.rx",
+ description="Size of receive buffer",
+ minimum=1, maximum=2 ** 16 - 2,
+ default=64))
+
+ return True
+
+ def build(self, env):
+ device = env[":target"].identifier
+ global props
+ props["id"] = self.instance
+
+ env.substitutions = props
+ env.outbasepath = "modm/src/modm/platform/uart"
+
+ env.template("uart.hpp.in", "uart_{}.hpp".format(self.instance))
+ env.template("uart.cpp.in", "uart_{}.cpp".format(self.instance))
+
+
+def init(module):
+ module.name = ":platform:uart"
+ module.description = "Universal Synchronous Asynchronous Receiver Transmitter (UART)"
+
+def prepare(module, options):
+ device = options[":target"]
+ if not (device.has_driver("usart:samg*")):
+ return False
+
+ module.depends(
+ ":architecture:uart",
+ ":math:algorithm",
+ ":cmsis:device",
+ ":platform:gpio",
+ ":platform:clockgen")
+
+ global props
+ drivers = options[":target"].get_all_drivers("usart")
+ for driver in drivers:
+ for instance in driver["instance"]:
+ module.add_submodule(Instance(driver, instance))
+
+ props["target"] = device.identifier
+ return True
+
+def build(env):
+ global props
+ env.substitutions = props
+ env.outbasepath = "modm/src/modm/platform/uart"
+ env.copy("uart_base.hpp")
diff --git a/src/modm/platform/uart/samg/uart.cpp.in b/src/modm/platform/uart/samg/uart.cpp.in
new file mode 100644
index 0000000000..d3462c082d
--- /dev/null
+++ b/src/modm/platform/uart/samg/uart.cpp.in
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2021, Jeff McBride
+ *
+ * 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 "uart_{{ id }}.hpp"
+
+#include
+
+namespace
+{
+ static modm::atomic::Queue rxBuffer;
+ static modm::atomic::Queue txBuffer;
+}
+
+MODM_ISR(FLEXCOM{{ id }})
+{
+ using namespace modm::platform;
+ if(Uart{{ id }}::isReceiveReady()) {
+ uint8_t data = (uint8_t)USART{{ id }}->US_RHR;
+ Uart{{ id }}::read(data);
+ rxBuffer.push(data);
+ }
+
+ if(Uart{{ id }}::isTransmitReady()) {
+ if(txBuffer.isEmpty()) {
+ USART{{ id }}->US_IDR = US_IDR_TXRDY;
+ } else {
+ USART{{ id }}->US_THR = txBuffer.get();
+ txBuffer.pop();
+ }
+ }
+}
+
+namespace modm::platform
+{
+
+bool
+Uart{{ id }}::read(uint8_t &dataOut) {
+ if(rxBuffer.isEmpty()) {
+ return false;
+ } else {
+ dataOut = rxBuffer.get();
+ rxBuffer.pop();
+ return true;
+ }
+}
+
+std::size_t
+Uart{{ id }}::read(uint8_t *data, std::size_t length) {
+ uint32_t i = 0;
+ for(; i < length; i++) {
+ if(rxBuffer.isEmpty()) {
+ return i;
+ } else {
+ data[i] = rxBuffer.get();
+ rxBuffer.pop();
+ }
+ }
+ return i;
+}
+
+bool
+Uart{{ id }}::write(uint8_t data)
+{
+ if(txBuffer.isEmpty() && isTransmitReady()) {
+ USART{{ id }}->US_THR = data;
+ } else {
+ if(!txBuffer.push(data)) {
+ return false;
+ }
+ // Enable tx interrupt
+ USART{{ id }}->US_IER = US_IER_TXRDY;
+ }
+ return true;
+}
+
+std::size_t
+Uart{{ id }}::write(const uint8_t *data, std::size_t length)
+{
+ uint32_t i = 0;
+ for(; i < length; i++) {
+ if(!write(data[i])) {
+ return i;
+ }
+ }
+ return i;
+}
+
+bool
+Uart{{ id }}::isWriteFinished()
+{
+ return txBuffer.isEmpty() && isTransmitReady();
+}
+
+void
+Uart{{ id }}::flushWriteBuffer()
+{
+ while(!isWriteFinished());
+}
+
+void
+Uart{{ id }}::setParity(Parity parity)
+{
+ USART{{ id }}->US_MR = (USART{{ id }}->US_MR & ~US_MR_PAR_Msk) | (uint32_t)parity;
+}
+
+void
+Uart{{ id }}::setWordLength(WordLength length)
+{
+ if(length == WordLength::Bit9) {
+ USART{{ id }}->US_MR |= US_MR_MODE9;
+ } else {
+ USART{{ id }}->US_MR &= ~US_MR_MODE9;
+ USART{{ id }}->US_MR =
+ (USART{{ id }}->US_MR & ~US_MR_CHRL_Msk)
+ | US_MR_CHRL((uint32_t)length);
+ }
+}
+
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/uart/samg/uart.hpp.in b/src/modm/platform/uart/samg/uart.hpp.in
new file mode 100644
index 0000000000..93b394f596
--- /dev/null
+++ b/src/modm/platform/uart/samg/uart.hpp.in
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2021, Jeff McBride
+ *
+ * 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 "uart_base.hpp"
+#include
+#include
+#include
+#include
+
+namespace modm::platform
+{
+
+/**
+ * Universal synchronous asynchronous receiver transmitter (USART{{ id }})
+ *
+ * @author Jeff McBride
+ * @ingroup modm_platform_uart modm_platform_uart_{{id}}
+ */
+class Uart{{ id }} : public UartBase, public modm::Uart
+{
+public:
+ static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }};
+ static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }};
+
+ template< class... Pins >
+ static void
+ connect()
+ {
+ using RxPin = GetPin_t;
+ using TxPin = GetPin_t;
+ static_assert(
+ !std::is_same_v,
+ "Rx and Tx cannot use the same pin!");
+ using Flexcom = Peripherals::Flexcom<{{ id | int }}>;
+ using RxConnector = typename RxPin::template Connector;
+ using TxConnector = typename TxPin::template Connector;
+ RxConnector::connect();
+ TxConnector::connect();
+ }
+
+
+ template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) >
+ static inline void
+ initialize(
+ Parity parity=Parity::Disabled,
+ WordLength length=WordLength::Bit8,
+ uint8_t irq_priority = 5)
+ {
+ ClockGen::enable();
+ FLEXCOM{{ id }}->FLEXCOM_MR = FLEXCOM_MR_OPMODE_USART;
+ constexpr auto result = Prescaler::from_function(
+ SystemClock::Mck,
+ baudrate,
+ 1,
+ 65535,
+ [](uint32_t x) { return x * 8; }
+ );
+
+ USART{{ id }}->US_BRGR = result.index;
+
+ // Use 8x oversampling (this affects baud rate generation)
+ USART{{ id }}->US_MR = US_MR_OVER;
+
+ setParity(parity);
+ setWordLength(length);
+
+ USART{{ id }}->US_CR = US_CR_RXEN | US_CR_TXEN;
+
+ // Enable the IRQ
+
+ NVIC_SetPriority(FLEXCOM{{ id }}_IRQn, irq_priority);
+ NVIC_EnableIRQ(FLEXCOM{{ id }}_IRQn);
+ }
+
+ static bool read(uint8_t &dataOut);
+
+ static std::size_t read(uint8_t *data, std::size_t length);
+
+ static bool write(uint8_t data);
+
+ static std::size_t write(const uint8_t *data, std::size_t length);
+
+ static bool isWriteFinished();
+
+ static void flushWriteBuffer();
+
+ static void setParity(Parity parity);
+
+ static void setWordLength(WordLength length);
+
+ static inline bool isTransmitReady() { return USART{{ id }}->US_CSR & US_CSR_TXRDY; }
+
+ static inline bool isReceiveReady() { return USART{{ id }}->US_CSR & US_CSR_RXRDY; }
+};
+
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/samg/uart_base.hpp b/src/modm/platform/uart/samg/uart_base.hpp
new file mode 100644
index 0000000000..5d3a46b094
--- /dev/null
+++ b/src/modm/platform/uart/samg/uart_base.hpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, Jeff McBride
+ *
+ * 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 "../device.hpp"
+
+namespace modm::platform
+{
+
+class UartBase
+{
+public:
+ enum class Parity : uint32_t
+ {
+ Disabled = US_MR_PAR_NO,
+ Even = US_MR_PAR_EVEN,
+ Odd = US_MR_PAR_ODD,
+ Space = US_MR_PAR_SPACE,
+ Mark = US_MR_PAR_MARK,
+ MultiDrop = US_MR_PAR_MULTIDROP
+ };
+
+ enum class WordLength : uint32_t
+ {
+ Bit5 = 0,
+ Bit6,
+ Bit7,
+ Bit8,
+ Bit9
+ };
+};
+
+} // namespace modm::platform