diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 6722d31c22..57659fb3dc 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -68,10 +68,14 @@ jobs:
if: always()
run: |
(cd examples && ../tools/scripts/examples_compile.py generic)
- - name: Examples SAM Devices
+ - name: Examples SAMD Devices
if: always()
run: |
(cd examples && ../tools/scripts/examples_compile.py samd)
+ - name: Examples SAMG Devices
+ if: always()
+ run: |
+ (cd examples && ../tools/scripts/examples_compile.py samg55_xplained_pro)
- name: Execute Python Scripts
if: always()
run: |
diff --git a/README.md b/README.md
index 81fca96f93..bb861836d7 100644
--- a/README.md
+++ b/README.md
@@ -74,10 +74,10 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git
## Microcontrollers
-modm can create a HAL for 3172 devices of these vendors:
+modm can create a HAL for 3175 devices of these vendors:
- STMicroelectronics STM32: 2621 devices.
-- Microchip SAM: 163 devices.
+- Microchip SAM: 166 devices.
- Microchip AVR: 388 devices.
Here is a table with all device families and the peripheral drivers they support:
@@ -96,7 +96,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
|
STM32 |
-SAM |
+SAM |
AT |
Peripheral |
@@ -113,6 +113,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
L1 |
L4 |
D21 |
+G |
90 |
Mega |
Tiny |
@@ -132,6 +133,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
○ |
○ |
+○ |
✅ |
✅ |
@@ -149,6 +151,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✅ |
✗ |
+✗ |
○ |
○ |
✗ |
@@ -167,6 +170,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
✅ |
○ |
+✗ |
○ |
○ |
○ |
@@ -186,6 +190,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
○ |
✗ |
+✗ |
○ |
✗ |
@@ -206,6 +211,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
Ethernet |
✗ |
@@ -224,6 +230,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
External Interrupt |
✅ |
@@ -242,6 +249,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
External Memory |
✗ |
@@ -260,6 +268,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
GPIO |
✅ |
@@ -278,6 +287,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+✅ |
I2C |
✅ |
@@ -293,6 +303,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
○ |
+○ |
✅ |
✅ |
✅ |
@@ -314,6 +325,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
Random Generator |
✗ |
@@ -332,6 +344,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
SPI |
✅ |
@@ -347,6 +360,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
○ |
+○ |
✅ |
✅ |
✅ |
@@ -368,6 +382,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
Timer |
✅ |
@@ -386,6 +401,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○ |
○ |
○ |
+○ |
UART |
✅ |
@@ -401,6 +417,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅ |
✅ |
✅ |
+○ |
✅ |
✅ |
○ |
@@ -422,6 +439,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
USB |
✅ |
@@ -440,6 +458,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✗ |
✗ |
✗ |
+✗ |
@@ -523,9 +542,11 @@ We have out-of-box support for many development boards including documentation.
Raspberry Pi |
SAMD21-MINI |
+SAMG55-XPLAINED-PRO |
STM32-F4VE |
STM32F030-DEMO |
Smart Response XE |
+
diff --git a/examples/samg55_xplained_pro/blink/main.cpp b/examples/samg55_xplained_pro/blink/main.cpp
new file mode 100644
index 0000000000..c6f374328c
--- /dev/null
+++ b/examples/samg55_xplained_pro/blink/main.cpp
@@ -0,0 +1,13 @@
+#include "modm/board.hpp"
+
+using namespace modm::platform;
+
+int main() {
+ Board::initialize();
+
+ while(true) {
+ Board::Led::toggle();
+ modm::delay_ms(500);
+ }
+
+}
\ No newline at end of file
diff --git a/examples/samg55_xplained_pro/blink/project.xml b/examples/samg55_xplained_pro/blink/project.xml
new file mode 100644
index 0000000000..15d63cd482
--- /dev/null
+++ b/examples/samg55_xplained_pro/blink/project.xml
@@ -0,0 +1,9 @@
+
+ modm:samg55-xplained-pro
+
+
+
+
+ modm:build:scons
+
+
\ No newline at end of file
diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb
index 026f0a9cf9..a875db6af9 100644
--- a/ext/microchip/module.lb
+++ b/ext/microchip/module.lb
@@ -28,26 +28,35 @@ def prepare(module, options):
pp = {}
def validate(env):
device = env[":target"]
- name = device.partname.split("-")[0]
+ # Some families use the variant in header defines, some do not (e.g. SAMG)
+ names = [
+ "".join([device.identifier[f] for f in ["platform", "family", "series", "pin", "flash", "variant"]]),
+ "".join([device.identifier[f] for f in ["platform", "family", "series", "pin", "flash"]]),
+ ]
- define = "__{}__".format(name.upper())
+ device_define = None
family_file = None
+ device_header = None
for famfile in Path(localpath("sam")).glob("**/sam.h"):
content = famfile.read_text(encoding="utf-8", errors="replace")
match = re.findall(r"defined\((?P__SAM.*?__)\)", content)
- if match is not None and define in match:
- family_file = famfile.relative_to(localpath("."))
+ for n in names:
+ define = "__{}__".format(n.upper())
+ if match is not None and define in match:
+ family_file = famfile.relative_to(localpath("."))
+ device_header = "{}.h".format(n)
+ device_define = define
+
if family_file is None:
raise ValidateException("No device define found for '{}'!".format(device.partname))
family_folder = family_file.parent
- device_header = "{}.h".format(name)
global pp
pp = {
- "define": define,
+ "define": device_define,
"folder": family_folder,
"device_header": device_header,
}
diff --git a/ext/microchip/sam b/ext/microchip/sam
index bc94859670..4876890d97 160000
--- a/ext/microchip/sam
+++ b/ext/microchip/sam
@@ -1 +1 @@
-Subproject commit bc94859670c5e1a4076502345b24269ece59befd
+Subproject commit 4876890d97a22deb9273bb91169e74364b66ee3b
diff --git a/ext/modm-devices b/ext/modm-devices
index 2b667e16f0..56f0ccaa90 160000
--- a/ext/modm-devices
+++ b/ext/modm-devices
@@ -1 +1 @@
-Subproject commit 2b667e16f0e519bfeb3412596b7dd0495a227c72
+Subproject commit 56f0ccaa902ad695958b01ce470fd008c0a3b18b
diff --git a/repo.lb b/repo.lb
index f66fd674e2..c9850f5a3d 100644
--- a/repo.lb
+++ b/repo.lb
@@ -87,6 +87,7 @@ class DevicesCache(dict):
"stm32l0", "stm32l1", "stm32l4",
"at90", "attiny", "atmega",
"samd21",
+ "samg55",
"hosted"]
device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)]
# These files are ignored due to various issues
diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp
new file mode 100644
index 0000000000..631644627f
--- /dev/null
+++ b/src/modm/board/samg55_xplained_pro/board.hpp
@@ -0,0 +1,55 @@
+/*
+ * 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
+
+/// @ingroup modm_board_samg55_xplained_pro
+namespace Board
+{
+using namespace modm::literals;
+using namespace modm::platform;
+
+struct SystemClock
+{
+ static constexpr uint32_t PllAMult = 3662;
+ static constexpr uint32_t Frequency = PllAMult * SlowClkFreqHz;
+ static bool inline
+ enable()
+ {
+ ClockGen::setFlashLatency();
+ ClockGen::updateCoreFrequency();
+ ClockGen::enableExternal32Khz(false);
+ ClockGen::enablePllA();
+ ClockGen::selectMasterClk();
+ return true;
+ }
+};
+
+using Led = GpioA6;
+using Button = GpioA2;
+
+inline void
+initialize()
+{
+ // Turn off the watchdog
+ WDT->WDT_MR = (WDT_MR_WDDIS_Msk);
+
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ Led::setOutput(modm::Gpio::Low);
+
+ Button::setInput();
+}
+
+} // namespace Board
+
diff --git a/src/modm/board/samg55_xplained_pro/board.xml b/src/modm/board/samg55_xplained_pro/board.xml
new file mode 100644
index 0000000000..a53d64f16a
--- /dev/null
+++ b/src/modm/board/samg55_xplained_pro/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+
+
+
+ modm:board:samg55-xplained-pro
+
+
diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb
new file mode 100644
index 0000000000..3b293c4151
--- /dev/null
+++ b/src/modm/board/samg55_xplained_pro/module.lb
@@ -0,0 +1,28 @@
+#!/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/.
+# -----------------------------------------------------------------------------
+
+def init(module):
+ module.name = ":board:samg55-xplained-pro"
+ module.description = "Microchip SAMG55 Xplained Pro"
+
+def prepare(module, options):
+ if not options[":target"].partname == "samg55j19a-au":
+ return False
+
+ module.depends(":platform:clockgen", ":platform:gpio", ":platform:core");
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ env.copy('board.hpp')
+
+ # TODO: openocd config?
\ No newline at end of file
diff --git a/src/modm/platform/clock/samg/clockgen.cpp b/src/modm/platform/clock/samg/clockgen.cpp
new file mode 100644
index 0000000000..0cd1020221
--- /dev/null
+++ b/src/modm/platform/clock/samg/clockgen.cpp
@@ -0,0 +1,121 @@
+/**
+ * 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 "clockgen.hpp"
+#include
+
+
+// CMSIS Core compliance
+uint32_t modm_fastdata SystemCoreClock(8000000);
+modm_weak void SystemCoreClockUpdate() { /* Nothing to update */ }
+
+namespace modm::platform
+{
+uint16_t modm_fastdata delay_fcpu_MHz = computeDelayMhz(8'000'000);
+uint16_t modm_fastdata delay_ns_per_loop = computeDelayNsPerLoop(8'000'000);
+
+
+static uint16_t divFromMasterClkPrescaler(MasterClkPrescaler pres) {
+ switch(pres) {
+ case MasterClkPrescaler::CLK_1:
+ return 1;
+ case MasterClkPrescaler::CLK_2:
+ return 2;
+ case MasterClkPrescaler::CLK_4:
+ return 4;
+ case MasterClkPrescaler::CLK_8:
+ return 8;
+ case MasterClkPrescaler::CLK_16:
+ return 16;
+ case MasterClkPrescaler::CLK_32:
+ return 32;
+ case MasterClkPrescaler::CLK_64:
+ return 64;
+ case MasterClkPrescaler::CLK_3:
+ return 3;
+ default:
+ return 1;
+ }
+}
+
+void ClockGen::enableExternal32Khz(bool crystal_bypass) {
+ if(crystal_bypass) {
+ SUPC->SUPC_MR |= SUPC_MR_OSCBYPASS;
+ }
+
+ SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_XTALSEL;
+}
+
+
+
+
+uint32_t ClockGen::masterClkFrequency() {
+ uint32_t mckr = PMC->PMC_MCKR;
+ MasterClkSource src = (MasterClkSource)((mckr & PMC_MCKR_CSS_Msk) >> PMC_MCKR_CSS_Pos);
+ MasterClkPrescaler pres = (MasterClkPrescaler)((mckr & PMC_MCKR_PRES_Msk) >> PMC_MCKR_PRES_Pos);
+ uint32_t div = divFromMasterClkPrescaler(pres);
+ switch(src) {
+ case MasterClkSource::SLOW_CLK:
+ return SlowClkFreqHz / div;
+ case MasterClkSource::MAIN_CLK:
+ return mainClkFrequency() / div;
+ case MasterClkSource::PLLA_CLK:
+ return pllAFrequency() / div;
+ case MasterClkSource::PLLB_CLK:
+ return pllBFrequency() / div;
+ default:
+ return 0;
+ }
+}
+
+uint32_t ClockGen::mainClkFrequency() {
+ // NOTE: This does not support external main clk. To add this, we'll need
+ // a way for user supplied input frequency. For now, this assumes we're
+ // using the internal RC for main clock.
+ uint32_t moscrcf = (PMC->CKGR_MOR & CKGR_MOR_MOSCRCF_Msk) >> CKGR_MOR_MOSCRCF_Pos;
+ if(moscrcf == (uint32_t)MainInternalFreq::Rc8Mhz) {
+ return 8'000'000;
+ } else if(moscrcf == (uint32_t)MainInternalFreq::Rc16Mhz) {
+ return 16'000'000;
+ } else if(moscrcf == (uint32_t)MainInternalFreq::Rc24Mhz) {
+ return 24'000'000;
+ } else {
+ return 0;
+ }
+}
+
+uint32_t ClockGen::pllAFrequency() {
+ uint32_t mul = ((PMC->CKGR_PLLAR & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos) + 1;
+ uint32_t freq = SlowClkFreqHz * mul;
+ if(PMC->PMC_MCKR & PMC_MCKR_PLLADIV2) {
+ freq /= 2;
+ }
+ return freq;
+}
+
+uint32_t ClockGen::pllBFrequency() {
+ uint32_t mul = ((PMC->CKGR_PLLBR & CKGR_PLLBR_MULB_Msk) >> CKGR_PLLBR_MULB_Pos) + 1;
+ uint32_t freq = SlowClkFreqHz * mul;
+ if(PMC->PMC_MCKR & PMC_MCKR_PLLBDIV2) {
+ freq /= 2;
+ }
+ return freq;
+}
+
+
+void ClockGen::setMainInternalFreq(MainInternalFreq freq) {
+ PMC->CKGR_MOR =
+ (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCRCF_Msk)) |
+ CKGR_MOR_MOSCRCF((uint32_t)freq) |
+ CKGR_MOR_KEY_PASSWD;
+}
+
+} // namespace modm::platform
\ No newline at end of file
diff --git a/src/modm/platform/clock/samg/clockgen.hpp b/src/modm/platform/clock/samg/clockgen.hpp
new file mode 100644
index 0000000000..652754ca78
--- /dev/null
+++ b/src/modm/platform/clock/samg/clockgen.hpp
@@ -0,0 +1,112 @@
+/**
+ * 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
+#include "../device.hpp"
+#include
+
+extern uint32_t SystemCoreClock;
+
+namespace modm::platform {
+
+enum class MasterClkSource : uint32_t {
+ SLOW_CLK = PMC_MCKR_CSS_SLOW_CLK_Val,
+ MAIN_CLK = PMC_MCKR_CSS_MAIN_CLK_Val,
+ PLLA_CLK = PMC_MCKR_CSS_PLLA_CLK_Val,
+ PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK_Val
+};
+
+enum class MasterClkPrescaler : uint32_t {
+ CLK_1 = 0,
+ CLK_2,
+ CLK_4,
+ CLK_8,
+ CLK_16,
+ CLK_32,
+ CLK_64,
+ CLK_3
+};
+
+enum class MainInternalFreq : uint32_t {
+ Rc8Mhz = 0,
+ Rc16Mhz,
+ Rc24Mhz
+};
+
+static constexpr uint32_t SlowClkFreqHz = 32'768;
+
+/**
+ * Clock Generator Controller for SAMG5x devices
+ *
+ * This class provides control of the clock generation for the CPU and
+ * peripherals.
+ *
+ * There are two source clocks: a "slow" 32kHz clock which can be sourced from
+ * an external oscillator/crystal or internal RC oscillator, and a "main" clock which
+ * can be sourced from the 8/16/24MHz selectable internal RC oscillator, or from
+ * an external oscillator/crystal.
+ *
+ * In addition, there are two PLLs (PLLA, and PLLB), which run off of the slow
+ * clock. PLLB is used to provide a 48MHz USB clock (external slow crystal is
+ * required for USB), and PLLA can (optionally) be used to provide a faster
+ * clock for the CPU and peripherals.
+ *
+ * This class does not allow full control of all possible clock configurations.
+ *
+ * @author Jeff McBride
+ * @ingroup modm_platform_clockgen
+ */
+class ClockGen {
+public:
+ /**
+ * Select external source for slow clock
+ *
+ * \param crystal_bypass
+ * Set true to enable use of external oscillator input on XIN, instead
+ * of crystal.
+ */
+ static void enableExternal32Khz(bool crystal_bypass = false);
+
+ template
+ static void enablePllA(uint32_t wait_cycles = 50);
+
+ template
+ static void enablePllB(uint32_t wait_cycles = 50);
+
+ template
+ static void selectMasterClk();
+
+ static void setMainInternalFreq(MainInternalFreq freq);
+
+ template
+ static void setFlashLatency();
+
+ template
+ static void updateCoreFrequency();
+
+ /** Returns the configured frequency of the main clock */
+ static uint32_t mainClkFrequency();
+
+ /** Returns the configured frequency of the master clock */
+ static uint32_t masterClkFrequency();
+
+ /** Returns the configured frequency of PLL A output */
+ static uint32_t pllAFrequency();
+
+ /** Returns the configured frequency of PLL B output */
+ static uint32_t pllBFrequency();
+};
+
+} // namespace modm::platform
+
+#include "clockgen_impl.hpp"
\ No newline at end of file
diff --git a/src/modm/platform/clock/samg/clockgen_impl.hpp b/src/modm/platform/clock/samg/clockgen_impl.hpp
new file mode 100644
index 0000000000..9a62556b28
--- /dev/null
+++ b/src/modm/platform/clock/samg/clockgen_impl.hpp
@@ -0,0 +1,79 @@
+/*
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+namespace modm::platform {
+
+template
+void ClockGen::selectMasterClk() {
+ // Datasheet says when selecting PLL as source, write PRES first, otherwise
+ // write CSS first.
+ if(src == MasterClkSource::PLLA_CLK || src == MasterClkSource::PLLB_CLK) {
+ PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres);
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
+ PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS((uint32_t)src);
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
+ } else {
+ PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_CSS_Msk) | PMC_MCKR_CSS((uint32_t)src);
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
+ PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres);
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
+ }
+}
+
+template
+void ClockGen::enablePllA(uint32_t wait_cycles) {
+ static_assert(multiplier > 8 && multiplier < 7502, "Valid PLL MUL range is 9-7501");
+ PMC->CKGR_PLLAR =
+ CKGR_PLLAR_MULA(multiplier-1) |
+ CKGR_PLLAR_PLLACOUNT(wait_cycles) |
+ CKGR_PLLAR_PLLAEN(1);
+ // Wait for lock bit
+ while(!(PMC->PMC_SR & PMC_SR_LOCKA)) {}
+}
+
+template
+void ClockGen::enablePllB(uint32_t wait_cycles) {
+ static_assert(multiplier > 8 && multiplier < 7502, "Valid PLL MUL range is 9-7501");
+ PMC->CKGR_PLLBR =
+ CKGR_PLLBR_MULB(multiplier-1) |
+ CKGR_PLLBR_PLLBCOUNT(wait_cycles) |
+ CKGR_PLLBR_PLLBEN(1);
+ // Wait for lock bit
+ while(!(PMC->PMC_SR & PMC_SR_LOCKB)) {}
+}
+
+template
+void ClockGen::updateCoreFrequency() {
+ SystemCoreClock = Core_Hz;
+ delay_fcpu_MHz = computeDelayMhz(Core_Hz);
+ delay_ns_per_loop = computeDelayNsPerLoop(Core_Hz);
+}
+
+template
+void ClockGen::setFlashLatency() {
+ uint32_t fws = 0;
+ if(Core_Hz <= 20'000'000) {
+ fws = 0;
+ } else if(Core_Hz <= 40'000'000) {
+ fws = 1;
+ } else if (Core_Hz <= 60'000'000) {
+ fws = 2;
+ } else if (Core_Hz <= 80'000'000) {
+ fws = 3;
+ } else if (Core_Hz <= 100'000'000) {
+ fws = 4;
+ } else {
+ fws = 5;
+ }
+ EFC->EEFC_FMR = (EFC->EEFC_FMR & ~EEFC_FMR_FWS_Msk) | EEFC_FMR_FWS(fws);
+}
+
+} // namespace modm::platform
diff --git a/src/modm/platform/clock/samg/module.lb b/src/modm/platform/clock/samg/module.lb
new file mode 100644
index 0000000000..8ba8046ee3
--- /dev/null
+++ b/src/modm/platform/clock/samg/module.lb
@@ -0,0 +1,30 @@
+#!/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/.
+# -----------------------------------------------------------------------------
+
+def init(module):
+ module.name = ":platform:clockgen"
+ module.description = "Clock Generator (CKGR)"
+
+def prepare(module, options):
+ if not options[":target"].has_driver("pmc:samg*"):
+ return False
+
+ module.depends(":cmsis:device", ":platform:clock")
+ return True
+
+def build(env):
+ device = env[":target"]
+
+ env.outbasepath = "modm/src/modm/platform/clock"
+ env.copy("clockgen.hpp")
+ env.copy("clockgen_impl.hpp")
+ env.copy("clockgen.cpp")
diff --git a/src/modm/platform/clock/systick/module.lb b/src/modm/platform/clock/systick/module.lb
index 131edd9e56..e57815ce4a 100644
--- a/src/modm/platform/clock/systick/module.lb
+++ b/src/modm/platform/clock/systick/module.lb
@@ -41,7 +41,8 @@ def build(env):
# SysTick clock prescaler is dynamically chosen as /1 or /8
div = 8
# SAMD: Prescaler not implemented
- if target.platform == "sam": div = 1;
+ if target.platform == "sam" and target.family in ["d"]:
+ div = 1
# H742/43: Prescaler not implemented in revY
elif target.family == "h7" and target.name in ["42", "43"] and target.revision == "y":
div = 1
diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb
index 4149c519a7..4951a92f9e 100644
--- a/src/modm/platform/core/sam/module.lb
+++ b/src/modm/platform/core/sam/module.lb
@@ -35,7 +35,8 @@ def build(env):
# delay code that must be tuned for each family
# (cycles per loop, setup cost in loops, us shift)
tuning = {
- "d": (3, 4, 5), # CM0 tested on D21 in RAM
+ "d": (3, 4, 5), # CM0 tested on D21 in RAM
+ "g": (6, 4, 5) # G55
}.get(target.family, (4, 4, 5))
env.substitutions.update({
diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in
index 6bd7663bfb..5c99d8d829 100644
--- a/src/modm/platform/core/sam/startup_platform.c.in
+++ b/src/modm/platform/core/sam/startup_platform.c.in
@@ -48,6 +48,8 @@ __modm_initialize_platform(void)
%% elif target.family == "l"
%% if target.series == "21"
%% endif
+
+%% elif target.family == "g"
%% endif
}
diff --git a/src/modm/platform/gpio/sam/config.hpp.in b/src/modm/platform/gpio/sam/config.hpp.in
index ca55ca328e..0e4cd32505 100644
--- a/src/modm/platform/gpio/sam/config.hpp.in
+++ b/src/modm/platform/gpio/sam/config.hpp.in
@@ -106,11 +106,13 @@ struct {{ gpio["port"] ~ gpio["pin"] }}
};
%% endfor
+%% if gpio["signal"] | length > 0
using Signals =
std::tuple<
%% for signal in gpio["signal"]
{{ signal["full_name"] }}{{ ">;" if loop.last else ","}}
%% endfor
+%% endif
};
} // namespace gpio
using Gpio{{ gpio["port"] ~ gpio["pin"] }} = Gpio;
diff --git a/src/modm/platform/gpio/sam/enable.cpp.in b/src/modm/platform/gpio/sam/enable.cpp.in
new file mode 100644
index 0000000000..5ddf333994
--- /dev/null
+++ b/src/modm/platform/gpio/sam/enable.cpp.in
@@ -0,0 +1,38 @@
+/*
+ * 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 "../device.hpp"
+#include
+
+void
+modm_gpio_enable(void)
+{
+
+%% if target["family"] in ["g"]
+
+ PMC->PMC_PCER0 =
+%% for port in options["enable_ports"]
+%% if not loop.last
+ (1<PIO_OWER = 0xFFFFFFFF;
+%% endfor
+
+%%endif
+}
+
+MODM_HARDWARE_INIT_ORDER(modm_gpio_enable, 80);
diff --git a/src/modm/platform/gpio/sam/module.lb b/src/modm/platform/gpio/sam/module.lb
index 35d5650f0f..9289c0a04b 100644
--- a/src/modm/platform/gpio/sam/module.lb
+++ b/src/modm/platform/gpio/sam/module.lb
@@ -15,6 +15,19 @@
import copy
from collections import defaultdict, OrderedDict
+def port_ranges(gpios):
+ ports = {p: (32, 0) for p in set(p["port"] for p in gpios)}
+ for gpio in gpios:
+ pin = int(gpio["pin"])
+ pmin, pmax = ports[gpio["port"]]
+ ports[gpio["port"]] = (min(pin, pmin), max(pin, pmax))
+
+ ports = [{"name": k.upper(), "start": v[0], "width": v[1] - v[0] + 1} for k,v in ports.items()]
+ ports.sort(key=lambda p: p["name"])
+ return ports
+
+bprops = {}
+
def init(module):
module.name = ":platform:gpio"
module.description = "General Purpose I/O (GPIO)"
@@ -24,6 +37,16 @@ def prepare(module, options):
if not device.has_driver("gpio:sam*"):
return False
+ bprops["ranges"] = port_ranges(device.get_driver("gpio")["gpio"])
+ bprops["ports"] = OrderedDict([(k, i) for i, k in enumerate([p["name"] for p in bprops["ranges"]])])
+
+ module.add_set_option(
+ EnumerationOption(
+ name="enable_ports",
+ description="Enable clock for these GPIO ports during startup",
+ enumeration=list(bprops["ports"].keys())),
+ default=list(bprops["ports"].keys()))
+
module.depends(
":architecture:gpio",
":cmsis:device",
@@ -42,12 +65,16 @@ def build(env):
peripherals = OrderedDict()
for gpio in driver["gpio"]:
signals = []
- for signal in gpio["signal"]:
+ for signal in gpio.get("signal", []):
signal.update({
"peripheral": signal["driver"].capitalize(),
"function": signal["function"].capitalize(),
"name": signal["name"].capitalize(),
})
+ # SAMG devices have X1 peripheral connection, which is "extra function",
+ # but it is not configured by the normal alternate IO function. Skip these.
+ if signal['function'] == 'X1':
+ continue
# This is a hack for L variant devices, which have a Ac without instance *and*
# a Ac with instance 1. Why???
if (device.identifier.get("variant") == "l"
@@ -80,5 +107,7 @@ def build(env):
bprops["gpios"] = driver["gpio"]
env.template("config.hpp.in")
env.copy("connector.hpp")
- env.copy("pin.hpp")
+ if len(env["enable_ports"]):
+ env.template("enable.cpp.in")
+ env.template("pin.hpp.in")
env.copy("unused.hpp")
diff --git a/src/modm/platform/gpio/sam/pin.hpp b/src/modm/platform/gpio/sam/pin.hpp.in
similarity index 65%
rename from src/modm/platform/gpio/sam/pin.hpp
rename to src/modm/platform/gpio/sam/pin.hpp.in
index cd9af440a7..73fad6d9f1 100644
--- a/src/modm/platform/gpio/sam/pin.hpp
+++ b/src/modm/platform/gpio/sam/pin.hpp.in
@@ -28,24 +28,6 @@ enum class InputType
PullDown = 0x2, ///< pull-down on input
};
-enum class OutputType
-{
- PushPull = 0x0, ///< push-pull on output
- OpenDrain = 0x1, ///< open-drain on output
-};
-
-enum class OutputSpeed
-{
- Low = 0,
- Medium = 0x1,
- High = 0x2,
- VeryHigh = 0x3, ///< 30 pF (80 MHz Output max speed on 15 pF)
- MHz2 = Low,
- MHz25 = Medium,
- MHz50 = High,
- MHz100 = VeryHigh,
-};
-
enum class PeripheralPin
{
BitBang,
@@ -104,6 +86,176 @@ struct OneOfSignals
static constexpr bool value = ((std::is_same_v) | ...);
};
+%% if target["family"] in ["g"]
+
+template
+struct PinMuxMixin
+{
+ inline static void set(uint8_t){};
+};
+
+template
+struct PinMuxMixin
+{
+ inline static void
+ set(uint8_t cfg)
+ {
+ uint8_t bit0 = cfg & 1;
+ uint8_t bit1 = (cfg & 2) >> 1;
+ if constexpr (PinConfig::port == PortName::A)
+ {
+ PIOA->PIO_ABCDSR[0] = (PIOA->PIO_ABCDSR[0] & ~(1<PIO_ABCDSR[1] = (PIOA->PIO_ABCDSR[1] & ~(1<PIO_ABCDSR[0] = (PIOB->PIO_ABCDSR[0] & ~(1<PIO_ABCDSR[1] = (PIOB->PIO_ABCDSR[1] & ~(1<::set(cfg);
+ }
+};
+
+template
+class GpioSet : protected PinMuxMixin
+{
+protected:
+ using PinMux = PinMuxMixin;
+
+ static constexpr uint32_t
+ mask(PortName port)
+ {
+ return (((PinConfigs::port == port) ? 1u << PinConfigs::pin : 0u) | ...);
+ }
+
+ template
+ inline static constexpr volatile uint32_t*
+ getPortReg(size_t offset)
+ {
+ if constexpr (port == PortName::A) {
+ return (uint32_t*)((uint8_t*)PIOA + offset);
+ }
+ if constexpr (port == PortName::B) {
+ return (uint32_t*)((uint8_t*)PIOB + offset);
+ }
+ }
+
+ inline static constexpr void
+ setPortReg(size_t offset)
+ {
+ if constexpr (mask(PortName::A) != 0) { *getPortReg(offset) = mask(PortName::A); }
+ if constexpr (mask(PortName::B) != 0) { *getPortReg(offset) = mask(PortName::B); }
+ }
+
+ template
+ inline static constexpr uint32_t
+ readPortReg(size_t offset)
+ {
+ if constexpr (port == PortName::A)
+ {
+ static_assert(mask(PortName::A) != 0,
+ "Trying to read port which is not in the GpioSet!");
+ return *getPortReg(offset) & mask(PortName::A);
+ } else if constexpr (port == PortName::B)
+ {
+ static_assert(mask(PortName::B) != 0,
+ "Trying to read port which is not in the GpioSet!");
+ return *getPortReg(offset) & mask(PortName::B);
+ }
+ }
+
+public:
+ inline static void
+ setOutput()
+ {
+ setPortReg(PIO_OER_OFFSET);
+ }
+
+ inline static void
+ setOutput(bool status)
+ {
+ setOutput();
+ set(status);
+ }
+
+ static void
+ setInput()
+ {
+ setPortReg(PIO_ODR_OFFSET); // Disable output driver
+ }
+
+ static void
+ setInput(InputType type)
+ {
+ configure(type);
+ setInput();
+ }
+
+ static void
+ configure(InputType type)
+ {
+ if(type == InputType::Floating) {
+ setPortReg(PIO_PPDDR_OFFSET); // disable pull down
+ setPortReg(PIO_PUDR_OFFSET); // disable pull up
+ } else if (type == InputType::PullDown) {
+ setPortReg(PIO_PPDER_OFFSET); // Enable pull down
+ setPortReg(PIO_PUDR_OFFSET); // disable pull up
+ } else {
+ setPortReg(PIO_PPDDR_OFFSET); // Disable pull down
+ setPortReg(PIO_PUER_OFFSET); // Enable pull up
+ }
+ }
+
+ static void
+ setAnalogInput()
+ {}
+
+ static void
+ set()
+ {
+ setPortReg(PIO_SODR_OFFSET);
+ }
+
+ static void
+ set(bool status)
+ {
+ if (status)
+ set();
+ else
+ reset();
+ }
+
+ static void
+ reset()
+ {
+ setPortReg(PIO_CODR_OFFSET);
+ }
+
+ static void
+ toggle()
+ {
+ if constexpr (mask(PortName::A) != 0) {
+ volatile uint32_t *reg = getPortReg(PIO_ODSR_OFFSET);
+ uint32_t tmp = *reg;
+ tmp ^= mask(PortName::A);
+ *reg = tmp;
+ }
+ if constexpr (mask(PortName::B) != 0) {
+ volatile uint32_t *reg = getPortReg(PIO_ODSR_OFFSET);
+ uint32_t tmp = *reg;
+ tmp ^= mask(PortName::B);
+ *reg = tmp;
+ }
+ }
+
+ static void
+ disconnect()
+ {
+ setInput(InputType::Floating);
+ }
+};
+
+%% else
+
template
struct PinCfgMixin
{
@@ -189,10 +341,6 @@ class GpioSet : protected PinCfgMixin
set(status);
}
- static void setOutput(OutputType, OutputSpeed) { setOutput(); }
-
- static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {}
-
static void
setInput()
{
@@ -250,6 +398,8 @@ class GpioSet : protected PinCfgMixin
setInput(InputType::Floating);
}
};
+%% endif
+
template
class Gpio : public GpioSet, public ::modm::GpioIO
@@ -259,7 +409,6 @@ class Gpio : public GpioSet, public ::modm::GpioIO
public:
// For backwards compability with bitbang API
using InputType = ::modm::platform::InputType;
- using OutputType = ::modm::platform::OutputType;
template
struct As;
@@ -273,7 +422,11 @@ class Gpio : public GpioSet, public ::modm::GpioIO
inline static bool
read()
{
+%% if target["family"] in ["g"]
+ return Base::readPortReg(PIO_PDSR_OFFSET);
+%% else
return Base::readPortReg(PORT_IN_OFFSET);
+%% endif
}
inline static void
@@ -332,6 +485,16 @@ struct Gpio::As : public Gpio
inline static void
connect()
{
+%% if target["family"] in ["g"]
+ Pio* PIOBase;
+ if constexpr (PinConfig::port == PortName::A) {
+ PIOBase = PIOA;
+ } else {
+ PIOBase = PIOB;
+ }
+ PIOBase->PIO_PDR = (1<::As : public Gpio
PORT->Group[uint32_t(PinConfig::port)].PMUX[PinConfig::pin >> 1].bit.PMUXE =
uint32_t(PinSignal::function);
}
+%% endif
}
+
};
};
diff --git a/src/modm/platform/gpio/sam/unused.hpp b/src/modm/platform/gpio/sam/unused.hpp
index 64011c5c55..47660cb182 100644
--- a/src/modm/platform/gpio/sam/unused.hpp
+++ b/src/modm/platform/gpio/sam/unused.hpp
@@ -34,13 +34,6 @@ class GpioUnused : public ::modm::GpioIO
static constexpr uint8_t pin = uint8_t(-1);
static constexpr uint32_t mask = 0;
-protected:
- /// @cond
- static void setAlternateFunction(uint8_t) {}
- static void setAnalogInput() {}
- /// @endcond
-
-public:
// GpioOutput
// start documentation inherited
static void setOutput() {}
@@ -51,16 +44,12 @@ class GpioUnused : public ::modm::GpioIO
static void toggle() {}
static bool isSet() { return false; }
// stop documentation inherited
- static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {}
- static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {}
// GpioInput
// start documentation inherited
static void setInput() {}
static bool read() { return false; }
// end documentation inherited
- static void configure(InputType) {}
- static void setInput(InputType) {}
// GpioIO
// start documentation inherited
diff --git a/test/modm/mock/module.lb b/test/modm/mock/module.lb
index adc64c8ee9..b5a7fc55ca 100644
--- a/test/modm/mock/module.lb
+++ b/test/modm/mock/module.lb
@@ -99,6 +99,10 @@ class LogicAnalyzer(Module):
if not core or "m0" in core["type"] or "m7" in core["type"]:
return False
+ # gpio_sampler only supports stm32
+ if options[":target"].identifier["platform"] != "stm32":
+ return False
+
module.depends(":stdc++", ":driver:gpio_sampler", ":debug")
return True
@@ -106,7 +110,6 @@ class LogicAnalyzer(Module):
env.outbasepath = "modm-test/src/modm-test/mock"
env.copy("logic_analyzer.hpp")
-
def init(module):
module.name = ":mock"