diff --git a/.github/workflows/compile-all.yml b/.github/workflows/compile-all.yml index bbc7d7c854..6fc0c0ee66 100644 --- a/.github/workflows/compile-all.yml +++ b/.github/workflows/compile-all.yml @@ -737,3 +737,29 @@ jobs: with: name: stm32h7-compile-all-2 path: test/all/log + + nrf52-compile-all: + if: github.event.label.name == 'ci:hal' + runs-on: ubuntu-24.04 + container: + image: ghcr.io/modm-ext/modm-build-cortex-m:2025-05-18 + steps: + - name: Check out repository + uses: actions/checkout@v4 + 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 --break-system-packages modm + - name: Compile HAL for all nrf52 + run: | + (cd test/all && python3 run_all.py nrf52 --quick-remaining) + - name: Upload log artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: nrf52-compile-all + path: test/all/log diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 2bc3e11d55..ad5d18015a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -286,6 +286,26 @@ jobs: name: hal-compile-quick-avr path: test/all/log + nrf-examples: + runs-on: ubuntu-24.04 + container: + image: ghcr.io/modm-ext/modm-build-cortex-m:2025-05-18 + steps: + - name: Check out repository + uses: actions/checkout@v4 + 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 --break-system-packages modm + - name: Examples NRF52840 Without Discovery Board + if: always() + run: | + (cd examples && ../tools/scripts/examples_compile.py nrf) + hal-compile-quick-1: runs-on: ubuntu-24.04 container: @@ -304,7 +324,7 @@ jobs: - name: Quick compile HAL for Cortex-M Part 1 if: always() run: | - (cd test/all && python3 run_all.py stm32 sam rp --quick --split 4 --part 0) + (cd test/all && python3 run_all.py stm32 sam rp nrf --quick --split 4 --part 0) - name: Upload log artifacts uses: actions/upload-artifact@v4 with: @@ -329,7 +349,7 @@ jobs: - name: Quick compile HAL for Cortex-M Part 2 if: always() run: | - (cd test/all && python3 run_all.py stm32 sam rp --quick --split 4 --part 1) + (cd test/all && python3 run_all.py stm32 sam rp nrf --quick --split 4 --part 1) - name: Upload log artifacts uses: actions/upload-artifact@v4 with: @@ -354,7 +374,7 @@ jobs: - name: Quick compile HAL for Cortex-M Part 3 if: always() run: | - (cd test/all && python3 run_all.py stm32 sam rp --quick --split 4 --part 2) + (cd test/all && python3 run_all.py stm32 sam rp nrf --quick --split 4 --part 2) - name: Upload log artifacts uses: actions/upload-artifact@v4 with: @@ -379,7 +399,7 @@ jobs: - name: Quick compile HAL for Cortex-M Part 4 if: always() run: | - (cd test/all && python3 run_all.py stm32 sam rp --quick --split 4 --part 3) + (cd test/all && python3 run_all.py stm32 sam rp nrf --quick --split 4 --part 3) - name: Upload log artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index ed2a042e9e..dc48e1b97d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -79,3 +79,8 @@ jobs: if: always() run: | (cd examples && ../tools/scripts/examples_compile.py avr) + + - name: Compile NRF Examples + if: always() + run: | + (cd examples && ../tools/scripts/examples_compile.py nrf) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 86f14b48d6..a3186674d0 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -135,3 +135,9 @@ jobs: shell: bash run: | (cd examples && ../tools/scripts/examples_compile.py avr arduino_nano arduino_uno srxe) + + - name: Compile NRF Examples + if: always() + shell: bash + run: | + (cd examples && ../tools/scripts/examples_compile.py nrf) diff --git a/.gitmodules b/.gitmodules index 72de297697..6ebe2ba5a6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,12 @@ [submodule "ext/st/stm32"] path = ext/st/stm32 url = https://github.com/modm-io/cmsis-header-stm32.git +[submodule "ext/nordic/nrf"] + path = ext/nordic/nrf + url = https://github.com/elli89/cmsis-header-nrf.git [submodule "ext/modm-devices"] path = ext/modm-devices - url = https://github.com/modm-io/modm-devices.git + url = https://github.com/elli89/modm-devices.git [submodule "ext/ros/ros-lib"] path = ext/ros/ros-lib url = https://github.com/modm-io/ros-lib diff --git a/examples/nrf/blink/main.cpp b/examples/nrf/blink/main.cpp new file mode 100644 index 0000000000..4820b98eff --- /dev/null +++ b/examples/nrf/blink/main.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010-2011, Fabian Greif + * Copyright (c) 2013-2014, 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 modm::platform; + +typedef GpioOutputP0_22 Led; + +int +main() +{ + Led::setOutput(); + + while (true) + { + Led::toggle(); + + modm::delay_ms(1000); + } +} + diff --git a/examples/nrf/blink/project.xml b/examples/nrf/blink/project.xml new file mode 100644 index 0000000000..bcccfaddf4 --- /dev/null +++ b/examples/nrf/blink/project.xml @@ -0,0 +1,14 @@ + + + + + + + modm:architecture:delay + modm:platform:core + modm:platform:clock + modm:platform:hfclk + modm:platform:gpio + modm:build:scons + + diff --git a/ext/nordic/device.hpp.in b/ext/nordic/device.hpp.in new file mode 100644 index 0000000000..5106855867 --- /dev/null +++ b/ext/nordic/device.hpp.in @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_DEVICE_HPP +#define MODM_DEVICE_HPP + +#define DONT_USE_CMSIS_INIT 1 +%% for define in defines +#define {{ define }} 1 +%% endfor + +#include +// Defines for example the modm_always_inline macro +#include + +// Include external device headers: +%% for header in headers +#include <{{ header }}> +%% endfor + +#endif // MODM_DEVICE_HPP diff --git a/ext/nordic/module.lb b/ext/nordic/module.lb new file mode 100644 index 0000000000..e39d90775d --- /dev/null +++ b/ext/nordic/module.lb @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Niklas Hauser +# Copyright (c) 2020, Hannes Ellinger +# +# 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/. +# ----------------------------------------------------------------------------- + +import re +from pathlib import Path + + +# ----------------------------------------------------------------------------- +def init(module): + module.name = ":cmsis:device" + + +def prepare(module, options): + device = options[":target"] + if device.identifier["platform"] != "nrf": + return False + + module.depends(":cmsis:core") + return True + + +pp = {} + + +def validate(env): + device = env[":target"] + device_name = device.identifier.string.split("-")[0] + + define = "{}".format(device_name.upper()) + family_file = None + famfile = Path(localpath("nrf/nrf.h")) + content = famfile.read_text(encoding="utf-8", errors="replace") + match = re.findall(r"defined \((?P_?.*)\)", content) + for m in match: + if define in m: + family_file = famfile.relative_to(localpath(".")) + + if family_file is None: + raise ValidateException("No device define found for '{}'!".format(device.partname)) + + family_folder = family_file.parent + if device_name == "nrf51822": + device_header = "nrf51.h" + elif device_name == "nrf52832": + device_header = "nrf52.h" + else: + device_header = "{}.h".format(device_name) + + global pp + pp = { + "define": define, + "folder": family_folder, + "device_header": device_header, + } + + +def build(env): + global pp + env.collect(":build:path.include", "modm/ext/cmsis/device") + + env.outbasepath = "modm/ext/cmsis/device" + files = [pp["device_header"], "system_"+pp["device_header"]] + for file in files: + env.copy(localpath(pp["folder"], file), file) + + env.substitutions = { + "headers": [pp["device_header"]], + "defines": [pp["define"]], + } + env.outbasepath = "modm/src/modm/platform" + env.template("device.hpp.in") diff --git a/ext/nordic/nrf b/ext/nordic/nrf new file mode 160000 index 0000000000..5972dc25f1 --- /dev/null +++ b/ext/nordic/nrf @@ -0,0 +1 @@ +Subproject commit 5972dc25f15b19f110075e781b7162feaf04d413 diff --git a/repo.lb b/repo.lb index 95caae6990..3566f9466a 100644 --- a/repo.lb +++ b/repo.lb @@ -94,6 +94,7 @@ class DevicesCache(dict): "same70", "sams70", "samv70", "samv71", "samd51", "same51", "same53", "same54", "rp2040", + "nrf", "hosted"] device_file_names = [dfn for dfn in device_file_names if any(s in dfn for s in supported)] # These files are ignored due to various issues diff --git a/src/modm/platform/clock/nrf/hfclk.cpp.in b/src/modm/platform/clock/nrf/hfclk.cpp.in new file mode 100644 index 0000000000..15a7e453c3 --- /dev/null +++ b/src/modm/platform/clock/nrf/hfclk.cpp.in @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013-2014, Kevin Läufer + * Copyright (c) 2014-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 "../device.hpp" +#include "hfclk.hpp" + +namespace modm::platform +{ +uint16_t modm_fastdata delay_fcpu_MHz(64); +uint16_t modm_fastdata delay_ns_per_loop({{ loops * 1000 / 64 }}); +} diff --git a/src/modm/platform/clock/nrf/hfclk.hpp b/src/modm/platform/clock/nrf/hfclk.hpp new file mode 100644 index 0000000000..18eddaa871 --- /dev/null +++ b/src/modm/platform/clock/nrf/hfclk.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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" + +namespace modm::platform +{ + +/** + * Clock management + * + * \ingroup modm_platform_hfclk + */ +class HighFrequencyClockController +{ +public: + template< uint32_t Core_Hz > + void + updateCoreFrequency(); +}; + +} + +#include "hfclk_impl.hpp" diff --git a/src/modm/platform/clock/nrf/hfclk_impl.hpp.in b/src/modm/platform/clock/nrf/hfclk_impl.hpp.in new file mode 100644 index 0000000000..902c8a6607 --- /dev/null +++ b/src/modm/platform/clock/nrf/hfclk_impl.hpp.in @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, Ethan Slattery + * + * 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 + +namespace modm::platform +{ +/// @cond +extern uint16_t delay_fcpu_MHz; +extern uint16_t delay_ns_per_loop; +/// @endcond + +template< uint32_t Core_Hz > +void +HighFrequencyClockController::updateCoreFrequency() +{ + delay_fcpu_MHz = Core_Hz / 64'000'000; + delay_ns_per_loop = ::round({{loops}}000.f / (Core_Hz / 64'000'000)); +} + +} + diff --git a/src/modm/platform/clock/nrf/module.lb b/src/modm/platform/clock/nrf/module.lb new file mode 100644 index 0000000000..9e8bd64299 --- /dev/null +++ b/src/modm/platform/clock/nrf/module.lb @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# This file is part of the modm project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# ----------------------------------------------------------------------------- + +def init(module): + module.name = ":platform:hfclk" + module.description = "High Frequency Clock Controller (HFCLK)" + +def prepare(module, options): + if not options[":target"].has_driver("clock", ["nrf51", "nrf52"]): + return False + + module.depends(":cmsis:device") + return True + +def build(env): + device = env[":target"] + core = device.get_driver("core")["type"] + + if "m0" in core: + loops = 4 + elif "m7" in core: + loops = 1 + else: + loops = 3 + + env.substitutions = {"loops": loops} + env.outbasepath = "modm/src/modm/platform/clock" + env.copy("hfclk.hpp") + env.template("hfclk.cpp.in") + env.template("hfclk_impl.hpp.in") diff --git a/src/modm/platform/core/nrf/linkerscript/nrf.ld.in b/src/modm/platform/core/nrf/linkerscript/nrf.ld.in new file mode 100644 index 0000000000..22f848265b --- /dev/null +++ b/src/modm/platform/core/nrf/linkerscript/nrf.ld.in @@ -0,0 +1,44 @@ +%% import "../../cortex/linker.macros" as linker with context +{{ linker.copyright() }} + +{{ linker.prefix() }} +%% set table_heap = [] +%% set table_copy = [] +%% set table_zero = [] + +SECTIONS +{ +{{ linker.section_rom_start("FLASH") }} + +{{ linker.section_vector_rom("FLASH") }} + +{{ linker.section_stack("RAM", "VECTOR_TABLE_OFFSET - TOTAL_STACK_SIZE") }} + +{{ linker.section_vector_ram("RAM") }} + +{{ linker.section_rom("FLASH") }} + +{{ linker.section("FLASH", "fastcode") }} + +{{ linker.section_ram("RAM", "FLASH") }} + +{{ linker.section("RAM AT >FLASH", "fastdata") }} + +{{ linker.section_heap("RAM", "heap1") }} + +{{ linkerscript_sections | indent(first=True) }} + + /* TABLES! TABLES! ALL THE TABLES YOU COULD EVER WANT! TABLES! */ +{{ linker.section_table_zero("FLASH") }} + +{{ linker.section_table_copy("FLASH") }} + +{{ linker.section_table_extern("FLASH") }} + +%% set heap_table = [{"name": "heap1", "prop": "0x000f"}] +{{ linker.section_table_heap("FLASH", heap_table) }} + +{{ linker.section_rom_end("FLASH") }} + +{{ linker.section_debug() }} +} diff --git a/src/modm/platform/core/nrf/module.lb b/src/modm/platform/core/nrf/module.lb new file mode 100644 index 0000000000..fab4391326 --- /dev/null +++ b/src/modm/platform/core/nrf/module.lb @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2019, Ethan Slattery +# +# 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/. +# ----------------------------------------------------------------------------- + +import math + +def init(module): + module.name = ":platform:core" + module.description = """\ +# Nordic nRF core module + +Provides NRF specific linkerscripts and startup code. +""" + +def prepare(module, options): + if options[":target"].identifier.platform != "nrf": + return False + + module.depends(":platform:cortex-m") + return True + + +def build(env): + env.substitutions = {"target": env[":target"].identifier} + env.outbasepath = "modm/src/modm/platform/core" + # startup helper code + env.template("startup_platform.c.in") + + # us_shift is an optimization to limit error via fractional math + frequency = 64e6 + us_shift = 32 - math.ceil(math.log2(frequency)) + + env.substitutions.update({ + "with_cm0": env[":target"].has_driver("core:cortex-m0*"), + "with_cm7": env[":target"].has_driver("core:cortex-m7*"), + "loop": 4, + "shift": 4, + "us_shift": us_shift, + "with_assert": env.has_module(":architecture:assert") + }) + env.template("../cortex/delay_ns.cpp.in", "delay_ns.cpp") + env.template("../cortex/delay_ns.hpp.in", "delay_ns.hpp") + env.template("../cortex/delay_impl.hpp.in", "delay_impl.hpp") + + +def post_build(env): + env.substitutions = env.query("::cortex-m:linkerscript") + env.substitutions.update(env.query("::cortex-m:vector_table")) + env.outbasepath = "modm/link" + env.template("../cortex/ram.ld.in", "linkerscript.ld") diff --git a/src/modm/platform/core/nrf/startup_platform.c.in b/src/modm/platform/core/nrf/startup_platform.c.in new file mode 100644 index 0000000000..65e7e93d99 --- /dev/null +++ b/src/modm/platform/core/nrf/startup_platform.c.in @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016, Sascha Schade + * Copyright (c) 2016-2017, Fabian Greif + * Copyright (c) 2016-2017, 2019, Niklas Hauser + * Copyright (c) 2020, Hannes Ellinger + * + * This file is part of the modm project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +// ---------------------------------------------------------------------------- + +#include "../device.hpp" + +/** + * This code should _only_ enable internal memories and nothing else. + * Since this is the first code executed after a reset, you do not + * have access to _any_ data stored in RAM, since it has not yet been + * initialized. + * In the worst case you won't even have access to the stack, if the + * memory containing the stack is not physically enabled yet. + * In that case, consider using inline assembly to manage stack access + * manually, until the memory is enabled. + */ +void +__modm_initialize_platform(void) +{ + /* Enable SWO trace functionality. If ENABLE_SWO is not defined, SWO pin will be used as GPIO (see Product + Specification to see which one). */ + #if defined (ENABLE_SWO) + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Serial << CLOCK_TRACECONFIG_TRACEMUX_Pos; + NRF_P1->PIN_CNF[0] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + #endif + + /* Enable Trace functionality. If ENABLE_TRACE is not defined, TRACE pins will be used as GPIOs (see Product + Specification to see which ones). */ + #if defined (ENABLE_TRACE) + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; + NRF_CLOCK->TRACECONFIG |= CLOCK_TRACECONFIG_TRACEMUX_Parallel << CLOCK_TRACECONFIG_TRACEMUX_Pos; + NRF_P0->PIN_CNF[7] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + NRF_P1->PIN_CNF[0] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + NRF_P0->PIN_CNF[12] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + NRF_P0->PIN_CNF[11] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + NRF_P1->PIN_CNF[9] = (GPIO_PIN_CNF_DRIVE_H0H1 << GPIO_PIN_CNF_DRIVE_Pos) | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + #endif + +// /* Workaround for Errata 36 "CLOCK: Some registers are not reset when expected" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_36()){ +// NRF_CLOCK->EVENTS_DONE = 0; +// NRF_CLOCK->EVENTS_CTTO = 0; +// NRF_CLOCK->CTIV = 0; +// } + +// /* Workaround for Errata 66 "TEMP: Linearity specification not met with default settings" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_66()){ +// NRF_TEMP->A0 = NRF_FICR->TEMP.A0; +// NRF_TEMP->A1 = NRF_FICR->TEMP.A1; +// NRF_TEMP->A2 = NRF_FICR->TEMP.A2; +// NRF_TEMP->A3 = NRF_FICR->TEMP.A3; +// NRF_TEMP->A4 = NRF_FICR->TEMP.A4; +// NRF_TEMP->A5 = NRF_FICR->TEMP.A5; +// NRF_TEMP->B0 = NRF_FICR->TEMP.B0; +// NRF_TEMP->B1 = NRF_FICR->TEMP.B1; +// NRF_TEMP->B2 = NRF_FICR->TEMP.B2; +// NRF_TEMP->B3 = NRF_FICR->TEMP.B3; +// NRF_TEMP->B4 = NRF_FICR->TEMP.B4; +// NRF_TEMP->B5 = NRF_FICR->TEMP.B5; +// NRF_TEMP->T0 = NRF_FICR->TEMP.T0; +// NRF_TEMP->T1 = NRF_FICR->TEMP.T1; +// NRF_TEMP->T2 = NRF_FICR->TEMP.T2; +// NRF_TEMP->T3 = NRF_FICR->TEMP.T3; +// NRF_TEMP->T4 = NRF_FICR->TEMP.T4; +// } + +// /* Workaround for Errata 98 "NFCT: Not able to communicate with the peer" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_98()){ +// *(volatile uint32_t *)0x4000568Cul = 0x00038148ul; +// } + +// /* Workaround for Errata 103 "CCM: Wrong reset value of CCM MAXPACKETSIZE" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_103()){ +// NRF_CCM->MAXPACKETSIZE = 0xFBul; +// } + +// /* Workaround for Errata 115 "RAM: RAM content cannot be trusted upon waking up from System ON Idle or System OFF mode" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_115()){ +// *(volatile uint32_t *)0x40000EE4ul = (*(volatile uint32_t *)0x40000EE4ul & 0xFFFFFFF0ul) | (*(uint32_t *)0x10000258ul & 0x0000000Ful); +// } + +// /* Workaround for Errata 120 "QSPI: Data read or written is corrupted" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_120()){ +// *(volatile uint32_t *)0x40029640ul = 0x200ul; +// } + +// /* Workaround for Errata 136 "System: Bits in RESETREAS are set when they should not be" found at the Errata document +// for your device located at https://infocenter.nordicsemi.com/index.jsp */ +// if (nrf52_errata_136()){ +// if (NRF_POWER->RESETREAS & POWER_RESETREAS_RESETPIN_Msk){ +// NRF_POWER->RESETREAS = ~POWER_RESETREAS_RESETPIN_Msk; +// } +// } + + /* Enable the FPU if the compiler used floating point unit instructions. __FPU_USED is a MACRO defined by the + * compiler. Since the FPU consumes energy, remember to disable FPU use in the compiler if floating point unit + * operations are not used in your code. */ + #if (__FPU_USED == 1) + SCB->CPACR |= (3UL << 20) | (3UL << 22); + __DSB(); + __ISB(); + #endif + + /* Configure NFCT pins as GPIOs if NFCT is not to be used in your code. If CONFIG_NFCT_PINS_AS_GPIOS is not defined, + two GPIOs (see Product Specification to see which ones) will be reserved for NFC and will not be available as + normal GPIOs. */ + #if defined (CONFIG_NFCT_PINS_AS_GPIOS) + if ((NRF_UICR->NFCPINS & UICR_NFCPINS_PROTECT_Msk) == (UICR_NFCPINS_PROTECT_NFC << UICR_NFCPINS_PROTECT_Pos)){ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NRF_UICR->NFCPINS &= ~UICR_NFCPINS_PROTECT_Msk; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NVIC_SystemReset(); + } + #endif + + /* Configure GPIO pads as pPin Reset pin if Pin Reset capabilities desired. If CONFIG_GPIO_AS_PINRESET is not + defined, pin reset will not be available. One GPIO (see Product Specification to see which one) will then be + reserved for PinReset and not available as normal GPIO. */ + #if defined (CONFIG_GPIO_AS_PINRESET) + if (((NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) != (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos)) || + ((NRF_UICR->PSELRESET[1] & UICR_PSELRESET_CONNECT_Msk) != (UICR_PSELRESET_CONNECT_Connected << UICR_PSELRESET_CONNECT_Pos))){ + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NRF_UICR->PSELRESET[0] = 18; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NRF_UICR->PSELRESET[1] = 18; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos; + while (NRF_NVMC->READY == NVMC_READY_READY_Busy){} + NVIC_SystemReset(); + } + #endif +} diff --git a/src/modm/platform/gpio/nrf/base.hpp.in b/src/modm/platform/gpio/nrf/base.hpp.in new file mode 100644 index 0000000000..47bd4dc1ac --- /dev/null +++ b/src/modm/platform/gpio/nrf/base.hpp.in @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_NRF_GPIO_BASE_HPP +#define MODM_NRF_GPIO_BASE_HPP + +#include "../device.hpp" +#include +#include +#include + +namespace modm::platform +{ + +/// @ingroup modm_platform_gpio +struct Gpio +{ + enum class + Dir + { + Input = 0, + Output = 1, + }; + + enum class + Input + { + Connect = 0, + Disconnect = 1, + }; + + enum class + Pull + { + Disabled = 0, + Pulldown = 1, + Pullup = 3, + }; + + enum class + Drive + { + S0S1 = 0, + H0S1 = 1, + S0H1 = 2, + H0H1 = 3, + D0S1 = 4, + D0H1 = 5, + S0D1 = 6, + H0D1 = 7, + }; + + enum class + Sense + { + Disabled = 0, + High = 2, + Low = 3, + }; + + /// @cond + enum class + Signal + { + BitBang, + }; + /// @endcond + +protected: + + // Enum Class To Integer helper functions. + static constexpr uint32_t + i(Dir dir) {return uint32_t(dir); } + static constexpr uint32_t + i(Input input) {return uint32_t(input); } + static constexpr uint32_t + i(Pull pull) {return uint32_t(pull); } + static constexpr uint32_t + i(Drive drive) {return uint32_t(drive); } + static constexpr uint32_t + i(Sense sense) {return uint32_t(sense); } +}; + +} // namespace modm::platform + +#endif // MODM_NRF_GPIO_BASE_HPP diff --git a/src/modm/platform/gpio/nrf/connector.hpp b/src/modm/platform/gpio/nrf/connector.hpp new file mode 100644 index 0000000000..64fa4028b1 --- /dev/null +++ b/src/modm/platform/gpio/nrf/connector.hpp @@ -0,0 +1,36 @@ +/* + * 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 + +namespace modm +{ + +namespace platform +{ + +// GpioConnector only exists for backwards compability with bitbang API. +template +struct GpioConnector +{ + template + static constexpr bool Contains = true; + + inline static void connect() {} + inline static void disconnect() {} +}; +/// @endcond + +} // namespace platform + +} // namespace modm diff --git a/src/modm/platform/gpio/nrf/module.lb b/src/modm/platform/gpio/nrf/module.lb new file mode 100644 index 0000000000..aa7249269f --- /dev/null +++ b/src/modm/platform/gpio/nrf/module.lb @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (c) 2016-2018, Niklas Hauser +# Copyright (c) 2017, Fabian Greif +# +# 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 port_ranges(gpios): + ports = {p: (32, 0) for p in set(p["port"] for p in gpios)} + for gpio in gpios: + pin = int(gpio["pin"]) + pmin, pmax = ports[gpio["port"]] + ports[gpio["port"]] = (min(pin, pmin), max(pin, pmax)) + + ports = [{"name": k.upper(), "start": v[0], "width": v[1] - v[0] + 1} for k,v in ports.items()] + ports.sort(key=lambda p: p["name"]) + return ports + + +def translate(s): + name = "" + for part in s.split("_"): + name += part.capitalize() + return name + + +# ----------------------------------------------------------------------------- +def init(module): + module.name = ":platform:gpio" + module.description = "General Purpose I/O (GPIO)" + + +def prepare(module, options): + module.depends(":architecture:register", ":architecture:gpio", ":cmsis:device", ":math:utils") + return options[":target"].has_driver("gpio:nrf*") + + +def build(env): + device = env[":target"] + driver = device.get_driver("gpio") + + all_signals = set([translate(s["name"]) for s in driver["signal"]]) + print(all_signals) + + peripherals = [] + all_drivers = [d for d in device._properties["driver"] if d["name"] not in ["gpio", "core"]] + for d in all_drivers: + dname = translate(d["name"]) + if "instance" in d: + peripherals.extend([dname + translate(i) for i in d["instance"]]) + else: + peripherals.append(dname) + + subs = device.properties + subs["target"] = device.identifier + subs["driver"] = driver + subs["gpios"] = driver["gpio"] + subs["all_signals"] = list(all_signals) + subs["all_peripherals"] = sorted(list(set(peripherals))) + + env.substitutions = subs + env.outbasepath = "modm/src/modm/platform/gpio" + + env.template("pin.hpp.in") + env.template("unused.hpp.in") + env.template("base.hpp.in") + env.copy("connector.hpp") + + # FIXME: Move to modm:platform:core! + env.outbasepath = "modm/src/modm/platform/core" + env.template("peripherals.hpp.in") diff --git a/src/modm/platform/gpio/nrf/peripherals.hpp.in b/src/modm/platform/gpio/nrf/peripherals.hpp.in new file mode 100644 index 0000000000..1da624f285 --- /dev/null +++ b/src/modm/platform/gpio/nrf/peripherals.hpp.in @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019, 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/. + */ +// ---------------------------------------------------------------------------- + +#pragma once + +namespace modm::platform +{ + +enum class +Peripheral +{ + BitBang, +%% for per in all_peripherals + {{ per }}, +%% endfor + // Syscfg = Sys, +}; + +} diff --git a/src/modm/platform/gpio/nrf/pin.hpp.in b/src/modm/platform/gpio/nrf/pin.hpp.in new file mode 100644 index 0000000000..d1579c165a --- /dev/null +++ b/src/modm/platform/gpio/nrf/pin.hpp.in @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_NRF_GPIO_HPP +#define MODM_NRF_GPIO_HPP + +#include "../device.hpp" +#include "base.hpp" + +namespace modm::platform +{ + +%% for gpio in gpios + +%% set port = "P" ~ gpio["port"] | upper +%% set reg = "NRF_" ~ port +%% set pin = gpio["pin"] +%% set pinname = port ~ "_" ~ pin + +/// @cond +class Gpio{{ pinname }}; +using GpioOutput{{ pinname }} = Gpio{{ pinname }}; +using GpioInput{{ pinname }} = Gpio{{ pinname }}; +/// @endcond + +/// IO class for Pin {{pinname}} +/// @ingroup modm_platform_gpio +class Gpio{{ pinname }} : public Gpio, public ::modm::GpioIO +{ +public: + using Output = Gpio{{ pinname }}; + using Input = Gpio{{ pinname }}; + using IO = Gpio{{ pinname }}; + using Type = Gpio{{ pinname }}; + // static constexpr Port port = Port::P{{port}}; ///< Port name + static constexpr uint8_t pin = {{pin}}; ///< Pin number + +protected: + static constexpr uint32_t mask = 0x1 << pin; + +public: + // GpioOutput + // start documentation inherited + inline static void setOutput() { {{reg}}->DIRSET |= mask; } + inline static void setOutput(bool status) { setOutput(); set(status); } + inline static void set() { {{reg}}->OUTSET |= mask; } + inline static void set(bool status) { if(status) set(); else reset(); } + inline static void reset() { {{reg}}->OUTCLR |= mask; } + inline static void toggle() { + if (isSet()) { reset(); } + else { set(); } + } + inline static bool isSet() { return ({{reg}}->OUT & mask); } + // stop documentation inherited + // inline static void configure(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::configure(type, speed); } + // inline static void setOutput(OutputType type, OutputSpeed speed = OutputSpeed::MHz50) { PinSet::setOutput(type, speed); } + // GpioInput + // start documentation inherited + inline static void setInput() { {{reg}}->DIRCLR |= mask; } + inline static bool read() { return ({{reg}}->IN & mask); } + // end documentation inherited TODO + // inline static void configure(InputType type) { PinSet::configure(type); } + // inline static void setInput(InputType type) { PinSet::setInput(type); } + + // GpioIO + // start documentation inherited + inline static Direction getDirection() { + if ({{reg}}->DIR |= mask) { + return Direction::Out; + } else { + return Direction::In; + } + } + // end documentation inherited TODO + // inline static void disconnect() { + // setInput(InputType::Floating); + // {{reg}}->AFR[af_id] &= ~af_mask; + // %% if target["family"] in ["l4"] and target["name"] in ["71", "75", "76", "85", "86"] + // {{reg}}->ASCR &= ~mask; + // %% endif + + // } + +public: +#ifdef __DOXYGEN__ + /// @{ + /// Connect to any software peripheral + using BitBang = GpioSignal; + %% for sig in all_signals + /// Connect to {{ sig }} + using {{ sig }} = GpioSignal; + %% endfor + /// @} +#endif + + /// @cond + enum class + Signal + { + BitBang, +%% for sig in all_signals | sort + {{ sig }}, +%% endfor + }; + + /// @cond + template< Peripheral peripheral > + struct BitBang { static void connect(); + static_assert( + (peripheral == Peripheral::BitBang), + "Gpio{{ pinname }}::BitBang only connects to software drivers!"); + }; + %% for sig in all_signals + template< Peripheral peripheral > + struct {{ sig }} { static void connect(); }; + %% endfor + /// @endcond +}; + +/// @cond +template<> +struct Gpio{{ pinname }}::BitBang +{ + using Gpio = Gpio{{ pinname }}; + static constexpr Gpio::Signal Signal = Gpio::Signal::BitBang; + inline static void connect() {} +}; + +%% endfor + +/// @endcond + +} // namespace modm::platform + +#endif // MODM_NRF_GPIO_HPP diff --git a/src/modm/platform/gpio/nrf/port.hpp.in b/src/modm/platform/gpio/nrf/port.hpp.in new file mode 100644 index 0000000000..3249a87dc2 --- /dev/null +++ b/src/modm/platform/gpio/nrf/port.hpp.in @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_NRF_GPIO_PORT_HPP +#define MODM_NRF_GPIO_PORT_HPP + +#include "base.hpp" +#include +#include + +namespace modm +{ + +namespace platform +{ + +/// @cond +template< Gpio::Port port > +struct GpioPortInfo; +template< class StartGpio, int8_t Width > +struct GpioSetShim +{ + static typename GpioPortInfo::template check check; +}; +/// @endcond + +/// @ingroup modm_platform_gpio +template< class StartGpio, int8_t Width > +class GpioPort : public ::modm::GpioPort/** @cond */, public GpioSetShim /** @endcond */ +{ + using PinSet = GpioSetShim; +public: + using PinSet::width; + static_assert(width <= 16, "Only a maximum of 16 pins are supported by GpioPort!"); + using PortType = std::conditional_t< (width > 8), + uint16_t, + uint8_t >; + static constexpr DataOrder getDataOrder() + { return Width > 0 ? GpioPort::DataOrder::Normal : GpioPort::DataOrder::Reversed; } + +protected: + using PinSet::mask; + using PinSet::inverted; + static constexpr uint8_t StartPin = Width > 0 ? StartGpio::pin : StartGpio::pin - width + 1; + static constexpr uint8_t StartPinReversed = (8 - StartPin - width) + 8; + +public: + static PortType isSet() + { + uint16_t r{0}; +%% for port, id in ports.items() + if constexpr (mask({{id}})) r = (GPIOP{{port}}->ODR & mask({{id}})) ^ inverted({{id}}); +%% endfor + if constexpr (getDataOrder() == modm::GpioPort::DataOrder::Reversed) + return bitReverse(r) >> StartPinReversed; + else return r >> StartPin; + } + + static PortType read() + { + uint16_t r{0}; +%% for port, id in ports.items() + if constexpr (mask({{id}})) r = (GPIOP{{port}}->IDR & mask({{id}})) ^ inverted({{id}}); +%% endfor + if constexpr (getDataOrder() == modm::GpioPort::DataOrder::Reversed) + return bitReverse(r) >> StartPinReversed; + else return r >> StartPin; + } + + static void write(PortType data) + { + uint16_t p; + if constexpr (getDataOrder() == modm::GpioPort::DataOrder::Reversed) + p = bitReverse(uint16_t(uint16_t(data) << StartPinReversed)); + else p = uint16_t(data) << StartPin; +%% for port, id in ports.items() + if constexpr (mask({{id}})) { p ^= inverted({{id}}); + GPIOP{{port}}->BSRR = ((~p & mask({{id}})) << 16) | (p & mask({{id}})); + } +%% endfor + } +}; + +/// @cond +// ------ Port Width Information ------ +%% for port in ranges +template<> +struct GpioPortInfo +{ + static constexpr int8_t Width = {{port["width"]}}; + static constexpr int8_t StartPin = {{port["start"]}}; + static constexpr int8_t EndPin = StartPin + Width; + + template< uint8_t QueryStartPin, int8_t QueryWidth > + struct check + { + static constexpr bool isNormal = QueryWidth > 0; + static constexpr bool isReversed = QueryWidth < 0; + static constexpr int8_t width = isNormal ? QueryWidth : -QueryWidth; + static_assert(isReversed or width <= Width, + "GpioPort Width out of bounds! Maximum is {{port["width"]}}."); + + static_assert(isReversed or (QueryStartPin + QueryWidth <= EndPin), + "GpioPort StartPin + Width out of bounds! Maximum is {{port["start"] + port["width"] - 1}}."); + static_assert(isNormal or (StartPin <= QueryStartPin + QueryWidth + 1), + "GpioPort StartPin - Width out of bounds! Minimum is {{port["start"]}}."); + }; +}; +%% endfor + +// ------ Translator classes from Gpio + Width -> GpioSet ------ +template< class StartGpio, int8_t offset > +struct GpioShim +{ + static constexpr typename StartGpio::Port port = StartGpio::port; + static constexpr uint16_t mask = (1ul << (StartGpio::pin + offset)); + static constexpr bool isInverted = StartGpio::isInverted; +}; +%% for width in range(-16, 0) +template< class StartGpio > +struct GpioSetShim : public GpioSet< + %% for offset in (range(width + 1, 1) | reverse) + GpioShim{% if not loop.last %},{% endif %} + %% endfor + > { static typename GpioPortInfo::template check check; }; +%% endfor +%% for width in range(0, 17) +template< class StartGpio > +struct GpioSetShim : public GpioSet< + %% for offset in (range(width) | reverse) + GpioShim{% if not loop.last %},{% endif %} + %% endfor + > { static typename GpioPortInfo::template check check; }; +%% endfor +/// @endcond + +} // namespace platform + +} // namespace modm + +#endif // MODM_NRF_GPIO_PORT_HPP diff --git a/src/modm/platform/gpio/nrf/unused.hpp.in b/src/modm/platform/gpio/nrf/unused.hpp.in new file mode 100644 index 0000000000..14bda2c124 --- /dev/null +++ b/src/modm/platform/gpio/nrf/unused.hpp.in @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017-2018, 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/. + */ +// ---------------------------------------------------------------------------- + +#ifndef MODM_NRF_GPIO_PIN_UNUSED_HPP +#define MODM_NRF_GPIO_PIN_UNUSED_HPP + +#include "base.hpp" +#include + +namespace modm +{ + +namespace platform +{ + +/** + * Dummy implementation of an I/O pin. + * + * This class can be used when a pin is not required. All functions + * are dummy functions which do nothing. `read()` will always + * return `false`. + * + * For example when creating a software SPI with the modm::SoftwareSimpleSpi + * class and the return channel (MISO - Master In Slave Out) is not needed, + * a good way is to use this class as a parameter when defining the + * SPI class. + * + * Example: + * @code + * #include + * + * namespace pin + * { + * typedef GpioOutputD7 Clk; + * typedef GpioOutputD5 Mosi; + * } + * + * modm::SoftwareSpiMaster< pin::Clk, pin::Mosi, GpioUnused > Spi; + * + * ... + * Spi::write(0xaa); + * @endcode + * + * @author Fabian Greif + * @author Niklas Hauser + * @ingroup modm_platform_gpio + */ +class GpioUnused : public Gpio, public ::modm::GpioIO +{ +public: + using Output = GpioUnused; + using Input = GpioUnused; + using IO = GpioUnused; + using Type = GpioUnused; + // static constexpr Port port = Port(-1); + static constexpr uint8_t pin = uint8_t(-1); + +protected: + static constexpr uint16_t mask = 0; + +public: + // GpioOutput + // start documentation inherited + static void setOutput() {} + static void setOutput(bool) {} + static void set() {} + static void set(bool) {} + static void reset() {} + static void toggle() {} + static bool isSet() { return false; } + // stop documentation inherited + // static void configure(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + // static void setOutput(OutputType, OutputSpeed = OutputSpeed::MHz50) {} + + // GpioInput + // start documentation inherited + static void setInput() {} + static bool read() { return false; } + // end documentation inherited + // static void configure(InputType) {} + // static void setInput(InputType) {} + + // GpioIO + // start documentation inherited + static Direction getDirection() { return Direction::Special; } + // end documentation inherited +}; + +} // namespace platform + +} // namespace modm + +#endif // MODM_NRF_GPIO_PIN_UNUSED_HPP