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