diff --git a/.github/workflows/compile-all.yml b/.github/workflows/compile-all.yml
index 226a3b2d2d..ff89e15407 100644
--- a/.github/workflows/compile-all.yml
+++ b/.github/workflows/compile-all.yml
@@ -31,7 +31,7 @@ jobs:
name: avr-compile-all
path: test/all/log
- sam-compile-all:
+ sam-d0x-d1x-d2x-compile-all:
if: github.event.label.name == 'ci:hal'
runs-on: ubuntu-20.04
container:
@@ -47,9 +47,61 @@ jobs:
- name: Update lbuild
run: |
pip3 install --upgrade --upgrade-strategy=eager modm
- - name: Compile HAL for all SAM
+ - name: Compile HAL for SAMD0x, SAMD1x, SAMD2x
run: |
- (cd test/all && python3 run_all.py sam --quick-remaining)
+ (cd test/all && python3 run_all.py samd0 samd1 samd2 --quick-remaining)
+ - name: Upload log artifacts
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: sam-compile-all
+ path: test/all/log
+
+ sam-x5x-compile-all:
+ if: github.event.label.name == 'ci:hal'
+ runs-on: ubuntu-20.04
+ container:
+ image: ghcr.io/modm-ext/modm-build-cortex-m:2022-09-27
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ submodules: 'recursive'
+ - name: Fix Git permission/ownership problem
+ run: |
+ git config --global --add safe.directory /__w/modm/modm
+ - name: Update lbuild
+ run: |
+ pip3 install --upgrade --upgrade-strategy=eager modm
+ - name: Compile HAL for SAMD5x, SAME5x, SAMG5x
+ run: |
+ (cd test/all && python3 run_all.py samd5 same5 samg5 --quick-remaining)
+ - name: Upload log artifacts
+ if: always()
+ uses: actions/upload-artifact@v2
+ with:
+ name: sam-compile-all
+ path: test/all/log
+
+ sam-x7x-compile-all:
+ if: github.event.label.name == 'ci:hal'
+ runs-on: ubuntu-20.04
+ container:
+ image: ghcr.io/modm-ext/modm-build-cortex-m:2022-09-27
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ submodules: 'recursive'
+ - name: Fix Git permission/ownership problem
+ run: |
+ git config --global --add safe.directory /__w/modm/modm
+ - name: Update lbuild
+ run: |
+ pip3 install --upgrade --upgrade-strategy=eager modm
+ - name: Compile HAL for SAME7x, SAMS7x, SAMV7x
+ run: |
+ (cd test/all && python3 run_all.py same7 sams7 samv7 --quick-remaining)
- name: Upload log artifacts
if: always()
uses: actions/upload-artifact@v2
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index c476fff2c3..8bdb2c584b 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -92,15 +92,19 @@ jobs:
- name: Examples SAMD Devices
if: always()
run: |
- (cd examples && ../tools/scripts/examples_compile.py samd)
+ (cd examples && ../tools/scripts/examples_compile.py samd samd21_xplained_pro)
- name: Examples SAMG Devices
if: always()
run: |
(cd examples && ../tools/scripts/examples_compile.py samg55_xplained_pro)
+ - name: Examples SAME5x Devices
+ if: always()
+ run: |
+ (cd examples && ../tools/scripts/examples_compile.py same54_xplained_pro)
- name: Examples SAMV Devices
if: always()
run: |
- (cd examples && ../tools/scripts/examples_compile.py samv)
+ (cd examples && ../tools/scripts/examples_compile.py samv samv71_xplained_ultra)
- name: Examples RP20 Devices
if: always()
run: |
diff --git a/README.md b/README.md
index f12b1d9c64..d549a05b27 100644
--- a/README.md
+++ b/README.md
@@ -80,10 +80,10 @@ git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git
## Microcontrollers
-modm can create a HAL for 3304 devices of these vendors:
+modm can create a HAL for 3534 devices of these vendors:
- STMicroelectronics STM32: 2729 devices.
-- Microchip SAM: 186 devices.
+- Microchip SAM: 416 devices.
- Microchip AVR: 388 devices.
- Raspberry Pi: 1 device.
@@ -103,7 +103,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
STM32
-SAM
+SAM
RP
AT
@@ -121,9 +121,10 @@ Please [discover modm's peripheral drivers for your specific device][discover].
L1
L4
L5
-D21
-G55
-V70
+D1x D2x DAx
+D5x E5x
+E7x S7x V7x
+G5x
20
90
Mega
@@ -144,8 +145,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
○
-✅
○
+○
+✅
✅
○
✅
@@ -166,8 +168,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
✕
-✕
○
+○
+✕
✕
○
○
@@ -188,8 +191,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
○
○
-✕
○
+○
+✕
✕
○
○
@@ -210,10 +214,11 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
○
-✕
+○
○
✕
✕
+✕
○
✕
@@ -232,8 +237,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
○
-✕
○
+○
+✕
✅
✕
✕
@@ -254,7 +260,8 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕
✕
✕
-✕
+○
+○
✕
✕
✕
@@ -278,6 +285,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
○
○
+○
✅
✅
✅
@@ -300,6 +308,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕
✕
○
+✕
○
✕
✕
@@ -326,6 +335,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
✅
+✅
I2 C
✅
@@ -344,6 +354,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○
○
○
+○
✅
✅
✅
@@ -366,6 +377,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
○
○
○
+○
✕
✕
✕
@@ -386,8 +398,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
✕
-✕
○
+○
+✕
✕
✕
✕
@@ -408,8 +421,9 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
○
-✅
○
+○
+✅
✅
✅
✅
@@ -433,6 +447,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
✅
+✅
✕
✕
✕
@@ -452,9 +467,10 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
○
-✅
○
○
+✅
+○
○
○
○
@@ -475,7 +491,8 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
✅
✅
-○
+✅
+✅
✅
✅
✅
@@ -502,6 +519,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✕
✕
✕
+✕
USB
✅
@@ -521,6 +539,7 @@ Please [discover modm's peripheral drivers for your specific device][discover].
✅
○
✅
+✅
✕
✕
✕
@@ -613,11 +632,15 @@ We have out-of-box support for many development boards including documentation.
Raspberry Pi Pico
SAMD21-MINI
+SAMD21-XPLAINED-PRO
+SAME54-XPLAINED-PRO
SAMG55-XPLAINED-PRO
+
+SAMV71-XPLAINED-ULTRA
Smart Response XE
STM32-F4VE
-
STM32F030-DEMO
+
THINGPLUS-RP2040
diff --git a/examples/samd/interrupt/main.cpp b/examples/samd/interrupt/main.cpp
index 0e3a9aceb3..c35747a826 100644
--- a/examples/samd/interrupt/main.cpp
+++ b/examples/samd/interrupt/main.cpp
@@ -27,7 +27,7 @@ int
main()
{
Board::initialize();
- ExternalInterrupt::initialize();
+ ExternalInterrupt::initialize(Board::SystemClock::ClockGen32kHz);
ExtInt<3>::initialize(&isr);
ExtInt<3>::connect();
while (1)
diff --git a/examples/samd21_xplained_pro/blink/main.cpp b/examples/samd21_xplained_pro/blink/main.cpp
new file mode 100644
index 0000000000..90716c8993
--- /dev/null
+++ b/examples/samd21_xplained_pro/blink/main.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016-2017, Niklas Hauser
+ *
+ * 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
+
+using namespace Board;
+
+int
+main()
+{
+ Board::initialize();
+
+ // Use the logging streams to print some messages.
+ // Change MODM_LOG_LEVEL above to enable or disable these messages
+ MODM_LOG_DEBUG << "debug" << modm::endl;
+ MODM_LOG_INFO << "info" << modm::endl;
+ MODM_LOG_WARNING << "warning" << modm::endl;
+ MODM_LOG_ERROR << "error" << modm::endl;
+
+ uint32_t counter(0);
+
+ while (true)
+ {
+ Led0::toggle();
+ modm::delay(Button::read() ? 500ms : 100ms);
+
+ MODM_LOG_INFO << "loop: " << counter++ << modm::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/samd21_xplained_pro/blink/project.xml b/examples/samd21_xplained_pro/blink/project.xml
new file mode 100644
index 0000000000..2c9638f352
--- /dev/null
+++ b/examples/samd21_xplained_pro/blink/project.xml
@@ -0,0 +1,9 @@
+
+ modm:samd21-xplained-pro
+
+ ../../../build/samd21_xplained_pro/blink
+
+
+ modm:build:scons
+
+
diff --git a/examples/samd21_xplained_pro/usbserial/main.cpp b/examples/samd21_xplained_pro/usbserial/main.cpp
new file mode 100644
index 0000000000..2bf24ef589
--- /dev/null
+++ b/examples/samd21_xplained_pro/usbserial/main.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016-2017, Niklas Hauser
+ * Copyright (c) 2022, Christopher Durand
+ *
+ * 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 Board;
+
+modm::IODeviceWrapper usb_io_device;
+modm::IOStream usb_stream(usb_io_device);
+
+int
+main()
+{
+ Board::initialize();
+ Board::initializeUsbFs();
+
+ tusb_init();
+
+ usb_stream << "Hello from USB" << modm::endl;
+
+ uint32_t counter(0);
+
+ modm::PeriodicTimer timer{500ms};
+ while (true)
+ {
+ tud_task();
+ if (timer.execute()) {
+ Led0::toggle();
+ usb_stream << "loop: " << counter++ << modm::endl;
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/samd21_xplained_pro/usbserial/project.xml b/examples/samd21_xplained_pro/usbserial/project.xml
new file mode 100644
index 0000000000..8ab1e6a5a9
--- /dev/null
+++ b/examples/samd21_xplained_pro/usbserial/project.xml
@@ -0,0 +1,12 @@
+
+ modm:samd21-xplained-pro
+
+ device.cdc
+ ../../../build/samd21_xplained_pro/usbserial
+
+
+ modm:tinyusb
+ modm:build:scons
+ modm:processing:timer
+
+
diff --git a/examples/same54_xplained_pro/blink/main.cpp b/examples/same54_xplained_pro/blink/main.cpp
new file mode 100644
index 0000000000..90716c8993
--- /dev/null
+++ b/examples/same54_xplained_pro/blink/main.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016-2017, Niklas Hauser
+ *
+ * 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
+
+using namespace Board;
+
+int
+main()
+{
+ Board::initialize();
+
+ // Use the logging streams to print some messages.
+ // Change MODM_LOG_LEVEL above to enable or disable these messages
+ MODM_LOG_DEBUG << "debug" << modm::endl;
+ MODM_LOG_INFO << "info" << modm::endl;
+ MODM_LOG_WARNING << "warning" << modm::endl;
+ MODM_LOG_ERROR << "error" << modm::endl;
+
+ uint32_t counter(0);
+
+ while (true)
+ {
+ Led0::toggle();
+ modm::delay(Button::read() ? 500ms : 100ms);
+
+ MODM_LOG_INFO << "loop: " << counter++ << modm::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/same54_xplained_pro/blink/project.xml b/examples/same54_xplained_pro/blink/project.xml
new file mode 100644
index 0000000000..fa74596a8f
--- /dev/null
+++ b/examples/same54_xplained_pro/blink/project.xml
@@ -0,0 +1,10 @@
+
+ modm:same54-xplained-pro
+
+ ../../../build/same54_xplained_pro/blink
+
+
+ modm:build:scons
+ modm:platform:uart:1
+
+
diff --git a/examples/same54_xplained_pro/usbserial/main.cpp b/examples/same54_xplained_pro/usbserial/main.cpp
new file mode 100644
index 0000000000..2bf24ef589
--- /dev/null
+++ b/examples/same54_xplained_pro/usbserial/main.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2016-2017, Niklas Hauser
+ * Copyright (c) 2022, Christopher Durand
+ *
+ * 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 Board;
+
+modm::IODeviceWrapper usb_io_device;
+modm::IOStream usb_stream(usb_io_device);
+
+int
+main()
+{
+ Board::initialize();
+ Board::initializeUsbFs();
+
+ tusb_init();
+
+ usb_stream << "Hello from USB" << modm::endl;
+
+ uint32_t counter(0);
+
+ modm::PeriodicTimer timer{500ms};
+ while (true)
+ {
+ tud_task();
+ if (timer.execute()) {
+ Led0::toggle();
+ usb_stream << "loop: " << counter++ << modm::endl;
+ }
+ }
+
+ return 0;
+}
diff --git a/examples/same54_xplained_pro/usbserial/project.xml b/examples/same54_xplained_pro/usbserial/project.xml
new file mode 100644
index 0000000000..a43ae490d0
--- /dev/null
+++ b/examples/same54_xplained_pro/usbserial/project.xml
@@ -0,0 +1,12 @@
+
+ modm:same54-xplained-pro
+
+ device.cdc
+ ../../../build/same54_xplained_pro/usbserial
+
+
+ modm:tinyusb
+ modm:build:scons
+ modm:processing:timer
+
+
diff --git a/examples/samv71_xplained_ultra/blink/main.cpp b/examples/samv71_xplained_ultra/blink/main.cpp
new file mode 100644
index 0000000000..63d68f8de1
--- /dev/null
+++ b/examples/samv71_xplained_ultra/blink/main.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016-2017, Niklas Hauser
+ *
+ * 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
+
+using namespace Board;
+
+int
+main()
+{
+ Board::initialize();
+
+ // Use the logging streams to print some messages.
+ // Change MODM_LOG_LEVEL above to enable or disable these messages
+ MODM_LOG_DEBUG << "debug" << modm::endl;
+ MODM_LOG_INFO << "info" << modm::endl;
+ MODM_LOG_WARNING << "warning" << modm::endl;
+ MODM_LOG_ERROR << "error" << modm::endl;
+
+ uint32_t counter(0);
+
+ while (true)
+ {
+ Led0::toggle();
+ Led1::toggle();
+ modm::delay(ButtonSW0::read() ? 500ms : 100ms);
+
+ MODM_LOG_INFO << "loop: " << counter++ << modm::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/samv71_xplained_ultra/blink/project.xml b/examples/samv71_xplained_ultra/blink/project.xml
new file mode 100644
index 0000000000..44ac35294a
--- /dev/null
+++ b/examples/samv71_xplained_ultra/blink/project.xml
@@ -0,0 +1,9 @@
+
+ modm:samv71-xplained-ultra
+
+ ../../../build/samv71_xplained_ultra/blink
+
+
+ modm:build:scons
+
+
diff --git a/ext/hathach/module.lb b/ext/hathach/module.lb
index 085eaf4bf0..f0273488c2 100644
--- a/ext/hathach/module.lb
+++ b/ext/hathach/module.lb
@@ -101,11 +101,12 @@ def build(env):
env.copy("tinyusb/src/portable/st/synopsys", "portable/st/synopsys/")
elif target.platform == "sam":
- if target.family == "g":
+ if target.family == "g5x":
tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAMG"
env.copy("tinyusb/src/portable/microchip/samg/", "portable/microchip/samg/")
else:
- tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAM{}{}".format(target.family.upper(), target.series.upper())
+ series = "e5x" if target.series.startswith("e5") else target.series
+ tusb_config["CFG_TUSB_MCU"] = "OPT_MCU_SAM{}".format(series.upper())
env.copy("tinyusb/src/portable/microchip/samd/", "portable/microchip/samd/")
elif target.platform == "rp":
@@ -179,8 +180,10 @@ def build(env):
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"]:
+ elif target.platform == "sam" and target.family in ["g5x"]:
irqs = ["UDP"]
+ elif target.platform == "sam" and target.family in ["d5x/e5x"]:
+ irqs = ["USB_OTHER", "USB_SOF_HSOF", "USB_TRCPT0", "USB_TRCPT1"]
elif target.platform == "rp" and target.family in ["20"]:
irqs = ["USBCTRL_IRQ"]
else:
diff --git a/ext/hathach/tusb_port.cpp.in b/ext/hathach/tusb_port.cpp.in
index 37f4554039..29006dfc79 100644
--- a/ext/hathach/tusb_port.cpp.in
+++ b/ext/hathach/tusb_port.cpp.in
@@ -66,7 +66,7 @@ tusb_get_device_serial(uint16_t *serial_str)
((uint32_t *) UID_BASE), ((uint32_t *) UID_BASE) + 1,
((uint32_t *) UID_BASE) + 2, ((uint32_t *) UID_BASE) + 3
%% elif target.platform in ["sam"]
- %% if "samd51" in target.string
+ %% if "samd51" in target.string or "same5" in target.string
(uint32_t *)0x008061FC, (uint32_t *)0x00806010,
(uint32_t *)0x00806014, (uint32_t *)0x00806018
%% else
diff --git a/ext/microchip/module.lb b/ext/microchip/module.lb
index a875db6af9..f5522900c3 100644
--- a/ext/microchip/module.lb
+++ b/ext/microchip/module.lb
@@ -23,16 +23,53 @@ def prepare(module, options):
return False
module.depends(":cmsis:core")
+
+ module.add_query(
+ EnvironmentQuery(name="clock-map", factory=clock_map))
+
return True
pp = {}
+
+def clock_map_gclk(env, header):
+ clock_pattern = re.compile(r"^#define\s+(?P(MCLK|PM))_(?P(AHB|APB[A-E]))MASK_(?P([A-Z0-9])+(_2X)?(_[A-Z]+)?)_Pos(\s+)(?P([0-9]+))")
+ peripheral_pattern = re.compile(r"^(?P[A-Za-z]+([0-9]+[A-Za-z]+)?(_2X)?(_[A-Z]+)?)(?P[0-9]?)$")
+ clock_map = {}
+ with open(header, "r") as clock_header:
+ for line in clock_header:
+ m = clock_pattern.match(line)
+ if m:
+ m2 = peripheral_pattern.match(m.group("per"))
+ (peripheral, instance) = (m2.group("name"), m2.group("instance"))
+ if (peripheral, instance) in clock_map:
+ clock_map[(peripheral, instance)].append((m.group("clk_per"), m.group("clk"), m.group("pos")))
+ else:
+ clock_map[(peripheral, instance)] = [(m.group("clk_per"), m.group("clk"), m.group("pos"))]
+ return clock_map
+
+def clock_map(env):
+ folder = Path(localpath(pp["folder"])) / "component"
+ clock_file = None
+ if (folder / "mclk.h").exists():
+ clock_file = folder / "mclk.h"
+ elif (folder / "mclk_100.h").exists():
+ clock_file = folder / "mclk_100.h"
+ elif (folder / "pm.h").exists():
+ clock_file = folder / "pm.h"
+ elif (folder / "pm_100.h").exists():
+ clock_file = folder / "pm_100.h"
+
+ if clock_file is not None:
+ return clock_map_gclk(env, clock_file)
+ return {}
+
def validate(env):
device = env[":target"]
# 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"]]),
+ "".join([device.identifier[f] for f in ["platform", "series", "pin", "flash", "variant"]]),
+ "".join([device.identifier[f] for f in ["platform", "series", "pin", "flash"]]),
]
device_define = None
@@ -44,6 +81,10 @@ def validate(env):
for n in names:
define = "__{}__".format(n.upper())
if match is not None and define in match:
+ # In case of multiple matches the most specific header is selected.
+ # The one with the matching variant suffix will have the longest name.
+ if device_define is not None and len(device_define) > len(define):
+ continue
family_file = famfile.relative_to(localpath("."))
device_header = "{}.h".format(n)
device_define = define
diff --git a/ext/microchip/sam b/ext/microchip/sam
index 718fa0f98c..f903c3f55c 160000
--- a/ext/microchip/sam
+++ b/ext/microchip/sam
@@ -1 +1 @@
-Subproject commit 718fa0f98cbc7d35becddabc3612d73d03929cf3
+Subproject commit f903c3f55c1273ce1d121ee860e4fe7d2c6033bb
diff --git a/ext/modm-devices b/ext/modm-devices
index 05fca29f63..2714f2321f 160000
--- a/ext/modm-devices
+++ b/ext/modm-devices
@@ -1 +1 @@
-Subproject commit 05fca29f6398f847a99215ed0ab07d54f541cc3a
+Subproject commit 2714f2321f89bece09f0b339909ebe37e133858d
diff --git a/repo.lb b/repo.lb
index 570f35afce..87fc2b561c 100644
--- a/repo.lb
+++ b/repo.lb
@@ -86,7 +86,9 @@ class DevicesCache(dict):
"stm32h7",
"stm32l0", "stm32l1", "stm32l4", "stm32l5",
"at90", "attiny", "atmega",
- "samd21", "samg55", "samv70",
+ "samd21", "samg55",
+ "same70", "sams70", "samv70", "samv71",
+ "samd51", "same51", "same53", "same54",
"rp2040",
"hosted"]
device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)]
diff --git a/src/modm/board/feather_m0/board.hpp b/src/modm/board/feather_m0/board.hpp
index 2b18fd3551..19860e77eb 100644
--- a/src/modm/board/feather_m0/board.hpp
+++ b/src/modm/board/feather_m0/board.hpp
@@ -2,6 +2,7 @@
* Copyright (c) 2016-2017, Sascha Schade
* Copyright (c) 2017-2018, Niklas Hauser
* Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -60,42 +61,39 @@ using RadioCs = GpioA06;
// This is the red LED by the USB jack.
using Led = D13;
-/// samd21g18a running at 48MHz generated from the external 32.768 KHz crystal
+/// samd21g18a running at 48MHz generated from the external 32.768 kHz crystal
struct SystemClock
{
- static constexpr uint32_t Frequency = 48_MHz;
- static constexpr uint32_t Usb = 48_MHz;
- // static constexpr uint32_t Ahb = Frequency;
- // static constexpr uint32_t Apba = Frequency;
- // static constexpr uint32_t Apbb = Frequency;
- // static constexpr uint32_t Apbc = Frequency;
-
- // static constexpr uint32_t Adc = Apb2;
-
- // static constexpr uint32_t SercomSlow = Apb2;
- // static constexpr uint32_t Sercom0 = Apb2;
- // static constexpr uint32_t Sercom1 = Apb2;
- // static constexpr uint32_t Sercom2 = Apb2;
- // static constexpr uint32_t Sercom3 = Apb2;
- // static constexpr uint32_t Sercom4 = Apb2;
- // static constexpr uint32_t Sercom5 = Apb2;
-
- // static constexpr uint32_t Apb1Timer = Apb1 * 2;
- // static constexpr uint32_t Apb2Timer = Apb2 * 1;
- // static constexpr uint32_t Timer1 = Apb2Timer;
- // static constexpr uint32_t Timer2 = Apb1Timer;
- // static constexpr uint32_t Timer3 = Apb1Timer;
- // static constexpr uint32_t Timer4 = Apb1Timer;
+ static constexpr uint32_t Dfll48m = 48_MHz;
+ static constexpr uint32_t Xosc32k = 32768_Hz;
+
+ static constexpr uint32_t Frequency = Dfll48m;
+ static constexpr uint32_t Usb = Dfll48m;
+
+ static constexpr uint32_t Sercom0 = Frequency;
+ static constexpr uint32_t SercomSlow = Xosc32k;
+
+ static constexpr auto ClockGen32kHz = ClockGenerator::Generator2;
static bool inline
enable()
{
+ // Configure GCLK generator 2 with external 32k crystal source
+ GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms);
+ GenericClockController::enableGenerator();
+
+ // generate 48 MHz from 32768 Hz crystal reference
+ GenericClockController::connect(ClockGen32kHz);
+ GenericClockController::enableDfll48mClosedLoop();
+
GenericClockController::setFlashLatency();
- GenericClockController::initExternalCrystal();
- GenericClockController::initDFLL48MHz();
- GenericClockController::initOsc8MHz();
- GenericClockController::setSystemClock(ClockSource::DFLL48M);
+ GenericClockController::setSystemClock();
GenericClockController::updateCoreFrequency();
+
+ GenericClockController::connect(ClockGenerator::System);
+ GenericClockController::connect(ClockGen32kHz);
+
+ GenericClockController::connect(ClockGenerator::System);
return true;
}
};
diff --git a/src/modm/board/samd21_mini/board.hpp b/src/modm/board/samd21_mini/board.hpp
index 89e81f8fce..5ef27846eb 100644
--- a/src/modm/board/samd21_mini/board.hpp
+++ b/src/modm/board/samd21_mini/board.hpp
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2016-2017, Sascha Schade
* Copyright (c) 2017-2018, Niklas Hauser
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -25,39 +26,30 @@ using namespace modm::literals;
struct SystemClock
{
- static constexpr uint32_t Frequency = 48_MHz;
- // static constexpr uint32_t Ahb = Frequency;
- // static constexpr uint32_t Apba = Frequency;
- // static constexpr uint32_t Apbb = Frequency;
- // static constexpr uint32_t Apbc = Frequency;
-
- static constexpr uint32_t Usb = 48_MHz;
- // static constexpr uint32_t Adc = Apb2;
-
- // static constexpr uint32_t SercomSlow = Apb2;
- // static constexpr uint32_t Sercom0 = Apb2;
- // static constexpr uint32_t Sercom1 = Apb2;
- // static constexpr uint32_t Sercom2 = Apb2;
- // static constexpr uint32_t Sercom3 = Apb2;
- // static constexpr uint32_t Sercom4 = Apb2;
- // static constexpr uint32_t Sercom5 = Apb2;
-
- // static constexpr uint32_t Apb1Timer = Apb1 * 2;
- // static constexpr uint32_t Apb2Timer = Apb2 * 1;
- // static constexpr uint32_t Timer1 = Apb2Timer;
- // static constexpr uint32_t Timer2 = Apb1Timer;
- // static constexpr uint32_t Timer3 = Apb1Timer;
- // static constexpr uint32_t Timer4 = Apb1Timer;
+ static constexpr uint32_t Dfll48m = 48_MHz;
+ static constexpr uint32_t Xosc32k = 32768_Hz;
+
+ static constexpr uint32_t Frequency = Dfll48m;
+ static constexpr uint32_t Usb = Dfll48m;
+
+ static constexpr auto ClockGen32kHz = ClockGenerator::Generator2;
static bool inline
enable()
{
+ // Configure GCLK generator 2 with external 32k crystal source
+ GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms);
+ GenericClockController::enableGenerator();
+
+ // generate 48 MHz from 32768 Hz crystal reference
+ GenericClockController::connect(ClockGen32kHz);
+ GenericClockController::enableDfll48mClosedLoop();
+
GenericClockController::setFlashLatency();
- GenericClockController::initExternalCrystal();
- GenericClockController::initDFLL48MHz();
- GenericClockController::initOsc8MHz();
- GenericClockController::setSystemClock(ClockSource::DFLL48M);
+ GenericClockController::setSystemClock();
GenericClockController::updateCoreFrequency();
+
+ GenericClockController::connect(ClockGenerator::System);
return true;
}
};
@@ -103,10 +95,10 @@ initialize()
}
inline void
-initializeUsbFs(uint8_t priority=3)
+initializeUsbFs(uint8_t priority = 3)
{
- Usb::initialize(priority);
- Usb::connect();
+ modm::platform::Usb::initialize(priority);
+ modm::platform::Usb::connect();
}
/// @}
diff --git a/src/modm/board/samd21_xplained_pro/board.hpp b/src/modm/board/samd21_xplained_pro/board.hpp
new file mode 100644
index 0000000000..4b4ce82b84
--- /dev/null
+++ b/src/modm/board/samd21_xplained_pro/board.hpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2022, Christopher Durand
+ *
+ * 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
+
+#define MODM_BOARD_HAS_LOGGER
+
+namespace Board
+{
+/// @ingroup modm_board_samd21_xplained_pro
+/// @{
+using namespace modm::literals;
+using namespace modm::platform;
+
+
+struct SystemClock
+{
+ static constexpr uint32_t Dfll48m = 48_MHz;
+ static constexpr uint32_t Xosc32k = 32768_Hz;
+
+ static constexpr uint32_t Frequency = Dfll48m;
+ static constexpr uint32_t Usb = Dfll48m;
+
+ static constexpr auto ClockGen32kHz = ClockGenerator::Generator2;
+
+ static constexpr uint32_t Sercom3 = Frequency;
+ static constexpr uint32_t SercomSlow = Xosc32k;
+
+ static bool inline
+ enable()
+ {
+ // Configure GCLK generator 2 with external 32k crystal source
+ GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms);
+ GenericClockController::enableGenerator();
+
+ // generate 48 MHz from 32768 Hz crystal reference
+ GenericClockController::connect(ClockGen32kHz);
+ GenericClockController::enableDfll48mClosedLoop();
+
+ GenericClockController::setFlashLatency();
+ GenericClockController::setSystemClock();
+ GenericClockController::updateCoreFrequency();
+
+ GenericClockController::connect(ClockGenerator::System);
+
+ GenericClockController::connect(ClockGenerator::System);
+ GenericClockController::connect(ClockGen32kHz);
+
+ return true;
+ }
+};
+
+using Led0 = GpioB30;
+using Button = GpioA15;
+
+// No SoftwareGpioPort yet for SAM
+struct Leds
+{
+ static constexpr std::size_t width{1};
+
+ static void setOutput()
+ {
+ Led0::setOutput();
+ }
+
+ static void write(uint32_t value)
+ {
+ Led0::set(value & 1);
+ }
+
+ static void toggle()
+ {
+ Led0::toggle();
+ }
+};
+
+struct Debug
+{
+ using Uart = Uart3;
+ using UartTx = GpioA22;
+ using UartRx = GpioA23;
+};
+
+using LoggerDevice = modm::IODeviceWrapper;
+
+inline void
+initialize()
+{
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ Debug::Uart::initialize();
+ Debug::Uart::connect();
+
+ Led0::setOutput(modm::Gpio::Low);
+
+ Button::setInput(Button::InputType::PullUp);
+}
+
+inline void initializeUsbFs()
+{
+ modm::platform::Usb::initialize();
+ modm::platform::Usb::connect();
+}
+/// @}
+
+} // namespace Board
+
diff --git a/src/modm/board/samd21_xplained_pro/board.xml b/src/modm/board/samd21_xplained_pro/board.xml
new file mode 100644
index 0000000000..507a259706
--- /dev/null
+++ b/src/modm/board/samd21_xplained_pro/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+ samd21j18a-au
+
+
+ modm:board:samd21-xplained-pro
+
+
diff --git a/src/modm/board/samd21_xplained_pro/module.lb b/src/modm/board/samd21_xplained_pro/module.lb
new file mode 100644
index 0000000000..359aebb225
--- /dev/null
+++ b/src/modm/board/samd21_xplained_pro/module.lb
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, Jeff McBride
+# Copyright (c) 2021-2022, Christopher Durand
+#
+# 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:samd21-xplained-pro"
+ module.description = "Microchip SAMD21 Xplained Pro"
+
+def prepare(module, options):
+ if not options[":target"].partname == "samd21j18a-au":
+ return False
+
+ module.depends(":debug", ":platform:gclk", ":platform:gpio", ":platform:core", ":platform:usb", ":platform:uart:3")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ env.substitutions = {
+ "with_logger": True,
+ "with_assert": env.has_module(":architecture:assert")
+ }
+ env.template("../board.cpp.in", "board.cpp")
+ env.copy('board.hpp')
+
+ env.outbasepath = "modm/openocd/modm/board/"
+ env.collect(":build:openocd.source", "board/atmel_samd21_xplained_pro.cfg");
diff --git a/src/modm/board/same54_xplained_pro/board.hpp b/src/modm/board/same54_xplained_pro/board.hpp
new file mode 100644
index 0000000000..a242acb4e8
--- /dev/null
+++ b/src/modm/board/same54_xplained_pro/board.hpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2021-2022, Christopher Durand
+ * 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
+
+#define MODM_BOARD_HAS_LOGGER
+
+namespace Board
+{
+/// @ingroup modm_board_same54_xplained_pro
+/// @{
+using namespace modm::literals;
+using namespace modm::platform;
+
+
+struct SystemClock
+{
+ static constexpr uint32_t Frequency = 120_MHz;
+
+ static constexpr uint32_t Clock48MHz = 48_MHz;
+ static constexpr auto Generator48MHz = ClockGenerator::Generator1;
+
+ static constexpr uint32_t Clock32k = 32768;
+ static constexpr auto Generator32k = ClockGenerator::Generator2;
+
+ static constexpr uint32_t Usb = Clock48MHz;
+
+ static constexpr uint32_t Sercom2 = Frequency;
+ static constexpr uint32_t SercomSlow = Clock32k;
+
+ static bool inline
+ enable()
+ {
+ GenericClockController::enableExternalCrystal<12_MHz>(Xosc::Xosc1);
+ GenericClockController::enableDpll, 120_MHz>();
+
+ GenericClockController::setFlashLatency();
+ GenericClockController::updateCoreFrequency();
+ GenericClockController::setSystemClock();
+
+ GenericClockController::enableExternalCrystal32k(Xosc32StartupTime::Start_500ms);
+ GenericClockController::enableGenerator();
+
+ // generate 48 MHz clock from external 32768 Hz crystal reference
+ GenericClockController::connect(Generator32k);
+ GenericClockController::enableDfll48mClosedLoop();
+
+ GenericClockController::enableGenerator();
+ GenericClockController::connect(Generator48MHz);
+
+ GenericClockController::connect(ClockGenerator::System);
+ GenericClockController::connect(Generator32k);
+
+ return true;
+ }
+};
+
+using Led0 = GpioC18;
+using Button = GpioB31;
+
+// No SoftwareGpioPort yet for SAM
+struct Leds
+{
+ static constexpr std::size_t width{1};
+
+ static void setOutput()
+ {
+ Led0::setOutput();
+ }
+
+ static void write(uint32_t value)
+ {
+ Led0::set(value & 1);
+ }
+};
+
+struct Debug
+{
+ using Uart = Uart2;
+ using UartTx = GpioB25;
+ using UartRx = GpioB24;
+};
+
+using LoggerDevice = modm::IODeviceWrapper;
+
+inline void
+initialize()
+{
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ Debug::Uart::initialize();
+ Debug::Uart::connect();
+
+ Led0::setOutput(modm::Gpio::Low);
+
+ Button::setInput(InputType::PullUp);
+}
+
+inline void initializeUsbFs()
+{
+ modm::platform::Usb::initialize();
+ modm::platform::Usb::connect();
+}
+/// @}
+
+} // namespace Board
+
diff --git a/src/modm/board/same54_xplained_pro/board.xml b/src/modm/board/same54_xplained_pro/board.xml
new file mode 100644
index 0000000000..9f074a31ee
--- /dev/null
+++ b/src/modm/board/same54_xplained_pro/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+ same54p20a-au
+
+
+ modm:board:same54-xplained-pro
+
+
diff --git a/src/modm/board/same54_xplained_pro/module.lb b/src/modm/board/same54_xplained_pro/module.lb
new file mode 100644
index 0000000000..5bb733767c
--- /dev/null
+++ b/src/modm/board/same54_xplained_pro/module.lb
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, Jeff McBride
+# Copyright (c) 2021-2022, Christopher Durand
+#
+# 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:same54-xplained-pro"
+ module.description = "Microchip SAME54 Xplained Pro"
+
+def prepare(module, options):
+ if not options[":target"].partname == "same54p20a-au":
+ return False
+
+ module.depends(":debug", ":platform:gclk", ":platform:gpio", ":platform:core", ":platform:usb", ":platform:uart:2")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ env.substitutions = {
+ "with_logger": True,
+ "with_assert": env.has_module(":architecture:assert")
+ }
+ env.template("../board.cpp.in", "board.cpp")
+ env.copy('board.hpp')
+
+ env.outbasepath = "modm/openocd/modm/board/"
+ env.copy(repopath("tools/openocd/modm/atmel_same54_xplained_pro.cfg"), "atmel_same54_xplained_pro.cfg")
+ env.collect(":build:openocd.source", "modm/board/atmel_same54_xplained_pro.cfg")
diff --git a/src/modm/board/samg55_xplained_pro/board.hpp b/src/modm/board/samg55_xplained_pro/board.hpp
index 55be67fed6..1d9c0628e0 100644
--- a/src/modm/board/samg55_xplained_pro/board.hpp
+++ b/src/modm/board/samg55_xplained_pro/board.hpp
@@ -39,6 +39,8 @@ struct SystemClock
static constexpr uint32_t Pck6 = Mck;
static constexpr uint32_t Pck7 = Mck;
+ static constexpr uint32_t Usart7 = Mck;
+
static bool inline
enable()
{
@@ -62,7 +64,7 @@ struct SystemClock
using Led = GpioA6;
using Button = GpioA2;
-using DebugUart = Uart7;
+using DebugUart = Usart7;
using TxPin = GpioA28;
using RxPin = GpioA27;
diff --git a/src/modm/board/samg55_xplained_pro/module.lb b/src/modm/board/samg55_xplained_pro/module.lb
index b3302f866c..3aec8017fb 100644
--- a/src/modm/board/samg55_xplained_pro/module.lb
+++ b/src/modm/board/samg55_xplained_pro/module.lb
@@ -20,7 +20,7 @@ def prepare(module, options):
module.depends(
":platform:clockgen",
- ":platform:uart:7",
+ ":platform:usart:7",
":platform:gpio",
":platform:core",
":platform:usb",
@@ -31,4 +31,4 @@ def build(env):
env.outbasepath = "modm/src/modm/board"
env.copy('board.hpp')
- # TODO: openocd config?
\ No newline at end of file
+ # TODO: openocd config?
diff --git a/src/modm/board/samv71_xplained_ultra/board.hpp b/src/modm/board/samv71_xplained_ultra/board.hpp
new file mode 100644
index 0000000000..9ae9e57e10
--- /dev/null
+++ b/src/modm/board/samv71_xplained_ultra/board.hpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2022, Christopher Durand
+ *
+ * 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
+#include
+
+#define MODM_BOARD_HAS_LOGGER
+
+namespace Board
+{
+/// @ingroup modm_board_samv71_xplained_ultra
+/// @{
+using namespace modm::literals;
+using namespace modm::platform;
+
+struct SystemClock
+{
+ // 300MHz system clock generated by PLLA from internal Rc 12MHz clock
+ static constexpr uint32_t PllAMult = 25;
+ static constexpr uint32_t Frequency = 300_MHz;
+ static constexpr uint32_t Mck = Frequency / 2; // 150 MHz max.
+ static constexpr uint32_t Usart1 = Mck;
+// static constexpr uint32_t Usb = 48_MHz;
+
+ static bool inline
+ enable()
+ {
+ ClockGen::setFlashLatency(); // Flash runs off MCK
+
+ ClockGen::enableMainInternal(MainInternalFreq::Rc12Mhz);
+ ClockGen::selectMainClockSource(MainClockSource::Internal);
+ ClockGen::enablePllA();
+ ClockGen::selectMasterClk();
+ ClockGen::updateCoreFrequency();
+
+ return true;
+ }
+};
+
+using Led0 = GpioA23;
+using Led1 = GpioC9;
+using ButtonSW0 = GpioA9;
+
+// No SoftwareGpioPort yet for SAM
+struct Leds
+{
+ static constexpr std::size_t width{2};
+
+ static void setOutput()
+ {
+ Led0::setOutput();
+ Led1::setOutput();
+ }
+
+ static void write(uint32_t value)
+ {
+ Led0::set(value & 1);
+ Led1::set(value & 2);
+ }
+};
+
+struct Debug
+{
+ using Uart = Usart1;
+ using UartTx = GpioB4;
+ using UartRx = GpioA21;
+};
+
+using LoggerDevice = modm::IODeviceWrapper;
+
+inline void
+initialize()
+{
+ // Turn off the watchdog
+ WDT->WDT_MR = WDT_MR_WDDIS_Msk;
+
+ SystemClock::enable();
+ SysTickTimer::initialize();
+
+ // Disable JTAG TDI function on debug UART TX pin
+ MATRIX->CCFG_SYSIO |= CCFG_SYSIO_SYSIO4;
+
+ Debug::Uart::initialize();
+ Debug::Uart::connect();
+
+ Leds::setOutput();
+ ButtonSW0::setInput(InputType::PullUp);
+}
+
+/*
+// TODO: usb
+inline void initializeUsbFs()
+{
+ //SystemClock::enableUsb();
+ //modm::platform::Usb::initialize();
+}
+*/
+/// @}
+
+} // namespace Board
+
diff --git a/src/modm/board/samv71_xplained_ultra/board.xml b/src/modm/board/samv71_xplained_ultra/board.xml
new file mode 100644
index 0000000000..b13402668a
--- /dev/null
+++ b/src/modm/board/samv71_xplained_ultra/board.xml
@@ -0,0 +1,14 @@
+
+
+
+ ../../../../repo.lb
+
+
+
+
+ samv71q21b-aab
+
+
+ modm:board:samv71-xplained-ultra
+
+
diff --git a/src/modm/board/samv71_xplained_ultra/module.lb b/src/modm/board/samv71_xplained_ultra/module.lb
new file mode 100644
index 0000000000..1b08b54603
--- /dev/null
+++ b/src/modm/board/samv71_xplained_ultra/module.lb
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, Jeff McBride
+# Copyright (c) 2022, Christopher Durand
+#
+# 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:samv71-xplained-ultra"
+ module.description = "Microchip SAMV71 Xplained Ultra"
+
+def prepare(module, options):
+ if not options[":target"].partname == "samv71q21b-aab":
+ return False
+
+ module.depends(":debug", ":platform:clockgen", ":platform:gpio", ":platform:core", ":platform:usart:1") #, ":platform:usb")
+ return True
+
+def build(env):
+ env.outbasepath = "modm/src/modm/board"
+ env.substitutions = {
+ "with_logger": True,
+ "with_assert": env.has_module(":architecture:assert")
+ }
+ env.template("../board.cpp.in", "board.cpp")
+ env.copy('board.hpp')
+ env.copy(repopath("tools/openocd/modm/atmel_samv71_xplained_ultra.cfg"), "atmel_samv71_xplained_ultra.cfg")
+ env.collect(":build:openocd.source", "modm/src/modm/board/atmel_samv71_xplained_ultra.cfg")
diff --git a/src/modm/platform/clock/sam/gclk.cpp.in b/src/modm/platform/clock/sam/gclk.cpp.in
index b2e3890a41..0b7e1759f0 100644
--- a/src/modm/platform/clock/sam/gclk.cpp.in
+++ b/src/modm/platform/clock/sam/gclk.cpp.in
@@ -1,6 +1,7 @@
/*
* Copyright (c) 2020-2021, Niklas Hauser
* Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -23,100 +24,53 @@ namespace modm::platform
constinit uint16_t modm_fastdata delay_fcpu_MHz(computeDelayMhz(GenericClockController::BootFrequency));
constinit uint16_t modm_fastdata delay_ns_per_loop(computeDelayNsPerLoop(GenericClockController::BootFrequency));
+%% if target.family == "d1x/d2x/dax"
bool
-GenericClockController::initOsc8MHz(uint32_t waitCycles)
+GenericClockController::configureOsc8m(Osc8mPrescaler prescaler, uint32_t waitCycles)
{
- SYSCTRL->OSC8M.bit.PRESC = 0x0;
+ SYSCTRL->OSC8M.bit.PRESC = static_cast(prescaler);
SYSCTRL->OSC8M.bit.ONDEMAND = true;
SYSCTRL->OSC8M.bit.RUNSTDBY = false;
SYSCTRL->OSC8M.bit.ENABLE = true;
while (!SYSCTRL->PCLKSR.bit.OSC8MRDY && --waitCycles);
return waitCycles;
}
+%% endif
+%% if target.family == "d1x/d2x/dax"
+/// Enable DFLL48M in open-loop mode
bool
-GenericClockController::initExternalCrystal(uint32_t waitCycles)
+GenericClockController::enableDfll48m(uint32_t waitCycles)
{
- // Enable external crystal.
- SYSCTRL->XOSC32K.reg =
- SYSCTRL_XOSC32K_STARTUP( 0x6u ) |
- SYSCTRL_XOSC32K_XTALEN |
- SYSCTRL_XOSC32K_RUNSTDBY |
- SYSCTRL_XOSC32K_EN32K;
- // separate call, as described in chapter 15.6.3
- SYSCTRL->XOSC32K.bit.ENABLE = 1;
- while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY and --waitCycles) ;
-
- // Write Generic Clock Generator configuration
- GCLK->GENCTRL.reg =
- GCLK_GENCTRL_ID(uint32_t(ClockGenerator::ExternalCrystal32K)) |
- GCLK_GENCTRL_SRC_XOSC32K |
- GCLK_GENCTRL_IDC |
- GCLK_GENCTRL_GENEN;
- // Wait for synchronization.
- while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles) ;
-
- return waitCycles;
-}
-
-bool
-GenericClockController::initDFLL48MHz(uint32_t waitCycles)
-{
- // Put ExternalCrystal as source for the PLL
- GCLK->CLKCTRL.reg =
- GCLK_CLKCTRL_ID(uint32_t(ClockMux::DFLL48M)) |
- GCLK_CLKCTRL_GEN(uint32_t(ClockGenerator::ExternalCrystal32K)) |
- GCLK_CLKCTRL_CLKEN;
- // Wait for synchronization.
- while (GCLK->STATUS.bit.SYNCBUSY and --waitCycles) ;
-
- // Errata 1.2.1: Disable the OnDemand mode
+ // Errata 1.2.1: Disable OnDemand mode
SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0;
- // Wait for synchronization.
- while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ;
+ // Wait for synchronization
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles;
- SYSCTRL->DFLLMUL.reg =
- SYSCTRL_DFLLMUL_CSTEP( 31 ) |
- SYSCTRL_DFLLMUL_FSTEP( 511 ) |
- SYSCTRL_DFLLMUL_MUL(48_MHz / 32'768_Hz);
- // Wait for synchronization.
- while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ;
+ // TODO: is returning the right thing to do?
+ if (waitCycles == 0)
+ return false;
+
+ // read calibration data from "NVM Software Calibration Area" (128 bits from address 0x806020)
+ // DFLL coarse value is in bits 63:58
+ const auto calibrationData = *reinterpret_cast(0x806020 + 4);
+ const auto dfllCoarseCalibration = (calibrationData >> (58 - 32)) & 0b11'1111;
+ SYSCTRL->DFLLVAL.reg = (dfllCoarseCalibration << SYSCTRL_DFLLVAL_COARSE_Pos)
+ | (512 << SYSCTRL_DFLLVAL_FINE_Pos);
// Write full configuration to DFLL control register
- SYSCTRL->DFLLCTRL.reg |=
- SYSCTRL_DFLLCTRL_MODE | // Enable the closed loop mode
+ SYSCTRL->DFLLCTRL.reg = (SYSCTRL->DFLLCTRL.reg |
SYSCTRL_DFLLCTRL_WAITLOCK | // No output until DFLL is locked.
- SYSCTRL_DFLLCTRL_QLDIS ; // Disable Quick lock
- // Wait for synchronization.
- while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ;
+ SYSCTRL_DFLLCTRL_QLDIS) // Disable quick lock
+ // Disable closed-loop and USB clock recovery mode
+ & ~(SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_USBCRM);
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY);
- // Enable the DFLL
SYSCTRL->DFLLCTRL.bit.ENABLE = true;
- // Wait for locks flags
- while (
- not (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) or
- not (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF));
- // Wait for synchronization.
- while (!SYSCTRL->PCLKSR.bit.DFLLRDY and --waitCycles) ;
-
- return waitCycles;
-}
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles;
-bool
-GenericClockController::setSystemClock(ClockSource source, uint32_t waitCycles)
-{
- GCLK->GENDIV.reg =
- GCLK_GENDIV_ID(uint32_t(ClockGenerator::System)) |
- GCLK_GENDIV_DIV(0u);
- GCLK->GENCTRL.reg =
- GCLK_GENCTRL_ID(uint32_t(ClockGenerator::System)) |
- GCLK_GENCTRL_SRC(uint32_t(source)) |
- GCLK_GENCTRL_IDC |
- GCLK_GENCTRL_GENEN;
- // Wait for synchronization.
- while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) ;
-
- return waitCycles;
+ return waitCycles > 0;
}
+%% endif
}
diff --git a/src/modm/platform/clock/sam/gclk.hpp b/src/modm/platform/clock/sam/gclk.hpp
deleted file mode 100644
index 84d25abfa7..0000000000
--- a/src/modm/platform/clock/sam/gclk.hpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (c) 2019, Ethan Slattery
- * Copyright (c) 2020, Erik Henriksson
- *
- * 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
-
-namespace modm::platform
-{
-/// @ingroup modm_platform_gclk
-/// @{
-
-enum class
-ClockSource : uint32_t
-{
- OSC8M = GCLK_GENCTRL_SRC_OSC8M_Val,
- DFLL48M = GCLK_GENCTRL_SRC_DFLL48M_Val
-};
-
-enum class
-ClockGenerator : uint32_t
-{
- System = 0,
- ExternalCrystal32K = 1,
- ULP32K = 2,
- Internal8M = 3,
-};
-
-enum class
-ClockPeripheral : uint32_t
-{
- Dfll48 = GCLK_CLKCTRL_ID_DFLL48_Val,
- Fdpll = GCLK_CLKCTRL_ID_FDPLL_Val,
- Fdpll32K = GCLK_CLKCTRL_ID_FDPLL32K_Val,
- Wdt = GCLK_CLKCTRL_ID_WDT_Val,
- Rtc = GCLK_CLKCTRL_ID_RTC_Val,
- Eic = GCLK_CLKCTRL_ID_EIC_Val,
- Usb = GCLK_CLKCTRL_ID_USB_Val,
- Evsys0 = GCLK_CLKCTRL_ID_EVSYS_0_Val,
- Evsys1 = GCLK_CLKCTRL_ID_EVSYS_1_Val,
- Evsys2 = GCLK_CLKCTRL_ID_EVSYS_2_Val,
- Evsys3 = GCLK_CLKCTRL_ID_EVSYS_3_Val,
- Evsys4 = GCLK_CLKCTRL_ID_EVSYS_4_Val,
- Evsys5 = GCLK_CLKCTRL_ID_EVSYS_5_Val,
- Evsys6 = GCLK_CLKCTRL_ID_EVSYS_6_Val,
- Evsys7 = GCLK_CLKCTRL_ID_EVSYS_7_Val,
- Evsys8 = GCLK_CLKCTRL_ID_EVSYS_8_Val,
- Evsys9 = GCLK_CLKCTRL_ID_EVSYS_9_Val,
- Evsys10 = GCLK_CLKCTRL_ID_EVSYS_10_Val,
- Evsys11 = GCLK_CLKCTRL_ID_EVSYS_11_Val,
- SercomXSlow = GCLK_CLKCTRL_ID_SERCOMX_SLOW_Val,
- Sercom0 = GCLK_CLKCTRL_ID_SERCOM0_CORE_Val,
- Sercom1 = GCLK_CLKCTRL_ID_SERCOM1_CORE_Val,
- Sercom2 = GCLK_CLKCTRL_ID_SERCOM2_CORE_Val,
- Sercom3 = GCLK_CLKCTRL_ID_SERCOM3_CORE_Val,
- Sercom4 = GCLK_CLKCTRL_ID_SERCOM4_CORE_Val,
- Sercom5 = GCLK_CLKCTRL_ID_SERCOM5_CORE_Val,
- Tcc0 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val,
- Tcc1 = GCLK_CLKCTRL_ID_TCC0_TCC1_Val,
- Tcc2 = GCLK_CLKCTRL_ID_TCC2_TC3_Val,
- Tc3 = GCLK_CLKCTRL_ID_TCC2_TC3_Val,
- Tc4 = GCLK_CLKCTRL_ID_TC4_TC5_Val,
- Tc5 = GCLK_CLKCTRL_ID_TC4_TC5_Val,
- Tc6 = GCLK_CLKCTRL_ID_TC6_TC7_Val,
- Tc7 = GCLK_CLKCTRL_ID_TC6_TC7_Val,
- Adc = GCLK_CLKCTRL_ID_ADC_Val,
- AcDig = GCLK_CLKCTRL_ID_AC_DIG_Val,
- AcAna = GCLK_CLKCTRL_ID_AC_ANA_Val,
- Dac = GCLK_CLKCTRL_ID_DAC_Val,
- Ptc = GCLK_CLKCTRL_ID_PTC_Val,
- I2s0 = GCLK_CLKCTRL_ID_I2S_0_Val,
- I2s1 = GCLK_CLKCTRL_ID_I2S_1_Val
-};
-/// @}
-
-/**
- * Clock management
- *
- * \ingroup modm_platform_gclk
- */
-class GenericClockController
-{
-public:
- static constexpr uint32_t BootFrequency = 1'000'000;
-
- template< uint32_t Core_Hz>
- static uint32_t
- setFlashLatency();
-
- template< uint32_t Core_Hz >
- static void
- updateCoreFrequency();
-
- static bool
- initOsc8MHz(uint32_t waitCycles = 2048);
-
- static bool
- initExternalCrystal(uint32_t waitCycles = 2048);
-
- static bool
- initDFLL48MHz(uint32_t waitCycles = 2048);
-
- static bool
- setSystemClock(
- ClockSource source = ClockSource::OSC8M,
- uint32_t waitCycles = 2048);
-
- template< ClockPeripheral peripheral >
- static void
- connect(ClockGenerator clockGen);
-
-private:
-
- enum class
- ClockMux : uint32_t
- {
- DFLL48M = 0,
- };
-};
-
-}
-
-#include "gclk_impl.hpp"
diff --git a/src/modm/platform/clock/sam/gclk.hpp.in b/src/modm/platform/clock/sam/gclk.hpp.in
new file mode 100644
index 0000000000..397b0acc0e
--- /dev/null
+++ b/src/modm/platform/clock/sam/gclk.hpp.in
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2019, Ethan Slattery
+ * Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2022, Christopher Durand
+ *
+ * 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
+#include
+#include
+
+namespace modm::platform
+{
+/// @ingroup modm_platform_gclk
+/// @{
+
+enum class
+ClockSource : uint32_t
+{
+%% for source in clock_sources
+ {{ source.name }} = {{ source.value }}{% if not loop.last %},{% endif %}
+%% endfor
+};
+
+enum class
+ClockGenerator : uint32_t
+{
+ System = 0,
+ Main = 0,
+%% for i in range(0, generator_count)
+ Generator{{i}} = {{i}}{% if not loop.last %},{% endif %}
+%% endfor
+};
+
+struct GeneratorConfiguration
+{
+ ClockSource source{};
+
+ /**
+ * The clock will be divided by the selected divider.
+ * Normal integer division and division by 2^(N+1) is supported.
+ * The registers will be automatically configured in the appropriate mode
+ * for division by powers of 2.
+ *
+ * @warning Not all generator instances support the full 16 bit divider value range.
+ * This is currently not checked yet at compile-time. Only the maximum limits
+ * are verified.
+ */
+ uint32_t divider = 1;
+
+ bool gpioOutputEnabled = false;
+};
+
+enum class
+ClockPeripheral : uint32_t
+{
+%% for clock in peripheral_clocks
+%% set instance=clock.get("instance", "")
+%% set name=clock.get("name", "")
+%% if clock["peripheral"] in ["sysctrl", "oscctrl"]
+%% set peripheral=""
+%% else
+%% set peripheral=clock["peripheral"]
+%% endif
+%% if clock["peripheral"] == "sercom" and name == "core"
+%% set name=""
+%% endif
+%% set id=peripheral.capitalize() + instance + name.capitalize()
+ {{ id }} = {{clock["value"]}}{% if not loop.last %},{% endif %}
+%% endfor
+};
+
+%% if has_dpll
+
+struct DpllSource
+{
+ enum class DpllReference
+ {
+ // values correspond to REFCLOCK field in DPLLCTRLB
+%% if target.family == "d5x/e5x"
+ Gclk = 0,
+ Xosc32 = 1,
+ Xosc0 = 2,
+ Xosc1 = 3
+%% elif target.family == "d1x/d2x/dax"
+ Xosc32 = 0,
+ Xosc = 1,
+ Gclk = 2
+%% endif
+ };
+
+ DpllReference reference{};
+ frequency_t frequency{};
+
+ constexpr bool isXoscSource() const
+ {
+%% if target.family == "d5x/e5x"
+ return (reference == DpllReference::Xosc0)
+ || (reference == DpllReference::Xosc1);
+%% elif target.family == "d1x/d2x/dax"
+ return reference == DpllReference::Xosc;
+%% endif
+ }
+};
+
+template
+constexpr DpllSource GclkSource = DpllSource{DpllSource::DpllReference::Gclk, freq};
+
+template
+constexpr DpllSource Xosc32Source = DpllSource{DpllSource::DpllReference::Xosc32, freq};
+
+%% if target.family == "d5x/e5x"
+template
+constexpr DpllSource Xosc0Source = DpllSource{DpllSource::DpllReference::Xosc0, freq};
+
+template
+constexpr DpllSource Xosc1Source = DpllSource{DpllSource::DpllReference::Xosc1, freq};
+%% elif target.family == "d1x/d2x/dax"
+template
+constexpr DpllSource XoscSource = DpllSource{DpllSource::DpllReference::Xosc, freq};
+%% endif
+
+struct DpllConfig
+{
+%% if target.family == "d5x/e5x"
+ static constexpr uint16_t MultiplierFractionalBits{5};
+ static constexpr uint16_t MultiplierIntegerBits{13};
+ static constexpr unsigned MaxReference{kHz(3200)};
+ static constexpr unsigned MinOutput{MHz(96)};
+ static constexpr unsigned MaxOutput{MHz(200)};
+%% elif target.family == "d1x/d2x/dax"
+ static constexpr uint16_t MultiplierFractionalBits{4};
+ static constexpr uint16_t MultiplierIntegerBits{12};
+ static constexpr unsigned MaxReference{MHz(2)};
+ static constexpr unsigned MinOutput{MHz(48)};
+ static constexpr unsigned MaxOutput{MHz(96)};
+%% endif
+ static constexpr unsigned MinReference{kHz(32)};
+ // divider operation: f_out = f_in / (2 * (DIV + 1))
+ static constexpr uint16_t XoscDividerBits{11};
+
+ uint16_t integerMultiplier{};
+ uint16_t fractionalMultiplier{};
+ uint16_t xoscDivider{};
+};
+
+%% if target.family == "d5x/e5x"
+enum class DpllInstance
+{
+ Dpll0 = 0,
+ Dpll1 = 1
+};
+%% endif
+%% endif
+
+%% if target.family == "d5x/e5x"
+enum class Xosc
+{
+ Xosc0 = 0,
+ Xosc1 = 1
+};
+%% endif
+
+enum class XoscStartupTime
+{
+ Start_31us = 0x0u,
+ Start_61us = 0x1u,
+ Start_122us = 0x2u,
+ Start_244us = 0x3u,
+ Start_488us = 0x4u,
+ Start_977us = 0x5u,
+ Start_1953us = 0x6u,
+ Start_3906us = 0x7u,
+ Start_7813us = 0x8u,
+ Start_15625us = 0x9u,
+ Start_31250us = 0xAu,
+ Start_61250us = 0xBu,
+ Start_125000us = 0xCu,
+ Start_250000us = 0xDu,
+ Start_500000us = 0xEu,
+ Start_1000000us = 0xFu
+};
+
+enum class Xosc32StartupTime
+{
+%% if target.family == "d1x/d2x/dax"
+ Start_132us = 0x0u,
+ Start_1ms = 0x1u,
+ Start_63ms = 0x2u,
+ Start_125ms = 0x3u,
+ Start_500ms = 0x4u,
+ Start_1000ms = 0x5u,
+ Start_2000ms = 0x6u,
+ Start_4000ms = 0x7u
+%% else
+ Start_63ms = 0x0u,
+ Start_125ms = 0x1u,
+ Start_500ms = 0x2u,
+ Start_1000ms = 0x3u,
+ Start_2000ms = 0x4u,
+ Start_4000ms = 0x5u,
+ Start_8000ms = 0x6u
+%% endif
+};
+
+%% if target.family == "d1x/d2x/dax"
+ enum class Osc8mPrescaler
+ {
+ Prescaler1 = 0x0,
+ Prescaler2 = 0x1,
+ Prescaler4 = 0x2,
+ Prescaler8 = 0x3
+ };
+%% endif
+
+/// @}
+
+/**
+ * Clock management
+ *
+ * @ingroup modm_platform_gclk
+ */
+class GenericClockController
+{
+public:
+ static constexpr uint32_t BootFrequency = {{ boot_frequency }};
+
+ template< uint32_t Core_Hz, uint16_t Vdd_mV=3300 >
+ static uint32_t
+ setFlashLatency();
+
+ template< uint32_t Core_Hz >
+ static void
+ updateCoreFrequency();
+
+%% if target.family == "d1x/d2x/dax"
+ /// Configure frequency of the internal oscillator
+ static bool
+ configureOsc8m(Osc8mPrescaler prescaler, uint32_t waitCycles = 10'000);
+
+ /// Enable DFLL48M in open-loop mode
+ static bool
+ enableDfll48m(uint32_t waitCycles = 10'000);
+%% endif
+
+ /// Enable DFLL48M in closed-loop mode
+ /// @warning The reference clock on GCLK channel 0 must be available prior to calling this function
+ template
+ static bool
+ enableDfll48mClosedLoop(uint32_t waitCycles = 10'000);
+
+ /// Enable PLL with automatically computed coefficients
+ /// If the specified tolerance is exceeded, compilation will fail.
+ template
+ static bool
+%% if target.family == "d5x/e5x"
+ enableDpll(DpllInstance instance = DpllInstance::Dpll0);
+%% else
+ enableDpll();
+%% endif
+
+ /// Enable PLL with manual configuration
+ template
+ static bool
+%% if target.family == "d5x/e5x"
+ enableDpll(DpllInstance instance = DpllInstance::Dpll0);
+%% else
+ enableDpll();
+%% endif
+
+ static inline void
+%% if target.family == "d5x/e5x"
+ disableDpll(DpllInstance instance = DpllInstance::Dpll0);
+%% else
+ disableDpll();
+%% endif
+
+ template
+ static bool
+%% if target.family == "d5x/e5x"
+ enableExternalCrystal(Xosc clock, uint32_t waitCycles = (1000u << unsigned(startupTime)));
+%% else
+ enableExternalCrystal(uint32_t waitCycles = (1000u << unsigned(startupTime)));
+%% endif
+
+ static inline bool
+%% if target.family == "d5x/e5x"
+ enableExternalClock(Xosc clock, uint32_t waitCycles = 10'000);
+%% else
+ enableExternalClock(uint32_t waitCycles = 10'000);
+%% endif
+
+ static inline bool
+ enableExternalCrystal32k(Xosc32StartupTime time);
+
+ static inline bool
+ enableExternalClock32k();
+
+ template
+ static void
+ connect(ClockGenerator clockGen);
+
+ template
+ static void
+ enableGenerator();
+
+ template
+ static void
+ enableGenerator();
+
+ template
+ static void
+ disableGenerator();
+
+ /**
+ * Convenience function to configure "Main" clock generator source
+ * with a divider of 1.
+ *
+ * Equivalent to:
+ * @code
+ * const auto config = GeneratorConfiguration{ .source = SOURCE, .divider = 1 };
+ * GenericClockController::enableGenerator();
+ * @endcode
+ */
+ template
+ static bool
+ setSystemClock();
+
+private:
+ static inline void
+ sync();
+
+ static inline void
+ sync(ClockGenerator clockGen);
+};
+
+#if defined(__DOXYGEN__)
+/// Peripheral AHB/APB bus clock control
+/// @ingroup modm_platform_gclk
+template
+struct PeripheralClock
+{
+ /// Enable peripheral clock
+ static void enable();
+ /// Disable peripheral clock
+ static void disable();
+};
+#else
+template
+struct PeripheralClock;
+#endif
+
+}
+
+#include "gclk_impl.hpp"
diff --git a/src/modm/platform/clock/sam/gclk_impl.hpp.in b/src/modm/platform/clock/sam/gclk_impl.hpp.in
index 32485f9bbc..6a2a198802 100644
--- a/src/modm/platform/clock/sam/gclk_impl.hpp.in
+++ b/src/modm/platform/clock/sam/gclk_impl.hpp.in
@@ -2,6 +2,7 @@
* Copyright (c) 2019, Ethan Slattery
* Copyright (c) 2020, Erik Henriksson
* Copyright (c) 2021, Niklas Hauser
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -12,12 +13,41 @@
// ----------------------------------------------------------------------------
#include
-#include
+#include
+#include
+#include
+#include
namespace modm::platform
{
extern "C" uint32_t SystemCoreClock;
+%% for peripheral, clocks in clock_map.items()
+template<>
+%% set name=peripheral[0]
+%% set instance=peripheral[1]
+%% if instance
+struct PeripheralClock>
+%% else
+struct PeripheralClock
+%% endif
+{
+ static inline void enable()
+ {
+%% for clock in clocks
+ {{clock[0]}}->{{clock[1]}}MASK.reg |= (1u << {{clock[2]}});
+%% endfor
+ }
+
+ static inline void disable()
+ {
+%% for clock in clocks
+ {{clock[0]}}->{{clock[1]}}MASK.reg &= ~(1u << {{clock[2]}});
+%% endfor
+ }
+};
+%% endfor
+
template< uint32_t Core_Hz >
void
GenericClockController::updateCoreFrequency()
@@ -27,28 +57,577 @@ GenericClockController::updateCoreFrequency()
delay_ns_per_loop = computeDelayNsPerLoop(Core_Hz);
}
-template< uint32_t Core_Hz >
+template< uint32_t Core_Hz, uint16_t Vdd_mV=3300 >
uint32_t
GenericClockController::setFlashLatency()
{
+%% if target.family == "d5x/e5x":
+{% raw %}
+ // TODO: move to device files
+ constexpr std::array, 6> waitStates0{{
+ {22_MHz, 0}, { 44_MHz, 1}, { 67_MHz, 2},
+ {89_MHz, 3}, {111_MHz, 4}, {120_MHz, 5}
+ }};
+ constexpr std::array, 6> waitStates1{{
+ { 24_MHz, 0}, { 51_MHz, 1}, { 77_MHz, 2},
+ {101_MHz, 3}, {119_MHz, 4}, {120_MHz, 5}
+ }};
+{% endraw %}
+ constexpr auto waitStates = (Vdd_mV >= 2700) ? waitStates1 : waitStates0;
+ constexpr auto rws = std::lower_bound(std::begin(waitStates), std::end(waitStates), Core_Hz,
+ [](auto w1, auto w2) {
+ return w1.first < w2;
+ })->second;
+ NVMCTRL->CTRLA.bit.RWS = rws;
+ NVMCTRL->CTRLA.bit.AUTOWS = 0;
+%% else
// See table 41.11 (NVM Characteristics) in the datasheet
if constexpr (Core_Hz > 24_MHz) {
NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val;
} else {
NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_SINGLE_Val;
}
+%% endif
return Core_Hz;
}
+template
+bool
+GenericClockController::enableDfll48mClosedLoop(uint32_t waitCycles)
+{
+ static_assert(reference > 732_Hz, "DFLL48 reference frequency must be larger than 732 Hz");
+ static_assert(reference < 43_kHz, "DFLL48 reference frequency must be less than 33 kHz");
+ constexpr auto multiplier = uint16_t(std::round(48_MHz / double(reference)));
+
+%% if target.family == "d1x/d2x/dax"
+ // Errata 1.2.1: Disable OnDemand mode
+ SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0;
+ // Wait for synchronization
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles;
+
+ // TODO: is returning the right thing to do?
+ if (waitCycles == 0)
+ return false;
+
+ // read DFLL coarse value calibration from "NVM Software Calibration Area" (0x806020, 128 bits)
+ // DFLL coarse calibration value is in bits 63:58
+ const auto calibrationData = *reinterpret_cast(0x806020 + 4);
+ const auto dfllCoarseCalibration = (calibrationData >> (58 - 32)) & 0b11'1111;
+ SYSCTRL->DFLLVAL.reg = (dfllCoarseCalibration << SYSCTRL_DFLLVAL_COARSE_Pos);
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY);
+
+ // Skip coarse lock phase with DFLLVAL.COARSE pre-programmed (17.6.7.1.2 in datasheet)
+ SYSCTRL->DFLLCTRL.bit.BPLCKC = 1;
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY);
+
+ // Set multiplier and adjustment step sizes
+ // TODO: evaluate step size values
+ SYSCTRL->DFLLMUL.reg =
+ SYSCTRL_DFLLMUL_CSTEP(8) |
+ SYSCTRL_DFLLMUL_FSTEP(8) |
+ SYSCTRL_DFLLMUL_MUL(multiplier);
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY);
+
+ // Write full configuration to DFLL control register
+ SYSCTRL->DFLLCTRL.reg = (SYSCTRL->DFLLCTRL.reg |
+ SYSCTRL_DFLLCTRL_MODE | // Enable closed-loop mode
+ SYSCTRL_DFLLCTRL_WAITLOCK | // No output until DFLL is locked.
+ SYSCTRL_DFLLCTRL_QLDIS) // Disable quick lock
+ // Disable USB clock recovery mode
+ & ~(SYSCTRL_DFLLCTRL_USBCRM);
+
+ SYSCTRL->DFLLCTRL.bit.ENABLE = true;
+ while (!SYSCTRL->PCLKSR.bit.DFLLRDY && waitCycles > 0) --waitCycles;
+ // wait for PLL lock
+ while (!SYSCTRL->PCLKSR.bit.DFLLLCKC && waitCycles > 0) --waitCycles;
+ while (!SYSCTRL->PCLKSR.bit.DFLLLCKF && waitCycles > 0) --waitCycles;
+%% else
+ // Set multiplier and adjustment step sizes
+ // TODO: evaluate step size values
+ OSCCTRL->DFLLMUL.reg =
+ OSCCTRL_DFLLMUL_CSTEP(8) |
+ OSCCTRL_DFLLMUL_FSTEP(8) |
+ OSCCTRL_DFLLMUL_MUL(multiplier);
+ while (OSCCTRL->DFLLSYNC.bit.DFLLMUL);
+
+ OSCCTRL->DFLLCTRLB.reg =
+ OSCCTRL_DFLLCTRLB_MODE | // Enable closed-loop mode
+ OSCCTRL_DFLLCTRLB_QLDIS; // Disable quick lock
+ while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB);
+
+ OSCCTRL->DFLLCTRLA.bit.ONDEMAND = 0;
+ OSCCTRL->DFLLCTRLA.bit.ENABLE = 1;
+ while (OSCCTRL->DFLLSYNC.bit.ENABLE);
+
+ // wait for PLL lock
+ while (!OSCCTRL->STATUS.bit.DFLLLCKC && waitCycles > 0) --waitCycles;
+ while (!OSCCTRL->STATUS.bit.DFLLLCKF && waitCycles > 0) --waitCycles;
+ while (!OSCCTRL->STATUS.bit.DFLLRDY && waitCycles > 0) --waitCycles;
+%% endif
+
+ return waitCycles > 0;
+}
+
+%% if target.family == "d1x/d2x/dax"
template< ClockPeripheral peripheral >
void
GenericClockController::connect(ClockGenerator clockGen)
{
+ GCLK->CLKCTRL.bit.ID = uint8_t(peripheral);
+ GCLK->CLKCTRL.bit.CLKEN = 0;
+ while (GCLK->CLKCTRL.bit.CLKEN);
+
GCLK->CLKCTRL.reg =
GCLK_CLKCTRL_CLKEN |
GCLK_CLKCTRL_GEN(uint32_t(clockGen)) |
- GCLK_CLKCTRL_ID(uint32_t(peripheral));
+ GCLK_CLKCTRL_ID(uint8_t(peripheral));
+ sync();
+}
+
+void
+GenericClockController::sync()
+{
+ while (GCLK->STATUS.bit.SYNCBUSY);
+}
+
+void
+GenericClockController::sync(ClockGenerator)
+{
+ sync();
+}
+
+%% elif target.family == "d5x/e5x"
+void
+GenericClockController::sync()
+{
+ constexpr auto mask = GCLK_SYNCBUSY_GENCTRL_Msk | GCLK_SYNCBUSY_SWRST;
+ while (GCLK->SYNCBUSY.reg & mask);
+}
+
+void
+GenericClockController::sync(ClockGenerator clockGen)
+{
+ const auto bit = static_cast(clockGen) << GCLK_SYNCBUSY_GENCTRL_Pos;
+ while (GCLK->SYNCBUSY.reg & bit);
+}
+
+template< ClockPeripheral peripheral >
+void
+GenericClockController::connect(ClockGenerator clockGen)
+{
+ auto& reg = GCLK->PCHCTRL[static_cast(peripheral)].reg;
+ reg = 0;
+ while (reg != 0);
+ reg = GCLK_PCHCTRL_CHEN | (static_cast(clockGen) << GCLK_PCHCTRL_GEN_Pos);
+ sync(clockGen);
+}
+%% endif
+
+template
+bool
+%% if target.family == "d5x/e5x"
+GenericClockController::enableExternalCrystal(Xosc clock, uint32_t waitCycles)
+%% else
+GenericClockController::enableExternalCrystal(uint32_t waitCycles)
+%% endif
+{
+%% if target.family == "d5x/e5x"
+%% set reg="OSCCTRL->XOSCCTRL[int(clock)]"
+%% set prefix="OSCCTRL_XOSCCTRL"
+%% set status="OSCCTRL->STATUS.reg & (OSCCTRL_STATUS_XOSCRDY0 << int(clock))"
+ PeripheralClock::enable();
+%% elif target.family == "d1x/d2x/dax"
+%% set reg="SYSCTRL->XOSC"
+%% set prefix="SYSCTRL_XOSC"
+%% set status="SYSCTRL->PCLKSR.bit.XOSCRDY"
+ PeripheralClock::enable();
+%% endif
+ {{reg}}.bit.STARTUP = uint32_t(startupTime);
+ {{reg}}.bit.XTALEN = 1;
+ // Automatic gain control can only be enabled when the crystal is running
+%% if target.family == "d5x/e5x"
+ {{reg}}.bit.ENALC = 0;
+ if constexpr (frequency <= 8'000'000) {
+ {{reg}}.bit.IMULT = 0x3;
+ {{reg}}.bit.IPTAT = 0x2;
+ } else if constexpr (frequency <= 16'000'000) {
+ {{reg}}.bit.IMULT = 0x4;
+ {{reg}}.bit.IPTAT = 0x3;
+ } else if constexpr (frequency <= 24'000'000) {
+ {{reg}}.bit.IMULT = 0x5;
+ {{reg}}.bit.IPTAT = 0x3;
+ } else {
+ {{reg}}.bit.IMULT = 0x6;
+ {{reg}}.bit.IPTAT = 0x3;
+ }
+%% else
+ {{reg}}.bit.AMPGC = 0;
+ if constexpr (frequency <= 2'000'000) {
+ {{reg}}.bit.GAIN = 0x0;
+ } else if constexpr (frequency <= 4'000'000) {
+ {{reg}}.bit.GAIN = 0x1;
+ } else if constexpr (frequency <= 8'000'000) {
+ {{reg}}.bit.GAIN = 0x2;
+ } else if constexpr (frequency <= 16'000'000) {
+ {{reg}}.bit.GAIN = 0x3;
+ } else {
+ {{reg}}.bit.GAIN = 0x4;
+ }
+%% endif
+
+ // force oscillator start even if no clock sink is using it yet
+ {{reg}}.bit.ONDEMAND = 0;
+
+ {{reg}}.bit.ENABLE = 1;
+ while(!({{status}}) && --waitCycles);
+ // Enable automatic gain control on success
+%% if target.family == "d5x/e5x"
+ {{reg}}.bit.ENALC = waitCycles > 0;
+%% else
+ {{reg}}.bit.AMPGC = waitCycles > 0;
+%% endif
+ return waitCycles;
+}
+
+bool
+%% if target.family == "d5x/e5x"
+GenericClockController::enableExternalClock(Xosc clock, uint32_t waitCycles)
+%% else
+GenericClockController::enableExternalClock(uint32_t waitCycles)
+%% endif
+{
+%% if target.family == "d5x/e5x"
+%% set reg="OSCCTRL->XOSCCTRL[int(clock)]"
+%% set status="OSCCTRL->STATUS.reg & (OSCCTRL_STATUS_XOSCRDY0 << int(clock))"
+ PeripheralClock::enable();
+%% elif target.family == "d1x/d2x/dax"
+%% set reg="SYSCTRL->XOSC"
+%% set status="SYSCTRL->PCLKSR.bit.XOSCRDY"
+ PeripheralClock::enable();
+%% endif
+ // force oscillator start even if no clock sink is using it yet
+ {{reg}}.bit.ONDEMAND = 0;
+
+ {{reg}}.bit.STARTUP = 0;
+ {{reg}}.bit.XTALEN = 0;
+ {{reg}}.bit.ENABLE = 1;
+ while(!({{status}}) && --waitCycles);
+ return waitCycles;
+}
+
+bool
+GenericClockController::enableExternalCrystal32k(Xosc32StartupTime time)
+{
+%% if target.family == "d1x/d2x/dax"
+%% set reg="SYSCTRL->XOSC32K"
+%% set status="SYSCTRL->PCLKSR.bit.XOSC32KRDY"
+ PeripheralClock::enable();
+%% else
+%% set reg="OSC32KCTRL->XOSC32K"
+%% set status="OSC32KCTRL->STATUS.bit.XOSC32KRDY"
+ PeripheralClock::enable();
+%% endif
+ // force oscillator start even if no clock sink is using it yet
+ {{reg}}.bit.ONDEMAND = 0;
+%% if target.family == "d1x/d2x/dax"
+ // disable non-functional automatic gain control, see errata 1.1.1
+ {{reg}}.bit.AAMPEN = 0;
+%% endif
+ {{reg}}.bit.EN1K = 1;
+ {{reg}}.bit.EN32K = 1;
+ {{reg}}.bit.RUNSTDBY = 1;
+ {{reg}}.bit.XTALEN = 1;
+ {{reg}}.bit.STARTUP = static_cast(time);
+ {{reg}}.bit.ENABLE = 1;
+ while(!({{status}}));
+ return true;
+}
+
+bool
+GenericClockController::enableExternalClock32k()
+{
+%% if target.family == "d1x/d2x/dax"
+%% set reg="SYSCTRL->XOSC32K"
+%% set status="SYSCTRL->PCLKSR.bit.XOSC32KRDY"
+ PeripheralClock::enable();
+%% else
+%% set reg="OSC32KCTRL->XOSC32K"
+%% set status="OSC32KCTRL->STATUS.bit.XOSC32KRDY"
+ PeripheralClock::enable();
+%% endif
+ // force oscillator start even if no clock sink is using it yet
+ {{reg}}.bit.ONDEMAND = 0;
+
+ {{reg}}.bit.EN1K = 1;
+ {{reg}}.bit.EN32K = 1;
+ {{reg}}.bit.RUNSTDBY = 1;
+ {{reg}}.bit.XTALEN = 0;
+ {{reg}}.bit.STARTUP = 0;
+ {{reg}}.bit.ENABLE = 1;
+ while(!({{status}}));
+ return true;
+}
+
+%% if has_dpll
+
+/// @cond
+namespace detail
+{
+
+struct DpllConfigCalculation : DpllConfig
+{
+ double bestOutputFrequency{};
+};
+
+consteval DpllConfigCalculation
+findDpllConfig(double inputClock, double target, bool fractional)
+{
+ uint32_t maxMultiplier = (1u << DpllConfig::MultiplierIntegerBits);
+ if (fractional) {
+ maxMultiplier = (1u << (DpllConfig::MultiplierIntegerBits + DpllConfig::MultiplierFractionalBits));
+ target *= (1 << DpllConfig::MultiplierFractionalBits);
+ }
+ // f_pll = f_reference * (1 + N_int + N_frac/(2^frac_bits))
+ const auto idealMultiplier = (target / inputClock) - 1;
+ const uint32_t multplier = std::min(maxMultiplier, std::round(idealMultiplier));
+ if (fractional) {
+ const auto output = inputClock * (multplier + 1) / (1u << DpllConfig::MultiplierFractionalBits);
+ return DpllConfigCalculation {
+ {.integerMultiplier = uint16_t(multplier >> DpllConfig::MultiplierFractionalBits),
+ .fractionalMultiplier = uint16_t(multplier & ((1u << DpllConfig::MultiplierFractionalBits) - 1)),
+ .xoscDivider = 0},
+ output
+ };
+ } else {
+ const auto output = inputClock * (multplier + 1);
+ return DpllConfigCalculation {
+ {.integerMultiplier = uint16_t(multplier),
+ .fractionalMultiplier = 0,
+ .xoscDivider = 0},
+ output
+ };
+ }
}
+consteval DpllConfigCalculation
+calculateDpllConfigXosc(double xoscClock, double target, bool fractional)
+{
+ DpllConfigCalculation bestConfig;
+ bestConfig.bestOutputFrequency = std::numeric_limits::max();
+ auto minError = std::numeric_limits::max();
+
+ for (uint32_t div = 0; div < (1u << DpllConfig::XoscDividerBits); ++div) {
+ // Xosc divider divides by / (2*(div + 1))
+ const double reference = xoscClock / (2*(div + 1));
+ if (reference < DpllConfig::MinReference || reference > DpllConfig::MaxReference) continue;
+ auto result = findDpllConfig(reference, target, fractional);
+ const auto output = result.bestOutputFrequency;
+ result.xoscDivider = div;
+ const double error = std::abs(target - output);
+ if (error < minError && output >= DpllConfig::MinOutput && output <= DpllConfig::MaxOutput) {
+ bestConfig = result;
+ minError = error;
+ }
+ }
+ return bestConfig;
}
+consteval DpllConfigCalculation
+findDpllConfigXosc(double xoscClock, double target, uint16_t tolerance_ppm [[maybe_unused]])
+{
+ return calculateDpllConfigXosc(xoscClock, target, false);
+
+ // TODO: fractional mode is disabled for now, it was causing an unstable PLL for some parameters
+ // it can still be used with manually specified coefficients
+
+ // For reducing PLL jitter first try to find a suitable configuration in PLL integer mode.
+ // In case no solution is found fractional mode is used.
+ /*DpllConfigCalculation bestConfig{calculateDpllConfigXosc(xoscClock, target, false)};
+ const auto output = bestConfig.bestOutputFrequency;
+ const double error = std::abs(target - output);
+
+ // treat errors below tolerance as exact match
+ if ((error / target) < (tolerance_ppm * double(1e-6))) {
+ return bestConfig;
+ }
+
+ return calculateDpllConfigXosc(xoscClock, target, true);*/
+}
+
+}
+/// @endcond
+
+template
+bool
+%% if target.family == "d5x/e5x"
+GenericClockController::enableDpll(DpllInstance instance)
+%% else
+GenericClockController::enableDpll()
+%% endif
+{
+ constexpr auto config = [&]() {
+ if constexpr (source.isXoscSource()) {
+ return detail::findDpllConfigXosc(source.frequency, output, tolerance_ppm);
+ } else {
+ const auto resultInt = detail::findDpllConfig(source.frequency, output, false);
+ // TODO: fractional mode is disabled for now, because of PLL instability issues
+ //if (std::abs((resultInt.bestOutputFrequency / output) - 1) < (tolerance_ppm* double(1e-6))) {
+ return resultInt;
+ //}
+ //return detail::findDpllConfig(source.frequency, output, true);
+ }
+ }();
+
+ static_assert(std::abs((config.bestOutputFrequency / output) - 1) <= tolerance_ppm, "DPLL clock tolerance exceeded");
+%% if target.family == "d5x/e5x"
+ return enableDpll(instance);
+%% else
+ return enableDpll();
+%% endif
+}
+
+/// Enable PLL with manual configuration
+template
+bool
+%% if target.family == "d5x/e5x"
+GenericClockController::enableDpll(DpllInstance instance)
+%% else
+GenericClockController::enableDpll()
+%% endif
+{
+ constexpr auto reference = source.isXoscSource() ?
+ double(source.frequency) / (2 * (config.xoscDivider + 1)) :
+ double(source.frequency);
+
+ static_assert(reference >= DpllConfig::MinReference, "DPLL reference frequency is too low");
+ static_assert(reference <= DpllConfig::MaxReference, "DPLL reference frequency is too high");
+
+ static_assert(config.integerMultiplier < (1 << DpllConfig::MultiplierIntegerBits),
+ "DPLL integer multplier out of range");
+ static_assert(config.fractionalMultiplier < (1 << DpllConfig::MultiplierFractionalBits),
+ "DPLL fractional multplier out of range");
+
+ constexpr auto intFactor = config.integerMultiplier;
+ constexpr auto fracFactor = config.fractionalMultiplier / (1 << DpllConfig::MultiplierFractionalBits);
+ constexpr auto output = reference * (1 + intFactor + fracFactor);
+
+ static_assert(output >= DpllConfig::MinOutput, "DPLL output frequency is too low");
+ static_assert(output <= DpllConfig::MaxOutput, "DPLL output frequency is too high");
+
+%% if target.family == "d1x/d2x/dax"
+%% set dpll_peripheral="SYSCTRL"
+%% else
+%% set dpll_peripheral="OSCCTRL"
+%% endif
+
+%% if target.family == "d5x/e5x"
+%% set instance="Dpll[int(instance)]."
+%% else
+%% set instance=""
+%% endif
+ PeripheralClock::enable();
+ {{dpll_peripheral}}->{{instance}}DPLLRATIO.bit.LDR = config.integerMultiplier;
+ {{dpll_peripheral}}->{{instance}}DPLLRATIO.bit.LDRFRAC = config.fractionalMultiplier;
+ {{dpll_peripheral}}->{{instance}}DPLLCTRLB.bit.DIV = config.xoscDivider;
+ {{dpll_peripheral}}->{{instance}}DPLLCTRLB.bit.REFCLK = static_cast(source.reference);
+%% if target.family != "d1x/d2x/dax"
+ while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.DPLLRATIO);
+%% endif
+ // force oscillator start even if no clock sink is using it yet
+ {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ONDEMAND = 0;
+
+ {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ENABLE = 1;
+%% if target.family == "d1x/d2x/dax"
+ while(!{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.ENABLE);
+%% else
+ while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.ENABLE);
+%% endif
+ uint32_t counter{50000};
+ while (counter && !{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.CLKRDY) --counter;
+ while (counter && !{{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.LOCK) --counter;
+ return bool(counter);
+}
+
+void
+%% if target.family == "d5x/e5x"
+GenericClockController::disableDpll(DpllInstance instance)
+%% else
+GenericClockController::disableDpll()
+%% endif
+{
+ {{dpll_peripheral}}->{{instance}}DPLLCTRLA.bit.ENABLE = 0;
+%% if target.family == "d1x/d2x/dax"
+ while({{dpll_peripheral}}->{{instance}}DPLLSTATUS.bit.ENABLE);
+%% else
+ while({{dpll_peripheral}}->{{instance}}DPLLSYNCBUSY.bit.ENABLE);
+%% endif
+}
+%% endif
+
+template
+void
+GenericClockController::enableGenerator()
+{
+ constexpr bool powerOf2 = std::has_single_bit(config.divider) && (config.divider != 1);
+ constexpr auto dividerReg = powerOf2 ? std::countr_zero(config.divider) : config.divider;
+ // TODO: check individual divider limits for each generator instance
+ static_assert(config.divider != 0 && dividerReg <= std::numeric_limits::max(), "Divider out of range");
+
+%% if target.family == "d1x/d2x/dax"
+ constexpr auto gen = static_cast(clockGen);
+ GCLK->GENDIV.reg =
+ GCLK_GENDIV_ID(gen) |
+ GCLK_GENDIV_DIV(dividerReg);
+ GCLK->GENCTRL.reg =
+ GCLK_GENCTRL_ID(gen) |
+ GCLK_GENCTRL_SRC(static_cast(config.source)) |
+ (powerOf2 ? GCLK_GENCTRL_DIVSEL : 0u) |
+ (config.gpioOutputEnabled ? GCLK_GENCTRL_OE : 0u) |
+ GCLK_GENCTRL_IDC |
+ GCLK_GENCTRL_GENEN;
+%% else
+ GCLK->GENCTRL[static_cast(clockGen)].reg =
+ GCLK_GENCTRL_DIV(dividerReg) |
+ (powerOf2 ? GCLK_GENCTRL_DIVSEL : 0u) |
+ (config.gpioOutputEnabled ? GCLK_GENCTRL_OE : 0u) |
+ GCLK_GENCTRL_IDC |
+ GCLK_GENCTRL_GENEN |
+ GCLK_GENCTRL_SRC(static_cast(config.source));
+%% endif
+ sync(clockGen);
+}
+
+template
+void
+GenericClockController::enableGenerator()
+{
+ constexpr auto config = GeneratorConfiguration {
+ .source = source,
+ .divider = 1,
+ .gpioOutputEnabled = false
+ };
+ GenericClockController::enableGenerator();
+}
+
+template
+void
+GenericClockController::disableGenerator()
+{
+%% if target.family == "d1x/d2x/dax"
+ GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(static_cast(clockGen));
+%% else
+ GCLK->GENCTRL[static_cast(clockGen)].bit.GENEN = false;
+%% endif
+}
+
+template
+bool
+GenericClockController::setSystemClock()
+{
+ constexpr auto config = GeneratorConfiguration{ .source = source, .divider = 1 };
+ GenericClockController::enableGenerator();
+ sync(ClockGenerator::Main);
+ return true;
+}
+
+}
diff --git a/src/modm/platform/clock/sam/module.lb b/src/modm/platform/clock/sam/module.lb
index f68cb6aa3f..a710bf3923 100644
--- a/src/modm/platform/clock/sam/module.lb
+++ b/src/modm/platform/clock/sam/module.lb
@@ -3,6 +3,7 @@
#
# Copyright (c) 2016-2018, Niklas Hauser
# Copyright (c) 2017, Fabian Greif
+# Copyright (c) 2022, Christopher Durand
#
# This file is part of the modm project.
#
@@ -19,11 +20,42 @@ def prepare(module, options):
if not options[":target"].has_driver("gclk:sam"):
return False
- module.depends(":cmsis:device", ":architecture:delay")
+ module.depends(":cmsis:device", ":architecture:delay", ":platform:clock")
return True
def build(env):
+ target = env[":target"].identifier
+ driver = env[":target"].get_driver("gclk")
+
+ if target.family not in ["d1x/d2x/dax", "d5x/e5x"]:
+ raise RuntimeError("Unsupported device")
+
+ if target.family == "d5x/e5x":
+ boot_frequency = "48'000'000"
+ else: # d1x/d2x/dax
+ boot_frequency = "1'000'000"
+
+ clock_map = env.query(":cmsis:device:clock-map")
+ if target.series == "d21":
+ # Variant "l" devices have one comparator with instance number "1" and one without.
+ # To be compatible with the gpio data, instance number "0" is used for the first.
+ if target.variant == "l":
+ ac0 = clock_map[('AC', '')]
+ del clock_map[('AC', '')]
+ clock_map[('AC', '0')] = ac0
+ elif ('AC', '1') in clock_map:
+ del clock_map[('AC', '1')]
+
+ env.substitutions = {
+ "target": target,
+ "boot_frequency": boot_frequency,
+ "clock_map": clock_map,
+ "peripheral_clocks": driver["clock"],
+ "clock_sources": driver["source"],
+ "generator_count": int(driver["generators"][0]),
+ "has_dpll": True # True for all supported devices for now
+ }
env.outbasepath = "modm/src/modm/platform/clock"
- env.copy("gclk.hpp")
+ env.template("gclk.hpp.in")
env.template("gclk.cpp.in")
env.template("gclk_impl.hpp.in")
diff --git a/src/modm/platform/clock/sam_pmc/clockgen.cpp.in b/src/modm/platform/clock/sam_pmc/clockgen.cpp.in
index 38b272c5c7..de949ac750 100644
--- a/src/modm/platform/clock/sam_pmc/clockgen.cpp.in
+++ b/src/modm/platform/clock/sam_pmc/clockgen.cpp.in
@@ -70,10 +70,10 @@ ClockGen::masterClkFrequency()
return mainClkFrequency() / div;
case MasterClkSource::PLLA_CLK:
return pllAFrequency() / div;
-%% if target.family in ["g"]
+%% if target.family == "g5x"
case MasterClkSource::PLLB_CLK:
return pllBFrequency() / div;
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
case MasterClkSource::UPLL_CLK:
return 240'000'000; // UPLLDIV2
%% endif
@@ -112,10 +112,10 @@ uint32_t
ClockGen::pllAFrequency()
{
uint32_t mul = ((PMC->CKGR_PLLAR & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos) + 1;
-%% if target.family in ["g"]
+%% if target.family == "g5x"
uint32_t freq = SlowClkFreqHz * mul;
if(PMC->PMC_MCKR & PMC_MCKR_PLLADIV2) freq /= 2;
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
uint32_t freq = mainClkFrequency() * mul;
const auto diva = ((PMC->CKGR_PLLAR & CKGR_PLLAR_DIVA_Msk) >> CKGR_PLLAR_DIVA_Pos);
if (diva == 0) return 0; // PLLA is disabled
@@ -124,7 +124,7 @@ ClockGen::pllAFrequency()
return freq;
}
-%% if target.family in ["g"]
+%% if target.family == "g5x"
uint32_t
ClockGen::pllBFrequency()
{
diff --git a/src/modm/platform/clock/sam_pmc/clockgen.hpp.in b/src/modm/platform/clock/sam_pmc/clockgen.hpp.in
index 37479970c5..a8f4657a95 100644
--- a/src/modm/platform/clock/sam_pmc/clockgen.hpp.in
+++ b/src/modm/platform/clock/sam_pmc/clockgen.hpp.in
@@ -36,9 +36,9 @@ 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,
-%% if target.family in ["g"]
+%% if target.family == "g5x"
PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK_Val,
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
UPLL_CLK = PMC_MCKR_CSS_UPLL_CLK_Val,
%% endif
};
@@ -60,7 +60,7 @@ enum class
MasterClkDivider : uint8_t
{
Div1 = 0,
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
Div2 = PMC_MCKR_MDIV_PCK_DIV2_Val,
Div4 = PMC_MCKR_MDIV_PCK_DIV4_Val,
Div3 = PMC_MCKR_MDIV_PCK_DIV3_Val,
@@ -123,7 +123,7 @@ ClockPeripheral : uint32_t
Tc10 = ID_TC3_CHANNEL1,
Tc11 = ID_TC3_CHANNEL2,
#endif
-%% if target.family in ["g"]
+%% if target.family == "g5x"
Pdmic0 = ID_PDMIC0,
Pdmic1 = ID_PDMIC1,
Mem2Mem = ID_MEM2MEM,
@@ -139,7 +139,7 @@ ClockPeripheral : uint32_t
Flexcom5 = ID_FLEXCOM5,
Flexcom6 = ID_FLEXCOM6,
Flexcom7 = ID_FLEXCOM7,
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
Xdmac = ID_XDMAC,
#ifdef ID_UART0
Uart0 = ID_UART0,
@@ -199,20 +199,24 @@ ClockPeripheral : uint32_t
Ssc = ID_SSC,
Afec0 = ID_AFEC0,
Afec1 = ID_AFEC1,
+#ifdef ID_DACC
Dacc = ID_DACC,
+#endif
Pwm0 = ID_PWM0,
Pwm1 = ID_PWM1,
Icm = ID_ICM,
Acc = ID_ACC,
Usb = ID_USBHS,
+#ifdef ID_MLB
Mlb = ID_MLB,
+#endif
Aes = ID_AES,
Trng = ID_TRNG,
Isi = ID_ISI,
%% endif
};
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
enum class
UtmiRefClk : uint32_t
{
@@ -273,13 +277,13 @@ public:
static void
enablePllA(uint32_t wait_cycles = 50);
-%% if target.family in ["g"]
+%% if target.family == "g5x"
template
static void
enablePllB(uint32_t wait_cycles = 50);
%% endif
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
template
static void
enableUPll(uint32_t wait_cycles = 50);
@@ -310,7 +314,7 @@ public:
static uint32_t
pllAFrequency();
-%% if target.family in ["g"]
+%% if target.family == "g5x"
/** Returns the configured frequency of PLL B output */
static uint32_t
pllBFrequency();
diff --git a/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in b/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in
index 97f84d5cfa..e3b05ddee7 100644
--- a/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in
+++ b/src/modm/platform/clock/sam_pmc/clockgen_impl.hpp.in
@@ -49,11 +49,11 @@ ClockGen::selectMasterClk()
{
// Datasheet says when selecting PLL as source, write PRES first, otherwise
// write CSS first.
-%% if target.family in ["g"]
+%% if target.family == "g5x"
static_assert(divider == MasterClkDivider::Div1, "Divider for master clock is not supported");
constexpr bool isPll = (src == MasterClkSource::PLLA_CLK
|| src == MasterClkSource::PLLB_CLK);
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
constexpr bool isPll = (src == MasterClkSource::PLLA_CLK
|| src == MasterClkSource::UPLL_CLK);
%% endif
@@ -61,7 +61,7 @@ ClockGen::selectMasterClk()
if constexpr (isPll) {
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES((uint32_t)pres);
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | PMC_MCKR_MDIV(uint32_t(divider));
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
%% endif
@@ -72,7 +72,7 @@ ClockGen::selectMasterClk()
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)) {}
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~PMC_MCKR_MDIV_Msk) | PMC_MCKR_MDIV(uint32_t(divider));
while(!(PMC->PMC_SR & PMC_SR_MCKRDY)) {}
%% endif
@@ -83,10 +83,10 @@ template
void
ClockGen::enablePllA(uint32_t wait_cycles)
{
-%% if target.family in ["g"]
+%% if target.family == "g5x"
static_assert(9 <= multiplier && multiplier <= 7501, "Valid PLL MUL range is 9-7501");
static_assert(divider == 1, "PLL divider not supported");
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
static_assert(1 <= multiplier && multiplier <= 62, "Valid PLL MUL range is 1-62");
static_assert(1 <= divider && divider <= 255, "Valid PLL DIV range is 1-255");
%% endif
@@ -94,9 +94,9 @@ ClockGen::enablePllA(uint32_t wait_cycles)
PMC->CKGR_PLLAR =
CKGR_PLLAR_MULA(multiplier-1) |
CKGR_PLLAR_PLLACOUNT(wait_cycles) |
-%% if target.family in ["g"]
+%% if target.family == "g5x"
CKGR_PLLAR_PLLAEN(1);
-%% elif target.family in ["v"]
+%% elif target.family == "e7x/s7x/v7x"
CKGR_PLLAR_ONE |
CKGR_PLLAR_DIVA(divider);
%% endif
@@ -104,7 +104,7 @@ ClockGen::enablePllA(uint32_t wait_cycles)
while(!(PMC->PMC_SR & PMC_SR_LOCKA)) {}
}
-%% if target.family in ["g"]
+%% if target.family == "g5x"
template
void
ClockGen::enablePllB(uint32_t wait_cycles)
@@ -119,7 +119,7 @@ ClockGen::enablePllB(uint32_t wait_cycles)
}
%% endif
-%% if target.family in ["v"]
+%% if target.family == "e7x/s7x/v7x"
template
void
ClockGen::enableUPll(uint32_t wait_cycles)
diff --git a/src/modm/platform/clock/sam_pmc/module.lb b/src/modm/platform/clock/sam_pmc/module.lb
index 5ea13eabac..1ebc680e78 100644
--- a/src/modm/platform/clock/sam_pmc/module.lb
+++ b/src/modm/platform/clock/sam_pmc/module.lb
@@ -15,8 +15,7 @@ def init(module):
module.description = "Clock Generator (CKGR)"
def prepare(module, options):
- if not (options[":target"].has_driver("pmc:samg*")
- or options[":target"].has_driver("pmc:samv*")):
+ if not options[":target"].has_driver("pmc:sam*"):
return False
module.depends(":cmsis:device", ":platform:clock")
diff --git a/src/modm/platform/clock/systick/module.lb b/src/modm/platform/clock/systick/module.lb
index cb0681e145..e1b7d5b908 100644
--- a/src/modm/platform/clock/systick/module.lb
+++ b/src/modm/platform/clock/systick/module.lb
@@ -40,8 +40,13 @@ def build(env):
# SysTick clock prescaler is dynamically chosen as /1 or /8
div = 8
- # SAMD: Prescaler not implemented
- if target.platform == "sam" and target.family in ["d"]:
+ # SAMD5x/E5x: Prescaler not implemented
+ if target.platform == "sam" and target.family == "d5x/e5x":
+ div = 1
+ # systick clock is too fast to run at 4 Hz, increase frequency
+ freq = 8
+ # SAMD2x: Prescaler not implemented
+ elif target.platform == "sam" and target.family == "d1x/d2x/dax":
div = 1
# H742/43: Prescaler not implemented in revY
elif target.family == "h7" and target.name in ["42", "43", "50", "53"] and target.revision == "y":
diff --git a/src/modm/platform/core/sam/module.lb b/src/modm/platform/core/sam/module.lb
index ac910d949a..98257adf99 100644
--- a/src/modm/platform/core/sam/module.lb
+++ b/src/modm/platform/core/sam/module.lb
@@ -35,9 +35,10 @@ def build(env):
# delay code that must be tuned for each family
# (cycles per loop, setup cost in loops, max cpu frequency)
tuning = {
- "d": (3, 4, 48), # CM0 tested on D21 in RAM
- "g": (6, 4, 120), # G55
- "v": (4, 4, 300), # V70
+ "d1x/d2x/dax": (3, 4, 48), # CM0 tested on D21 in RAM
+ "d5x/e5x": (4, 4, 120), # CM4 tested on E54 in RAM
+ "g5x": (6, 4, 120), # G55
+ "e7x/s7x/v7x": (4, 4, 300), # CM7 tested on V71
}[target.family]
# us_shift is an optimization to limit error via fractional math
diff --git a/src/modm/platform/core/sam/startup_platform.c.in b/src/modm/platform/core/sam/startup_platform.c.in
index 5c99d8d829..5c6c5fd1d3 100644
--- a/src/modm/platform/core/sam/startup_platform.c.in
+++ b/src/modm/platform/core/sam/startup_platform.c.in
@@ -24,8 +24,7 @@
void
__modm_initialize_platform(void)
{
-%% if target.family == "d"
-%% if target.series == "21"
+%% if target.series == "d21"
// Overwriting the default value of the NVMCTRL.CTRLB.MANW bit (errata reference 13134)
NVMCTRL->CTRLB.bit.MANW = 1;
@@ -41,15 +40,5 @@ __modm_initialize_platform(void)
DMAC->QOSCTRL.bit.DQOS = 2;
DMAC->QOSCTRL.bit.FQOS = 2;
DMAC->QOSCTRL.bit.WRBQOS = 2;
-
-%% elif target.series == "21"
-%% endif
-
-%% elif target.family == "l"
-%% if target.series == "21"
%% endif
-
-%% elif target.family == "g"
-%% endif
-
}
diff --git a/src/modm/platform/extint/sam/extint.hpp b/src/modm/platform/extint/sam/extint.hpp
index 9f6a365298..01977727de 100644
--- a/src/modm/platform/extint/sam/extint.hpp
+++ b/src/modm/platform/extint/sam/extint.hpp
@@ -52,10 +52,10 @@ class ExternalInterrupt
* @param clockGen
* The clock generator to use for the peripheral. If any interrupts are to
* be used to akeup the CPU from standby mode, make sure this clock is
- * actually running in standby. Defaults to external 32.768kHz crystal osc.
+ * actually running in standby.
*/
static void
- initialize(ClockGenerator clockGen = ClockGenerator::ExternalCrystal32K,
+ initialize(ClockGenerator clockGen,
int priority = (1ul << __NVIC_PRIO_BITS) - 1ul);
protected:
diff --git a/src/modm/platform/extint/sam/module.lb b/src/modm/platform/extint/sam/module.lb
index ab3272c3de..8433841392 100644
--- a/src/modm/platform/extint/sam/module.lb
+++ b/src/modm/platform/extint/sam/module.lb
@@ -15,7 +15,7 @@ def init(module):
module.description = "External Interrupt"
def prepare(module, options):
- if not options[":target"].partname.startswith("samd"):
+ if not options[":target"].partname.startswith("samd2"):
return False
module.depends(":cmsis:device", ":platform:gclk", ":platform:gpio")
diff --git a/src/modm/platform/gpio/sam/config.hpp.in b/src/modm/platform/gpio/sam/config.hpp.in
index d8c878f2a1..7e14305cd5 100644
--- a/src/modm/platform/gpio/sam/config.hpp.in
+++ b/src/modm/platform/gpio/sam/config.hpp.in
@@ -37,7 +37,7 @@ struct Peripherals
struct {{ name }}
{
%% for signal, index_list in peripheral["signals"].items()
-%% if not index_list or name == "Adc"
+%% if not index_list or (name in ["Adc", "Afec"] and not target["family"] == "d5x/e5x")
struct {{ signal }} {};
%% else
template
@@ -62,7 +62,7 @@ enum class PortName
%% if "instances" in peripheral
%% for instance in peripheral["instances"]
%% for signal, index_list in peripheral["signals"].items()
-%% if index_list
+%% if index_list and name != "Afec"
%% for index in index_list
template<> template<>
struct Peripherals::{{ name }}<{{ instance }}>::{{ signal }}<{{ index }}> {};
@@ -73,7 +73,7 @@ struct Peripherals::{{ name }}<{{ instance }}>::{{ signal }} {};
%% endif
%% endfor
%% endfor
-%% elif name != "Adc"
+%% elif name not in ["Adc", "Afec"] or target["family"] == "d5x/e5x"
%% for signal, index_list in peripheral["signals"].items()
%% for index in index_list
template<>
@@ -103,12 +103,12 @@ struct {{ gpio["port"] ~ gpio["pin"] }}
%% else
using peripheral = Peripherals::{{ signal["peripheral"] }};
%% endif
- %% if "index" in signal and signal["peripheral"] != "Adc"
+ %% if "index" in signal and (signal["peripheral"] not in ["Adc", "Afec"] or target["family"] == "d5x/e5x")
using signal = peripheral::{{ signal["name"] }}<{{ signal["index"] }}>;
%% else
using signal = peripheral::{{ signal["name"] }};
%% endif
- %% if signal["peripheral"] == "Adc" and "index" in signal
+ %% if signal["peripheral"] in ["Adc", "Afec"] and "index" in signal
static constexpr int32_t AdcChannel = {{ signal["index"] }};
%% else
static constexpr int32_t AdcChannel = -1;
diff --git a/src/modm/platform/gpio/sam/enable.cpp.in b/src/modm/platform/gpio/sam/enable.cpp.in
index efa607f865..f73a33a91d 100644
--- a/src/modm/platform/gpio/sam/enable.cpp.in
+++ b/src/modm/platform/gpio/sam/enable.cpp.in
@@ -16,8 +16,7 @@ void
modm_gpio_enable(void)
{
-%% if target["family"] in ["g", "v"]
-
+%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
PMC->PMC_PCER0 =
%% for port in options.enable_ports
(1<) | ...);
};
-%% if target["family"] in ["g", "v"]
+%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
template
struct PinMuxMixin
@@ -379,7 +379,6 @@ public:
setInput(InputType type)
{
configure(type);
- setInput();
}
static void
@@ -473,7 +472,7 @@ public:
inline static bool
read()
{
-%% if target["family"] in ["g", "v"]
+%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
return Base::template readPortReg(PIO_PDSR_OFFSET);
%% else
return Base::template readPortReg(PORT_IN_OFFSET);
@@ -483,7 +482,7 @@ public:
inline static bool
isSet()
{
-%% if target["family"] in ["g", "v"]
+%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
return Base::template readPortReg(PIO_ODSR_OFFSET);
%% else
return Base::template readPortReg(PORT_OUT_OFFSET);
@@ -547,7 +546,7 @@ struct Gpio::As : public Gpio
connect()
{
-%% if target["family"] in ["g", "v"]
+%% if target["family"] in ["g5x", "e7x/s7x/v7x"]
// X1 denotes an "extra function", such as an ADC pin, which is not
// enabled by the PIO ABCD register.
static_assert(PinSignal::function != PinFunction::X1,
diff --git a/src/modm/platform/uart/cortex/itm.cpp.in b/src/modm/platform/uart/cortex/itm.cpp.in
index a089112a9f..1ca1f0f89f 100644
--- a/src/modm/platform/uart/cortex/itm.cpp.in
+++ b/src/modm/platform/uart/cortex/itm.cpp.in
@@ -134,6 +134,12 @@ Itm::discardTransmitBuffer()
%% endif
}
+%% if target.platform == "sam"
+// Some SAM headers define a PORT macro
+#pragma push_macro("PORT")
+#undef PORT
+%% endif
+
bool
Itm::write_itm(uint32_t data, uint8_t size)
{
@@ -163,6 +169,10 @@ Itm::write_itm(uint32_t data, uint8_t size)
return true;
}
+%% if target.platform == "sam"
+#pragma pop_macro("PORT")
+%% endif
+
void
Itm::update()
{
diff --git a/src/modm/platform/uart/sam/module.lb b/src/modm/platform/uart/sam-sercom/module.lb
similarity index 100%
rename from src/modm/platform/uart/sam/module.lb
rename to src/modm/platform/uart/sam-sercom/module.lb
diff --git a/src/modm/platform/uart/sam-sercom/uart.cpp.in b/src/modm/platform/uart/sam-sercom/uart.cpp.in
new file mode 100644
index 0000000000..1c527e4535
--- /dev/null
+++ b/src/modm/platform/uart/sam-sercom/uart.cpp.in
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020, Erik Henriksson
+ *
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+%% set name = "Uart" ~ id
+%% set hal = "UartHal" ~ id
+
+#include "../device.hpp"
+#include "uart_hal_{{ id }}.hpp"
+#include "uart_{{ id }}.hpp"
+
+namespace modm::platform
+{
+
+void
+{{ name }}::writeBlocking(uint8_t data)
+{
+ while(!{{ hal }}::isTransmitRegisterEmpty());
+ {{ hal }}::write(data);
+}
+
+void
+{{ name }}::writeBlocking(const uint8_t *data, std::size_t length)
+{
+ while (length-- != 0) {
+ writeBlocking(*data++);
+ }
+}
+
+void
+{{ name }}::flushWriteBuffer()
+{
+ return;
+}
+
+bool
+{{ name }}::write(uint8_t data)
+{
+ if({{ hal }}::isTransmitRegisterEmpty()) {
+ {{ hal }}::write(data);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+std::size_t
+{{ name }}::write(const uint8_t *data, std::size_t length)
+{
+ uint32_t i = 0;
+ for (; i < length; ++i)
+ {
+ if (!write(*data++)) {
+ return i;
+ }
+ }
+ return i;
+}
+
+bool
+{{ name }}::isWriteFinished()
+{
+ return {{ hal }}::isTransmitRegisterEmpty();
+}
+
+std::size_t
+{{ name }}::discardTransmitBuffer()
+{
+ return 0;
+}
+
+bool
+{{ name }}::read(uint8_t &data)
+{
+ if({{ hal }}::isReceiveRegisterNotEmpty()) {
+ {{ hal }}::read(data);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+std::size_t
+{{ name }}::read(uint8_t *data, std::size_t length)
+{
+ (void)length; // avoid compiler warning
+ if(read(*data)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+std::size_t
+{{ name }}::discardReceiveBuffer()
+{
+ return 0;
+}
+
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam-sercom/uart.hpp.in b/src/modm/platform/uart/sam-sercom/uart.hpp.in
new file mode 100644
index 0000000000..d0170e5b52
--- /dev/null
+++ b/src/modm/platform/uart/sam-sercom/uart.hpp.in
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2020, Erik Henriksson
+ *
+ * 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/.
+ */
+// ----------------------------------------------------------------------------
+
+%% set hal = "UartHal" ~ id
+%% set name = "Uart" ~ id
+
+#pragma once
+
+#include
+#include "uart_base.hpp"
+#include "uart_hal_{{ id }}.hpp"
+#include
+
+namespace modm::platform
+{
+
+/**
+ * Universal asynchronous receiver transmitter ({{ "Uart" | upper ~ id }})
+ *
+ * @author Erik Henriksson
+ * @ingroup modm_platform_uart modm_platform_uart_{{id}}
+ */
+class {{ name }} : public UartBase, public ::modm::Uart
+{
+
+ // Separate because that makes GCC print the template args.
+ template
+ struct ValidateNotSame {
+ static_assert(
+ !std::is_same_v,
+ "Rx and Tx cannot use the same signal!");
+ };
+
+ template
+ static void
+ configurePadMapping()
+ {
+ constexpr auto rxIndex = detail::get_pad_index_v;
+ constexpr auto txIndex = detail::get_pad_index_v;
+
+ PeripheralClock>::enable();
+
+ if constexpr (rxIndex == 0) {
+ {{ hal }}::setRxPinout(RxPinout::RxPad0);
+ } else if constexpr (rxIndex == 1) {
+ {{ hal }}::setRxPinout(RxPinout::RxPad1);
+ } else if constexpr (rxIndex == 2) {
+ {{ hal }}::setRxPinout(RxPinout::RxPad2);
+ } else if constexpr (rxIndex == 3) {
+ {{ hal }}::setRxPinout(RxPinout::RxPad3);
+ } else {
+ // assert is always false
+ static_assert(!std::is_same_v, "Invalid rx signal");
+ }
+
+ if constexpr (txIndex == 0) {
+ {{ hal }}::setTxPinout(TxPinout::TxPad0_XckPad1);
+%% if target.family != "d5x/e5x"
+ } else if constexpr (txIndex == 2) {
+ {{ hal }}::setTxPinout(TxPinout::TxPad2_XckPad3);
+%% endif
+ } else {
+ // assert is always false
+ static_assert(!std::is_same_v, "Invalid tx signal");
+ }
+ }
+
+public:
+ 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 Sercom = Peripherals::Sercom<{{ id | int }}>;
+ using RxConnector = typename RxPin::template Connector, Sercom::Pad<1>, Sercom::Pad<2>, Sercom::Pad<3>>;
+ using TxConnector = typename TxPin::template Connector, Sercom::Pad<2>>;
+ ValidateNotSame {};
+
+ configurePadMapping();
+ RxConnector::connect();
+ TxConnector::connect();
+ }
+
+ template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) >
+ static void
+ initialize(Parity parity = Parity::Disabled)
+ {
+ {{ hal }}::initialize(parity);
+ {{ hal }}::setTransmitterEnable(true);
+ {{ hal }}::setReceiverEnable(true);
+ }
+
+ static void
+ writeBlocking(uint8_t data);
+
+ static void
+ writeBlocking(const uint8_t *data, std::size_t length);
+
+ static void
+ flushWriteBuffer();
+
+ static bool
+ write(uint8_t data);
+
+ static std::size_t
+ write(const uint8_t *data, std::size_t length);
+
+ static bool
+ isWriteFinished();
+
+ static std::size_t
+ discardTransmitBuffer();
+
+ static bool
+ read(uint8_t &data);
+
+ static std::size_t
+ read(uint8_t *buffer, std::size_t length);
+
+ static std::size_t
+ discardReceiveBuffer();
+};
+
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam-sercom/uart_base.hpp.in b/src/modm/platform/uart/sam-sercom/uart_base.hpp.in
new file mode 100644
index 0000000000..03aaf96da3
--- /dev/null
+++ b/src/modm/platform/uart/sam-sercom/uart_base.hpp.in
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, Erik Henriksson
+ *
+ * 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
+#include
+#include
+
+/// @cond
+namespace modm::platform::detail
+{
+ template
+ struct get_pad_index;
+
+ template typename T, int index>
+ struct get_pad_index>
+ {
+ static constexpr int value{index};
+ };
+
+ template
+ static constexpr auto get_pad_index_v = get_pad_index::value;
+}
+/// @endcond
+
+namespace modm::platform
+{
+/**
+ * Base class for the UART classes
+ *
+ * Provides some common enum that do not depend on the specific UART.
+ *
+ * @ingroup modm_platform_uart
+ */
+class UartBase
+{
+public:
+ enum class Parity : uint32_t
+ {
+ Even = 0,
+ Odd = 1,
+ Disabled = 2,
+ };
+
+ enum class RxPinout : uint8_t
+ {
+ RxPad0 = 0,
+ RxPad1 = 1,
+ RxPad2 = 2,
+ RxPad3 = 3
+ };
+
+ enum class TxPinout : uint8_t
+ {
+ TxPad0_XckPad1 = 0,
+%% if target.family != "d5x/e5x"
+ TxPad2_XckPad3 = 1,
+%% endif
+ TxPad0_RtsTePad2_CtsPad3 = 2,
+%% if target.family in ["d5x/e5x", "l1x", "l2x"]
+ TxPad0_XckPad1_RtsTePad2 = 3
+%% endif
+ };
+};
+
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam/uart_hal.hpp.in b/src/modm/platform/uart/sam-sercom/uart_hal.hpp.in
similarity index 92%
rename from src/modm/platform/uart/sam/uart_hal.hpp.in
rename to src/modm/platform/uart/sam-sercom/uart_hal.hpp.in
index dce1143e6b..833a4f6430 100644
--- a/src/modm/platform/uart/sam/uart_hal.hpp.in
+++ b/src/modm/platform/uart/sam-sercom/uart_hal.hpp.in
@@ -108,6 +108,14 @@ public:
/// Returns true if data can be written
static inline bool
isTransmitRegisterEmpty();
+
+ /// Configure mapping between rx pin functions and IO pads
+ static inline void
+ setRxPinout(RxPinout rxPinout);
+
+ /// Configure mapping between tx pin functions and IO pads
+ static inline void
+ setTxPinout(TxPinout txPinout);
};
} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in b/src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in
similarity index 86%
rename from src/modm/platform/uart/sam/uart_hal_impl.hpp.in
rename to src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in
index 8cfbddcb59..8947a40bee 100644
--- a/src/modm/platform/uart/sam/uart_hal_impl.hpp.in
+++ b/src/modm/platform/uart/sam-sercom/uart_hal_impl.hpp.in
@@ -55,18 +55,13 @@ void
{{ name }}::initialize(Parity parity)
{
// Enable peripheral clock in power manager.
- PM->APBCMASK.bit.{{ peripheral }}_ = true;
- GenericClockController::connect(ClockGenerator::System);
- while (GCLK->STATUS.bit.SYNCBUSY);
+ PeripheralClock>::enable();
// Reset USART
reset();
// Set clock mode internal
{{ peripheral }}->USART.CTRLA.bit.MODE = 0x1;
// Set asynchronous mode
{{ peripheral }}->USART.CTRLA.bit.CMODE = 0x0;
- // Set rx/tx pins
- {{ peripheral }}->USART.CTRLA.bit.RXPO = 0x3; // Pad3
- {{ peripheral }}->USART.CTRLA.bit.TXPO = 0x1; // Pad2;
// Set character size to 8 bits
{{ peripheral }}->USART.CTRLB.bit.CHSIZE = 0x0;
// Set Data order to LSB first
@@ -77,11 +72,11 @@ void
{{ peripheral }}->USART.CTRLB.bit.SBMODE = 0x0;
// Oversampling 8x or 16x
- constexpr uint32_t scalar = (baudrate * 16l > SystemClock::Frequency) ? 8 : 16;
+ constexpr uint32_t scalar = (baudrate * 16l > SystemClock::{{ sercom | capitalize }}) ? 8 : 16;
{{ peripheral }}->USART.CTRLA.bit.SAMPR = (scalar == 16) ? 0x1 : 0x3;
// Prescaler 13 bit integer, 3 bit fractional
constexpr auto result = Prescaler::from_range(
- SystemClock::Frequency/(scalar/8), baudrate, 1, (1ul << 16) - 1ul);
+ SystemClock::{{ sercom | capitalize }}/(scalar/8), baudrate, 1, (1ul << 16) - 1ul);
assertBaudrateInTolerance< result.frequency, baudrate, tolerance >();
{{ peripheral }}->USART.BAUD.FRAC.BAUD = result.prescaler >> 3;
{{ peripheral }}->USART.BAUD.FRAC.FP = result.prescaler & 0b111;
@@ -141,4 +136,16 @@ bool
return {{ peripheral }}->USART.INTFLAG.bit.DRE;
}
+void
+{{ name }}::setRxPinout(RxPinout rxPinout)
+{
+ {{ peripheral }}->USART.CTRLA.bit.RXPO = static_cast(rxPinout);
+}
+
+void
+{{ name }}::setTxPinout(TxPinout txPinout)
+{
+ {{ peripheral }}->USART.CTRLA.bit.TXPO = static_cast(txPinout);
+}
+
} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam/uart.cpp.in b/src/modm/platform/uart/sam/uart.cpp.in
index 1c527e4535..24c45a1f07 100644
--- a/src/modm/platform/uart/sam/uart.cpp.in
+++ b/src/modm/platform/uart/sam/uart.cpp.in
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2021, Jeff McBride
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -9,55 +10,92 @@
*/
// ----------------------------------------------------------------------------
-%% set name = "Uart" ~ id
-%% set hal = "UartHal" ~ id
+#include "{{ type }}_{{ id }}.hpp"
-#include "../device.hpp"
-#include "uart_hal_{{ id }}.hpp"
-#include "uart_{{ id }}.hpp"
+#include
-namespace modm::platform
-{
+%% set name="{}{}".format(type.capitalize(), id)
+%% set reg=name.upper()
+%% if type == "usart" and target.family == "e7x/s7x/v7x" and target.variant == "b"
+%% set reg_suffix="_USART"
+%% else
+%% set reg_suffix=""
+%% endif
-void
-{{ name }}::writeBlocking(uint8_t data)
+namespace
{
- while(!{{ hal }}::isTransmitRegisterEmpty());
- {{ hal }}::write(data);
+ static modm::atomic::Queue rxBuffer;
+ static modm::atomic::Queue txBuffer;
}
-void
-{{ name }}::writeBlocking(const uint8_t *data, std::size_t length)
+MODM_ISR({{ peripheral | upper }}{{ id }})
{
- while (length-- != 0) {
- writeBlocking(*data++);
+ using namespace modm::platform;
+ if({{ name }}::isReceiveReady()) {
+ uint8_t data = (uint8_t){{ name }}::Regs()->{{ prefix }}_RHR;
+ {{ name }}::read(data);
+ rxBuffer.push(data);
+ }
+
+ if({{ name }}::isTransmitReady()) {
+ if(txBuffer.isEmpty()) {
+ {{ name }}::Regs()->{{ prefix }}_IDR = {{ prefix }}_IDR_TXRDY_Msk;
+ } else {
+ {{ name }}::Regs()->{{ prefix }}_THR = txBuffer.get();
+ txBuffer.pop();
+ }
}
}
-void
-{{ name }}::flushWriteBuffer()
+namespace modm::platform
{
- return;
+
+bool
+{{ name }}::read(uint8_t &dataOut) {
+ if(rxBuffer.isEmpty()) {
+ return false;
+ } else {
+ dataOut = rxBuffer.get();
+ rxBuffer.pop();
+ return true;
+ }
+}
+
+std::size_t
+{{ name }}::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
{{ name }}::write(uint8_t data)
{
- if({{ hal }}::isTransmitRegisterEmpty()) {
- {{ hal }}::write(data);
- return true;
+ if(txBuffer.isEmpty() && isTransmitReady()) {
+ Regs()->{{ prefix }}_THR = data;
} else {
- return false;
+ if(!txBuffer.push(data)) {
+ return false;
+ }
+ // Enable tx interrupt
+ Regs()->{{ prefix }}_IER = {{ prefix }}_IER_TXRDY_Msk;
}
+ return true;
}
std::size_t
{{ name }}::write(const uint8_t *data, std::size_t length)
{
uint32_t i = 0;
- for (; i < length; ++i)
- {
- if (!write(*data++)) {
+ for(; i < length; i++) {
+ if(!write(data[i])) {
return i;
}
}
@@ -67,41 +105,34 @@ std::size_t
bool
{{ name }}::isWriteFinished()
{
- return {{ hal }}::isTransmitRegisterEmpty();
+ return txBuffer.isEmpty() && isTransmitReady();
}
-std::size_t
-{{ name }}::discardTransmitBuffer()
+void
+{{ name }}::flushWriteBuffer()
{
- return 0;
+ while(!isWriteFinished());
}
-bool
-{{ name }}::read(uint8_t &data)
+void
+{{ name }}::setParity(Parity parity)
{
- if({{ hal }}::isReceiveRegisterNotEmpty()) {
- {{ hal }}::read(data);
- return true;
- } else {
- return false;
- }
+ Regs()->{{ prefix }}_MR = (Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR{{reg_suffix}}_PAR_Msk) | (uint32_t)parity;
}
-std::size_t
-{{ name }}::read(uint8_t *data, std::size_t length)
+%% if type == "usart"
+void
+{{ name }}::setWordLength(WordLength length)
{
- (void)length; // avoid compiler warning
- if(read(*data)) {
- return 1;
+ if(length == WordLength::Bit9) {
+ Regs()->{{ prefix }}_MR |= {{ prefix }}_MR{{reg_suffix}}_MODE9_Msk;
} else {
- return 0;
+ Regs()->{{ prefix }}_MR &= ~{{ prefix }}_MR{{reg_suffix}}_MODE9_Msk;
+ Regs()->{{ prefix }}_MR =
+ (Regs()->{{ prefix }}_MR & ~{{ prefix }}_MR_CHRL_Msk)
+ | {{ prefix }}_MR_CHRL((uint32_t)length);
}
}
+%% endif
-std::size_t
-{{ name }}::discardReceiveBuffer()
-{
- return 0;
-}
-
-} // namespace modm::platform
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam/uart.hpp.in b/src/modm/platform/uart/sam/uart.hpp.in
index 56e4a3f424..0d157f1014 100644
--- a/src/modm/platform/uart/sam/uart.hpp.in
+++ b/src/modm/platform/uart/sam/uart.hpp.in
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2021, Jeff McBride
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -9,37 +10,50 @@
*/
// ----------------------------------------------------------------------------
-%% set hal = "UartHal" ~ id
-%% set name = "Uart" ~ id
-
#pragma once
+#include "{{ type }}_base.hpp"
#include
-#include "uart_base.hpp"
-#include "uart_hal_{{ id }}.hpp"
-#include
+#include
+#include
+#include
+
+%% set name="{}{}".format(type.capitalize(), id)
+%% set reg=name.upper()
+%% if type == "usart" and target.family == "e7x/s7x/v7x" and target.variant == "b"
+%% set reg_suffix="_USART"
+%% else
+%% set reg_suffix=""
+%% endif
namespace modm::platform
{
/**
- * Universal asynchronous receiver transmitter ({{ "Uart" | upper ~ id }})
+%% if type == "usart"
+ * Universal synchronous asynchronous receiver transmitter (USART{{ id }})
+%% else
+ * Universal asynchronous receiver transmitter (UART{{ id }})
+%% endif
*
- * @author Erik Henriksson
- * @ingroup modm_platform_uart modm_platform_uart_{{id}}
+ * @author Jeff McBride
+ * @author Christopher Durand
+ * @ingroup modm_platform_{{type}} modm_platform_{{type}}_{{id}}
*/
-class {{ name }} : public UartBase, public ::modm::Uart
+class {{ name }} : public {{ type | capitalize }}Base, public modm::Uart
{
-
- // Separate because that makes GCC print the template args.
- template
- struct ValidateNotSame {
- static_assert(
- !std::is_same_v,
- "Rx and Tx cannot use the same signal!");
- };
-
+private:
+ // prevent name collision between ::Uart* from SAM header with modm::Uart
+%% if type == "uart"
+ static auto* Regs() { return reinterpret_cast<::Uart*>{{ reg }}; }
+%% else
+ static auto* Regs() { return {{ reg }}; }
+%% endif
+ friend void ::{{ peripheral | upper }}{{ id }}_IRQHandler();
public:
+ static constexpr size_t RxBufferSize = {{ options["buffer.rx"] }};
+ static constexpr size_t TxBufferSize = {{ options["buffer.tx"] }};
+
template< class... Pins >
static void
connect()
@@ -49,52 +63,89 @@ public:
static_assert(
!std::is_same_v,
"Rx and Tx cannot use the same pin!");
- using Sercom = Peripherals::Sercom<{{ id | int }}>;
- using RxConnector = typename RxPin::template Connector, Sercom::Pad<1>, Sercom::Pad<2>, Sercom::Pad<3>>;
- using TxConnector = typename TxPin::template Connector, Sercom::Pad<2>>;
- ValidateNotSame {};
+ using Peripheral = Peripherals::{{ peripheral }}<{{ id | int }}>;
+ %% if peripheral == "Flexcom"
+ using RxConnector = typename RxPin::template Connector;
+ using TxConnector = typename TxPin::template Connector;
+ %% elif type == "usart"
+ using RxConnector = typename RxPin::template Connector>;
+ using TxConnector = typename TxPin::template Connector>;
+ %% elif type == "uart"
+ using RxConnector = typename RxPin::template Connector>;
+ using TxConnector = typename TxPin::template Connector>;
+ %% endif
RxConnector::connect();
TxConnector::connect();
}
+
template< class SystemClock, baudrate_t baudrate, percent_t tolerance=pct(1) >
- static void
- initialize(Parity parity = Parity::Disabled)
+ static inline void
+ initialize(
+ Parity parity=Parity::Disabled,
+%% if type == "usart"
+ WordLength length=WordLength::Bit8,
+%% endif
+ uint8_t irq_priority = 5)
{
- {{ hal }}::initialize(parity);
- {{ hal }}::setTransmitterEnable(true);
- {{ hal }}::setReceiverEnable(true);
+ ClockGen::enable();
+
+%% if peripheral == "Flexcom"
+ FLEXCOM{{ id }}->FLEXCOM_MR = FLEXCOM_MR_OPMODE_USART;
+%% endif
+ constexpr auto result = Prescaler::from_function(
+ SystemClock::{{type | capitalize}}{{id}},
+ baudrate,
+ 1,
+ 65535,
+%% if type == "usart"
+ [](uint32_t x) { return x * 8; }
+%% else
+ [](uint32_t x) { return x * 16; }
+%% endif
+ );
+
+ Regs()->{{ prefix }}_BRGR = result.index;
+
+%% if type == "usart"
+ // Use 8x oversampling (this affects baud rate generation)
+ Regs()->{{ prefix }}_MR = {{ prefix }}_MR{{reg_suffix}}_OVER;
+%% endif
+
+ setParity(parity);
+%% if type == "usart"
+ setWordLength(length);
+%% endif
+ Regs()->{{ prefix }}_CR = {{ prefix }}_CR_RXEN_Msk | {{ prefix }}_CR_TXEN_Msk;
+ // Enable rx interrupt
+ Regs()->{{ prefix }}_IER = {{ prefix }}_IER_RXRDY_Msk;
+
+ // Enable the IRQ
+ NVIC_SetPriority({{ peripheral | upper }}{{ id }}_IRQn, irq_priority);
+ NVIC_EnableIRQ({{ peripheral | upper }}{{ id }}_IRQn);
}
- static void
- writeBlocking(uint8_t data);
+ static bool read(uint8_t &dataOut);
- static void
- writeBlocking(const uint8_t *data, std::size_t length);
+ static std::size_t read(uint8_t *data, std::size_t length);
- static void
- flushWriteBuffer();
+ static bool write(uint8_t data);
- static bool
- write(uint8_t data);
+ static std::size_t write(const uint8_t *data, std::size_t length);
- static std::size_t
- write(const uint8_t *data, std::size_t length);
+ static bool isWriteFinished();
- static bool
- isWriteFinished();
+ static void flushWriteBuffer();
- static std::size_t
- discardTransmitBuffer();
+ static void setParity(Parity parity);
- static bool
- read(uint8_t &data);
+%% if type == "usart"
+ static void setWordLength(WordLength length);
+%% endif
- static std::size_t
- read(uint8_t *buffer, std::size_t length);
+ static inline bool isTransmitReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_TXRDY_Msk; }
- static std::size_t
- discardReceiveBuffer();
+ static inline bool isReceiveReady() { return Regs()->{{ prefix }}_{{ sr }} & {{ prefix }}_{{ sr }}_RXRDY_Msk; }
};
-} // namespace modm::platform
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/samg/module.lb b/src/modm/platform/uart/sam/uart/module.lb
similarity index 69%
rename from src/modm/platform/uart/samg/module.lb
rename to src/modm/platform/uart/sam/uart/module.lb
index 21747d916c..6443f5f231 100644
--- a/src/modm/platform/uart/samg/module.lb
+++ b/src/modm/platform/uart/sam/uart/module.lb
@@ -10,7 +10,17 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------
-props = {}
+def _get_properties(env, instance=None):
+ device = env[":target"]
+ return {
+ "type" : "uart",
+ "peripheral" : "Uart",
+ "id" : instance,
+ "prefix" : "UART",
+ "sr" : "SR",
+ "target" : device.identifier
+ }
+
class Instance(Module):
def __init__(self, driver, instance):
@@ -40,24 +50,21 @@ class Instance(Module):
return True
def build(self, env):
- device = env[":target"].identifier
- global props
- props["id"] = self.instance
-
- env.substitutions = props
+ env.substitutions = _get_properties(env, self.instance)
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))
+ 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)"
+ module.description = "Universal Asynchronous Receiver Transmitter (UART)"
+
def prepare(module, options):
device = options[":target"]
- if not (device.has_driver("usart:samg*")):
+ if (not device.has_driver("uart:sam*")) or device.has_driver("sercom:sam"):
return False
module.depends(
@@ -67,17 +74,15 @@ def prepare(module, options):
":platform:gpio",
":platform:clockgen")
- global props
- drivers = options[":target"].get_all_drivers("usart")
+ drivers = options[":target"].get_all_drivers("uart")
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.substitutions = _get_properties(env)
env.outbasepath = "modm/src/modm/platform/uart"
- env.copy("uart_base.hpp")
+ env.template("../uart_base.hpp.in", "uart_base.hpp")
diff --git a/src/modm/platform/uart/sam/uart_base.hpp.in b/src/modm/platform/uart/sam/uart_base.hpp.in
index 3d0e0eb723..ad5f4123e9 100644
--- a/src/modm/platform/uart/sam/uart_base.hpp.in
+++ b/src/modm/platform/uart/sam/uart_base.hpp.in
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2020, Erik Henriksson
+ * Copyright (c) 2021, Jeff McBride
+ * Copyright (c) 2022, Christopher Durand
*
* This file is part of the modm project.
*
@@ -11,31 +12,52 @@
#pragma once
-#include
#include "../device.hpp"
-#include
-#include
-#include
-
namespace modm::platform
{
-/**
- * Base class for the UART classes
- *
- * Provides some common enum that do not depend on the specific UART.
- *
- * @ingroup modm_platform_uart
- */
-class UartBase
+
+/// @ingroup modm_platform_uart
+class {{type | capitalize}}Base
{
public:
enum class Parity : uint32_t
{
- Even = 0,
- Odd = 1,
- Disabled = 2,
+%% if type == "usart"
+%% if target.family == "e7x/s7x/v7x" and target.variant == "b"
+ Disabled = US_MR_USART_PAR_NO_Val,
+ Even = US_MR_USART_PAR_EVEN_Val,
+ Odd = US_MR_USART_PAR_ODD_Val,
+ Space = US_MR_USART_PAR_SPACE_Val,
+ Mark = US_MR_USART_PAR_MARK_Val,
+ MultiDrop = US_MR_USART_PAR_MULTIDROP_Val
+%% else
+ 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
+%% endif
+%% else
+ Disabled = UART_MR_PAR_NO,
+ Even = UART_MR_PAR_EVEN,
+ Odd = UART_MR_PAR_ODD,
+ Space = UART_MR_PAR_SPACE,
+ Mark = UART_MR_PAR_MARK
+%% endif
+ };
+
+%% if type == "usart"
+ enum class WordLength : uint32_t
+ {
+ Bit5 = 0,
+ Bit6,
+ Bit7,
+ Bit8,
+ Bit9
};
+%% endif
};
-} // namespace modm::platform
+} // namespace modm::platform
diff --git a/src/modm/platform/uart/sam/usart/module.lb b/src/modm/platform/uart/sam/usart/module.lb
new file mode 100644
index 0000000000..d9768d5861
--- /dev/null
+++ b/src/modm/platform/uart/sam/usart/module.lb
@@ -0,0 +1,89 @@
+#!/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 _get_properties(env, instance=None):
+ device = env[":target"]
+ peripheral = "Flexcom" if device.has_driver("flexcom") else "Usart"
+ return {
+ "type" : "usart",
+ "peripheral" : peripheral,
+ "id" : instance,
+ "prefix" : "US",
+ "sr" : "CSR",
+ "target" : device.identifier
+ }
+
+
+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:usart")
+
+ 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):
+ env.substitutions = _get_properties(env, self.instance)
+ env.outbasepath = "modm/src/modm/platform/usart"
+
+ env.template("../uart.hpp.in", "usart_{}.hpp".format(self.instance))
+ env.template("../uart.cpp.in", "usart_{}.cpp".format(self.instance))
+
+
+def init(module):
+ module.name = ":platform:usart"
+ module.description = "Universal Synchronous Asynchronous Receiver Transmitter (USART)"
+
+
+def prepare(module, options):
+ device = options[":target"]
+ if (not device.has_driver("usart:sam*")) or device.has_driver("sercom:sam"):
+ return False
+
+ module.depends(
+ ":architecture:uart",
+ ":math:algorithm",
+ ":cmsis:device",
+ ":platform:gpio",
+ ":platform:clockgen")
+
+ drivers = options[":target"].get_all_drivers("usart")
+ for driver in drivers:
+ for instance in driver["instance"]:
+ module.add_submodule(Instance(driver, instance))
+
+ return True
+
+
+def build(env):
+ env.substitutions = _get_properties(env)
+ env.outbasepath = "modm/src/modm/platform/usart"
+ env.template("../uart_base.hpp.in", "usart_base.hpp")
diff --git a/src/modm/platform/uart/samg/uart.cpp.in b/src/modm/platform/uart/samg/uart.cpp.in
deleted file mode 100644
index d3462c082d..0000000000
--- a/src/modm/platform/uart/samg/uart.cpp.in
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 93b394f596..0000000000
--- a/src/modm/platform/uart/samg/uart.hpp.in
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 3ec44a304c..0000000000
--- a/src/modm/platform/uart/samg/uart_base.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
-{
-
-/// @ingroup modm_platform_uart
-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
diff --git a/src/modm/platform/usb/sam/usb.hpp.in b/src/modm/platform/usb/sam/usb.hpp.in
index 1201e1eb46..5db79081a8 100644
--- a/src/modm/platform/usb/sam/usb.hpp.in
+++ b/src/modm/platform/usb/sam/usb.hpp.in
@@ -28,7 +28,7 @@ public:
static void
initialize(uint8_t priority=3)
{
-%% if target["family"] in ["g"]
+%% if target["family"] == "g5x"
// 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!");
@@ -42,18 +42,23 @@ public:
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);
+ static_assert(SystemClock::Usb == 48_MHz, "Usb must have a 48MHz clock!");
+ PeripheralClock::enable();
+%% if target["family"] == "d5x/e5x"
+ NVIC_SetPriority(USB_0_IRQn, priority);
+ NVIC_SetPriority(USB_1_IRQn, priority);
+ NVIC_SetPriority(USB_2_IRQn, priority);
+ NVIC_SetPriority(USB_3_IRQn, priority);
+%% else
NVIC_SetPriority(USB_IRQn, priority);
+%% endif
%% 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"]
+%% if target["family"] != "g5x"
template
static void
connect()
diff --git a/tools/openocd/modm/atmel_same54_xplained_pro.cfg b/tools/openocd/modm/atmel_same54_xplained_pro.cfg
new file mode 100644
index 0000000000..3b3adccc7f
--- /dev/null
+++ b/tools/openocd/modm/atmel_same54_xplained_pro.cfg
@@ -0,0 +1,12 @@
+#
+# Atmel SAME54 Xplained Pro evaluation kit.
+# https://www.microchip.com/en-us/development-tool/ATSAME54-XPRO
+#
+
+source [find interface/cmsis-dap.cfg]
+
+# chip name
+set CHIPNAME ATSAME54P20
+
+source [find target/atsame5x.cfg]
+
diff --git a/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg b/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg
new file mode 100644
index 0000000000..cb02483dc0
--- /dev/null
+++ b/tools/openocd/modm/atmel_samv71_xplained_ultra.cfg
@@ -0,0 +1,14 @@
+#
+# Atmel SAMV71 Xplained Ultra evaluation kit.
+# http://www.atmel.com/tools/ATSAMV71-XULT.aspx
+#
+# To connect using the EDBG chip on the dev kit over USB, you will
+# first need to source [find interface/cmsis-dap.cfg]
+# however, since this board also has a SWD+ETM connector, we don't
+# automatically source that file here.
+
+source [find interface/cmsis-dap.cfg]
+
+set CHIPNAME samv71
+
+source [find target/atsamv.cfg]
diff --git a/tools/scripts/generate_hal_matrix.py b/tools/scripts/generate_hal_matrix.py
index 498316d42d..d31d889993 100755
--- a/tools/scripts/generate_hal_matrix.py
+++ b/tools/scripts/generate_hal_matrix.py
@@ -41,7 +41,7 @@ def hal_get_modules():
short_id.naming_schema = "{platform}-{family}"
elif target.platform == "sam":
- short_id.naming_schema = "{platform}{family}{series}"
+ short_id.naming_schema = "{platform}{series}"
short_id.set("platform", target.platform) # invalidate caches
minimal_targets[short_id.string].append(target)
@@ -106,6 +106,7 @@ def hal_get_modules():
"flash": "Internal Flash",
"timer": "Timer",
"i2c": "I2 C",
+ "usart": "UART"
}
mname = remap.get(mname, mname.upper())
modules.add(mname)
@@ -202,7 +203,11 @@ def hal_create_table(targets, platforms, common_table=False):
{% endfor %}
Peripheral
{% for fam in families -%}
+{% if fam[0] == "sam" -%}
+{{ fam[1] | upper | replace("X", "x") | replace("/", " ") }}
+{% else -%}
{{ fam[1] | capitalize }}
+{% endif -%}
{% endfor %}
{%- for per in pers | sort %}
{{ per }}
diff --git a/tools/scripts/generate_module_docs.py b/tools/scripts/generate_module_docs.py
index d90a3631f6..c346bb4e47 100755
--- a/tools/scripts/generate_module_docs.py
+++ b/tools/scripts/generate_module_docs.py
@@ -160,7 +160,7 @@ def replace(text, key, content):
return re.sub(r"# {0}.*?# /{0}".format(key), "# {0}\n{1}\n# /{0}".format(key, content), text, flags=re.DOTALL | re.MULTILINE)
def url_name(name):
- for c in ":., ({": name = name.replace(c, "-");
+ for c in ":.,/ ({": name = name.replace(c, "-");
for c in "})": name = name.replace(c, "");
return name