From 93bba13981a7f111114c94391b5158d4d14d2bfd Mon Sep 17 00:00:00 2001 From: Jeff McBride Date: Wed, 8 Sep 2021 16:23:57 -0700 Subject: [PATCH] Support USB on samg family - Currently suports device mode only, not host - Adds example CDC app for samg55_xplained_pro board --- README.md | 2 +- .../samg55_xplained_pro/blink/project.xml | 2 +- .../samg55_xplained_pro/usbserial/main.cpp | 29 ++++++++++++++ .../samg55_xplained_pro/usbserial/project.xml | 13 ++++++ ext/hathach/module.lb | 14 +++++-- src/modm/board/samg55_xplained_pro/board.hpp | 20 ++++++++++ src/modm/board/samg55_xplained_pro/module.lb | 2 +- src/modm/platform/clock/samg/clockgen.hpp | 40 +++++++++++++++++++ .../platform/clock/samg/clockgen_impl.hpp | 31 ++++++++++++++ src/modm/platform/usb/sam/module.lb | 13 +++++- .../platform/usb/sam/{usb.hpp => usb.hpp.in} | 22 ++++++++++ 11 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 examples/samg55_xplained_pro/usbserial/main.cpp create mode 100644 examples/samg55_xplained_pro/usbserial/project.xml rename src/modm/platform/usb/sam/{usb.hpp => usb.hpp.in} (62%) diff --git a/README.md b/README.md index bb861836d7..1c5b16b4d7 100644 --- a/README.md +++ b/README.md @@ -455,7 +455,7 @@ Please [discover modm's peripheral drivers for your specific device][discover]. ✅ ✅ ✅ -✗ +✅ ✗ ✗ ✗ diff --git a/examples/samg55_xplained_pro/blink/project.xml b/examples/samg55_xplained_pro/blink/project.xml index 15d63cd482..1efaab682b 100644 --- a/examples/samg55_xplained_pro/blink/project.xml +++ b/examples/samg55_xplained_pro/blink/project.xml @@ -1,7 +1,7 @@ modm:samg55-xplained-pro - + modm:build:scons diff --git a/examples/samg55_xplained_pro/usbserial/main.cpp b/examples/samg55_xplained_pro/usbserial/main.cpp new file mode 100644 index 0000000000..b5a7373073 --- /dev/null +++ b/examples/samg55_xplained_pro/usbserial/main.cpp @@ -0,0 +1,29 @@ +#include +#include + +using namespace modm::platform; + +int main() { + Board::initialize(); + + // Pull DP low briefly on reset to make sure USB host disconnects/reconnects + MATRIX->CCFG_SYSIO |= (CCFG_SYSIO_SYSIO11 | CCFG_SYSIO_SYSIO10); + GpioA22::setOutput(false); + modm::delay_ms(5); + GpioA22::setInput(); + + Board::initializeUsbFs(); + + tusb_init(); + + while (true) + { + // Read any received data and echo it back + uint8_t buf[64]; + uint32_t read_count; + tud_task(); + read_count = UsbUart0::read(buf, sizeof(buf)); + UsbUart0::write(buf, read_count); + Board::Led::toggle(); + } +} \ No newline at end of file diff --git a/examples/samg55_xplained_pro/usbserial/project.xml b/examples/samg55_xplained_pro/usbserial/project.xml new file mode 100644 index 0000000000..6890ba3390 --- /dev/null +++ b/examples/samg55_xplained_pro/usbserial/project.xml @@ -0,0 +1,13 @@ + + modm:samg55-xplained-pro + + + + + + + + modm:tinyusb + modm:build:scons + + \ No newline at end of file diff --git a/ext/hathach/module.lb b/ext/hathach/module.lb index d8dba8ed2c..9acb160e9f 100644 --- a/ext/hathach/module.lb +++ b/ext/hathach/module.lb @@ -22,7 +22,9 @@ def init(module): def prepare(module, options): if not (options[":target"].has_driver("usb") or options[":target"].has_driver("usb_otg_fs") or - options[":target"].has_driver("usb_otg_hs")): + options[":target"].has_driver("usb_otg_hs") or + options[":target"].has_driver("udp") or + options[":target"].has_driver("uhp")): return False configs = {"device.cdc", "device.msc", "device.vendor", "device.midi", "device.dfu"} @@ -153,11 +155,16 @@ def build(env): raise ValidateException("Unknown ITF device class '{}'!".format(devclass)) endpoints[itf_prefix + "_OUT"] = hex(endpoint_counter) + # SAMG doesn't support using the same EP number for IN and OUT + if target.platform == "sam" and target.family in ["g"]: + endpoint_counter += 1 endpoints[itf_prefix + "_IN"] = hex(0x80 | endpoint_counter) if target.platform == "stm32": irq_data = env.query(":platform:usb:irqs") irqs = irq_data["port_irqs"][speed] + elif target.platform == "sam" and target.family in ["g"]: + irqs = ["UDP"] else: irqs = ["USB"] @@ -220,9 +227,10 @@ Configuration options: """ def prepare(self, module, options): - # Only OTG has Host Mode + # On STM32, only OTG has Host Mode if not (options[":target"].has_driver("usb_otg_fs") or - options[":target"].has_driver("usb_otg_hs")): + options[":target"].has_driver("usb_otg_hs") or + options[":target"].has_driver("uhp:samg*")): return False paths = {p.parent for p in Path(localpath("tinyusb/src/class/")).glob("*/*_host.h")} diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp index 631644627f..33022b1711 100644 --- a/src/modm/board/samg55_xplained_pro/board.hpp +++ b/src/modm/board/samg55_xplained_pro/board.hpp @@ -20,8 +20,13 @@ using namespace modm::platform; struct SystemClock { + // Chosen to achieve 120MHz system clock static constexpr uint32_t PllAMult = 3662; + // Chosen to achieve 48MHz USB clock + static constexpr uint32_t PllBMult = 1465; + static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz; + static constexpr uint32_t Usb = PllBMult * SlowClkFreqHz; static bool inline enable() { @@ -32,6 +37,15 @@ struct SystemClock ClockGen::selectMasterClk(); return true; } + + static bool inline + enableUsb() + { + ClockGen::enablePllB(); + // Use PLLB as USB clock source + PMC->PMC_USB = PMC_USB_USBS; + return true; + } }; using Led = GpioA6; @@ -51,5 +65,11 @@ initialize() Button::setInput(); } +inline void initializeUsbFs() +{ + SystemClock::enableUsb(); + modm::platform::Usb::initialize(); +} + } // namespace Board diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb index 3b293c4151..6b766ea002 100644 --- a/src/modm/board/samg55_xplained_pro/module.lb +++ b/src/modm/board/samg55_xplained_pro/module.lb @@ -18,7 +18,7 @@ def prepare(module, options): if not options[":target"].partname == "samg55j19a-au": return False - module.depends(":platform:clockgen", ":platform:gpio", ":platform:core"); + module.depends(":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usb"); return True def build(env): diff --git a/src/modm/platform/clock/samg/clockgen.hpp b/src/modm/platform/clock/samg/clockgen.hpp index 652754ca78..c3b745ba5f 100644 --- a/src/modm/platform/clock/samg/clockgen.hpp +++ b/src/modm/platform/clock/samg/clockgen.hpp @@ -43,6 +43,34 @@ enum class MainInternalFreq : uint32_t { Rc24Mhz }; +enum class ClockPeripheral : uint32_t { + FlexCom0 = ID_FLEXCOM0, + FlexCom1 = ID_FLEXCOM1, + FlexCom2 = ID_FLEXCOM2, + FlexCom3 = ID_FLEXCOM3, + FlexCom4 = ID_FLEXCOM4, + FlexCom5 = ID_FLEXCOM5, + FlexCom6 = ID_FLEXCOM6, + FlexCom7 = ID_FLEXCOM7, + PioA = ID_PIOA, + PioB = ID_PIOB, + Pdmic0 = ID_PDMIC0, + Pdmic1 = ID_PDMIC1, + Mem2Mem = ID_MEM2MEM, + I2sc0 = ID_I2SC0, + I2sc1 = ID_I2SC1, + Tc0 = ID_TC0_CHANNEL0, + Tc1 = ID_TC0_CHANNEL1, + Tc2 = ID_TC0_CHANNEL2, + Tc3 = ID_TC1_CHANNEL0, + Tc4 = ID_TC1_CHANNEL1, + Tc5 = ID_TC1_CHANNEL2, + Adc = ID_ADC, + Uhp = ID_UHP, + Udp = ID_UDP, + Crccu = ID_CRCCU +}; + static constexpr uint32_t SlowClkFreqHz = 32'768; /** @@ -105,6 +133,18 @@ class ClockGen { /** Returns the configured frequency of PLL B output */ static uint32_t pllBFrequency(); + + template< ClockPeripheral peripheral > + static void + enable(); + + template< ClockPeripheral peripheral > + static bool + isEnabled(); + + template< ClockPeripheral peripheral > + static void + disable(); }; } // namespace modm::platform diff --git a/src/modm/platform/clock/samg/clockgen_impl.hpp b/src/modm/platform/clock/samg/clockgen_impl.hpp index 9a62556b28..aa511eae16 100644 --- a/src/modm/platform/clock/samg/clockgen_impl.hpp +++ b/src/modm/platform/clock/samg/clockgen_impl.hpp @@ -76,4 +76,35 @@ void ClockGen::setFlashLatency() { EFC->EEFC_FMR = (EFC->EEFC_FMR & ~EEFC_FMR_FWS_Msk) | EEFC_FMR_FWS(fws); } +template< ClockPeripheral peripheral > +void ClockGen::enable() { + constexpr uint32_t id = (uint32_t)peripheral; + if constexpr (id < 32) { + PMC->PMC_PCER0 = (1<PMC_PCER1 = (1<<(id-32)); + } +} + +template< ClockPeripheral peripheral > +bool ClockGen::isEnabled() { + constexpr uint32_t id = (uint32_t)peripheral; + if constexpr (id < 32) { + return PMC->PMC_PCSR0 & (1<PMC_PCSR1 & (1<<(id-32)); + } +} + +template< ClockPeripheral peripheral > +void ClockGen::disable() { + constexpr uint32_t id = (uint32_t)peripheral; + if constexpr (id < 32) { + PMC->PMC_PCDR0 = (1<PMC_PCDR1 = (1<<(id-32)); + } +} + + } // namespace modm::platform diff --git a/src/modm/platform/usb/sam/module.lb b/src/modm/platform/usb/sam/module.lb index 398033deb5..2c01dc9031 100644 --- a/src/modm/platform/usb/sam/module.lb +++ b/src/modm/platform/usb/sam/module.lb @@ -16,7 +16,8 @@ def init(module): def prepare(module, options): device = options[":target"] - if not (device.has_driver("usb:sam*")): + if not (device.has_driver("usb:sam*") or + device.has_driver("udp:sam*")): return False module.depends( @@ -27,9 +28,17 @@ def prepare(module, options): return True +def validate(env): + if "samg" in str(env[":target"].identifier) and env.has_module(":tinyusb:host"): + raise ValidateException("USB HOST mode is not currently supported on SAMG family") + def build(env): + device = env[":target"] + properties = device.properties + properties["target"] = device.identifier + env.substitutions = properties env.outbasepath = "modm/src/modm/platform/usb" - env.copy("usb.hpp") + env.template("usb.hpp.in") if env.has_module(":tinyusb:device:cdc"): env.copy(repopath("ext/hathach/uart.hpp"), "uart.hpp") diff --git a/src/modm/platform/usb/sam/usb.hpp b/src/modm/platform/usb/sam/usb.hpp.in similarity index 62% rename from src/modm/platform/usb/sam/usb.hpp rename to src/modm/platform/usb/sam/usb.hpp.in index d8f7dd9364..4b83ab59d1 100644 --- a/src/modm/platform/usb/sam/usb.hpp +++ b/src/modm/platform/usb/sam/usb.hpp.in @@ -12,9 +12,11 @@ #pragma once +#include #include #include + namespace modm::platform { @@ -25,13 +27,32 @@ class Usb static void initialize(uint8_t priority=3) { +%% if target["family"] in ["g"] + // only the USB device mode is supported for now + static_assert(modm::Tolerance::isValueInTolerance(48_MHz, SystemClock::Usb, 0.25_pct), + "The USB clock frequency must be within 0.25\% of 48MHz!"); + // Select DM/DP function for PA21/22 + MATRIX->CCFG_SYSIO &= ~(CCFG_SYSIO_SYSIO11 | CCFG_SYSIO_SYSIO10); + // Put USB in device mode + MATRIX->CCFG_USBMR = CCFG_USBMR_USBMODE; + // Enable the 48MHz clock + PMC->PMC_SCER = PMC_SCER_UDP; + // Enable the CPU clock + ClockGen::enable(); + NVIC_SetPriority(UDP_IRQn, priority); +%% else static_assert(SystemClock::Frequency == 48_MHz, "Usb must have a 48MHz clock!"); PM->APBBMASK.reg |= PM_APBBMASK_USB; PM->AHBMASK.reg |= PM_AHBMASK_USB; GenericClockController::connect(ClockGenerator::System); NVIC_SetPriority(USB_IRQn, priority); +%% endif } +// SAMG does not provide a connect method for USB. It's not defined in the SVD, +// and the USB pins are fixed. Instead, calling Usb::initialize() switches the +// IO pin function to the USB controller. +%% if target["family"] not in ["g"] template static void connect() @@ -46,6 +67,7 @@ class Usb DpConnector::connect(); DmConnector::connect(); } +%% endif }; } // namespace modm::platform