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..65b4030df0 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: @@ -21,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 @@ -34,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 \ 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 diff --git a/.murdock b/.murdock index 321c1d150f..b625b96ddc 100755 --- a/.murdock +++ b/.murdock @@ -178,22 +178,18 @@ 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 -# 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 - previous_merge="$(git log --merges --max-count 1 --skip 1 --pretty=format:%H)" - export CI_BASE_COMMIT="${previous_merge}" -fi +# this function returns 0 when this build is building a merge queue branch. +is_merge_queue_build() { + startswith "gh-readonly-queue/" "${CI_BUILD_BRANCH}" +} # fullbuild logic # non-full-builds are those where can_fast_ci_run might reduce the build @@ -216,8 +212,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 +319,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. 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 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 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/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/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. */ 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 /** @} */ 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/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/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))) 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/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 */ +/** @} */ diff --git a/boards/nrf51dk/include/periph_conf.h b/boards/nrf51dk/include/periph_conf.h index 4399bd50b3..1a90e196bb 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 /** @} */ /** @@ -73,6 +82,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 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/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" { 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..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,6 +163,42 @@ static const adc_conf_t adc_config[] = { #define ADC_NUMOF ARRAY_SIZE(adc_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 } #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/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" { 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. 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 * @{ 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) /** @} */ /** 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/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/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 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/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 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 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 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 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/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); +} 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/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/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 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 new file mode 100644 index 0000000000..1dd3a89071 --- /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 + + 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); 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) 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"); 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/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, 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..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 /** @@ -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 */ 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/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 diff --git a/cpu/sam0_common/periph/adc.c b/cpu/sam0_common/periph/adc.c index 59c236a7e8..71764dba72 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,104 @@ 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 + *adc0 = false; + *adc1 = false; + 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; } 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); +} 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 diff --git a/cpu/samd5x/include/periph_cpu.h b/cpu/samd5x/include/periph_cpu.h index f89b6ee498..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 /** @} */ /** @@ -198,6 +204,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. 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/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 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); diff --git a/cpu/stm32/periph/spi.c b/cpu/stm32/periph/spi.c index 47e324eb95..b312a37c7a 100644 --- a/cpu/stm32/periph/spi.c +++ b/cpu/stm32/periph/spi.c @@ -28,8 +28,6 @@ #include -#include "bitarithm.h" -#include "cpu.h" #include "mutex.h" #include "periph/gpio.h" #include "periph/spi.h" @@ -65,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) { @@ -81,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) @@ -235,30 +224,30 @@ 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_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,11 +266,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; - /* Only modify CR2 if needed */ - if (cr2_extra_settings) { - dev(bus)->CR2 = (SPI_CR2_SETTINGS | cr2_extra_settings); - } + dev(bus)->CR1 = cr1; + dev(bus)->CR2 = cr2; } void spi_release(spi_t bus) @@ -396,10 +382,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])) { @@ -414,9 +402,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); + } } } 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 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 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 . 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/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) 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/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 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 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/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/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 */ 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 diff --git a/drivers/pcf857x/pcf857x.c b/drivers/pcf857x/pcf857x.c index a37d21b394..32416a8656 100644 --- a/drivers/pcf857x/pcf857x.c +++ b/drivers/pcf857x/pcf857x.c @@ -14,15 +14,12 @@ * @{ */ -#include +#include #include +#include #include "pcf857x.h" -#include "irq.h" -#include "log.h" -#include "thread.h" - #if IS_USED(MODULE_PCF857X_IRQ) #include "event/thread.h" #endif @@ -108,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) @@ -125,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 */ @@ -135,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); @@ -154,7 +153,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,8 +163,8 @@ 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); - return -PCF857X_ERROR_INV_MODE; + case GPIO_IN_PD: DEBUG_DEV("gpio mode GPIO_IN_PD not supported", dev); + return -EINVAL; case GPIO_OUT: dev->modes &= ~(1 << pin); /* set mode bit to 0 */ dev->out &= ~(1 << pin); /* set output bit to 0 */ break; @@ -232,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; @@ -265,7 +264,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 +285,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 +318,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); } @@ -432,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) { @@ -473,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; 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 diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index ab84f587a1..8063c3b68a 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 @@ -258,6 +263,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 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 0000000000..d9fa59f174 Binary files /dev/null and b/pkg/esp32_sdk/patches/0032-hal-gdma-include-stddef.h-for-NULL.patch differ 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))) 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; 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 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/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" 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/include/random.h b/sys/include/random.h index 49cc59aeaf..56baa9f7a6 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,16 @@ * - 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. + * + * 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 + * @brief Common interface to the software PRNG */ #ifndef RANDOM_H @@ -54,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. 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/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); } diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 6b4641da12..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_USEC); - 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_USEC); - 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; } 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); } 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/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") 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 ea0a79974d..9b47ccae52 100644 --- a/tests/drivers/cc110x/Makefile.ci +++ b/tests/drivers/cc110x/Makefile.ci @@ -21,8 +21,10 @@ BOARD_INSUFFICIENT_MEMORY := \ microduino-corerf \ msb-430 \ msb-430h \ + nucleo-f030r8 \ nucleo-f031k6 \ nucleo-f042k6 \ + nucleo-f070rb \ nucleo-f072rb \ nucleo-f302r8 \ nucleo-f303k8 \ 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/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/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; +} 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; +} diff --git a/tests/periph/selftest_shield/Makefile b/tests/periph/selftest_shield/Makefile new file mode 100644 index 0000000000..06de04b29a --- /dev/null +++ b/tests/periph/selftest_shield/Makefile @@ -0,0 +1,42 @@ +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 + +USEMODULE += tiny_strerror + +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..a2dd818d91 --- /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-with-config` + +## 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..313908d00c --- /dev/null +++ b/tests/periph/selftest_shield/main.c @@ -0,0 +1,1102 @@ +/* + * 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 tests + * @{ + * + * @file + * @brief Test application for Peripheral Self-Testing using the + * Peripheral Selftest Shield + * + * @author Marian Buschsieweke + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "architecture.h" +#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 */ +#include "tiny_strerror.h" + +/* BEGIN: controls of the behavior of the testing app: */ +#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) +# 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 +# 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 + * 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) || 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) +# 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 MAYBE_UNUSED do_assert(bool failed, uint16_t line) +{ + if (failed) { + printf("CRITICAL "); + print_test_failed(line); + ARCHITECTURE_BREAKPOINT(1); + while (1) { + /* stop */ + } + } +} + +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) { + 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 + +/** + * @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) +{ + 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_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++) { + 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_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(); + /* 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_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); + } + 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_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(); + /* 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_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); + } + 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_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 */ + 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_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); + gpio_clear(ARDUINO_PIN_4); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_FALLING, gpio_cb, &cnt)); + + /* 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_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); + gpio_set(ARDUINO_PIN_4); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_RISING, gpio_cb, &cnt)); + + /* 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_NO_ERROR(gpio_init(ARDUINO_PIN_4, GPIO_OUT)); + gpio_set(ARDUINO_PIN_4); + ASSERT_NO_ERROR(gpio_init_int(ARDUINO_PIN_3, GPIO_IN, GPIO_BOTH, gpio_cb, &cnt)); + + /* 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_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); + 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_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; + 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 && DETAILED_OUTPUT) { + printf("%" 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_NO_ERROR(timer_init(TIMER, TIMER_FREQ_UART_TEST, NULL, NULL)); + 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 = 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_SPI_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); + + uint16_t byte_time; + 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); + 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); + + /* 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_NO_ERROR(timer_init(TIMER, TIMER_FREQ_SPI_TEST, NULL, NULL)); + timer_start(TIMER); + } + + bool failed = false; + 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++) { + ASSERT_NO_ERROR(pcf857x_gpio_init(&egpios, spi_clk_check_pins[i], GPIO_IN)); + } + } + + /* 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]; + 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"); + 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_NO_ERROR(adc_init(adc_line)); + + 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_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) +{ + 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 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)); + 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_NO_ERROR(pcf857x_init(&egpios, ¶ms)); + } + + 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..005393cb48 --- /dev/null +++ b/tests/periph/selftest_shield/tests-with-config/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)) 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 \ 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");