From 1e6ac1f093710111045d77c83ae490b5619c4f0c Mon Sep 17 00:00:00 2001 From: Michel Rottleuthner Date: Thu, 2 Nov 2023 11:49:59 +0100 Subject: [PATCH 01/85] dist/tools/uf2: add target to also copy families.json file --- dist/tools/uf2/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dist/tools/uf2/Makefile b/dist/tools/uf2/Makefile index 36195ea553..23f45d18e3 100644 --- a/dist/tools/uf2/Makefile +++ b/dist/tools/uf2/Makefile @@ -5,8 +5,11 @@ PKG_LICENSE=MIT include $(RIOTBASE)/pkg/pkg.mk -all: $(CURDIR)/uf2conv.py +all: $(CURDIR)/uf2conv.py $(CURDIR)/uf2families.json $(CURDIR)/uf2conv.py: cp $(PKG_SOURCE_DIR)/utils/uf2conv.py . chmod a+x uf2conv.py + +$(CURDIR)/uf2families.json: + cp $(PKG_SOURCE_DIR)/utils/uf2families.json . From cea7fcec2f16acf5c13912524405c4e5bd09fa96 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 23 Oct 2023 13:11:23 +0200 Subject: [PATCH 02/85] cpu/native: fix bug in periph_timer Also use `CLOCK_MONOTONIC` for the timeouts, not just for `timer_read()`. This fixes mismatches between when a timeout occurs and what is expected in the context of the values returned by `timer_read()`. --- cpu/native/periph/timer.c | 42 ++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/cpu/native/periph/timer.c b/cpu/native/periph/timer.c index 229ae6efaf..e275dc5574 100644 --- a/cpu/native/periph/timer.c +++ b/cpu/native/periph/timer.c @@ -26,18 +26,19 @@ * @} */ -#include -#include +#include #include #include #include #include -#include +#include +#include #include "cpu.h" #include "cpu_conf.h" #include "native_internal.h" #include "periph/timer.h" +#include "time_units.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -49,7 +50,9 @@ static unsigned long time_null; static timer_cb_t _callback; static void *_cb_arg; -static struct itimerval itv; +static struct itimerspec its; + +static timer_t itimer_monotonic; /** * returns ticks for give timespec @@ -89,8 +92,15 @@ int timer_init(tim_t dev, uint32_t freq, timer_cb_t cb, void *arg) _callback = cb; _cb_arg = arg; + + if (timer_create(CLOCK_MONOTONIC, NULL, &itimer_monotonic) != 0) { + DEBUG_PUTS("Failed to create a monotonic itimer"); + return -1; + } + if (register_interrupt(SIGALRM, native_isr_timer) != 0) { - DEBUG("darn!\n\n"); + DEBUG_PUTS("Failed to register SIGALRM handler"); + return -1; } return 0; @@ -104,14 +114,14 @@ static void do_timer_set(unsigned int offset, bool periodic) offset = NATIVE_TIMER_MIN_RES; } - memset(&itv, 0, sizeof(itv)); - itv.it_value.tv_sec = (offset / 1000000); - itv.it_value.tv_usec = offset % 1000000; + memset(&its, 0, sizeof(its)); + its.it_value.tv_sec = offset / NATIVE_TIMER_SPEED; + its.it_value.tv_nsec = (offset % NATIVE_TIMER_SPEED) * (NS_PER_SEC / NATIVE_TIMER_SPEED); if (periodic) { - itv.it_interval = itv.it_value; + its.it_interval = its.it_value; } - DEBUG("timer_set(): setting %lu.%06lu\n", itv.it_value.tv_sec, itv.it_value.tv_usec); + DEBUG("timer_set(): setting %lu.%09lu\n", (unsigned long)its.it_value.tv_sec, its.it_value.tv_nsec); } int timer_set(tim_t dev, int channel, unsigned int offset) @@ -171,8 +181,8 @@ void timer_start(tim_t dev) DEBUG("%s\n", __func__); _native_syscall_enter(); - if (real_setitimer(ITIMER_REAL, &itv, NULL) == -1) { - err(EXIT_FAILURE, "timer_arm: setitimer"); + if (timer_settime(itimer_monotonic, 0, &its, NULL) == -1) { + err(EXIT_FAILURE, "timer_start: timer_settime"); } _native_syscall_leave(); } @@ -183,13 +193,13 @@ void timer_stop(tim_t dev) DEBUG("%s\n", __func__); _native_syscall_enter(); - struct itimerval zero = {0}; - if (real_setitimer(ITIMER_REAL, &zero, &itv) == -1) { - err(EXIT_FAILURE, "timer_arm: setitimer"); + struct itimerspec zero = {0}; + if (timer_settime(itimer_monotonic, 0, &zero, &its) == -1) { + err(EXIT_FAILURE, "timer_stop: timer_settime"); } _native_syscall_leave(); - DEBUG("time left: %lu.%06lu\n", itv.it_value.tv_sec, itv.it_value.tv_usec); + DEBUG("time left: %lu.%09lu\n", (unsigned long)its.it_value.tv_sec, its.it_value.tv_nsec); } unsigned int timer_read(tim_t dev) From 50b841e1546233f24c9d92772c2a2a127c6ecc7c Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 26 Oct 2023 11:12:58 +0200 Subject: [PATCH 03/85] cpu/native: drop unused `real_setitimer` --- cpu/native/syscalls.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/cpu/native/syscalls.c b/cpu/native/syscalls.c index 6b4dcaaf8f..82de52e303 100644 --- a/cpu/native/syscalls.c +++ b/cpu/native/syscalls.c @@ -88,8 +88,6 @@ int (*real_pause)(void); int (*real_pipe)(int[2]); int (*real_select)(int nfds, ...); int (*real_poll)(struct pollfd *fds, ...); -int (*real_setitimer)(int which, const struct itimerval - *restrict value, struct itimerval *restrict ovalue); int (*real_setsid)(void); int (*real_setsockopt)(int socket, ...); int (*real_socket)(int domain, int type, int protocol); @@ -535,7 +533,6 @@ void _native_init_syscalls(void) *(void **)(&real_dup2) = dlsym(RTLD_NEXT, "dup2"); *(void **)(&real_select) = dlsym(RTLD_NEXT, "select"); *(void **)(&real_poll) = dlsym(RTLD_NEXT, "poll"); - *(void **)(&real_setitimer) = dlsym(RTLD_NEXT, "setitimer"); *(void **)(&real_setsid) = dlsym(RTLD_NEXT, "setsid"); *(void **)(&real_setsockopt) = dlsym(RTLD_NEXT, "setsockopt"); *(void **)(&real_socket) = dlsym(RTLD_NEXT, "socket"); From edf47f6f6084b939059391f25f332e56163f765e Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Tue, 7 Nov 2023 13:05:56 +0100 Subject: [PATCH 04/85] ci: build github merge train branches with murdock --- .murdock.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.murdock.yml b/.murdock.yml index e0f2ac2572..8fd2da818c 100644 --- a/.murdock.yml +++ b/.murdock.yml @@ -3,6 +3,8 @@ push: # these two enable potential bors support: - '^staging$' - '^trying$' + # github merge trains: + - '^gh-readonly-queue/' pr: enable_comments: true From 1e7f488448d7a0a214e17b6202b472563787e7cd Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 8 Nov 2023 08:03:14 +0100 Subject: [PATCH 05/85] .murdock: adapt bors to merge queue --- .murdock | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.murdock b/.murdock index 321c1d150f..d1147e33a9 100755 --- a/.murdock +++ b/.murdock @@ -178,19 +178,27 @@ check_label() { return $? } -# this function returns 0 when this is build is building one of the two bors -# branches ("staging" or "trying", 1 otherwise. -is_bors_build() { - test "${CI_BUILD_BRANCH}" = "staging" || test "${CI_BUILD_BRANCH}" = "trying" +# true if "$2" starts with "$1", false otherwise +startswith() { + case "${2}" in + ${1}*) true ;; + *) false ;; + esac } -# Set CI_BASE_COMMIT in case of a bors build. -# This assumes that bors always adds a single merge commit (possibly itself +# this function returns 0 when this is build is building one of the two merge +# queue branches ("staging" or "trying", 1 otherwise. +is_merge_queue_build() { + startswith "gh-readonly-queue/" "${CI_BUILD_BRANCH}" +} + +# Set CI_BASE_COMMIT in case of a merge queue build. +# This assumes that merge queue always adds a single merge commit (possibly itself # including multiple merges for PRs) on the base branch. # That git command outputs the hash of the second merge commit (the one before # the topmost merge), which should be the base branch HEAD. # (CI_BASE_COMMIT is used by `can_fast_ci_run.py` to only build changes.) -if test -z "${CI_BASE_COMMIT}" && is_bors_build ; then +if test -z "${CI_BASE_COMMIT}" && is_merge_queue_build; then previous_merge="$(git log --merges --max-count 1 --skip 1 --pretty=format:%H)" export CI_BASE_COMMIT="${previous_merge}" fi @@ -216,8 +224,8 @@ fi # configurations. if [ -z "${QUICK_BUILD}" ]; then export QUICK_BUILD=0 - if is_bors_build ; then - # always do full build for bors' branches + if is_merge_queue_build; then + # always do full build for merge queue' branches true elif [ ${FULL_BUILD} -eq 1 ]; then # full build if building nightly or full build requested by label @@ -323,14 +331,6 @@ error() { exit 1 } -# true if "$2" starts with "$1", false otherwise -startswith() { - case "${2}" in - ${1}*) true ;; - *) false ;; - esac -} - # if MURDOCK_HOOK is set, this function will execute it and pass on all it's # parameters. should the hook script exit with negative exit code, hook() makes # this script exit with error, too. From d41e368bf7895785ce748c4e940103fd7922f4c5 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 8 Nov 2023 11:31:07 +0100 Subject: [PATCH 06/85] .github/workflows/*: Allow merge_groups and prs to be required --- .github/workflows/check-commits.yml | 9 +++++++++ .github/workflows/check-labels.yml | 3 +++ .github/workflows/labeler.yml | 2 ++ .github/workflows/static-test.yml | 1 + .github/workflows/tools-buildtest.yml | 1 + .github/workflows/tools-test.yml | 2 ++ 6 files changed, 18 insertions(+) diff --git a/.github/workflows/check-commits.yml b/.github/workflows/check-commits.yml index d294f37836..c4d15bacf7 100644 --- a/.github/workflows/check-commits.yml +++ b/.github/workflows/check-commits.yml @@ -2,6 +2,8 @@ name: check-commits on: pull_request: types: [opened, reopened, synchronize] + merge_group: + jobs: check-commits: runs-on: ubuntu-latest @@ -24,3 +26,10 @@ jobs: - name: Run checks run: | ./dist/tools/${{ matrix.check }}/check.sh "${{ github.base_ref }}" + check-commits-success: + needs: check-commits + if: github.event_name != 'merge_group' + runs-on: ubuntu-latest + steps: + - name: check-commits succeeded + run: exit 0 diff --git a/.github/workflows/check-labels.yml b/.github/workflows/check-labels.yml index 0639b4c97c..167ab44c58 100644 --- a/.github/workflows/check-labels.yml +++ b/.github/workflows/check-labels.yml @@ -4,8 +4,11 @@ on: types: [opened, reopened, labeled, unlabeled, synchronize] pull_request_review: types: [submitted, dismissed] + merge_group: + jobs: check-labels: + if: github.event_name != 'merge_group' runs-on: ubuntu-latest steps: - uses: RIOT-OS/check-labels-action@v1.1.1 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 0523287795..7b4efd24fd 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -3,10 +3,12 @@ name: pr-labeler on: pull_request_target: types: [opened, synchronize, reopened] + merge_group: jobs: triage: runs-on: ubuntu-latest + if: github.event_name != 'merge_group' steps: - uses: actions/labeler@main with: diff --git a/.github/workflows/static-test.yml b/.github/workflows/static-test.yml index 81b2a51786..c9f10c6fa8 100644 --- a/.github/workflows/static-test.yml +++ b/.github/workflows/static-test.yml @@ -13,6 +13,7 @@ on: pull_request: branches: - '*' + merge_group: jobs: static-tests: diff --git a/.github/workflows/tools-buildtest.yml b/.github/workflows/tools-buildtest.yml index 35bc9e8dde..d16a3481a2 100644 --- a/.github/workflows/tools-buildtest.yml +++ b/.github/workflows/tools-buildtest.yml @@ -11,6 +11,7 @@ on: pull_request: branches: - '*' + merge_group: jobs: tools-build: diff --git a/.github/workflows/tools-test.yml b/.github/workflows/tools-test.yml index 8f6226de81..5d5f4fe82c 100644 --- a/.github/workflows/tools-test.yml +++ b/.github/workflows/tools-test.yml @@ -9,9 +9,11 @@ on: pull_request: branches: - '*' + merge_group: jobs: python-tests: + if: github.event_name != 'merge_group' runs-on: ubuntu-latest steps: - uses: actions/checkout@main From f849816686192e9d42d749e726b938a2b6fd08fb Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Wed, 8 Nov 2023 13:23:51 +0100 Subject: [PATCH 07/85] ci: workflows: static-test: fix CI_BASE_BRANCH for merge_groups --- .github/workflows/static-test.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/static-test.yml b/.github/workflows/static-test.yml index c9f10c6fa8..65b4030df0 100644 --- a/.github/workflows/static-test.yml +++ b/.github/workflows/static-test.yml @@ -22,11 +22,18 @@ jobs: - uses: actions/checkout@main with: fetch-depth: 0 + - name: set CI_BASE_BRANCH + run: | + if [ -n "${{ github.base_ref }}" ]; then + echo "CI_BASE_BRANCH=${{ github.base_ref }}" >> $GITHUB_ENV + elif [ -n "${{ github.event.merge_group.base_ref }}" ]; then + echo "CI_BASE_BRANCH=${{ github.event.merge_group.base_ref }}" | sed s.=refs/heads/.=. >> $GITHUB_ENV + fi - name: Setup git run: | - # Note: ${{ github.base_ref }} is empty when not in a PR - if [ -n "${{ github.base_ref }}" ]; then - git fetch origin ${{ github.base_ref }}:${{ github.base_ref }} --no-tags + # Note: CI_BASE_BRANCH is empty when not in a PR + if [ -n "${CI_BASE_BRANCH}" ]; then + git fetch origin ${CI_BASE_BRANCH}:${CI_BASE_BRANCH} --no-tags else git config diff.renameLimit 16384 fi @@ -35,9 +42,9 @@ jobs: run: docker pull riot/static-test-tools:latest - name: Run static-tests run: | - # Note: ${{ github.base_ref }} is empty when not in a PR + # Note: ${CI_BASE_BRANCH} is empty when not in a PR docker run --rm \ - -e CI_BASE_BRANCH=${{ github.base_ref }} \ + -e CI_BASE_BRANCH \ -e GITHUB_RUN_ID=${GITHUB_RUN_ID} \ -v $(pwd):/data/riotbuild \ riot/static-test-tools:latest \ From a6c2bb8ecf90cb4fc9dac6ba192682782233bf99 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 9 Nov 2023 09:28:15 +0100 Subject: [PATCH 08/85] .murdock: drop bors CI_BASE_BRANCH logic --- .murdock | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.murdock b/.murdock index d1147e33a9..3484193e32 100755 --- a/.murdock +++ b/.murdock @@ -192,17 +192,6 @@ is_merge_queue_build() { startswith "gh-readonly-queue/" "${CI_BUILD_BRANCH}" } -# Set CI_BASE_COMMIT in case of a merge queue build. -# This assumes that merge queue always adds a single merge commit (possibly itself -# including multiple merges for PRs) on the base branch. -# That git command outputs the hash of the second merge commit (the one before -# the topmost merge), which should be the base branch HEAD. -# (CI_BASE_COMMIT is used by `can_fast_ci_run.py` to only build changes.) -if test -z "${CI_BASE_COMMIT}" && is_merge_queue_build; then - previous_merge="$(git log --merges --max-count 1 --skip 1 --pretty=format:%H)" - export CI_BASE_COMMIT="${previous_merge}" -fi - # fullbuild logic # non-full-builds are those where can_fast_ci_run might reduce the build # automatically From 282c77c84c05b31413c7d61f599a5218e7633401 Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Thu, 9 Nov 2023 13:29:52 +0100 Subject: [PATCH 09/85] .murdock: clarify `is_merge_queue_build()` help text --- .murdock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.murdock b/.murdock index 3484193e32..b625b96ddc 100755 --- a/.murdock +++ b/.murdock @@ -186,8 +186,7 @@ startswith() { esac } -# this function returns 0 when this is build is building one of the two merge -# queue branches ("staging" or "trying", 1 otherwise. +# this function returns 0 when this build is building a merge queue branch. is_merge_queue_build() { startswith "gh-readonly-queue/" "${CI_BUILD_BRANCH}" } From 03e2537be5b6cd3bfba473c202c1dab8622f649f Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 9 Nov 2023 16:46:09 +0100 Subject: [PATCH 10/85] boards/common/esp32x: fix doxygen group --- boards/common/esp32x/doc.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/boards/common/esp32x/doc.txt b/boards/common/esp32x/doc.txt index 66c6d33599..875728f9f4 100644 --- a/boards/common/esp32x/doc.txt +++ b/boards/common/esp32x/doc.txt @@ -8,9 +8,10 @@ /** * @defgroup boards_common_esp32x ESP32x Common + * @ingroup boards_common * @brief Definitions and configurations that are common for - * all ESP32 boards. + * all ESP32x boards. * - * For detailed information about the ESP32, configuring and compiling RIOT - * for ESP32 boards, please refer \ref esp32_riot. + * For detailed information about ESP32x SoCs, configuring and compiling RIOT + * for ESP32x boards, please refer \ref esp32_riot. */ From 3889592db5df8cad7e4ced7dcaa16b2ee213c0f1 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 7 Nov 2023 16:28:19 +0100 Subject: [PATCH 11/85] drivers/mtd_sdmmc: support mounting ext2/3/4 filesystems --- drivers/mtd_sdmmc/mtd_sdmmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd_sdmmc/mtd_sdmmc.c b/drivers/mtd_sdmmc/mtd_sdmmc.c index ab0881af0d..615704726d 100644 --- a/drivers/mtd_sdmmc/mtd_sdmmc.c +++ b/drivers/mtd_sdmmc/mtd_sdmmc.c @@ -250,5 +250,8 @@ MTD_SDMMC_DEV(0, CONFIG_SDMMC_GENERIC_MTD_OFFSET); #ifdef MODULE_FATFS_VFS MTD_SDMMC_DEV_FS(0, CONFIG_SDMMC_GENERIC_MTD_OFFSET, fatfs); #endif +#ifdef MODULE_LWEXT4 +MTD_SDMMC_DEV_FS(0, CONFIG_SDMMC_GENERIC_MTD_OFFSET, lwext4); +#endif #endif From 616932f24b14643778b216c3340b0237cbe179bb Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 7 Nov 2023 16:29:16 +0100 Subject: [PATCH 12/85] pkg/lwext4: don't include debug strings by default --- pkg/lwext4/Makefile.include | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/lwext4/Makefile.include b/pkg/lwext4/Makefile.include index 3f50cff979..02d64291a0 100644 --- a/pkg/lwext4/Makefile.include +++ b/pkg/lwext4/Makefile.include @@ -4,6 +4,7 @@ DIRS += $(RIOTPKG)/lwext4/fs CFLAGS += -DCONFIG_USE_DEFAULT_CFG=1 CFLAGS += -DCONFIG_HAVE_OWN_OFLAGS=0 +CFLAGS += -DCONFIG_DEBUG_PRINTF=0 # select ext2/3/4 feature level based on module name ifneq (,$(filter lwext4_vfs,$(USEMODULE))) From f731ec7bff5ff53cff043970dc0d58b4fd0a3654 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 7 Nov 2023 16:29:43 +0100 Subject: [PATCH 13/85] sys/malloc_tracing: fix calloc printouts --- sys/malloc_thread_safe/malloc_wrappers.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sys/malloc_thread_safe/malloc_wrappers.c b/sys/malloc_thread_safe/malloc_wrappers.c index cf226cc402..3210b28093 100644 --- a/sys/malloc_thread_safe/malloc_wrappers.c +++ b/sys/malloc_thread_safe/malloc_wrappers.c @@ -53,7 +53,7 @@ void __attribute__((used)) __wrap_free(void *ptr) { if (IS_USED(MODULE_MALLOC_TRACING)) { uinttxtptr_t pc = cpu_get_caller_pc(); - printf("free(%p) @0x%" PRIxTXTPTR ")\n", ptr, pc); + printf("free(%p) @ 0x%" PRIxTXTPTR ")\n", ptr, pc); } assert(!irq_is_in()); mutex_lock(&_lock); @@ -73,7 +73,7 @@ void * __attribute__((used)) __wrap_calloc(size_t nmemb, size_t size) size_t total_size; if (__builtin_mul_overflow(nmemb, size, &total_size)) { if (IS_USED(MODULE_MALLOC_TRACING)) { - printf("calloc(%u, %u) @0x%" PRIxTXTPTR " overflowed\n", + printf("calloc(%u, %u) @ 0x%" PRIxTXTPTR " overflowed\n", (unsigned)nmemb, (unsigned)size, pc); } return NULL; @@ -87,7 +87,7 @@ void * __attribute__((used)) __wrap_calloc(size_t nmemb, size_t size) } if (IS_USED(MODULE_MALLOC_TRACING)) { - printf("calloc(%u, %u) @0x%" PRIxTXTPTR " returned %p\n", + printf("calloc(%u, %u) @ 0x%" PRIxTXTPTR " returned %p\n", (unsigned)nmemb, (unsigned)size, pc, res); } From e2cee4255dfb3bd7668a4a70511742266d92bec6 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 7 Nov 2023 19:08:39 +0100 Subject: [PATCH 14/85] pkg/lwext4: don't consider fs mounted if recover fails --- pkg/lwext4/fs/lwext4_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lwext4/fs/lwext4_fs.c b/pkg/lwext4/fs/lwext4_fs.c index 47e1989156..56abfdb9ea 100644 --- a/pkg/lwext4/fs/lwext4_fs.c +++ b/pkg/lwext4/fs/lwext4_fs.c @@ -214,7 +214,6 @@ static int _mount(vfs_mount_t *mountp) } mp->os_locks = &_lwext4_os_lock; - mp->mounted = true; res = ext4_recover(fs->mp.name); if (res != EOK && res != ENOTSUP) { @@ -228,6 +227,7 @@ static int _mount(vfs_mount_t *mountp) return -res; } + mp->mounted = true; ext4_cache_write_back(fs->mp.name, 1); return -res; From 85cebf332def0fdb2703bbec443db7748f9c790e Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Mon, 6 Nov 2023 13:46:23 +0100 Subject: [PATCH 15/85] dist/tools/uf2: Ignore the .json file --- dist/tools/uf2/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/tools/uf2/.gitignore b/dist/tools/uf2/.gitignore index f791596322..863cf29443 100644 --- a/dist/tools/uf2/.gitignore +++ b/dist/tools/uf2/.gitignore @@ -1 +1,2 @@ uf2conv.py +uf2families.json From 4b88a420adf4819d54d38bc7f168ce0c921c8d69 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 9 Nov 2023 11:05:54 +0100 Subject: [PATCH 16/85] drivers/pcf857x: minor cleanup - make sure format specifiers match variable arguments in debug statements - drop unused headers --- drivers/pcf857x/pcf857x.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/pcf857x/pcf857x.c b/drivers/pcf857x/pcf857x.c index a37d21b394..aa4744395d 100644 --- a/drivers/pcf857x/pcf857x.c +++ b/drivers/pcf857x/pcf857x.c @@ -19,10 +19,6 @@ #include "pcf857x.h" -#include "irq.h" -#include "log.h" -#include "thread.h" - #if IS_USED(MODULE_PCF857X_IRQ) #include "event/thread.h" #endif @@ -154,7 +150,7 @@ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode) assert(dev != NULL); assert(pin < dev->pin_num); - DEBUG_DEV("pin=%u mode=%u", dev, pin, mode); + DEBUG_DEV("pin=%u mode=%u", dev, (unsigned)pin, (unsigned)mode); /* * Since the LOW output is the only actively driven level possible with @@ -164,7 +160,7 @@ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode) * with the weak pull-up to emulate them. */ switch (mode) { - case GPIO_IN_PD: DEBUG_DEV("gpio mode GPIO_IN_PD not supported", dev, mode); + case GPIO_IN_PD: DEBUG_DEV("gpio mode GPIO_IN_PD not supported", dev); return -PCF857X_ERROR_INV_MODE; case GPIO_OUT: dev->modes &= ~(1 << pin); /* set mode bit to 0 */ dev->out &= ~(1 << pin); /* set output bit to 0 */ @@ -265,7 +261,7 @@ int pcf857x_gpio_read(pcf857x_t *dev, gpio_t pin) assert(dev != NULL); assert(pin < dev->pin_num); - DEBUG_DEV("pin=%u", dev, pin); + DEBUG_DEV("pin=%u", dev, (unsigned)pin); /* * If we use the interrupt, we always have an up-to-date input snapshot @@ -286,7 +282,7 @@ void pcf857x_gpio_write(pcf857x_t *dev, gpio_t pin, int value) assert(dev != NULL); assert(pin < dev->pin_num); - DEBUG_DEV("pin=%u value=%d", dev, pin, value); + DEBUG_DEV("pin=%u value=%d", dev, (unsigned)pin, value); /* set pin bit value */ if (value) { @@ -319,19 +315,19 @@ void pcf857x_gpio_write(pcf857x_t *dev, gpio_t pin, int value) void pcf857x_gpio_clear(pcf857x_t *dev, gpio_t pin) { - DEBUG_DEV("pin=%u", dev, pin); + DEBUG_DEV("pin=%u", dev, (unsigned)pin); return pcf857x_gpio_write(dev, pin, 0); } void pcf857x_gpio_set(pcf857x_t *dev, gpio_t pin) { - DEBUG_DEV("pin=%u", dev, pin); + DEBUG_DEV("pin=%u", dev, (unsigned)pin); return pcf857x_gpio_write(dev, pin, 1); } void pcf857x_gpio_toggle(pcf857x_t *dev, gpio_t pin) { - DEBUG_DEV("pin=%u", dev, pin); + DEBUG_DEV("pin=%u", dev, (unsigned)pin); return pcf857x_gpio_write(dev, pin, (dev->out & (1 << pin)) ? 0 : 1); } From d6797867fbf0ba47e554ac4969f71b6c971ca2bc Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 10 Nov 2023 11:12:20 +0100 Subject: [PATCH 17/85] sys/flash_utils: Fix signature in documentation The function signature in the Doxygen generated doc was incorrect. Given that the implementation does have the correct signature, the impact may not be that large. Still, incorrect doc is confusing, so let's fix it. --- sys/include/flash_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/include/flash_utils.h b/sys/include/flash_utils.h index dc4721f1c7..44f54b8991 100644 --- a/sys/include/flash_utils.h +++ b/sys/include/flash_utils.h @@ -207,7 +207,7 @@ void flash_puts(FLASH_ATTR const char *flash); * @param[in] n number of bytes to copy * */ -void * flash_memcpy(void *dest, FLASH_ATTR const char *src, size_t n); +void * flash_memcpy(void *dest, FLASH_ATTR const void *src, size_t n); #elif !IS_ACTIVE(HAS_FLASH_UTILS_ARCH) # define FLASH_ATTR # define PRIsflash "s" From 2421919295e4d13fff2c9e2a8bca25d0769ddd0a Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 3 Nov 2023 15:21:28 +0100 Subject: [PATCH 18/85] periph/adc: introduce periph_adc_continous --- drivers/include/periph/adc.h | 27 +++++++++++++++++++++++++++ kconfigs/Kconfig.features | 5 +++++ 2 files changed, 32 insertions(+) diff --git a/drivers/include/periph/adc.h b/drivers/include/periph/adc.h index b6eb4de713..c91d05c12e 100644 --- a/drivers/include/periph/adc.h +++ b/drivers/include/periph/adc.h @@ -128,6 +128,33 @@ int adc_init(adc_t line); */ int32_t adc_sample(adc_t line, adc_res_t res); +/** + * @brief Configure the ADC with a given resolution for continuous sampling + * + * @note requires the `periph_adc_continuous` feature + * + * @param[in] res resolution to use for conversion + */ +void adc_continuous_begin(adc_res_t res); + +/** + * @brief Sample an ADC line without powering off the ADC afterward + * + * @note requires the `periph_adc_continuous` feature + * + * @brief Sample a value from the given ADC line + * + * @return the sampled value on success + */ +int32_t adc_continuous_sample(adc_t line); + +/** + * @brief Disable the ADC to save power + * + * @note requires the `periph_adc_continuous` feature + */ +void adc_continuous_stop(void); + #ifdef __cplusplus } #endif diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index ab84f587a1..cb824d97b1 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -188,6 +188,11 @@ config HAS_PERIPH_ADC help Indicates that an ADC peripheral is present. +config HAS_PERIPH_ADC_CONTINUOUS + bool + help + Indicates that an ADC peripheral can be left on between measurements. + config HAS_PERIPH_CAN bool help From b289d69b4f5cf2fbb2dda92b067761dbc12fa1be Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 3 Nov 2023 15:11:40 +0100 Subject: [PATCH 19/85] cpu/sam0_common: implement periph_adc_continous --- cpu/sam0_common/Kconfig | 1 + cpu/sam0_common/Makefile.features | 1 + cpu/sam0_common/periph/adc.c | 155 +++++++++++++++++++++++------- 3 files changed, 123 insertions(+), 34 deletions(-) diff --git a/cpu/sam0_common/Kconfig b/cpu/sam0_common/Kconfig index 2f9b333e95..fe4c1afc42 100644 --- a/cpu/sam0_common/Kconfig +++ b/cpu/sam0_common/Kconfig @@ -8,6 +8,7 @@ config CPU_COMMON_SAM0 bool select HAS_PERIPH_CPUID + select HAS_PERIPH_ADC_CONTINUOUS select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 2d2035c20d..2f01bb135e 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -7,6 +7,7 @@ ifeq (,$(filter $(CPU_MODELS_WITHOUT_DMA),$(CPU_MODEL))) FEATURES_PROVIDED += periph_dma endif +FEATURES_PROVIDED += periph_adc_continuous FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_in_address_space FEATURES_PROVIDED += periph_flashpage_pagewise diff --git a/cpu/sam0_common/periph/adc.c b/cpu/sam0_common/periph/adc.c index 59c236a7e8..cf65a41605 100644 --- a/cpu/sam0_common/periph/adc.c +++ b/cpu/sam0_common/periph/adc.c @@ -49,16 +49,6 @@ static int _adc_configure(Adc *dev, adc_res_t res); static mutex_t _lock = MUTEX_INIT; -static inline void _prep(void) -{ - mutex_lock(&_lock); -} - -static inline void _done(void) -{ - mutex_unlock(&_lock); -} - static inline void _wait_syncbusy(Adc *dev) { #ifdef ADC_STATUS_SYNCBUSY @@ -263,7 +253,7 @@ int adc_init(adc_t line) const uint8_t adc = 0; #endif - _prep(); + mutex_lock(&_lock); uint8_t muxpos = (adc_channels[line].inputctrl & ADC_INPUTCTRL_MUXPOS_Msk) >> ADC_INPUTCTRL_MUXPOS_Pos; @@ -284,34 +274,44 @@ int adc_init(adc_t line) gpio_init_mux(sam0_adc_pins[adc][muxneg], GPIO_MUX_B); } - _done(); + mutex_unlock(&_lock); return 0; } -int32_t adc_sample(adc_t line, adc_res_t res) +static Adc *_dev(adc_t line) { - if (line >= ADC_NUMOF) { - DEBUG("adc: line arg not applicable\n"); - return -1; - } - /* The SAMD5x/SAME5x family has two ADCs: ADC0 and ADC1. */ #ifdef ADC0 - Adc *dev = adc_channels[line].dev; + return adc_channels[line].dev; #else - Adc *dev = ADC; + (void)line; + return ADC; #endif +} - bool diffmode = adc_channels[line].inputctrl & ADC_INPUTCTRL_DIFFMODE; - - _prep(); - - if (_adc_configure(dev, res) != 0) { - _done(); - DEBUG("adc: configuration failed\n"); - return -1; +static Adc *_adc(uint8_t dev) +{ + /* The SAMD5x/SAME5x family has two ADCs: ADC0 and ADC1. */ +#ifdef ADC0 + switch (dev) { + case 0: + return ADC0; + case 1: + return ADC1; + default: + return NULL; } +#else + (void)dev; + return ADC; +#endif +} + +static int32_t _sample(adc_t line) +{ + Adc *dev = _dev(line); + bool diffmode = adc_channels[line].inputctrl & ADC_INPUTCTRL_DIFFMODE; dev->INPUTCTRL.reg = ADC_GAIN_FACTOR_DEFAULT | adc_channels[line].inputctrl @@ -319,7 +319,6 @@ int32_t adc_sample(adc_t line, adc_res_t res) #ifdef ADC_CTRLB_DIFFMODE dev->CTRLB.bit.DIFFMODE = diffmode; #endif - _wait_syncbusy(dev); /* Start the conversion */ @@ -331,9 +330,6 @@ int32_t adc_sample(adc_t line, adc_res_t res) uint16_t sample = dev->RESULT.reg; int result; - _adc_poweroff(dev); - _done(); - /* in differential mode we lose one bit for the sign */ if (diffmode) { result = 2 * (int16_t)sample; @@ -341,11 +337,102 @@ int32_t adc_sample(adc_t line, adc_res_t res) result = sample; } + return result; +} + +static uint8_t _shift_from_res(adc_res_t res) +{ /* 16 bit mode is implemented as oversampling */ if ((res & 0x3) == 1) { /* ADC does automatic right shifts beyond 16 samples */ - result <<= (4 - MIN(4, res >> 2)); + return 4 - MIN(4, res >> 2); + } + return 0; +} + +static void _get_adcs(bool *adc0, bool *adc1) +{ +#ifndef ADC1 + *adc0 = true; + *adc1 = false; + return; +#else + for (unsigned i = 0; i < ADC_NUMOF; ++i) { + if (adc_channels[i].dev == ADC0) { + *adc0 = true; + } else if (adc_channels[i].dev == ADC1) { + *adc1 = true; + } + } +#endif +} + +static uint8_t _shift; +void adc_continuous_begin(adc_res_t res) +{ + bool adc0, adc1; + _get_adcs(&adc0, &adc1); + + mutex_lock(&_lock); + + if (adc0) { + _adc_configure(_adc(0), res); + } + if (adc1) { + _adc_configure(_adc(1), res); } - return result; + _shift = _shift_from_res(res); +} + +int32_t adc_continuous_sample(adc_t line) +{ + int val; + assert(line < ADC_NUMOF); + + mutex_lock(&_lock); + val = _sample(line) << _shift; + mutex_unlock(&_lock); + + return val; +} + +void adc_continuous_stop(void) +{ + bool adc0, adc1; + _get_adcs(&adc0, &adc1); + + if (adc0) { + _adc_poweroff(_adc(0)); + } + if (adc1) { + _adc_poweroff(_adc(1)); + } + + mutex_unlock(&_lock); +} + +int32_t adc_sample(adc_t line, adc_res_t res) +{ + if (line >= ADC_NUMOF) { + DEBUG("adc: line arg not applicable\n"); + return -1; + } + + mutex_lock(&_lock); + + Adc *dev = _dev(line); + + if (_adc_configure(dev, res) != 0) { + DEBUG("adc: configuration failed\n"); + mutex_unlock(&_lock); + return -1; + } + + int val = _sample(line) << _shift_from_res(res); + + _adc_poweroff(dev); + mutex_unlock(&_lock); + + return val; } From d3dfd4baf064bcd0ea1440e5edfab89b67e3f07a Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 3 Nov 2023 15:16:01 +0100 Subject: [PATCH 20/85] periph/adc_continous: add test for ADC continous API --- tests/periph/adc_continuous/Makefile | 9 ++++ tests/periph/adc_continuous/Makefile.ci | 3 ++ tests/periph/adc_continuous/README.md | 13 ++++++ tests/periph/adc_continuous/main.c | 58 +++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 tests/periph/adc_continuous/Makefile create mode 100644 tests/periph/adc_continuous/Makefile.ci create mode 100644 tests/periph/adc_continuous/README.md create mode 100644 tests/periph/adc_continuous/main.c diff --git a/tests/periph/adc_continuous/Makefile b/tests/periph/adc_continuous/Makefile new file mode 100644 index 0000000000..136730c9e0 --- /dev/null +++ b/tests/periph/adc_continuous/Makefile @@ -0,0 +1,9 @@ +BOARD ?= same54-xpro +include ../Makefile.periph_common + +FEATURES_REQUIRED += periph_adc +FEATURES_REQUIRED += periph_adc_continuous +USEMODULE += ztimer +USEMODULE += ztimer_msec + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph/adc_continuous/Makefile.ci b/tests/periph/adc_continuous/Makefile.ci new file mode 100644 index 0000000000..72db76ccb5 --- /dev/null +++ b/tests/periph/adc_continuous/Makefile.ci @@ -0,0 +1,3 @@ +BOARD_INSUFFICIENT_MEMORY := \ + atmega8 \ + # diff --git a/tests/periph/adc_continuous/README.md b/tests/periph/adc_continuous/README.md new file mode 100644 index 0000000000..7ad2990acf --- /dev/null +++ b/tests/periph/adc_continuous/README.md @@ -0,0 +1,13 @@ +Expected result +=============== +When running this test, you should see the samples of all configured ADC lines +continuously streamed to std-out. + +Background +========== +This test application will initialize each configured ADC lines to sample with +10-bit accuracy. Once configured the application will continuously convert each +available channel and print the conversion results to std-out. + +For verification of the output connect the ADC pins to known voltage levels +and compare the output. diff --git a/tests/periph/adc_continuous/main.c b/tests/periph/adc_continuous/main.c new file mode 100644 index 0000000000..4799aa1c40 --- /dev/null +++ b/tests/periph/adc_continuous/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2014-2015 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for peripheral ADC drivers + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "ztimer.h" +#include "periph/adc.h" + +#define RES ADC_RES_10BIT +#define DELAY_MS 100U + +int main(void) +{ + int sample = 0; + + puts("\nRIOT ADC peripheral driver test\n"); + puts("This test will sample all available ADC lines once every 100ms with\n" + "a 10-bit resolution and print the sampled results to STDIO\n\n"); + + /* initialize all available ADC lines */ + for (unsigned i = 0; i < ADC_NUMOF; i++) { + if (adc_init(ADC_LINE(i)) < 0) { + printf("Initialization of ADC_LINE(%u) failed\n", i); + return 1; + } else { + printf("Successfully initialized ADC_LINE(%u)\n", i); + } + } + + adc_continuous_begin(RES); + while (1) { + for (unsigned i = 0; i < ADC_NUMOF; i++) { + sample = adc_continuous_sample(ADC_LINE(i)); + printf("ADC_LINE(%u): %i\n", i, sample); + } + ztimer_sleep(ZTIMER_MSEC, DELAY_MS); + } + + adc_continuous_stop(); + return 0; +} From 75f10a572512b119c339353fa696b362bfab49fa Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 3 Nov 2023 18:06:19 +0100 Subject: [PATCH 21/85] gnrc/ipv6/nib: don't do multicast neighbor solicitation on 6LoWPAN --- sys/net/gnrc/network_layer/ipv6/nib/nib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/net/gnrc/network_layer/ipv6/nib/nib.c b/sys/net/gnrc/network_layer/ipv6/nib/nib.c index 03997aeed2..5fe55a0529 100644 --- a/sys/net/gnrc/network_layer/ipv6/nib/nib.c +++ b/sys/net/gnrc/network_layer/ipv6/nib/nib.c @@ -1397,7 +1397,8 @@ static bool _resolve_addr(const ipv6_addr_t *dst, gnrc_netif_t *netif, return false; } - if (IS_ACTIVE(CONFIG_GNRC_IPV6_NIB_ARSM)) { + /* don't do multicast address resolution on 6lo */ + if (!gnrc_netif_is_6ln(netif)) { _probe_nbr(entry, reset); } From d97ea9dbd3ccd9818512279ca774462c9d25b499 Mon Sep 17 00:00:00 2001 From: krzysztof-cabaj Date: Fri, 10 Nov 2023 14:05:14 +0100 Subject: [PATCH 22/85] boards/nucleo-f070rb: SPI config: Kconfig, Makefile.features, periph_conf.h --- boards/nucleo-f070rb/Kconfig | 2 ++ boards/nucleo-f070rb/Makefile.features | 2 ++ boards/nucleo-f070rb/include/periph_conf.h | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/boards/nucleo-f070rb/Kconfig b/boards/nucleo-f070rb/Kconfig index d7247820d2..d1270af5a8 100644 --- a/boards/nucleo-f070rb/Kconfig +++ b/boards/nucleo-f070rb/Kconfig @@ -16,9 +16,11 @@ config BOARD_NUCLEO_F070RB # Put defined MCU peripherals here (in alphabetical order) select HAS_PERIPH_ADC + select HAS_PERIPH_DMA select HAS_PERIPH_I2C select HAS_PERIPH_PWM select HAS_PERIPH_RTC + select HAS_PERIPH_SPI select HAS_PERIPH_TIMER select HAS_PERIPH_UART diff --git a/boards/nucleo-f070rb/Makefile.features b/boards/nucleo-f070rb/Makefile.features index 52347c9e8f..450efe912c 100644 --- a/boards/nucleo-f070rb/Makefile.features +++ b/boards/nucleo-f070rb/Makefile.features @@ -3,12 +3,14 @@ CPU_MODEL = stm32f070rb # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_adc +FEATURES_PROVIDED += periph_dma FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm # For RTC, Nucleos with MB1136 C-02 or MB1136 C-03 -sticker on it have the # required LSE oscillator provided on the X2 slot. # See Nucleo User Manual UM1724 section 5.6.2. FEATURES_PROVIDED += periph_rtc +FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/nucleo-f070rb/include/periph_conf.h b/boards/nucleo-f070rb/include/periph_conf.h index ca117e8eb5..2127ece9ef 100644 --- a/boards/nucleo-f070rb/include/periph_conf.h +++ b/boards/nucleo-f070rb/include/periph_conf.h @@ -148,6 +148,10 @@ static const adc_conf_t adc_config[] = { #define ADC_NUMOF ARRAY_SIZE(adc_config) /** @} */ +static const spi_conf_t spi_config[] = {{},}; + +#define SPI_NUMOF ARRAY_SIZE(spi_config) + #ifdef __cplusplus } #endif From cee7cccfd0e3b418abfd053655c68f5975fa796d Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 10 Nov 2023 11:13:22 +0100 Subject: [PATCH 23/85] cpu/avr8_common/flash_utils: use C and linker for aliases `flash_()` is implemented by `_P()` provided by the AVR libc on AVR targets. Previously, the preprocessor was used to do the aliasing, but this causes issues with LLVM: The signatures of e.g. `printf_P()` expects `const char *`, whereas flash utils expects `FLASH_ATTR const char *`. For GCC this will just implicitly drop the `FLASH_ATTR`, while it requires an explicit cast for LLVM. To implement the explicit cast, `static inline` function wrappers where used instead where possible. But for the variadic functions (e.g. `printf(fmt, ...)`) the linker is used to provide the aliases, as there is no way to pass the variadic functions throw in C. The alternative would be to implement `flash_printf()` by calling `vprintf_P()`, but that increased ROM size quite a bit. Finally, a work around for a bug in Ubuntu's toolchain has been added: An unused function that calls to `printf_P()`, `fprintf_P()` and `snprintf_P()`. Since this function is garbage collected anyway, it has no impact on the generated ELF file. --- cpu/avr8_common/Makefile.include | 4 + cpu/avr8_common/include/flash_utils_arch.h | 78 +++++++++++++++---- .../work_around_for_shitty_ubuntu_toolchain.c | 15 ++++ 3 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 cpu/avr8_common/work_around_for_shitty_ubuntu_toolchain.c diff --git a/cpu/avr8_common/Makefile.include b/cpu/avr8_common/Makefile.include index ce73116c97..295bdea2aa 100644 --- a/cpu/avr8_common/Makefile.include +++ b/cpu/avr8_common/Makefile.include @@ -7,4 +7,8 @@ ifneq (,$(filter printf_float,$(USEMODULE))) LINKFLAGS += -Wl,-u,vfprintf -lprintf_flt -lm endif +# Add aliases for flash_printf, flash_fprintf, flash_snprintf: +LINKFLAGS += -Wl,--defsym=flash_printf=printf_P +LINKFLAGS += -Wl,--defsym=flash_fprintf=fprintf_P +LINKFLAGS += -Wl,--defsym=flash_snprintf=snprintf_P include $(RIOTMAKE)/arch/avr8.inc.mk diff --git a/cpu/avr8_common/include/flash_utils_arch.h b/cpu/avr8_common/include/flash_utils_arch.h index 9663da8b89..43127e65e4 100644 --- a/cpu/avr8_common/include/flash_utils_arch.h +++ b/cpu/avr8_common/include/flash_utils_arch.h @@ -21,7 +21,7 @@ #define FLASH_UTILS_ARCH_H #include -#include +#include #include #ifdef __cplusplus @@ -35,20 +35,68 @@ extern "C" { #define FLASH_ATTR __flash #define PRIsflash "S" #define TO_FLASH(x) __extension__({static FLASH_ATTR const char __c[] = (x); &__c[0];}) -#define flash_strcmp strcmp_P -#define flash_strncmp strncmp_P -#define flash_strlen strlen_P -#define flash_strcpy strcpy_P -#define flash_strncpy strncpy_P -#define flash_printf printf_P -/* avrlibc seemingly forgot to provide vprintf_P(), but vfprintf_P() is there */ -#define flash_vprintf(fmt, arglist) vfprintf_P(stdout, fmt, arglist) -#define flash_fprintf fprintf_P -#define flash_vfprintf vfprintf_P -#define flash_snprintf snprintf_P -#define flash_vsnprintf vsnprintf_P -#define flash_puts puts_P -#define flash_memcpy memcpy_P + +static inline int flash_strcmp(const char *ram, FLASH_ATTR const char *flash) +{ + return strcmp_P(ram, (const char *)flash); +} + +static inline int flash_strncmp(const char *ram, FLASH_ATTR const char *flash, size_t n) +{ + return strncmp_P(ram, (const char *)flash, n); +} + +static inline size_t flash_strlen(FLASH_ATTR const char *flash) +{ + return strlen_P((const char *)flash); +} + +static inline char * flash_strcpy(char *ram, FLASH_ATTR const char *flash) +{ + return strcpy_P(ram, (const char *)flash); +} + +static inline char * flash_strncpy(char *ram, FLASH_ATTR const char *flash, size_t n) +{ + return strncpy_P(ram, (const char *)flash, n); +} + + +static inline int flash_vprintf(FLASH_ATTR const char *flash, va_list args) +{ + /* vprintf_P() is not provided by avr-libc. But vfprintf_P() with + * stdout as stream can be used to implement it */ + return vfprintf_P(stdout, (const char *)flash, args); +} + +static inline int flash_vfprintf(FILE *stream, FLASH_ATTR const char *flash, + va_list args) +{ + return vfprintf_P(stream, (const char *)flash, args); +} + +static inline int flash_vsnprintf(char *buf, size_t buf_len, + FLASH_ATTR const char *flash, va_list args) +{ + return vsnprintf_P(buf, buf_len, (const char *)flash, args); +} + +static inline void flash_puts(FLASH_ATTR const char *flash) +{ + puts_P((const char *)flash); +} + +static inline void * flash_memcpy(void *dest, FLASH_ATTR const void *src, + size_t n) +{ + return memcpy_P(dest, (const void *)src, n); +} + +/* aliases need to be provided by the linker, as passing through va-args is + * not possible */ +int flash_printf(FLASH_ATTR const char *flash, ...); +int flash_fprintf(FILE *stream, FLASH_ATTR const char *flash, ...); +int flash_snprintf(char *buf, size_t buf_len, FLASH_ATTR const char *flash, ...); #endif /* Doxygen */ diff --git a/cpu/avr8_common/work_around_for_shitty_ubuntu_toolchain.c b/cpu/avr8_common/work_around_for_shitty_ubuntu_toolchain.c new file mode 100644 index 0000000000..dfe4535ffc --- /dev/null +++ b/cpu/avr8_common/work_around_for_shitty_ubuntu_toolchain.c @@ -0,0 +1,15 @@ +#include +#include + +/* The outdated linker from Ubuntu's toolchain contains a bug in which it will + * garbage collect symbols referenced only by --defsym= command line options, + * and subsequently complain that the symbols are not defined. Adding other + * references to those symbols from an unused function makes that buggy linker + * happy. Since this function is never used, it will be garbage collected and + * not impact the ROM size. */ +void work_around_for_shitty_ubuntu_toolchain_and_not_expected_to_be_called(void) +{ + printf_P(NULL); + fprintf_P(stdout, NULL); + snprintf_P(NULL, 0, NULL); +} From d7cf39551f87e2619799451613635296851a8b44 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 27 Jun 2023 22:13:32 +0200 Subject: [PATCH 24/85] tests/periph/selftest_shield: add test application This test application makes use of the RIOT Peripheral Selftest Shield, which connects e.g. PWM to ADC or SPI MOSI to SPI MISO, UART TXD to RXD, etc. This provides quick and fully automated self testing capabilities. Please note that the simplicity and ease of use of the hardware comes with a prices: There are whole classes of issues that cannot be detected automatically. This test cannot replace other testing approaches (such as manual testing or PHiLIP on the HiL), but only complement them. --- tests/periph/selftest_shield/Makefile | 40 + .../periph/selftest_shield/Makefile.board.dep | 5 + tests/periph/selftest_shield/README.md | 134 +++ tests/periph/selftest_shield/main.c | 1046 +++++++++++++++++ .../tests-with-config/01-run.py | 18 + tests/periph/selftest_shield/tests/01-run.py | 21 + 6 files changed, 1264 insertions(+) create mode 100644 tests/periph/selftest_shield/Makefile create mode 100644 tests/periph/selftest_shield/Makefile.board.dep create mode 100644 tests/periph/selftest_shield/README.md create mode 100644 tests/periph/selftest_shield/main.c create mode 100755 tests/periph/selftest_shield/tests-with-config/01-run.py create mode 100755 tests/periph/selftest_shield/tests/01-run.py diff --git a/tests/periph/selftest_shield/Makefile b/tests/periph/selftest_shield/Makefile new file mode 100644 index 0000000000..d3166a9b86 --- /dev/null +++ b/tests/periph/selftest_shield/Makefile @@ -0,0 +1,40 @@ +BOARD ?= arduino-due + +include ../Makefile.periph_common + +FEATURES_REQUIRED += arduino_pins +FEATURES_REQUIRED += arduino_shield_uno + +FEATURES_OPTIONAL += arduino_analog +FEATURES_OPTIONAL += arduino_i2c +FEATURES_OPTIONAL += arduino_pwm +FEATURES_OPTIONAL += arduino_shield_isp +FEATURES_OPTIONAL += arduino_spi +FEATURES_OPTIONAL += arduino_uart +FEATURES_OPTIONAL += periph_adc +FEATURES_OPTIONAL += periph_gpio +FEATURES_OPTIONAL += periph_gpio_irq +FEATURES_OPTIONAL += periph_i2c +FEATURES_OPTIONAL += periph_pwm +FEATURES_OPTIONAL += periph_spi +FEATURES_OPTIONAL += periph_timer +FEATURES_OPTIONAL += periph_uart + +STOP_ON_FAILURE ?= 0 +DETAILED_OUTPUT ?= 0 + +include $(RIOTBASE)/Makefile.include + +ifneq ($(MCU),esp32) + # We only need 1 thread (+ the Idle thread on some platforms) and we really + # want this app working on as many boards as possible + CFLAGS += -DMAXTHREADS=2 +else + # ESP32x SoCs uses an extra thread for esp_timer + CFLAGS += -DMAXTHREADS=3 +endif + +CFLAGS += \ + '-DSTOP_ON_FAILURE=$(STOP_ON_FAILURE)' \ + '-DDETAILED_OUTPUT=$(DETAILED_OUTPUT)' \ + # diff --git a/tests/periph/selftest_shield/Makefile.board.dep b/tests/periph/selftest_shield/Makefile.board.dep new file mode 100644 index 0000000000..7d1378f079 --- /dev/null +++ b/tests/periph/selftest_shield/Makefile.board.dep @@ -0,0 +1,5 @@ +ifneq (,$(filter periph_i2c,$(FEATURES_USED))) + ifneq (,$(filter arduino_i2c,$(FEATURES_USED))) + USEMODULE += pcf8574 + endif +endif diff --git a/tests/periph/selftest_shield/README.md b/tests/periph/selftest_shield/README.md new file mode 100644 index 0000000000..8c1706ceff --- /dev/null +++ b/tests/periph/selftest_shield/README.md @@ -0,0 +1,134 @@ +# Peripheral Test Battery using the Peripheral Selftest Shield + +@warning Before you power your board, make sure the shield's VCC voltage + selector is matching the logic level of your board. E.g. having the + VCC selector at 5 V and testing on a 3.3 V logic board with pins that + are not 5 V tolerant, you might damage your board permanently. + +## Quick Start + +0. For the MCU family you want to test, find a compatible board + - the board needs to be mechanically and electrically compatible with + Arduino UNO shields + - The Arduino GPIO pin mapping and ideally mapping of SPI, I2C, ADC, and + PWM needs to be provided +1. Unplug your board (so that it is not powered) +2. Connect the shield +3. Move the VCC selector to match the logic level of the board. + - If unsure, go for 3.3 V + - The nRF52840DK works fine with 3.3 V, despite the logic level being only + 3.0 V +4. Close the loops for the peripherals you want to test be moving the DIP + switches into the `ON` position, leave the untested open by moving it in the + `OFF` position + - Start by enabling all but UART + - If the UART test is actually run and fails, also enable the UART switch + - (Background: If the UART at D0 and D1 is used for stdio, it cannot be + looped and tested) +5. Flash and run the test + - In this directory, run `make BOARD= flash test` + +## Details + +This test application does a full self test of any of the following peripheral +driver, if supported by the board and Arduino I/O mapping is available: + +- `periph_adc` (1) +- `periph_gpio` +- `periph_gpio_irq` +- `periph_i2c` (2) +- `periph_pwm` +- `periph_spi` (3) +- `periph_uart` (4) + +Notes: +1. The ADC input is generated via a 4-bit R-2R resistor ladder and the I2C + attached GPIO extender and/or PWM. If neither is available, no tests are + performed. If both are available, up to three ADC channels are tested: + Those muxed to pins A0, A1, and A2. +2. I2C is indirectly tested by operating the PCF8574 I2C GPIO extender and + observing the result using internal GPIOs connected to the external ones. + This test is only enabled if both `periph_i2c` and `periph_gpio` is + available +3. Correctness of the bit order and the clock phase is not checked due to the + limitations of the loopback mode testing approach. The clock polarity is + validated (at idle level), the correctness of the clock frequency is roughly + tested when `periph_timer` is implemented. +4. UART self testing is skipped when the D0/D1 UART interface is used for + stdio. Correctness of the symbol rate (often incorrectly referred to as + "baudrate") is only checked for plausibility when `periph_timer` is + available. Checking different bits per character, number of stop bits, + parity bit etc. is possible using `periph_timer`, but not yet implemented + here. + +## Debugging Failures + +### Increase Verbosity + +Compile with `make DETAILED_OUTPUT=1` to increase the verbosity of the output +at the cost of increased ROM. When you start seeing link failures due to the +ROM cost, you could disable some of the succeeding tests by blacklisting the +peripheral features unaffected. However, be aware some peripherals are tested +with the help of others; e.g. disabling the ADC will prevent testing the +duty cycle of PWM outputs for correctness. + +### Early Critical Failure + +If the application fails before performing any tests, e.g. the output looks +similar to this: + +``` +START +main(): This is RIOT! (Version: 2023.10-devel-348-gf1c68-peripheral-selftest) +self-testing peripheral drivers +=============================== +CRITICAL FAILURE in tests/periph/selftest_shield/main.c: +``` + +This will indicate a failure to initialize the GPIO extender. Possible reasons +are: + +1. Wrong I2C configuration + - Is the I2C bus (typically in the `periph_conf.h` or in a file included by + `periph_conf.h`) correctly configured? Is it connected to the Arduino UNO + pins D14 / D15? + - Is the `ARDUINO_I2C_UNO` macro correctly defined to the index of the + I2C bus connected Arduino UNO pins D14 / D15 +2. Missing pull-up resistor + - The shield has no pull up resistors on the I2C bus + - All RIOT I2C drivers will enable the internal MCU pull up resistors for + the I2C buses by default. But some MCUs do not have internal pull up + resistors (on some pins) or may not be able to enable them when used in + I2C mode. + - If possible, the I2C driver / I2C board configuration in `periph_conf.h` + should be configured so that robust operation works out of the box without + having to add external pull ups. + - If the board depends on external pulls ups (no suitable internal pull ups + can be used and the board has no external pull ups on D15 / D15), pull + ups need to be added to the board. The easiest is to just plug in an + I2C sensor/EEPROM breakout into the I2C pin socket at J2, as those + breakout boards typically have external pull up resistors integrated. +3. Bug in the I2C driver + - A bug in the I2C driver could cause this + - A logic analyzer connected to the I2C Header (J3), the I2C Socket (J2), + or the I2C Groove Connector (J1) will come in handy +4. Hardware issue + - This is the least likely cause :wink: + +### Repeated Instances of the Same Error + +Many tests are performed in a loop. E.g. some tests are repeated for +`flaky_test_repetitions` (by default `100`), as some tests may pass by chance. +Some tests also iterate over different configurations (such as clock speeds) +and repeat the same tests for each configuration. + +To reduce the noise, you can pass `STOP_ON_FAILURE=1` as environment +variable or as parameter to `make` on compilation to abort the test run on the +first failing test. + +## Configuration + +The test requires no configuration and will by default test all of the above +peripheral drivers for which Arduino I/O mappings exists. To disable a subset +of the tests (e.g. to safe RAM/ROM to trim down the test to a low end board), it +is possible to disable features (`FEATURES_BLACKLIST += periph_`). diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c new file mode 100644 index 0000000000..9450968d4f --- /dev/null +++ b/tests/periph/selftest_shield/main.c @@ -0,0 +1,1046 @@ +/* + * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the Peripheral GPIO Low-Level API + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#include "arduino_iomap.h" +#include "macros/units.h" +#include "macros/utils.h" +#include "modules.h" +#include "pcf857x.h" +#include "periph/adc.h" +#include "periph/gpio.h" +#include "periph/pwm.h" +#include "periph/spi.h" +#include "periph/timer.h" +#include "periph/uart.h" +#include "stdio_uart.h" /* for STDIO_UART_DEV */ + +/* BEGIN: controls of the behavior of the testing app: */ +#define ENABLE_DEBUG 1 +#include "debug.h" + +#ifndef STOP_ON_FAILURE +#define STOP_ON_FAILURE 0 +#endif + +#ifndef DETAILED_OUTPUT +#define DETAILED_OUTPUT 0 +#endif +/* END: controls of the behavior of the testing app: */ + +/* In order to run the periph_uart test all of the following needs to be true: + * - periph_uart needs to be used + * - an I/O mapping for the UART at D0/D1 needs to be provided + * - this UART dev is not busy with stdio + */ +#if defined(ARDUINO_UART_D0D1) && defined(MODULE_PERIPH_UART) +# define ENABLE_UART_TEST (STDIO_UART_DEV != ARDUINO_UART_D0D1) +# define UART_TEST_DEV ARDUINO_UART_D0D1 +#else +# define ENABLE_UART_TEST 0 +#endif + +/* In order to run the periph_pwm test, all of the following needs to be true: + * - periph_pwm needs to be used (so that we actually have something to test) + * - periph_adc needs to be used (so that we can validate the output duty cycle) + * - At least one of: + * - Arduino I/O mapping for PWM on D5 *AND* analog pin A2 is provided + * - Arduino I/O mapping for PWM on D6 *AND* analog pin A1 is provided + */ +#if defined(MODULE_PERIPH_PWM) && defined(MODULE_PERIPH_ADC) +# if defined(ARDUINO_PIN_5_PWM_DEV) && defined(ARDUINO_A2) +# define ENABLE_PWM_TEST_D5 1 +# else +# define ENABLE_PWM_TEST_D5 0 +# endif +# if defined(ARDUINO_PIN_6_PWM_DEV) && defined(ARDUINO_A1) +# define ENABLE_PWM_TEST_D6 1 +# else +# define ENABLE_PWM_TEST_D6 0 +# endif +# define ENABLE_PWM_TEST (ENABLE_PWM_TEST_D5 || ENABLE_PWM_TEST_D6) +#else +# define ENABLE_PWM_TEST 0 +# define ENABLE_PWM_TEST_D5 0 +# define ENABLE_PWM_TEST_D6 0 +#endif + +/* In order to run the periph_adc test, we need: + * - periph_adc support (so that we have something to test) + * - Arduino I/O mapping for ADC (so that we know which line to test) + * - The PCF857x driver (so that we can control the R-2R resistor ladder + * connected to the GPIO expander. + */ +#if defined(MODULE_PERIPH_ADC) && defined(ARDUINO_A0) && defined(MODULE_PCF857X) && 0 +// TODO: Re-anble when PCB is fixed +# define ENABLE_ADC_TEST 1 +#else +# define ENABLE_ADC_TEST 0 +#endif + +/* We want the code to be compile-tested even when tests are disabled. We + * provide dummy parameters for the tests when they are off. The actual + * values don't matter, as the tests will be disabled anyway when the + * parameters are missing. But having the compiler checking the syntax and + * doing static analysis is useful in any case. */ +#ifndef ARDUINO_PIN_5_PWM_DEV +# define ARDUINO_PIN_5_PWM_DEV -1 +# define ARDUINO_PIN_5_PWM_CHAN -1 +#endif +#ifndef ARDUINO_PIN_6_PWM_DEV +# define ARDUINO_PIN_6_PWM_DEV -1 +# define ARDUINO_PIN_6_PWM_CHAN -1 +#endif +#ifndef ARDUINO_A0 +# define ARDUINO_A0 -1 +#endif +#ifndef ARDUINO_A1 +# define ARDUINO_A1 -1 +#endif +#ifndef ARDUINO_A2 +# define ARDUINO_A2 -1 +#endif +#ifndef UART_TEST_DEV +# define UART_TEST_DEV UART_DEV(0) +#endif +#ifndef TIMER +# define TIMER TIMER_DEV(0) +#endif + +/* A higher clock frequency is beneficial in being able to actually measure the + * difference of half a SPI clock cycle when clock phase is 1, compared to + * clock phase being 0. Most MCUs can clock their timers from the core clock + * directly, so the CPU clock is the highest clock frequency available. But + * some can't, so we handle them here explicitly. */ +#ifndef TIMER_FREQ_SPI_TEST +# if defined(CPU_SAM3) +# define TIMER_FREQ_SPI_TEST CLOCK_CORECLOCK / 4 +# elif defined(CPU_NRF52) || defined(CPU_NRF51) +# define TIMER_FREQ_SPI_TEST MHZ(16) +# else +# define TIMER_FREQ_SPI_TEST CLOCK_CORECLOCK +# endif +#endif + +/* for the UART test a slower frequency is more beneficial. We assume the + * timer to be 16 bit (tossing away the upper 16 bit of 32 bit timers) to + * ease the test. But the duration for transferring 8 bytes of UART data in + * ticks easily overflows 16 bit at higher frequencies, so we just go for + * a lower frequency instead. */ +#ifndef TIMER_FREQ_UART_TEST +# if defined(__AVR__) +# define TIMER_FREQ_UART_TEST CLOCK_CORECLOCK / 64 +# else +# define TIMER_FREQ_UART_TEST MHZ(1) +# endif +#endif + +static const char testdata[8] = "Selftest"; +static const spi_t spi_buses[] = { +#ifdef ARDUINO_SPI_D11D12D13 + ARDUINO_SPI_D11D12D13, +#endif +#ifdef ARDUINO_SPI_ISP + ARDUINO_SPI_ISP, +#endif +}; +static const gpio_t spi_clk_check_pins[] = { +#ifdef ARDUINO_SPI_D11D12D13 + PCF857X_GPIO_PIN(0, 2), +#endif +#ifdef ARDUINO_SPI_ISP + PCF857X_GPIO_PIN(0, 3), +#endif +}; + +static struct { + char data[8]; + uint8_t pos; +} serial_buf; + +/* This module is only used when both periph_i2c and the Arduino I/O + * mapping are present. If this module is not used, this test is optimized + * out as dead branch. We still want the compile test, though. */ +static const pcf857x_params_t params = { +#ifdef MODULE_PCF857X + .dev = ARDUINO_I2C_UNO, + .exp = PCF857X_EXP_PCF8574, +#endif +}; + +static pcf857x_t egpios; + +/* Some tests may pass due to luck (e.g. when expecting a pull up but input + * is floating, it will still get lucky from time to time). We repeat those + * in a loop to have some confidence that the test is consistently passing */ +static const unsigned flaky_test_repetitions = 100; + +/* We are trying to not spent much memory on error message for compatibility + * with as many boards as possible. The idea is that the line number where a + * test failed and careful commenting in the code provides the same level of + * insight with only a little inconvenience of having to open the editor. */ +static void print_test_failed(uint16_t line) +{ + printf("FAILURE in " __FILE__ ":%" PRIu16 "\n", line); +} + +static bool do_test(bool failed, uint16_t line) +{ + if (failed) { + print_test_failed(line); + if (STOP_ON_FAILURE) { + printf("Stopping, as STOP_ON_FAILURE==1\n"); + ARCHITECTURE_BREAKPOINT(1); + while (1) { + /* stop */ + } + } + } + + return failed; +} + +static void do_assert(bool failed, uint16_t line) +{ + if (failed) { + printf("CRITICAL "); + print_test_failed(line); + ARCHITECTURE_BREAKPOINT(1); + while (1) { + /* stop */ + } + } +} + +static void print_result(bool failed) +{ + if (failed) { + printf("[FAILED]\n"); + } + else { + printf("[OK]\n"); + } +} + +static void print_skipped(void) +{ + printf("(skipped)\n"); +} + +static void _print_start(const char *name, const char *detail, uint16_t line) +{ + if (DETAILED_OUTPUT) { + printf("Starting test for %s (%s) at " __FILE__ ":%" PRIu16"\n", name, + detail, line); + } + else { + printf("Starting test for %s at " __FILE__ ":%" PRIu16"\n", name, + line); + } +} + +#if DETAILED_OUTPUT +# define print_start(name, detail) _print_start(name, detail, __LINE__) +#else +# define print_start(name, detail) _print_start(name, NULL, __LINE__) +#endif + +#define TEST(x) do_test(!(x), __LINE__) + +#define ASSERT(x) do_assert(!(x), __LINE__) + +static void stupid_delay(unsigned count) +{ + while (count > 0) { + /* tell optimizer that the value of `count` is used and changed, so + * that the down-counting loop is not detected as dead code */ + __asm__ volatile ( + "" + /* outputs: */ + : "+r"(count) + /* inputs: */ + : + /* clobbers: */ + ); + count--; + } +} + +static void brief_delay(void) +{ + stupid_delay(100); +} + +static void long_delay(void) +{ + stupid_delay(10000); +} + +static bool periph_gpio_test_push_pull(void) +{ + bool failed = false; + print_start("GPIO", "push-pull"); + ASSERT(gpio_init(ARDUINO_PIN_3, GPIO_IN) == 0); + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + + gpio_clear(ARDUINO_PIN_4); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) == 0); + } + + gpio_set(ARDUINO_PIN_4); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) != 0); + } + + print_result(failed); + + return failed; +} + +static bool periph_gpio_test_input_pull_up(void) +{ + bool failed = false; + print_start("GPIO", "input pull-up"); + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + if (gpio_init(ARDUINO_PIN_3, GPIO_IN_PU) == 0) { + /* give pull resistor a little time to pull */ + brief_delay(); + /* pull up should pull both D3 and D4 up, as D4 is connected to D3 via + * the testing shield */ + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) != 0); + failed |= TEST(gpio_read(ARDUINO_PIN_4) != 0); + } + + /* push/pull on D4 should still be able to force down D3 */ + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + gpio_clear(ARDUINO_PIN_4); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) == 0); + } + print_result(failed); + } + else { + print_skipped(); + } + + return failed; +} + +static bool periph_gpio_test_input_pull_down(void) +{ + bool failed = false; + print_start("GPIO", "input pull-down"); + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + if (gpio_init(ARDUINO_PIN_3, GPIO_IN_PD) == 0) { + /* give pull resistor a little time to pull */ + brief_delay(); + /* pull down should pull both D3 and D4 down, as D4 is connected to D3 + * via the testing shield */ + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) == 0); + failed |= TEST(gpio_read(ARDUINO_PIN_4) == 0); + } + + /* push/pull on D4 should still be able to force up D3 */ + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + gpio_set(ARDUINO_PIN_4); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_3) != 0); + } + print_result(failed); + } + else { + print_skipped(); + } + + return failed; +} + +static bool periph_gpio_test_open_drain_pull_up(void) +{ + bool failed = false; + print_start("GPIO", "open-drain pull-up"); + + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + if (gpio_init(ARDUINO_PIN_3, GPIO_OD_PU) == 0) { + gpio_set(ARDUINO_PIN_3); + /* give pull resistor a little time to pull */ + brief_delay(); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_4) != 0); + } + gpio_clear(ARDUINO_PIN_3); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_4) == 0); + } + print_result(failed); + } + else { + print_skipped(); + } + + return failed; +} + +static bool periph_gpio_test_open_drain_no_pull_up(void) +{ + bool failed = false; + print_start("GPIO", "open-drain no-pull-up"); + + /* we cannot test without pull up, but the input pin may have the pull + * up just as well */ + if ((gpio_init(ARDUINO_PIN_3, GPIO_OD) == 0) + && (gpio_init(ARDUINO_PIN_4, GPIO_IN_PU))) { + gpio_set(ARDUINO_PIN_3); + /* give pull resistor a little time to pull */ + brief_delay(); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_4) != 0); + } + gpio_clear(ARDUINO_PIN_3); + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + failed |= TEST(gpio_read(ARDUINO_PIN_4) == 0); + } + print_result(failed); + } + else { + print_skipped(); + } + + return failed; +} + +static bool periph_gpio_test(void) +{ + bool failed = false; + + failed |= periph_gpio_test_push_pull(); + failed |= periph_gpio_test_input_pull_up(); + failed |= periph_gpio_test_input_pull_down(); + failed |= periph_gpio_test_open_drain_pull_up(); + failed |= periph_gpio_test_open_drain_no_pull_up(); + + return failed; +} + +static void gpio_cb(void *arg) +{ + atomic_uint *cnt = arg; + atomic_fetch_add(cnt, 1); +} + +static bool periph_gpio_irq_test_falling(void) +{ + bool failed = false; + print_start("GPIO-IRQ", "falling-edge"); + atomic_uint cnt = 0; + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + gpio_clear(ARDUINO_PIN_4); + ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_FALLING, gpio_cb, &cnt) == 0); + + /* no stray IRQ */ + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 0); + + /* no IRQ on false edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 0); + + /* one IRQ on matching edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 1); + + /* still no IRQ on false edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 1); + + /* another IRQ on matching edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* no IRQs while disabled */ + gpio_irq_disable(ARDUINO_PIN_3); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* no stray IRQs when re-enabled */ + gpio_irq_enable(ARDUINO_PIN_3); + failed |= TEST(atomic_load(&cnt) == 2); + + /* still no IRQ on false edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* another IRQ on matching edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 3); + + print_result(failed); + return failed; +} + +static bool periph_gpio_irq_test_rising(void) +{ + bool failed = false; + print_start("GPIO-IRQ", "rising-edge"); + atomic_uint cnt = 0; + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + gpio_set(ARDUINO_PIN_4); + ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_RISING, gpio_cb, &cnt) == 0); + + /* no stray IRQ */ + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 0); + + /* no IRQ on false edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 0); + + /* one IRQ on matching edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 1); + + /* still no IRQ on false edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 1); + + /* another IRQ on matching edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* no IRQs while disabled */ + gpio_irq_disable(ARDUINO_PIN_3); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* no stray IRQs when re-enabled */ + gpio_irq_enable(ARDUINO_PIN_3); + failed |= TEST(atomic_load(&cnt) == 2); + + /* still no IRQ on false edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* another IRQ on matching edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 3); + + print_result(failed); + return failed; +} + +static bool periph_gpio_irq_test_both(void) +{ + bool failed = false; + print_start("GPIO-IRQ", "both-edges"); + atomic_uint cnt = 0; + ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + gpio_set(ARDUINO_PIN_4); + ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_BOTH, gpio_cb, &cnt) == 0); + + /* no stray IRQ */ + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 0); + + /* IRQ on falling edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 1); + + /* another IRQ on rising edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 2); + + /* and another IRQ on falling edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 3); + + /* and another IRQ on rising edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 4); + + /* no IRQs while disabled */ + gpio_irq_disable(ARDUINO_PIN_3); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 4); + + /* no stray IRQs when re-enabled */ + gpio_irq_enable(ARDUINO_PIN_3); + failed |= TEST(atomic_load(&cnt) == 4); + + /* an IRQ on falling edge */ + gpio_clear(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 5); + + /* another IRQ on rising edge */ + gpio_set(ARDUINO_PIN_4); + brief_delay(); + failed |= TEST(atomic_load(&cnt) == 6); + + print_result(failed); + return failed; +} + +static bool periph_gpio_irq_test(void) +{ + bool failed = false; + + failed |= periph_gpio_irq_test_falling(); + failed |= periph_gpio_irq_test_rising(); + failed |= periph_gpio_irq_test_both(); + + return failed; +} + +static bool periph_i2c_test(void) +{ + bool failed = false; + print_start("I2C", "GPIO extender"); + ASSERT(gpio_init(ARDUINO_PIN_8, GPIO_IN) == 0); + ASSERT(gpio_init(ARDUINO_PIN_9, GPIO_OUT) == 0); + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 0), GPIO_OUT) == 0); + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 1), GPIO_IN) == 0); + + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + gpio_set(ARDUINO_PIN_9); + failed |= TEST(pcf857x_gpio_read(&egpios, PCF857X_GPIO_PIN(0, 1)) != 0); + gpio_clear(ARDUINO_PIN_9); + failed |= TEST(pcf857x_gpio_read(&egpios, PCF857X_GPIO_PIN(0, 1)) == 0); + } + + for (unsigned i = 0; i < flaky_test_repetitions; i++) { + pcf857x_gpio_set(&egpios, PCF857X_GPIO_PIN(0, 0)); + failed |= TEST(gpio_read(ARDUINO_PIN_8) != 0); + pcf857x_gpio_clear(&egpios, PCF857X_GPIO_PIN(0, 0)); + failed |= TEST(gpio_read(ARDUINO_PIN_8) == 0); + } + + print_result(failed); + return failed; +} + +static void uart_rx_cb(void *arg, uint8_t data) +{ + (void)arg; + if (serial_buf.pos < sizeof(serial_buf.data)) { + serial_buf.data[serial_buf.pos] = data; + } + serial_buf.pos++; +} + +static bool periph_uart_rxtx_test(uint32_t symbolrate) +{ + bool failed = 0; + uint16_t duration_ticks = 0; + uint16_t bit_ticks = 0; + uint16_t start; + memset(&serial_buf, 0, sizeof(serial_buf)); + + ASSERT(uart_init(UART_TEST_DEV, symbolrate, uart_rx_cb, NULL) == 0); + + if (IS_USED(MODULE_PERIPH_TIMER)) { + bit_ticks = TIMER_FREQ_UART_TEST / symbolrate; + duration_ticks = 9ULL * sizeof(testdata) * bit_ticks; + } + + /* test that data send matches data received */ + if (IS_USED(MODULE_PERIPH_TIMER)) { + start = timer_read(TIMER); + } + uart_write(UART_TEST_DEV, (void *)testdata, sizeof(testdata)); + if (IS_USED(MODULE_PERIPH_TIMER)) { + uint16_t stop = timer_read(TIMER); + /* expecting actual duration within 75% to 200% of the expected. */ + failed |= TEST(stop - start > duration_ticks - (duration_ticks >> 2)); + failed |= TEST(stop - start < (duration_ticks << 1)); + if (failed) { + DEBUG("%" PRIu32 " Bd, expected %" PRIu16 " ticks, got %" PRIu16 + " ticks\n", + symbolrate, duration_ticks, (uint16_t)(stop - start)); + } + } + long_delay(); + failed |= TEST(memcmp(testdata, serial_buf.data, sizeof(serial_buf.data)) == 0); + failed |= TEST(serial_buf.pos == sizeof(testdata)); + + /* test that no data is received when UART is off */ + uart_poweroff(UART_TEST_DEV); + uart_write(UART_TEST_DEV, (void *)testdata, sizeof(testdata)); + long_delay(); + failed |= TEST(serial_buf.pos == sizeof(testdata)); + + /* test that data is received again when UART is back on */ + memset(&serial_buf, 0, sizeof(serial_buf)); + uart_poweron(UART_TEST_DEV); + long_delay(); + /* no data expected yet */ + failed |= TEST(serial_buf.pos == 0); + /* now writing with device back online */ + uart_write(UART_TEST_DEV, (void *)testdata, sizeof(testdata)); + long_delay(); + failed |= TEST(memcmp(testdata, serial_buf.data, sizeof(serial_buf.data)) == 0); + failed |= TEST(serial_buf.pos == sizeof(testdata)); + + return failed; +} + +static bool periph_uart_test_slow(void) +{ + bool failed = false; + print_start("UART", "slow"); + failed |= periph_uart_rxtx_test(9600); + print_result(failed); + return failed; +} + +static bool periph_uart_test_fast(void) +{ + bool failed = false; + print_start("UART", "fast"); + failed |= periph_uart_rxtx_test(115200); + print_result(failed); + return failed; +} + +static bool periph_uart_test(void) +{ + bool failed = false; + + if (IS_USED(MODULE_PERIPH_TIMER)) { + ASSERT(timer_init(TIMER, TIMER_FREQ_UART_TEST, NULL, NULL) == 0); + timer_start(TIMER); + } + + failed |= periph_uart_test_slow(); + failed |= periph_uart_test_fast(); + return failed; +} + +static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, + uint32_t clk_hz, gpio_t clk_check, bool idle_level, + const char *test_in_detail) +{ + (void)test_in_detail; + bool failed = 0; + print_start("SPI", test_in_detail); + uint16_t byte_transfer_ticks = 0; + memset(&serial_buf, 0, sizeof(serial_buf)); + + if (IS_USED(MODULE_PERIPH_TIMER)) { + byte_transfer_ticks = 8ULL * TIMER_FREQ_UART_TEST / clk_hz; + } + + /* D10 is C̅S̅, D7 is connected to C̅S̅ */ + spi_cs_t cs = ARDUINO_PIN_10; + gpio_t cs_check = ARDUINO_PIN_7; + gpio_init(cs_check, GPIO_IN); + spi_init_cs(bus, cs); + + spi_acquire(bus, cs, mode, clk); + + if (IS_USED(MODULE_PCF857X)) { + failed |= TEST(idle_level == (bool)pcf857x_gpio_read(&egpios, clk_check)); + } + + /* C̅S̅ should still be HIGH while no chip is selected */ + failed |= TEST(gpio_read(cs_check) != 0); + + for (uint8_t i = 0; i < UINT8_MAX; i++) { + uint16_t start = 0; + if (IS_USED(MODULE_PERIPH_TIMER)) { + start = timer_read(TIMER); + } + uint8_t received = spi_transfer_byte(bus, cs, true, i); + uint16_t stop = 0; + if (IS_USED(MODULE_PERIPH_TIMER)) { + stop = timer_read(TIMER); + } + failed |= TEST(received == i); + uint16_t byte_time = (uint16_t)(stop - start); + /* We allow the actual SPI clock to be slower than requested, but not + * faster. So the transfer needs to take *at least* the theoretical + * time. Given the overhead of, this already has some room for error */ + failed |= TEST(byte_time >= byte_transfer_ticks); + /* C̅S̅ should be still LOW while chip is selected */ + failed |= TEST(gpio_read(cs_check) == 0); + } + + failed |= TEST(spi_transfer_byte(bus, cs, false, UINT8_MAX) == UINT8_MAX); + /* C̅S̅ should be again HIGH while now that no chip is selected */ + failed |= TEST(gpio_read(cs_check) != 0); + + /* no also test for different sizes */ + for (unsigned i = 1; i <= sizeof(testdata); i++) { + uint8_t target[sizeof(testdata) + 4]; + memset(target, 0x55, sizeof(target)); + /* C̅S̅ should be HIGH before chip is selected */ + failed |= TEST(gpio_read(cs_check) != 0); + spi_transfer_bytes(bus, cs, false, testdata, target, i); + /* C̅S̅ should be HIGH again after transfer */ + failed |= TEST(gpio_read(cs_check) != 0); + + /* first part of data read should be the test data */ + for (unsigned j = 0; j < i; j++) { + failed |= TEST(target[j] == testdata[j]); + } + + /* rest of the target should be the canary value */ + for (unsigned j = i; j < sizeof(testdata); j++) { + failed |= TEST(target[j] == 0x55); + } + } + + if (IS_USED(MODULE_PCF857X)) { + failed |= TEST(idle_level == (bool)pcf857x_gpio_read(&egpios, clk_check)); + } + + spi_release(bus); + print_result(failed); + + return failed; +} + +static bool periph_spi_test(void) +{ + if (IS_USED(MODULE_PERIPH_TIMER)) { + ASSERT(timer_init(TIMER, TIMER_FREQ_SPI_TEST, NULL, NULL) == 0); + timer_start(TIMER); + } + + bool failed = false; + static const spi_clk_t clocks[] = { SPI_CLK_100KHZ, SPI_CLK_1MHZ, SPI_CLK_10MHZ }; + static const uint32_t clk_hzs[] = { KHZ(100), MHZ(1), MHZ(10) }; + + if (IS_USED(MODULE_PCF857X)) { + for (int i = 0; i < (int)ARRAY_SIZE(spi_clk_check_pins); i++) { + ASSERT(pcf857x_gpio_init(&egpios, spi_clk_check_pins[i], GPIO_IN) == 0); + } + } + + /* using a signed comparison here to also compile when no SPI buses are + * available for testing */ + for (int i = 0; i < (int)ARRAY_SIZE(spi_buses); i++) { + spi_t bus = spi_buses[i]; + gpio_t clk_check = spi_clk_check_pins[i]; + for (unsigned j = 0; j < ARRAY_SIZE(clocks); j++) { + spi_clk_t clk = clocks[j]; + uint32_t clk_hz = clk_hzs[j]; + failed |= periph_spi_rxtx_test(bus, SPI_MODE_0, clk, clk_hz, clk_check, false, "mode 0"); + failed |= periph_spi_rxtx_test(bus, SPI_MODE_1, clk, clk_hz, clk_check, false, "mode 1"); + failed |= periph_spi_rxtx_test(bus, SPI_MODE_2, clk, clk_hz, clk_check, true, "mode 2"); + failed |= periph_spi_rxtx_test(bus, SPI_MODE_3, clk, clk_hz, clk_check, true, "mode 3"); + } + } + return failed; +} + +static bool periph_pwm_test_chan(pwm_t pwm_dev, uint8_t pwm_chan, pwm_mode_t pwm_mode, adc_t adc_line) +{ + bool failed = false; + print_start("PWM", "duty cycle (via ADC)"); + + failed |= TEST(pwm_init(pwm_dev, pwm_mode, CLOCK_CORECLOCK, 256) > 0); + if (failed) { + /* pwm mode not supported, most likely */ + return failed; + } + + ASSERT(adc_init(adc_line) == 0); + + for (uint16_t i = 0; i <= UINT8_MAX; i++) { + pwm_set(pwm_dev, pwm_chan, i); + /* give voltage at ADC some time to settle */ + brief_delay(); + uint16_t sample = adc_sample(adc_line, ADC_RES_10BIT); + uint16_t expected = i << 2; + + /* let's allow for quite some error here */ + const uint16_t delta = 64; + uint16_t lower = expected <= delta ? 0 : expected - delta; + uint16_t upper = MIN(1023, expected + delta); + bool test_failed = TEST((lower <= sample) && (upper >= sample)); + if (test_failed) { + printf("%" PRIu16 " <= %" PRIu16 " <= %" PRIu16": FAILED\n", + lower, sample, upper); + } + failed |= test_failed; + } + + print_result(failed); + return failed; +} + +static bool periph_pwm_test(void) +{ + bool failed = false; + static const pwm_mode_t modes[] = { PWM_LEFT, PWM_RIGHT, PWM_CENTER }; + + for (unsigned i = 0; i < ARRAY_SIZE(modes); i++) { + if (ENABLE_PWM_TEST_D5) { + failed |= periph_pwm_test_chan(ARDUINO_PIN_5_PWM_DEV, ARDUINO_PIN_5_PWM_CHAN, modes[i], ARDUINO_A2); + } + if (ENABLE_PWM_TEST_D6) { + failed |= periph_pwm_test_chan(ARDUINO_PIN_6_PWM_DEV, ARDUINO_PIN_6_PWM_CHAN, modes[i], ARDUINO_A1); + } + } + + return failed; +} + +static void r_2r_dac_init(void) +{ + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 4), GPIO_OUT) == 0); + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 5), GPIO_OUT) == 0); + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 6), GPIO_OUT) == 0); + ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 7), GPIO_OUT) == 0); +} + +static void r_2r_dac_write(uint8_t val) +{ + pcf857x_gpio_write(&egpios, PCF857X_GPIO_PIN(0, 4), val & (1U << 3)); + pcf857x_gpio_write(&egpios, PCF857X_GPIO_PIN(0, 5), val & (1U << 2)); + pcf857x_gpio_write(&egpios, PCF857X_GPIO_PIN(0, 6), val & (1U << 1)); + pcf857x_gpio_write(&egpios, PCF857X_GPIO_PIN(0, 7), val & (1U << 0)); +} + +static bool periph_adc_test(void) +{ + bool failed = false; + print_start("ADC", "sample R2R DAC output"); + + adc_init(ARDUINO_A0); + r_2r_dac_init(); + + for (uint8_t i = 0; i < 16; i++) { + r_2r_dac_write(i); + uint16_t sample = adc_sample(ARDUINO_A0, ADC_RES_10BIT); + uint16_t expected = i << 6; + + /* the resistors are said to be rather accurate, so lets be a bit + * more strict here */ + const uint16_t delta = 16; + uint16_t lower = expected <= delta ? 0 : expected - delta; + uint16_t upper = MIN(1023, expected + delta); + bool test_failed = TEST((lower <= sample) && (upper >= sample)); + if (test_failed) { + printf("%" PRIu16 " <= %" PRIu16 " <= %" PRIu16": FAILED\n", + lower, sample, upper); + } + failed |= test_failed; + } + + print_result(failed); + return failed; +} + +int main(void) +{ + bool failed = false; + + printf("self-testing peripheral drivers\n" + "===============================\n"); + + /* the GPIO extender is used by the I2C test and the ADC test, so only + * initialize it once here */ + if (IS_USED(MODULE_PCF857X)) { + ASSERT(pcf857x_init(&egpios, ¶ms) == PCF857X_OK); + } + + if (IS_USED(MODULE_PERIPH_GPIO)) { + failed |= periph_gpio_test(); + } + + if (IS_USED(MODULE_PERIPH_GPIO_IRQ)) { + failed |= periph_gpio_irq_test(); + } + + if (IS_USED(MODULE_PCF857X) && IS_USED(MODULE_PERIPH_GPIO)) { + failed |= periph_i2c_test(); + } + + if (ENABLE_UART_TEST) { + failed |= periph_uart_test(); + } + + if (IS_USED(MODULE_PERIPH_SPI)) { + failed |= periph_spi_test(); + } + + if (ENABLE_PWM_TEST) { + failed |= periph_pwm_test(); + } + + if (ENABLE_ADC_TEST) { + failed |= periph_adc_test(); + } + + if (failed) { + printf("\n\nSOME TESTS FAILED\n"); + return 1; + } + + printf("\n\nALL TESTS SUCCEEDED\n"); + return 0; +} diff --git a/tests/periph/selftest_shield/tests-with-config/01-run.py b/tests/periph/selftest_shield/tests-with-config/01-run.py new file mode 100755 index 0000000000..606d413ed1 --- /dev/null +++ b/tests/periph/selftest_shield/tests-with-config/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect('ALL TESTS SUCCEEDED') + + +if __name__ == "__main__": + sys.exit(run(testfunc)) diff --git a/tests/periph/selftest_shield/tests/01-run.py b/tests/periph/selftest_shield/tests/01-run.py new file mode 100755 index 0000000000..005393cb48 --- /dev/null +++ b/tests/periph/selftest_shield/tests/01-run.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +# @author Marian Buschsieweke + +import sys +from testrunner import run + + +def testfunc(child): + child.expect("self-testing peripheral drivers") + child.expect("ALL TESTS SUCCEEDED") + + +if __name__ == "__main__": + sys.exit(run(testfunc)) From e99cafba338de110971cec7c5c9a28a435c1641e Mon Sep 17 00:00:00 2001 From: Mihai Renea Date: Wed, 8 Nov 2023 10:08:37 +0100 Subject: [PATCH 25/85] drivers/at: fix URC collision with command response --- drivers/at/at.c | 159 ++++++++++++------ drivers/include/at.h | 71 +++++--- tests/drivers/at/main.c | 62 +++++++ .../at/tests-with-config/sneaky_urc.py | 85 ++++++++++ 4 files changed, 307 insertions(+), 70 deletions(-) create mode 100755 tests/drivers/at/tests-with-config/sneaky_urc.py diff --git a/drivers/at/at.c b/drivers/at/at.c index d927329999..1d1f522cfc 100644 --- a/drivers/at/at.c +++ b/drivers/at/at.c @@ -6,6 +6,39 @@ * directory for more details. */ +/* A note on URCs (Unsolicited Result Codes), regardless of whether URC handling + * is enabled or not. + * + * Some DCEs (Data Circuit-terminating Equipment, aka modem), like the LTE + * modules from uBlox define a grace period where URCs are guaranteed NOT to be + * sent as the time span between: + * - the command EOL character reception AND command being internally accepted + * - the EOL character of the last response line + * + * As follows, there is an indeterminate amount of time between: + * - the command EOL character being sent + * - the command EOL character reception AND command being internally accepted, + * i.e. the begin of the grace period + * + * In other words, we can get a URC (or more?) just after issuing the command + * and before the first line of response. The net effect is that such URCs will + * appear to be the first line of response to the last issued command. + * + * The current solution is to skip characters that don't match the expected + * response, at the expense of losing these URCs. Note, we may already lose URCs + * when calling at_drain() just before any at_send_cmd(). Success partially + * depends on whether command echoing is enabled or not: + * 1. echo enabled: by observation, it seems that the grace period begins + * BEFORE the echoed command. This has the advantage that we ALWAYS know what + * the first line of response must look like and so if it doesn't, then it's a + * URC. Thus, any procedure that calls at_send_cmd() will catch and discard + * these URCs. + * 2. echo disabled: commands that expect a response (e.g. at_send_cmd_get_resp_wait_ok()) + * will catch and discard any URC (or, if MODULE_AT_URC enabled, hand it over + * to the URC callbacks). For the rest, it is the application's responsibility + * to handle it. + */ + #include #include #include @@ -32,6 +65,10 @@ #define AT_EVENT_PRIO EVENT_PRIO_HIGHEST #endif +#if defined(MODULE_AT_URC) +static int _check_urc(clist_node_t *node, void *arg); +#endif + #if defined(MODULE_AT_URC_ISR) static void _event_process_urc(event_t *_event) { @@ -62,8 +99,7 @@ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t isrpipe_init(&dev->isrpipe, (uint8_t *)buf, bufsize); - return uart_init(uart, baudrate, _isrpipe_write_one_wrapper, - dev); + return uart_init(uart, baudrate, _isrpipe_write_one_wrapper, dev); } int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout) @@ -99,6 +135,15 @@ out: return res; } +int at_wait_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout) +{ + int res; + do { + res = at_expect_bytes(dev, bytes, timeout); + } while (res != 0 && res != -ETIMEDOUT); + return res; +} + void at_send_bytes(at_dev_t *dev, const char *bytes, size_t len) { uart_write(dev->uart, (const uint8_t *)bytes, len); @@ -175,11 +220,12 @@ int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout) uart_write(dev->uart, (const uint8_t *)CONFIG_AT_SEND_EOL, AT_SEND_EOL_LEN); if (!IS_ACTIVE(CONFIG_AT_SEND_SKIP_ECHO)) { - if (at_expect_bytes(dev, command, timeout)) { + if (at_wait_bytes(dev, command, timeout)) { return -1; } - if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL AT_RECV_EOL_1 AT_RECV_EOL_2, timeout)) { + if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL AT_RECV_EOL_1 AT_RECV_EOL_2, + timeout)) { return -2; } } @@ -218,11 +264,7 @@ ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, goto out; } - res = at_readline(dev, resp_buf, len, false, timeout); - if (res == 0) { - /* skip possible empty line */ - res = at_readline(dev, resp_buf, len, false, timeout); - } + res = at_readline_skip_empty(dev, resp_buf, len, false, timeout); out: return res; @@ -242,35 +284,35 @@ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const c goto out; } - res = at_readline(dev, resp_buf, len, false, timeout); - if (res == 0) { - /* skip possible empty line */ - res = at_readline(dev, resp_buf, len, false, timeout); - } + /* URCs may occur right after the command has been sent and before the + * expected response */ + do { + res = at_readline_skip_empty(dev, resp_buf, len, false, timeout); - /* Strip the expected prefix */ - if (res > 0 && resp_prefix && *resp_prefix) { - size_t prefix_len = strlen(resp_prefix); - if (strncmp(resp_buf, resp_prefix, prefix_len) == 0) { - size_t remaining_len = strlen(resp_buf) - prefix_len; - /* The one extra byte in the copy is the terminating nul byte */ - memmove(resp_buf, resp_buf + prefix_len, remaining_len + 1); - res -= prefix_len; + /* Strip the expected prefix */ + if (res > 0 && resp_prefix && *resp_prefix) { + size_t prefix_len = strlen(resp_prefix); + if (strncmp(resp_buf, resp_prefix, prefix_len) == 0) { + size_t remaining_len = strlen(resp_buf) - prefix_len; + /* The one extra byte in the copy is the terminating nul byte */ + memmove(resp_buf, resp_buf + prefix_len, remaining_len + 1); + res -= prefix_len; + break; + } } - } + } while (res >= 0); /* wait for OK */ if (res >= 0) { - res_ok = at_readline(dev, ok_buf, sizeof(ok_buf), false, timeout); - if (res_ok == 0) { - /* skip possible empty line */ - res_ok = at_readline(dev, ok_buf, sizeof(ok_buf), false, timeout); + res_ok = at_readline_skip_empty(dev, ok_buf, sizeof(ok_buf), false, timeout); + if (res_ok < 0) { + return -1; } ssize_t len_ok = sizeof(CONFIG_AT_RECV_OK) - 1; if ((len_ok != 0) && (strcmp(ok_buf, CONFIG_AT_RECV_OK) == 0)) { } else { - /* Something else then OK */ + /* Something else than OK */ res = -1; } } @@ -295,8 +337,6 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, goto out; } - /* only clear the response buffer after sending the command, - * so the same buffer can be used for command and response */ memset(resp_buf, '\0', len); while (1) { @@ -358,19 +398,16 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout uart_write(dev->uart, (const uint8_t *)command, cmdlen); uart_write(dev->uart, (const uint8_t *)CONFIG_AT_SEND_EOL, AT_SEND_EOL_LEN); - if (at_expect_bytes(dev, command, timeout)) { - return -1; + if (!IS_ACTIVE(CONFIG_AT_SEND_SKIP_ECHO)) { + if (at_wait_bytes(dev, command, timeout)) { + return -1; + } + if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL, timeout)) { + return -2; + } } - if (at_expect_bytes(dev, CONFIG_AT_SEND_EOL AT_RECV_EOL_2, timeout)) { - return -2; - } - - if (at_expect_bytes(dev, ">", timeout)) { - return -3; - } - - return 0; + return at_wait_bytes(dev, ">", timeout); } int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout) @@ -378,16 +415,31 @@ int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout) int res; char resp_buf[64]; - res = at_send_cmd_get_resp(dev, command, resp_buf, sizeof(resp_buf), timeout); + res = at_send_cmd_get_resp(dev, command, resp_buf, sizeof(resp_buf), + timeout); - if (res > 0) { - ssize_t len_ok = sizeof(CONFIG_AT_RECV_OK) - 1; - if ((len_ok != 0) && (strcmp(resp_buf, CONFIG_AT_RECV_OK) == 0)) { - res = 0; + size_t const len_ok = sizeof(CONFIG_AT_RECV_OK) - 1; + size_t const len_err = sizeof(CONFIG_AT_RECV_ERROR) - 1; + size_t const len_cme_cms = sizeof("+CME ERROR:") - 1; + + while (res >= 0) { + if (strncmp(resp_buf, CONFIG_AT_RECV_OK, len_ok) == 0) { + return 0; } - else { - res = -1; + else if (strncmp(resp_buf, CONFIG_AT_RECV_ERROR, len_err) == 0) { + return -1; } + else if (strncmp(resp_buf, "+CME ERROR:", len_cme_cms) == 0) { + return -2; + } + else if (strncmp(resp_buf, "+CMS ERROR:", len_cme_cms) == 0) { + return -2; + } + /* probably a sneaky URC */ +#ifdef MODULE_AT_URC + clist_foreach(&dev->urc_list, _check_urc, resp_buf); +#endif + res = at_readline_skip_empty(dev, resp_buf, sizeof(resp_buf), false, timeout); } return res; @@ -445,6 +497,17 @@ out: return res; } +ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len, + bool keep_eol, uint32_t timeout) +{ + ssize_t res = at_readline(dev, resp_buf, len, keep_eol, timeout); + if (res == 0) { + /* skip possible empty line */ + res = at_readline(dev, resp_buf, len, keep_eol, timeout); + } + return res; +} + #ifdef MODULE_AT_URC void at_add_urc(at_dev_t *dev, at_urc_t *urc) { diff --git a/drivers/include/at.h b/drivers/include/at.h index 90126a0630..4b0133ca47 100644 --- a/drivers/include/at.h +++ b/drivers/include/at.h @@ -195,8 +195,8 @@ typedef struct { * @param[in] buf input buffer * @param[in] bufsize size of @p buf * - * @returns success code UART_OK on success - * @returns error code UART_NODEV or UART_NOBAUD otherwise + * @retval success code UART_OK on success + * @retval error code UART_NODEV or UART_NOBAUD otherwise */ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t bufsize); @@ -209,8 +209,8 @@ int at_dev_init(at_dev_t *dev, uart_t uart, uint32_t baudrate, char *buf, size_t * @param[in] command command string to send * @param[in] timeout timeout (in usec) * - * @returns 0 when device answers "OK" - * @returns <0 otherwise + * @retval 0 when device answers "OK" + * @retval <0 otherwise */ int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout); @@ -224,8 +224,8 @@ int at_send_cmd_wait_ok(at_dev_t *dev, const char *command, uint32_t timeout); * @param[in] command command string to send * @param[in] timeout timeout (in usec) * - * @return 0 when prompt is received - * @return <0 otherwise + * @retval 0 when prompt is received + * @retval <0 otherwise */ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout); @@ -243,8 +243,8 @@ int at_send_cmd_wait_prompt(at_dev_t *dev, const char *command, uint32_t timeout * @param[in] len len of @p buffer * @param[in] timeout timeout (in usec) * - * @returns length of response on success - * @returns <0 on error + * @retval n length of response on success + * @retval <0 on error */ ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf, size_t len, uint32_t timeout); @@ -263,8 +263,8 @@ ssize_t at_send_cmd_get_resp(at_dev_t *dev, const char *command, char *resp_buf, * @param[in] len len of @p buffer * @param[in] timeout timeout (in usec) * - * @returns length of response on success - * @returns <0 on error + * @retval n length of response on success + * @retval <0 on error */ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const char *resp_prefix, char *resp_buf, size_t len, uint32_t timeout); @@ -286,9 +286,9 @@ ssize_t at_send_cmd_get_resp_wait_ok(at_dev_t *dev, const char *command, const c * @param[in] keep_eol true to keep the CR character in the response * @param[in] timeout timeout (in usec) * - * @returns length of response on success - * @returns -1 on error - * @returns -2 on CMS or CME error + * @retval n length of response on success + * @retval -1 on error + * @retval -2 on CMS or CME error */ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf, size_t len, bool keep_eol, uint32_t timeout); @@ -300,11 +300,23 @@ ssize_t at_send_cmd_get_lines(at_dev_t *dev, const char *command, char *resp_buf * @param[in] bytes buffer containing bytes to expect (NULL-terminated) * @param[in] timeout timeout (in usec) * - * @returns 0 on success - * @returns <0 otherwise + * @retval 0 on success + * @retval <0 otherwise */ int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout); +/** + * @brief Repeatedly calls at_expect_bytes() until a match or timeout occurs + * + * @param[in] dev device to operate on + * @param[in] bytes buffer containing bytes to expect (NULL-terminated) + * @param[in] timeout timeout (in usec) + * + * @retval 0 on success + * @retval <0 otherwise + */ +int at_wait_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout); + /** * @brief Receives bytes into @p bytes buffer until the string pattern * @p string is received or the buffer is full. @@ -317,8 +329,8 @@ int at_expect_bytes(at_dev_t *dev, const char *bytes, uint32_t timeout); * bytes. * @param[in] timeout timeout (in usec) of inactivity to finish read * - * @returns 0 on success - * @returns <0 on error + * @retval 0 on success + * @retval <0 on error */ int at_recv_bytes_until_string(at_dev_t *dev, const char *string, char *bytes, size_t *bytes_len, @@ -341,7 +353,7 @@ void at_send_bytes(at_dev_t *dev, const char *bytes, size_t len); * @param[in] len maximum number of bytes to receive * @param[in] timeout timeout (in usec) of inactivity to finish read * - * @returns Number of bytes read, eventually zero if no bytes available + * @retval n Number of bytes read, eventually zero if no bytes available */ ssize_t at_recv_bytes(at_dev_t *dev, char *bytes, size_t len, uint32_t timeout); @@ -352,8 +364,8 @@ ssize_t at_recv_bytes(at_dev_t *dev, char *bytes, size_t len, uint32_t timeout); * @param[in] command command to send * @param[in] timeout timeout (in usec) * - * @returns 0 on success - * @returns <0 otherwise + * @retval 0 on success + * @retval <0 otherwise */ int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout); @@ -366,11 +378,26 @@ int at_send_cmd(at_dev_t *dev, const char *command, uint32_t timeout); * @param[in] keep_eol true to keep the CR character in the response * @param[in] timeout timeout (in usec) * - * @returns line length on success - * @returns <0 on error + * @retval n line length on success + * @retval <0 on error */ ssize_t at_readline(at_dev_t *dev, char *resp_buf, size_t len, bool keep_eol, uint32_t timeout); +/** + * @brief Read a line from device, skipping a possibly empty line. + * + * @param[in] dev device to operate on + * @param[in] resp_buf buffer to store line + * @param[in] len size of @p resp_buf + * @param[in] keep_eol true to keep the CR character in the response + * @param[in] timeout timeout (in usec) + * + * @retval n line length on success + * @retval <0 on error + */ +ssize_t at_readline_skip_empty(at_dev_t *dev, char *resp_buf, size_t len, + bool keep_eol, uint32_t timeout); + /** * @brief Drain device input buffer * diff --git a/tests/drivers/at/main.c b/tests/drivers/at/main.c index 4313700423..e424ac8b78 100644 --- a/tests/drivers/at/main.c +++ b/tests/drivers/at/main.c @@ -285,6 +285,67 @@ static int remove_urc(int argc, char **argv) } #endif +static int sneaky_urc(int argc, char **argv) +{ + (void)argc; + (void)argv; + + int res = 0; + char resp_buf[128]; + +#ifdef MODULE_AT_URC + at_urc_t urc = {.cb = _urc_cb, .code = "+CSCON"}; + at_add_urc(&at_dev, &urc); +#endif + + res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=1", US_PER_SEC); + + if (res) { + puts("Error AT+CFUN=1"); + res = 1; + goto exit; + } + + res = at_send_cmd_get_resp_wait_ok(&at_dev, "AT+CEREG?", + "+CEREG:", resp_buf, + sizeof(resp_buf), US_PER_SEC); + if (res < 0) { + puts("Error AT+CEREG?"); + res = 1; + goto exit; + } + + res = at_send_cmd_wait_prompt(&at_dev, "AT+USECMNG=0,0,\"cert\",128", US_PER_SEC); + if (res) { + puts("Error AT+USECMNG"); + res = 1; + goto exit; + } + + res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=8", US_PER_SEC); + + if (res != -1) { + puts("Error AT+CFUN=8"); + res = 1; + goto exit; + } + + res = at_send_cmd_wait_ok(&at_dev, "AT+CFUN=9", US_PER_SEC); + + if (res != -2) { + puts("Error AT+CFUN=9"); + res = 1; + goto exit; + } + + res = 0; +exit: +#ifdef MODULE_AT_URC + at_remove_urc(&at_dev, &urc); +#endif + return res; +} + static const shell_command_t shell_commands[] = { { "init", "Initialize AT device", init }, { "send", "Send a command and wait response", send }, @@ -296,6 +357,7 @@ static const shell_command_t shell_commands[] = { { "drain", "Drain AT device", drain }, { "power_on", "Power on AT device", power_on }, { "power_off", "Power off AT device", power_off }, + { "sneaky_urc", "Test sneaky URC interference", sneaky_urc}, #ifdef MODULE_AT_URC { "add_urc", "Register an URC", add_urc }, { "remove_urc", "De-register an URC", remove_urc }, diff --git a/tests/drivers/at/tests-with-config/sneaky_urc.py b/tests/drivers/at/tests-with-config/sneaky_urc.py new file mode 100755 index 0000000000..7f4d059219 --- /dev/null +++ b/tests/drivers/at/tests-with-config/sneaky_urc.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +# This script simulates a modem sending a "sneaky URC", a URC that happens exactly +# after the command is received, but before the first line of answer is sent. This +# is possible behavior at least on the U-Blox LTE modules. +# +# Running this test requires the board to be connected twice +# - one connection simulating the modem (e.g.: /dev/ttyUSB0), connected to the +# serial device this script is listening to +# - one to access the RIOT CLI (e.g.: debugger, USB or a second serial connection) +# +# How to get it running: +# 1. Adapt the `EOL_IN`, `EOL_OUT`, `ECHO_ON` variables below to match your use case +# 2. Run this script with the baud rate and the serial dev the device is connected +# to, e.g.: +# $ ./sneaky_urc 115200 /dev/ttyUSB0 +# 4. run the test (e.g. make term) +# 5. inside the test console: +# a) run the `init` command (e.g. init 0 115200) +# b) run `sneaky_urc` command +# +# If the command echoing is enabled, you will miss the URCs, but the commands +# should work (e.g. the URC should not interfere with response parsing). +# +# If command echoing is enabled AND `MODULE_AT_URC` is defined, you should see +# *some* of the URCs being parsed. + +import sys +import pexpect + +baud = sys.argv[1] +ser = sys.argv[2] +tty = pexpect.spawn(f'picocom --baud {baud} {ser}') + +# EOL sent by the device +EOL_IN = '\r' +# EOL to send back to the device +EOL_OUT = '\r\n' +# Command echoing enabled +ECHO_ON = False + +CFUN_CMD = "AT+CFUN=1" + EOL_IN +CFUN_ERR_CMD = "AT+CFUN=8" + EOL_IN +CFUN_CME_CMD = "AT+CFUN=9" + EOL_IN +CEREG_CMD = "AT+CEREG?" + EOL_IN +USECMNG_CMD = "AT+USECMNG=0,0,\"cert\",128" + EOL_IN + +while True: + try: + idx = tty.expect_exact([CFUN_CMD, CFUN_ERR_CMD, CFUN_CME_CMD, CEREG_CMD, USECMNG_CMD]) + if idx == 0: + print(CFUN_CMD) + tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT) + if ECHO_ON: + tty.send(CFUN_CMD) + tty.send(EOL_OUT + "OK" + EOL_OUT) + if idx == 1: + print(CFUN_ERR_CMD) + tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT) + if ECHO_ON: + tty.send(CFUN_ERR_CMD) + tty.send(EOL_OUT + "ERROR" + EOL_OUT) + if idx == 2: + print(CFUN_CME_CMD) + tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT) + if ECHO_ON: + tty.send(CFUN_CME_CMD) + tty.send(EOL_OUT + "+CME ERROR:" + EOL_OUT) + elif idx == 3: + print(CEREG_CMD) + tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT) + if ECHO_ON: + tty.send(CEREG_CMD) + tty.send(EOL_OUT + "+CEREG: 0,1" + EOL_OUT) + tty.send(EOL_OUT + "OK" + EOL_OUT) + elif idx == 4: + print(USECMNG_CMD) + tty.send(EOL_OUT + "+CSCON: 1" + EOL_OUT) + if ECHO_ON: + tty.send(USECMNG_CMD) + tty.send(">") + except pexpect.EOF: + print("ERROR: EOF") + except pexpect.TIMEOUT: + print("ERROR: TIMEOUT") From 6425fda812b06a9261a53ddbbee78ed296362067 Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Fri, 10 Nov 2023 16:00:00 +0100 Subject: [PATCH 26/85] gcoap: Use millisecond timer for observe option values --- sys/Makefile.dep | 1 - sys/include/net/gcoap.h | 6 +++--- sys/net/application_layer/gcoap/gcoap.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 8a7771accc..5339c1d8d3 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -517,7 +517,6 @@ ifneq (,$(filter gcoap,$(USEMODULE))) USEMODULE += sock_udp USEMODULE += sock_util USEMODULE += ztimer_msec - USEMODULE += ztimer_usec USEMODULE += event_callback USEMODULE += event_timeout_ztimer USEMODULE += random diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h index 89070169d9..96f7229312 100644 --- a/sys/include/net/gcoap.h +++ b/sys/include/net/gcoap.h @@ -600,11 +600,11 @@ extern "C" { * @brief See CONFIG_GCOAP_OBS_VALUE_WIDTH */ #if (CONFIG_GCOAP_OBS_VALUE_WIDTH == 3) -#define GCOAP_OBS_TICK_EXPONENT (5) +#define GCOAP_OBS_TICK_EXPONENT (0) #elif (CONFIG_GCOAP_OBS_VALUE_WIDTH == 2) -#define GCOAP_OBS_TICK_EXPONENT (16) +#define GCOAP_OBS_TICK_EXPONENT (6) #elif (CONFIG_GCOAP_OBS_VALUE_WIDTH == 1) -#define GCOAP_OBS_TICK_EXPONENT (24) +#define GCOAP_OBS_TICK_EXPONENT (14) #endif /** diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 6b4641da12..d52ccdf596 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -1645,7 +1645,7 @@ int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code) if (coap_get_observe(pdu) == COAP_OBS_REGISTER) { /* generate initial notification value */ - uint32_t now = ztimer_now(ZTIMER_USEC); + uint32_t now = ztimer_now(ZTIMER_MSEC); pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF; coap_opt_add_uint(pdu, COAP_OPT_OBSERVE, pdu->observe_value); } @@ -1672,7 +1672,7 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, if (hdrlen > 0) { coap_pkt_init(pdu, buf, len, hdrlen); - uint32_t now = ztimer_now(ZTIMER_USEC); + uint32_t now = ztimer_now(ZTIMER_MSEC); pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF; coap_opt_add_uint(pdu, COAP_OPT_OBSERVE, pdu->observe_value); From a6a8f6e1cdd2afed48be1d71ba849f2fd760e4d3 Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Fri, 10 Nov 2023 16:01:10 +0100 Subject: [PATCH 27/85] gcoap: Separate out observe option generation in own function --- sys/net/application_layer/gcoap/gcoap.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index d52ccdf596..04954fcfb7 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -1630,6 +1630,14 @@ ssize_t gcoap_req_send_tl(const uint8_t *buf, size_t len, return ((res > 0 || res == -ENOTCONN) ? res : 0); } +static void _add_generated_observe_option(coap_pkt_t *pdu) +{ + /* generate initial notification value */ + uint32_t now = ztimer_now(ZTIMER_MSEC); + pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF; + coap_opt_add_uint(pdu, COAP_OPT_OBSERVE, pdu->observe_value); +} + int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code) { int header_len = coap_build_reply(pdu, code, buf, len, 0); @@ -1644,10 +1652,7 @@ int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code) pdu->payload_len = len - header_len; if (coap_get_observe(pdu) == COAP_OBS_REGISTER) { - /* generate initial notification value */ - uint32_t now = ztimer_now(ZTIMER_MSEC); - pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF; - coap_opt_add_uint(pdu, COAP_OPT_OBSERVE, pdu->observe_value); + _add_generated_observe_option(pdu); } return 0; @@ -1672,9 +1677,7 @@ int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, if (hdrlen > 0) { coap_pkt_init(pdu, buf, len, hdrlen); - uint32_t now = ztimer_now(ZTIMER_MSEC); - pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF; - coap_opt_add_uint(pdu, COAP_OPT_OBSERVE, pdu->observe_value); + _add_generated_observe_option(pdu); return GCOAP_OBS_INIT_OK; } From fe6c94070b3fa6e40741406124772ab2b84021ca Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 10 Nov 2023 15:13:32 +0100 Subject: [PATCH 28/85] boards/nucleo-wl55jc: Add missing `ARDUINO_I2C_UNO` The feature `arduino_i2c` was already provided, but the define this feature indicates to be present is missing. --- boards/nucleo-wl55jc/include/arduino_iomap.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/boards/nucleo-wl55jc/include/arduino_iomap.h b/boards/nucleo-wl55jc/include/arduino_iomap.h index b743d846c5..754c4687cc 100644 --- a/boards/nucleo-wl55jc/include/arduino_iomap.h +++ b/boards/nucleo-wl55jc/include/arduino_iomap.h @@ -30,6 +30,16 @@ extern "C" { #endif +/** + * @name Arduino's I2C buses + * @{ + */ +/** + * @brief The first I2C bus is where shields for the Arduino UNO expect it + */ +#define ARDUINO_I2C_UNO I2C_DEV(0) +/** @} */ + /** * @brief Mapping of MCU pins to Arduino pins * @{ From ca93d1793e151f16d7359193e664262b6068423b Mon Sep 17 00:00:00 2001 From: krzysztof-cabaj Date: Fri, 10 Nov 2023 14:46:05 +0100 Subject: [PATCH 29/85] boards/nucleo-f070rb: full SPI config in periph_conf.h --- boards/nucleo-f070rb/include/periph_conf.h | 49 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/boards/nucleo-f070rb/include/periph_conf.h b/boards/nucleo-f070rb/include/periph_conf.h index 2127ece9ef..9a0e907ab8 100644 --- a/boards/nucleo-f070rb/include/periph_conf.h +++ b/boards/nucleo-f070rb/include/periph_conf.h @@ -102,6 +102,21 @@ static const uart_conf_t uart_config[] = { #define UART_NUMOF ARRAY_SIZE(uart_config) /** @} */ +/** + * @name DMA streams configuration + * @{ + */ +static const dma_conf_t dma_config[] = { + { .stream = 1 }, + { .stream = 2 }, +}; + +#define DMA_SHARED_ISR_0 isr_dma1_ch2_3_dma2_ch1_2 +#define DMA_SHARED_ISR_0_STREAMS { 0, 1 } /* Indexes 0 and 1 of dma_config share the same isr */ + +#define DMA_NUMOF ARRAY_SIZE(dma_config) +/** @} */ + /** * @name PWM configuration * @{ @@ -148,9 +163,41 @@ static const adc_conf_t adc_config[] = { #define ADC_NUMOF ARRAY_SIZE(adc_config) /** @} */ -static const spi_conf_t spi_config[] = {{},}; +/** + * @name SPI configuration + * + * To find appriopate device and pins find in the MCU datasheet table + * concerning "Alternate function AF0 to AF7" a texts similar to + * SPI[X]_MOSI/_MISO/_SCK where SPI[X] is SPI device. + * + * For nucleo-f070rb this information is in the datasheet, Tables 11, 12 and 13, + * page 30. + * @{ + */ +static const spi_conf_t spi_config[] = { + { + .dev = SPI1, + .mosi_pin = GPIO_PIN(PORT_A, 7), + .miso_pin = GPIO_PIN(PORT_A, 6), + .sclk_pin = GPIO_PIN(PORT_A, 5), + .cs_pin = GPIO_UNDEF, + .mosi_af = GPIO_AF0, + .miso_af = GPIO_AF0, + .sclk_af = GPIO_AF0, + .cs_af = GPIO_AF0, + .rccmask = RCC_APB2ENR_SPI1EN, + .apbbus = APB2, +#ifdef MODULE_PERIPH_DMA + .tx_dma = 1, + .tx_dma_chan = 0, + .rx_dma = 0, + .rx_dma_chan = 0, +#endif + } +}; #define SPI_NUMOF ARRAY_SIZE(spi_config) +/** @} */ #ifdef __cplusplus } From 5abdc7eb5f9cb8b075be733d9d3c99205d607bd1 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 10 Nov 2023 18:35:09 +0100 Subject: [PATCH 30/85] cpu/sam0_common: adc_continuous: fix uninitialized access --- cpu/sam0_common/periph/adc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpu/sam0_common/periph/adc.c b/cpu/sam0_common/periph/adc.c index cf65a41605..71764dba72 100644 --- a/cpu/sam0_common/periph/adc.c +++ b/cpu/sam0_common/periph/adc.c @@ -357,6 +357,8 @@ static void _get_adcs(bool *adc0, bool *adc1) *adc1 = false; return; #else + *adc0 = false; + *adc1 = false; for (unsigned i = 0; i < ADC_NUMOF; ++i) { if (adc_channels[i].dev == ADC0) { *adc0 = true; From 396d3bbe2b4b61733d56b36c4e3d72d71d425f67 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 10 Nov 2023 19:20:16 +0100 Subject: [PATCH 31/85] drivers/mtd: introduce mtd_dev_get() --- drivers/include/mtd.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/include/mtd.h b/drivers/include/mtd.h index 6a638be274..eda7ded808 100644 --- a/drivers/include/mtd.h +++ b/drivers/include/mtd.h @@ -73,6 +73,7 @@ #ifndef MTD_H #define MTD_H +#include #include #include "xfa.h" @@ -517,6 +518,19 @@ int mtd_erase_sector(mtd_dev_t *mtd, uint32_t sector, uint32_t num); */ int mtd_power(mtd_dev_t *mtd, enum mtd_power_state power); +/** + * @brief Get an MTD device by index + * + * @param[in] idx Index of the MTD device + * + * @return MTD_0 for @p idx 0 and so on + * NULL if no MTD device exists for the given index + */ +static inline mtd_dev_t *mtd_dev_get(unsigned idx) +{ + return ((MTD_NUMOF != 0) && (idx < MTD_NUMOF)) ? mtd_dev_xfa[idx] : NULL; +} + #ifdef __cplusplus } #endif From 63cd55cc7bdb00902ac6437a6b3501d7051b22b2 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 10 Nov 2023 19:22:06 +0100 Subject: [PATCH 32/85] drivers/mtd_default: deprecate mtd_default_get_dev() --- drivers/include/mtd_default.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/include/mtd_default.h b/drivers/include/mtd_default.h index 671218635a..0c90a1d477 100644 --- a/drivers/include/mtd_default.h +++ b/drivers/include/mtd_default.h @@ -69,6 +69,8 @@ extern mtd_emulated_t mtd_emulated_dev0; /** * @brief Get the default MTD device by index * + * @deprecated Use @ref mtd_dev_get instead + * * @param[in] idx Index of the MTD device * * @return MTD_0 for @p idx 0 and so on From c2f93c05f1631ad211043e8e77858958ae9140a9 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Fri, 10 Nov 2023 19:24:18 +0100 Subject: [PATCH 33/85] treewide: mtd_default_get_dev() -> mtd_dev_get() --- sys/include/usb/usbus/msc.h | 2 +- sys/usb/usbus/msc/msc.c | 2 +- tests/drivers/mtd_raw/main.c | 8 ++++---- tests/sys/usbus_msc/main.c | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sys/include/usb/usbus/msc.h b/sys/include/usb/usbus/msc.h index 58e2e57d2c..9670530831 100644 --- a/sys/include/usb/usbus/msc.h +++ b/sys/include/usb/usbus/msc.h @@ -25,7 +25,7 @@ #include #include "usb/usbus.h" #include "usb/usbus/msc/scsi.h" -#include "mtd_default.h" +#include "mtd.h" #ifdef __cplusplus extern "C" { diff --git a/sys/usb/usbus/msc/msc.c b/sys/usb/usbus/msc/msc.c index dec3581360..7a3fb84fc6 100644 --- a/sys/usb/usbus/msc/msc.c +++ b/sys/usb/usbus/msc/msc.c @@ -374,7 +374,7 @@ static void _init(usbus_t *usbus, usbus_handler_t *handler) /* Auto-configure all MTD devices */ if (CONFIG_USBUS_MSC_AUTO_MTD) { for (unsigned i = 0; i < MTD_NUMOF; i++) { - usbus_msc_add_lun(usbus, mtd_default_get_dev(i)); + usbus_msc_add_lun(usbus, mtd_dev_get(i)); } } } diff --git a/tests/drivers/mtd_raw/main.c b/tests/drivers/mtd_raw/main.c index 8f10f9f11c..1252f28e86 100644 --- a/tests/drivers/mtd_raw/main.c +++ b/tests/drivers/mtd_raw/main.c @@ -25,7 +25,7 @@ #include #include "od.h" -#include "mtd_default.h" +#include "mtd.h" #include "shell.h" #include "board.h" #include "macros/units.h" @@ -45,7 +45,7 @@ static mtd_dev_t *_get_dev(int argc, char **argv) return NULL; } - return mtd_default_get_dev(idx); + return mtd_dev_get(idx); } static uint64_t _get_size(mtd_dev_t *dev) @@ -283,7 +283,7 @@ static int cmd_info(int argc, char **argv) for (unsigned i = 0; i < MTD_NUMOF; ++i) { printf(" -=[ MTD_%d ]=-\n", i); - _print_info(mtd_default_get_dev(i)); + _print_info(mtd_dev_get(i)); } return 0; } @@ -453,7 +453,7 @@ int main(void) for (unsigned i = 0; i < MTD_NUMOF; ++i) { printf("init MTD_%d… ", i); - mtd_dev_t *dev = mtd_default_get_dev(i); + mtd_dev_t *dev = mtd_dev_get(i); int res = mtd_init(dev); if (res) { printf("error: %d\n", res); diff --git a/tests/sys/usbus_msc/main.c b/tests/sys/usbus_msc/main.c index 56020aff48..07c20d8d37 100644 --- a/tests/sys/usbus_msc/main.c +++ b/tests/sys/usbus_msc/main.c @@ -48,7 +48,7 @@ MTD_EMULATED_DEV(0, SECTOR_COUNT, PAGES_PER_SECTOR, PAGE_SIZE); #endif /* MODULE_MTD_EMULATED */ -#include "mtd_default.h" +#include "mtd.h" #include "shell.h" #include "usb/usbus.h" #include "usb/usbus/msc.h" @@ -76,7 +76,7 @@ static int _cmd_add_lun(int argc, char **argv) puts("error: invalid MTD device specified"); return -2; } - mtd_dev = mtd_default_get_dev(dev); + mtd_dev = mtd_dev_get(dev); ret = usbus_msc_add_lun(usbus, mtd_dev); if (ret != 0) { printf("Cannot add LUN device (error:%d %s)\n", ret, strerror(-ret)); @@ -104,7 +104,7 @@ static int _cmd_remove_lun(int argc, char **argv) puts("error: invalid MTD device specified"); return -2; } - mtd_dev = mtd_default_get_dev(dev); + mtd_dev = mtd_dev_get(dev); ret = usbus_msc_remove_lun(usbus, mtd_dev); if (ret == -EAGAIN) { printf("MTD device was not registered\n"); From 744f3ecbbb336b30cedbc311d1b59c551b1de941 Mon Sep 17 00:00:00 2001 From: krzysztof-cabaj Date: Fri, 10 Nov 2023 21:39:31 +0100 Subject: [PATCH 34/85] tests/drivers/cc110x: add nucleo-f070rb to Makefile.ci --- tests/drivers/cc110x/Makefile.ci | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/drivers/cc110x/Makefile.ci b/tests/drivers/cc110x/Makefile.ci index ea0a79974d..39b64861f1 100644 --- a/tests/drivers/cc110x/Makefile.ci +++ b/tests/drivers/cc110x/Makefile.ci @@ -23,6 +23,7 @@ BOARD_INSUFFICIENT_MEMORY := \ msb-430h \ nucleo-f031k6 \ nucleo-f042k6 \ + nucleo-f070rb \ nucleo-f072rb \ nucleo-f302r8 \ nucleo-f303k8 \ From ab8649bd2f61f0fd57fd2d1504a4d520c8316032 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 2 Aug 2023 18:55:28 +0200 Subject: [PATCH 35/85] pkg/esp32_sdk: patch for LCD driver on ESP32-S3 --- .../0032-hal-gdma-include-stddef.h-for-NULL.patch | Bin 0 -> 684 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pkg/esp32_sdk/patches/0032-hal-gdma-include-stddef.h-for-NULL.patch diff --git a/pkg/esp32_sdk/patches/0032-hal-gdma-include-stddef.h-for-NULL.patch b/pkg/esp32_sdk/patches/0032-hal-gdma-include-stddef.h-for-NULL.patch new file mode 100644 index 0000000000000000000000000000000000000000..d9fa59f174a881e87b3f2076150291bdc86bda98 GIT binary patch literal 684 zcmb7BO>5jR5WUZ@cqEs!UfcSx*SjVJhnBR5v=p`>hf;zp$sfTh8QTjz^v72Y354Dj zMg#Mh_vVe}Zt_RuoENIDwP0EqwwF!T?8Gj&+6ZG+Aq&$KwG|TIyu-nc-~|{fUjl+L zPIp13#Fvv(6ArrblkOH=w`*O^uXW<=azmfhVoN+)vw^_vsSW%f%w@@UWyTP(pht&O z{cQD8;@6McpP#-$id2f<=+uz5=BP^aP7fzzF)zm0Ch5@l3Ev;??}_3#CTM>gy|Zqa z)0JfBQHoinue29z6?z(m!2Ju3h(c=m!9sV+wW~cIrp$I)dhf2H4@9PK8pLtiFHq@O z1M0KdsdvWyLB?dRi|t-!I!Tl?Jd@jcUu$s2wpq6Bd8QAFq6lYtsVcG3!8>gU6kWdE Date: Wed, 2 Aug 2023 18:59:58 +0200 Subject: [PATCH 36/85] cpu/esp32/esp-idf: add LCD driver of ESP-IDF --- cpu/esp32/esp-idf/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpu/esp32/esp-idf/Makefile b/cpu/esp32/esp-idf/Makefile index baf5c761a6..47090883ea 100644 --- a/cpu/esp32/esp-idf/Makefile +++ b/cpu/esp32/esp-idf/Makefile @@ -36,6 +36,10 @@ ifneq (,$(filter esp_idf_heap,$(USEMODULE))) DIRS += heap endif +ifneq (,$(filter esp_idf_lcd,$(USEMODULE))) + DIRS += lcd +endif + ifneq (,$(filter esp_idf_nvs_flash,$(USEMODULE))) DIRS += nvs_flash endif From 70053c5284f38f053904a8f4eb00c8a245784601 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 2 Aug 2023 19:06:33 +0200 Subject: [PATCH 37/85] cpu/esp32: add LCD low-level parallel interface suppport --- cpu/esp32/Kconfig.common | 1 + cpu/esp32/Makefile | 4 + cpu/esp32/Makefile.dep | 6 + cpu/esp32/Makefile.include | 5 + cpu/esp32/esp-idf/lcd/Kconfig | 16 ++ cpu/esp32/esp-idf/lcd/Makefile | 36 +++++ cpu/esp32/esp-lcd/Kconfig | 54 +++++++ cpu/esp32/esp-lcd/Makefile | 9 ++ cpu/esp32/esp-lcd/esp_lcd_gpio.c | 269 +++++++++++++++++++++++++++++++ cpu/esp32/esp-lcd/esp_lcd_mcu.c | 260 +++++++++++++++++++++++++++++ cpu/esp32/include/irq_arch.h | 1 + cpu/esp32/include/sdkconfig.h | 11 ++ cpu/esp32/irq_arch.c | 20 +++ 13 files changed, 692 insertions(+) create mode 100644 cpu/esp32/esp-idf/lcd/Kconfig create mode 100644 cpu/esp32/esp-idf/lcd/Makefile create mode 100644 cpu/esp32/esp-lcd/Kconfig create mode 100644 cpu/esp32/esp-lcd/Makefile create mode 100644 cpu/esp32/esp-lcd/esp_lcd_gpio.c create mode 100644 cpu/esp32/esp-lcd/esp_lcd_mcu.c diff --git a/cpu/esp32/Kconfig.common b/cpu/esp32/Kconfig.common index 35c9ed6016..9551dc5f4d 100644 --- a/cpu/esp32/Kconfig.common +++ b/cpu/esp32/Kconfig.common @@ -35,6 +35,7 @@ rsource "bootloader/Kconfig" rsource "esp-ble-nimble/Kconfig" rsource "esp-idf/Kconfig" rsource "esp-idf-api/Kconfig" +rsource "esp-lcd/Kconfig" rsource "periph/Kconfig" endif diff --git a/cpu/esp32/Makefile b/cpu/esp32/Makefile index 91a69869e9..cc18edea8c 100644 --- a/cpu/esp32/Makefile +++ b/cpu/esp32/Makefile @@ -29,6 +29,10 @@ ifneq (, $(filter esp_freertos, $(USEMODULE))) DIRS += freertos endif +ifneq (, $(filter esp_lcd, $(USEMODULE))) + DIRS += esp-lcd +endif + ifneq (, $(filter stdio_usb_serial_jtag, $(USEMODULE))) DIRS += stdio_usb_serial_jtag endif diff --git a/cpu/esp32/Makefile.dep b/cpu/esp32/Makefile.dep index 09521bd845..ca2556901e 100644 --- a/cpu/esp32/Makefile.dep +++ b/cpu/esp32/Makefile.dep @@ -128,6 +128,12 @@ ifneq (,$(filter esp_idf_heap,$(USEMODULE))) USEPKG += tlsf endif +ifneq (,$(filter lcd_parallel_ll_mcu,$(USEMODULE))) + USEMODULE += esp_lcd + USEMODULE += esp_idf_lcd + USEMODULE += esp_idf_heap +endif + ifneq (,$(filter mtd periph_flashpage,$(USEMODULE))) USEMODULE += esp_idf_spi_flash endif diff --git a/cpu/esp32/Makefile.include b/cpu/esp32/Makefile.include index a0a62d24fc..3b1bed8517 100644 --- a/cpu/esp32/Makefile.include +++ b/cpu/esp32/Makefile.include @@ -113,6 +113,7 @@ PSEUDOMODULES += esp_hw_counter PSEUDOMODULES += esp_idf_gpio_hal PSEUDOMODULES += esp_i2c_hw PSEUDOMODULES += esp_jtag +PSEUDOMODULES += esp_lcd_gpio PSEUDOMODULES += esp_rtc_timer_32k PSEUDOMODULES += esp_spi_ram PSEUDOMODULES += esp_spi_oct @@ -167,6 +168,10 @@ ifneq (,$(filter esp_spi_ram,$(USEMODULE))) INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_hw_support/include/soc/$(CPU_FAM) endif +ifneq (,$(filter esp_idf_lcd,$(USEMODULE))) + INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/include +endif + ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE))) INCLUDES += -I$(ESP32_SDK_DIR)/components/spi_flash/include endif diff --git a/cpu/esp32/esp-idf/lcd/Kconfig b/cpu/esp32/esp-idf/lcd/Kconfig new file mode 100644 index 0000000000..8a8156adb7 --- /dev/null +++ b/cpu/esp32/esp-idf/lcd/Kconfig @@ -0,0 +1,16 @@ +# Copyright (c) 2022 Gunar Schorcht +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +config MODULE_ESP_IDF_LCD + bool + depends on TEST_KCONFIG + depends on MODULE_ESP_IDF && HAS_ESP_LCD + + default y if MODULE_LCD_PARALLEL_LL_MCU + + help + ESP-IDF code for peripheral GPIO. diff --git a/cpu/esp32/esp-idf/lcd/Makefile b/cpu/esp32/esp-idf/lcd/Makefile new file mode 100644 index 0000000000..7a97024cbe --- /dev/null +++ b/cpu/esp32/esp-idf/lcd/Makefile @@ -0,0 +1,36 @@ +MODULE = esp_idf_lcd + +# source files to be compiled for this module +ESP32_SDK_SRC = \ + components/esp_lcd/src/esp_lcd_common.c \ + components/esp_lcd/src/esp_lcd_panel_io.c \ + components/esp_pm/pm_locks.c \ + components/soc/$(CPU_FAM)/lcd_periph.c \ + # + +ifeq (esp32s3,$(CPU_FAM)) + ESP32_SDK_SRC += \ + components/driver/gdma.c \ + components/esp_lcd/src/esp_lcd_panel_io_i80.c \ + components/hal/gdma_hal.c \ + components/hal/lcd_hal.c \ + components/soc/$(CPU_FAM)/gdma_periph.c \ + # +else ifneq (,$(filter esp32 esp32s2,$(CPU_FAM))) + ESP32_SDK_SRC = \ + components/driver/i2s.c \ + components/esp_lcd/src/esp_lcd_panel_io_i2s.c \ + components/hal/i2s_hal.c \ + # +endif + +# additional include pathes required by this module +INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/include +INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/interface + +include $(RIOTBASE)/Makefile.base + +ESP32_SDK_BIN = $(BINDIR)/$(MODULE) + +include ../esp_idf.mk +include ../esp_idf_cflags.mk diff --git a/cpu/esp32/esp-lcd/Kconfig b/cpu/esp32/esp-lcd/Kconfig new file mode 100644 index 0000000000..0f534c6f1e --- /dev/null +++ b/cpu/esp32/esp-lcd/Kconfig @@ -0,0 +1,54 @@ +# Copyright (c) 2023 Gunar Schorcht +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_ESP_LCD + bool "Enable LCD low-level parallel interface driver" + depends on MODULE_LCD + default y if HAVE_LCD_PARALLEL_LL_MCU + help + Enabe the MCU-driven low-level MCU 8080 8-/16-bit parallel interface + driver. + +if MODULE_ESP_LCD + +config MODULE_ESP_LCD_GPIO + bool "GPIO-driven low-level parallel interface driver" + depends on !CPU_FAM_ESP32 && !CPU_FAM_ESP32S2 && !CPU_FAM_ESP32S3 + default y + help + The ESP32x SoC variant used does not have a peripheral for the parallel + low-level interface. However, it can be emulated with special low-level + GPIO operations. It is faster than the GPIO-driven 8-/16-bit parallel + interface implemented in the LCD driver, but requires 4 kByte RAM for + 8-bit data bus width and 8 kByte RAM for 16-bit data bus width. + +config LCD_WRITE_CLOCK_MHZ + int "LCD write clock rate in MHz" + range 1 80 + depends on CPU_FAM_ESP32 || CPU_FAM_ESP32S2 || CPU_FAM_ESP32S3 + default 10 if CPU_FAM_ESP32 + default 40 if CPU_FAM_ESP32S2 + default 20 if CPU_FAM_ESP32S3 + help + Defines the clock rate that is used for the LCD write signal. It + depends on used ESP32x SoC variant and used display interface. + +config LCD_DATA_BUF_SIZE + int "LCD data buffer size in byte" + depends on CPU_FAM_ESP32 || CPU_FAM_ESP32S2 || CPU_FAM_ESP32S3 + default 512 + help + Defines the size of the buffers used to write data to the LCD + screen. Since double buffering is used, there are two buffers + of this size. One buffer is used first by the LCD driver to + write the data that needs to be transferred to the LCD, and + one buffer from which the DMA then transfers the data to the + LCD peripherals. This allows data to be written before the + DMA transfer is complete. The larger the buffers, the better + the performance, but the higher the memory requirements. + +endif diff --git a/cpu/esp32/esp-lcd/Makefile b/cpu/esp32/esp-lcd/Makefile new file mode 100644 index 0000000000..0281ca80a6 --- /dev/null +++ b/cpu/esp32/esp-lcd/Makefile @@ -0,0 +1,9 @@ +MODULE = esp_lcd + +ifneq (,$(filter esp32 esp32s2 esp32s3,$(CPU_FAM))) + SRC = esp_lcd_mcu.c +else + SRC = esp_lcd_gpio.c +endif + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/esp-lcd/esp_lcd_gpio.c b/cpu/esp32/esp-lcd/esp_lcd_gpio.c new file mode 100644 index 0000000000..12f2dcd51b --- /dev/null +++ b/cpu/esp32/esp-lcd/esp_lcd_gpio.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief GPIO-driven low-Level parallel interface implementation for LCDs + * + * @author Gunar Schorcht + * @} + */ + +#include + +#include "periph/gpio.h" +#include "lcd.h" +#include "ztimer.h" + +#if MODULE_ESP_LCD_GPIO + +#include "soc/gpio_reg.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +typedef struct { + uint32_t set_mask_0; /* port 0 set mask */ + uint32_t set_mask_1; /* port 0 set mask */ + uint32_t clr_mask_0; /* port 1 clear mask */ + uint32_t clr_mask_1; /* port 1 clear mask */ +} _pin_mask_t; + +static _pin_mask_t _low_byte_masks[256] = {}; + +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) +static _pin_mask_t _high_byte_masks[256] = {}; +#endif + +/* + * Following functions are not implemented by intention to let the + * GPIO-driven low-level implementation handle the configuration + * of the GPIOs. The function `_lcd_ll_mcu_set_data_dir` is used to + * initialize the GPIO masks when the clear masks are completely 0. + */ +#if 0 + +static void _lcd_ll_mcu_init(lcd_t *dev) +{ + (void)dev; +} + +static void _lcd_ll_mcu_cmd_start(lcd_t *dev, uint8_t cmd, bool cont) +{ + (void)dev; + (void)cmd; + (void)cont; +} + +#endif + +static void _lcd_ll_mcu_set_data_dir(lcd_t *dev, bool output) +{ + DEBUG("[lcd_ll_mcu] %s %u\n", __func__, output); + + /* sanity check to ensure that data pins can be handled as array */ + assert((&dev->params->d7_pin - &dev->params->d0_pin) == 7); +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + assert((&dev->params->d15_pin - &dev->params->d8_pin) == 7); +#endif + + if ((_low_byte_masks[0].clr_mask_0 == 0) && + (_low_byte_masks[0].clr_mask_1 == 0)) { + /* initialize the mask array if it is not yet initialized */ + const gpio_t *pins = &dev->params->d0_pin; + + for (unsigned data = 0; data < 256; data++) { + for (unsigned i = 0; i < 8; i++) { + if (data & (1 << i)) { + /* set mask */ + if (pins[i] < 32) { + _low_byte_masks[data].set_mask_0 |= 1 << pins[i]; + } + else { + _low_byte_masks[data].set_mask_1 |= 1 << (pins[i] - 32); + } + } + else { + /* clear mask */ + if (pins[i] < 32) { + _low_byte_masks[data].clr_mask_0 |= 1 << pins[i]; + } + else { + _low_byte_masks[data].clr_mask_1 |= 1 << (pins[i] - 32); + } + } + } + } +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + pins = &dev->params->d8_pin; + + for (unsigned data = 0; data < 256; data++) { + for (unsigned i = 0; i < 8; i++) { + if (data & (1 << i)) { + /* set mask */ + if (pins[i] < 32) { + _high_byte_masks[data].set_mask_0 |= 1 << pins[i]; + } + else { + _high_byte_masks[data].set_mask_1 |= 1 << (pins[i] - 32); + } + } + else { + /* clear mask */ + if (pins[i] < 32) { + _high_byte_masks[data].clr_mask_0 |= 1 << pins[i]; + } + else { + _high_byte_masks[data].clr_mask_1 |= 1 << (pins[i] - 32); + } + } + } + } +#endif + } + + gpio_init(dev->params->d0_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d1_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d2_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d3_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d4_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d5_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d6_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d7_pin, output ? GPIO_OUT : GPIO_IN); +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + gpio_init(dev->params->d8_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d9_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d10_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d11_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d12_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d13_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d14_pin, output ? GPIO_OUT : GPIO_IN); + gpio_init(dev->params->d15_pin, output ? GPIO_OUT : GPIO_IN); +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ +} + +static void _lcd_ll_mcu_write_data(lcd_t *dev, bool cont, + uint16_t data, unsigned pin_num) +{ + if (gpio_is_valid(dev->params->cs_pin)) { + gpio_clear(dev->params->cs_pin); + } + + gpio_clear(dev->params->wrx_pin); + + uint8_t _byte = data & 0xff; + + uint32_t set_mask_0 = _low_byte_masks[_byte].set_mask_0; + uint32_t clr_mask_0 = _low_byte_masks[_byte].clr_mask_0; + uint32_t set_mask_1 = _low_byte_masks[_byte].set_mask_1; + uint32_t clr_mask_1 = _low_byte_masks[_byte].clr_mask_1; + +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + _byte = data >> 8; + + set_mask_0 |= _high_byte_masks[_byte].set_mask_0; + clr_mask_0 |= _high_byte_masks[_byte].clr_mask_0; + set_mask_1 |= _high_byte_masks[_byte].set_mask_1; + clr_mask_1 |= _high_byte_masks[_byte].clr_mask_1; +#endif + + *((uint32_t *)GPIO_OUT_W1TS_REG) = set_mask_0; + *((uint32_t *)GPIO_OUT_W1TC_REG) = clr_mask_0; + *((uint32_t *)GPIO_OUT1_W1TS_REG) = set_mask_1; + *((uint32_t *)GPIO_OUT1_W1TC_REG) = clr_mask_1; + + gpio_set(dev->params->wrx_pin); + + if (gpio_is_valid(dev->params->cs_pin) && !cont) { + gpio_set(dev->params->cs_pin); + } +} + +static uint16_t _lcd_ll_mcu_read_data(lcd_t *dev, bool cont, unsigned pin_num) +{ + const gpio_t *pins = &dev->params->d0_pin; + + if (gpio_is_valid(dev->params->cs_pin)) { + gpio_clear(dev->params->cs_pin); + } + + gpio_clear(dev->params->rdx_pin); + + uint32_t in_0 = *((uint32_t *)GPIO_IN_REG); + uint32_t in_1 = *((uint32_t *)GPIO_IN1_REG); + + gpio_set(dev->params->rdx_pin); + + if (gpio_is_valid(dev->params->cs_pin) && !cont) { + gpio_set(dev->params->cs_pin); + }; + + uint16_t in = 0; + + for (unsigned i = 0; i < pin_num; i++) { + if (pins[i] < 32) { + in |= in_0 & (1 << pins[i]) ? 1 : 0; + } + else { + in |= in_1 & (1 << (pins[i] - 32)) ? 1 : 0; + } + } + + return in; +} + +static void _lcd_ll_mcu_write_byte(lcd_t *dev, bool cont, uint8_t out) +{ + DEBUG("[lcd_ll_mcu] write byte: %02x\n", out); + + _lcd_ll_mcu_write_data(dev, cont, out, 8); +} + +static uint8_t _lcd_ll_mcu_read_byte(lcd_t *dev, bool cont) +{ + return _lcd_ll_mcu_read_data(dev, cont, 8); +} + +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + +static void _lcd_ll_mcu_write_word(lcd_t *dev, bool cont, uint16_t out) +{ + DEBUG("[lcd_ll_mcu] write word: %04x\n", out); + + _lcd_ll_mcu_write_data(dev, cont, out, 16); +} + +static uint16_t _lcd_ll_mcu_read_word(lcd_t *dev, bool cont) +{ + return _lcd_ll_mcu_read_data(dev, cont, 16); +} + +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ + +const lcd_ll_par_driver_t lcd_ll_par_driver = { + .init = lcd_ll_par_gpio_init, /* GPIO-driven `init` is used */ + .set_data_dir = _lcd_ll_mcu_set_data_dir, + .cmd_start = lcd_ll_par_gpio_cmd_start, /* GPIO-driven `cmd_start` is used */ + .write_byte = _lcd_ll_mcu_write_byte, + .read_byte = _lcd_ll_mcu_read_byte, +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + .write_word = _lcd_ll_mcu_write_word, + .read_word = _lcd_ll_mcu_read_word, +#endif +}; + +#else /* MODULE_ESP_LCD_GPIO */ + +/* the GPIO-driven low-level interface is not used */ +const lcd_ll_par_driver_t lcd_ll_par_driver = { +}; + +#endif /* MODULE_ESP_LCD_GPIO */ diff --git a/cpu/esp32/esp-lcd/esp_lcd_mcu.c b/cpu/esp32/esp-lcd/esp_lcd_mcu.c new file mode 100644 index 0000000000..0439bf4030 --- /dev/null +++ b/cpu/esp32/esp-lcd/esp_lcd_mcu.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Peripheral low-Level parallel interface implementation for LCDs + * + * @author Gunar Schorcht + * @} + */ + +#include +#include + +#include "lcd.h" +#include "lcd_internal.h" +#include "log.h" +#include "macros/units.h" +#include "periph/gpio.h" +#include "ztimer.h" + +#include "esp_lcd_panel_io.h" +#include "soc/gpio_reg.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#if !defined(CPU_FAM_ESP32) && !defined(CPU_FAM_ESP32S2) && !defined(CPU_FAM_ESP32S3) +#error "ESP32x SoC family not supported" +#endif + +#ifndef CONFIG_LCD_WRITE_CLOCK_MHZ +#if CONFIG_LCD_I80_COLOR_IN_PSRAM +/* PCLK has to be low enough for SPI RAM */ +#define CONFIG_LCD_WRITE_CLOCK_MHZ 2 +#else + +#if defined(CPU_FAM_ESP32S3) +#define CONFIG_LCD_WRITE_CLOCK_MHZ 20 +#elif defined(CPU_FAM_ESP32S2) +#define CONFIG_LCD_WRITE_CLOCK_MHZ 40 +#else /* ESP32 */ +#define CONFIG_LCD_WRITE_CLOCK_MHZ 10 +#endif + +#endif /* CONFIG_LCD_I80_COLOR_IN_PSRAM */ +#endif /* CONFIG_LCD_WRITE_CLOCK_MHZ */ + +static_assert(CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE >= 32, + "CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE mus be at least 32"); + +/* ESP32x SoCs support only one LCD peripheral so we can use single instances + * of the following variables */ + +int _cmd = -1; /* means no command needed in ESP-IDF */ + +size_t _idx_bytes = 0; +uint8_t _data_bytes[CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE]; +uint8_t _trans_bytes[CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE]; + +esp_lcd_i80_bus_handle_t _i80_bus_handle = NULL; +esp_lcd_panel_io_handle_t _i80_io_handle = NULL; + +/* indicates that a transfer of the data buffer is still in progress and must + * not be overwritten */ +static bool _dma_transfer_in_progress = false; + +static bool _dma_transfer_done(esp_lcd_panel_io_handle_t io_handle, + esp_lcd_panel_io_event_data_t *io_event_data, + void *user_ctx) +{ + (void)io_handle; + (void)io_event_data; + (void)user_ctx; + + _dma_transfer_in_progress = false; + + return false; +} + +static void _lcd_ll_mcu_init(lcd_t *dev) +{ + esp_lcd_i80_bus_config_t i80_bus_config = { + .dc_gpio_num = dev->params->dcx_pin, + .wr_gpio_num = dev->params->wrx_pin, + .clk_src = LCD_CLK_SRC_PLL160M, +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + .data_gpio_nums = { + dev->params->d0_pin, + dev->params->d1_pin, + dev->params->d2_pin, + dev->params->d3_pin, + dev->params->d4_pin, + dev->params->d5_pin, + dev->params->d6_pin, + dev->params->d7_pin, + dev->params->d8_pin, + dev->params->d9_pin, + dev->params->d10_pin, + dev->params->d11_pin, + dev->params->d12_pin, + dev->params->d13_pin, + dev->params->d14_pin, + dev->params->d15_pin, + }, + .bus_width = 16, +#else + .data_gpio_nums = { + dev->params->d0_pin, + dev->params->d1_pin, + dev->params->d2_pin, + dev->params->d3_pin, + dev->params->d4_pin, + dev->params->d5_pin, + dev->params->d6_pin, + dev->params->d7_pin, + }, + .bus_width = 8, +#endif + .max_transfer_bytes = dev->params->rgb_channels * 40 * sizeof(uint16_t), + }; + + esp_lcd_panel_io_i80_config_t i80_io_config = { + .cs_gpio_num = gpio_is_valid(dev->params->cs_pin) ? (int)dev->params->cs_pin + : -1, + .pclk_hz = MHZ(CONFIG_LCD_WRITE_CLOCK_MHZ), + .trans_queue_depth = 10, + .dc_levels = { + .dc_idle_level = 0, + .dc_cmd_level = 0, + .dc_dummy_level = 1, + .dc_data_level = 1, + }, + .on_color_trans_done = _dma_transfer_done, + .user_ctx = NULL, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + + esp_lcd_new_i80_bus(&i80_bus_config, &_i80_bus_handle); + esp_lcd_new_panel_io_i80(_i80_bus_handle, &i80_io_config, &_i80_io_handle); + + if (gpio_is_valid(dev->params->rdx_pin)) { + gpio_init(dev->params->rdx_pin, GPIO_IN_PU); + gpio_set(dev->params->rdx_pin); + } + + if (gpio_is_valid(dev->params->cs_pin)) { + gpio_init(dev->params->cs_pin, GPIO_OUT); + gpio_clear(dev->params->cs_pin); + } +} + +static void _lcd_ll_mcu_set_data_dir(lcd_t *dev, bool output) +{ + (void)dev; + (void)output; + LOG_ERROR("[lcd_ll_mcu] set dir: %d is not supported\n", output); + /* not supported yet */ +} + +static void _lcd_ll_mcu_cmd_start(lcd_t *dev, uint8_t cmd, bool cont) +{ + DEBUG("[lcd_ll_mcu] write cmd: %02x\n", cmd); + + if (!cont) { + /* cmd without parameters */ + esp_lcd_panel_io_tx_param(_i80_io_handle, cmd, NULL, 0); + _cmd = -1; + } + else { + /* cmd with parameters */ + _cmd = cmd; + } +} + +static void _lcd_ll_mcu_transfer(lcd_t *dev, bool cont) +{ + if (!cont) { + /* if no further data follow, send the command with the data in the buffer */ + esp_lcd_panel_io_tx_param(_i80_io_handle, _cmd, _data_bytes, _idx_bytes); + _cmd = -1; + _idx_bytes = 0; + } + else if (_idx_bytes == CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE) { + /* spin lock as long as a DMA data transfer is still in progress */ + while (_dma_transfer_in_progress) {} + + /* copy data buffer to the DMA transfer buffer */ + memcpy(_trans_bytes, _data_bytes, _idx_bytes); + /* start DMA data transfer */ + _dma_transfer_in_progress = true; + esp_lcd_panel_io_tx_color(_i80_io_handle, _cmd, _data_bytes, _idx_bytes); + + /* It should only be possible to follow more data than + * CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE with the RAMWR command. + * Transferring more data to continue the operation with cmd=-1 does + * not seem to work. Therefore a RAMWRC generated in this + * case for further data */ + _cmd = (_cmd == LCD_CMD_RAMWR) ? LCD_CMD_RAMWRC : _cmd; + _idx_bytes = 0; + } +} + +static void _lcd_ll_mcu_write_byte(lcd_t *dev, bool cont, uint8_t out) +{ + DEBUG("[lcd_ll_mcu] write byte: %02x\n", out); + + _data_bytes[_idx_bytes++] = out; + /* transfer the data if necessary */ + _lcd_ll_mcu_transfer(dev, cont); +} + +static uint8_t _lcd_ll_mcu_read_byte(lcd_t *dev, bool cont) +{ + LOG_ERROR("[lcd_ll_mcu] read from LCD is not supported\n"); + return 0; +} + +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + +static void _lcd_ll_mcu_write_word(lcd_t *dev, bool cont, uint16_t out) +{ + DEBUG("[lcd_ll_mcu] write word: %04x\n", out); + + /* out is given in BE order */ + _data_bytes[_idx_bytes++] = out >> 8; + _data_bytes[_idx_bytes++] = out & 0xff; + /* transfer the data if necessary */ + _lcd_ll_mcu_transfer(dev, cont); +} + +static uint16_t _lcd_ll_mcu_read_word(lcd_t *dev, bool cont) +{ + LOG_ERROR("[lcd_ll_mcu] read from LCD is not supported\n"); + /* not supported yet */ + return 0; +} + +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ + +const lcd_ll_par_driver_t lcd_ll_par_driver = { + .init = _lcd_ll_mcu_init, + .set_data_dir = _lcd_ll_mcu_set_data_dir, + .cmd_start = _lcd_ll_mcu_cmd_start, + .write_byte = _lcd_ll_mcu_write_byte, + .read_byte = _lcd_ll_mcu_read_byte, +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + .write_word = _lcd_ll_mcu_write_word, + .read_word = _lcd_ll_mcu_read_word, +#endif +}; diff --git a/cpu/esp32/include/irq_arch.h b/cpu/esp32/include/irq_arch.h index a321c44d22..da7c759a7b 100644 --- a/cpu/esp32/include/irq_arch.h +++ b/cpu/esp32/include/irq_arch.h @@ -47,6 +47,7 @@ extern "C" { #define CPU_INUM_WDT 13 /**< Level interrupt with low priority 1 */ #define CPU_INUM_SOFTWARE 17 /**< Level interrupt with low priority 1 */ #define CPU_INUM_ETH 18 /**< Level interrupt with low priority 1 */ +#define CPU_INUM_LCD 18 /**< Level interrupt with low priority 1 */ #define CPU_INUM_TIMER 19 /**< Level interrupt with medium priority 2 */ #define CPU_INUM_FRC2 20 /**< Level interrupt with medium priority 2 */ #define CPU_INUM_SYSTIMER 20 /**< Level interrupt with medium priority 2 */ diff --git a/cpu/esp32/include/sdkconfig.h b/cpu/esp32/include/sdkconfig.h index ae581d4a25..489f279684 100644 --- a/cpu/esp32/include/sdkconfig.h +++ b/cpu/esp32/include/sdkconfig.h @@ -233,6 +233,17 @@ #endif /* !CONFIG_ESP_FLASHPAGE_CAPACITY */ +/** + * LCD driver configuration + */ +#if MODULE_ESP_IDF_LCD +#ifndef CONFIG_LCD_DATA_BUF_SIZE +#define CONFIG_LCD_DATA_BUF_SIZE 512 +#endif + +#define CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE CONFIG_LCD_DATA_BUF_SIZE +#endif + #endif /* DOXYGEN */ /** diff --git a/cpu/esp32/irq_arch.c b/cpu/esp32/irq_arch.c index 37f96cb191..7510df5052 100644 --- a/cpu/esp32/irq_arch.c +++ b/cpu/esp32/irq_arch.c @@ -87,10 +87,19 @@ static const struct intr_handle_data_t _irq_data_table[] = { { ETS_USB_SERIAL_JTAG_INTR_SOURCE, CPU_INUM_SERIAL_JTAG, 1 }, #endif { ETS_RMT_INTR_SOURCE, CPU_INUM_RMT, 1 }, +#if defined(CPU_FAM_ESP32) || defined(CPU_FAM_ESP32S2) + { ETS_I2S0_INTR_SOURCE, CPU_INUM_LCD, 1 }, +#elif defined(CPU_FAM_ESP32S3) + { ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 }, +#endif }; #define IRQ_DATA_TABLE_SIZE ARRAY_SIZE(_irq_data_table) +#if defined(CPU_FAM_ESP32) && MODULE_ESP_LCD && MODULE_ESP_ETH +#error "esp_eth and esp_lcd can't be used at the same time because of an interrupt conflict" +#endif + void esp_irq_init(void) { #ifdef SOC_CPU_HAS_FLEXIBLE_INTC @@ -172,6 +181,17 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, return ESP_OK; } +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, + uint32_t reg, uint32_t mask, + intr_handler_t handler, + void *arg, intr_handle_t *ret_handle) +{ + /* TODO status register and status mask handling for shared interrupts */ + (void)reg; + (void)mask; + return esp_intr_alloc(source, flags, handler, arg, ret_handle); +} + esp_err_t esp_intr_free(intr_handle_t handle) { return esp_intr_disable(handle); From 4e8596814570b2fb7563aa3014bcc79fe285b2c1 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sun, 30 Jul 2023 11:55:15 +0200 Subject: [PATCH 38/85] boards/esp32s3-wt32-sc01-plus: enable LCD parallel interface --- boards/esp32s3-wt32-sc01-plus/Kconfig | 1 + boards/esp32s3-wt32-sc01-plus/Makefile.dep | 1 + 2 files changed, 2 insertions(+) diff --git a/boards/esp32s3-wt32-sc01-plus/Kconfig b/boards/esp32s3-wt32-sc01-plus/Kconfig index d6d1eeed0d..502e789d25 100644 --- a/boards/esp32s3-wt32-sc01-plus/Kconfig +++ b/boards/esp32s3-wt32-sc01-plus/Kconfig @@ -24,6 +24,7 @@ config BOARD_ESP32S3_WT32_SC01_PLUS select HAVE_FT5X06 select HAVE_LCD_PARALLEL if MODULE_ST7796 + select HAVE_LCD_PARALLEL_LL_MCU if MODULE_ST7796 select HAVE_MTD_SDCARD_DEFAULT select HAVE_ST7796 diff --git a/boards/esp32s3-wt32-sc01-plus/Makefile.dep b/boards/esp32s3-wt32-sc01-plus/Makefile.dep index 0502f41f03..42899babef 100644 --- a/boards/esp32s3-wt32-sc01-plus/Makefile.dep +++ b/boards/esp32s3-wt32-sc01-plus/Makefile.dep @@ -29,6 +29,7 @@ endif ifneq (,$(filter st7796,$(USEMODULE))) USEMODULE += lcd_parallel + USEMODULE += lcd_parallel_ll_mcu endif ifneq (,$(filter touch_dev,$(USEMODULE))) From b6252b334d024933fbb475ee7ce7095a21851c7e Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 13 Nov 2023 14:08:29 +0100 Subject: [PATCH 39/85] tests/periph/selftest_shield: fix integration The test should execute only with `make test-with-config` and not with `make test`, as boards without the shield cannot pass the test. For some reason I accidentally added both variants, which makes no sense. This drops the `make test` variant. Finally, the `README.md` is updated to refer to `make test-with-config` instead of `make test`. --- tests/periph/selftest_shield/README.md | 2 +- .../tests-with-config/01-run.py | 7 +++++-- tests/periph/selftest_shield/tests/01-run.py | 21 ------------------- 3 files changed, 6 insertions(+), 24 deletions(-) delete mode 100755 tests/periph/selftest_shield/tests/01-run.py diff --git a/tests/periph/selftest_shield/README.md b/tests/periph/selftest_shield/README.md index 8c1706ceff..a2dd818d91 100644 --- a/tests/periph/selftest_shield/README.md +++ b/tests/periph/selftest_shield/README.md @@ -26,7 +26,7 @@ - (Background: If the UART at D0 and D1 is used for stdio, it cannot be looped and tested) 5. Flash and run the test - - In this directory, run `make BOARD= flash test` + - In this directory, run `make BOARD= flash test-with-config` ## Details diff --git a/tests/periph/selftest_shield/tests-with-config/01-run.py b/tests/periph/selftest_shield/tests-with-config/01-run.py index 606d413ed1..005393cb48 100755 --- a/tests/periph/selftest_shield/tests-with-config/01-run.py +++ b/tests/periph/selftest_shield/tests-with-config/01-run.py @@ -1,17 +1,20 @@ #!/usr/bin/env python3 -# Copyright (C) 2022 Otto-von-Guericke-Universität Magdeburg +# Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg # # This file is subject to the terms and conditions of the GNU Lesser # General Public License v2.1. See the file LICENSE in the top level # directory for more details. +# @author Marian Buschsieweke + import sys from testrunner import run def testfunc(child): - child.expect('ALL TESTS SUCCEEDED') + child.expect("self-testing peripheral drivers") + child.expect("ALL TESTS SUCCEEDED") if __name__ == "__main__": diff --git a/tests/periph/selftest_shield/tests/01-run.py b/tests/periph/selftest_shield/tests/01-run.py deleted file mode 100755 index 005393cb48..0000000000 --- a/tests/periph/selftest_shield/tests/01-run.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - -# Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg -# -# This file is subject to the terms and conditions of the GNU Lesser -# General Public License v2.1. See the file LICENSE in the top level -# directory for more details. - -# @author Marian Buschsieweke - -import sys -from testrunner import run - - -def testfunc(child): - child.expect("self-testing peripheral drivers") - child.expect("ALL TESTS SUCCEEDED") - - -if __name__ == "__main__": - sys.exit(run(testfunc)) From d6dac4bee28613534424a0fa9497ea327ed64c6a Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 13 Nov 2023 15:53:58 +0100 Subject: [PATCH 40/85] tests/periph/selftest_shield: Fix copy-paste error in comment --- tests/periph/selftest_shield/main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 9450968d4f..84e192e2f7 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg + * Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -11,7 +11,8 @@ * @{ * * @file - * @brief Test application for the Peripheral GPIO Low-Level API + * @brief Test application for Peripheral Self-Testing using the + * Peripheral Selftest Shield * * @author Marian Buschsieweke * From 882ab0059ae12d57c92713b8efadf798add316ed Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 13 Nov 2023 16:17:01 +0100 Subject: [PATCH 41/85] boards/nucleo-f030r8: enable SPI and I2C The SPI configuration was already provided, but the feature never exposed. The common PB8/PB9 I2C config is used to for I2C. --- boards/nucleo-f030r8/Kconfig | 2 ++ boards/nucleo-f030r8/Makefile.features | 2 ++ boards/nucleo-f030r8/include/periph_conf.h | 1 + 3 files changed, 5 insertions(+) diff --git a/boards/nucleo-f030r8/Kconfig b/boards/nucleo-f030r8/Kconfig index 5196350ec8..5f3e842841 100644 --- a/boards/nucleo-f030r8/Kconfig +++ b/boards/nucleo-f030r8/Kconfig @@ -16,8 +16,10 @@ config BOARD_NUCLEO_F030R8 # Put defined MCU peripherals here (in alphabetical order) select HAS_PERIPH_ADC + select HAS_PERIPH_I2C select HAS_PERIPH_PWM select HAS_PERIPH_RTC + select HAS_PERIPH_SPI select HAS_PERIPH_TIMER select HAS_PERIPH_UART diff --git a/boards/nucleo-f030r8/Makefile.features b/boards/nucleo-f030r8/Makefile.features index f958cc13bb..69099e625b 100644 --- a/boards/nucleo-f030r8/Makefile.features +++ b/boards/nucleo-f030r8/Makefile.features @@ -3,11 +3,13 @@ CPU_MODEL = stm32f030r8 # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_adc +FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm # For RTC, Nucleos with MB1136 C-02 or MB1136 C-03 -sticker on it have the # required LSE oscillator provided on the X2 slot. # See Nucleo User Manual UM1724 section 5.6.2. FEATURES_PROVIDED += periph_rtc +FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/nucleo-f030r8/include/periph_conf.h b/boards/nucleo-f030r8/include/periph_conf.h index e2ec7f1b55..126aa925b6 100644 --- a/boards/nucleo-f030r8/include/periph_conf.h +++ b/boards/nucleo-f030r8/include/periph_conf.h @@ -33,6 +33,7 @@ #include "periph_cpu.h" #include "clk_conf.h" +#include "cfg_i2c1_pb8_pb9.h" #ifdef __cplusplus extern "C" { From 6b30f5cd1b33ded1d6eefa211135adfa6365ed0c Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 14 Nov 2023 12:55:28 +0100 Subject: [PATCH 42/85] tests: add nucleo-f030r8 to Makefile.ci where needed --- tests/drivers/atwinc15x0/Makefile.ci | 1 + tests/drivers/cc110x/Makefile.ci | 1 + tests/drivers/nrf24l01p_ng/Makefile.ci | 1 + tests/pkg/cryptoauthlib_internal-tests/Makefile.ci | 1 + 4 files changed, 4 insertions(+) diff --git a/tests/drivers/atwinc15x0/Makefile.ci b/tests/drivers/atwinc15x0/Makefile.ci index ab7b51228f..877613d9ab 100644 --- a/tests/drivers/atwinc15x0/Makefile.ci +++ b/tests/drivers/atwinc15x0/Makefile.ci @@ -11,6 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := \ i-nucleo-lrwan1 \ msb-430 \ msb-430h \ + nucleo-f030r8 \ nucleo-f031k6 \ nucleo-f042k6 \ nucleo-f303k8 \ diff --git a/tests/drivers/cc110x/Makefile.ci b/tests/drivers/cc110x/Makefile.ci index 39b64861f1..9b47ccae52 100644 --- a/tests/drivers/cc110x/Makefile.ci +++ b/tests/drivers/cc110x/Makefile.ci @@ -21,6 +21,7 @@ BOARD_INSUFFICIENT_MEMORY := \ microduino-corerf \ msb-430 \ msb-430h \ + nucleo-f030r8 \ nucleo-f031k6 \ nucleo-f042k6 \ nucleo-f070rb \ diff --git a/tests/drivers/nrf24l01p_ng/Makefile.ci b/tests/drivers/nrf24l01p_ng/Makefile.ci index 91e8cf30e8..f983796390 100644 --- a/tests/drivers/nrf24l01p_ng/Makefile.ci +++ b/tests/drivers/nrf24l01p_ng/Makefile.ci @@ -11,6 +11,7 @@ BOARD_INSUFFICIENT_MEMORY := \ atxmega-a3bu-xplained \ bluepill-stm32f030c8 \ i-nucleo-lrwan1 \ + nucleo-f030r8 \ nucleo-f031k6 \ nucleo-f042k6 \ nucleo-l011k4 \ diff --git a/tests/pkg/cryptoauthlib_internal-tests/Makefile.ci b/tests/pkg/cryptoauthlib_internal-tests/Makefile.ci index 0bb9153ec9..af98930dd4 100644 --- a/tests/pkg/cryptoauthlib_internal-tests/Makefile.ci +++ b/tests/pkg/cryptoauthlib_internal-tests/Makefile.ci @@ -19,6 +19,7 @@ BOARD_INSUFFICIENT_MEMORY := \ i-nucleo-lrwan1 \ mega-xplained \ microduino-corerf \ + nucleo-f030r8 \ nucleo-f302r8 \ nucleo-l011k4 \ nucleo-l031k6 \ From c7ded84e0c89e9fa29753be5eab0cde293c56425 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 14 Nov 2023 16:16:28 +0100 Subject: [PATCH 43/85] boards/nucleo-wl55jc: Fix Arduino SPI bus The macro `ARDUINO_SPI_D11D12D13` is used to refer to the SPI bus on the pins D11/D12/D13 on Arduino UNO compatible boards. For all Nucleo64 boards this is `SPI_DEV(0)`, but for this board `SPI_DEV(0)` is internally connected to the radio. Instead `SPI_DEV(1)` is connected to the correct pins. This provides the macro explicitly in `periph_conf.h`, which takes preference over the fallback in `boards/common/nucleo64` when provided. --- boards/nucleo-wl55jc/include/periph_conf.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/boards/nucleo-wl55jc/include/periph_conf.h b/boards/nucleo-wl55jc/include/periph_conf.h index 37a2e709e7..788ad89299 100644 --- a/boards/nucleo-wl55jc/include/periph_conf.h +++ b/boards/nucleo-wl55jc/include/periph_conf.h @@ -117,7 +117,12 @@ static const spi_conf_t spi_config[] = { #endif }; -#define SPI_NUMOF ARRAY_SIZE(spi_config) +#define SPI_NUMOF ARRAY_SIZE(spi_config) +/** + * @brief Provide ARDUINO_SPI_D11D12D13 explicitly, as the first SPI + * interface is connected to the radio. + */ +#define ARDUINO_SPI_D11D12D13 SPI_DEV(1) /** @} */ /** From 08379cde27b50ce2acbd907728740a52ea70686b Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 15 Nov 2023 09:56:45 +0100 Subject: [PATCH 44/85] boards/p-nucleo-wb55: provide Arduino features This provides the features: - `arduino_i2c` - `arduino_spi` - `arduino_uart` --- boards/p-nucleo-wb55/Kconfig | 3 +++ boards/p-nucleo-wb55/Makefile.features | 3 +++ boards/p-nucleo-wb55/include/arduino_iomap.h | 27 ++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/boards/p-nucleo-wb55/Kconfig b/boards/p-nucleo-wb55/Kconfig index 46a32593a4..e98ad3e64e 100644 --- a/boards/p-nucleo-wb55/Kconfig +++ b/boards/p-nucleo-wb55/Kconfig @@ -26,8 +26,11 @@ config BOARD_P_NUCLEO_WB55 # Put other features for this board (in alphabetical order) select HAS_ARDUINO_ANALOG + select HAS_ARDUINO_I2C select HAS_ARDUINO_PINS select HAS_ARDUINO_SHIELD_UNO + select HAS_ARDUINO_SPI + select HAS_ARDUINO_UART select HAS_RIOTBOOT select HAS_TINYUSB_DEVICE diff --git a/boards/p-nucleo-wb55/Makefile.features b/boards/p-nucleo-wb55/Makefile.features index a59e19133e..07a3805f89 100644 --- a/boards/p-nucleo-wb55/Makefile.features +++ b/boards/p-nucleo-wb55/Makefile.features @@ -14,7 +14,10 @@ FEATURES_PROVIDED += periph_usbdev # Put other features for this board (in alphabetical order) FEATURES_PROVIDED += arduino_analog +FEATURES_PROVIDED += arduino_i2c FEATURES_PROVIDED += arduino_pins FEATURES_PROVIDED += arduino_shield_uno +FEATURES_PROVIDED += arduino_spi +FEATURES_PROVIDED += arduino_uart FEATURES_PROVIDED += riotboot FEATURES_PROVIDED += tinyusb_device diff --git a/boards/p-nucleo-wb55/include/arduino_iomap.h b/boards/p-nucleo-wb55/include/arduino_iomap.h index 3e08f092dd..edfe3d8b20 100644 --- a/boards/p-nucleo-wb55/include/arduino_iomap.h +++ b/boards/p-nucleo-wb55/include/arduino_iomap.h @@ -88,6 +88,33 @@ extern "C" { #define ARDUINO_ANALOG_PIN_LAST 5 /** @} */ +/** + * @name Arduino's default UART device + * @{ + */ +#define ARDUINO_UART_D0D1 UART_DEV(1) +/** @} */ + +/** + * @name Arduino's I2C buses + * @{ + */ +/** + * @brief The only configured I2C + */ +#define ARDUINO_I2C_UNO I2C_DEV(0) +/** @} */ + +/** + * @name Arduino's SPI buses + * @{ + */ +/** + * @brief SPI_DEV(0) is connected to D11/D12/D13 + */ +#define ARDUINO_SPI_D11D12D13 SPI_DEV(0) +/** @} */ + #ifdef __cplusplus } #endif From 7446cf543a00ac77f82dfe9d1bcbd9abaff45471 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 15 Nov 2023 09:57:42 +0100 Subject: [PATCH 45/85] boards/common/nucleo64: fix doc --- boards/common/nucleo64/include/arduino_iomap.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/boards/common/nucleo64/include/arduino_iomap.h b/boards/common/nucleo64/include/arduino_iomap.h index c6debeedc3..b1bc691283 100644 --- a/boards/common/nucleo64/include/arduino_iomap.h +++ b/boards/common/nucleo64/include/arduino_iomap.h @@ -41,10 +41,13 @@ extern "C" { * @name Arduino's SPI buses * @{ */ -/** - * @brief SPI_DEV(1) is connected to D11/D12/D13 - */ #if !defined(ARDUINO_SPI_D11D12D13) && defined(SPI_NUMOF) +/** + * @brief SPI_DEV(0) is connected to D11/D12/D13 for most Nucleo-64 boards + * + * This can be overwritten in `boards/nucleo-/include/periph_conf.h` by + * providing a custom `ARDUINO_SPI_D11D12D13`. + */ #define ARDUINO_SPI_D11D12D13 SPI_DEV(0) #endif /** @} */ From 89a16604d37959cd28d98c572462438be2ed5765 Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Wed, 15 Nov 2023 12:23:39 +0100 Subject: [PATCH 46/85] cpu/esp32: Fix kconfig of esp-lcd --- cpu/esp32/esp-idf/Kconfig | 1 + cpu/esp32/esp-idf/heap/Kconfig | 1 + cpu/esp32/esp-idf/lcd/Kconfig | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cpu/esp32/esp-idf/Kconfig b/cpu/esp32/esp-idf/Kconfig index 6e4a813a6c..1967cb12dc 100644 --- a/cpu/esp32/esp-idf/Kconfig +++ b/cpu/esp32/esp-idf/Kconfig @@ -26,6 +26,7 @@ rsource "eth/Kconfig" rsource "event/Kconfig" rsource "gpio/Kconfig" rsource "heap/Kconfig" +rsource "lcd/Kconfig" rsource "nvs_flash/Kconfig" rsource "rmt/Kconfig" rsource "spi_flash/Kconfig" diff --git a/cpu/esp32/esp-idf/heap/Kconfig b/cpu/esp32/esp-idf/heap/Kconfig index d67264f68a..f056d3eb17 100644 --- a/cpu/esp32/esp-idf/heap/Kconfig +++ b/cpu/esp32/esp-idf/heap/Kconfig @@ -10,6 +10,7 @@ config MODULE_ESP_IDF_HEAP bool depends on TEST_KCONFIG depends on MODULE_ESP_IDF + default y if MODULE_LCD_PARALLEL_LL_MCU select PACKAGE_TLSF help ESP-IDF heap library. This library is required if external SPI RAM diff --git a/cpu/esp32/esp-idf/lcd/Kconfig b/cpu/esp32/esp-idf/lcd/Kconfig index 8a8156adb7..1dd3a89071 100644 --- a/cpu/esp32/esp-idf/lcd/Kconfig +++ b/cpu/esp32/esp-idf/lcd/Kconfig @@ -8,7 +8,7 @@ config MODULE_ESP_IDF_LCD bool depends on TEST_KCONFIG - depends on MODULE_ESP_IDF && HAS_ESP_LCD + depends on MODULE_ESP_IDF default y if MODULE_LCD_PARALLEL_LL_MCU From af6bb03bc74e33a2c1d19c7d613738cd2ee75f88 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 15 Nov 2023 20:17:35 +0100 Subject: [PATCH 47/85] tests/periph/selftest_shield: improve SPI test - fix a copy-paste error (`TIMER_FREQ_UART_TEST` was used in the SPI test, but that should be `TIMER_FREQ_SPI_TEST`) - use 400 kHz as slow SPI frequency, as faster STM32 MCUs just cannot divide the APB clock down to 100 kHz - when detailed output is enabled, print the SPI clock in addition to the SPI mode to ease figuring out what went wrong - only have one `FAILURE` message for a too fast byte transfer per check, rather than per transmitted byte, to reduce the noise - work around a bug of `periph_timer` on STM32 by reducing the clock speed of the timer for the SPI test --- tests/periph/selftest_shield/main.c | 38 ++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 84e192e2f7..bf9f70834a 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -19,6 +19,7 @@ * @} */ +#include #include #include #include @@ -137,7 +138,7 @@ * directly, so the CPU clock is the highest clock frequency available. But * some can't, so we handle them here explicitly. */ #ifndef TIMER_FREQ_SPI_TEST -# if defined(CPU_SAM3) +# if defined(CPU_SAM3) || defined(CPU_STM32) # define TIMER_FREQ_SPI_TEST CLOCK_CORECLOCK / 4 # elif defined(CPU_NRF52) || defined(CPU_NRF51) # define TIMER_FREQ_SPI_TEST MHZ(16) @@ -784,13 +785,14 @@ static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, const char *test_in_detail) { (void)test_in_detail; - bool failed = 0; + bool failed = false; + bool transfer_too_fast = false; print_start("SPI", test_in_detail); uint16_t byte_transfer_ticks = 0; memset(&serial_buf, 0, sizeof(serial_buf)); if (IS_USED(MODULE_PERIPH_TIMER)) { - byte_transfer_ticks = 8ULL * TIMER_FREQ_UART_TEST / clk_hz; + byte_transfer_ticks = 8ULL * TIMER_FREQ_SPI_TEST / clk_hz; } /* D10 is C̅S̅, D7 is connected to C̅S̅ */ @@ -808,6 +810,7 @@ static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, /* C̅S̅ should still be HIGH while no chip is selected */ failed |= TEST(gpio_read(cs_check) != 0); + uint16_t byte_time; for (uint8_t i = 0; i < UINT8_MAX; i++) { uint16_t start = 0; if (IS_USED(MODULE_PERIPH_TIMER)) { @@ -819,15 +822,25 @@ static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, stop = timer_read(TIMER); } failed |= TEST(received == i); - uint16_t byte_time = (uint16_t)(stop - start); - /* We allow the actual SPI clock to be slower than requested, but not - * faster. So the transfer needs to take *at least* the theoretical - * time. Given the overhead of, this already has some room for error */ - failed |= TEST(byte_time >= byte_transfer_ticks); - /* C̅S̅ should be still LOW while chip is selected */ + if (IS_USED(MODULE_PERIPH_TIMER)) { + byte_time = (uint16_t)(stop - start); + /* We allow the actual SPI clock to be slower than requested, but + * not faster. So the transfer needs to take *at least* the + * theoretical time. Given the overhead of, this already has some + * room for error */ + transfer_too_fast |= (byte_time < byte_transfer_ticks); + /* C̅S̅ should be still LOW while chip is selected */ + } failed |= TEST(gpio_read(cs_check) == 0); } + if (DETAILED_OUTPUT && transfer_too_fast) { + printf("Ticks expected to transfer byte: >= %" PRIu16 ", but got: %" + PRIu16 "\n", + byte_transfer_ticks, byte_time); + } + + failed |= TEST(!transfer_too_fast); failed |= TEST(spi_transfer_byte(bus, cs, false, UINT8_MAX) == UINT8_MAX); /* C̅S̅ should be again HIGH while now that no chip is selected */ failed |= TEST(gpio_read(cs_check) != 0); @@ -871,8 +884,8 @@ static bool periph_spi_test(void) } bool failed = false; - static const spi_clk_t clocks[] = { SPI_CLK_100KHZ, SPI_CLK_1MHZ, SPI_CLK_10MHZ }; - static const uint32_t clk_hzs[] = { KHZ(100), MHZ(1), MHZ(10) }; + static const spi_clk_t clocks[] = { SPI_CLK_400KHZ, SPI_CLK_1MHZ, SPI_CLK_10MHZ }; + static const uint32_t clk_hzs[] = { KHZ(400), MHZ(1), MHZ(10) }; if (IS_USED(MODULE_PCF857X)) { for (int i = 0; i < (int)ARRAY_SIZE(spi_clk_check_pins); i++) { @@ -888,6 +901,9 @@ static bool periph_spi_test(void) for (unsigned j = 0; j < ARRAY_SIZE(clocks); j++) { spi_clk_t clk = clocks[j]; uint32_t clk_hz = clk_hzs[j]; + if (DETAILED_OUTPUT) { + printf("SPI CLK %" PRIu32 " Hz\n", clk_hz); + } failed |= periph_spi_rxtx_test(bus, SPI_MODE_0, clk, clk_hz, clk_check, false, "mode 0"); failed |= periph_spi_rxtx_test(bus, SPI_MODE_1, clk, clk_hz, clk_check, false, "mode 1"); failed |= periph_spi_rxtx_test(bus, SPI_MODE_2, clk, clk_hz, clk_check, true, "mode 2"); From 61af0f38f8f4ec92f5f6dbb8b818cee24fe76747 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 16 Nov 2023 08:57:06 +0100 Subject: [PATCH 48/85] boards/p-nucleo-wb55: Add pinout and clean up doc --- boards/p-nucleo-wb55/doc.txt | 88 +- doc/doxygen/src/pinouts/p-nucleo-wb55.svg | 16306 ++++++++++++++++++++ 2 files changed, 16354 insertions(+), 40 deletions(-) create mode 100644 doc/doxygen/src/pinouts/p-nucleo-wb55.svg diff --git a/boards/p-nucleo-wb55/doc.txt b/boards/p-nucleo-wb55/doc.txt index c972900528..655f1cb598 100644 --- a/boards/p-nucleo-wb55/doc.txt +++ b/boards/p-nucleo-wb55/doc.txt @@ -8,29 +8,37 @@ Hardware ![st-nucleo-wb55](https://miro.medium.com/max/700/1*9OG-4Ix4EzHX9uBpMve2IA.jpeg) +## Pinout + +@image html pinouts/p-nucleo-wb55.svg "Pinout for the p-nucleo-wb55" width=50% + MCU --- -| MCU | STM32WB55RG | -|:---------- |:------------------------------- | -| Family | ARM Cortex-M4 | -| Vendor | ST Microelectronics | -| RAM | 256KB | -| Flash | 512KB | -| Frequency | 64MHz | -| FPU | yes | -| Timers | 8 (3x 16-bit, 1x 32-bit [TIM5]) | -| LPTimers | 2x 16-bit | -| ADCs | 1x 19-channel 12-bit | -| UARTs | 1 | -| LUARTs | 1 | -| SPIs | 1 | -| I2Cs | 2 | -| RTC | 1 | -| Vcc | 1.65V - 3.6V | -| Datasheet | [Datasheet](https://www.st.com/resource/en/datasheet/stm32wb55cc.pdf)| -| Reference Manual | [Reference Manual](https://www.st.com/resource/en/datasheet/stm32wb55cc.pdf) | -| User Manual | [User Manual](https://www.st.com/content/ccc/resource/technical/document/user_manual/group1/13/58/22/1a/f2/ff/43/5c/DM00517423/files/DM00517423.pdf/jcr:content/translations/en.DM00517423.pdf) | +| MCU | STM32WB55RG | +|:----------------- |:------------------------------------- | +| Family | ARM Cortex-M4 | +| Vendor | ST Microelectronics | +| RAM | 256 KiB | +| Flash | 512 KiB | +| Frequency | 64 MHz | +| FPU | yes | +| Timers | 8 (3x 16-bit, 1x 32-bit [TIM5]) | +| LPTimers | 2x 16-bit | +| ADCs | 1x 19-channel 12-bit | +| UARTs | 1 | +| LUARTs | 1 | +| SPIs | 1 | +| I2Cs | 2 | +| RTC | 1 | +| Vcc | 1.65V - 3.6V | +| Datasheet | [Datasheet][Datasheet] | +| Reference Manual | [Reference Manual][Reference Manual] | +| User Manual | [User Manual][User Manual] | + +[Datasheet]: https://www.st.com/resource/en/datasheet/stm32wb55cc.pdf +[Reference Manual]: https://www.st.com/resource/en/reference_manual/rm0434-multiprotocol-wireless-32bit-mcu-armbased-cortexm4-with-fpu-bluetooth-lowenergy-and-802154-radio-solution-stmicroelectronics.pdf +[User Manual]: https://www.st.com/content/ccc/resource/technical/document/user_manual/group1/13/58/22/1a/f2/ff/43/5c/DM00517423/files/DM00517423.pdf/jcr:content/translations/en.DM00517423.pdf Overview ======== @@ -82,25 +90,25 @@ User Interface Implementation Status --------------------- -| Device | ID | Supported | Comments | -|:---------------- |:----------------- |:------- |:------- | -| MCU | stm32wb | yes | | -| | M0+ co-processor | no | | -| | BLE | no | | -| | 802.15.4 | no | | -| Low-level driver | GPIO | yes | | -| | UART | yes | UART1 | -| | LPUART | yes | LPUART1 | -| | I2C | yes | I2C1 | -| | SPI | yes | | -| | ADC | yes | | -| | RTT | yes | | -| | RTC | yes | | -| | RNG | yes | | -| | Timer | yes | TIM2 | -| | WDT | yes | | -| | USB | yes | | -| | PWM | no | | -| | AES | no | | +| Device | ID | Supported | Comments | +|:----------------- |:----------------- |:--------- |:--------- | +| MCU | stm32wb | yes | | +| | M0+ co-processor | no | | +| | BLE | no | | +| | 802.15.4 | no | | +| Low-level driver | GPIO | yes | | +| | UART | yes | UART1 | +| | LPUART | yes | LPUART1 | +| | I2C | yes | I2C1 | +| | SPI | yes | | +| | ADC | yes | | +| | RTT | yes | | +| | RTC | yes | | +| | RNG | yes | | +| | Timer | yes | TIM2 | +| | WDT | yes | | +| | USB | yes | | +| | PWM | no | | +| | AES | no | | */ diff --git a/doc/doxygen/src/pinouts/p-nucleo-wb55.svg b/doc/doxygen/src/pinouts/p-nucleo-wb55.svg new file mode 100644 index 0000000000..f66a813e38 --- /dev/null +++ b/doc/doxygen/src/pinouts/p-nucleo-wb55.svg @@ -0,0 +1,16306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f51ca593ba8c497bdfe8128c63a9509f7b23aa35 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 13 Nov 2023 16:11:22 +0100 Subject: [PATCH 49/85] tests/periph/selftest_shield: re-enable ADC test The R-2R resistor ladder dac --> ADC test was disabled due to a bug in the v0.1 version of the shield. Since this has been fixed in v0.2 and v0.3 of the shield, it can be re-enabled. The comment regarding the high accuracy of the resistor is dropped, as v0.3 has been ordered with cost efficient resistors rather than with accurate ones. As a result, the tolerance for error has been increased to 10%. This quite a bit more lax than I have hoped for, but false positives would be something to avoid. --- tests/periph/selftest_shield/main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 84e192e2f7..94ea7fa1fb 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -93,10 +93,9 @@ * - periph_adc support (so that we have something to test) * - Arduino I/O mapping for ADC (so that we know which line to test) * - The PCF857x driver (so that we can control the R-2R resistor ladder - * connected to the GPIO expander. + * connected to the GPIO expander). */ -#if defined(MODULE_PERIPH_ADC) && defined(ARDUINO_A0) && defined(MODULE_PCF857X) && 0 -// TODO: Re-anble when PCB is fixed +#if defined(MODULE_PERIPH_ADC) && defined(ARDUINO_A0) && defined(MODULE_PCF857X) # define ENABLE_ADC_TEST 1 #else # define ENABLE_ADC_TEST 0 @@ -979,9 +978,9 @@ static bool periph_adc_test(void) uint16_t sample = adc_sample(ARDUINO_A0, ADC_RES_10BIT); uint16_t expected = i << 6; - /* the resistors are said to be rather accurate, so lets be a bit - * more strict here */ - const uint16_t delta = 16; + /* The resistors on board v0.3 are not that accurate, so allow for 10% + * error margin */ + const uint16_t delta = 1024 / 10; uint16_t lower = expected <= delta ? 0 : expected - delta; uint16_t upper = MIN(1023, expected + delta); bool test_failed = TEST((lower <= sample) && (upper >= sample)); From 66d38101d85983ab82a08387fc06f8e04fe64412 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 14 Nov 2023 13:10:20 +0100 Subject: [PATCH 50/85] tests/periph/selftest_shield: Consistently use `DETAILED_OUTPUT` There already is `DETAILED_OUTPUT` to trade ROM size for more verbose error messages, no need to abuse `DEBUG()` for the same as well. --- tests/periph/selftest_shield/main.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 94ea7fa1fb..108c4ba339 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -26,6 +26,7 @@ #include #include +#include "architecture.h" #include "arduino_iomap.h" #include "macros/units.h" #include "macros/utils.h" @@ -40,9 +41,6 @@ #include "stdio_uart.h" /* for STDIO_UART_DEV */ /* BEGIN: controls of the behavior of the testing app: */ -#define ENABLE_DEBUG 1 -#include "debug.h" - #ifndef STOP_ON_FAILURE #define STOP_ON_FAILURE 0 #endif @@ -715,8 +713,8 @@ static bool periph_uart_rxtx_test(uint32_t symbolrate) /* expecting actual duration within 75% to 200% of the expected. */ failed |= TEST(stop - start > duration_ticks - (duration_ticks >> 2)); failed |= TEST(stop - start < (duration_ticks << 1)); - if (failed) { - DEBUG("%" PRIu32 " Bd, expected %" PRIu16 " ticks, got %" PRIu16 + if (failed && DETAILED_OUTPUT) { + printf("%" PRIu32 " Bd, expected %" PRIu16 " ticks, got %" PRIu16 " ticks\n", symbolrate, duration_ticks, (uint16_t)(stop - start)); } From 1a19005c22b3b0cd8230ddab36511984541a397c Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 16 Nov 2023 14:28:20 +0100 Subject: [PATCH 51/85] drivers/at24cxxx: add defines for AT24CS04 & AT24CS08 --- drivers/at24cxxx/include/at24cxxx_defines.h | 54 +++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/at24cxxx/include/at24cxxx_defines.h b/drivers/at24cxxx/include/at24cxxx_defines.h index 69ea85003e..9837ad6c50 100644 --- a/drivers/at24cxxx/include/at24cxxx_defines.h +++ b/drivers/at24cxxx/include/at24cxxx_defines.h @@ -275,6 +275,52 @@ extern "C" { / AT24CXXX_POLL_DELAY_US)) /** @} */ +/** + * @name AT24CS04 constants + * @{ + */ +/** + * @brief 512 Byte memory + */ +#define AT24CS04_EEPROM_SIZE (512U) +/** + * @brief 32 pages of 16 bytes each + */ +#define AT24CS04_PAGE_SIZE (16U) +/** + * @brief Delay to complete write operation + */ +#define AT24CS04_PAGE_WRITE_DELAY_US (5000U) +/** + * @brief Number of poll attempts + */ +#define AT24CS04_MAX_POLLS (1 + (AT24CS04_PAGE_WRITE_DELAY_US \ + / AT24CXXX_POLL_DELAY_US)) +/** @} */ + +/** + * @name AT24CS08 constants + * @{ + */ +/** + * @brief 1 kiB memory + */ +#define AT24CS08_EEPROM_SIZE (1024U) +/** + * @brief 64 pages of 16 bytes each + */ +#define AT24CS08_PAGE_SIZE (16U) +/** + * @brief Delay to complete write operation + */ +#define AT24CS08_PAGE_WRITE_DELAY_US (5000U) +/** + * @brief Number of poll attempts + */ +#define AT24CS08_MAX_POLLS (1 + (AT24CS08_PAGE_WRITE_DELAY_US \ + / AT24CXXX_POLL_DELAY_US)) +/** @} */ + /** * @name AT24C1024 constants * @{ @@ -369,6 +415,14 @@ extern "C" { #define AT24CXXX_EEPROM_SIZE (AT24C01A_EEPROM_SIZE) #define AT24CXXX_PAGE_SIZE (AT24C01A_PAGE_SIZE) #define AT24CXXX_MAX_POLLS (AT24C01A_MAX_POLLS) +#elif IS_USED(MODULE_AT24CS04) +#define AT24CXXX_EEPROM_SIZE (AT24CS04_EEPROM_SIZE) +#define AT24CXXX_PAGE_SIZE (AT24CS04_PAGE_SIZE) +#define AT24CXXX_MAX_POLLS (AT24CS04_MAX_POLLS) +#elif IS_USED(MODULE_AT24CS08) +#define AT24CXXX_EEPROM_SIZE (AT24CS08_EEPROM_SIZE) +#define AT24CXXX_PAGE_SIZE (AT24CS08_PAGE_SIZE) +#define AT24CXXX_MAX_POLLS (AT24CS08_MAX_POLLS) #elif IS_USED(MODULE_AT24MAC) #define AT24CXXX_EEPROM_SIZE (AT24MAC_EEPROM_SIZE) #define AT24CXXX_PAGE_SIZE (AT24MAC_PAGE_SIZE) From 24893fa06e589fd340fdb4248b2da23e51e98acb Mon Sep 17 00:00:00 2001 From: MrKevinWeiss Date: Fri, 17 Nov 2023 15:43:02 +0100 Subject: [PATCH 52/85] release-notes.txt: add 2023.10 release notes --- release-notes.txt | 470 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 470 insertions(+) diff --git a/release-notes.txt b/release-notes.txt index a222c8fd20..f9e82acb91 100644 --- a/release-notes.txt +++ b/release-notes.txt @@ -1,3 +1,473 @@ +RIOT-2023.10 - Release Notes +============================ +RIOT is a multi-threading operating system which enables soft real-time +capabilities and comes with support for a range of devices that are typically +found in the Internet of Things: 8-bit and 16-bit microcontrollers as well as +light-weight 32-bit processors. + +RIOT is based on the following design principles: energy-efficiency, soft +real-time capabilities, small memory footprint, modularity, and uniform API +access, independent of the underlying hardware (with partial POSIX compliance). + +RIOT is developed by an international open-source community which is +independent of specific vendors (e.g. similarly to the Linux community) and is +licensed with a non-viral copyleft license (LGPLv2.1), which allows indirect +business models around the free open-source software platform provided by RIOT. + + +About this release +================== + +The 2023.10 release includes: + +- PSA Crypto API implementation, one step closer to a secure IoT! +- A bunch of default drivers for boards +- Improved clang support + +116 pull requests, composed of 318 commits, have been merged since the +last release, and 2 issues have been solved. 23 people contributed with +code in 95 days. 1109 files have been touched with 437877 (+) insertions and +3189 deletions (-). + + +Notations used below +==================== + + + means new feature/item + * means modified feature/item + - means removed feature/item + + +New features and changes +======================== + +System Libraries +---------------- + ++ drivers/periph_sdmmc: define a High-level SDIO/SD/MMC API and low- + level SDMMC peripheral driver interface (#19539) ++ sys/event: add event_is_queued() (#19966) +* sys/shell/gnrc_netif: fix ifconfig set language issue (#19970) +* sys/shell/gnrc_txtsnd: Move to separate module (#19973) +* sys: PSA Crypto API implementation (#18547) + +Networking +---------- + ++ coap: add missing Content-Format definitions (#19875) ++ coap: add missing option numbers (#19874) ++ drivers/atwinc15x0: support dynamic scanning and connection to AP (#19387) ++ sys/net/sock: add sock_aux_ttl (#19836) +* cmds_gnrc_netif: Support enabling/disabling lwIP netifs from + gnrc_netif shell (#19972) + +Packages +-------- + ++ mcufont: Initial addition of MCUFont package (#19726) +* pkg/driver_cryptocell_310: Fix Makefile (#19959) +* pkg/flashdb: bump to 2.0.0 (#19863) +* pkg/littlefs2: bump to v2.8 (#19942) +* pkg/lvgl: bump to v8.3.9 (#19901) +* lwip: bump to v2.2.0 (#19780) + +Boards +------ + ++ boards/stm32f723e-disco: enable ST7789 display (#19939) ++ boards/stm32f769i-disco: enable FMC with SDRAM support (#19851) ++ boards/stm32l496g-disco: enable ST7789 display and touch panel (#19938) ++ boards: add Silabs EFM32 Giant Gecko GG11 Starter Kit (#19923) ++ boards: add support for ESP32-S3 WT32 SC01 Plus board (#19917) +* boards/adafruit-itsybitsy-m4: configure littleFS on external flash (#19355) +* boards/nucleo64: fix SPI Arduino mapping for most boards (#19935) +* boards/sipeed_longan_nano: separate board definition for Sipeed + Longan Nano TFT (#19824) +* boards/stm32f469i-disco: enable FMC with SDRAM support (#19910) +* boards: complete SD Card MTD definition for several bords (#19914) + +CPU +--- + ++ cpu/stm32/periph: add FMC/FSMC support for STM32 (#19843) ++ cpu/sam0_common/periph: add low-level SDMMC peripheral driver for SDHC (#19760) ++ cpu/efm32/periph: add DAC support for EFM32 Series 1 (VDAC) (#19887) +* cpu/atmega_common: hook up BAT LOW irq to power bus (#19822) +* cpu/riscv_common: remove picolibc from blacklisting in CI (#19862) +* cpu/stm32/periph/eth: Disable hardware checksums (#19952) +* cpu/stm32: bump cmsis packages version (#19904) +* cpu/stm32: fix ld script for SRAM4 (#19842) +* dist/tools/esptools: upgrade ESP32x toolchains to GCC version 12.2 (#19452) +* sys/psa_crypto: Ed25519 (EdDSA) support (#19954) + +Device Drivers +-------------- + ++ drivers/ft5x06: introduce conversion for X and Y coordinates (#19867) ++ drivers/st77xx: introduce rotation defines (#19919) ++ drivers/lcd: add MCU 8080 16-bit parallel mode support (#19937) ++ drivers/lcd: add MCU-driven low-level parallel interface (#19941) ++ drivers/lcd: support MCU 8080 8-bit parallel mode (#19915) ++ drivers/touch_dev_gestures: add gesture recognition for touch devices (#19884) +* drivers/ft5x06: use a pointer to config parameters instead of copying + them (#19866) +* drivers/sdmmc: store SDMMC device descriptor references in XFA (#19899) +* drivers/stmpe811: changes for interrupt-driven touch handling and + gesture recognition (#19885) + +Documentation +------------- + ++ tests/pkg/lwip: Add README.md (#19949) ++ drivers/periph: Add documentation on thread safety and initialization (#19794) +* boards/sltb009a: complete and fix documentation (#19888) +* doc: fix references and inches unit (#19948) + +Build System / Tooling +---------------------- + ++ compile_and_test_for_boards: Add no-compile flag (#19817) ++ dist/testbed-support: Add openmote board [backport 2023.10] (#19984) +* dist/tools/jlink: fix DBG_PID assignment (#19960) +* dist/tools/usb-serial: call ttys.py with its path (#19823) +* make: COMPILE_COMMANDS_PATH adapt for external apps (#19869) + +Examples +-------- + ++ examples/gcoap: add saml11-xpro to CI boards with insufficient memory (#19933) + +Testing +------- + ++ tests/drivers/touch_dev: allow to test a touch device in polling mode (#19882) +* .github/test-on-iotlab: prefer Toulouse site for dwm1001 board (#19950) +* .github: drop test-on-ryot workflow (#19847) +* clang floating point handling fix (#19852) +* gh-actions: remove reporting release tests to Matrix (#19879) +* tests/gcoap_fileserver: only enable test with GCC (#19870) +* tests/net/gcoap_fileserver: disable test on CI (#19898) +* tests/net/gcoap_fileserver: Fix failing nightlies (#19856) +* tests/pkg/relic: skip CI testing with samr21-xpro and llvm toolchain (#19902) + +And 13 minor changes. + + +Bug fixes (37) +============== + +* tree-wide: mixed box of compilation fixes with clang (#19634) +* drivers/stmpe811: introduce conversion for X and Y coordinates (#19883) +* pkg/tinyusb: add missing include (#19893) +* release-test.yml: Add strasbourg creds [backport 2023.10] (#20013) +* tests/gcoap_fileserver: add zep_dispatcher to TEST_DEPS (#19864) +* boards/esp32-wt32-sc01-plus: fix I2C driver selection in Kconfig (#19945) +* boards/esp32s3-wt32-sc01-plus: fix Kconfig (#19953) +* boards/msb-430: Fix periph config & improve doc (#19922) +* boards: fix documentation for GD32V boards and doxygen 1.9.4 (#19931) +* cpu/efm32: fix DAC configuration (#19886) +* cpu/esp32: fix heap definition for ESP32-S2 and ESP32-S3 (#19956) +* cpu/esp32: fix Octal SPI RAM for ESP32-S3 (#19957) +* cpu/esp32: fix RISC-V ISA for ESP32-C3 with GCC 12.2 (#19962) +* cpu/sam0_common/periph/sdhc: busy waiting and clock fixes (#19815) +* drivers/at86rf215: switch example config to use EXT3 on same54-xpro (#19912) +* drivers/enc28j60: disable flow control (#19845) +* drivers/ft5x06: fix initialization if callback function parameter is + NULL (#19880) +* drivers/ft5x06: fix vendor ID for FT6xx6 and FTxxxx register + addresses (#19860) +* drivers/mtd_default: fix for boards that define MTD_NUMOF (#19907) +* drivers/mtd_spi_nor: fix init when only ztimer_msec is used (#19908) +* drivers/st77xx: implement initialization (#19827) +* drivers/stmpe811: fix initialization if callback function parameter + is NULL (#19881) +* drivers: rename st7735 to more generic st77xx (#19825) +* gcoap: fix underflow when correcting ETag from cache [backport + 2023.10] (#19987) +* gnrc_ipv6_nib: disable router advertisements on interface startup (#19920) +* gnrc_ipv6_nib: fix for border router with non-6lo interfaces (#19900) +* nanocoap: prevent integer underflow in coap_opt_put_uri_pathquery() + [backport 2023.10] (#20038) +* netdev/ieee802154_submac: support setting promiscuous mode option (#19906) +* nib/_nib-6ln: bail out early if address is no longer assigned + [backport 2023.10] (#20037) +* pkg/nanocbor: Update for fixed nanocbor_skip_simple() [backport + 2023.10] (#19988) +* pkg/tinydtls: allow to set buffer size from application again (#19892) +* posix_sockets.c: Fix 2 byte int compilation errors (#19946) +* sys/psa_crypto: Fix macro for public key max size and SE example + [backport 2023.10] (#20039) +* sys/shell/ping: fix ping packet size overflow (#19927) +* tests/drivers/disp_dev: fix off by one in display area (#19844) +* tests/pkg/lvgl*: fix the main thread stack size for ESPs (#19865) +* ztimer/periodic: reinit remove from right clock and handle acquired + ztimer (#19826) + + +Known issues +============ + +Network related issues (52) +--------------------------- + +* 6lo: RIOT does not receive packets from Linux when short_addr is set (#11033) +* Address registration handling inappropriate (#15867) +* app/netdev: application stops working after receiving frames with + assertion or completely without error (#8271) +* at86rf2xx: Dead lock when sending while receiving (#8242) +* cpu/esp8266: Tracking open problems of esp_wifi netdev driver (#10861) +* dist/tools/sliptty/start_network.sh: IPv6 connectivity is broken on + PC (#14689) +* driver/mrf24j40: blocks shell input with auto_init_gnrc_netif (#12943) +* drivers/at86rf215: Incorrect channel number set for subGHz (#15906) +* DTLS examples cannot send message to localhost (#14315) +* Emcute cannot create a double-byte name (#12642) +* ethernet: Missing multicast addr assignment (#13493) +* ethos: fails to respond to first message. (#11988) +* ethos: Unable to handle fragmented IPv6 packets from Linux kernel (#12264) +* example/gnrc_border_router cannot answer after some time (#19578) +* examples/cord_ep: Dead lock when (re-)registering in callback + function (#12884) +* examples/gnrc_border_router: esp_wifi crashes on disconnect (#14679) +* Forwarding a packet back to its link layer source should not be + allowed (#5051) +* gcoap example request on tap I/F fails with NIB issue (#8199) +* gcoap: Suspected crosstalk between requests (possible NULL call) (#14390) +* Global IPv6 addresses remain deprecated after receiving RA (#19846) +* gnrc ipv6: multicast packets are not dispatched to the upper layers (#5230) +* gnrc_border_router stops routing after a while (#16398) +* gnrc_border_router: Kconfig and C disagree about number of addresses + per interface (#19947) +* gnrc_icmpv6_echo: flood-pinging another node leads to leaks in own + packet buffer (#12565) +* gnrc_ipv6: Multicast is not forwarded if routing node listens to the + address (#4527) +* gnrc_netif_pktq leaks memory (#17924) +* gnrc_rpl: missing bounds checks in _parse_options (#16085) +* gnrc_rpl: nib route not updated when topology / DODAG changes (#17327) +* gnrc_rpl: old routes are not deleted (#19423) +* gnrc_rpl: takes unusually long time to start routing packets (#19147) +* gnrc_sock_udp: Possible Race condition on copy in application buffer (#10389) +* gnrc_tcp: gnrc_tcp_recv() never generates -ECONNABORTED (#17896) +* gomach: Resetting netif with cli doesn't return (#10370) +* ieee802154_submac: IPv6 fragmentation broken (#16998) +* LoRaWan node ISR stack overflowed (#14962) +* LWIP TCP Communication Error (#19676) +* lwip_sock_tcp / sock_async: received events before calling + sock_accept() are lost due to race condition. (#16303) +* Missing drop implementations in netdev_driver_t::recv (#10410) +* Neighbor Discovery not working after router reboot when using SLAAC (#11038) +* netdev_ieee802154: Mismatch between radio ll address and in memory + address (#10380) +* nrf52: Not able to add global or ULA address to interface (#13280) +* nrfmin: communication not possible after multicast ping with no + interval (#11405) +* ping6 is failing when testing with cc2538dk (#13997) +* pkg/tinydtls: auxiliary data API does not work for async sockets (#16054) +* pkg/tinydtls: DTLS handshake does not work (#19595) +* samr30 xpro doesn't seem to use its radio ok (#12761) +* scan-build errors found during 2019.07 testing (#11852) +* stale border router does not get replaced (#12210) +* test/lwip: enabling both, IPv4 and IPv6, results in unexpected + behavior (#18097) +* tests/lwip: does not compile for IPv4 on 6LoWPAN-based boards. (#17162) +* two nodes livelock sending neighbor solicitations back and forth + between each other (#16670) +* xbee: setting PAN ID sometimes fails (#10338) + +Timer related issues (7) +------------------------ + +* misc issues with tests/trickle (#9052) +* MSP430: periph_timer clock config wrong (#8251) +* periph/timer: `timer_set()` underflow safety check (tracking issue) (#13072) +* periph_timer: systematic proportional error in timer_set (#10545) +* saml21 system time vs rtc (#10523) +* stm32_common/periph/rtc: current implementation broken/poor accuracy (#8746) +* sys/newlib: gettimeofday() returns time since boot, not current wall + time. (#9187) + +Drivers related issues (12) +--------------------------- + +* at86rf2xx: Simultaneous use of different transceiver types is not + supported (#4876) +* cpu/msp430: GPIO driver doesn't work properly (#9419) +* driver/hts221: Temperature and Humidity readings incorrect (#12445) +* examples/dtls-wolfssl not working on pba-d-01-kw2x (#13527) +* fail to send data to can bus (#12371) +* mdt_erase success, but vfs_format resets board (esp32-heltec- + lora32-v2) (#14506) +* periph/spi: Switching between CPOL=0,1 problems on Kinetis with + software CS (#6567) +* periph: GPIO drivers are not thread safe (#4866) +* PWM: Single-phase initialization creates flicker (#15121) +* STM32: SPI clock not returning to idle state and generating + additional clock cycles (#11104) +* TCP client cannot send read only data (#16541) +* tests/periph_flashpage: unexpected behavior on nucleo-l4r5zi (#17599) + +Native related issues (4) +------------------------- + +* examples/micropython: floating point exception while testing on + native (#15870) +* native getchar is blocking RIOT (#16834) +* native not float safe (#495) +* native: tlsf: early malloc will lead to a crash (#5796) + +Other platforms related issues (16) +----------------------------------- + +* Failing tests on FE310 (Hifive1b) (#13086) +* boards/hifive1: flashing issue (#13104) +* cpu/sam0: flashpage write / read cycle produces different results + depending on code layout in flash (#14929) +* cpu/stm32f1: CPU hangs after wake-up from STOP power mode (#13918) +* esp32-wroom-32: tests/netstats_l2 failing sometimes (#14237) +* examples/gnrc_border_router: esp_wifi_init failed with return value + 257 on ESP32-C3 with nimble_rpble (#19319) +* gcoap/esp8266: Stack overflow with gcoap example (#13606) +* Interrupt callback function is instantly called on samd51 after + setting it from within interrupt callback function (#19861) +* MPU doesn't work on cortex-m0+ (#14822) +* newlib-nano: Printf formatting does not work properly for some + numeric types (#1891) +* periph_timer: Test coverage & broken on STM32F767ZI (#15072) +* riscv: ISR stack is too small for ENABLE_DEBUG in core files (#16395) +* stm32152re: hardfault when DBGMCU_CR_DBG* bits are set and branch + after __WFI() (#14015) +* stm32f7: Large performance difference between stm32f746 and stm32f767 (#14728) +* sys/riotboot/flashwrite: unaligned write when skipping + `RIOTBOOT_MAGIC` on stm32wb (#15917) + +Build system related issues (7) +------------------------------- + +* `buildtest` uses wrong build directory (#9742) +* Build dependencies - processing order issues (#9913) +* dist/tools/cppcheck/cppchck.sh: errors when running with Cppcheck + 1.89 (#12771) +* EXTERNAL_MODULE_DIRS silently ignores non-existent entries (#17696) +* make: ccache leads to differing binaries (#14264) +* make: use of immediate value of variables before they have their + final value (#8913) +* missing build dependencies in the rust build (#19714) + +Other issues (67) +----------------- + +* _NVIC_SystemReset stuck in infinite loop when calling pm_reboot + through shell after flashing with J-Link (#13044) +* `make term` no longer works with JLinkExe v6.94 (#16022) +* at86rf215 stops receiving until sending a packet (#19653) +* b-l072z-lrwan1: tests/ztimer_overhead: test failure (#19224) +* backport_pr: Only works for when fork is in user (not in + organization) (#18486) +* benchmark_udp: hammering with low interval causes issues (#16808) +* boards/esp32-wroom-32: tests/mtd_raw flakey (#16130) +* Builds fail when different execstack options are around in objects (#18522) +* Can't build relic with benchmarks or tests (#12897) +* CC2538-CC2592EM has a very weak transmit power (#17543) +* CC2538DK board docs: broken links (#12889) +* cpp: Exception handling undefined (#17523) +* cpu/stm32: some tests are failing on CM33 (l5, u5) (#17439) +* doc/boards: information concerning access to RIOT shell (#17453) +* doc/LOSTANDFOUND: not rendered as expected (#17063) +* edbg: long lines flooded over serial become garbled (#14548) +* examples / tests: LoRa tests fail on platforms that don't support + LoRa (#14520) +* examples/gcoap: client broken (#19379) +* feather-m0: `make flash` reports "device unsupported" (#17722) +* flashing issue on frdm-k64f (#15903) +* frdm-k22f failing tests/periph_flashpage (#17057) +* frdm-k22f fails tests/periph_timer (#19543) +* Freeze into semtech_loramac_send call (pkg/semtech-loramac) (#18790) +* gcoap: gcoap_req_send and related should return negative for errors (#19393) +* gnrc_ipv6_nib: Neighbor Solicitation ping-pong (#18164) +* I2C not working under RIOT with U8G2 pkg (#16381) +* ieee802154_security: Nonce is reused after reboot (#16844) +* kconfiglib.py choice override of menuconfig bug (#19069) +* lwip: drivers/at86rf2xx/at86rf2xx_netdev.c invalid state during TCP + disconnect (#17209) +* lwip: invalid state transition on ieee802154_submac users (#17208) +* Making the newlib thread-safe (#4488) +* mcuboot: flashes but no output (#17524) +* MTD is confusing (#17663) +* nanocoap: incomplete response to /.well-known/core request (#10731) +* Order of auto_init functions (#13541) +* periph_rtt: rtt_set_alarm() blocks IRQ for 80 plus usec on STM32 (#19520) +* pkg/tinydtls: Multiple issues (#16108) +* Potential race condition in compile_and_test_for_board.py (#12621) +* RIOT is saw-toothing in energy consumption (even when idling) (#5009) +* riotboot/nrf52840dk: flashing slot1 with JLINK fails (#14576) +* riotboot: ECC faults (eg. in STM32L5 or STM32WB) not handled + gracefully (#17874) +* rust-gcoap example is incompatible with littlefs2 (#17817) +* Samr30/gpio: Erasing then write mux can generate spurious IRQ (#19993) +* samr34-xpro: some tests failing (#19223) +* sock_dtls: unable to send big messages (#17996) +* spurious IRQs in `periph_timer` (#18976) +* stdio_ethos: infinite shell loop (#17972) +* stdio_tinyusb_cdc_acm hangs with picolibc (#19277) +* STM32 Nucleo boards improperly clocked (#19778) +* sys/riotboot: documentation issues (#11243) +* tests/lwip target board for python test is hardcoded to native (#6533) +* tests/periph_flashpage: failing on stm32l475ve (#17280) +* tests/pkg/relic is failing on samr21-xpro when built using llvm (#19903) +* tests/pkg_libhydrogen: test fails on master for the samr21-xpro with + LLVM (#15066) +* tests/pkg_libschc: Failing test_reassemble_success_ack_always (#19445) +* tests/test_tools: test fails while testing on samr21-xpro/iotlab-m3 (#15888) +* tests: broken with stdio_rtt if auto_init is disabled (#13120) +* tests: some tests don't work with `newlib` lock functions. (#12732) +* Types in `byteorder.h` need a cleanup (#14737) +* USB identifiers with funny characters create mojibake (#17776) +* usbus/msc: wrong error handling and behavior after usb reset (#19478) +* Use of multiple CAN bus on compatible boards (#14801) +* ztimer is incompatible with real-time requirements (#18883) + +There are 161 known issues in this release + + +Fixed Issues since the last release (2023.07) +============================================= + +- at86rf215 stops receiving when sam0_eth is in use (#19911) +- drivers/st7735: faulty driver initialization (#19818) + +2 fixed issues since last release (2023.07) + + +Acknowledgements +================ +We would like to thank all companies that provided us with hardware for porting +and testing RIOT-OS. Further thanks go to companies and institutions that +directly sponsored development time. And finally, big thanks to all of you +contributing in so many different ways to make RIOT worthwhile! + + +More information +================ +http://www.riot-os.org + + +Matrix and Forum +================ +* Join the RIOT Matrix room at: #riot-os:matrix.org +* Join the RIOT Forum at: forum.riot-os.org + + +License +======= +* The code developed by the RIOT community is licensed under the GNU Lesser + General Public License (LGPL) version 2.1 as published by the Free Software + Foundation. +* Some external sources and packages are published under a separate license. + +All code files contain licensing information. + + RIOT-2023.07 - Release Notes ============================ RIOT is a multi-threading operating system which enables soft real-time From 57488a57ab1dbfff911b64d8fd128e03ea4914c0 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 20 Nov 2023 15:16:19 +0100 Subject: [PATCH 53/85] tests/periph/selftest_shield: timer allocation conflict - Detect when the same timer is used by `ztimer` (pulled in as dependency for a peripheral driver, e.g. `periph_adc` on STM32F3) and the test application - Try to provide a better default (e.g. `TIMER_DEV(1)` when `ztimer_periph_timer` is in use, `TIMER_DEV(0)` otherwise) --- tests/periph/selftest_shield/main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 6352a14391..9cbea56a2b 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -126,7 +126,17 @@ # define UART_TEST_DEV UART_DEV(0) #endif #ifndef TIMER -# define TIMER TIMER_DEV(0) +# if IS_USED(MODULE_ZTIMER_PERIPH_TIMER) && CONFIG_ZTIMER_USEC_DEV == TIMER_DEV(0) +# define TIMER TIMER_DEV(1) +# else +# define TIMER TIMER_DEV(0) +# endif +#endif + +#if IS_USED(MODULE_ZTIMER_PERIPH_TIMER) +# if CONFIG_ZTIMER_USEC_DEV == TIMER +# error "Same timer used for ztimer and test" +# endif #endif /* A higher clock frequency is beneficial in being able to actually measure the From 986488db85bbde92d4c855db0890a03a03f4c823 Mon Sep 17 00:00:00 2001 From: Kyle Burk Date: Mon, 3 Jul 2023 22:49:29 -0600 Subject: [PATCH 54/85] cpu/stm32/f1: prevent corrupting AFIO->MAPR The `SWJ_CFG` field of the `AFIO_MAPR` register is write only and values read are undefined (random). Hence, using `AFIO->MAPR |= mask;` to enable flags can corrupt the state of the `SWJ_CFG` (configure it to an unintended value). Two helper functions have been introduced: - `afio_mapr_read()` reads the value, but sanitizes the `SWJ_CFG` field to zero - `afio_mapr_write()` writes the given value, but applies the `SWJ_CFG` configured by the board before writing. Finally, the `nucleo-f103rb` and `bluepill*`/`blackpill*` boards have been updated to no longer specify `STM32F1_DISABLE_JTAG`, as this is handled by the `SWJ_CFG` setting (which defaults to disabling JTAG). --- .../common/blxxxpill/include/board_common.h | 8 --- boards/nucleo-f103rb/Makefile.include | 7 --- cpu/stm32/cpu_init.c | 25 ++++++-- cpu/stm32/include/periph/f1/periph_cpu.h | 61 +++++++++++++++++++ cpu/stm32/periph/i2c_2.c | 2 +- 5 files changed, 83 insertions(+), 20 deletions(-) diff --git a/boards/common/blxxxpill/include/board_common.h b/boards/common/blxxxpill/include/board_common.h index f2f0b244ba..cf10b02003 100644 --- a/boards/common/blxxxpill/include/board_common.h +++ b/boards/common/blxxxpill/include/board_common.h @@ -56,14 +56,6 @@ extern "C" { #define XTIMER_BACKOFF (19) /** @} */ -/* The boards debug header only exports SWD, so JTAG-only pins PA15, PB3(*), - * and PB4 can be remapped as regular GPIOs instead. (Note: PB3 is also used as - * SWO. The user needs to take care to not enable SWO with the debugger while - * at the same time PB3 is used as GPIO. But RIOT does not use SWO in any case, - * so if a user adds this feature in her/his own code, she/he should be well - * aware of this.) - */ -#define STM32F1_DISABLE_JTAG /**< Disable JTAG to allow pins being used as GPIOs */ #ifdef __cplusplus } #endif diff --git a/boards/nucleo-f103rb/Makefile.include b/boards/nucleo-f103rb/Makefile.include index f0ba8bb233..8db8eaed90 100644 --- a/boards/nucleo-f103rb/Makefile.include +++ b/boards/nucleo-f103rb/Makefile.include @@ -1,9 +1,2 @@ # load the common Makefile.include for Nucleo boards include $(RIOTBOARD)/common/nucleo64/Makefile.include - -# On-board debugger uses SWD, so JTAG-only pins PA15, PB3(*), and PB4 can be -# remapped as regular GPIOs instead. (Note: PB3 is also used as SWO. The user -# needs to take care to not enable SWO with the debugger while at the same time -# PB3 is used as GPIO. But RIOT does not use SWO in any case, so if a user adds -# this feature in her/his own code, she/he should be well aware of this.) -CFLAGS += -DSTM32F1_DISABLE_JTAG diff --git a/cpu/stm32/cpu_init.c b/cpu/stm32/cpu_init.c index 4f7c46067e..30b1956961 100644 --- a/cpu/stm32/cpu_init.c +++ b/cpu/stm32/cpu_init.c @@ -333,6 +333,26 @@ void _wlx5xx_init_subghz_debug_pins(void) #endif } +static void swj_init(void) +{ +#if defined(CPU_FAM_STM32F1) + /* Only if the selected SWJ config differs from the reset value, we + * actually need to do something. Since both sides are compile time + * constants, this hole code gets optimized out by default */ + if (CONFIG_AFIO_MAPR_SWJ_CFG != SWJ_CFG_FULL_SWJ) { + /* The remapping periph clock must first be enabled */ + RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; + /* Handling of the MAPR register is a bit involved due to the + * write-only nature of the SWJ_CFG field, which returns undefined + * garbage on read. `afio_mapr_read()` will read the current MAPR + * value, but clear the SWF_CFG vield. `afio_mapr_wriote()` will then + * write the value read back, but apply the `SWF_CFG` configuration + * from `CONFIG_AFIO_MAPR_SWJ_CFG` first.*/ + afio_mapr_write(afio_mapr_read()); + } +#endif +} + void cpu_init(void) { /* initialize the Cortex-M core */ @@ -362,10 +382,7 @@ void cpu_init(void) /* initialize stdio prior to periph_init() to allow use of DEBUG() there */ early_init(); -#ifdef STM32F1_DISABLE_JTAG - RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; - AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE; -#endif + swj_init(); /* trigger static peripheral initialization */ periph_init(); diff --git a/cpu/stm32/include/periph/f1/periph_cpu.h b/cpu/stm32/include/periph/f1/periph_cpu.h index 3225c29030..9a4d33608b 100644 --- a/cpu/stm32/include/periph/f1/periph_cpu.h +++ b/cpu/stm32/include/periph/f1/periph_cpu.h @@ -107,6 +107,67 @@ enum { }; /** @} */ +/** + * @brief Possible values of the `SWJ_CFG` field in the AFIO->MAPR register + * + * @details This wraps the vendor header file preprocessor macros into a + * C language `enum`. + */ +typedef enum { + /** + * @brief Both JTAG-DP and SW-DP enabled, reset state + */ + SWJ_CFG_FULL_SWJ = 0, + /** + * @brief Both JTAG-DP and SW-DP enabled, but NJTRST disabled and pin + * usable as GPIO + */ + SWJ_CFG_NO_NJTRST = AFIO_MAPR_SWJ_CFG_NOJNTRST, + /** + * @brief Only SW-DP enabled, JTAG pins usable as GPIOS + */ + SWJ_CFG_NO_JTAG_DP = AFIO_MAPR_SWJ_CFG_JTAGDISABLE, + /** + * @brief Neither JTAG-DP nor SW-DP enabled, JTAG and SWD pins usable as + * GPIOS + */ + SWJ_CFG_DISABLED = AFIO_MAPR_SWJ_CFG_DISABLE, +} afio_mapr_swj_cfg_t; + +#ifndef CONFIG_AFIO_MAPR_SWJ_CFG +/** + * @brief By default, disable JTAG and keep only SWD + * + * This frees the JTAG pins for use as regular GPIOs. We do not support flashing + * or debugging via JTAG anyway, so there is nothing lost except for a few bytes + * of ROM to initialize the `SWJ_CFG` register. + */ +#define CONFIG_AFIO_MAPR_SWJ_CFG SWJ_CFG_NO_JTAG_DP +#endif + +/** + * @brief Read the current value of the AFIO->MAPR register reproducibly + * + * This will explicitly clear the write-only `SWJ_CFG` field [26:24], as the + * values read back are undefined. + */ +static inline uint32_t afio_mapr_read(void) +{ + return AFIO->MAPR & (~(AFIO_MAPR_SWJ_CFG_Msk)); +} + +/** + * @brief Write to the AFIO->MAPR register apply the SWJ configuration + * specified via @ref CONFIG_AFIO_MAPR_SWJ_CFG + * + * @pre @p new_value has all bits in the range [26:24] cleared (the + * `SWJ_CFG` field). + */ +static inline void afio_mapr_write(uint32_t new_value) +{ + AFIO->MAPR = CONFIG_AFIO_MAPR_SWJ_CFG | new_value; +} + #ifdef __cplusplus } #endif diff --git a/cpu/stm32/periph/i2c_2.c b/cpu/stm32/periph/i2c_2.c index 6a1f53e48d..2fcd74e8b3 100644 --- a/cpu/stm32/periph/i2c_2.c +++ b/cpu/stm32/periph/i2c_2.c @@ -120,7 +120,7 @@ static void _init_pins(i2c_t dev) /* The remapping periph clock must first be enabled */ RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; /* Then the remap can occur */ - AFIO->MAPR |= AFIO_MAPR_I2C1_REMAP; + afio_mapr_write(afio_mapr_read() | AFIO_MAPR_I2C1_REMAP); } gpio_init_af(i2c_config[dev].scl_pin, GPIO_AF_OUT_OD); gpio_init_af(i2c_config[dev].sda_pin, GPIO_AF_OUT_OD); From 04d08e4c2e54cf7f204971445a14f7579a000e60 Mon Sep 17 00:00:00 2001 From: krzysztof-cabaj Date: Mon, 20 Nov 2023 21:34:13 +0100 Subject: [PATCH 55/85] boards/nucleo-l433rc: doc update - image and MCU table --- boards/nucleo-l433rc/doc.txt | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/boards/nucleo-l433rc/doc.txt b/boards/nucleo-l433rc/doc.txt index d88a275d3b..721ca53905 100644 --- a/boards/nucleo-l433rc/doc.txt +++ b/boards/nucleo-l433rc/doc.txt @@ -8,6 +8,33 @@ The Nucleo-L433RC is a board from ST's Nucleo family supporting a ARM Cortex-M4 STM32L433RC microcontroller with 64KiB of RAM and 256KiB of Flash. +### Hardware + +![Nucleo64 L433RC](https://www.st.com/bin/ecommerce/api/image.PF264788.en.feature-description-include-personalized-no-cpn-large.jpg) + +### MCU + +| MCU | STM32L476RG | +|:---------- |:------------------- | +| Family | ARM Cortex-M4 | +| Vendor | ST Microelectronics | +| RAM | 64KiB | +| Flash | 256KiB | +| Frequency | up to 80MHz | +| FPU | yes | +| Timers | 11 (2x watchdog, 1 SysTick, 7x 16-bit, 1x 32-bit) | +| ADCs | 1x 12-bit | +| UARTs | 5 (four USARTs and one Low-Power UART) | +| SPIs | 3 | +| I2Cs | 3 | +| RTC | 1 | +| CAN | 1 | +| Vcc | 1.71 V - 3.6V | +| Datasheet | [Datasheet](https://www.st.com/resource/en/datasheet/stm32l433rc.pdf) | +| Reference Manual | [Reference Manual](https://www.st.com/resource/en/reference_manual/rm0394-stm32l41xxx42xxx43xxx44xxx45xxx46xxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) | +| Programming Manual | [Programming Manual](http://www.st.com/content/ccc/resource/technical/document/programming_manual/6c/3a/cb/e7/e4/ea/44/9b/DM00046982.pdf/files/DM00046982.pdf/jcr:content/translations/en.DM00046982.pdf) | +| Board Manual | [Board Manual](http://www.st.com/resource/en/user_manual/um2206-stm32-nucleo64p-boards-mb1319-stmicroelectronics.pdf) | + ### Flashing the Board Using ST-LINK Removable Media On-board ST-LINK programmer provides via composite USB device removable media. From 097b99f4f2c128295303a7e4baa30133f14599b0 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 10 Nov 2023 17:40:36 +0100 Subject: [PATCH 56/85] cpu/stm32: always apply /CS settings The CR2 register was only written to if the settings differ from the reset value. This wasn't actually a bug, since it was cleared in `spi_release()` to the reset value again. Still, it looks like a bug, may cause a pipeline flush due to the branch, and increased `.text` size. So let's get rid of this. --- cpu/stm32/periph/spi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index 47e324eb95..720b06b8e0 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -278,10 +278,7 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) } #endif dev(bus)->CR1 = cr1_settings; - /* Only modify CR2 if needed */ - if (cr2_extra_settings) { - dev(bus)->CR2 = (SPI_CR2_SETTINGS | cr2_extra_settings); - } + dev(bus)->CR2 = (SPI_CR2_SETTINGS | cr2_extra_settings); } void spi_release(spi_t bus) From 3848a37d147561b820793ad0db0529dcc8354bce Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 21 Nov 2023 09:01:56 +0100 Subject: [PATCH 57/85] boards/nucleo-f303re: add more timers --- boards/common/stm32/include/cfg_timer_tim2_tim15_tim16.h | 4 ++++ boards/nucleo-f303re/include/periph_conf.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/boards/common/stm32/include/cfg_timer_tim2_tim15_tim16.h b/boards/common/stm32/include/cfg_timer_tim2_tim15_tim16.h index 2f2ad1e4e8..56d08e1c9b 100644 --- a/boards/common/stm32/include/cfg_timer_tim2_tim15_tim16.h +++ b/boards/common/stm32/include/cfg_timer_tim2_tim15_tim16.h @@ -36,7 +36,11 @@ static const timer_conf_t timer_config[] = { { .dev = TIM2, .max = 0xffffffff, +#if defined(RCC_APB1ENR_TIM2EN) + .rcc_mask = RCC_APB1ENR_TIM2EN, +#else .rcc_mask = RCC_APB1ENR1_TIM2EN, +#endif .bus = APB1, .irqn = TIM2_IRQn }, diff --git a/boards/nucleo-f303re/include/periph_conf.h b/boards/nucleo-f303re/include/periph_conf.h index d04f6b8542..53d00b210e 100644 --- a/boards/nucleo-f303re/include/periph_conf.h +++ b/boards/nucleo-f303re/include/periph_conf.h @@ -33,7 +33,7 @@ #include "periph_cpu.h" #include "clk_conf.h" -#include "cfg_timer_tim2.h" +#include "cfg_timer_tim2_tim15_tim16.h" #ifdef __cplusplus extern "C" { From e7c9451b55a8048e3b8c68e87d3c73fbe436a8f1 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Nov 2023 09:22:37 +0100 Subject: [PATCH 58/85] drivers/pcf857x: use errno code for error reporting This makes it easier to use common error reporting such as `strerror()` or `tiny_strerror()` to give more insight on why something failed. The custom error codes via `enum` have been updated to be synonymous with the `errno` codes for backward compatibility. In addition, `pcf857x_init()` has been updated to no longer or together the return code, but rather abort on the first fail transaction and return the error code as is. Otherwise (when both fail due to different error codes) the returned error code may be garbage. --- drivers/include/pcf857x.h | 39 ++++++++++++++++++++------------------- drivers/pcf857x/pcf857x.c | 27 +++++++++++++++------------ 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/drivers/include/pcf857x.h b/drivers/include/pcf857x.h index c2ce3ba888..d0cd02f6e7 100644 --- a/drivers/include/pcf857x.h +++ b/drivers/include/pcf857x.h @@ -245,10 +245,10 @@ extern "C" { #endif +#include #include #include -#include "kernel_defines.h" #include "periph/gpio.h" #include "periph/i2c.h" @@ -326,14 +326,18 @@ typedef uint8_t pcf857x_data_t; /**< type that can mask all expander pins */ #endif /* MODULE_PCF8575 || DOXYGEN */ /** @} */ -/** Definition of PCF857X driver error codes */ +/** + * @brief Definition of PCF857X driver error codes + * + * @deprecated These are aliases for errno error codes now, use them directly + */ typedef enum { - PCF857X_OK, /**< success */ - PCF857X_ERROR_I2C, /**< I2C communication error */ - PCF857X_ERROR_INV_EXP, /**< invalid expander variant */ - PCF857X_ERROR_INV_MODE, /**< invalid pin mode */ - PCF857X_ERROR_INV_FLANK, /**< invalid interrupt flank */ - PCF857X_ERROR_INT_PIN, /**< interrupt pin initialization failed */ + PCF857X_OK = 0, /**< success */ + PCF857X_ERROR_I2C = ENXIO, /**< I2C communication error */ + PCF857X_ERROR_INV_EXP = ENOTSUP, /**< invalid expander variant */ + PCF857X_ERROR_INV_MODE = EINVAL, /**< invalid pin mode */ + PCF857X_ERROR_INV_FLANK = EINVAL, /**< invalid interrupt flank */ + PCF857X_ERROR_INT_PIN = ENOSYS, /**< interrupt pin initialization failed */ } pcf857x_error_codes_t; /** @@ -453,9 +457,8 @@ typedef struct { * has to be defined by the default configuration parameter * #PCF857X_PARAM_INT_PIN (pcf857x_params_t::int_pin). * - * @retval PCF857X_OK on success - * @retval PCF857X_ERROR_* a negative error code on error, - * see #pcf857x_error_codes_t + * @retval 0 on success + * @retval <0 a negative errno error code on error */ int pcf857x_init(pcf857x_t *dev, const pcf857x_params_t *params); @@ -472,13 +475,12 @@ int pcf857x_init(pcf857x_t *dev, const pcf857x_params_t *params); * the driver physically supports only the modes #GPIO_IN_PU and * #GPIO_OD_PU. The other logically identical modes #GPIO_IN, #GPIO_OUT * and #GPIO_OD are emulated. For the #GPIO_IN_PU mode the function returns - * with #PCF857X_ERROR_INV_MODE. + * with `-EINVAL`. * - After initialization in #GPIO_OUT mode the pin is actively driven LOW, * after initialization in all other modes the pin is pulled-up to HIGH. * - * @retval PCF857X_OK on success - * @retval PCF857X_ERROR_* a negative error code on error, - * see #pcf857x_error_codes_t + * @retval 0 on success + * @retval <0 a negative errno error code on error */ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode); @@ -504,7 +506,7 @@ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode); * the driver physically supports only the modes #GPIO_IN_PU and * #GPIO_OD_PU. The other logically identical modes #GPIO_IN, #GPIO_OUT * and #GPIO_OD are emulated. For the #GPIO_IN_PU mode the function returns - * with #PCF857X_ERROR_INV_MODE. + * with `-EINVAL`. * - After initialization in #GPIO_OUT mode the pin is actively driven LOW, * after initialization in all other modes the pin is pulled-up to HIGH. * @@ -515,9 +517,8 @@ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode); * @param[in] isr ISR that is called back from interrupt context * @param[in] arg optional argument passed to the callback * - * @retval PCF857X_OK on success - * @retval PCF857X_ERROR_* a negative error code on error, - * see #pcf857x_error_codes_t + * @retval 0 on success + * @retval <0 a negative errno error code on error */ int pcf857x_gpio_init_int(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode, diff --git a/drivers/pcf857x/pcf857x.c b/drivers/pcf857x/pcf857x.c index aa4744395d..32416a8656 100644 --- a/drivers/pcf857x/pcf857x.c +++ b/drivers/pcf857x/pcf857x.c @@ -14,8 +14,9 @@ * @{ */ -#include +#include #include +#include #include "pcf857x.h" @@ -104,7 +105,7 @@ int pcf857x_init(pcf857x_t *dev, const pcf857x_params_t *params) dev->params.addr += PCF8575_BASE_ADDR; break; #endif - default: return -PCF857X_ERROR_INV_EXP; + default: return -ENOTSUP; } #if IS_USED(MODULE_PCF857X_IRQ) @@ -121,7 +122,7 @@ int pcf857x_init(pcf857x_t *dev, const pcf857x_params_t *params) /* initialize the interrupt pin */ if (gpio_init_int(dev->params.int_pin, GPIO_IN_PU, GPIO_FALLING, _irq_isr, (void*)dev)) { - return -PCF857X_ERROR_INT_PIN; + return -ENOSYS; } #endif /* MODULE_PCF857X_IRQ */ @@ -131,13 +132,15 @@ int pcf857x_init(pcf857x_t *dev, const pcf857x_params_t *params) /* write 1 to all pins to switch them to INPUTS pulled up to HIGH */ dev->out = ~0; - res |= _write(dev, dev->out); + res = _write(dev, dev->out); - /* initial read all pins */ - res |= _read(dev, &dev->in); + if (!res) { + /* initial read all pins */ + res = _read(dev, &dev->in); - /* set all pin modes to INPUT and set internal output data to 1 (HIGH) */ - dev->modes = ~0; + /* set all pin modes to INPUT and set internal output data to 1 (HIGH) */ + dev->modes = ~0; + } _release(dev); @@ -161,7 +164,7 @@ int pcf857x_gpio_init(pcf857x_t *dev, gpio_t pin, gpio_mode_t mode) */ switch (mode) { case GPIO_IN_PD: DEBUG_DEV("gpio mode GPIO_IN_PD not supported", dev); - return -PCF857X_ERROR_INV_MODE; + return -EINVAL; case GPIO_OUT: dev->modes &= ~(1 << pin); /* set mode bit to 0 */ dev->out &= ~(1 << pin); /* set output bit to 0 */ break; @@ -228,7 +231,7 @@ int pcf857x_gpio_init_int(pcf857x_t *dev, gpio_t pin, dev->enabled[pin] = true; break; default: DEBUG_DEV("invalid flank %d for pin %d", dev, flank, pin); - return -PCF857X_ERROR_INV_FLANK; + return -EINVAL; } return PCF857X_OK; @@ -428,7 +431,7 @@ static int _read(const pcf857x_t *dev, pcf857x_data_t *data) if (res != 0) { DEBUG_DEV("could not read data, reason %d (%s)", dev, res, strerror(res * -1)); - return -PCF857X_ERROR_I2C; + return res; } if (dev->pin_num == 8) { @@ -469,7 +472,7 @@ static int _write(const pcf857x_t *dev, pcf857x_data_t data) if (res != 0) { DEBUG_DEV("could not write data, reason %d (%s)", dev, res, strerror(res * -1)); - return -PCF857X_ERROR_I2C; + return res; } return PCF857X_OK; From 64b95a34e9e100e6ea28021968fda4d37b8c4031 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Tue, 21 Nov 2023 12:09:42 +0100 Subject: [PATCH 59/85] tests/periph/selftest_shield: improve output on error Use `tiny_strerror()` to report back errors when calling initialization functions. --- tests/periph/selftest_shield/Makefile | 2 + tests/periph/selftest_shield/main.c | 90 ++++++++++++++++++--------- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/tests/periph/selftest_shield/Makefile b/tests/periph/selftest_shield/Makefile index d3166a9b86..06de04b29a 100644 --- a/tests/periph/selftest_shield/Makefile +++ b/tests/periph/selftest_shield/Makefile @@ -20,6 +20,8 @@ FEATURES_OPTIONAL += periph_spi FEATURES_OPTIONAL += periph_timer FEATURES_OPTIONAL += periph_uart +USEMODULE += tiny_strerror + STOP_ON_FAILURE ?= 0 DETAILED_OUTPUT ?= 0 diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index 9cbea56a2b..cf5f2726f6 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -40,6 +40,7 @@ #include "periph/timer.h" #include "periph/uart.h" #include "stdio_uart.h" /* for STDIO_UART_DEV */ +#include "tiny_strerror.h" /* BEGIN: controls of the behavior of the testing app: */ #ifndef STOP_ON_FAILURE @@ -232,7 +233,7 @@ static bool do_test(bool failed, uint16_t line) return failed; } -static void do_assert(bool failed, uint16_t line) +static void MAYBE_UNUSED do_assert(bool failed, uint16_t line) { if (failed) { printf("CRITICAL "); @@ -244,6 +245,18 @@ static void do_assert(bool failed, uint16_t line) } } +static void do_assert_no_error(int retval, uint16_t line) +{ + if (retval != 0) { + printf("ERROR in " __FILE__ ":%" PRIu16 " with code %s\n", + line, tiny_strerror(retval)); + ARCHITECTURE_BREAKPOINT(1); + while (1) { + /* stop */ + } + } +} + static void print_result(bool failed) { if (failed) { @@ -277,9 +290,28 @@ static void _print_start(const char *name, const char *detail, uint16_t line) # define print_start(name, detail) _print_start(name, NULL, __LINE__) #endif +/** + * @brief Expression @p x must evaluate, otherwise fail but continue other + * tests + * + * @pre The test is safe to continue even if the test fails + */ #define TEST(x) do_test(!(x), __LINE__) - +/** + * @brief Expression @p x must evaluate, otherwise fail and abort + * + * @pre The test is ***NOT*** safe to continue if the test fails + */ #define ASSERT(x) do_assert(!(x), __LINE__) +/** + * @brief The expression @p must return 0, otherwise abort + * + * @pre The return value will be a positive or negative errno code, if it + * is not zero + * + * This prints the errno code and aborts if @p x is not evaluating to zero + */ +#define ASSERT_NO_ERROR(x) do_assert_no_error(x, __LINE__) static void stupid_delay(unsigned count) { @@ -312,8 +344,8 @@ static bool periph_gpio_test_push_pull(void) { bool failed = false; print_start("GPIO", "push-pull"); - ASSERT(gpio_init(ARDUINO_PIN_3, GPIO_IN) == 0); - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_3, GPIO_IN)); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_clear(ARDUINO_PIN_4); for (unsigned i = 0; i < flaky_test_repetitions; i++) { @@ -334,7 +366,7 @@ static bool periph_gpio_test_input_pull_up(void) { bool failed = false; print_start("GPIO", "input pull-up"); - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_IN)); if (gpio_init(ARDUINO_PIN_3, GPIO_IN_PU) == 0) { /* give pull resistor a little time to pull */ brief_delay(); @@ -346,7 +378,7 @@ static bool periph_gpio_test_input_pull_up(void) } /* push/pull on D4 should still be able to force down D3 */ - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_clear(ARDUINO_PIN_4); for (unsigned i = 0; i < flaky_test_repetitions; i++) { failed |= TEST(gpio_read(ARDUINO_PIN_3) == 0); @@ -364,7 +396,7 @@ static bool periph_gpio_test_input_pull_down(void) { bool failed = false; print_start("GPIO", "input pull-down"); - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_IN)); if (gpio_init(ARDUINO_PIN_3, GPIO_IN_PD) == 0) { /* give pull resistor a little time to pull */ brief_delay(); @@ -376,7 +408,7 @@ static bool periph_gpio_test_input_pull_down(void) } /* push/pull on D4 should still be able to force up D3 */ - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_set(ARDUINO_PIN_4); for (unsigned i = 0; i < flaky_test_repetitions; i++) { failed |= TEST(gpio_read(ARDUINO_PIN_3) != 0); @@ -395,7 +427,7 @@ static bool periph_gpio_test_open_drain_pull_up(void) bool failed = false; print_start("GPIO", "open-drain pull-up"); - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_IN) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_IN)); if (gpio_init(ARDUINO_PIN_3, GPIO_OD_PU) == 0) { gpio_set(ARDUINO_PIN_3); /* give pull resistor a little time to pull */ @@ -468,9 +500,9 @@ static bool periph_gpio_irq_test_falling(void) bool failed = false; print_start("GPIO-IRQ", "falling-edge"); atomic_uint cnt = 0; - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_clear(ARDUINO_PIN_4); - ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_FALLING, gpio_cb, &cnt) == 0); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_FALLING, gpio_cb, &cnt)); /* no stray IRQ */ brief_delay(); @@ -531,9 +563,9 @@ static bool periph_gpio_irq_test_rising(void) bool failed = false; print_start("GPIO-IRQ", "rising-edge"); atomic_uint cnt = 0; - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_set(ARDUINO_PIN_4); - ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_RISING, gpio_cb, &cnt) == 0); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_RISING, gpio_cb, &cnt)); /* no stray IRQ */ brief_delay(); @@ -594,9 +626,9 @@ static bool periph_gpio_irq_test_both(void) bool failed = false; print_start("GPIO-IRQ", "both-edges"); atomic_uint cnt = 0; - ASSERT(gpio_init(ARDUINO_PIN_4, GPIO_OUT) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); gpio_set(ARDUINO_PIN_4); - ASSERT(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_BOTH, gpio_cb, &cnt) == 0); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_BOTH, gpio_cb, &cnt)); /* no stray IRQ */ brief_delay(); @@ -667,10 +699,10 @@ static bool periph_i2c_test(void) { bool failed = false; print_start("I2C", "GPIO extender"); - ASSERT(gpio_init(ARDUINO_PIN_8, GPIO_IN) == 0); - ASSERT(gpio_init(ARDUINO_PIN_9, GPIO_OUT) == 0); - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 0), GPIO_OUT) == 0); - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 1), GPIO_IN) == 0); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_8, GPIO_IN)); + ASSERT_NO_ERROR(gpio_init(ARDUINO_PIN_9, GPIO_OUT)); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 0), GPIO_OUT)); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 1), GPIO_IN)); for (unsigned i = 0; i < flaky_test_repetitions; i++) { gpio_set(ARDUINO_PIN_9); @@ -707,7 +739,7 @@ static bool periph_uart_rxtx_test(uint32_t symbolrate) uint16_t start; memset(&serial_buf, 0, sizeof(serial_buf)); - ASSERT(uart_init(UART_TEST_DEV, symbolrate, uart_rx_cb, NULL) == 0); + ASSERT_NO_ERROR(uart_init(UART_TEST_DEV, symbolrate, uart_rx_cb, NULL)); if (IS_USED(MODULE_PERIPH_TIMER)) { bit_ticks = TIMER_FREQ_UART_TEST / symbolrate; @@ -778,7 +810,7 @@ static bool periph_uart_test(void) bool failed = false; if (IS_USED(MODULE_PERIPH_TIMER)) { - ASSERT(timer_init(TIMER, TIMER_FREQ_UART_TEST, NULL, NULL) == 0); + ASSERT_NO_ERROR(timer_init(TIMER, TIMER_FREQ_UART_TEST, NULL, NULL)); timer_start(TIMER); } @@ -886,7 +918,7 @@ static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, static bool periph_spi_test(void) { if (IS_USED(MODULE_PERIPH_TIMER)) { - ASSERT(timer_init(TIMER, TIMER_FREQ_SPI_TEST, NULL, NULL) == 0); + ASSERT_NO_ERROR(timer_init(TIMER, TIMER_FREQ_SPI_TEST, NULL, NULL)); timer_start(TIMER); } @@ -896,7 +928,7 @@ static bool periph_spi_test(void) if (IS_USED(MODULE_PCF857X)) { for (int i = 0; i < (int)ARRAY_SIZE(spi_clk_check_pins); i++) { - ASSERT(pcf857x_gpio_init(&egpios, spi_clk_check_pins[i], GPIO_IN) == 0); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, spi_clk_check_pins[i], GPIO_IN)); } } @@ -931,7 +963,7 @@ static bool periph_pwm_test_chan(pwm_t pwm_dev, uint8_t pwm_chan, pwm_mode_t pwm return failed; } - ASSERT(adc_init(adc_line) == 0); + ASSERT_NO_ERROR(adc_init(adc_line)); for (uint16_t i = 0; i <= UINT8_MAX; i++) { pwm_set(pwm_dev, pwm_chan, i); @@ -975,10 +1007,10 @@ static bool periph_pwm_test(void) static void r_2r_dac_init(void) { - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 4), GPIO_OUT) == 0); - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 5), GPIO_OUT) == 0); - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 6), GPIO_OUT) == 0); - ASSERT(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 7), GPIO_OUT) == 0); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 4), GPIO_OUT)); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 5), GPIO_OUT)); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 6), GPIO_OUT)); + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, PCF857X_GPIO_PIN(0, 7), GPIO_OUT)); } static void r_2r_dac_write(uint8_t val) @@ -1029,7 +1061,7 @@ int main(void) /* the GPIO extender is used by the I2C test and the ADC test, so only * initialize it once here */ if (IS_USED(MODULE_PCF857X)) { - ASSERT(pcf857x_init(&egpios, ¶ms) == PCF857X_OK); + ASSERT_NO_ERROR(pcf857x_init(&egpios, ¶ms)); } if (IS_USED(MODULE_PERIPH_GPIO)) { From fb9abe3626a8cedc1c33286bbb60b8cd564571be Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Nov 2023 09:34:38 +0100 Subject: [PATCH 60/85] tests/periph/selftest_shield: fix a misplaced comment Move it to the correct place --- tests/periph/selftest_shield/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/periph/selftest_shield/main.c b/tests/periph/selftest_shield/main.c index cf5f2726f6..313908d00c 100644 --- a/tests/periph/selftest_shield/main.c +++ b/tests/periph/selftest_shield/main.c @@ -868,8 +868,8 @@ static bool periph_spi_rxtx_test(spi_t bus, spi_mode_t mode, spi_clk_t clk, * theoretical time. Given the overhead of, this already has some * room for error */ transfer_too_fast |= (byte_time < byte_transfer_ticks); - /* C̅S̅ should be still LOW while chip is selected */ } + /* C̅S̅ should be still LOW while chip is selected */ failed |= TEST(gpio_read(cs_check) == 0); } From df70e00b923993b304eaaa5e217782243a02c4ae Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 23 Nov 2023 16:50:23 +0100 Subject: [PATCH 61/85] sys/random doc: Make implementation list visible --- sys/include/random.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sys/include/random.h b/sys/include/random.h index 49cc59aeaf..317caa8bc4 100644 --- a/sys/include/random.h +++ b/sys/include/random.h @@ -10,10 +10,6 @@ * @defgroup sys_random Random * @ingroup sys * @brief Pseudo Random Number Generator (PRNG) - * @{ - * - * @file - * @brief Common interface to the software PRNG * * Various implementations of a PRNG are available: * - Tiny Mersenne Twister (default) @@ -24,6 +20,11 @@ * - Hardware Random Number Generator (non-seedable) * HWRNG differ in how they generate random numbers and may not use a PRNG internally. * Refer to the manual of your MCU for details. + * + * @{ + * + * @file + * @brief Common interface to the software PRNG */ #ifndef RANDOM_H From 4c97a278267166ef058d94f5452f9ca73fdb80ce Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 16:51:19 +0200 Subject: [PATCH 62/85] cpu/sam0_common: implement freqm peripheral --- cpu/sam0_common/periph/freqm.c | 259 +++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 cpu/sam0_common/periph/freqm.c diff --git a/cpu/sam0_common/periph/freqm.c b/cpu/sam0_common/periph/freqm.c new file mode 100644 index 0000000000..4bea82909f --- /dev/null +++ b/cpu/sam0_common/periph/freqm.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2023 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_sam0_common + * @ingroup drivers_periph_freqm + * @{ + * + * @file freqm.c + * @brief Frequency meter driver implementation + * + * @author Urs Gompper + * + * @} + */ + +#include "periph/freqm.h" + +/* TODO: Remove defines when Microchip vendor files (which include these + * defines) get updated. + */ +/* FREQM_GCLK_ID_REF is defined in newer versions of vendor header files */ +#ifndef FREQM_GCLK_ID_REF +#define FREQM_GCLK_ID_REF (FREQM_GCLK_ID_MSR + 1) +#endif + +/* Channel Enable Mask */ +#define GCLK_PCHCTRL_CHEN_Msk (_U_(0x1) << GCLK_PCHCTRL_CHEN_Pos) +/* Enable Mask */ +#define FREQM_CTRLA_ENABLE_Msk (_U_(0x1) << FREQM_CTRLA_ENABLE_Pos) +/* Start Measurement Mask */ +#define FREQM_CTRLB_START_Msk (_U_(0x1) << FREQM_CTRLB_START_Pos) +/* Measurement Done Interrupt Enable Mask */ +#define FREQM_INTENSET_DONE_Msk (_U_(0x1) << FREQM_INTENSET_DONE_Pos) +/* Measurement Done Mask */ +#define FREQM_INTFLAG_DONE_Msk (_U_(0x1) << FREQM_INTFLAG_DONE_Pos) +/* FREQM Status Mask */ +#define FREQM_STATUS_BUSY_Msk (_U_(0x1) << FREQM_STATUS_BUSY_Pos) +/* Sticky Count Value Overflow Mask */ +#define FREQM_STATUS_OVF_Msk (_U_(0x1) << FREQM_STATUS_OVF_Pos) + +/* check if pin has peripheral function GCLK */ +static int _freqm_pin(gpio_t pin) +{ + for (unsigned i = 0; i < ARRAY_SIZE(gclk_io_pins); ++i) { + if (gclk_io_pins[i] == pin) { + return i; + } + } + + return -1; +} + +static void _gclk_connect(uint8_t id, uint8_t src, uint32_t flags) +{ + GCLK->GENCTRL[id].reg = GCLK_GENCTRL_SRC(src) | GCLK_GENCTRL_GENEN | flags | GCLK_GENCTRL_IDC; + while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(id)) {} +} + +static int _freqm_gpio_init(gpio_t msr_gpio_src, uint8_t *gclk_io_id) +{ + /* Check if selected pin has peripheral function GCLK */ + int index = _freqm_pin(msr_gpio_src); + + /* Fail assertion if pin has no peripheral function GCLK */ + assert(index > 0); + + /* Lookup which GCLK_IO[x] must be used */ + *gclk_io_id = gclk_io_ids[index]; + /* GCLK_IO[0] and GCLK_IO[1] can't be used here. They are associated with + GCLKGEN[0] and GCLKGEN[1] respectively. These in turn are used by + SAM0_GCLK_MAIN and SAM0_GCLK_32KHZ respectively */ + assert(*gclk_io_id > 1); + + /* Initialize GPIO as input */ + gpio_init(msr_gpio_src, GPIO_IN); + /* Enable peripheral function GCLK/IO on GPIO */ + gpio_init_mux(msr_gpio_src, GPIO_MUX_M); + /* Connect GCLK_IO[*gclk_io_id] with input pin */ + _gclk_connect(*gclk_io_id, GCLK_SOURCE_GCLKIN, 0); + + return 0; +} + +static void _freqm_clock_init(uint8_t pin, uint8_t gclk_src) +{ + /* Selection of the Generator and write Lock for FREQM_MSR */ + GCLK->PCHCTRL[FREQM_GCLK_ID_MSR].reg = GCLK_PCHCTRL_GEN(pin) | GCLK_PCHCTRL_CHEN_Msk; + /* Wait for synchronization */ + while ((GCLK->PCHCTRL[FREQM_GCLK_ID_MSR].reg & GCLK_PCHCTRL_CHEN_Msk) != + GCLK_PCHCTRL_CHEN_Msk) {} + + /* Selection of the Generator and write Lock for FREQM_REF */ + GCLK->PCHCTRL[FREQM_GCLK_ID_REF].reg = GCLK_PCHCTRL_GEN(gclk_src) | GCLK_PCHCTRL_CHEN_Msk; + /* Wait for synchronization */ + while ((GCLK->PCHCTRL[FREQM_GCLK_ID_REF].reg & GCLK_PCHCTRL_CHEN_Msk) != + GCLK_PCHCTRL_CHEN_Msk) {} +} + +static struct { + freqm_cb_t callback; + void *context; + freqm_t idx; + uint8_t period_cnt; +} freqm_obj; + +struct _sync_ctx { + mutex_t lock; /**< Mutex for blocking till measurement is done */ + uint32_t hz; /**< Measured frequency in Hz */ + bool overflow; /**< Overflow in FREQM counter */ +}; + +/** + * @brief Mutex for locking the FREQM device + */ +static mutex_t msr_lock = MUTEX_INIT; + +static void _freqm_enable(uint8_t refnum) +{ + mutex_lock(&msr_lock); + + /* Save refnum for frequency calculation */ + freqm_obj.period_cnt = refnum; + + FREQM->CFGA.reg = (uint16_t)(FREQM_CFGA_REFNUM(refnum)); + + /* Enable DONE Interrupt */ + FREQM->INTENSET.reg = FREQM_INTENSET_DONE_Msk; + + /* Enable FREQM */ + FREQM->CTRLA.reg = FREQM_CTRLA_ENABLE_Msk; + + /* Wait for Sync */ + while ((FREQM->SYNCBUSY.reg) != 0U) {} +} + +static void _freqm_disable(void) +{ + /* Disable DONE Interrupt */ + FREQM->INTENCLR.reg = FREQM_INTENCLR_MASK; + /* Disable FREQM */ + FREQM->CTRLA.reg &= ~FREQM_CTRLA_ENABLE_Msk; + /* Wait for Sync */ + while ((FREQM->SYNCBUSY.reg) != 0U) {} + + mutex_unlock(&msr_lock); +} + +bool _freqm_get_measurement(uint32_t *result) +{ + const freqm_config_t *config = &freqm_config[freqm_obj.idx]; + + /* Calculate measured frequency */ + uint64_t result_tmp = FREQM->VALUE.reg * (uint64_t)(sam0_gclk_freq(config->gclk_src)); + + result_tmp = result_tmp / freqm_obj.period_cnt; + *result = (uint32_t)result_tmp; + + /* Read overflow status */ + bool overflow_condition = ((int)FREQM->STATUS.reg & FREQM_STATUS_OVF_Msk); + + /* Clear overflow status */ + FREQM->STATUS.reg = FREQM_STATUS_OVF_Msk; + + return overflow_condition; +} + +uint32_t _us_to_ref_clock_counts(uint32_t period_us, uint8_t clock_id) +{ + uint64_t clk_cnt = (uint64_t)period_us * sam0_gclk_freq(clock_id) / US_PER_SEC; + + if (clk_cnt > UINT8_MAX) { + return UINT8_MAX; + } + else if (clk_cnt == 0) { + return 1; + } + else { + return clk_cnt; + } +} + +static void _sync_cb(uint32_t res, bool overflow, void *_ctx) +{ + struct _sync_ctx *ctx = _ctx; + + ctx->hz = res; + ctx->overflow = overflow; + mutex_unlock(&ctx->lock); +} + +int freqm_frequency_get(freqm_t idx, uint32_t *result, uint32_t period_us) +{ + struct _sync_ctx ctx = { .lock = MUTEX_INIT_LOCKED }; + + /* Invoke non-blocking FREQM measure function */ + freqm_frequency_get_async(idx, _sync_cb, &ctx, period_us); + + /* Block until measurement is done */ + mutex_lock(&ctx.lock); + + *result = ctx.hz; + return ctx.overflow ? -EOVERFLOW : 0; +} + +void freqm_frequency_get_async(freqm_t idx, freqm_cb_t freqm_cb, void *context, uint32_t period_us) +{ + const freqm_config_t *config = &freqm_config[idx]; + + uint8_t refnum = _us_to_ref_clock_counts(period_us, config->gclk_src); + + _freqm_enable(refnum); + + /* Register callback function */ + freqm_obj.callback = freqm_cb; + freqm_obj.context = context; + freqm_obj.idx = idx; + + /* Clear the Done Interrupt flag */ + FREQM->INTFLAG.reg = FREQM_INTFLAG_DONE_Msk; + + /* Start measurement */ + FREQM->CTRLB.reg = FREQM_CTRLB_START_Msk; +} + +void irq_freqm(void) +{ + /* Clear the Done Interrupt flag */ + FREQM->INTFLAG.reg = FREQM_INTFLAG_DONE_Msk; + + uint32_t result = 0; + + bool overflow_condition = _freqm_get_measurement(&result); + + /* Invoke the callback function */ + freqm_obj.callback(result, overflow_condition, freqm_obj.context); + + _freqm_disable(); +} + +void freqm_init(freqm_t idx) +{ + uint8_t gclk_io_id = 0; + const freqm_config_t *config = &freqm_config[idx]; + + /* Sanity check configuration */ + assert(config->gclk_src <= GCLK_GEN_NUM_MSB); + + _freqm_gpio_init(config->pin, &gclk_io_id); + _freqm_clock_init(gclk_io_id, config->gclk_src); + + /* Enable interrupt */ + NVIC_EnableIRQ(FREQM_IRQn); +} From afcd4801bd702474c15aa1a781a3f4e81a5c4243 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 16:52:33 +0200 Subject: [PATCH 63/85] drivers/include: add header definition for freqm --- drivers/include/periph/freqm.h | 100 +++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 drivers/include/periph/freqm.h diff --git a/drivers/include/periph/freqm.h b/drivers/include/periph/freqm.h new file mode 100644 index 0000000000..706114182e --- /dev/null +++ b/drivers/include/periph/freqm.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_periph_freqm FREQM + * @ingroup drivers_periph + * @brief FREQM peripheral driver interface + * + * This interface allows to configure and use the Frequency Meter (FREQM) + * peripheral. + * + * The Frequency Meter uses the frequency of a known reference clock to + * determine the frequency of a signal connected via GPIO. + * + * @{ + * + * @file + * @brief FREQM peripheral driver interface definitions + * + * @author Urs Gompper + */ + +#ifndef PERIPH_FREQM_H +#define PERIPH_FREQM_H + +#include +#include + +#include "periph_cpu.h" +#include "periph/gpio.h" +#include "time_units.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Frequency meter callback function. + * When a measurement is done the callbackfunction is called. + * + * @param result measured frequency in hz + * @param overflow overflow in sticky counter + * @param context pointer to user defined context data + */ +typedef void (*freqm_cb_t)(uint32_t result, bool overflow, void *context); + +/** + * @brief Define default Frequency meter type identifier + */ +#ifndef HAVE_FREQM_T +typedef uint_fast8_t freqm_t; +#endif + +/** + * @brief Initialize the frequency meter + * + * @param[in] idx index of the configuration + */ +void freqm_init(freqm_t idx); + +/** + * @brief Read number of periods of measured clock and calculate its frequency + * + * This function returns after triggering the measurement and calls + * @p freqm_callback , with the calculated result and @p context , when the + * measurement is done. + * + * @param[in] idx index of the configuration + * @param[in] freqm_cb callback function when measurement is ready + * @param[in] context context for the callback function + * @param[in] period_us measurement duration in microseconds + */ +void freqm_frequency_get_async(freqm_t idx, freqm_cb_t freqm_cb, void *context, + uint32_t period_us); + +/** + * @brief Read number of periods of measured clock and calculate its frequency + * + * This function uses a blocking mutex to wait for the measurement to finish. + * + * @param[in] idx index of the configuration + * @param[out] result calculated frequency + * @param[in] period_us measurement duration in microseconds + * + * @return -EOVERFLOW if FREQM sticky counter has an overflow + * @return 0 on success + */ +int freqm_frequency_get(freqm_t idx, uint32_t *result, uint32_t period_us); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* PERIPH_FREQM_H */ From 4a9ae47499857ebe0adcc1649f361ec91b973462 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 16:53:17 +0200 Subject: [PATCH 64/85] tests/periph: add test-application for peripheral freqm --- tests/periph/freqm/Makefile | 7 +++++ tests/periph/freqm/README.md | 14 ++++++++++ tests/periph/freqm/main.c | 52 ++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/periph/freqm/Makefile create mode 100644 tests/periph/freqm/README.md create mode 100644 tests/periph/freqm/main.c diff --git a/tests/periph/freqm/Makefile b/tests/periph/freqm/Makefile new file mode 100644 index 0000000000..fe22b6f7ec --- /dev/null +++ b/tests/periph/freqm/Makefile @@ -0,0 +1,7 @@ +BOARD ?= same54-xpro + +include ../Makefile.periph_common + +USEMODULE += periph_freqm + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph/freqm/README.md b/tests/periph/freqm/README.md new file mode 100644 index 0000000000..ac40fd85ef --- /dev/null +++ b/tests/periph/freqm/README.md @@ -0,0 +1,14 @@ +Peripheral FREQM Test Application +===================================== + +This application tests the frequency meter (FREQM) functionality. This is done +by measuring the frequency of a clock, connected to a GPIO, with an internal +clock as reference. + +Expected Output on Success +-------------------------- + + main(): This is RIOT! (Version: ) + FREQM peripheral driver test + Measured clock frequency: Hz + Test run finished. diff --git a/tests/periph/freqm/main.c b/tests/periph/freqm/main.c new file mode 100644 index 0000000000..432957c971 --- /dev/null +++ b/tests/periph/freqm/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 ML!PA Consulting GmbH + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Application to test functionality of the frequency meter + * peripheral + * + * @author Urs Gompper + * @} + */ + +#include +#include +#include +#include +#include + +#include "periph/freqm.h" + +int main(void) +{ + puts("FREQM peripheral driver test"); + + /* Initialize frequency meter peripheral */ + freqm_init(0); + + uint32_t period_us = UINT32_MAX; + uint32_t freq_hz = 0; + + /* Measure in blocking mode */ + if (!freqm_frequency_get(0, &freq_hz, period_us)) { + printf("Measured Clock Frequency: %ld Hz\n", freq_hz); + } + else { + puts("Overflow occurred to the FREQM value counter!"); + return EXIT_FAILURE; + } + + puts("Test run finished."); + + /* main thread exits */ + return 0; +} From 5479c7eb96f3a765b5ea11a18acaeaad27a2e649 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 17:03:16 +0200 Subject: [PATCH 65/85] cpu/samd5x: add conditional enabling of freqm peripheral --- cpu/samd5x/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpu/samd5x/cpu.c b/cpu/samd5x/cpu.c index 0001b2e867..19f27beed3 100644 --- a/cpu/samd5x/cpu.c +++ b/cpu/samd5x/cpu.c @@ -325,6 +325,9 @@ void cpu_init(void) #ifdef MODULE_PERIPH_PM | MCLK_APBAMASK_PM #endif +#ifdef MODULE_PERIPH_FREQM + | MCLK_APBAMASK_FREQM +#endif #ifdef MODULE_PERIPH_GPIO_IRQ | MCLK_APBAMASK_EIC #endif From c2f7aa66f9652620550be93ac30fc20e3948e6b7 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 17:01:03 +0200 Subject: [PATCH 66/85] boards/same54-xpro: add periph_freqm to Makefile.features --- boards/same54-xpro/Makefile.features | 1 + 1 file changed, 1 insertion(+) diff --git a/boards/same54-xpro/Makefile.features b/boards/same54-xpro/Makefile.features index 076663ed20..3e3491ebeb 100644 --- a/boards/same54-xpro/Makefile.features +++ b/boards/same54-xpro/Makefile.features @@ -14,6 +14,7 @@ FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_usbdev +FEATURES_PROVIDED += periph_freqm # Put other features for this board (in alphabetical order) FEATURES_PROVIDED += riotboot From 82f803b7cfe82f766a2f4231a3412afd2d129e42 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 16:59:52 +0200 Subject: [PATCH 67/85] boards/same54-xpro: add freqm peripheral to Kconfig --- boards/same54-xpro/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/boards/same54-xpro/Kconfig b/boards/same54-xpro/Kconfig index 8dbdbd0b54..e93ff58243 100644 --- a/boards/same54-xpro/Kconfig +++ b/boards/same54-xpro/Kconfig @@ -18,6 +18,7 @@ config BOARD_SAME54_XPRO select HAS_PERIPH_RTC select HAS_PERIPH_RTT select HAS_PERIPH_PWM + select HAS_PERIPH_FREQM select HAS_PERIPH_SDMMC select HAS_PERIPH_SPI select HAS_PERIPH_TIMER From 97bde07e0da7e6fa0a4e393342834854ced5b95a Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Wed, 25 Oct 2023 17:09:07 +0200 Subject: [PATCH 68/85] drivers/periph_common: add peripheral freqm to Kconfig --- drivers/periph_common/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/periph_common/Kconfig b/drivers/periph_common/Kconfig index 4db0969b8e..564a6283ad 100644 --- a/drivers/periph_common/Kconfig +++ b/drivers/periph_common/Kconfig @@ -139,6 +139,10 @@ config MODULE_PERIPH_RTT depends on HAS_PERIPH_RTT select MODULE_PERIPH_COMMON +config MODULE_PERIPH_FREQM + bool "Frequency Meter driver" + depends on HAS_PERIPH_FREQM + config MODULE_PERIPH_RTT_SET_COUNTER bool "rtc_set_counter() implementation in the RTT peripheral driver" depends on HAS_PERIPH_RTT_SET_COUNTER && MODULE_PERIPH_RTT From b1e31fbf611709f79f479347400935c78feee7cc Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Mon, 6 Nov 2023 08:40:53 +0100 Subject: [PATCH 69/85] cpu/samd5x: define GCLK pins --- cpu/samd5x/include/periph_cpu.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cpu/samd5x/include/periph_cpu.h b/cpu/samd5x/include/periph_cpu.h index f89b6ee498..8d98a8841c 100644 --- a/cpu/samd5x/include/periph_cpu.h +++ b/cpu/samd5x/include/periph_cpu.h @@ -198,6 +198,28 @@ static const gpio_t rtc_tamper_pins[RTC_NUM_OF_TAMPERS] = { GPIO_PIN(PC, 0), GPIO_PIN(PC, 1) }; +/** + * @brief Pins that have peripheral function GCLK + */ +static const gpio_t gclk_io_pins[] = { + GPIO_PIN(PA, 10), GPIO_PIN(PA, 11), GPIO_PIN(PA, 14), + GPIO_PIN(PA, 15), GPIO_PIN(PA, 16), GPIO_PIN(PA, 17), + GPIO_PIN(PA, 27), GPIO_PIN(PA, 30), GPIO_PIN(PB, 10), + GPIO_PIN(PB, 11), GPIO_PIN(PB, 12), GPIO_PIN(PB, 13), + GPIO_PIN(PB, 14), GPIO_PIN(PB, 15), GPIO_PIN(PB, 16), + GPIO_PIN(PB, 17), GPIO_PIN(PB, 18), GPIO_PIN(PB, 19), + GPIO_PIN(PB, 20), GPIO_PIN(PB, 21), GPIO_PIN(PB, 22), + GPIO_PIN(PB, 23) +}; + +/** + * @brief GCLK IDs of pins that have peripheral function GCLK - This maps + * directly to gclk_io_pins. + */ +static const uint8_t gclk_io_ids[] = { + 4, 5, 0, 1, 2, 3, 1, 0, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1 +}; + /** * @brief NVM User Page Mapping - Dedicated Entries * Config values will be applied at power-on. From 6849ef9827e455b9d9df93b7135f909b4186706e Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Mon, 6 Nov 2023 09:55:10 +0100 Subject: [PATCH 70/85] boards/same54-xpro: added FREQM peripheral configuration --- boards/same54-xpro/include/periph_conf.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/boards/same54-xpro/include/periph_conf.h b/boards/same54-xpro/include/periph_conf.h index ae85accbab..5a402d196b 100644 --- a/boards/same54-xpro/include/periph_conf.h +++ b/boards/same54-xpro/include/periph_conf.h @@ -406,6 +406,18 @@ static const sam0_common_gmac_config_t sam_gmac_config[] = { }; /** @} */ +/** + * @name FREQM peripheral configuration + * @{ + */ +static const freqm_config_t freqm_config[] = { + { + .pin = GPIO_PIN(PB, 17), + .gclk_src = SAM0_GCLK_32KHZ + } +}; +/** @} */ + #ifdef __cplusplus } #endif From f352609c5e41b98a6a05b88a081d259befce4885 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Mon, 6 Nov 2023 10:03:15 +0100 Subject: [PATCH 71/85] cpu/sam0_common: added peripheral FREQM configuration declaration --- cpu/sam0_common/include/periph_cpu_common.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 8f5e9cb47f..31b146a96e 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -945,6 +945,14 @@ typedef struct { */ #define WDT_HAS_INIT (1) +/** + * @brief Frequency meter configuration + */ +typedef struct { + gpio_t pin; /**< GPIO at which the frequency is to be measured */ + uint8_t gclk_src; /**< GCLK source select for reference */ +} freqm_config_t; + #if defined(REV_DMAC) || DOXYGEN /** * @name sam0 DMA peripheral From 12acc8dec9fa9a552f3bcb2ca6a7d583b2358eff Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Mon, 6 Nov 2023 10:38:01 +0100 Subject: [PATCH 72/85] cpu/samd5x: make GCLK definitions overwritable --- cpu/samd5x/include/periph_cpu.h | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/cpu/samd5x/include/periph_cpu.h b/cpu/samd5x/include/periph_cpu.h index 8d98a8841c..450941af1f 100644 --- a/cpu/samd5x/include/periph_cpu.h +++ b/cpu/samd5x/include/periph_cpu.h @@ -70,13 +70,19 @@ enum { * @name SAMD5x GCLK definitions * @{ */ -enum { - SAM0_GCLK_MAIN = 0, /**< 120 MHz main clock */ - SAM0_GCLK_32KHZ, /**< 32 kHz clock */ - SAM0_GCLK_TIMER, /**< 4-8 MHz clock for xTimer */ - SAM0_GCLK_PERIPH, /**< 12-48 MHz (DFLL) clock */ - SAM0_GCLK_100MHZ, /**< 100MHz FDPLL clock */ -}; +#define SAM0_GCLK_MAIN 0 /**< 120 MHz main clock */ +#ifndef SAM0_GCLK_32KHZ +#define SAM0_GCLK_32KHZ 1 /**< 32 kHz clock */ +#endif +#ifndef SAM0_GCLK_TIMER +#define SAM0_GCLK_TIMER 2 /**< 4-8 MHz clock for xTimer */ +#endif +#ifndef SAM0_GCLK_PERIPH +#define SAM0_GCLK_PERIPH 3 /**< 12-48 MHz (DFLL) clock */ +#endif +#ifndef SAM0_GCLK_100MHZ +#define SAM0_GCLK_100MHZ 4 /**< 100MHz FDPLL clock */ +#endif /** @} */ /** From a2e4fa30fde3d6d0883637df69af15aa95381d20 Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Mon, 6 Nov 2023 10:39:14 +0100 Subject: [PATCH 73/85] boards/same54-xpro: overwrite GCLK definitions --- boards/same54-xpro/Makefile.include | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/boards/same54-xpro/Makefile.include b/boards/same54-xpro/Makefile.include index 9e5bbf58a1..f8f1b33e90 100644 --- a/boards/same54-xpro/Makefile.include +++ b/boards/same54-xpro/Makefile.include @@ -3,4 +3,11 @@ # debugger. TTY_BOARD_FILTER := --model 'EDBG CMSIS-DAP' +# Overwrite GCLK definitions, so that GCLK_IO[2..7] can be connected to GPIOs. +# This way the frequency of signals, connected to these pins, can be measured +# with the FREQM peripheral. +CFLAGS += -DSAM0_GCLK_TIMER=8 +CFLAGS += -DSAM0_GCLK_PERIPH=9 +CFLAGS += -DSAM0_GCLK_100MHZ=10 + include $(RIOTMAKE)/boards/sam0.inc.mk From bfb3b5fe724c251f378fd20a60a5a1da5c76821e Mon Sep 17 00:00:00 2001 From: Urs Gompper Date: Thu, 23 Nov 2023 20:28:26 +0100 Subject: [PATCH 74/85] dist/tools/doccheck: add FREQM config to generic_exclude_pattern --- dist/tools/doccheck/generic_exclude_patterns | 1 + 1 file changed, 1 insertion(+) diff --git a/dist/tools/doccheck/generic_exclude_patterns b/dist/tools/doccheck/generic_exclude_patterns index 45f56c164f..afd2c9c218 100644 --- a/dist/tools/doccheck/generic_exclude_patterns +++ b/dist/tools/doccheck/generic_exclude_patterns @@ -20,6 +20,7 @@ warning: Member EPD_BW_SPI_DISPLAY_UPDATE_OPTION_[A-Z0-9_]* \(macro definition\) warning: Member EPD_BW_SPI_WAIT_[A-Z0-9_]* \(macro definition\) of warning: Member F_CPU \(macro definition\) of warning: Member F_RC_OSCILLATOR \(macro definition\) of +warning: Member freqm_config\[\] \(variable\) of warning: Member FXOS8700_PARAM_ADDR \(macro definition\) of warning: Member FXOS8700_PARAM_I2C \(macro definition\) of warning: Member FXOS8700_PARAM_RENEW_INTERVAL \(macro definition\) of From e9e1f9cce48cb6a593025320cd3a8c9388032a58 Mon Sep 17 00:00:00 2001 From: chrysn Date: Thu, 23 Nov 2023 16:53:52 +0100 Subject: [PATCH 75/85] sys/random doc: Point to auto initialization and its caveats --- sys/include/random.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sys/include/random.h b/sys/include/random.h index 317caa8bc4..56baa9f7a6 100644 --- a/sys/include/random.h +++ b/sys/include/random.h @@ -21,6 +21,11 @@ * HWRNG differ in how they generate random numbers and may not use a PRNG internally. * Refer to the manual of your MCU for details. * + * By default, the `auto_init_random` module is enabled, which initializes the + * PRNG on startup. However, there is no lower limit on the entropy provided at + * that time. Unless the `periph_hwrng` module is used, entropy may be as + * little as zero (the constant may even be the same across devices). + * * @{ * * @file @@ -55,6 +60,9 @@ extern "C" { /** * @brief initializes PRNG with a seed * + * Users only need to call this if the `auto_init_random` module is disabled, + * or provides insufficient quality entropy. + * * @warning Currently, the random module uses a global state * => multiple calls to @ref random_init will reset the existing * state of the PRNG. From 7057aa674d2a1f41949e5749b17fb6efa08b8279 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 10 Nov 2023 17:46:10 +0100 Subject: [PATCH 76/85] cpu/stm32: Provide `spi_mode_t` This doesn't change the firmware, since for all STM32 MCUs with an SPI driver the register setting in the mode did match the SPI mode number by chance. But for some STM32 MCUs with no SPI driver yet the register layout is indeed different. This will help to provide an SPI driver for them as well. --- cpu/stm32/include/periph_cpu.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index 95a9bd4036..67d9a06ec1 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -170,6 +170,36 @@ typedef struct { #define USBDEV_NUM_ENDPOINTS 8 #endif +/* unify names across STM32 families */ +#ifdef SPI_CR1_CPHA_Msk +# define STM32_SPI_CPHA_Msk SPI_CR1_CPHA_Msk +#endif +#ifdef SPI_CFG2_CPHA_Msk +# define STM32_SPI_CPHA_Msk SPI_CFG2_CPHA_Msk +#endif +#ifdef SPI_CR1_CPOL_Msk +# define STM32_SPI_CPOL_Msk SPI_CR1_CPOL_Msk +#endif +#ifdef SPI_CFG2_CPOL_Msk +# define STM32_SPI_CPOL_Msk SPI_CFG2_CPOL_Msk +#endif + +/** + * @name Override the SPI mode values + * + * As the mode is set in bit 3 and 2 of the configuration register, we put the + * correct configuration there + * @{ + */ +#define HAVE_SPI_MODE_T +typedef enum { + SPI_MODE_0 = 0, /**< CPOL=0, CPHA=0 */ + SPI_MODE_1 = STM32_SPI_CPHA_Msk, /**< CPOL=0, CPHA=1 */ + SPI_MODE_2 = STM32_SPI_CPOL_Msk, /**< CPOL=1, CPHA=0 */ + SPI_MODE_3 = STM32_SPI_CPOL_Msk | STM32_SPI_CPHA_Msk, /**< CPOL=1, CPHA=0 */ +} spi_mode_t; +/** @} */ + #endif /* !DOXYGEN */ #ifdef __cplusplus From 63a2a50b5fc6142f5088a61819e8b66b716d79b5 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Mon, 13 Nov 2023 16:13:02 +0100 Subject: [PATCH 77/85] cpu/stm32/periph_spi: Fix /CS handling Previously, the /CS signal was performed by enabling / disabling the SPI peripheral. This had the disadvantage that clock polarity settings where not applied starting with `spi_acquire()`, as assumed by e.g. the SPI SD card driver, but only just before transmitting data. Now the SPI peripheral is enabled on `spi_acquire()` and only disabled when calling `spi_release()`, and the `SPI_CR2_SSOE` bit in the `CR2` register is used for hardware /CS handling (as supposed to). --- cpu/stm32/periph/spi.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index 720b06b8e0..f512d759e9 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -29,7 +29,6 @@ #include #include "bitarithm.h" -#include "cpu.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -246,19 +245,19 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), br); - uint16_t cr1_settings = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR); + uint16_t cr1 = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR | SPI_CR1_SPE); /* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */ - uint16_t cr2_extra_settings = 0; + uint16_t cr2 = SPI_CR2_SETTINGS; if (cs != SPI_HWCS_MASK) { - cr1_settings |= (SPI_CR1_SSM | SPI_CR1_SSI); + cr1 |= (SPI_CR1_SSM | SPI_CR1_SSI); } else { - cr2_extra_settings = (SPI_CR2_SSOE); + cr2 = (SPI_CR2_SSOE); } #ifdef MODULE_PERIPH_DMA if (_use_dma(&spi_config[bus])) { - cr2_extra_settings |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN; + cr2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN; dma_acquire(spi_config[bus].tx_dma); dma_setup(spi_config[bus].tx_dma, @@ -277,8 +276,8 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) 0); } #endif - dev(bus)->CR1 = cr1_settings; - dev(bus)->CR2 = (SPI_CR2_SETTINGS | cr2_extra_settings); + dev(bus)->CR1 = cr1; + dev(bus)->CR2 = cr2; } void spi_release(spi_t bus) @@ -393,10 +392,12 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, assert(out || in); /* active the given chip select line */ - dev(bus)->CR1 |= (SPI_CR1_SPE); /* this pulls the HW CS line low */ if ((cs != SPI_HWCS_MASK) && gpio_is_valid(cs)) { gpio_clear((gpio_t)cs); } + else { + dev(bus)->CR2 |= SPI_CR2_SSOE; + } #ifdef MODULE_PERIPH_DMA if (_use_dma(&spi_config[bus])) { @@ -411,9 +412,11 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, /* release the chip select if not specified differently */ if ((!cont) && gpio_is_valid(cs)) { - dev(bus)->CR1 &= ~(SPI_CR1_SPE); /* pull HW CS line high */ if (cs != SPI_HWCS_MASK) { gpio_set((gpio_t)cs); } + else { + dev(bus)->CR2 &= ~(SPI_CR2_SSOE); + } } } From f4729c28ec0f9ea77bfaf4d4b1a56ad0d48fd72d Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 15 Nov 2023 17:56:57 +0100 Subject: [PATCH 78/85] cpu/stm32/periph_spi: improve prescaler calculation With only 8 possible prescalers, we can just loop over the values and shift the clock. In addition to being much easier to read, using shifts over divisions can be a lot faster on CPUs without hardware division. In addition an `assert()` is added that checks if the API contract regarding the SPI frequency is honored. If the requested clock is too low to be generated, we should rather have a blown assertion than hard to trace communication errors. Finally, the term prescaler is used instead of divider, as divider may imply that the frequency is divided by the given value n, but in fact is divided by 2^(n+1). --- cpu/stm32/periph/spi.c | 56 +++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index f512d759e9..b312a37c7a 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -28,7 +28,6 @@ #include -#include "bitarithm.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -64,9 +63,9 @@ static mutex_t locks[SPI_NUMOF]; static uint32_t clocks[SPI_NUMOF]; /** - * @brief Clock divider cache + * @brief Clock prescaler cache */ -static uint8_t dividers[SPI_NUMOF]; +static uint8_t prescalers[SPI_NUMOF]; static inline SPI_TypeDef *dev(spi_t bus) { @@ -80,33 +79,24 @@ static inline bool _use_dma(const spi_conf_t *conf) } #endif -/** - * @brief Multiplier for clock divider calculations - * - * Makes the divider calculation fixed point - */ -#define SPI_APB_CLOCK_SHIFT (4U) -#define SPI_APB_CLOCK_MULT (1U << SPI_APB_CLOCK_SHIFT) - -static uint8_t _get_clkdiv(const spi_conf_t *conf, uint32_t clock) +static uint8_t _get_prescaler(const spi_conf_t *conf, uint32_t clock) { uint32_t bus_clock = periph_apb_clk(conf->apbbus); - /* Shift bus_clock with SPI_APB_CLOCK_SHIFT to create a fixed point int */ - uint32_t div = (bus_clock << SPI_APB_CLOCK_SHIFT) / (2 * clock); - DEBUG("[spi] clock: divider: %"PRIu32"\n", div); - /* Test if the divider is 2 or smaller, keeping the fixed point in mind */ - if (div <= SPI_APB_CLOCK_MULT) { - return 0; + + uint8_t prescaler = 0; + uint32_t prescaled_clock = bus_clock >> 1; + const uint8_t prescaler_max = SPI_CR1_BR_Msk >> SPI_CR1_BR_Pos; + for (; (prescaled_clock > clock) && (prescaler < prescaler_max); prescaler++) { + prescaled_clock >>= 1; } - /* determine MSB and compensate back for the fixed point int shift */ - uint8_t rounded_div = bitarithm_msb(div) - SPI_APB_CLOCK_SHIFT; - /* Determine if rounded_div is not a power of 2 */ - if ((div & (div - 1)) != 0) { - /* increment by 1 to ensure that the clock speed at most the - * requested clock speed */ - rounded_div++; - } - return rounded_div > BR_MAX ? BR_MAX : rounded_div; + + /* If the callers asks for an SPI frequency of at most x, bad things will + * happen if this cannot be met. So let's have a blown assertion + * rather than runtime failures that require a logic analyzer to + * debug. */ + assert(prescaled_clock <= clock); + + return prescaler; } void spi_init(spi_t bus) @@ -234,16 +224,16 @@ void spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) periph_clk_en(spi_config[bus].apbbus, spi_config[bus].rccmask); /* enable device */ if (clk != clocks[bus]) { - dividers[bus] = _get_clkdiv(&spi_config[bus], clk); + prescalers[bus] = _get_prescaler(&spi_config[bus], clk); clocks[bus] = clk; } - uint8_t br = dividers[bus]; + uint8_t br = prescalers[bus]; - DEBUG("[spi] acquire: requested clock: %"PRIu32", resulting clock: %"PRIu32 - " BR divider: %u\n", + DEBUG("[spi] acquire: requested clock: %" PRIu32 + " Hz, resulting clock: %" PRIu32 " Hz, BR prescaler: %u\n", clk, - periph_apb_clk(spi_config[bus].apbbus)/(1 << (br + 1)), - br); + periph_apb_clk(spi_config[bus].apbbus) >> (br + 1), + (unsigned)br); uint16_t cr1 = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR | SPI_CR1_SPE); /* Settings to add to CR2 in addition to SPI_CR2_SETTINGS */ From fa0b6824d2381700364662284d4ecb24c0f1f034 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 24 Nov 2023 09:48:26 +0100 Subject: [PATCH 79/85] boards/nrf51dk: add periph_adc --- boards/nrf51dk/include/periph_conf.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/boards/nrf51dk/include/periph_conf.h b/boards/nrf51dk/include/periph_conf.h index 4399bd50b3..cfc98f2483 100644 --- a/boards/nrf51dk/include/periph_conf.h +++ b/boards/nrf51dk/include/periph_conf.h @@ -73,6 +73,30 @@ static const i2c_conf_t i2c_config[] = { #define I2C_NUMOF ARRAY_SIZE(i2c_config) /** @} */ +/** + * @name ADC configuration + * + * The ADC channels have a fixed mapping: + * + * | Channel | MCU Pin | Arduino pin on board | + * |:---------- |:--------- |:------------------------------------- | + * | AIN0 | P0.26 | -- (exposed, by no Arduino UNO pin) | + * | AIN1 | P0.27 | -- (exposed, by no Arduino UNO pin) | + * | AIN2 | P0.01 | A0 | + * | AIN3 | P0.02 | A1 | + * | AIN4 | P0.03 | A2 | + * | AIN5 | P0.04 | A3 | + * | AIN6 | P0.05 | A4 | + * | AIN7 | P0.06 | A5 | + * + * Expose those on Arduino pins A0 to A5 + * @{ + */ +static const adc_conf_t adc_config[] = {2, 3, 4, 5, 6, 7}; + +#define ADC_NUMOF ARRAY_SIZE(adc_config) +/** @} */ + #ifdef __cplusplus } #endif From f55af927359b02f4779f83f7a887c3917776eccd Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 24 Nov 2023 09:48:42 +0100 Subject: [PATCH 80/85] boards/nrf51dk: add arduino features --- boards/nrf51dk/Kconfig | 7 ++ boards/nrf51dk/Makefile.features | 7 ++ boards/nrf51dk/include/arduino_iomap.h | 123 +++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 boards/nrf51dk/include/arduino_iomap.h diff --git a/boards/nrf51dk/Kconfig b/boards/nrf51dk/Kconfig index be2b3812bf..3f157b8e70 100644 --- a/boards/nrf51dk/Kconfig +++ b/boards/nrf51dk/Kconfig @@ -12,6 +12,13 @@ config BOARD_NRF51DK default y select BOARD_COMMON_NRF51 select CPU_MODEL_NRF51X22XXAC + select HAS_ARDUINO_ANALOG + select HAS_ARDUINO_I2C + select HAS_ARDUINO_PINS + select HAS_ARDUINO_SHIELD_ISP + select HAS_ARDUINO_SHIELD_UNO + select HAS_ARDUINO_SPI + select HAS_PERIPH_ADC select HAS_PERIPH_I2C select HAS_PERIPH_SPI select HAS_PERIPH_UART diff --git a/boards/nrf51dk/Makefile.features b/boards/nrf51dk/Makefile.features index bd3cdbeace..3bf236b315 100644 --- a/boards/nrf51dk/Makefile.features +++ b/boards/nrf51dk/Makefile.features @@ -1,11 +1,18 @@ CPU_MODEL = nrf51x22xxac # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_uart FEATURES_PROVIDED += periph_uart_hw_fc FEATURES_PROVIDED += vdd_lc_filter_reg1 +FEATURES_PROVIDED += arduino_analog +FEATURES_PROVIDED += arduino_i2c +FEATURES_PROVIDED += arduino_pins +FEATURES_PROVIDED += arduino_shield_isp +FEATURES_PROVIDED += arduino_shield_uno +FEATURES_PROVIDED += arduino_spi # include common nrf51 based boards features include $(RIOTBOARD)/common/nrf51/Makefile.features diff --git a/boards/nrf51dk/include/arduino_iomap.h b/boards/nrf51dk/include/arduino_iomap.h new file mode 100644 index 0000000000..3d6f5c60f9 --- /dev/null +++ b/boards/nrf51dk/include/arduino_iomap.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2023 Otto-von-Guericke-Universität Magdeburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup boards_nrf51dk + * @{ + * + * @file + * @brief Mapping from MCU pins to Arduino pins + * + * You can use the defines in this file for simplified interaction with the + * Arduino specific pin numbers. + * + * @author Marian Buschsieweke + */ + +#ifndef ARDUINO_IOMAP_H +#define ARDUINO_IOMAP_H + +#include "periph/gpio.h" +#include "periph/adc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Arduino's SPI buses + * @{ + */ +/** + * @brief SPI_DEV(0) is connected to D11/D12/D13 + */ +/* D11/D12/D13 are on P0.25, P0.28, P0.29 */ +#define ARDUINO_SPI_D11D12D13 SPI_DEV(0) +/* The ISP SPI bus is also connected to P0.25, P0.28, P0.29 */ +#define ARDUINO_SPI_ISP SPI_DEV(0) +/** @} */ + +/** + * @name Arduino's I2C buses + * @{ + */ +/** + * @brief The first I2C bus is where shields for the Arduino UNO/Mega expect + * it + */ +#define ARDUINO_I2C_UNO I2C_DEV(0) +/** @} */ + +/** + * @name Mapping of MCU pins to Arduino pins + * @{ + */ +/* P3 header on the nRF51DK */ +#define ARDUINO_PIN_0 GPIO_PIN(0, 12) +#define ARDUINO_PIN_1 GPIO_PIN(0, 13) +#define ARDUINO_PIN_2 GPIO_PIN(0, 14) +#define ARDUINO_PIN_3 GPIO_PIN(0, 15) +#define ARDUINO_PIN_4 GPIO_PIN(0, 16) +#define ARDUINO_PIN_5 GPIO_PIN(0, 17) +#define ARDUINO_PIN_6 GPIO_PIN(0, 18) +#define ARDUINO_PIN_7 GPIO_PIN(0, 19) + +/* P4 header on the nRF51DK */ +#define ARDUINO_PIN_8 GPIO_PIN(0, 20) +#define ARDUINO_PIN_9 GPIO_PIN(0, 23) +#define ARDUINO_PIN_10 GPIO_PIN(0, 24) +#define ARDUINO_PIN_11 GPIO_PIN(0, 25) +#define ARDUINO_PIN_12 GPIO_PIN(0, 28) +#define ARDUINO_PIN_13 GPIO_PIN(0, 29) +/* SCL and SDA on this header are not available as digital pins in the + * Arduino world. It is P0.30 and P0.07 here, though. Also what would be + * AREF in the Arduino world is P0.00 here */ + +/* P2 header on the nRF51DK */ +#define ARDUINO_PIN_14 GPIO_PIN(0, 1) +#define ARDUINO_PIN_15 GPIO_PIN(0, 2) +#define ARDUINO_PIN_16 GPIO_PIN(0, 3) +#define ARDUINO_PIN_17 GPIO_PIN(0, 4) +#define ARDUINO_PIN_18 GPIO_PIN(0, 5) +#define ARDUINO_PIN_19 GPIO_PIN(0, 6) + +#define ARDUINO_PIN_LAST 19 +/** @} */ + +/** + * @name Aliases for analog pins + * @{ + */ +#define ARDUINO_PIN_A0 ARDUINO_PIN_14 +#define ARDUINO_PIN_A1 ARDUINO_PIN_15 +#define ARDUINO_PIN_A2 ARDUINO_PIN_16 +#define ARDUINO_PIN_A3 ARDUINO_PIN_17 +#define ARDUINO_PIN_A4 ARDUINO_PIN_18 +#define ARDUINO_PIN_A5 ARDUINO_PIN_19 +/** @} */ + +/** + * @name Mapping of Arduino analog pins to RIOT ADC lines + * @{ + */ +#define ARDUINO_A0 ADC_LINE(0) +#define ARDUINO_A1 ADC_LINE(1) +#define ARDUINO_A2 ADC_LINE(2) +#define ARDUINO_A3 ADC_LINE(3) +#define ARDUINO_A4 ADC_LINE(4) +#define ARDUINO_A5 ADC_LINE(5) + +#define ARDUINO_ANALOG_PIN_LAST 5 +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* ARDUINO_IOMAP_H */ +/** @} */ From e4074602439ca64ab531a7cdc265975b082e59af Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Fri, 24 Nov 2023 10:03:07 +0100 Subject: [PATCH 81/85] cpu/nrf51: fix periph_i2c driver The `i2c_read_bytes()` and `i2c_write_bytes()` function return the number of bytes written / read, instead of `0` as the API contract says. This fixes the issue. --- cpu/nrf51/periph/i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpu/nrf51/periph/i2c.c b/cpu/nrf51/periph/i2c.c index cd2efd09e9..4bfd801655 100644 --- a/cpu/nrf51/periph/i2c.c +++ b/cpu/nrf51/periph/i2c.c @@ -103,7 +103,7 @@ static int write(i2c_t dev, uint16_t addr, const void *data, int len, } } - return len; + return 0; } void i2c_init(i2c_t dev) @@ -198,7 +198,7 @@ int i2c_read_bytes(i2c_t dev, uint16_t address, void *data, size_t length, while (i2c(dev)->EVENTS_STOPPED == 0) {} NRF_PPI->CHENCLR = (1 << i2c_config[dev].ppi); - return length; + return 0; } int i2c_read_regs(i2c_t dev, uint16_t address, uint16_t reg, From f18208eb6437d8bb1854bed7ec017f3c6b33a599 Mon Sep 17 00:00:00 2001 From: chrysn Date: Sat, 25 Nov 2023 19:29:03 +0100 Subject: [PATCH 82/85] makefile: Don't $(info) when the output can break info-boards-supported --- Makefile.include | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile.include b/Makefile.include index 609a1f8fec..cd91664f68 100644 --- a/Makefile.include +++ b/Makefile.include @@ -117,7 +117,6 @@ ifneq ($(RIOT_CI_BUILD),1) ifeq ($(MAKELEVEL),0) ifneq (,$(BOARDSDIR)) $(warning Using BOARDSDIR is deprecated use EXTERNAL_BOARD_DIRS instead) - $(info EXTERNAL_BOARD_DIRS can contain multiple folders separated by space) endif endif endif From 63faa5f16231489788634c9b782a4cf3df273c20 Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Wed, 22 Nov 2023 15:27:59 +0100 Subject: [PATCH 83/85] cpu/nrf5x: clean up periph_uart - nRF51: Use `uart_conf_t` for consistency with nRF52 - nRF52832: Use UARTE (UART with EasyDMA) over UART (without DMA), as done for all other nRF52 family members - use `UARTE_PRESENT` to detect whether an UARTE can be used, rather than family names --- boards/acd52832/include/periph_conf.h | 18 +- boards/airfy-beacon/include/periph_conf.h | 20 +- boards/calliope-mini/include/periph_conf.h | 19 +- boards/dwm1001/include/periph_conf.h | 20 +- boards/microbit/include/periph_conf.h | 21 +- boards/nrf51dk/include/periph_conf.h | 23 +- boards/nrf51dongle/include/periph_conf.h | 23 +- boards/nrf52832-mdk/include/periph_conf.h | 18 +- boards/nrf52dk/include/periph_conf.h | 18 +- boards/nrf6310/include/periph_conf.h | 21 +- boards/ruuvitag/include/periph_conf.h | 18 +- boards/thingy52/include/periph_conf.h | 18 +- boards/yunjia-nrf51822/include/periph_conf.h | 21 +- cpu/nrf51/include/periph_cpu.h | 1 - cpu/nrf52/Kconfig | 7 +- cpu/nrf52/Makefile.features | 6 +- cpu/nrf5x_common/include/periph_cpu_common.h | 10 +- cpu/nrf5x_common/periph/uart.c | 390 +++++++++---------- 18 files changed, 397 insertions(+), 275 deletions(-) diff --git a/boards/acd52832/include/periph_conf.h b/boards/acd52832/include/periph_conf.h index c17278166f..736bba4082 100644 --- a/boards/acd52832/include/periph_conf.h +++ b/boards/acd52832/include/periph_conf.h @@ -43,9 +43,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 30) -#define UART_PIN_TX GPIO_PIN(0, 31) +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 30), + .tx_pin = GPIO_PIN(0, 31), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ /** diff --git a/boards/airfy-beacon/include/periph_conf.h b/boards/airfy-beacon/include/periph_conf.h index 094916c099..db0b912c31 100644 --- a/boards/airfy-beacon/include/periph_conf.h +++ b/boards/airfy-beacon/include/periph_conf.h @@ -31,13 +31,23 @@ /** * @name UART configuration - * - * The CPU only supports one UART device, so we keep it simple * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX 17 -#define UART_PIN_TX 18 +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 17), + .tx_pin = GPIO_PIN(0, 18), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/boards/calliope-mini/include/periph_conf.h b/boards/calliope-mini/include/periph_conf.h index 3d77c88048..5c62be7b73 100644 --- a/boards/calliope-mini/include/periph_conf.h +++ b/boards/calliope-mini/include/periph_conf.h @@ -33,10 +33,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -/* UART pin configuration */ -#define UART_PIN_RX 25 -#define UART_PIN_TX 24 +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 25), + .tx_pin = GPIO_PIN(0, 24), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/boards/dwm1001/include/periph_conf.h b/boards/dwm1001/include/periph_conf.h index 30ce5c8ede..83413e8c42 100644 --- a/boards/dwm1001/include/periph_conf.h +++ b/boards/dwm1001/include/periph_conf.h @@ -20,10 +20,10 @@ #ifndef PERIPH_CONF_H #define PERIPH_CONF_H -#include "periph_cpu.h" #include "cfg_clock_32_1.h" #include "cfg_rtt_default.h" #include "cfg_timer_default.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { @@ -33,9 +33,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 11) -#define UART_PIN_TX GPIO_PIN(0, 5) +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 11), + .tx_pin = GPIO_PIN(0, 5), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ /** diff --git a/boards/microbit/include/periph_conf.h b/boards/microbit/include/periph_conf.h index a5fac39602..90c5af1c6f 100644 --- a/boards/microbit/include/periph_conf.h +++ b/boards/microbit/include/periph_conf.h @@ -29,13 +29,24 @@ extern "C" { #endif /** - * @name UART configuration + * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -/* UART pin configuration */ -#define UART_PIN_RX 25 -#define UART_PIN_TX 24 +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 25), + .tx_pin = GPIO_PIN(0, 24), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/boards/nrf51dk/include/periph_conf.h b/boards/nrf51dk/include/periph_conf.h index 4399bd50b3..acaefceaa9 100644 --- a/boards/nrf51dk/include/periph_conf.h +++ b/boards/nrf51dk/include/periph_conf.h @@ -29,15 +29,24 @@ extern "C" { #endif /** - * @name UART configuration + * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -/* UART pin configuration */ -#define UART_PIN_RX 11 -#define UART_PIN_TX 9 -#define UART_PIN_RTS 8 -#define UART_PIN_CTS 10 +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 11), + .tx_pin = GPIO_PIN(0, 9), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_PIN(0, 8), + .cts_pin = GPIO_PIN(0, 10), +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/boards/nrf51dongle/include/periph_conf.h b/boards/nrf51dongle/include/periph_conf.h index a3eee64eb9..e8568cc0ff 100644 --- a/boards/nrf51dongle/include/periph_conf.h +++ b/boards/nrf51dongle/include/periph_conf.h @@ -29,15 +29,24 @@ extern "C" { #endif /** - * @name UART configuration + * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -/* UART pin configuration */ -#define UART_PIN_RX 11 -#define UART_PIN_TX 9 -#define UART_PIN_RTS 8 -#define UART_PIN_CTS 10 +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 11), + .tx_pin = GPIO_PIN(0, 9), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_PIN(0, 8), + .cts_pin = GPIO_PIN(0, 10), +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ #ifdef __cplusplus diff --git a/boards/nrf52832-mdk/include/periph_conf.h b/boards/nrf52832-mdk/include/periph_conf.h index f98988b054..4d95927b1d 100644 --- a/boards/nrf52832-mdk/include/periph_conf.h +++ b/boards/nrf52832-mdk/include/periph_conf.h @@ -35,9 +35,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 19) -#define UART_PIN_TX GPIO_PIN(0, 20) +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 19), + .tx_pin = GPIO_PIN(0, 20), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ #ifdef __cplusplus diff --git a/boards/nrf52dk/include/periph_conf.h b/boards/nrf52dk/include/periph_conf.h index e0d7991259..4bfe6e9ff2 100644 --- a/boards/nrf52dk/include/periph_conf.h +++ b/boards/nrf52dk/include/periph_conf.h @@ -48,9 +48,21 @@ static const spi_conf_t spi_config[] = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 8) -#define UART_PIN_TX GPIO_PIN(0, 6) +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 8), + .tx_pin = GPIO_PIN(0, 6), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ #ifdef __cplusplus diff --git a/boards/nrf6310/include/periph_conf.h b/boards/nrf6310/include/periph_conf.h index d87050ddee..bbe0d27efe 100644 --- a/boards/nrf6310/include/periph_conf.h +++ b/boards/nrf6310/include/periph_conf.h @@ -35,14 +35,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_IRQ_PRIO 1 +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 16), + .tx_pin = GPIO_PIN(0, 17), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_PIN(0, 19), + .cts_pin = GPIO_PIN(0, 18), +#endif + .irqn = UART0_IRQn, + }, +}; -/* UART pin configuration */ -#define UART_PIN_RX 16 -#define UART_PIN_TX 17 -#define UART_PIN_RTS 19 -#define UART_PIN_CTS 18 +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/boards/ruuvitag/include/periph_conf.h b/boards/ruuvitag/include/periph_conf.h index ee7390f9ff..9ec026a58d 100644 --- a/boards/ruuvitag/include/periph_conf.h +++ b/boards/ruuvitag/include/periph_conf.h @@ -50,9 +50,21 @@ static const spi_conf_t spi_config[] = { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 4) -#define UART_PIN_TX GPIO_PIN(0, 5) +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 4), + .tx_pin = GPIO_PIN(0, 5), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ #ifdef __cplusplus diff --git a/boards/thingy52/include/periph_conf.h b/boards/thingy52/include/periph_conf.h index 6e53f6f6c7..559afac802 100644 --- a/boards/thingy52/include/periph_conf.h +++ b/boards/thingy52/include/periph_conf.h @@ -33,9 +33,21 @@ extern "C" { * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -#define UART_PIN_RX GPIO_PIN(0, 2) -#define UART_PIN_TX GPIO_PIN(0, 3) +static const uart_conf_t uart_config[] = { + { + .dev = NRF_UARTE0, + .rx_pin = GPIO_PIN(0, 2), + .tx_pin = GPIO_PIN(0, 3), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UARTE0_UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR (isr_uart0) /** @} */ /** diff --git a/boards/yunjia-nrf51822/include/periph_conf.h b/boards/yunjia-nrf51822/include/periph_conf.h index 119c3bb54b..4da79573e8 100644 --- a/boards/yunjia-nrf51822/include/periph_conf.h +++ b/boards/yunjia-nrf51822/include/periph_conf.h @@ -29,13 +29,24 @@ extern "C" { #endif /** - * @name UART configuration + * @name UART configuration * @{ */ -#define UART_NUMOF (1U) -/* UART pin configuration */ -#define UART_PIN_RX 1 -#define UART_PIN_TX 2 +static const uart_conf_t uart_config[] = { + { /* Mapped to USB virtual COM port */ + .dev = NRF_UART0, + .rx_pin = GPIO_PIN(0, 1), + .tx_pin = GPIO_PIN(0, 2), +#ifdef MODULE_PERIPH_UART_HW_FC + .rts_pin = GPIO_UNDEF, + .cts_pin = GPIO_UNDEF, +#endif + .irqn = UART0_IRQn, + }, +}; + +#define UART_NUMOF ARRAY_SIZE(uart_config) +#define UART_0_ISR isr_uart0 /** @} */ /** diff --git a/cpu/nrf51/include/periph_cpu.h b/cpu/nrf51/include/periph_cpu.h index 371bf353d3..f09ba7dbef 100644 --- a/cpu/nrf51/include/periph_cpu.h +++ b/cpu/nrf51/include/periph_cpu.h @@ -34,7 +34,6 @@ extern "C" { * @brief Redefine some peripheral names to unify them between nRF51 and 52 * @{ */ -#define UART_IRQN (UART0_IRQn) #define SPI_SCKSEL (dev(bus)->PSELSCK) #define SPI_MOSISEL (dev(bus)->PSELMOSI) #define SPI_MISOSEL (dev(bus)->PSELMISO) diff --git a/cpu/nrf52/Kconfig b/cpu/nrf52/Kconfig index f76ad03b7c..af87fb19ca 100644 --- a/cpu/nrf52/Kconfig +++ b/cpu/nrf52/Kconfig @@ -19,19 +19,18 @@ config CPU_FAM_NRF52 select HAS_CPU_NRF52 select HAS_PERIPH_I2C_RECONFIGURE select HAS_PERIPH_SPI_GPIO_MODE + select HAS_PERIPH_UART_NONBLOCKING ## CPU Models config CPU_MODEL_NRF52805XXAA bool select CPU_CORE_CORTEX_M4 select CPU_FAM_NRF52 - select HAS_PERIPH_UART_NONBLOCKING config CPU_MODEL_NRF52810XXAA bool select CPU_CORE_CORTEX_M4 select CPU_FAM_NRF52 - select HAS_PERIPH_UART_NONBLOCKING config CPU_MODEL_NRF52811XXAA bool @@ -39,7 +38,6 @@ config CPU_MODEL_NRF52811XXAA select CPU_FAM_NRF52 select HAS_BLE_PHY_CODED select HAS_RADIO_NRF802154 - select HAS_PERIPH_UART_NONBLOCKING config CPU_MODEL_NRF52820XXAA bool @@ -47,7 +45,6 @@ config CPU_MODEL_NRF52820XXAA select CPU_FAM_NRF52 select HAS_BLE_PHY_CODED select HAS_RADIO_NRF802154 - select HAS_PERIPH_UART_NONBLOCKING config CPU_MODEL_NRF52832XXAA bool @@ -60,7 +57,6 @@ config CPU_MODEL_NRF52833XXAA select CPU_FAM_NRF52 select HAS_BLE_PHY_CODED select HAS_RADIO_NRF802154 - select HAS_PERIPH_UART_NONBLOCKING config CPU_MODEL_NRF52840XXAA bool @@ -68,7 +64,6 @@ config CPU_MODEL_NRF52840XXAA select CPU_FAM_NRF52 select HAS_BLE_PHY_CODED select HAS_RADIO_NRF802154 - select HAS_PERIPH_UART_NONBLOCKING select HAS_PERIPH_HASH_SHA_1 select HAS_PERIPH_HASH_SHA_224 select HAS_PERIPH_HASH_SHA_256 diff --git a/cpu/nrf52/Makefile.features b/cpu/nrf52/Makefile.features index a56403645e..06b9573103 100644 --- a/cpu/nrf52/Makefile.features +++ b/cpu/nrf52/Makefile.features @@ -23,9 +23,9 @@ ifneq (,$(filter nrf52840xxaa,$(CPU_MODEL))) FEATURES_PROVIDED += periph_ecc_ed25519 endif -ifeq (,$(filter nrf52832%,$(CPU_MODEL))) - FEATURES_PROVIDED += periph_uart_nonblocking -endif +# All nRF52 CPUs use UARTE (UART + EasyDMA) for UART, so that can be used +# in non-blocking mode +FEATURES_PROVIDED += periph_uart_nonblocking # The ADC does not depend on any board configuration, so always available FEATURES_PROVIDED += periph_adc diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index a41a36248b..40912952c6 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -316,13 +316,16 @@ typedef struct { */ uint8_t gpio_int_get_exti(gpio_t pin); -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) /** * @brief Structure for UART configuration data */ typedef struct { +#ifdef UARTE_PRESENT NRF_UARTE_Type *dev; /**< UART with EasyDMA device base * register address */ +#else + NRF_UART_Type *dev; /**< UART device base register address */ +#endif gpio_t rx_pin; /**< RX pin */ gpio_t tx_pin; /**< TX pin */ #ifdef MODULE_PERIPH_UART_HW_FC @@ -339,8 +342,6 @@ typedef struct { #define UART_TXBUF_SIZE (64) #endif -#endif /* ndef CPU_MODEL_NRF52832XXAA && ndef CPU_FAM_NRF51 */ - /** * @brief USBDEV buffers must be word aligned because of DMA restrictions */ @@ -407,9 +408,6 @@ typedef struct { #define SPI_SCKSEL (dev(bus)->PSEL.SCK) /**< Macro for SPI clk */ #define SPI_MOSISEL (dev(bus)->PSEL.MOSI) /**< Macro for SPI mosi */ #define SPI_MISOSEL (dev(bus)->PSEL.MISO) /**< Macro for SPI miso */ -#ifdef CPU_MODEL_NRF52832XXAA -#define UART_IRQN (UARTE0_UART0_IRQn) -#endif /** * @brief SPI configuration values diff --git a/cpu/nrf5x_common/periph/uart.c b/cpu/nrf5x_common/periph/uart.c index 80d7b29ee1..3e30f1622a 100644 --- a/cpu/nrf5x_common/periph/uart.c +++ b/cpu/nrf5x_common/periph/uart.c @@ -28,28 +28,32 @@ * @} */ -#include #include #include +#include "compiler_hints.h" #include "cpu.h" -#include "periph/uart.h" #include "periph/gpio.h" +#include "periph/uart.h" -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) -#define UART_INVALID (uart >= UART_NUMOF) -#define REG_BAUDRATE dev(uart)->BAUDRATE -#define REG_CONFIG dev(uart)->CONFIG -#define PSEL_RXD dev(uart)->PSEL.RXD -#define PSEL_TXD dev(uart)->PSEL.TXD -#define UART_IRQN uart_config[uart].irqn -#define UART_PIN_RX uart_config[uart].rx_pin -#define UART_PIN_TX uart_config[uart].tx_pin -#ifdef MODULE_PERIPH_UART_HW_FC -#define UART_PIN_RTS uart_config[uart].rts_pin -#define UART_PIN_CTS uart_config[uart].cts_pin +#ifdef UARTE_PRESENT +# define PSEL_RXD PSEL.RXD +# define PSEL_TXD PSEL.TXD +# define PSEL_RTS PSEL.RTS +# define PSEL_CTS PSEL.CTS +# define ENABLE_ON UARTE_ENABLE_ENABLE_Enabled +# define ENABLE_OFF UARTE_ENABLE_ENABLE_Disabled +# define UART_TYPE NRF_UARTE_Type +#else +# define PSEL_RXD PSELRXD +# define PSEL_TXD PSELTXD +# define PSEL_RTS PSELRTS +# define PSEL_CTS PSELCTS +# define ENABLE_ON UART_ENABLE_ENABLE_Enabled +# define ENABLE_OFF UART_ENABLE_ENABLE_Disabled +# define UART_TYPE NRF_UART_Type #endif -#define ISR_CTX isr_ctx[uart] + #define RAM_MASK (0x20000000) /** @@ -63,7 +67,9 @@ * @brief Allocate memory for the interrupt context */ static uart_isr_ctx_t isr_ctx[UART_NUMOF]; +#ifdef UARTE_PRESENT static uint8_t rx_buf[UART_NUMOF]; +#endif #ifdef MODULE_PERIPH_UART_NONBLOCKING @@ -81,149 +87,126 @@ static uint8_t uart_tx_rb_buf[UART_NUMOF][UART_TXBUF_SIZE]; */ void uart_isr_handler(void *arg); -static inline NRF_UARTE_Type *dev(uart_t uart) -{ - return uart_config[uart].dev; -} - -#else /* nrf51 and nrf52832 etc */ - -#define UART_INVALID (uart != 0) -#define REG_BAUDRATE NRF_UART0->BAUDRATE -#define REG_CONFIG NRF_UART0->CONFIG -#define PSEL_RXD NRF_UART0->PSELRXD -#define PSEL_TXD NRF_UART0->PSELTXD -#define UART_0_ISR isr_uart0 -#define ISR_CTX isr_ctx - -/** - * @brief Allocate memory for the interrupt context - */ -static uart_isr_ctx_t isr_ctx; - -#endif /* !CPU_MODEL_NRF52832XXAA && !CPU_FAM_NRF51 */ +/* use an enum to count the number of UART ISR macro names defined by the + * board */ +enum { +#ifdef UART_0_ISR + UART_0_ISR_NUM, +#endif +#ifdef UART_1_ISR + UART_1_ISR_NUM, +#endif + UART_ISR_NUMOF, +}; int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) { - if (UART_INVALID) { +/* ensure the ISR names have been defined as needed */ +#if !defined(CPU_NRF53) && !defined(CPU_NRF9160) + static_assert(UART_NUMOF == UART_ISR_NUMOF, "Define(s) of UART ISR name(s) missing"); +#endif + if ((unsigned)uart >= UART_NUMOF) { return UART_NODEV; } - /* remember callback addresses and argument */ - ISR_CTX.rx_cb = rx_cb; - ISR_CTX.arg = arg; + UART_TYPE *dev = uart_config[uart].dev; -#ifdef CPU_FAM_NRF51 - /* power on the UART device */ - NRF_UART0->POWER = 1; + /* remember callback addresses and argument */ + isr_ctx[uart].rx_cb = rx_cb; + isr_ctx[uart].arg = arg; + +#ifndef UARTE_PRESENT + /* only the legacy non-EasyDMA UART needs to be powered on explicitly */ + dev->POWER = 1; #endif /* reset configuration registers */ - REG_CONFIG = 0; + dev->CONFIG = 0; /* configure RX pin */ if (rx_cb) { - gpio_init(UART_PIN_RX, GPIO_IN); - PSEL_RXD = UART_PIN_RX; + gpio_init(uart_config[uart].rx_pin, GPIO_IN); + dev->PSEL_RXD = uart_config[uart].rx_pin; } /* configure TX pin */ - gpio_init(UART_PIN_TX, GPIO_OUT); - PSEL_TXD = UART_PIN_TX; + gpio_init(uart_config[uart].tx_pin, GPIO_OUT); + dev->PSEL_TXD = uart_config[uart].tx_pin; -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) /* enable HW-flow control if defined */ #ifdef MODULE_PERIPH_UART_HW_FC - /* set pin mode for RTS and CTS pins */ - if (UART_PIN_RTS != GPIO_UNDEF && UART_PIN_CTS != GPIO_UNDEF) { - gpio_init(UART_PIN_RTS, GPIO_OUT); - gpio_init(UART_PIN_CTS, GPIO_IN); - /* configure RTS and CTS pins to use */ - dev(uart)->PSEL.RTS = UART_PIN_RTS; - dev(uart)->PSEL.CTS = UART_PIN_CTS; - REG_CONFIG |= UART_CONFIG_HWFC_Msk; /* enable HW flow control */ - } -#else - dev(uart)->PSEL.RTS = 0xffffffff; /* pin disconnected */ - dev(uart)->PSEL.CTS = 0xffffffff; /* pin disconnected */ -#endif -#else -#ifdef MODULE_PERIPH_UART_HW_FC /* set pin mode for RTS and CTS pins */ - if (UART_PIN_RTS != GPIO_UNDEF && UART_PIN_CTS != GPIO_UNDEF) { - gpio_init(UART_PIN_RTS, GPIO_OUT); - gpio_init(UART_PIN_CTS, GPIO_IN); + if (uart_config[uart].rts_pin != GPIO_UNDEF && uart_config[uart].cts_pin != GPIO_UNDEF) { + gpio_init(uart_config[uart].rts_pin, GPIO_OUT); + gpio_init(uart_config[uart].cts_pin, GPIO_IN); /* configure RTS and CTS pins to use */ - NRF_UART0->PSELRTS = UART_PIN_RTS; - NRF_UART0->PSELCTS = UART_PIN_CTS; - REG_CONFIG |= UART_CONFIG_HWFC_Msk; /* enable HW flow control */ + dev->PSEL_RTS = uart_config[uart].rts_pin; + dev->PSEL_CTS = uart_config[uart].cts_pin; + dev->CONFIG |= UART_CONFIG_HWFC_Msk; /* enable HW flow control */ } -#else - NRF_UART0->PSELRTS = 0xffffffff; /* pin disconnected */ - NRF_UART0->PSELCTS = 0xffffffff; /* pin disconnected */ -#endif /* MODULE_PERIPH_UART_HW_FC */ + else #endif + { + dev->PSEL_RTS = 0xffffffff; /* pin disconnected */ + dev->PSEL_CTS = 0xffffffff; /* pin disconnected */ + } /* select baudrate */ switch (baudrate) { - case 1200: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; - break; - case 2400: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud2400; - break; - case 4800: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud4800; - break; - case 9600: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600; - break; - case 14400: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud14400; - break; - case 19200: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud19200; - break; - case 28800: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud28800; - break; - case 38400: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud38400; - break; - case 57600: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud57600; - break; - case 76800: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud76800; - break; - case 115200: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud115200; - break; - case 230400: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud230400; - break; - case 250000: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud250000; - break; - case 460800: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud460800; - break; - case 921600: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud921600; - break; - case 1000000: - REG_BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M; - break; - default: - return UART_NOBAUD; + case 1200: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; + break; + case 2400: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud2400; + break; + case 4800: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud4800; + break; + case 9600: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600; + break; + case 14400: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud14400; + break; + case 19200: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud19200; + break; + case 28800: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud28800; + break; + case 38400: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud38400; + break; + case 57600: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud57600; + break; + case 76800: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud76800; + break; + case 115200: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud115200; + break; + case 230400: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud230400; + break; + case 250000: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud250000; + break; + case 460800: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud460800; + break; + case 921600: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud921600; + break; + case 1000000: + dev->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1M; + break; + default: + return UART_NOBAUD; } /* enable the UART device */ -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) - dev(uart)->ENABLE = UARTE_ENABLE_ENABLE_Enabled; -#else - NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled; -#endif + dev->ENABLE = ENABLE_ON; #ifdef MODULE_PERIPH_UART_NONBLOCKING /* set up the TX buffer */ @@ -231,60 +214,60 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) #endif if (rx_cb) { -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) - dev(uart)->RXD.MAXCNT = 1; - dev(uart)->RXD.PTR = (uint32_t)&rx_buf[uart]; - dev(uart)->INTENSET = UARTE_INTENSET_ENDRX_Msk; - dev(uart)->SHORTS |= UARTE_SHORTS_ENDRX_STARTRX_Msk; - dev(uart)->TASKS_STARTRX = 1; +#ifdef UARTE_PRESENT + dev->RXD.MAXCNT = 1; + dev->RXD.PTR = (uint32_t)&rx_buf[uart]; + dev->INTENSET = UARTE_INTENSET_ENDRX_Msk; + dev->SHORTS |= UARTE_SHORTS_ENDRX_STARTRX_Msk; + dev->TASKS_STARTRX = 1; #else - NRF_UART0->INTENSET = UART_INTENSET_RXDRDY_Msk; - NRF_UART0->TASKS_STARTRX = 1; + dev->INTENSET = UART_INTENSET_RXDRDY_Msk; + dev->TASKS_STARTRX = 1; #endif } if (rx_cb || IS_USED(MODULE_PERIPH_UART_NONBLOCKING)) { #if defined(CPU_NRF53) || defined(CPU_NRF9160) - shared_irq_register_uart(dev(uart), uart_isr_handler, (void *)(uintptr_t)uart); + shared_irq_register_uart(dev, uart_isr_handler, (void *)(uintptr_t)uart); #else - NVIC_EnableIRQ(UART_IRQN); + NVIC_EnableIRQ(uart_config[uart].irqn); #endif } return UART_OK; } -/* nrf52840 || nrf52811 (using EasyDMA) */ -#if !defined(CPU_MODEL_NRF52832XXAA) && !defined(CPU_FAM_NRF51) +/* UART with EasyDMA */ +#ifdef UARTE_PRESENT static void _write_buf(uart_t uart, const uint8_t *data, size_t len) { - dev(uart)->EVENTS_ENDTX = 0; + uart_config[uart].dev->EVENTS_ENDTX = 0; if (IS_USED(MODULE_PERIPH_UART_NONBLOCKING)) { - dev(uart)->INTENSET = UARTE_INTENSET_ENDTX_Msk; + uart_config[uart].dev->INTENSET = UARTE_INTENSET_ENDTX_Msk; } /* set data to transfer to DMA TX pointer */ - dev(uart)->TXD.PTR = (uint32_t)data; - dev(uart)->TXD.MAXCNT = len; + uart_config[uart].dev->TXD.PTR = (uint32_t)data; + uart_config[uart].dev->TXD.MAXCNT = len; /* start transmission */ - dev(uart)->TASKS_STARTTX = 1; + uart_config[uart].dev->TASKS_STARTTX = 1; /* wait for the end of transmission */ if (!IS_USED(MODULE_PERIPH_UART_NONBLOCKING)) { - while (dev(uart)->EVENTS_ENDTX == 0) {} - dev(uart)->TASKS_STOPTX = 1; + while (uart_config[uart].dev->EVENTS_ENDTX == 0) {} + uart_config[uart].dev->TASKS_STOPTX = 1; } } void uart_write(uart_t uart, const uint8_t *data, size_t len) { - assert(uart < UART_NUMOF); + assume((unsigned)uart < UART_NUMOF); #ifdef MODULE_PERIPH_UART_NONBLOCKING for (size_t i = 0; i < len; i++) { /* in IRQ or interrupts disabled */ if (irq_is_in() || __get_PRIMASK()) { if (tsrb_full(&uart_tx_rb[uart])) { /* wait for end of ongoing transmission */ - if (dev(uart)->EVENTS_TXSTARTED) { - while (dev(uart)->EVENTS_ENDTX == 0) {} - dev(uart)->EVENTS_TXSTARTED = 0; + if (uart_config[uart].dev->EVENTS_TXSTARTED) { + while (uart_config[uart].dev->EVENTS_ENDTX == 0) {} + uart_config[uart].dev->EVENTS_TXSTARTED = 0; } /* free one spot in buffer */ tx_buf[uart] = tsrb_get_one(&uart_tx_rb[uart]); @@ -295,7 +278,7 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) else { /* if no transmission is ongoing and ring buffer is full free up a spot in the buffer by sending one byte */ - if (!dev(uart)->EVENTS_TXSTARTED && tsrb_full(&uart_tx_rb[uart])) { + if (!uart_config[uart].dev->EVENTS_TXSTARTED && tsrb_full(&uart_tx_rb[uart])) { tx_buf[uart] = tsrb_get_one(&uart_tx_rb[uart]); _write_buf(uart, &tx_buf[uart], 1); } @@ -304,7 +287,7 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) } /* if no transmission is ongoing bootstrap the transmission process by setting a single byte to be written */ - if (!dev(uart)->EVENTS_TXSTARTED) { + if (!uart_config[uart].dev->EVENTS_TXSTARTED) { if (!tsrb_empty(&uart_tx_rb[uart])) { tx_buf[uart] = tsrb_get_one(&uart_tx_rb[uart]); _write_buf(uart, &tx_buf[uart], 1); @@ -334,25 +317,33 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) void uart_poweron(uart_t uart) { - assert(uart < UART_NUMOF); + assume((unsigned)uart < UART_NUMOF); if (isr_ctx[uart].rx_cb) { - dev(uart)->TASKS_STARTRX = 1; + uart_config[uart].dev->TASKS_STARTRX = 1; } } void uart_poweroff(uart_t uart) { - assert(uart < UART_NUMOF); + assume((unsigned)uart < UART_NUMOF); - dev(uart)->TASKS_STOPRX = 1; + uart_config[uart].dev->TASKS_STOPRX = 1; } int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, uart_stop_bits_t stop_bits) { - - if (stop_bits != UART_STOP_BITS_1 && stop_bits != UART_STOP_BITS_2) { + assume((unsigned)uart < UART_NUMOF); + /* Not all nRF52 MCUs support 2 stop bits, but the vendor header files + * reflect the feature set. */ + switch (stop_bits) { + case UART_STOP_BITS_1: +#ifdef UARTE_CONFIG_STOP_Msk + case UART_STOP_BITS_2: +#endif + break; + default: return UART_NOMODE; } @@ -364,43 +355,43 @@ int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, return UART_NOMODE; } + /* Do not modify hardware flow control */ + uint32_t conf = uart_config[uart].dev->CONFIG & UARTE_CONFIG_HWFC_Msk; + +#ifdef UARTE_CONFIG_STOP_Msk if (stop_bits == UART_STOP_BITS_2) { - dev(uart)->CONFIG |= UARTE_CONFIG_STOP_Msk; - } - else { - dev(uart)->CONFIG &= ~UARTE_CONFIG_STOP_Msk; + conf |= UARTE_CONFIG_STOP_Msk; } +#endif if (parity == UART_PARITY_EVEN) { - dev(uart)->CONFIG |= UARTE_CONFIG_PARITY_Msk; - } - else { - dev(uart)->CONFIG &= ~UARTE_CONFIG_PARITY_Msk; + conf |= UARTE_CONFIG_PARITY_Msk; } + uart_config[uart].dev->CONFIG = conf; return UART_OK; } -static inline void irq_handler(uart_t uart) +static void irq_handler(uart_t uart) { - if (dev(uart)->EVENTS_ENDRX) { - dev(uart)->EVENTS_ENDRX = 0; + if (uart_config[uart].dev->EVENTS_ENDRX) { + uart_config[uart].dev->EVENTS_ENDRX = 0; /* make sure we actually received new data */ - if (dev(uart)->RXD.AMOUNT != 0) { + if (uart_config[uart].dev->RXD.AMOUNT != 0) { /* Process received byte */ isr_ctx[uart].rx_cb(isr_ctx[uart].arg, rx_buf[uart]); } } #ifdef MODULE_PERIPH_UART_NONBLOCKING - if (dev(uart)->EVENTS_ENDTX) { + if (uart_config[uart].dev->EVENTS_ENDTX) { /* reset flags and idsable ISR on EVENTS_ENDTX */ - dev(uart)->EVENTS_ENDTX = 0; - dev(uart)->EVENTS_TXSTARTED = 0; - dev(uart)->INTENCLR = UARTE_INTENSET_ENDTX_Msk; + uart_config[uart].dev->EVENTS_ENDTX = 0; + uart_config[uart].dev->EVENTS_TXSTARTED = 0; + uart_config[uart].dev->INTENCLR = UARTE_INTENSET_ENDTX_Msk; if (tsrb_empty(&uart_tx_rb[uart])) { - dev(uart)->TASKS_STOPTX = 1; + uart_config[uart].dev->TASKS_STOPTX = 1; } else { tx_buf[uart] = tsrb_get_one(&uart_tx_rb[uart]); _write_buf(uart, &tx_buf[uart], 1); @@ -411,13 +402,13 @@ static inline void irq_handler(uart_t uart) cortexm_isr_end(); } -#else /* nrf51 and nrf52832 etc */ +#else /* UART without EasyDMA*/ void uart_write(uart_t uart, const uint8_t *data, size_t len) { - (void)uart; + assume((unsigned)uart < UART_NUMOF); - NRF_UART0->TASKS_STARTTX = 1; + uart_config[uart].dev->TASKS_STARTTX = 1; for (size_t i = 0; i < len; i++) { /* This section of the function is not thread safe: @@ -430,36 +421,36 @@ void uart_write(uart_t uart, const uint8_t *data, size_t len) while loop. */ /* reset ready flag */ - NRF_UART0->EVENTS_TXDRDY = 0; + uart_config[uart].dev->EVENTS_TXDRDY = 0; /* write data into transmit register */ - NRF_UART0->TXD = data[i]; + uart_config[uart].dev->TXD = data[i]; /* wait for any transmission to be done */ - while (NRF_UART0->EVENTS_TXDRDY == 0) {} + while (uart_config[uart].dev->EVENTS_TXDRDY == 0) {} } - NRF_UART0->TASKS_STOPTX = 1; + uart_config[uart].dev->TASKS_STOPTX = 1; } void uart_poweron(uart_t uart) { - (void)uart; + assume((unsigned)uart < UART_NUMOF); - if (isr_ctx.rx_cb) { - NRF_UART0->TASKS_STARTRX = 1; + if (isr_ctx[uart].rx_cb) { + uart_config[uart].dev->TASKS_STARTRX = 1; } } void uart_poweroff(uart_t uart) { - (void)uart; + assume((unsigned)uart < UART_NUMOF); - NRF_UART0->TASKS_STOPRX = 1; + uart_config[uart].dev->TASKS_STOPRX = 1; } int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, uart_stop_bits_t stop_bits) { - (void)uart; + assume((unsigned)uart < UART_NUMOF); if (stop_bits != UART_STOP_BITS_1) { return UART_NOMODE; @@ -474,29 +465,27 @@ int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, } if (parity == UART_PARITY_EVEN) { - NRF_UART0->CONFIG |= UART_CONFIG_PARITY_Msk; + uart_config[uart].dev->CONFIG |= UART_CONFIG_PARITY_Msk; } else { - NRF_UART0->CONFIG &= ~UART_CONFIG_PARITY_Msk; + uart_config[uart].dev->CONFIG &= ~UART_CONFIG_PARITY_Msk; } return UART_OK; } -static inline void irq_handler(uart_t uart) +static void irq_handler(uart_t uart) { - (void)uart; - - if (NRF_UART0->EVENTS_RXDRDY == 1) { - NRF_UART0->EVENTS_RXDRDY = 0; - uint8_t byte = (uint8_t)(NRF_UART0->RXD & 0xff); - isr_ctx.rx_cb(isr_ctx.arg, byte); + if (uart_config[uart].dev->EVENTS_RXDRDY == 1) { + uart_config[uart].dev->EVENTS_RXDRDY = 0; + uint8_t byte = (uint8_t)(uart_config[uart].dev->RXD & 0xff); + isr_ctx[uart].rx_cb(isr_ctx[uart].arg, byte); } cortexm_isr_end(); } -#endif /* !CPU_MODEL_NRF52832XXAA && !CPU_FAM_NRF51 */ +#endif #if defined(CPU_NRF53) || defined(CPU_NRF9160) void uart_isr_handler(void *arg) @@ -519,4 +508,5 @@ void UART_1_ISR(void) irq_handler(UART_DEV(1)); } #endif + #endif /* def CPU_NRF53 || CPU_NRF9160 */ From a28003f0fadf8f5afb85779b34f16a3b6bc9d7bc Mon Sep 17 00:00:00 2001 From: Marian Buschsieweke Date: Thu, 23 Nov 2023 09:10:55 +0100 Subject: [PATCH 84/85] cpu/nrf5x_common: add whitespace to please linter Fixes the "comma should be followed by whitespace" warning from `static-tests`. --- cpu/nrf5x_common/include/periph_cpu_common.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index 40912952c6..19cbc5a98a 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -66,9 +66,9 @@ extern "C" { * The port definition is used (and zeroed) to suppress compiler warnings */ #if GPIO_COUNT > 1 -#define GPIO_PIN(x,y) ((x << 5) | y) +#define GPIO_PIN(x, y) ((x << 5) | y) #else -#define GPIO_PIN(x,y) ((x & 0) | y) +#define GPIO_PIN(x, y) ((x & 0) | y) #endif /** From b36e8e9d9d6518841bf22c235f024e3fae0be470 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Mon, 27 Nov 2023 16:56:14 +0100 Subject: [PATCH 85/85] kconfigs/Kconfig.features: define periph_freqm --- kconfigs/Kconfig.features | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index ab84f587a1..f3cc04a68f 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -258,6 +258,11 @@ config HAS_PERIPH_FLASHPAGE_RWEE help Indicates that the Flashpage peripheral is of the Read While Write. +config HAS_PERIPH_FREQM + bool + help + Indicates that a Frequency Meter peripheral is present. + config HAS_PERIPH_GPIO bool help