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