aboutsummaryrefslogtreecommitdiffstats
path: root/os
diff options
context:
space:
mode:
authorandru <7698720+AndruPol@users.noreply.github.com>2019-01-08 11:22:01 +0300
committerandru <7698720+AndruPol@users.noreply.github.com>2019-01-08 11:22:01 +0300
commit2168085ac76c280880a0c2262a7e63fd0ce7c952 (patch)
tree84cc3e094dbad1063034549f74ad7c617e5fe800 /os
parent5af099c3665e49ce9323fa79c56a51bf67b9ebab (diff)
downloadChibiOS-Contrib-2168085ac76c280880a0c2262a7e63fd0ce7c952.tar.gz
ChibiOS-Contrib-2168085ac76c280880a0c2262a7e63fd0ce7c952.tar.bz2
ChibiOS-Contrib-2168085ac76c280880a0c2262a7e63fd0ce7c952.zip
added NRF52 pwm, icu, i2c, radio esb drivers
Diffstat (limited to 'os')
-rw-r--r--os/hal/boards/NRF52-E73-2G4M04S/board.c81
-rw-r--r--os/hal/boards/NRF52-E73-2G4M04S/board.h164
-rw-r--r--os/hal/boards/NRF52-E73-2G4M04S/board.mk8
-rw-r--r--os/hal/ports/NRF5/LLD/PWMv2/driver.mk9
-rw-r--r--os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c402
-rw-r--r--os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.h243
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/driver.mk4
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c768
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.h424
-rw-r--r--os/hal/ports/NRF5/LLD/TWIMv1/driver.mk9
-rw-r--r--os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c420
-rw-r--r--os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.h210
-rw-r--r--os/hal/ports/NRF5/NRF52832/hal_lld.c26
-rw-r--r--os/hal/ports/NRF5/NRF52832/hal_lld.h32
-rw-r--r--os/hal/ports/NRF5/NRF52832/nrf_delay.h335
-rw-r--r--os/hal/ports/NRF5/NRF52832/platform.mk3
-rw-r--r--os/various/devices_lib/rf/nrf52_radio.c1111
-rw-r--r--os/various/devices_lib/rf/nrf52_radio.h256
18 files changed, 4396 insertions, 109 deletions
diff --git a/os/hal/boards/NRF52-E73-2G4M04S/board.c b/os/hal/boards/NRF52-E73-2G4M04S/board.c
new file mode 100644
index 0000000..adf002d
--- /dev/null
+++ b/os/hal/boards/NRF52-E73-2G4M04S/board.c
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2016 Stéphane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "hal.h"
+
+#if HAL_USE_PAL || defined(__DOXYGEN__)
+
+/**
+ * @brief PAL setup.
+ * @details Digital I/O ports static configuration as defined in @p board.h.
+ * This variable is used by the HAL when initializing the PAL driver.
+ */
+const PALConfig pal_default_config =
+{
+ .pads = {
+ PAL_MODE_UNCONNECTED, /* P0.0 : XTAL (32MHz) */
+ PAL_MODE_UNCONNECTED, /* P0.1 : XTAL (32MHz) */
+ PAL_MODE_UNCONNECTED, /* P0.2 */
+ PAL_MODE_UNCONNECTED, /* P0.3 */
+ PAL_MODE_UNCONNECTED, /* P0.4 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.5 : UART_RTS */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.6 : UART_TX */
+ PAL_MODE_INPUT_PULLUP, /* P0.7 : UART_CTS */
+ PAL_MODE_INPUT_PULLUP, /* P0.8 : UART_RX */
+ PAL_MODE_UNCONNECTED, /* P0.9 */
+ PAL_MODE_UNCONNECTED, /* P0.10 */
+ PAL_MODE_UNCONNECTED, /* P0.11 */
+ PAL_MODE_UNCONNECTED, /* P0.12 */
+ PAL_MODE_INPUT, /* P0.13: BTN1 */
+ PAL_MODE_INPUT, /* P0.14: BTN2 */
+ PAL_MODE_INPUT_PULLUP, /* P0.15: BTN3 */
+ PAL_MODE_INPUT_PULLUP, /* P0.16: BTN4 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.17: LED1 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.18: LED2 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.19: LED3 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.20: LED4 */
+ PAL_MODE_UNCONNECTED, /* P0.21 */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.22: SPI_SS */
+ PAL_MODE_INPUT_PULLUP, /* P0.23: SPI_MISO */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.24: SPI_MOSI */
+ PAL_MODE_OUTPUT_PUSHPULL, /* P0.25: SPI_SCK */
+ PAL_MODE_OUTPUT_OPENDRAIN, /* P0.26: SDA */
+ PAL_MODE_OUTPUT_OPENDRAIN, /* P0.27: SCL */
+ PAL_MODE_UNCONNECTED, /* P0.28 */
+ PAL_MODE_UNCONNECTED, /* P0.29 */
+ PAL_MODE_UNCONNECTED, /* P0.30 */
+ PAL_MODE_UNCONNECTED, /* P0.31 */
+ },
+};
+#endif
+
+/**
+ * @brief Early initialization code.
+ * @details This initialization is performed just after reset before BSS and
+ * DATA segments initialization.
+ */
+void __early_init(void)
+{
+}
+
+/**
+ * @brief Late initialization code.
+ * @note This initialization is performed after BSS and DATA segments
+ * initialization and before invoking the main() function.
+ */
+void boardInit(void)
+{
+}
diff --git a/os/hal/boards/NRF52-E73-2G4M04S/board.h b/os/hal/boards/NRF52-E73-2G4M04S/board.h
new file mode 100644
index 0000000..c78909f
--- /dev/null
+++ b/os/hal/boards/NRF52-E73-2G4M04S/board.h
@@ -0,0 +1,164 @@
+/*
+ Copyright (C) 2016 Stephane D'Alu
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#ifndef _BOARD_H_
+#define _BOARD_H_
+
+/* Board identifier. */
+#define BOARD_NRF52_EBYTE_E73
+#define BOARD_NAME "nRF52 EBYTE E73-2G4M04S"
+
+/* Board oscillators-related settings. */
+#define NRF5_XTAL_VALUE 32000000
+#define NRF5_HFCLK_SOURCE NRF5_HFCLK_HFXO
+#define NRF5_LFCLK_SOURCE NRF5_LFCLK_XTAL
+
+#define NRF5_HFCLK_HFINT 0
+#define NRF5_HFCLK_HFXO 1
+
+#define NRF5_LFCLK_RC 0
+#define NRF5_LFCLK_XTAL 1
+#define NRF5_LFCLK_SYNTH 2
+
+/*
+ * GPIO pins.
+ */
+/* Defined by board */
+#define BTN1 13U
+#define BTN2 14U
+#define BTN3 15U
+#define BTN4 16U
+#define LED1 17U
+#define LED2 18U
+#define LED3 19U
+#define LED4 20U
+#define UART_RTS 5U
+#define UART_TX 6U
+#define UART_CTS 7U
+#define UART_RX 8U
+#define NFC1 9U
+#define NFC2 10U
+#define I2C_SCL 27U
+#define I2C_SDA 26U
+
+/* Our definitions */
+#define SPI_SCK 25U
+#define SPI_MOSI 24U
+#define SPI_MISO 23U
+#define SPI_SS 22U
+
+/* Analog input */
+#define AIN0 2U
+#define AIN1 3U
+#define AIN2 4U
+#define AIN3 5U
+#define AIN4 28U
+#define AIN5 29U
+#define AIN6 30U
+#define AIN7 31U
+#define AREF0 AIN0
+#define AREF1 AIN1
+
+/*
+ * IO pins assignments.
+ */
+/* Defined by board */
+#define IOPORT1_BTN1 13U
+#define IOPORT1_BTN2 14U
+#define IOPORT1_BTN3 15U
+#define IOPORT1_BTN4 16U
+#define IOPORT1_LED1 17U
+#define IOPORT1_LED2 18U
+#define IOPORT1_LED3 19U
+#define IOPORT1_LED4 20U
+#define IOPORT1_UART_RTS 5U
+#define IOPORT1_UART_TX 6U
+#define IOPORT1_UART_CTS 7U
+#define IOPORT1_UART_RX 8U
+#define IOPORT1_NFC1 9U
+#define IOPORT1_NFC2 10U
+#define IOPORT1_I2C_SCL 27U
+#define IOPORT1_I2C_SDA 26U
+#define IOPORT1_RESET 21U
+
+/* Our definitions */
+#define IOPORT1_SPI_SCK 25U
+#define IOPORT1_SPI_MOSI 24U
+#define IOPORT1_SPI_MISO 23U
+#define IOPORT1_SPI_SS 22U
+
+/* Analog inpupt */
+#define IOPORT1_AIN0 2U
+#define IOPORT1_AIN1 3U
+#define IOPORT1_AIN2 4U
+#define IOPORT1_AIN3 5U
+#define IOPORT1_AIN4 28U
+#define IOPORT1_AIN5 29U
+#define IOPORT1_AIN6 30U
+#define IOPORT1_AIN7 31U
+#define IOPORT1_AREF0 IOPORT1_AIN0
+#define IOPORT1_AREF1 IOPORT1_AIN1
+
+/*
+ * IO lines assignments.
+ */
+/* Board defined */
+#define LINE_BTN1 PAL_LINE(IOPORT1, IOPORT1_BTN1)
+#define LINE_BTN2 PAL_LINE(IOPORT1, IOPORT1_BTN2)
+#define LINE_BTN3 PAL_LINE(IOPORT1, IOPORT1_BTN3)
+#define LINE_BTN4 PAL_LINE(IOPORT1, IOPORT1_BTN4)
+#define LINE_LED1 PAL_LINE(IOPORT1, IOPORT1_LED1)
+#define LINE_LED2 PAL_LINE(IOPORT1, IOPORT1_LED2)
+#define LINE_LED3 PAL_LINE(IOPORT1, IOPORT1_LED3)
+#define LINE_LED4 PAL_LINE(IOPORT1, IOPORT1_LED4)
+#define LINE_UART_RTS PAL_LINE(IOPORT1, IOPORT1_UART_RTS)
+#define LINE_UART_TX PAL_LINE(IOPORT1, IOPORT1_UART_TX)
+#define LINE_UART_CTS PAL_LINE(IOPORT1, IOPORT1_UART_CTS)
+#define LINE_UART_RX PAL_LINE(IOPORT1, IOPORT1_UART_RX)
+#define LINE_NFC1 PAL_LINE(IOPORT1, IOPORT1_NFC1)
+#define LINE_NFC2 PAL_LINE(IOPORT1, IOPORT1_NFC2)
+#define LINE_I2C_SCL PAL_LINE(IOPORT1, IOPORT1_I2C_SCL)
+#define LINE_I2C_SDA PAL_LINE(IOPORT1, IOPORT1_I2C_SDA)
+
+/* Our definitions */
+#define LINE_SPI_SCK PAL_LINE(IOPORT1, IOPORT1_SPI_SCK)
+#define LINE_SPI_MOSI PAL_LINE(IOPORT1, IOPORT1_SPI_MOSI)
+#define LINE_SPI_MISO PAL_LINE(IOPORT1, IOPORT1_SPI_MISO)
+#define LINE_SPI_SS PAL_LINE(IOPORT1, IOPORT1_SPI_SS)
+
+/* Analog line */
+#define LINE_AIN0 PAL_LINE(IOPORT1, IOPORT1_AIN0)
+#define LINE_AIN1 PAL_LINE(IOPORT1, IOPORT1_AIN1)
+#define LINE_AIN2 PAL_LINE(IOPORT1, IOPORT1_AIN2)
+#define LINE_AIN3 PAL_LINE(IOPORT1, IOPORT1_AIN3)
+#define LINE_AIN4 PAL_LINE(IOPORT1, IOPORT1_AIN4)
+#define LINE_AIN5 PAL_LINE(IOPORT1, IOPORT1_AIN5)
+#define LINE_AIN6 PAL_LINE(IOPORT1, IOPORT1_AIN6)
+#define LINE_AIN7 PAL_LINE(IOPORT1, IOPORT1_AIN7)
+#define LINE_AREF0 PAL_LINE(IOPORT1, IOPORT1_AREF0)
+#define LINE_AREF1 PAL_LINE(IOPORT1, IOPORT1_AREF1)
+
+#if !defined(_FROM_ASM_)
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void boardInit(void);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FROM_ASM_ */
+
+#endif /* _BOARD_H_ */
diff --git a/os/hal/boards/NRF52-E73-2G4M04S/board.mk b/os/hal/boards/NRF52-E73-2G4M04S/board.mk
new file mode 100644
index 0000000..8876668
--- /dev/null
+++ b/os/hal/boards/NRF52-E73-2G4M04S/board.mk
@@ -0,0 +1,8 @@
+# List of all the board related files.
+BOARDSRC = ${CHIBIOS_CONTRIB}/os/hal/boards/NRF52-E73-2G4M04S/board.c
+
+# Required include directories
+BOARDINC = ${CHIBIOS_CONTRIB}/os/hal/boards/NRF52-E73-2G4M04S
+
+ALLCSRC += $(BOARDSRC)
+ALLINC += $(BOARDINC)
diff --git a/os/hal/ports/NRF5/LLD/PWMv2/driver.mk b/os/hal/ports/NRF5/LLD/PWMv2/driver.mk
new file mode 100644
index 0000000..ce247df
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/PWMv2/driver.mk
@@ -0,0 +1,9 @@
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_PWM TRUE,$(HALCONF)),)
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c
+endif
+else
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c
+endif
+
+PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/PWMv2
diff --git a/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c b/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c
new file mode 100644
index 0000000..7162b1b
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.c
@@ -0,0 +1,402 @@
+/*
+ ChibiOS/HAL - Copyright (C) 2018 Andru
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file hal_pwm_lld.c
+ * @brief NRF52 PWM subsystem low level driver source.
+ *
+ * @addtogroup PWM
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_PWM || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+uint16_t pwm_seq[PWM_CHANNELS];
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief PWMD1 driver identifier.
+ * @note The driver PWMD1 enabled.
+ */
+#if NRF5_PWM_USE_PWM0 || defined(__DOXYGEN__)
+PWMDriver PWMD1;
+#endif
+
+/**
+ * @brief PWMD2 driver identifier.
+ * @note The driver PWMD2 enabled.
+ */
+#if NRF5_PWM_USE_PWM1 || defined(__DOXYGEN__)
+PWMDriver PWMD2;
+#endif
+
+/**
+ * @brief PWMD3 driver identifier.
+ * @note The driver PWMD3 enabled.
+ */
+#if NRF5_PWM_USE_PWM2 || defined(__DOXYGEN__)
+PWMDriver PWMD3;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static void pwm_lld_serve_interrupt(PWMDriver *pwmp) {
+ /* Deal with PWM period
+ */
+ if (pwmp->config->callback == NULL) {
+ return;
+ }
+
+ if ((pwmp->pwm->INTEN & PWM_INTEN_PWMPERIODEND_Msk) && (pwmp->pwm->EVENTS_PWMPERIODEND)) {
+ pwmp->config->callback(pwmp);
+ pwmp->pwm->EVENTS_PWMPERIODEND = 0;
+ (void)pwmp->pwm->EVENTS_PWMPERIODEND;
+ }
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if NRF5_PWM_USE_PWM0
+/**
+ * @brief PWM0 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(VectorB0) {
+ OSAL_IRQ_PROLOGUE();
+ pwm_lld_serve_interrupt(&PWMD1);
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_PWM_USE_PWM0 */
+
+#if NRF5_PWM_USE_PWM1
+/**
+ * @brief PWM1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(VectorC4) {
+ OSAL_IRQ_PROLOGUE();
+ pwm_lld_serve_interrupt(&PWMD2);
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_PWM_USE_PWM1 */
+
+#if NRF5_PWM_USE_PWM2
+/**
+ * @brief PWM2 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(VectorC8) {
+ OSAL_IRQ_PROLOGUE();
+ pwm_lld_serve_interrupt(&PWMD3);
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_PWM_USE_PWM2 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level PWM driver initialization.
+ *
+ * @notapi
+ */
+void pwm_lld_init(void) {
+
+#if NRF5_PWM_USE_PWM0
+ pwmObjectInit(&PWMD1);
+ PWMD1.channels = PWM_CHANNELS;
+ PWMD1.pwm = NRF_PWM0;
+#endif
+
+#if NRF5_PWM_USE_PWM1
+ pwmObjectInit(&PWMD2);
+ PWMD2.channels = PWM_CHANNELS;
+ PWMD2.pwm = NRF_PWM1;
+#endif
+
+#if NRF5_PWM_USE_PWM2
+ pwmObjectInit(&PWMD3);
+ PWMD3.channels = PWM_CHANNELS;
+ PWMD3.pwm = NRF_PWM2;
+#endif
+}
+
+/**
+ * @brief Configures and activates the PWM peripheral.
+ * @note Starting a driver that is already in the @p PWM_READY state
+ * disables all the active channels.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ *
+ * @notapi
+ */
+void pwm_lld_start(PWMDriver *pwmp) {
+ /* Prescaler value calculation: ftimer = 16MHz / 2^PRESCALER */
+ /* Prescaler value as a power of 2, must be 0..7 */
+ uint8_t i, psc_value;
+ for (psc_value = 0; psc_value < 8; psc_value++)
+ if (pwmp->config->frequency == (uint32_t)(16000000 >> psc_value))
+ break;
+
+ /* Prescaler value must be between 0..7, and a power of two. */
+ osalDbgAssert(psc_value <= 7, "invalid frequency");
+
+ /* Set PWM output lines */
+ for (i=0; i<PWM_CHANNELS; i++) {
+ const PWMChannelConfig *cfg_channel = &pwmp->config->channels[i];
+ uint32_t gpio_pin = PAL_PAD(cfg_channel->ioline);
+ if (cfg_channel->mode == PWM_OUTPUT_DISABLED) {
+ gpio_pin = PAL_NOLINE;
+ }
+
+ pwmp->pwm->PSEL.OUT[i] = gpio_pin << PWM_PSEL_OUT_PIN_Pos;
+ }
+
+ /* Enable PWM */
+ pwmp->pwm->ENABLE = PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos;
+
+ /* Set mode */
+ pwmp->pwm->MODE = PWM_MODE_UPDOWN_Up & PWM_MODE_UPDOWN_Msk;
+
+ /* Set prescaler */
+ pwmp->pwm->PRESCALER = psc_value & PWM_PRESCALER_PRESCALER_Msk;
+
+ /* Set period */
+ pwmp->pwm->COUNTERTOP = pwmp->period & PWM_COUNTERTOP_COUNTERTOP_Msk;
+
+ pwmp->pwm->LOOP = PWM_LOOP_CNT_Disabled & PWM_LOOP_CNT_Msk;
+ pwmp->pwm->DECODER = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) |
+ (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
+
+ pwmp->pwm->SEQ[0].PTR = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
+ pwmp->pwm->SEQ[0].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
+ pwmp->pwm->SEQ[0].REFRESH = 0;
+ pwmp->pwm->SEQ[0].ENDDELAY = 0;
+
+ pwmp->pwm->SEQ[1].PTR = ((uint32_t)(pwm_seq) << PWM_SEQ_PTR_PTR_Pos);
+ pwmp->pwm->SEQ[1].CNT = ((sizeof(pwm_seq) / sizeof(uint16_t)) << PWM_SEQ_CNT_CNT_Pos);
+ pwmp->pwm->SEQ[1].REFRESH = 0;
+ pwmp->pwm->SEQ[1].ENDDELAY = 0;
+
+ /* With clear shortcuts for period */
+ pwmp->pwm->SHORTS = 0;
+
+ /* Disable and reset interrupts */
+ pwmp->pwm->INTEN = 0;
+}
+
+/**
+ * @brief Deactivates the PWM peripheral.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ *
+ * @notapi
+ */
+void pwm_lld_stop(PWMDriver *pwmp) {
+#if NRF5_PWM_USE_PWM0
+ if (&PWMD1 == pwmp) {
+ nvicDisableVector(PWM0_IRQn);
+ }
+#endif
+
+#if NRF5_PWM_USE_PWM1
+ if (&PWMD2 == pwmp) {
+ nvicDisableVector(PWM1_IRQn);
+ }
+#endif
+
+#if NRF5_PWM_USE_PWM2
+ if (&PWMD3 == pwmp) {
+ nvicDisableVector(PWM2_IRQn);
+ }
+#endif
+
+ /* Stop PWM generation */
+ pwmp->pwm->TASKS_STOP = 1;
+
+ /* Disable PWM */
+ pwmp->pwm->ENABLE = (PWM_ENABLE_ENABLE_Disabled << PWM_ENABLE_ENABLE_Pos);
+}
+
+/**
+ * @brief Enables a PWM channel.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @post The channel is active using the specified configuration.
+ * @note The function has effect at the next cycle start.
+ * @note Channel notification is not enabled.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier (0...channels-1)
+ * @param[in] width PWM pulse width as clock pulses number
+ *
+ * @notapi
+ */
+void pwm_lld_enable_channel(PWMDriver *pwmp,
+ pwmchannel_t channel,
+ pwmcnt_t width) {
+ const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel];
+
+ /* Deal with corner case: 0% and 100% */
+ if ((width <= 0) || (width >= pwmp->period)) {
+ pwm_seq[channel] = pwmp->period & PWM_COUNTERTOP_COUNTERTOP_Msk;
+ if (cfg_channel->mode == PWM_OUTPUT_ACTIVE_LOW) pwm_seq[channel] |= 0x8000;
+ /* Really doing PWM */
+ } else {
+ pwm_seq[channel] = width & PWM_COUNTERTOP_COUNTERTOP_Msk;
+ if (cfg_channel->mode == PWM_OUTPUT_ACTIVE_HIGH) pwm_seq[channel] |= 0x8000;
+ }
+
+ pwmp->pwm->EVENTS_STOPPED = 0;
+ (void)pwmp->pwm->EVENTS_STOPPED;
+
+ pwmp->pwm->TASKS_SEQSTART[0] = 1;
+}
+
+/**
+ * @brief Disables a PWM channel and its notification.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @post The channel is disabled and its output line returned to the
+ * idle state.
+ * @note The function has effect at the next cycle start.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier (0...channels-1)
+ *
+ * @notapi
+ */
+void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) {
+ const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel];
+
+ pwm_seq[channel] = pwmp->period & PWM_COUNTERTOP_COUNTERTOP_Msk;
+ if (cfg_channel->mode == PWM_OUTPUT_ACTIVE_LOW) pwm_seq[channel] |= 0x8000;
+
+ pwmp->pwm->EVENTS_STOPPED = 0;
+ (void)pwmp->pwm->EVENTS_STOPPED;
+
+ pwmp->pwm->TASKS_SEQSTART[0] = 1;
+}
+
+/**
+ * @brief Enables the periodic activation edge notification.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ *
+ * @notapi
+ */
+void pwm_lld_enable_periodic_notification(PWMDriver *pwmp) {
+
+ /* Events clear */
+ pwmp->pwm->EVENTS_LOOPSDONE = 0;
+ pwmp->pwm->EVENTS_SEQEND[0] = 0;
+ pwmp->pwm->EVENTS_SEQEND[1] = 0;
+ pwmp->pwm->EVENTS_STOPPED = 0;
+#if CORTEX_MODEL >= 4
+ (void)pwmp->pwm->EVENTS_LOOPSDONE;
+ (void)pwmp->pwm->EVENTS_SEQEND[0];
+ (void)pwmp->pwm->EVENTS_SEQEND[1];
+ (void)pwmp->pwm->EVENTS_STOPPED;
+#endif
+
+ pwmp->pwm->INTENSET = PWM_INTENSET_PWMPERIODEND_Msk;
+
+ /* Enable interrupt */
+#if NRF5_PWM_USE_PWM0
+ if (&PWMD1 == pwmp) {
+ nvicEnableVector(PWM0_IRQn, NRF5_PWM_PWM0_PRIORITY);
+ }
+#endif
+
+#if NRF5_PWM_USE_PWM1
+ if (&PWMD2 == pwmp) {
+ nvicEnableVector(PWM1_IRQn, NRF5_PWM_PWM1_PRIORITY);
+ }
+#endif
+
+#if NRF5_PWM_USE_PWM2
+ if (&PWMD3 == pwmp) {
+ nvicEnableVector(PWM2_IRQn, NRF5_PWM_PWM2_PRIORITY);
+ }
+#endif
+}
+
+/**
+ * @brief Disables the periodic activation edge notification.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ *
+ * @notapi
+ */
+void pwm_lld_disable_periodic_notification(PWMDriver *pwmp) {
+ pwmp->pwm->INTENCLR = PWM_INTENCLR_PWMPERIODEND_Msk;
+}
+
+/**
+ * @brief Enables a channel de-activation edge notification.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @pre The channel must have been activated using @p pwmEnableChannel().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier (0...channels-1)
+ *
+ * @notapi
+ */
+void pwm_lld_enable_channel_notification(PWMDriver *pwmp,
+ pwmchannel_t channel) {
+}
+
+/**
+ * @brief Disables a channel de-activation edge notification.
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @pre The channel must have been activated using @p pwmEnableChannel().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] channel PWM channel identifier (0...channels-1)
+ *
+ * @notapi
+ */
+void pwm_lld_disable_channel_notification(PWMDriver *pwmp,
+ pwmchannel_t channel) {
+}
+
+#endif /* HAL_USE_PWM */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.h b/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.h
new file mode 100644
index 0000000..647b95e
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/PWMv2/hal_pwm_lld.h
@@ -0,0 +1,243 @@
+/*
+ ChibiOS/HAL - Copyright (C) 2018 Andru
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file hal_pwm_lld.h
+ * @brief NRF52 PWM subsystem low level driver header.
+ *
+ * @addtogroup PWM
+ * @{
+ */
+
+#ifndef HAL_PWM_LLD_H_
+#define HAL_PWM_LLD_H_
+
+#if HAL_USE_PWM || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of PWM channels per PWM driver.
+ */
+#define PWM_CHANNELS 4
+
+typedef enum {
+ PWM_FREQUENCY_16MHZ = 16000000, /** @brief 16MHz */
+ PWM_FREQUENCY_8MHZ = 8000000, /** @brief 8MHz */
+ PWM_FREQUENCY_4MHZ = 4000000, /** @brief 4MHz */
+ PWM_FREQUENCY_2MHZ = 2000000, /** @brief 2MHz */
+ PWM_FREQUENCY_1MHZ = 1000000, /** @brief 1MHz */
+ PWM_FREQUENCY_500KHZ = 500000, /** @brief 500kHz */
+ PWM_FREQUENCY_250KHZ = 250000, /** @brief 250kHz */
+ PWM_FREQUENCY_125KHZ = 125000, /** @brief 125kHz */
+} pwm_frequency_t;
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+
+/** @} */
+
+/*===========================================================================*/
+/* Configuration checks. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a PWM mode.
+ */
+typedef uint32_t pwmmode_t;
+
+/**
+ * @brief Type of a PWM channel.
+ */
+typedef uint8_t pwmchannel_t;
+
+/**
+ * @brief Type of a channels mask.
+ */
+typedef uint32_t pwmchnmsk_t;
+
+/**
+ * @brief Type of a PWM counter.
+ */
+typedef uint16_t pwmcnt_t;
+
+/**
+ * @brief Type of a PWM driver channel configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief Channel active logic level.
+ */
+ pwmmode_t mode;
+
+ /**
+ * @brief Channel callback pointer.
+ * @note This callback is invoked on the channel compare event. If set to
+ * @p NULL then the callback is disabled.
+ */
+ pwmcallback_t callback;
+ /* End of the mandatory fields.*/
+
+ /**
+ * @brief PAL line to toggle.
+ * @note Only used if mode is PWM_OUTPUT_HIGH or PWM_OUTPUT_LOW.
+ * @note When channel enabled it wont be possible to access this PAL line using the PAL
+ * driver.
+ */
+ ioline_t ioline;
+
+} PWMChannelConfig;
+
+/**
+ * @brief Type of a PWM driver configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief Timer clock in Hz.
+ * @note The low level can use assertions in order to catch invalid
+ * frequency specifications.
+ */
+ pwm_frequency_t frequency;
+ /**
+ * @brief PWM period in ticks.
+ * @note The low level can use assertions in order to catch invalid
+ * period specifications.
+ */
+ pwmcnt_t period;
+ /**
+ * @brief Periodic callback pointer.
+ * @note This callback is invoked on PWM counter reset. If set to
+ * @p NULL then the callback is disabled.
+ */
+ pwmcallback_t callback;
+ /**
+ * @brief Channels configurations.
+ */
+ PWMChannelConfig channels[PWM_CHANNELS];
+ /* End of the mandatory fields.*/
+} PWMConfig;
+
+/**
+ * @brief Structure representing a PWM driver.
+ */
+struct PWMDriver {
+ /**
+ * @brief Driver state.
+ */
+ pwmstate_t state;
+ /**
+ * @brief Current driver configuration data.
+ */
+ const PWMConfig *config;
+ /**
+ * @brief Current PWM period in ticks.
+ */
+ pwmcnt_t period;
+ /**
+ * @brief Mask of the enabled channels.
+ */
+ pwmchnmsk_t enabled;
+ /**
+ * @brief Number of channels in this instance.
+ */
+ pwmchannel_t channels;
+#if defined(PWM_DRIVER_EXT_FIELDS)
+ PWM_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Pointer to the PWM registers block.
+ */
+ NRF_PWM_Type *pwm;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Changes the period the PWM peripheral.
+ * @details This function changes the period of a PWM unit that has already
+ * been activated using @p pwmStart().
+ * @pre The PWM unit must have been activated using @p pwmStart().
+ * @post The PWM unit period is changed to the new value.
+ * @note The function has effect at the next cycle start.
+ * @note If a period is specified that is shorter than the pulse width
+ * programmed in one of the channels then the behavior is not
+ * guaranteed.
+ *
+ * @param[in] pwmp pointer to a @p PWMDriver object
+ * @param[in] period new cycle time in ticks
+ *
+ * @notapi
+ */
+#define pwm_lld_change_period(pwmp, period) \
+ do { \
+ (pwmp)->pwm->COUNTERTOP = period & PWM_COUNTERTOP_COUNTERTOP_Msk; \
+ } while(0)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if NRF5_PWM_USE_PWM0 || defined(__DOXYGEN__)
+extern PWMDriver PWMD1;
+#endif
+#if NRF5_PWM_USE_PWM1 || defined(__DOXYGEN__)
+extern PWMDriver PWMD2;
+#endif
+#if NRF5_PWM_USE_PWM2 || defined(__DOXYGEN__)
+extern PWMDriver PWMD3;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void pwm_lld_init(void);
+ void pwm_lld_start(PWMDriver *pwmp);
+ void pwm_lld_stop(PWMDriver *pwmp);
+ void pwm_lld_enable_channel(PWMDriver *pwmp,
+ pwmchannel_t channel,
+ pwmcnt_t width);
+ void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
+ void pwm_lld_enable_periodic_notification(PWMDriver *pwmp);
+ void pwm_lld_disable_periodic_notification(PWMDriver *pwmp);
+ void pwm_lld_enable_channel_notification(PWMDriver *pwmp,
+ pwmchannel_t channel);
+ void pwm_lld_disable_channel_notification(PWMDriver *pwmp,
+ pwmchannel_t channel);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_PWM */
+
+#endif /* HAL_PWM_LLD_H_ */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk b/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
index 6f3ce0e..333fe3c 100644
--- a/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
@@ -2,8 +2,12 @@ ifeq ($(USE_SMART_BUILD),yes)
ifneq ($(findstring HAL_USE_GPT TRUE,$(HALCONF)),)
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c
endif
+ifneq ($(findstring HAL_USE_ICU TRUE,$(HALCONF)),)
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c
+endif
else
PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c
endif
PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c b/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c
new file mode 100644
index 0000000..4966007
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c
@@ -0,0 +1,768 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/*
+ * Hardware Abstraction Layer for Extended Input Capture Unit
+ */
+#include "hal.h"
+
+#if (HAL_USE_ICU == TRUE) || defined(__DOXYGEN__)
+
+/**
+ * @brief Returns the compare value of the latest cycle.
+ *
+ * @param[in] chp Pointer to channel structure that fired the interrupt.
+ * @return The number of ticks.
+ *
+ * @notapi
+ */
+//#define icu_lld_get_compare(chp) (*((chp)->ccp) + 1)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief ICUD1 driver identifier.
+ * @note The driver ICUD1 allocates the complex timer TIMER0 when enabled.
+ */
+#if NRF5_ICU_USE_TIMER0 && !defined(__DOXYGEN__)
+ICUDriver ICUD1;
+#endif
+
+/**
+ * @brief ICUD2 driver identifier.
+ * @note The driver ICUD2 allocates the timer TIMER1 when enabled.
+ */
+#if NRF5_ICU_USE_TIMER1 && !defined(__DOXYGEN__)
+ICUDriver ICUD2;
+#endif
+
+/**
+ * @brief ICUD3 driver identifier.
+ * @note The driver ICUD3 allocates the timer TIMER2 when enabled.
+ */
+#if NRF5_ICU_USE_TIMER2 && !defined(__DOXYGEN__)
+ICUDriver ICUD3;
+#endif
+
+/**
+ * @brief ICUD4 driver identifier.
+ * @note The driver ICUD4 allocates the timer TIMER3 when enabled.
+ */
+#if NRF5_ICU_USE_TIMER3 && !defined(__DOXYGEN__)
+ICUDriver ICUD4;
+#endif
+
+/**
+ * @brief ICUD5 driver identifier.
+ * @note The driver ICUD5 allocates the timer TIMER4 when enabled.
+ */
+#if NRF5_ICU_USE_TIMER4 && !defined(__DOXYGEN__)
+ICUDriver ICUD5;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+/**
+ * @brief Returns pulse width.
+ * @details The time is defined as number of ticks.
+ *
+ * @param[in] icup Pointer to the ICUDriver object.
+ * @param[in] channel The timer channel that fired the interrupt.
+ * @param[in] compare Content of the CC register.
+ * @return The number of ticks.
+ *
+ * @notapi
+ */
+static icucnt_t get_time_width(const ICUDriver *icup,
+ uint8_t channel,
+ icucnt_t compare) {
+
+ const ICUChannel *chp = &icup->channel[channel];
+
+ /* Note! there is no overflow check because it handles under the hood of
+ unsigned subtraction math.*/
+ return compare - chp->last_idle;
+}
+
+/**
+ * @brief Returns pulse period.
+ * @details The time is defined as number of ticks.
+ *
+ * @param[in] icup Pointer to the ICUDriver object.
+ * @param[in] channel The timer channel that fired the interrupt.
+ * @param[in] compare Content of the CC register.
+ * @return The number of ticks.
+ *
+ * @notapi
+ */
+static icucnt_t get_time_period(const ICUDriver *icup,
+ uint8_t channel,
+ icucnt_t compare) {
+
+ const ICUChannel *chp = &icup->channel[channel];
+
+ /* Note! there is no overflow check because it handles under the hood of
+ unsigned subtraction math.*/
+ return compare - chp->last_idle;
+}
+
+/**
+ * @brief ICU width event.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ * @param[in] channel The timer channel that fired the interrupt.
+ *
+ * @notapi
+ */
+static void _isr_invoke_width_cb(ICUDriver *icup, uint8_t channel) {
+ ICUChannel *chp = &icup->channel[channel];
+ icucnt_t compare = icup->timer->CC[channel+2];
+ chp->last_active = compare;
+ if (ICU_CH_ACTIVE == chp->state) {
+ icup->result.width = get_time_width(icup, channel, compare);
+ if ((icup->state == ICU_ACTIVE) && (icup->config->width_cb != NULL))
+ icup->config->width_cb(icup);
+ chp->state = ICU_CH_IDLE;
+ }
+}
+
+/**
+ * @brief ICU period detect event.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ * @param[in] channel The timer channel that fired the interrupt.
+ *
+ * @notapi
+ */
+static void _isr_invoke_period_cb(ICUDriver *icup, uint8_t channel) {
+ ICUChannel *chp = &icup->channel[channel];
+ icucnt_t compare = (uint32_t)icup->timer->CC[channel];
+ icup->result.period = get_time_period(icup, channel, compare);
+ chp->last_idle = compare;
+ chp->state = ICU_CH_ACTIVE;
+ if ((icup->state == ICU_ACTIVE) && (icup->config->period_cb != NULL))
+ icup->config->period_cb(icup);
+ icup->state = ICU_ACTIVE;
+ /* Set overflow timeout */
+ icup->timer->CC[channel] = compare + ICU_WAIT_TIMEOUT;
+}
+
+/**
+ * @brief Shared IRQ handler.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ */
+void icu_lld_serve_gpiote_interrupt(ICUDriver *icup) {
+ uint8_t ch;
+ for (ch=0; ch<ICU_CHANNELS; ch++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[ch];
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+
+ /* Period event */
+ if (NRF_GPIOTE->INTENSET & (1 << gpiote_channel[0]) && NRF_GPIOTE->EVENTS_IN[gpiote_channel[0]]) {
+ _isr_invoke_period_cb(icup, ch);
+ NRF_GPIOTE->EVENTS_IN[gpiote_channel[0]] = 0;
+ (void) NRF_GPIOTE->EVENTS_IN[gpiote_channel[0]];
+ }
+ /* Width event */
+ if (NRF_GPIOTE->INTENSET & (1 << gpiote_channel[1]) && NRF_GPIOTE->EVENTS_IN[gpiote_channel[1]]) {
+ _isr_invoke_width_cb(icup, ch);
+ NRF_GPIOTE->EVENTS_IN[gpiote_channel[1]] = 0;
+ (void) NRF_GPIOTE->EVENTS_IN[gpiote_channel[1]];
+ }
+ }
+}
+
+/**
+ * @brief Overflow IRQ handler.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ */
+void icu_lld_serve_interrupt(ICUDriver *icup) {
+ uint8_t ch;
+ for (ch=0; ch<ICU_CHANNELS; ch++) {
+ /* Clear overflow events */
+ if (icup->timer->INTENSET & (1 << (TIMER_INTENSET_COMPARE0_Pos + ch)) &&
+ icup->timer->EVENTS_COMPARE[ch]) {
+ icup->timer->EVENTS_COMPARE[ch] = 0;
+ (void) icup->timer->EVENTS_COMPARE[ch];
+ /* Set next overlow compare */
+ icup->timer->CC[ch] = icup->timer->CC[ch] + ICU_WAIT_TIMEOUT;
+ }
+ }
+ if (icup->config->overflow_cb != NULL)
+ icup->config->overflow_cb(icup);
+ icup->state = ICU_WAITING;
+}
+
+/**
+ * @brief Starts every channel.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ *
+ * @note GPIO Line[0] -> GPIOTE channel[0] will detect start edge.
+ * @note GPIO Line[1] -> GPIOTE channel[1] will detect end edge.
+ */
+static void start_channels(ICUDriver *icup) {
+
+ /* Set each input channel that is used as: a normal input capture channel. */
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel->mode == ICU_INPUT_DISABLED) continue;
+
+ const uint32_t gpio_pin0 = PAL_PAD(cfg_channel->ioline[0]);
+ const uint32_t gpio_pin1 = PAL_PAD(cfg_channel->ioline[1]);
+ osalDbgAssert((gpio_pin0 < 32) &&
+ (gpio_pin1 < 32) &&
+ (gpio_pin0 != gpio_pin1),
+ "invalid Line configuration");
+
+ /* NRF52832 GPIOTE channels 0..7 */
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+ osalDbgAssert((gpiote_channel[0] < 8) &&
+ (gpiote_channel[1] < 8) &&
+ (gpiote_channel[0] != gpiote_channel[1]),
+ "invalid GPIOTE configuration");
+
+ /* NRF52832 PPI channels 0..19 */
+ const uint8_t *ppi_channel = cfg_channel->ppi_channel;
+ osalDbgAssert((gpiote_channel[0] < 20) &&
+ (gpiote_channel[1] < 20) &&
+ (gpiote_channel[0] != gpiote_channel[1]),
+ "invalid PPI configuration");
+
+ /* Program PPI events for period */
+ NRF_PPI->CH[ppi_channel[0]].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel[0]];
+ NRF_PPI->CH[ppi_channel[0]].TEP = (uint32_t) &icup->timer->TASKS_CAPTURE[channel];
+
+ /* Program PPI events for width */
+ NRF_PPI->CH[ppi_channel[1]].EEP = (uint32_t) &NRF_GPIOTE->EVENTS_IN[gpiote_channel[1]];
+ NRF_PPI->CH[ppi_channel[1]].TEP = (uint32_t) &icup->timer->TASKS_CAPTURE[channel+2];
+
+ /* Disable GPIOTE interrupts */
+ NRF_GPIOTE->INTENCLR = (GPIOTE_INTENCLR_PORT_Clear << GPIOTE_INTENCLR_PORT_Pos) |
+ (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+ NRF_GPIOTE->EVENTS_PORT = 1;
+
+ /* Clear GPIOTE channels */
+ NRF_GPIOTE->CONFIG[gpiote_channel[0]] &= ~(GPIOTE_CONFIG_PSEL_Msk | GPIOTE_CONFIG_POLARITY_Msk);
+ NRF_GPIOTE->CONFIG[gpiote_channel[1]] &= ~(GPIOTE_CONFIG_PSEL_Msk | GPIOTE_CONFIG_POLARITY_Msk);
+
+ /* Set GPIOTE channels */
+ if (cfg_channel->mode == ICU_INPUT_ACTIVE_HIGH) {
+ NRF_GPIOTE->CONFIG[gpiote_channel[0]] =
+ (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) |
+ ((gpio_pin0 << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PSEL_Msk) |
+ ((GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk);
+ NRF_GPIOTE->CONFIG[gpiote_channel[1]] =
+ (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) |
+ ((gpio_pin1 << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PSEL_Msk) |
+ ((GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk);
+ } else {
+ NRF_GPIOTE->CONFIG[gpiote_channel[0]] =
+ (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) |
+ ((gpio_pin0 << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PSEL_Msk) |
+ ((GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos)
+ & GPIOTE_CONFIG_POLARITY_Msk);
+ NRF_GPIOTE->CONFIG[gpiote_channel[1]] =
+ (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) |
+ ((gpio_pin1 << GPIOTE_CONFIG_PSEL_Pos) & GPIOTE_CONFIG_PSEL_Msk) |
+ ((GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
+ & GPIOTE_CONFIG_POLARITY_Msk);
+ }
+ }
+#endif
+
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if NRF5_ICU_USE_GPIOTE_PPI
+/**
+ * @brief GPIOTE events interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector58) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_gpiote_interrupt(&ICUD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_GPIOTE_PPI */
+
+#if NRF5_ICU_USE_TIMER0
+/**
+ * @brief TIMER0 compare interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector60) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_interrupt(&ICUD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_TIMER0 */
+
+#if NRF5_ICU_USE_TIMER1
+/**
+ * @brief TIMER1 interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector64) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_interrupt(&ICUD2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_TIMER1 */
+
+#if NRF5_ICU_USE_TIMER2
+/**
+ * @brief TIMER2 interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector68) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_interrupt(&ICUD3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_TIMER2 */
+
+#if NRF5_ICU_USE_TIMER3
+/**
+ * @brief TIMER3 interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(VectorA8) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_interrupt(&ICUD4);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_TIMER3 */
+
+#if NRF5_ICU_USE_TIMER4
+/**
+ * @brief TIMER4 interrupt handler.
+ * @note It is assumed that the various sources are only activated if the
+ * associated callback pointer is not equal to @p NULL in order to not
+ * perform an extra check in a potentially critical interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(VectorAC) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ icu_lld_serve_interrupt(&ICUD5);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_ICU_USE_TIMER4 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level ICU driver initialization.
+ *
+ * @notapi
+ */
+void icu_lld_init(void) {
+#if NRF5_ICU_USE_TIMER0
+ /* Driver initialization.*/
+ icuObjectInit(&ICUD1);
+ ICUD1.timer = NRF_TIMER0;
+#endif
+
+#if NRF5_ICU_USE_TIMER1
+ /* Driver initialization.*/
+ icuObjectInit(&ICUD2);
+ ICUD2.timer = NRF_TIMER1;
+#endif
+
+#if NRF5_ICU_USE_TIMER2
+ /* Driver initialization.*/
+ icuObjectInit(&ICUD3);
+ ICUD3.timer = NRF_TIMER2;
+#endif
+
+#if NRF5_ICU_USE_TIMER3
+ /* Driver initialization.*/
+ icuObjectInit(&ICUD4);
+ ICUD4.timer = NRF_TIMER3;
+#endif
+
+#if NRF5_ICU_USE_TIMER4
+ /* Driver initialization.*/
+ icuObjectInit(&ICUD5);
+ ICUD5.timer = NRF_TIMER4;
+#endif
+}
+
+/**
+ * @brief Configures and activates the ICU peripheral.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_start(ICUDriver *icup) {
+ size_t ch;
+ osalDbgAssert((&icup->config->iccfgp[0] != NULL) ||
+ (&icup->config->iccfgp[1] != NULL),
+ "invalid input configuration");
+ /* Prescaler value calculation: ftimer = 16MHz / 2^PRESCALER */
+ uint16_t psc_ratio = 16000000 / icup->config->frequency;
+ /* Prescaler ratio must be between 1 and 512, and a power of two. */
+ osalDbgAssert(psc_ratio <= 512 && !(psc_ratio & (psc_ratio - 1)),
+ "invalid frequency");
+ /* Prescaler value as a power of 2, must be 0..9 */
+ uint32_t psc_value;
+ for (psc_value = 0; psc_value < 10; psc_value++)
+ if (psc_ratio == (unsigned)(1 << psc_value))
+ break;
+
+ /* Configure as 32bits timer */
+ icup->timer->BITMODE = TIMER_BITMODE_BITMODE_32Bit;
+
+ /* Set timer mode */
+ icup->timer->MODE = TIMER_MODE_MODE_Timer;
+
+ /* Set prescaler */
+ icup->timer->PRESCALER = psc_value;
+
+ /* With clear shortcuts. */
+ icup->timer->SHORTS = 0;
+
+ /* Clear Timer */
+ icup->timer->TASKS_CLEAR = 1;
+
+ /* Disable and reset interrupts for compare events */
+ icup->timer->INTENCLR = (TIMER_INTENCLR_COMPARE0_Msk |
+ TIMER_INTENCLR_COMPARE1_Msk |
+ TIMER_INTENCLR_COMPARE2_Msk |
+ TIMER_INTENCLR_COMPARE3_Msk );
+
+ icup->timer->EVENTS_COMPARE[0] = 0;
+ icup->timer->EVENTS_COMPARE[1] = 0;
+ icup->timer->EVENTS_COMPARE[2] = 0;
+ icup->timer->EVENTS_COMPARE[3] = 0;
+ (void) icup->timer->EVENTS_COMPARE[0];
+ (void) icup->timer->EVENTS_COMPARE[1];
+ (void) icup->timer->EVENTS_COMPARE[2];
+ (void) icup->timer->EVENTS_COMPARE[3];
+
+ /* Enable GPIOTE and TIMER interrupt vectors.*/
+#if NRF5_ICU_USE_GPIOTE_PPI
+ nvicEnableVector(GPIOTE_IRQn, NRF5_ICU_GPIOTE_IRQ_PRIORITY);
+#endif
+#if NRF5_ICU_USE_TIMER0
+ if (&ICUD1 == icup) {
+ nvicEnableVector(TIMER0_IRQn, NRF5_ICU_TIMER0_IRQ_PRIORITY);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER1
+ if (&ICUD2 == icup) {
+ nvicEnableVector(TIMER1_IRQn, NRF5_ICU_TIMER1_IRQ_PRIORITY);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER2
+ if (&ICUD3 == icup) {
+ nvicEnableVector(TIMER2_IRQn, NRF5_ICU_TIMER2_IRQ_PRIORITY);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER3
+ if (&ICUD4 == icup) {
+ nvicEnableVector(TIMER3_IRQn, NRF5_ICU_TIMER3_IRQ_PRIORITY);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER4
+ if (&ICUD5 == icup) {
+ nvicEnableVector(TIMER4_IRQn, NRF5_ICU_TIMER4_IRQ_PRIORITY);
+ }
+#endif
+
+ /* clean channel structures and set pointers to channel configs */
+ for (ch=0; ch<ICU_CHANNELS; ch++) {
+ icup->channel[ch].last_active = 0;
+ icup->channel[ch].last_idle = 0;
+ icup->channel[ch].state = ICU_CH_IDLE;
+ }
+ /* Set GPIOTE & PPI channels */
+ start_channels(icup);
+}
+
+/**
+ * @brief Deactivates the ICU peripheral.
+ *
+ * @param[in] icup Pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_stop(ICUDriver *icup) {
+
+ if (icup->state == ICU_READY) {
+ /* Timer stop.*/
+ icup->timer->TASKS_STOP = 1;
+
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel == NULL) continue;
+
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+ const uint8_t *ppi_channel = cfg_channel->ppi_channel;
+
+ /* Disable Timer interrupt */
+ icup->timer->INTENCLR = 1 << (TIMER_INTENCLR_COMPARE0_Pos + channel);
+
+ /* Disable GPIOTE interrupts */
+ NRF_GPIOTE->INTENCLR = (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+
+ /* Disable PPI channels */
+ NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1]));
+
+ /* Clear GPIOTE channels */
+ NRF_GPIOTE->CONFIG[gpiote_channel[0]] &= ~(GPIOTE_CONFIG_PSEL_Msk | GPIOTE_CONFIG_POLARITY_Msk);
+ NRF_GPIOTE->CONFIG[gpiote_channel[1]] &= ~(GPIOTE_CONFIG_PSEL_Msk | GPIOTE_CONFIG_POLARITY_Msk);
+ }
+#endif
+
+#if NRF5_ICU_USE_GPIOTE_PPI
+ nvicDisableVector(GPIOTE_IRQn);
+#endif
+#if NRF5_ICU_USE_TIMER0
+ if (&ICUD1 == icup) {
+ nvicDisableVector(TIMER0_IRQn);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER1
+ if (&ICUD2 == icup) {
+ nvicDisableVector(TIMER1_IRQn);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER2
+ if (&ICUD3 == icup) {
+ nvicDisableVector(TIMER2_IRQn);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER3
+ if (&ICUD4 == icup) {
+ nvicDisableVector(TIMER3_IRQn);
+ }
+#endif
+#if NRF5_ICU_USE_TIMER4
+ if (&ICUD5 == icup) {
+ nvicDisableVector(TIMER4_IRQn);
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Starts the input capture.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_start_capture(ICUDriver *icup) {
+ /* Clear and start Timer */
+ icup->timer->TASKS_CLEAR = 1;
+ icup->timer->TASKS_START = 1;
+
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel == NULL) continue;
+
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+ const uint8_t *ppi_channel = cfg_channel->ppi_channel;
+
+ /* Enable interrupt for overflow events */
+ icup->timer->CC[channel] = ICU_WAIT_TIMEOUT;
+ icup->timer->INTENSET = 1 << (TIMER_INTENSET_COMPARE0_Pos + channel);
+
+ /* Enable PPI channels */
+ NRF_PPI->CHENSET = ((1 << ppi_channel[0]) | (1 << ppi_channel[1]));
+
+ /* Enable GPIOTE interrupt */
+ NRF_GPIOTE->INTENSET = (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+ }
+#endif
+}
+
+/**
+ * @brief Waits for a completed capture.
+ * @note The operation is performed in polled mode.
+ * @note In order to use this function notifications must be disabled.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ * @return The capture status.
+ * @retval false if the capture is successful.
+ * @retval true if a timer overflow occurred.
+ *
+ * @notapi
+ */
+bool icu_lld_wait_capture(ICUDriver *icup) {
+
+ /* If the driver is still in the ICU_WAITING state then we need to wait
+ for the first activation edge.*/
+ if (icup->state == ICU_WAITING)
+ if (icu_lld_wait_edge(icup))
+ return true;
+
+ /* This edge marks the availability of a capture result.*/
+ return icu_lld_wait_edge(icup);
+}
+
+/**
+ * @brief Stops the input capture.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_stop_capture(ICUDriver *icup) {
+ /* Timer stopped.*/
+ icup->timer->TASKS_STOP = 1;
+
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel == NULL) continue;
+
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+ const uint8_t *ppi_channel = cfg_channel->ppi_channel;
+
+ /* Disable Timer interrupt for overflow events */
+ icup->timer->INTENCLR = 1 << (TIMER_INTENCLR_COMPARE0_Pos + channel);
+
+ /* Disable GPIOTE interrupt */
+ NRF_GPIOTE->INTENCLR = (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+
+ /* Disable PPI channels */
+ NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1]));
+ }
+#endif
+}
+
+/**
+ * @brief Enables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart() and the
+ * capture started using @p icuStartCapture().
+ * @note If the notification is already enabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_enable_notifications(ICUDriver *icup) {
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel == NULL) continue;
+
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+
+ /* Enable Timer interrupt */
+ icup->timer->INTENSET = 1 << (TIMER_INTENSET_COMPARE0_Pos + channel);
+
+ /* Enable GPIOTE interrupt */
+ NRF_GPIOTE->INTENSET = (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+ }
+#endif
+}
+
+/**
+ * @brief Disables notifications.
+ * @pre The ICU unit must have been activated using @p icuStart() and the
+ * capture started using @p icuStartCapture().
+ * @note If the notification is already disabled then the call has no effect.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ *
+ * @notapi
+ */
+void icu_lld_disable_notifications(ICUDriver *icup) {
+ /* All interrupts disabled.*/
+#if NRF5_ICU_USE_GPIOTE_PPI
+ uint8_t channel;
+ for (channel = 0; channel<ICU_CHANNELS; channel++) {
+ const ICUChannelConfig *cfg_channel = &icup->config->iccfgp[channel];
+ if (cfg_channel == NULL) continue;
+
+ const uint8_t *gpiote_channel = cfg_channel->gpiote_channel;
+
+ /* Disable Timer interrupt for overflow events */
+ icup->timer->INTENCLR = 1 << (TIMER_INTENCLR_COMPARE0_Pos + channel);
+
+ /* Disable GPIOTE interrupt */
+ NRF_GPIOTE->INTENCLR = (1 << gpiote_channel[0]) | (1 << gpiote_channel[1]);
+ }
+#endif
+
+}
+
+#endif /* HAL_USE_ICU */
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.h b/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.h
new file mode 100644
index 0000000..7fcace7
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.h
@@ -0,0 +1,424 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+/*
+*/
+
+#ifndef HAL_ICU_LLD_H
+#define HAL_ICU_LLD_H
+
+#if (HAL_USE_ICU == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+/**
+ * @brief Number of ICU channels per ICU driver.
+ */
+#define ICU_CHANNELS 2 /* max channels */
+#define ICU_WAIT_TIMEOUT ( 0xFFFF ) /* first edge wait timeout */
+
+#define ICU_FREQUENCY_16MHZ 16000000 /** @brief 16MHz */
+#define ICU_FREQUENCY_8MHZ 8000000 /** @brief 8MHz */
+#define ICU_FREQUENCY_4MHZ 4000000 /** @brief 4MHz */
+#define ICU_FREQUENCY_2MHZ 2000000 /** @brief 2MHz */
+#define ICU_FREQUENCY_1MHZ 1000000 /** @brief 1MHz */
+#define ICU_FREQUENCY_500KHZ 500000 /** @brief 500kHz */
+#define ICU_FREQUENCY_250KHZ 250000 /** @brief 250kHz */
+#define ICU_FREQUENCY_125KHZ 125000 /** @brief 125kHz */
+#define ICU_FREQUENCY_62500HZ 62500 /** @brief 62500Hz */
+#define ICU_FREQUENCY_31250HZ 31250 /** @brief 31250Hz */
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief ICUD1 driver enable switch.
+ * @details If set to @p TRUE the support for ICUD1 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_ICU_USE_TIMER0) || defined(__DOXYGEN__)
+#define NRF5_ICU_USE_TIMER0 FALSE
+#endif
+
+/**
+ * @brief ICUD2 driver enable switch.
+ * @details If set to @p TRUE the support for ICUD2 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_ICU_USE_TIMER1) || defined(__DOXYGEN__)
+#define NRF5_ICU_USE_TIMER1 FALSE
+#endif
+
+/**
+ * @brief ICUD3 driver enable switch.
+ * @details If set to @p TRUE the support for ICUD3 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_ICU_USE_TIMER2) || defined(__DOXYGEN__)
+#define NRF5_ICU_USE_TIMER2 FALSE
+#endif
+
+/**
+ * @brief ICUD4 driver enable switch.
+ * @details If set to @p TRUE the support for ICUD4 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_ICU_USE_TIMER3) || defined(__DOXYGEN__)
+#define NRF5_ICU_USE_TIMER3 FALSE
+#endif
+
+/**
+ * @brief ICUD5 driver enable switch.
+ * @details If set to @p TRUE the support for ICUD5 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_ICU_USE_TIMER4) || defined(__DOXYGEN__)
+#define NRF5_ICU_USE_TIMER4 FALSE
+#endif
+
+/**
+ * @brief ICUD1 interrupt priority level setting.
+ */
+#if !defined(NRF5_ICU_TIMER0_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_ICU_TIMER0_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief ICUD2 interrupt priority level setting.
+ */
+#if !defined(NRF5_ICU_TIMER1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_ICU_TIMER1_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief ICUD3 interrupt priority level setting.
+ */
+#if !defined(NRF5_ICU_TIMER2_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_ICU_TIMER2_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief ICUD4 interrupt priority level setting.
+ */
+#if !defined(NRF5_ICU_TIMER3_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_ICU_TIMER3_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief ICUD5 interrupt priority level setting.
+ */
+#if !defined(NRF5_ICU_TIMER4_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_ICU_TIMER4_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief Allow driver to use GPIOTE/PPI to capture PAL line
+ */
+#if !defined(NRF5_ICU_USE_GPIOTE_PPI)
+#define NRF5_ICU_USE_GPIOTE_PPI TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !NRF5_ICU_USE_TIMER0 && !NRF5_ICU_USE_TIMER1 && \
+ !NRF5_ICU_USE_TIMER2 && !NRF5_ICU_USE_TIMER3 && \
+ !NRF5_ICU_USE_TIMER4
+#error "ICU driver activated but no TIMER peripheral assigned"
+#endif
+
+#if NRF5_ICU_USE_TIMER0 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ICU_TIMER0_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER0"
+#endif
+
+#if NRF5_ICU_USE_TIMER1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ICU_TIMER1_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER1"
+#endif
+
+#if NRF5_ICU_USE_TIMER2 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ICU_TIMER2_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER2"
+#endif
+
+#if NRF5_ICU_USE_TIMER3 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ICU_TIMER3_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER3"
+#endif
+
+#if NRF5_ICU_USE_TIMER4 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ICU_TIMER4_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER4"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+/**
+ * @brief Active level selector.
+ */
+typedef enum {
+ ICU_INPUT_DISABLED, /**< Channel disabled . */
+ ICU_INPUT_ACTIVE_HIGH, /**< Trigger on rising edge. */
+ ICU_INPUT_ACTIVE_LOW, /**< Trigger on falling edge. */
+} icumode_t;
+
+/**
+ * @brief ICU channel state.
+ */
+typedef enum {
+ ICU_CH_IDLE = 0, /**< Not initialized. */
+ ICU_CH_ACTIVE = 1 /**< First front detected. */
+} icuchannelstate_t;
+
+/**
+ * @brief ICU frequency type.
+ */
+typedef uint32_t icufreq_t;
+
+/**
+ * @brief ICU channel type.
+ */
+typedef enum {
+ ICU_CHANNEL_1 = 0, /**< Use TIMERx channel 0,2 */
+ ICU_CHANNEL_2 = 1, /**< Use TIMERx channel 1,3 */
+} icuchannel_t;
+
+/**
+ * @brief ICU counter type.
+ */
+typedef uint32_t icucnt_t;
+
+/**
+ * @brief ICU captured width and (or) period.
+ */
+typedef struct {
+ /**
+ * @brief Pulse width.
+ */
+ icucnt_t width;
+ /**
+ * @brief Pulse period.
+ */
+ icucnt_t period;
+} icuresult_t;
+
+/**
+ * @brief ICU Capture Channel Config structure definition.
+ */
+typedef struct {
+ /**
+ * @brief Specifies the channel capture mode.
+ */
+ icumode_t mode;
+
+#if NRF5_ICU_USE_GPIOTE_PPI || defined(__DOXYGEN__)
+ /**
+ * @brief PAL line to capture.
+ * @note When NRF5_ICU_USE_GPIOTE_PPI is used and channel enabled,
+ * it wont be possible to access this PAL line using the PAL
+ * driver.
+ */
+ ioline_t ioline[2];
+
+ /**
+ * @brief Unique GPIOTE channel to use. (2 channel)
+ * @note Only 8 GPIOTE channels are available on nRF52.
+ */
+ uint8_t gpiote_channel[2];
+
+ /**
+ * @brief Unique PPI channels to use. (2 channels)
+ * @note Only 20 PPI channels are available on nRF52
+ * (When Softdevice is enabled, only channels 0-7 are available)
+ */
+ uint8_t ppi_channel[2];
+#endif
+} ICUChannelConfig;
+
+/**
+ * @brief ICU Capture Channel structure definition.
+ */
+typedef struct {
+ /**
+ * @brief Channel state for the internal state machine.
+ */
+ icuchannelstate_t state;
+ /**
+ * @brief Cached value for pulse width calculation.
+ */
+ icucnt_t last_active;
+ /**
+ * @brief Cached value for period calculation.
+ */
+ icucnt_t last_idle;
+ /**
+ * @brief Pointer to Input Capture channel configuration.
+ */
+// const ICUChannelConfig *config;
+} ICUChannel;
+
+/**
+ * @brief ICU Config structure definition.
+ */
+typedef struct {
+ /**
+ * @brief Specifies the Timer clock in Hz.
+ */
+ icufreq_t frequency;
+ /**
+ * @brief Callback for pulse width measurement.
+ */
+ icucallback_t width_cb;
+ /**
+ * @brief Callback for cycle period measurement.
+ */
+ icucallback_t period_cb;
+ /**
+ * @brief Callback for timer overflow.
+ */
+ icucallback_t overflow_cb;
+ /**
+ * @brief Pointer to each Input Capture channel configuration.
+ * @note A NULL parameter indicates the channel as unused.
+ * @note In ICU mode, only Channel 1 OR Channel 2 may be used.
+ */
+ const ICUChannelConfig iccfgp[ICU_CHANNELS];
+} ICUConfig;
+
+/**
+ * @brief ICU Driver structure definition
+ */
+struct ICUDriver {
+ /**
+ * @brief NRF52 timer peripheral for Input Capture.
+ */
+ NRF_TIMER_Type *timer;
+ /**
+ * @brief Driver state for the internal state machine.
+ */
+ icustate_t state;
+ /**
+ * @brief Channels' data structures.
+ */
+ ICUChannel channel[ICU_CHANNELS];
+ /**
+ * @brief Timer base clock.
+ */
+ uint32_t clock;
+ /**
+ * @brief Pointer to configuration for the driver.
+ */
+ const ICUConfig *config;
+ /**
+ * @brief Period, width last value.
+ */
+ icuresult_t result;
+#if defined(ICU_DRIVER_EXT_FIELDS)
+ ICU_DRIVER_EXT_FIELDS
+#endif
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+/**
+ * @brief Returns the width of the latest pulse.
+ * @details The pulse width is defined as number of ticks between the start
+ * edge and the stop edge.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ * @return The number of ticks.
+ *
+ * @notapi
+ */
+#define icu_lld_get_width(icup) ((uint32_t)((icup)->result.width))
+
+/**
+ * @brief Returns the width of the latest cycle.
+ * @details The cycle width is defined as number of ticks between a start
+ * edge and the next start edge.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ * @return The number of ticks.
+ *
+ * @notapi
+ */
+#define icu_lld_get_period(icup) ((uint32_t)((icup)->result.period))
+
+/**
+ * @brief Check on notifications status.
+ *
+ * @param[in] icup pointer to the @p ICUDriver object
+ * @return The notifications status.
+ * @retval false if notifications are not enabled.
+ * @retval true if notifications are enabled.
+ *
+ * @notapi
+ */
+#define icu_lld_are_notifications_enabled(icup) ( 1 )
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+#if NRF5_ICU_USE_TIMER0 && !defined(__DOXYGEN__)
+extern ICUDriver ICUD1;
+#endif
+
+#if NRF5_ICU_USE_TIMER1 && !defined(__DOXYGEN__)
+extern ICUDriver ICUD2;
+#endif
+
+#if NRF5_ICU_USE_TIMER2 && !defined(__DOXYGEN__)
+extern ICUDriver ICUD3;
+#endif
+
+#if NRF5_ICU_USE_TIMER3 && !defined(__DOXYGEN__)
+extern ICUDriver ICUD4;
+#endif
+
+#if NRF5_ICU_USE_TIMER4 && !defined(__DOXYGEN__)
+extern ICUDriver ICUD5;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void icu_lld_init(void);
+ void icu_lld_start(ICUDriver *icup);
+ void icu_lld_stop(ICUDriver *icup);
+ void icu_lld_start_capture(ICUDriver *icup);
+ bool icu_lld_wait_capture(ICUDriver *icup);
+ void icu_lld_stop_capture(ICUDriver *icup);
+ void icu_lld_enable_notifications(ICUDriver *icup);
+ void icu_lld_disable_notifications(ICUDriver *icup);
+ void icu_lld_serve_interrupt(ICUDriver *icup);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_ICU */
+
+#endif /* HAL_ICU_LLD_H */
diff --git a/os/hal/ports/NRF5/LLD/TWIMv1/driver.mk b/os/hal/ports/NRF5/LLD/TWIMv1/driver.mk
new file mode 100644
index 0000000..43ad8e3
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TWIMv1/driver.mk
@@ -0,0 +1,9 @@
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),)
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c
+endif
+else
+PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c
+endif
+
+PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TWIMv1
diff --git a/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c b/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c
new file mode 100644
index 0000000..957c0be
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.c
@@ -0,0 +1,420 @@
+/*
+ Copyright (C) 2018 andru
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file NRF5/NRF52832/hal_i2c_lld.c
+ * @brief NRF52 I2C subsystem low level driver source.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#include "osal.h"
+#include "hal.h"
+#include "nrf_delay.h"
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* These macros are needed to see if the slave is stuck and we as master send dummy clock cycles to end its wait */
+#define I2C_HIGH(p) do { IOPORT1->OUTSET = (1UL << (p)); } while(0) /*!< Pulls I2C line high */
+#define I2C_LOW(p) do { IOPORT1->OUTCLR = (1UL << (p)); } while(0) /*!< Pulls I2C line low */
+#define I2C_INPUT(p) do { IOPORT1->DIRCLR = (1UL << (p)); } while(0) /*!< Configures I2C pin as input */
+#define I2C_OUTPUT(p) do { IOPORT1->DIRSET = (1UL << (p)); } while(0) /*!< Configures I2C pin as output */
+
+#define I2C_PIN_CNF \
+ ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
+ | (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
+ | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) \
+ | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
+ | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos))
+
+#define I2C_PIN_CNF_CLR \
+ ((GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) \
+ | (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos) \
+ | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) \
+ | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) \
+ | (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos))
+
+#if NRF5_I2C_USE_I2C0
+#define I2C0_IRQ_NUM SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn
+#define I2C0_IRQ_PRI NRF5_I2C_I2C0_IRQ_PRIORITY
+#endif
+#if NRF5_I2C_USE_I2C1
+#define I2C1_IRQ_NUM SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQn
+#define I2C1_IRQ_PRI NRF5_I2C_I2C1_IRQ_PRIORITY
+#endif
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief I2C0 driver identifier.
+ */
+#if NRF5_I2C_USE_I2C0 || defined(__DOXYGEN__)
+I2CDriver I2CD1;
+#endif
+
+/**
+ * @brief I2C1 driver identifier.
+ */
+#if NRF5_I2C_USE_I2C1 || defined(__DOXYGEN__)
+I2CDriver I2CD2;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+/**
+ * @brief Function for detecting stuck slaves (SDA = 0 and SCL = 1) and tries to clear the bus.
+ *
+ * @return
+ * @retval false Bus is stuck.
+ * @retval true Bus is clear.
+ */
+static void i2c_clear_bus(I2CDriver *i2cp) {
+ const I2CConfig *cfg = i2cp->config;
+ uint8_t i;
+
+ IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF;
+ IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF;
+
+ I2C_HIGH(cfg->sda_pad);
+ I2C_HIGH(cfg->scl_pad);
+
+ IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF_CLR;
+ IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF_CLR;
+
+ nrf_delay_us(4);
+
+ for(i = 0; i < 9; i++) {
+ if (palReadPad(IOPORT1, cfg->sda_pad)) {
+ if(i > 0)
+ break;
+ else
+ return;
+ }
+
+ I2C_LOW(cfg->scl_pad);
+ nrf_delay_us(4);
+ I2C_HIGH(cfg->scl_pad);
+ nrf_delay_us(4);
+ }
+
+ I2C_LOW(cfg->sda_pad);
+ nrf_delay_us(4);
+ I2C_HIGH(cfg->sda_pad);
+}
+
+#if defined(__GNUC__)
+__attribute__((noinline))
+#endif
+/**
+ * @brief Common IRQ handler.
+ * @note Tries hard to clear all the pending interrupt sources, we don't
+ * want to go through the whole ISR and have another interrupt soon
+ * after.
+ *
+ * @param[in] i2cp pointer to an I2CDriver
+ */
+static void i2c_serve_interrupt(I2CDriver *i2cp) {
+ NRF_TWIM_Type *i2c = i2cp->i2c;
+
+ if (i2c->EVENTS_ERROR) {
+ uint32_t err = i2c->ERRORSRC;
+ i2c->EVENTS_ERROR = 0;
+ (void)i2c->EVENTS_ERROR;
+
+ if (err & 0x01) // nRF52832 Product Specification v1.3 p.314 TWIM_ERRORSRC OVERRUN bit = 0x01
+ i2cp->errors |= I2C_OVERRUN;
+ if (err & (TWIM_ERRORSRC_ANACK_Msk | TWIM_ERRORSRC_DNACK_Msk))
+ i2cp->errors |= I2C_ACK_FAILURE;
+
+ i2c->TASKS_STOP = 1;
+
+ _i2c_wakeup_error_isr(i2cp);
+ } else if(i2c->EVENTS_STOPPED) {
+
+ i2c->EVENTS_STOPPED = 0;
+ (void)i2c->EVENTS_STOPPED;
+
+ _i2c_wakeup_isr(i2cp);
+ }
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if NRF5_I2C_USE_I2C0 || defined(__DOXYGEN__)
+
+OSAL_IRQ_HANDLER(Vector4C) {
+
+ OSAL_IRQ_PROLOGUE();
+ i2c_serve_interrupt(&I2CD1);
+ OSAL_IRQ_EPILOGUE();
+}
+
+#endif
+
+#if NRF5_I2C_USE_I2C1 || defined(__DOXYGEN__)
+
+OSAL_IRQ_HANDLER(Vector50) {
+
+ OSAL_IRQ_PROLOGUE();
+ i2c_serve_interrupt(&I2CD2);
+ OSAL_IRQ_EPILOGUE();
+}
+
+#endif
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level I2C driver initialization.
+ *
+ * @notapi
+ */
+void i2c_lld_init(void) {
+
+#if NRF5_I2C_USE_I2C0
+ i2cObjectInit(&I2CD1);
+ I2CD1.thread = NULL;
+ I2CD1.i2c = NRF_TWIM0;
+#endif
+
+#if NRF5_I2C_USE_I2C1
+ i2cObjectInit(&I2CD2);
+ I2CD2.thread = NULL;
+ I2CD2.i2c = NRF_TWIM1;
+#endif
+
+}
+
+/**
+ * @brief Configures and activates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_start(I2CDriver *i2cp) {
+ NRF_TWIM_Type *i2c = i2cp->i2c;
+
+ const I2CConfig *cfg = i2cp->config;
+
+ if (i2cp->state != I2C_STOP)
+ return;
+
+ osalDbgAssert(i2c->ENABLE == 0, "already in use");
+
+ i2c_clear_bus(i2cp);
+
+ IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF;
+ IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF;
+
+ i2c->SHORTS = 0;
+
+ i2c->EVENTS_STOPPED = 0;
+ i2c->EVENTS_ERROR = 0;
+ (void)i2c->EVENTS_STOPPED;
+ (void)i2c->EVENTS_ERROR;
+
+ i2c->PSEL.SCL = cfg->scl_pad;
+ i2c->PSEL.SDA = cfg->sda_pad;
+
+ switch (cfg->clock) {
+ case 100000:
+ i2c->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K100 << TWIM_FREQUENCY_FREQUENCY_Pos;
+ break;
+ case 250000:
+ i2c->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K250 << TWIM_FREQUENCY_FREQUENCY_Pos;
+ break;
+ case 400000:
+ i2c->FREQUENCY = TWIM_FREQUENCY_FREQUENCY_K400 << TWIM_FREQUENCY_FREQUENCY_Pos;
+ break;
+ default:
+ osalDbgAssert(0, "invalid I2C frequency");
+ break;
+ };
+
+#if NRF5_I2C_USE_I2C0
+ nvicEnableVector(I2C0_IRQ_NUM, I2C0_IRQ_PRI);
+#endif
+
+#if NRF5_I2C_USE_I2C1
+ nvicEnableVector(I2C1_IRQ_NUM, I2C1_IRQ_PRI);
+#endif
+
+ i2c->INTENSET = TWIM_INTENSET_STOPPED_Msk | TWIM_INTENSET_ERROR_Msk;
+
+ i2c->ENABLE = TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos;
+}
+
+/**
+ * @brief Deactivates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_stop(I2CDriver *i2cp) {
+ NRF_TWIM_Type *i2c = i2cp->i2c;
+ const I2CConfig *cfg = i2cp->config;
+
+ if (i2cp->state != I2C_STOP) {
+ i2c->SHORTS = 0;
+
+ i2c->INTENCLR = TWIM_INTENCLR_STOPPED_Msk | TWIM_INTENCLR_ERROR_Msk;
+
+#if NRF5_I2C_USE_I2C0
+ nvicDisableVector(I2C0_IRQ_NUM);
+#endif
+
+#if NRF5_I2C_USE_I2C1
+ nvicDisableVector(I2C1_IRQ_NUM);
+#endif
+
+ i2c->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos;
+
+ IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF_CLR;
+ IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF_CLR;
+ }
+}
+
+static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout) {
+
+ NRF_TWIM_Type *i2c = i2cp->i2c;
+ msg_t msg;
+
+ i2cp->errors = I2C_NO_ERROR;
+ i2cp->addr = addr;
+
+ uint8_t tx_bytes = txbytes;
+ uint8_t rx_bytes = rxbytes;
+
+ i2cp->i2c->SHORTS = 0;
+ i2c->ADDRESS = addr;
+
+ if (tx_bytes && rx_bytes) {
+ i2c->TXD.PTR = (uint32_t)txbuf;
+ i2c->TXD.MAXCNT = tx_bytes;
+ i2c->TXD.LIST = TWIM_TXD_LIST_LIST_ArrayList << TWIM_TXD_LIST_LIST_Pos;
+ i2c->RXD.PTR = (uint32_t)rxbuf;
+ i2c->RXD.MAXCNT = rx_bytes;
+ i2c->RXD.LIST = TWIM_RXD_LIST_LIST_ArrayList << TWIM_RXD_LIST_LIST_Pos;
+ i2cp->i2c->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Enabled << TWIM_SHORTS_LASTTX_STARTRX_Pos |
+ TWIM_SHORTS_LASTRX_STOP_Enabled << TWIM_SHORTS_LASTRX_STOP_Pos;
+ i2c->TASKS_STARTTX = 1;
+ } else if (tx_bytes && !rx_bytes) {
+ i2c->TXD.PTR = (uint32_t)txbuf;
+ i2c->TXD.MAXCNT = tx_bytes;
+ i2c->TXD.LIST = TWIM_TXD_LIST_LIST_ArrayList << TWIM_TXD_LIST_LIST_Pos;
+ i2cp->i2c->SHORTS = TWIM_SHORTS_LASTTX_STOP_Enabled << TWIM_SHORTS_LASTTX_STOP_Pos;
+ i2c->TASKS_STARTTX = 1;
+ } else if (!tx_bytes && rx_bytes) {
+ i2c->RXD.PTR = (uint32_t)rxbuf;
+ i2c->RXD.MAXCNT = rx_bytes;
+ i2c->RXD.LIST = TWIM_RXD_LIST_LIST_ArrayList << TWIM_RXD_LIST_LIST_Pos;
+ i2cp->i2c->SHORTS = TWIM_SHORTS_LASTRX_STOP_Enabled << TWIM_SHORTS_LASTRX_STOP_Pos;
+ i2c->TASKS_STARTRX = 1;
+ } else {
+ osalDbgAssert(0, "no bytes to transfer");
+ }
+
+ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
+
+ if (msg == MSG_TIMEOUT)
+ i2c->TASKS_STOP = 1;
+
+ return msg;
+}
+
+/**
+ * @brief Receives data via the I2C bus as master.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval MSG_OK if the function succeeded.
+ * @retval MSG_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ * @retval MSG_TIMEOUT if a timeout occurred before operation end. <b>After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state</b>.
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout) {
+
+ return _i2c_txrx_timeout(i2cp, addr, NULL, 0, rxbuf, rxbytes, timeout);
+}
+
+/**
+ * @brief Transmits data via the I2C bus as master.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address
+ * @param[in] txbuf pointer to the transmit buffer
+ * @param[in] txbytes number of bytes to be transmitted
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval MSG_OK if the function succeeded.
+ * @retval MSG_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ * @retval MSG_TIMEOUT if a timeout occurred before operation end. <b>After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state</b>.
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout) {
+
+ return _i2c_txrx_timeout(i2cp, addr, txbuf, txbytes, rxbuf, rxbytes, timeout);
+}
+
+#endif /* HAL_USE_I2C */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.h b/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.h
new file mode 100644
index 0000000..fb88a5e
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TWIMv1/hal_i2c_lld.h
@@ -0,0 +1,210 @@
+/*
+ Copyright (C) 2018 andru
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+/**
+ * @file NRF5/NRF52832/hal_i2c_lld.h
+ * @brief NRF52 I2C subsystem low level driver header.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#ifndef HAL_I2C_LLD_H
+#define HAL_I2C_LLD_H
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief I2C0 driver enable switch.
+ * @details If set to @p TRUE the support for I2C0 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(NRF5_I2C_USE_I2C0) || defined(__DOXYGEN__)
+#define NRF5_I2C_USE_I2C0 FALSE
+#endif
+
+/**
+ * @brief I2C1 driver enable switch.
+ * @details If set to @p TRUE the support for I2C1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(NRF5_I2C_USE_I2C1) || defined(__DOXYGEN__)
+#define NRF5_I2C_USE_I2C1 FALSE
+#endif
+
+/**
+ * @brief I2C0 interrupt priority level setting.
+ */
+#if !defined(NRF5_I2C_I2C0_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_I2C_I2C0_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief I2C1 interrupt priority level setting.
+ */
+#if !defined(NRF5_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_I2C_I2C1_IRQ_PRIORITY 3
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if NRF5_I2C_USE_I2C0 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_I2C_I2C0_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to I2C0"
+#endif
+
+#if NRF5_I2C_USE_I2C1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_I2C_I2C1_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to I2C1"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/* @brief Type representing I2C address. */
+typedef uint8_t i2caddr_t;
+
+/* @brief Type of I2C Driver condition flags. */
+typedef uint32_t i2cflags_t;
+
+/**
+ * @brief Driver configuration structure.
+ * @note Implementations may extend this structure to contain more,
+ * architecture dependent, fields.
+ */
+
+/**
+ * @brief Driver configuration structure.
+ */
+typedef struct {
+
+ /* @brief Clock to be used for the I2C bus. */
+ uint32_t clock;
+ /* @brief Pad number for SCL */
+ uint8_t scl_pad;
+ /* @brief Pad number for SDA */
+ uint8_t sda_pad;
+
+} I2CConfig;
+
+/**
+ * @brief Type of a structure representing an I2C driver.
+ */
+typedef struct I2CDriver I2CDriver;
+
+/**
+ * @brief Structure representing an I2C driver.
+ */
+struct I2CDriver {
+ /**
+ * @brief Driver state.
+ */
+ i2cstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const I2CConfig *config;
+ /**
+ * @brief Error flags.
+ */
+ i2cflags_t errors;
+#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the bus.
+ */
+ mutex_t mutex;
+#endif /* I2C_USE_MUTUAL_EXCLUSION */
+#if defined(I2C_DRIVER_EXT_FIELDS)
+ I2C_DRIVER_EXT_FIELDS
+#endif
+ /* @brief Thread waiting for I/O completion. */
+ thread_reference_t thread;
+ /* @brief Current slave address without R/W bit. */
+ i2caddr_t addr;
+
+ /* End of the mandatory fields.*/
+
+ /* @brief Low-level register access. */
+ NRF_TWIM_Type *i2c;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Get errors from I2C driver.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(__DOXYGEN__)
+
+#if NRF5_I2C_USE_I2C0
+extern I2CDriver I2CD1;
+#endif
+
+#if NRF5_I2C_USE_I2C1
+extern I2CDriver I2CD2;
+#endif
+
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void i2c_lld_init(void);
+ void i2c_lld_start(I2CDriver *i2cp);
+ void i2c_lld_stop(I2CDriver *i2cp);
+ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout);
+ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ systime_t timeout);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_I2C */
+
+#endif /* HAL_I2C_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/NRF52832/hal_lld.c b/os/hal/ports/NRF5/NRF52832/hal_lld.c
index 500de13..e63e57e 100644
--- a/os/hal/ports/NRF5/NRF52832/hal_lld.c
+++ b/os/hal/ports/NRF5/NRF52832/hal_lld.c
@@ -55,23 +55,37 @@
*/
void hal_lld_init(void)
{
- /* High frequency clock initialisation
+ /* High frequency clock initialization
*/
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
+
#if !defined(NRF5_XTAL_VALUE) && (NRF5_XTAL_VALUE != 32000000)
#error "A 32Mhz crystal is mandatory on nRF52 boards."
#endif
+#if (NRF5_HFCLK_SOURCE == NRF5_HFCLK_HFXO)
+ NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;
+ NRF_CLOCK->TASKS_HFCLKSTART = 1;
+ while (NRF_CLOCK->EVENTS_HFCLKSTARTED == 0);
+#endif
- /* Low frequency clock initialisation
- * Clock is only started if st driver requires it
+ /* Low frequency clock initialization
*/
+#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE)
+#if (NRF5_ST_USE_RTC0 || NRF5_ST_USE_RTC1) && \
+ (NRF5_LFCLK_SOURCE == NRF5_LFCLK_RC)
+#error "A NRF5_SYSTEM_TICKS_AS_RTC requires LFCLK clock to be started."
+#endif
+#endif
+
NRF_CLOCK->TASKS_LFCLKSTOP = 1;
+
+#if (NRF5_LFCLK_SOURCE != NRF5_LFCLK_RC)
NRF_CLOCK->LFCLKSRC = NRF5_LFCLK_SOURCE;
-
-#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) && \
- (NRF5_SYSTEM_TICKS == NRF5_SYSTEM_TICKS_AS_RTC)
+
+ NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
NRF_CLOCK->TASKS_LFCLKSTART = 1;
+ while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
#endif
}
diff --git a/os/hal/ports/NRF5/NRF52832/hal_lld.h b/os/hal/ports/NRF5/NRF52832/hal_lld.h
index 24784d3..a2a8cc3 100644
--- a/os/hal/ports/NRF5/NRF52832/hal_lld.h
+++ b/os/hal/ports/NRF5/NRF52832/hal_lld.h
@@ -60,22 +60,36 @@
/*===========================================================================*/
/**
+ * @brief Select source of High Frequency Clock (HFCLK)
+ * @details Possible values for source are:
+ * 0 : 64 MHz internal oscillator (HFINT)
+ * 1 : 32 MHz external crystal oscillator (HFXO)
+ */
+#if !defined(NRF5_HFCLK_SOURCE) || defined(__DOXYGEN__)
+#define NRF5_HFCLK_SOURCE NRF5_HFCLK_HFINT
+#endif
+
+/**
* @brief Select source of Low Frequency Clock (LFCLK)
* @details Possible values for source are:
* 0 : RC oscillator
- * 1 : External cristal
- * 2 : Synthetized clock from High Frequency Clock (HFCLK)
- * When cristal is not available it's preferable to use the
- * internal RC oscillator that synthezing the clock.
+ * 1 : External crystal
+ * 2 : Synthesized clock from High Frequency Clock (HFCLK)
+ * When crystal is not available it's preferable to use the
+ * internal RC oscillator that synthesizing the clock.
*/
#if !defined(NRF5_LFCLK_SOURCE) || defined(__DOXYGEN__)
-#define NRF5_LFCLK_SOURCE 0
+#define NRF5_LFCLK_SOURCE NRF5_LFCLK_RC
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
+#if (NRF5_HFCLK_SOURCE < 0) || (NRF5_HFCLK_SOURCE > 1)
+#error "Possible value for NRF5_HFCLK_SOURCE are HFINT=0, HFXO=1"
+#endif
+
#if (NRF5_LFCLK_SOURCE < 0) || (NRF5_LFCLK_SOURCE > 2)
#error "Possible value for NRF5_LFCLK_SOURCE are 0=RC, 1=XTAL, 2=Synth"
#endif
@@ -91,6 +105,14 @@
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
+#if 0 // moved to board.h
+#define NRF5_HFCLK_HFINT 0
+#define NRF5_HFCLK_HFXO 1
+
+#define NRF5_LFCLK_RC 0
+#define NRF5_LFCLK_XTAL 1
+#define NRF5_LFCLK_SYNTH 2
+#endif
#include "nvic.h"
diff --git a/os/hal/ports/NRF5/NRF52832/nrf_delay.h b/os/hal/ports/NRF5/NRF52832/nrf_delay.h
index 9b5df64..b73d8ae 100644
--- a/os/hal/ports/NRF5/NRF52832/nrf_delay.h
+++ b/os/hal/ports/NRF5/NRF52832/nrf_delay.h
@@ -1,97 +1,238 @@
-/*
- Copyright (C) 2015 Stephen Caudle
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-/**
- * @file NRF5/NRF52832/nrf_delay.h
- * @brief NRF5 Delay routines
- *
- * @{
- */
-
-#ifndef _NRF_DELAY_H
-#define _NRF_DELAY_H
-
-inline static void nrf_delay_us(uint32_t volatile number_of_us) __attribute__((always_inline));
-inline static void nrf_delay_us(uint32_t volatile number_of_us)
-{
-register uint32_t delay __asm ("r0") = number_of_us;
-__asm volatile (
-".syntax unified\n"
- "1:\n"
- " SUBS %0, %0, #1\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " NOP\n"
- " BNE 1b\n"
- ".syntax divided\n"
- : "+r" (delay));
-}
-#endif //__NRF_DELAY_H
+#ifndef _NRF_DELAY_H
+#define _NRF_DELAY_H
+
+/**
+ * @brief Function for delaying execution for number of microseconds.
+ *
+ * @note NRF52 has instruction cache and because of that delay is not precise.
+ *
+ * @param number_of_ms
+ */
+/*lint --e{438, 522} "Variable not used" "Function lacks side-effects" */
+#if defined ( __CC_ARM )
+
+static __ASM void __INLINE nrf_delay_us(uint32_t volatile number_of_us)
+{
+loop
+ SUBS R0, R0, #1
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+#ifdef NRF52
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+ NOP
+#endif
+ BNE loop
+ BX LR
+}
+
+#elif defined ( __ICCARM__ )
+
+static void __INLINE nrf_delay_us(uint32_t volatile number_of_us)
+{
+__ASM (
+"loop:\n\t"
+ " SUBS R0, R0, #1\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+#ifdef NRF52
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+ " NOP\n\t"
+#endif
+ " BNE.n loop\n\t");
+}
+
+#elif defined ( _WIN32 ) || defined ( __unix ) || defined( __APPLE__ )
+
+__STATIC_INLINE void nrf_delay_us(uint32_t volatile number_of_us);
+
+#ifndef CUSTOM_NRF_DELAY_US
+__STATIC_INLINE void nrf_delay_us(uint32_t volatile number_of_us)
+{}
+#endif
+
+#elif defined ( __GNUC__ )
+
+inline static void nrf_delay_us(uint32_t volatile number_of_us) __attribute__((always_inline));
+inline static void nrf_delay_us(uint32_t volatile number_of_us)
+{
+register uint32_t delay __ASM ("r0") = number_of_us;
+__ASM volatile (
+#ifdef NRF51
+ ".syntax unified\n"
+#endif
+ "1:\n"
+ " SUBS %0, %0, #1\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+#ifdef NRF52
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+ " NOP\n"
+#endif
+ " BNE 1b\n"
+#ifdef NRF51
+ ".syntax divided\n"
+#endif
+ : "+r" (delay));
+}
+#endif
+
+#endif
diff --git a/os/hal/ports/NRF5/NRF52832/platform.mk b/os/hal/ports/NRF5/NRF52832/platform.mk
index e71f591..f1fcfe7 100644
--- a/os/hal/ports/NRF5/NRF52832/platform.mk
+++ b/os/hal/ports/NRF5/NRF52832/platform.mk
@@ -20,7 +20,8 @@ endif
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/GPIOv1/driver.mk
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/UARTv1/driver.mk
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/SPIv1/driver.mk
-include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TWIv1/driver.mk
+include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TWIMv1/driver.mk
+include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/PWMv2/driver.mk
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/WDTv1/driver.mk
include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/RNGv1/driver.mk
diff --git a/os/various/devices_lib/rf/nrf52_radio.c b/os/various/devices_lib/rf/nrf52_radio.c
new file mode 100644
index 0000000..e55870f
--- /dev/null
+++ b/os/various/devices_lib/rf/nrf52_radio.c
@@ -0,0 +1,1111 @@
+/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
+ *
+ * The information contained herein is property of Nordic Semiconductor ASA.
+ * Terms and conditions of usage are described in detail in NORDIC
+ * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
+ *
+ * Licensees are granted free, non-transferable use of the information. NO
+ * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
+ * the file.
+ *
+ * Enhanced ShockBurst proprietary protocol to ChibiOS port
+ *
+ * ported on: 25/10/2018, by andru
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "ch.h"
+#include "hal.h"
+
+#include "nrf52_radio.h"
+
+
+#define BIT_MASK_UINT_8(x) (0xFF >> (8 - (x)))
+#define NRF52_PIPE_COUNT 9
+
+#define RADIO_SHORTS_COMMON ( RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk | \
+ RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk )
+
+// Constant parameters
+#define RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS (48) /**< 2MBit RX wait for ack timeout value. Smallest reliable value - 43 */
+#define RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS (64) /**< 1MBit RX wait for ack timeout value. Smallest reliable value - 59 */
+
+#define NRF52_ADDR_UPDATE_MASK_BASE0 (1 << 0) /*< Mask value to signal updating BASE0 radio address. */
+#define NRF52_ADDR_UPDATE_MASK_BASE1 (1 << 1) /*< Mask value to signal updating BASE1 radio address. */
+#define NRF52_ADDR_UPDATE_MASK_PREFIX (1 << 2) /*< Mask value to signal updating radio prefixes */
+
+#define NRF52_PID_RESET_VALUE 0xFF /**< Invalid PID value which is guaranteed to not collide with any valid PID value. */
+#define NRF52_PID_MAX 3 /**< Maximum value for PID. */
+#define NRF52_CRC_RESET_VALUE 0xFFFF /**< CRC reset value*/
+
+#ifndef NRF52_RADIO_USE_TIMER0
+#define NRF52_RADIO_USE_TIMER0 FALSE
+#endif
+
+#ifndef NRF52_RADIO_USE_TIMER1
+#define NRF52_RADIO_USE_TIMER1 FALSE
+#endif
+
+#ifndef NRF52_RADIO_USE_TIMER2
+#define NRF52_RADIO_USE_TIMER2 FALSE
+#endif
+
+#ifndef NRF52_RADIO_USE_TIMER3
+#define NRF52_RADIO_USE_TIMER3 FALSE
+#endif
+
+#ifndef NRF52_RADIO_USE_TIMER4
+#define NRF52_RADIO_USE_TIMER4 FALSE
+#endif
+
+#ifndef NRF52_RADIO_IRQ_PRIORITY
+#define NRF52_RADIO_IRQ_PRIORITY 3 /**< RADIO interrupt priority. */
+#endif
+
+#ifndef NRF52_RADIO_PPI_TIMER_START
+#error "PPI channel NRF52_RADIO_PPI_TIMER_START need to be defined"
+#endif
+
+#ifndef NRF52_RADIO_PPI_TIMER_STOP
+#error "PPI channel NRF52_RADIO_PPI_TIMER_STOP need to be defined"
+#endif
+
+#ifndef NRF52_RADIO_PPI_RX_TIMEOUT
+#error "PPI channel NRF52_RADIO_PPI_RX_TIMEOUT need to be defined"
+#endif
+
+#ifndef NRF52_RADIO_PPI_TX_START
+#error "PPI channel NRF52_RADIO_PPI_TX_START need to be defined"
+#endif
+
+#if (NRF52_RADIO_USE_TIMER0 == FALSE) && (NRF52_RADIO_USE_TIMER1 == FALSE) && \
+ (NRF52_RADIO_USE_TIMER2 == FALSE) && (NRF52_RADIO_USE_TIMER3 == FALSE) && \
+ (NRF52_RADIO_USE_TIMER4 == FALSE)
+#error "At least one hardware TIMER must be defined"
+#endif
+
+#ifndef NRF52_RADIO_INTTHD_PRIORITY
+#error "Interrupt handle thread priority need to be defined"
+#endif
+
+#ifndef NRF52_RADIO_EVTTHD_PRIORITY
+#error "Event thread priority need to be defined"
+#endif
+
+#define VERIFY_PAYLOAD_LENGTH(p) \
+do \
+{ \
+ if(p->length == 0 || \
+ p->length > NRF52_MAX_PAYLOAD_LENGTH || \
+ (RFD1.config.protocol == NRF52_PROTOCOL_ESB && \
+ p->length > RFD1.config.payload_length)) \
+ { \
+ return NRF52_ERROR_INVALID_LENGTH; \
+ } \
+}while(0)
+
+//Structure holding pipe info PID and CRC and ack payload.
+typedef struct
+{
+ uint16_t m_crc;
+ uint8_t m_pid;
+ uint8_t m_ack_payload;
+} pipe_info_t;
+
+// First in first out queue of payloads to be transmitted.
+typedef struct
+{
+ nrf52_payload_t * p_payload[NRF52_TX_FIFO_SIZE]; /**< Pointer to the actual queue. */
+ uint32_t entry_point; /**< Current start of queue. */
+ uint32_t exit_point; /**< Current end of queue. */
+ uint32_t count; /**< Current number of elements in the queue. */
+} nrf52_payload_tx_fifo_t;
+
+// First in first out queue of received payloads.
+typedef struct
+{
+ nrf52_payload_t * p_payload[NRF52_RX_FIFO_SIZE]; /**< Pointer to the actual queue. */
+ uint32_t entry_point; /**< Current start of queue. */
+ uint32_t exit_point; /**< Current end of queue. */
+ uint32_t count; /**< Current number of elements in the queue. */
+} nrf52_payload_rx_fifo_t;
+
+// These function pointers are changed dynamically, depending on protocol configuration and state.
+//static void (*on_radio_end)(RFDriver *rfp) = NULL;
+static void (*set_rf_payload_format)(RFDriver *rfp, uint32_t payload_length) = NULL;
+
+// The following functions are assigned to the function pointers above.
+static void on_radio_disabled_tx_noack(RFDriver *rfp);
+static void on_radio_disabled_tx(RFDriver *rfp);
+static void on_radio_disabled_tx_wait_for_ack(RFDriver *rfp);
+static void on_radio_disabled_rx(RFDriver *rfp);
+static void on_radio_disabled_rx_ack(RFDriver *rfp);
+
+static volatile uint16_t wait_for_ack_timeout_us;
+static nrf52_payload_t * p_current_payload;
+
+// TX FIFO
+static nrf52_payload_t tx_fifo_payload[NRF52_TX_FIFO_SIZE];
+static nrf52_payload_tx_fifo_t tx_fifo;
+
+// RX FIFO
+static nrf52_payload_t rx_fifo_payload[NRF52_RX_FIFO_SIZE];
+static nrf52_payload_rx_fifo_t rx_fifo;
+
+// Payload buffers
+static uint8_t tx_payload_buffer[NRF52_MAX_PAYLOAD_LENGTH + 2];
+static uint8_t rx_payload_buffer[NRF52_MAX_PAYLOAD_LENGTH + 2];
+
+static uint8_t pids[NRF52_PIPE_COUNT];
+static pipe_info_t rx_pipe_info[NRF52_PIPE_COUNT];
+
+ // disable and events semaphores.
+static binary_semaphore_t disable_sem;
+static binary_semaphore_t events_sem;
+
+RFDriver RFD1;
+
+// Function to do bytewise bit-swap on a unsigned 32 bit value
+static uint32_t bytewise_bit_swap(uint8_t const * p_inp) {
+ uint32_t inp = (*(uint32_t*)p_inp);
+
+ return __REV((uint32_t)__RBIT(inp)); //lint -esym(628, __rev) -esym(526, __rev) -esym(628, __rbit) -esym(526, __rbit) */
+}
+
+// Internal function to convert base addresses from nRF24L type addressing to nRF52 type addressing
+static uint32_t addr_conv(uint8_t const* p_addr) {
+ return __REV(bytewise_bit_swap(p_addr)); //lint -esym(628, __rev) -esym(526, __rev) */
+}
+
+static thread_t *rfEvtThread_p;
+static THD_WORKING_AREA(waRFEvtThread, 64);
+static THD_FUNCTION(rfEvtThread, arg) {
+ (void)arg;
+
+ chRegSetThreadName("rfevent");
+
+ while (!chThdShouldTerminateX()) {
+ chBSemWait(&events_sem);
+
+ nrf52_int_flags_t interrupts = RFD1.flags;
+ RFD1.flags = 0;
+
+ if (interrupts & NRF52_INT_TX_SUCCESS_MSK) {
+ chEvtBroadcastFlags(&RFD1.eventsrc, (eventflags_t) NRF52_EVENT_TX_SUCCESS);
+ }
+ if (interrupts & NRF52_INT_TX_FAILED_MSK) {
+ chEvtBroadcastFlags(&RFD1.eventsrc, (eventflags_t) NRF52_EVENT_TX_FAILED);
+ }
+ if (interrupts & NRF52_INT_RX_DR_MSK) {
+ chEvtBroadcastFlags(&RFD1.eventsrc, (eventflags_t) NRF52_EVENT_RX_RECEIVED);
+ }
+ }
+ chThdExit((msg_t) 0);
+}
+
+static thread_t *rfIntThread_p;
+static THD_WORKING_AREA(waRFIntThread, 64);
+static THD_FUNCTION(rfIntThread, arg) {
+ (void)arg;
+
+ chRegSetThreadName("rfint");
+
+ while (!chThdShouldTerminateX()) {
+ chBSemWait(&disable_sem);
+ switch (RFD1.state) {
+ case NRF52_STATE_PTX_TX:
+ on_radio_disabled_tx_noack(&RFD1);
+ break;
+ case NRF52_STATE_PTX_TX_ACK:
+ on_radio_disabled_tx(&RFD1);
+ break;
+ case NRF52_STATE_PTX_RX_ACK:
+ on_radio_disabled_tx_wait_for_ack(&RFD1);
+ break;
+ case NRF52_STATE_PRX:
+ on_radio_disabled_rx(&RFD1);
+ break;
+ case NRF52_STATE_PRX_SEND_ACK:
+ on_radio_disabled_rx_ack(&RFD1);
+ break;
+ default:
+ break;
+ }
+ }
+ chThdExit((msg_t) 0);
+}
+
+static void serve_radio_interrupt(RFDriver *rfp) {
+ (void) rfp;
+ if ((NRF_RADIO->INTENSET & RADIO_INTENSET_READY_Msk) && NRF_RADIO->EVENTS_READY) {
+ NRF_RADIO->EVENTS_READY = 0;
+ (void) NRF_RADIO->EVENTS_READY;
+ }
+ if ((NRF_RADIO->INTENSET & RADIO_INTENSET_DISABLED_Msk) && NRF_RADIO->EVENTS_DISABLED) {
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+ chSysLockFromISR();
+ chBSemSignalI(&disable_sem);
+ chSysUnlockFromISR();
+ }
+}
+
+/**
+ * @brief RADIO events interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector44) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ serve_radio_interrupt(&RFD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+static void set_rf_payload_format_esb_dpl(RFDriver *rfp, uint32_t payload_length) {
+ (void)payload_length;
+#if (NRF52_MAX_PAYLOAD_LENGTH <= 32)
+ // Using 6 bits for length
+ NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) |
+ (6 << RADIO_PCNF0_LFLEN_Pos) |
+ (3 << RADIO_PCNF0_S1LEN_Pos) ;
+#else
+ // Using 8 bits for length
+ NRF_RADIO->PCNF0 = (0 << RADIO_PCNF0_S0LEN_Pos) |
+ (8 << RADIO_PCNF0_LFLEN_Pos) |
+ (3 << RADIO_PCNF0_S1LEN_Pos) ;
+#endif
+ NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
+ (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
+ ((rfp->config.address.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) |
+ (0 << RADIO_PCNF1_STATLEN_Pos) |
+ (NRF52_MAX_PAYLOAD_LENGTH << RADIO_PCNF1_MAXLEN_Pos);
+}
+
+static void set_rf_payload_format_esb(RFDriver *rfp, uint32_t payload_length) {
+ NRF_RADIO->PCNF0 = (1 << RADIO_PCNF0_S0LEN_Pos) |
+ (0 << RADIO_PCNF0_LFLEN_Pos) |
+ (1 << RADIO_PCNF0_S1LEN_Pos);
+
+ NRF_RADIO->PCNF1 = (RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos) |
+ (RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) |
+ ((rfp->config.address.addr_length - 1) << RADIO_PCNF1_BALEN_Pos) |
+ (payload_length << RADIO_PCNF1_STATLEN_Pos) |
+ (payload_length << RADIO_PCNF1_MAXLEN_Pos);
+}
+
+/* Set BASE0 and BASE1 addresses & prefixes registers
+ * NRF52 { prefixes[0], base0_addr[0], base0_addr[1], base0_addr[2], base0_addr[3] } ==
+ * NRF24 { addr[0], addr[1], addr[2], addr[3], addr[4] }
+ */
+static void set_addresses(RFDriver *rfp, uint8_t update_mask) {
+ if (update_mask & NRF52_ADDR_UPDATE_MASK_BASE0) {
+ NRF_RADIO->BASE0 = addr_conv(rfp->config.address.base_addr_p0);
+ NRF_RADIO->DAB[0] = addr_conv(rfp->config.address.base_addr_p0);
+ }
+
+ if (update_mask & NRF52_ADDR_UPDATE_MASK_BASE1) {
+ NRF_RADIO->BASE1 = addr_conv(rfp->config.address.base_addr_p1);
+ NRF_RADIO->DAB[1] = addr_conv(rfp->config.address.base_addr_p1);
+ }
+
+ if (update_mask & NRF52_ADDR_UPDATE_MASK_PREFIX) {
+ NRF_RADIO->PREFIX0 = bytewise_bit_swap(&rfp->config.address.pipe_prefixes[0]);
+ NRF_RADIO->DAP[0] = bytewise_bit_swap(&rfp->config.address.pipe_prefixes[0]);
+ NRF_RADIO->PREFIX1 = bytewise_bit_swap(&rfp->config.address.pipe_prefixes[4]);
+ NRF_RADIO->DAP[1] = bytewise_bit_swap(&rfp->config.address.pipe_prefixes[4]);
+ }
+}
+
+static void set_tx_power(RFDriver *rfp) {
+ NRF_RADIO->TXPOWER = rfp->config.tx_power << RADIO_TXPOWER_TXPOWER_Pos;
+}
+
+static void set_bitrate(RFDriver *rfp) {
+ NRF_RADIO->MODE = rfp->config.bitrate << RADIO_MODE_MODE_Pos;
+
+ switch (rfp->config.bitrate) {
+ case NRF52_BITRATE_2MBPS:
+ wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_2MBPS;
+ break;
+ case NRF52_BITRATE_1MBPS:
+ wait_for_ack_timeout_us = RX_WAIT_FOR_ACK_TIMEOUT_US_1MBPS;
+ break;
+ }
+}
+
+static void set_protocol(RFDriver *rfp) {
+ switch (rfp->config.protocol) {
+ case NRF52_PROTOCOL_ESB_DPL:
+ set_rf_payload_format = set_rf_payload_format_esb_dpl;
+ break;
+ case NRF52_PROTOCOL_ESB:
+ set_rf_payload_format = set_rf_payload_format_esb;
+ break;
+ }
+}
+
+static void set_crc(RFDriver *rfp) {
+ NRF_RADIO->CRCCNF = rfp->config.crc << RADIO_CRCCNF_LEN_Pos;
+
+ if (rfp->config.crc == RADIO_CRCCNF_LEN_Two)
+ {
+ NRF_RADIO->CRCINIT = 0xFFFFUL; // Initial value
+ NRF_RADIO->CRCPOLY = 0x11021UL; // CRC poly: x^16+x^12^x^5+1
+ }
+ else if (rfp->config.crc == RADIO_CRCCNF_LEN_One)
+ {
+ NRF_RADIO->CRCINIT = 0xFFUL; // Initial value
+ NRF_RADIO->CRCPOLY = 0x107UL; // CRC poly: x^8+x^2^x^1+1
+ }
+}
+
+static void ppi_init(RFDriver *rfp) {
+ NRF_PPI->CH[NRF52_RADIO_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_READY;
+ NRF_PPI->CH[NRF52_RADIO_PPI_TIMER_START].TEP = (uint32_t)&rfp->timer->TASKS_START;
+
+ NRF_PPI->CH[NRF52_RADIO_PPI_TIMER_STOP].EEP = (uint32_t)&NRF_RADIO->EVENTS_ADDRESS;
+ NRF_PPI->CH[NRF52_RADIO_PPI_TIMER_STOP].TEP = (uint32_t)&rfp->timer->TASKS_STOP;
+
+ NRF_PPI->CH[NRF52_RADIO_PPI_RX_TIMEOUT].EEP = (uint32_t)&rfp->timer->EVENTS_COMPARE[0];
+ NRF_PPI->CH[NRF52_RADIO_PPI_RX_TIMEOUT].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE;
+
+ NRF_PPI->CH[NRF52_RADIO_PPI_TX_START].EEP = (uint32_t)&rfp->timer->EVENTS_COMPARE[1];
+ NRF_PPI->CH[NRF52_RADIO_PPI_TX_START].TEP = (uint32_t)&NRF_RADIO->TASKS_TXEN;
+}
+
+static void set_parameters(RFDriver *rfp) {
+ set_tx_power(rfp);
+ set_bitrate(rfp);
+ set_protocol(rfp);
+ set_crc(rfp);
+ set_rf_payload_format(rfp, rfp->config.payload_length);
+}
+
+static void reset_fifo(void) {
+ tx_fifo.entry_point = 0;
+ tx_fifo.exit_point = 0;
+ tx_fifo.count = 0;
+
+ rx_fifo.entry_point = 0;
+ rx_fifo.exit_point = 0;
+ rx_fifo.count = 0;
+}
+
+static void init_fifo(void) {
+ uint8_t i;
+ reset_fifo();
+
+ for (i = 0; i < NRF52_TX_FIFO_SIZE; i++) {
+ tx_fifo.p_payload[i] = &tx_fifo_payload[i];
+ }
+
+ for (i = 0; i < NRF52_RX_FIFO_SIZE; i++) {
+ rx_fifo.p_payload[i] = &rx_fifo_payload[i];
+ }
+}
+
+static void tx_fifo_remove_last(void) {
+ if (tx_fifo.count > 0) {
+ nvicDisableVector(RADIO_IRQn);
+
+ tx_fifo.count--;
+ if (++tx_fifo.exit_point >= NRF52_TX_FIFO_SIZE) {
+ tx_fifo.exit_point = 0;
+ }
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+ }
+}
+
+/** @brief Function to push the content of the rx_buffer to the RX FIFO.
+ *
+ * The module will point the register NRF_RADIO->PACKETPTR to a buffer for receiving packets.
+ * After receiving a packet the module will call this function to copy the received data to
+ * the RX FIFO.
+ *
+ * @param pipe Pipe number to set for the packet.
+ * @param pid Packet ID.
+ *
+ * @retval true Operation successful.
+ * @retval false Operation failed.
+ */
+static bool rx_fifo_push_rfbuf(RFDriver *rfp, uint8_t pipe, uint8_t pid) {
+ if (rx_fifo.count < NRF52_RX_FIFO_SIZE) {
+ if (rfp->config.protocol == NRF52_PROTOCOL_ESB_DPL) {
+ if (rx_payload_buffer[0] > NRF52_MAX_PAYLOAD_LENGTH) {
+ return false;
+ }
+
+ rx_fifo.p_payload[rx_fifo.entry_point]->length = rx_payload_buffer[0];
+ }
+ else if (rfp->state == NRF52_STATE_PTX_RX_ACK) {
+ // Received packet is an acknowledgment
+ rx_fifo.p_payload[rx_fifo.entry_point]->length = 0;
+ }
+ else {
+ rx_fifo.p_payload[rx_fifo.entry_point]->length = rfp->config.payload_length;
+ }
+
+ memcpy(rx_fifo.p_payload[rx_fifo.entry_point]->data, &rx_payload_buffer[2],
+ rx_fifo.p_payload[rx_fifo.entry_point]->length);
+
+ rx_fifo.p_payload[rx_fifo.entry_point]->pipe = pipe;
+ rx_fifo.p_payload[rx_fifo.entry_point]->rssi = NRF_RADIO->RSSISAMPLE;
+ rx_fifo.p_payload[rx_fifo.entry_point]->pid = pid;
+ if (++rx_fifo.entry_point >= NRF52_RX_FIFO_SIZE) {
+ rx_fifo.entry_point = 0;
+ }
+ rx_fifo.count++;
+
+ return true;
+ }
+
+ return false;
+}
+
+static void timer_init(RFDriver *rfp) {
+ // Configure the system timer with a 1 MHz base frequency
+ rfp->timer->PRESCALER = 4;
+ rfp->timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit;
+ rfp->timer->SHORTS = TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk;
+}
+
+static void start_tx_transaction(RFDriver *rfp) {
+ bool ack;
+
+ rfp->tx_attempt = 1;
+ rfp->tx_remaining = rfp->config.retransmit.count;
+
+ // Prepare the payload
+ p_current_payload = tx_fifo.p_payload[tx_fifo.exit_point];
+
+ // Handling ack if noack is set to false or if selctive auto ack is turned turned off
+ ack = !p_current_payload->noack || !rfp->config.selective_auto_ack;
+
+ switch (rfp->config.protocol) {
+ case NRF52_PROTOCOL_ESB:
+ set_rf_payload_format(rfp, p_current_payload->length);
+ tx_payload_buffer[0] = p_current_payload->pid;
+ tx_payload_buffer[1] = 0;
+ memcpy(&tx_payload_buffer[2], p_current_payload->data, p_current_payload->length);
+
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk;
+ NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk;
+
+ // Configure the retransmit counter
+ rfp->tx_remaining = rfp->config.retransmit.count;
+ rfp->state = NRF52_STATE_PTX_TX_ACK;
+ break;
+
+ case NRF52_PROTOCOL_ESB_DPL:
+ tx_payload_buffer[0] = p_current_payload->length;
+ tx_payload_buffer[1] = p_current_payload->pid << 1;
+ tx_payload_buffer[1] |= ack ? 0x00 : 0x01;
+ memcpy(&tx_payload_buffer[2], p_current_payload->data, p_current_payload->length);
+
+ if (ack) {
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk;
+ NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk | RADIO_INTENSET_READY_Msk;
+
+ // Configure the retransmit counter
+ rfp->tx_remaining = rfp->config.retransmit.count;
+ rfp->state = NRF52_STATE_PTX_TX_ACK;
+ }
+ else {
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON;
+ NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
+ rfp->state = NRF52_STATE_PTX_TX;
+ }
+ break;
+ }
+
+ NRF_RADIO->TXADDRESS = p_current_payload->pipe;
+ NRF_RADIO->RXADDRESSES = 1 << p_current_payload->pipe;
+
+ NRF_RADIO->FREQUENCY = rfp->config.address.rf_channel;
+ NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
+
+ NRF_RADIO->EVENTS_READY = 0;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void)NRF_RADIO->EVENTS_READY;
+ (void)NRF_RADIO->EVENTS_DISABLED;
+
+ nvicClearPending(RADIO_IRQn);
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ NRF_RADIO->TASKS_TXEN = 1;
+}
+
+static void on_radio_disabled_tx_noack(RFDriver *rfp) {
+ rfp->flags |= NRF52_INT_TX_SUCCESS_MSK;
+ tx_fifo_remove_last();
+
+ chBSemSignal(&events_sem);
+
+ if (tx_fifo.count == 0) {
+ rfp->state = NRF52_STATE_IDLE;
+ }
+ else {
+ start_tx_transaction(rfp);
+ }
+}
+
+static void on_radio_disabled_tx(RFDriver *rfp) {
+ // Remove the DISABLED -> RXEN shortcut, to make sure the radio stays
+ // disabled after the RX window
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON;
+
+ // Make sure the timer is started the next time the radio is ready,
+ // and that it will disable the radio automatically if no packet is
+ // received by the time defined in m_wait_for_ack_timeout_us
+ rfp->timer->CC[0] = wait_for_ack_timeout_us + 130;
+ rfp->timer->CC[1] = rfp->config.retransmit.delay - 130;
+ rfp->timer->TASKS_CLEAR = 1;
+ rfp->timer->EVENTS_COMPARE[0] = 0;
+ rfp->timer->EVENTS_COMPARE[1] = 0;
+ (void)rfp->timer->EVENTS_COMPARE[0];
+ (void)rfp->timer->EVENTS_COMPARE[1];
+
+ NRF_PPI->CHENSET = (1 << NRF52_RADIO_PPI_TIMER_START) |
+ (1 << NRF52_RADIO_PPI_RX_TIMEOUT) |
+ (1 << NRF52_RADIO_PPI_TIMER_STOP);
+ NRF_PPI->CHENCLR = (1 << NRF52_RADIO_PPI_TX_START);
+
+ NRF_RADIO->EVENTS_END = 0;
+ (void)NRF_RADIO->EVENTS_END;
+
+ if (rfp->config.protocol == NRF52_PROTOCOL_ESB) {
+ set_rf_payload_format(rfp, 0);
+ }
+
+ NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
+ rfp->state = NRF52_STATE_PTX_RX_ACK;
+}
+
+static void on_radio_disabled_tx_wait_for_ack(RFDriver *rfp) {
+ // This marks the completion of a TX_RX sequence (TX with ACK)
+
+ // Make sure the timer will not deactivate the radio if a packet is received
+ NRF_PPI->CHENCLR = (1 << NRF52_RADIO_PPI_TIMER_START) |
+ (1 << NRF52_RADIO_PPI_RX_TIMEOUT) |
+ (1 << NRF52_RADIO_PPI_TIMER_STOP);
+
+ // If the radio has received a packet and the CRC status is OK
+ if (NRF_RADIO->EVENTS_END && NRF_RADIO->CRCSTATUS != 0) {
+ rfp->timer->TASKS_STOP = 1;
+ NRF_PPI->CHENCLR = (1 << NRF52_RADIO_PPI_TX_START);
+ rfp->flags |= NRF52_INT_TX_SUCCESS_MSK;
+ rfp->tx_attempt++;// = rfp->config.retransmit.count - rfp->tx_remaining + 1;
+
+ tx_fifo_remove_last();
+
+ if (rfp->config.protocol != NRF52_PROTOCOL_ESB && rx_payload_buffer[0] > 0) {
+ if (rx_fifo_push_rfbuf(rfp, (uint8_t)NRF_RADIO->TXADDRESS, 0)) {
+ rfp->flags |= NRF52_INT_RX_DR_MSK;
+ }
+ }
+
+ chBSemSignal(&events_sem);
+
+ if ((tx_fifo.count == 0) || (rfp->config.tx_mode == NRF52_TXMODE_MANUAL)) {
+ rfp->state = NRF52_STATE_IDLE;
+ }
+ else {
+ start_tx_transaction(rfp);
+ }
+ }
+ else {
+ if (rfp->tx_remaining-- == 0) {
+ rfp->timer->TASKS_STOP = 1;
+ NRF_PPI->CHENCLR = (1 << NRF52_RADIO_PPI_TX_START);
+ // All retransmits are expended, and the TX operation is suspended
+ rfp->tx_attempt = rfp->config.retransmit.count + 1;
+ rfp->flags |= NRF52_INT_TX_FAILED_MSK;
+
+ chBSemSignal(&events_sem);
+
+ rfp->state = NRF52_STATE_IDLE;
+ }
+ else {
+ // There are still have more retransmits left, TX mode should be
+ // entered again as soon as the system timer reaches CC[1].
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk;
+ set_rf_payload_format(rfp, p_current_payload->length);
+ NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
+ rfp->state = NRF52_STATE_PTX_TX_ACK;
+ rfp->timer->TASKS_START = 1;
+ NRF_PPI->CHENSET = (1 << NRF52_RADIO_PPI_TX_START);
+ if (rfp->timer->EVENTS_COMPARE[1])
+ NRF_RADIO->TASKS_TXEN = 1;
+ }
+ }
+}
+
+static void clear_events_restart_rx(RFDriver *rfp) {
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON;
+ set_rf_payload_format(rfp, rfp->config.payload_length);
+ NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
+
+ NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+
+ NRF_RADIO->TASKS_DISABLE = 1;
+ while (NRF_RADIO->EVENTS_DISABLED == 0);
+
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+ NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
+
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk;
+ NRF_RADIO->TASKS_RXEN = 1;
+}
+
+static void on_radio_disabled_rx(RFDriver *rfp) {
+ bool ack = false;
+ bool retransmit_payload = false;
+ bool send_rx_event = true;
+ pipe_info_t * p_pipe_info;
+
+ if (NRF_RADIO->CRCSTATUS == 0) {
+ clear_events_restart_rx(rfp);
+ return;
+ }
+
+ if(rx_fifo.count >= NRF52_RX_FIFO_SIZE) {
+ clear_events_restart_rx(rfp);
+ return;
+ }
+
+ p_pipe_info = &rx_pipe_info[NRF_RADIO->RXMATCH];
+ if (NRF_RADIO->RXCRC == p_pipe_info->m_crc &&
+ (rx_payload_buffer[1] >> 1) == p_pipe_info->m_pid ) {
+ retransmit_payload = true;
+ send_rx_event = false;
+ }
+
+ p_pipe_info->m_pid = rx_payload_buffer[1] >> 1;
+ p_pipe_info->m_crc = NRF_RADIO->RXCRC;
+
+ if(rfp->config.selective_auto_ack == false || ((rx_payload_buffer[1] & 0x01) == 0))
+ ack = true;
+
+ if(ack) {
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_RXEN_Msk;
+
+ switch(rfp->config.protocol) {
+ case NRF52_PROTOCOL_ESB_DPL:
+ {
+ if (tx_fifo.count > 0 &&
+ (tx_fifo.p_payload[tx_fifo.exit_point]->pipe == NRF_RADIO->RXMATCH))
+ {
+ // Pipe stays in ACK with payload until TX fifo is empty
+ // Do not report TX success on first ack payload or retransmit
+ if (p_pipe_info->m_ack_payload != 0 && !retransmit_payload) {
+ if(++tx_fifo.exit_point >= NRF52_TX_FIFO_SIZE) {
+ tx_fifo.exit_point = 0;
+ }
+
+ tx_fifo.count--;
+
+ // ACK payloads also require TX_DS
+ // (page 40 of the 'nRF24LE1_Product_Specification_rev1_6.pdf').
+ rfp->flags |= NRF52_INT_TX_SUCCESS_MSK;
+ }
+
+ p_pipe_info->m_ack_payload = 1;
+
+ p_current_payload = tx_fifo.p_payload[tx_fifo.exit_point];
+
+ set_rf_payload_format(rfp, p_current_payload->length);
+ tx_payload_buffer[0] = p_current_payload->length;
+ memcpy(&tx_payload_buffer[2],
+ p_current_payload->data,
+ p_current_payload->length);
+ }
+ else {
+ p_pipe_info->m_ack_payload = 0;
+ set_rf_payload_format(rfp, 0);
+ tx_payload_buffer[0] = 0;
+ }
+
+ tx_payload_buffer[1] = rx_payload_buffer[1];
+ }
+ break;
+
+ case NRF52_PROTOCOL_ESB:
+ {
+ set_rf_payload_format(rfp, 0);
+ tx_payload_buffer[0] = rx_payload_buffer[0];
+ tx_payload_buffer[1] = 0;
+ }
+ break;
+ }
+
+ rfp->state = NRF52_STATE_PRX_SEND_ACK;
+ NRF_RADIO->TXADDRESS = NRF_RADIO->RXMATCH;
+ NRF_RADIO->PACKETPTR = (uint32_t)tx_payload_buffer;
+ }
+ else {
+ clear_events_restart_rx(rfp);
+ }
+
+ if (send_rx_event) {
+ // Push the new packet to the RX buffer and trigger a received event if the operation was
+ // successful.
+ if (rx_fifo_push_rfbuf(rfp, NRF_RADIO->RXMATCH, p_pipe_info->m_pid)) {
+ rfp->flags |= NRF52_INT_RX_DR_MSK;
+ chBSemSignal(&events_sem);
+ }
+ }
+}
+
+static void on_radio_disabled_rx_ack(RFDriver *rfp) {
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk;
+ set_rf_payload_format(rfp, rfp->config.payload_length);
+
+ NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
+
+ rfp->state = NRF52_STATE_PRX;
+}
+
+nrf52_error_t radio_disable(void) {
+ RFD1.state = NRF52_STATE_IDLE;
+
+ // Clear PPI
+ NRF_PPI->CHENCLR = (1 << NRF52_RADIO_PPI_TIMER_START) |
+ (1 << NRF52_RADIO_PPI_TIMER_STOP) |
+ (1 << NRF52_RADIO_PPI_RX_TIMEOUT);
+
+ reset_fifo();
+
+ memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
+ memset(pids, 0, sizeof(pids));
+
+ // Disable the radio
+ NRF_RADIO->SHORTS = RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos |
+ RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos;
+
+ nvicDisableVector(RADIO_IRQn);
+
+ // Terminate interrupts handle thread
+ chThdTerminate(rfIntThread_p);
+ chBSemSignal(&disable_sem);
+ chThdWait(rfIntThread_p);
+
+ // Terminate events handle thread
+ chThdTerminate(rfEvtThread_p);
+ RFD1.flags = 0;
+ chBSemSignal(&events_sem);
+ chThdWait(rfEvtThread_p);
+
+ RFD1.state = NRF52_STATE_UNINIT;
+
+ return NRF52_SUCCESS;
+}
+
+//
+nrf52_error_t radio_init(nrf52_config_t const *config) {
+ osalDbgAssert(config != NULL,
+ "config must be defined");
+ osalDbgAssert(&config->address != NULL,
+ "address must be defined");
+ osalDbgAssert(NRF52_RADIO_IRQ_PRIORITY <= 7,
+ "wrong radio irq priority");
+
+ if (RFD1.state != NRF52_STATE_UNINIT) {
+ nrf52_error_t err = radio_disable();
+ if (err != NRF52_SUCCESS)
+ return err;
+ }
+
+ RFD1.radio = NRF_RADIO;
+ RFD1.config = *config;
+ RFD1.flags = 0;
+
+ init_fifo();
+
+#if NRF52_RADIO_USE_TIMER0
+ RFD1.timer = NRF_TIMER0;
+#endif
+#if NRF52_RADIO_USE_TIMER1
+ RFD1.timer = NRF_TIMER1;
+#endif
+#if NRF52_RADIO_USE_TIMER2
+ RFD1.timer = NRF_TIMER2;
+#endif
+#if NRF52_RADIO_USE_TIMER3
+ RFD1.timer = NRF_TIMER3;
+#endif
+#if NRF52_RADIO_USE_TIMER4
+ RFD1.timer = NRF_TIMER4;
+#endif
+
+ set_parameters(&RFD1);
+
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_BASE0);
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_BASE1);
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_PREFIX);
+
+ ppi_init(&RFD1);
+ timer_init(&RFD1);
+
+ chBSemObjectInit(&disable_sem, TRUE);
+ chBSemObjectInit(&events_sem, TRUE);
+
+ chEvtObjectInit(&RFD1.eventsrc);
+
+ // interrupt handle thread
+ rfIntThread_p = chThdCreateStatic(waRFIntThread, sizeof(waRFIntThread),
+ NRF52_RADIO_INTTHD_PRIORITY, rfIntThread, NULL);
+
+ // events handle thread
+ rfEvtThread_p = chThdCreateStatic(waRFEvtThread, sizeof(waRFEvtThread),
+ NRF52_RADIO_EVTTHD_PRIORITY, rfEvtThread, NULL);
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ RFD1.state = NRF52_STATE_IDLE;
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_write_payload(nrf52_payload_t const * p_payload) {
+ if (RFD1.state == NRF52_STATE_UNINIT)
+ return NRF52_INVALID_STATE;
+ if(p_payload == NULL)
+ return NRF52_ERROR_NULL;
+ VERIFY_PAYLOAD_LENGTH(p_payload);
+ if (tx_fifo.count >= NRF52_TX_FIFO_SIZE)
+ return NRF52_ERROR_INVALID_LENGTH;
+
+ if (RFD1.config.mode == NRF52_MODE_PTX &&
+ p_payload->noack && !RFD1.config.selective_auto_ack )
+ {
+ return NRF52_ERROR_NOT_SUPPORTED;
+ }
+
+ nvicDisableVector(RADIO_IRQn);
+
+ memcpy(tx_fifo.p_payload[tx_fifo.entry_point], p_payload, sizeof(nrf52_payload_t));
+
+ pids[p_payload->pipe] = (pids[p_payload->pipe] + 1) % (NRF52_PID_MAX + 1);
+ tx_fifo.p_payload[tx_fifo.entry_point]->pid = pids[p_payload->pipe];
+
+ if (++tx_fifo.entry_point >= NRF52_TX_FIFO_SIZE) {
+ tx_fifo.entry_point = 0;
+ }
+
+ tx_fifo.count++;
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ if (RFD1.config.mode == NRF52_MODE_PTX &&
+ RFD1.config.tx_mode == NRF52_TXMODE_AUTO &&
+ RFD1.state == NRF52_STATE_IDLE)
+ {
+ start_tx_transaction(&RFD1);
+ }
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_read_rx_payload(nrf52_payload_t * p_payload) {
+ if (RFD1.state == NRF52_STATE_UNINIT)
+ return NRF52_INVALID_STATE;
+ if (p_payload == NULL)
+ return NRF52_ERROR_NULL;
+
+ if (rx_fifo.count == 0) {
+ return NRF52_ERROR_INVALID_LENGTH;
+ }
+
+ nvicDisableVector(RADIO_IRQn);
+
+ p_payload->length = rx_fifo.p_payload[rx_fifo.exit_point]->length;
+ p_payload->pipe = rx_fifo.p_payload[rx_fifo.exit_point]->pipe;
+ p_payload->rssi = rx_fifo.p_payload[rx_fifo.exit_point]->rssi;
+ p_payload->pid = rx_fifo.p_payload[rx_fifo.exit_point]->pid;
+ memcpy(p_payload->data, rx_fifo.p_payload[rx_fifo.exit_point]->data, p_payload->length);
+
+ if (++rx_fifo.exit_point >= NRF52_RX_FIFO_SIZE) {
+ rx_fifo.exit_point = 0;
+ }
+
+ rx_fifo.count--;
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_start_tx(void) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+
+ if (tx_fifo.count == 0) {
+ return NRF52_ERROR_INVALID_LENGTH;
+ }
+
+ start_tx_transaction(&RFD1);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_start_rx(void) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+
+ NRF_RADIO->INTENCLR = 0xFFFFFFFF;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+
+ NRF_RADIO->SHORTS = RADIO_SHORTS_COMMON | RADIO_SHORTS_DISABLED_TXEN_Msk;
+ NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
+ RFD1.state = NRF52_STATE_PRX;
+
+ NRF_RADIO->RXADDRESSES = RFD1.config.address.rx_pipes;
+ NRF_RADIO->FREQUENCY = RFD1.config.address.rf_channel;
+ NRF_RADIO->PACKETPTR = (uint32_t)rx_payload_buffer;
+
+ nvicClearPending(RADIO_IRQn);
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ NRF_RADIO->EVENTS_ADDRESS = 0;
+ NRF_RADIO->EVENTS_PAYLOAD = 0;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_ADDRESS;
+ (void) NRF_RADIO->EVENTS_PAYLOAD;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+
+ NRF_RADIO->TASKS_RXEN = 1;
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_stop_rx(void) {
+ if (RFD1.state != NRF52_STATE_PRX) {
+ return NRF52_INVALID_STATE;
+ }
+
+ NRF_RADIO->SHORTS = 0;
+ NRF_RADIO->INTENCLR = 0xFFFFFFFF;
+ NRF_RADIO->EVENTS_DISABLED = 0;
+ (void) NRF_RADIO->EVENTS_DISABLED;
+ NRF_RADIO->TASKS_DISABLE = 1;
+ while (NRF_RADIO->EVENTS_DISABLED == 0);
+ RFD1.state = NRF52_STATE_IDLE;
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_flush_tx(void) {
+ if (RFD1.state == NRF52_STATE_UNINIT)
+ return NRF52_INVALID_STATE;
+
+ nvicDisableVector(RADIO_IRQn);
+
+ tx_fifo.count = 0;
+ tx_fifo.entry_point = 0;
+ tx_fifo.exit_point = 0;
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_pop_tx(void) {
+ if (RFD1.state == NRF52_STATE_UNINIT)
+ return NRF52_INVALID_STATE;
+ if (tx_fifo.count == 0)
+ return NRF52_ERROR_INVALID_LENGTH;
+
+ nvicDisableVector(RADIO_IRQn);
+
+ if (++tx_fifo.entry_point >= NRF52_TX_FIFO_SIZE) {
+ tx_fifo.entry_point = 0;
+ }
+ tx_fifo.count--;
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_flush_rx(void) {
+ if (RFD1.state == NRF52_STATE_UNINIT)
+ return NRF52_INVALID_STATE;
+
+ nvicDisableVector(RADIO_IRQn);
+
+ rx_fifo.count = 0;
+ rx_fifo.entry_point = 0;
+ rx_fifo.exit_point = 0;
+
+ memset(rx_pipe_info, 0, sizeof(rx_pipe_info));
+
+ nvicEnableVector(RADIO_IRQn, NRF52_RADIO_IRQ_PRIORITY);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_set_base_address_0(uint8_t const * p_addr) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+ if (p_addr == NULL)
+ return NRF52_ERROR_NULL;
+
+ memcpy(RFD1.config.address.base_addr_p0, p_addr, 4);
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_BASE0);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_set_base_address_1(uint8_t const * p_addr) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+ if (p_addr == NULL)
+ return NRF52_ERROR_NULL;
+
+ memcpy(RFD1.config.address.base_addr_p1, p_addr, 4);
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_BASE1);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_set_prefixes(uint8_t const * p_prefixes, uint8_t num_pipes) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+ if (p_prefixes == NULL)
+ return NRF52_ERROR_NULL;
+ if (num_pipes > 8)
+ return NRF52_ERROR_INVALID_PARAM;
+
+ memcpy(RFD1.config.address.pipe_prefixes, p_prefixes, num_pipes);
+ RFD1.config.address.num_pipes = num_pipes;
+ RFD1.config.address.rx_pipes = BIT_MASK_UINT_8(num_pipes);
+
+ set_addresses(&RFD1, NRF52_ADDR_UPDATE_MASK_PREFIX);
+
+ return NRF52_SUCCESS;
+}
+
+nrf52_error_t radio_set_prefix(uint8_t pipe, uint8_t prefix) {
+ if (RFD1.state != NRF52_STATE_IDLE)
+ return NRF52_ERROR_BUSY;
+ if (pipe > 8)
+ return NRF52_ERROR_INVALID_PARAM;
+
+ RFD1.config.address.pipe_prefixes[pipe] = prefix;
+
+ NRF_RADIO->PREFIX0 = bytewise_bit_swap(&RFD1.config.address.pipe_prefixes[0]);
+ NRF_RADIO->PREFIX1 = bytewise_bit_swap(&RFD1.config.address.pipe_prefixes[4]);
+
+ return NRF52_SUCCESS;
+}
diff --git a/os/various/devices_lib/rf/nrf52_radio.h b/os/various/devices_lib/rf/nrf52_radio.h
new file mode 100644
index 0000000..2f94465
--- /dev/null
+++ b/os/various/devices_lib/rf/nrf52_radio.h
@@ -0,0 +1,256 @@
+/* Copyright (c) 2014 Nordic Semiconductor. All Rights Reserved.
+ *
+ * The information contained herein is property of Nordic Semiconductor ASA.
+ * Terms and conditions of usage are described in detail in NORDIC
+ * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
+ *
+ * Licensees are granted free, non-transferable use of the information. NO
+ * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
+ * the file.
+ *
+ * @brief Enhanced ShockBurst (ESB) is a basic protocol supporting two-way data
+ * packet communication including packet buffering, packet acknowledgment
+ * and automatic retransmission of lost packets.
+ *
+ * ported on: 25/10/2018, by andru
+ *
+ */
+
+#ifndef NRF52_RADIO_H_
+#define NRF52_RADIO_H_
+
+// Hard coded parameters - change if necessary
+#ifndef NRF52_MAX_PAYLOAD_LENGTH
+#define NRF52_MAX_PAYLOAD_LENGTH 32 /**< The max size of the payload. Valid values are 1 to 252 */
+#endif
+
+#define NRF52_CRC_RESET_VALUE 0xFFFF /**< CRC reset value*/
+
+#define NRF52_TX_FIFO_SIZE 8 /**< The size of the transmission first in first out buffer. */
+#define NRF52_RX_FIFO_SIZE 8 /**< The size of the reception first in first out buffer. */
+
+#define NRF52_RADIO_USE_TIMER0 FALSE /**< TIMER0 will be used by the module. */
+#define NRF52_RADIO_USE_TIMER1 TRUE /**< TIMER1 will be used by the module. */
+#define NRF52_RADIO_USE_TIMER2 FALSE /**< TIMER2 will be used by the module. */
+#define NRF52_RADIO_USE_TIMER3 FALSE /**< TIMER3 will be used by the module. */
+#define NRF52_RADIO_USE_TIMER4 FALSE /**< TIMER4 will be used by the module. */
+
+#define NRF52_RADIO_IRQ_PRIORITY 3 /**< RADIO interrupt priority. */
+#define NRF52_RADIO_INTTHD_PRIORITY (NORMALPRIO+2) /**< Interrupts handle thread priority. */
+#define NRF52_RADIO_EVTTHD_PRIORITY (NORMALPRIO+1) /**< Events handle thread priority */
+
+#define NRF52_RADIO_PPI_TIMER_START 10 /**< The PPI channel used for timer start. */
+#define NRF52_RADIO_PPI_TIMER_STOP 11 /**< The PPI channel used for timer stop. */
+#define NRF52_RADIO_PPI_RX_TIMEOUT 12 /**< The PPI channel used for RX timeout. */
+#define NRF52_RADIO_PPI_TX_START 13 /**< The PPI channel used for starting TX. */
+
+
+typedef enum {
+ NRF52_SUCCESS, /* Call was successful. */
+ NRF52_INVALID_STATE, /* Module is not initialized. */
+ NRF52_ERROR_BUSY, /* Module was not in idle state. */
+ NRF52_ERROR_NULL, /* Required parameter was NULL. */
+ NRF52_ERROR_INVALID_PARAM, /* Required parameter is invalid */
+ NRF52_ERROR_NOT_SUPPORTED, /* p_payload->noack was false while selective ack was not enabled. */
+ NRF52_ERROR_INVALID_LENGTH, /* Payload length was invalid (zero or larger than max allowed). */
+} nrf52_error_t;
+
+// Internal radio module state.
+typedef enum {
+ NRF52_STATE_UNINIT, /**< Module not initialized. */
+ NRF52_STATE_IDLE, /**< Module idle. */
+ NRF52_STATE_PTX_TX, /**< Module transmitting without ack. */
+ NRF52_STATE_PTX_TX_ACK, /**< Module transmitting with ack. */
+ NRF52_STATE_PTX_RX_ACK, /**< Module transmitting with ack and reception of payload with the ack response. */
+ NRF52_STATE_PRX, /**< Module receiving packets without ack. */
+ NRF52_STATE_PRX_SEND_ACK, /**< Module transmitting ack in RX mode. */
+} nrf52_state_t;
+
+/**@brief Events to indicate the last transmission/receiving status. */
+typedef enum {
+ NRF52_EVENT_TX_SUCCESS = 0x01, /**< Event triggered on TX success. */
+ NRF52_EVENT_TX_FAILED = 0x02, /**< Event triggered on TX failed. */
+ NRF52_EVENT_RX_RECEIVED = 0x04, /**< Event triggered on RX Received. */
+} nrf52_event_t;
+
+// Interrupt flags
+typedef enum {
+ NRF52_INT_TX_SUCCESS_MSK = 0x01, /**< The flag used to indicate a success since last event. */
+ NRF52_INT_TX_FAILED_MSK = 0x02, /**< The flag used to indicate a failiure since last event. */
+ NRF52_INT_RX_DR_MSK = 0x04, /**< The flag used to indicate a received packet since last event. */
+} nrf52_int_flags_t;
+
+/**Macro to create initializer for a TX data packet.
+ *
+ * @details This macro generates an initializer. It is more efficient
+ * than setting the individual parameters dynamically.
+ *
+ * @param[in] _pipe The pipe to use for the data packet.
+ * @param[in] ... Comma separated list of character data to put in the TX buffer.
+ * Supported values are from 1 to 63 characters.
+ *
+ * @return Initializer that sets up pipe, length and the byte array for content of the TX data.
+ */
+#define NRF52_CREATE_PAYLOAD(_pipe, ...) \
+ {.pipe = _pipe, .length = NUM_VA_ARGS(__VA_ARGS__), .data = {__VA_ARGS__}}; \
+ STATIC_ASSERT(NUM_VA_ARGS(__VA_ARGS__) > 0 && NUM_VA_ARGS(__VA_ARGS__) <= 63)
+
+/**@brief Enhanced ShockBurst protocol. */
+typedef enum {
+ NRF52_PROTOCOL_ESB, /*< Enhanced ShockBurst with fixed payload length. */
+ NRF52_PROTOCOL_ESB_DPL /*< Enhanced ShockBurst with dynamic payload length. */
+} nrf52_protocol_t;
+
+/**@brief Enhanced ShockBurst mode. */
+typedef enum {
+ NRF52_MODE_PTX, /*< Primary transmitter mode. */
+ NRF52_MODE_PRX /*< Primary receiver mode. */
+} nrf52_mode_t;
+
+/**@brief Enhanced ShockBurst bitrate mode. */
+typedef enum {
+ NRF52_BITRATE_2MBPS = RADIO_MODE_MODE_Nrf_2Mbit, /**< 2Mbit radio mode. */
+ NRF52_BITRATE_1MBPS = RADIO_MODE_MODE_Nrf_1Mbit, /**< 1Mbit radio mode. */
+} nrf52_bitrate_t;
+
+/**@brief Enhanced ShockBurst CRC modes. */
+typedef enum {
+ NRF52_CRC_16BIT = RADIO_CRCCNF_LEN_Two, /**< Use two byte CRC. */
+ NRF52_CRC_8BIT = RADIO_CRCCNF_LEN_One, /**< Use one byte CRC. */
+ NRF52_CRC_OFF = RADIO_CRCCNF_LEN_Disabled /**< Disable CRC. */
+} nrf52_crc_t;
+
+/**@brief Enhanced ShockBurst radio transmission power modes. */
+typedef enum {
+ NRF52_TX_POWER_4DBM = RADIO_TXPOWER_TXPOWER_Pos4dBm, /**< 4 dBm radio transmit power. */
+ NRF52_TX_POWER_0DBM = RADIO_TXPOWER_TXPOWER_0dBm, /**< 0 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG4DBM = RADIO_TXPOWER_TXPOWER_Neg4dBm, /**< -4 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG8DBM = RADIO_TXPOWER_TXPOWER_Neg8dBm, /**< -8 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG12DBM = RADIO_TXPOWER_TXPOWER_Neg12dBm, /**< -12 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG16DBM = RADIO_TXPOWER_TXPOWER_Neg16dBm, /**< -16 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG20DBM = RADIO_TXPOWER_TXPOWER_Neg20dBm, /**< -20 dBm radio transmit power. */
+ NRF52_TX_POWER_NEG30DBM = RADIO_TXPOWER_TXPOWER_Neg30dBm /**< -30 dBm radio transmit power. */
+} nrf52_tx_power_t;
+
+/**@brief Enhanced ShockBurst transmission modes. */
+typedef enum {
+ NRF52_TXMODE_AUTO, /*< Automatic TX mode - When the TX fifo is non-empty and the radio is idle packets will be sent automatically. */
+ NRF52_TXMODE_MANUAL, /*< Manual TX mode - Packets will not be sent until radio_start_tx() is called. Can be used to ensure consistent packet timing. */
+ NRF52_TXMODE_MANUAL_START /*< Manual start TX mode - Packets will not be sent until radio_start_tx() is called, but transmission will continue automatically until the TX fifo is empty. */
+} nrf52_tx_mode_t;
+
+/**@brief Enhanced ShockBurst addresses.
+ *
+ * @details The module is able to transmit packets with the TX address stored in tx_address.
+ The module can also receive packets from peers with up to eight different tx_addresses
+ stored in esb_addr_p0 - esb_addr_p7. esb_addr_p0 can have 5 arbitrary bytes
+ independent of the other addresses. esb_addr_p1 - esb_addr_p7 will share the
+ same four byte base address found in the last four bytes of esb_addr_p1.
+ They have an independent prefix byte found in esb_addr_p1[0] and esb_addr_p2 -
+ esb_addr_p7.
+*/
+typedef struct {
+ uint8_t base_addr_p0[4]; /**< Base address for pipe 0 encoded in big endian. */
+ uint8_t base_addr_p1[4]; /**< Base address for pipe 1-7 encoded in big endian. */
+ uint8_t pipe_prefixes[8]; /**< Address prefix for pipe P0 to P7. */
+ uint8_t num_pipes; /**< Number of pipes available. */
+ uint8_t addr_length; /**< Length of address including prefix */
+ uint8_t rx_pipes; /**< Bitfield for enabled RX pipes. */
+ uint8_t rf_channel; /**< Which channel is to be used. Must be in range 0 and 125 to be valid. */
+} nrf52_address_t;
+
+/**@brief Enhanced ShockBurst payload.
+ *
+ * @note The payload is used both for transmission and receive with ack and payload.
+*/
+typedef struct
+{
+ uint8_t length; /**< Length of the packet. Should be equal or less than NRF_ESB_MAX_PAYLOAD_LENGTH. */
+ uint8_t pipe; /**< Pipe used for this payload. */
+ int8_t rssi; /**< RSSI for received packet. */
+ uint8_t noack; /**< Flag indicating that this packet will not be acknowledged. */
+ uint8_t pid; /**< PID assigned during communication. */
+ uint8_t data[NRF52_MAX_PAYLOAD_LENGTH]; /**< The payload data. */
+} nrf52_payload_t;
+
+/**@brief Retransmit attempts delay and counter. */
+typedef struct {
+ uint16_t delay; /**< The delay between each retransmission of unacked packets. */
+ uint16_t count; /**< The number of retransmissions attempts before transmission fail. */
+} nrf52_retransmit_t;
+
+/**@brief Main nrf_esb configuration struct. */
+typedef struct {
+ nrf52_protocol_t protocol; /**< Enhanced ShockBurst protocol. */
+ nrf52_mode_t mode; /**< Enhanced ShockBurst default RX or TX mode. */
+
+ // General RF parameters
+ nrf52_bitrate_t bitrate; /**< Enhanced ShockBurst bitrate mode. */
+ nrf52_crc_t crc; /**< Enhanced ShockBurst CRC mode. */
+ nrf52_tx_power_t tx_power; /**< Enhanced ShockBurst radio transmission power mode.*/
+
+ // Control settings
+ nrf52_tx_mode_t tx_mode; /**< Enhanced ShockBurst transmit mode. */
+
+ bool selective_auto_ack; /**< Enable or disable selective auto acknowledgement. */
+
+ nrf52_retransmit_t retransmit; /**< Packet retransmit parameters */
+
+ uint8_t payload_length; /**< Enhanced ShockBurst static payload length */
+
+ nrf52_address_t address; /**< Address parameters structure */
+} nrf52_config_t;
+
+typedef struct {
+ /**
+ * @brief NRF52 radio peripheral.
+ */
+ NRF_RADIO_Type *radio;
+ /**
+ * @brief NRF52 timer peripheral.
+ */
+ NRF_TIMER_Type *timer;
+ /**
+ * @brief Driver state.
+ */
+ nrf52_state_t state;
+ /**
+ * @brief RF parameters.
+ */
+ nrf52_config_t config;
+ /**
+ * @brief Interrupts flag.
+ */
+ nrf52_int_flags_t flags;
+ /**
+ * @brief TX attempt number.
+ */
+ uint16_t tx_attempt;
+ /**
+ * @brief TX retransmits remaining.
+ */
+ uint16_t tx_remaining;
+ /**
+ * @brief Radio events source.
+ */
+ event_source_t eventsrc;
+} RFDriver;
+
+extern RFDriver RFD1;
+
+nrf52_error_t radio_init(nrf52_config_t const *config);
+nrf52_error_t radio_disable(void);
+nrf52_error_t radio_write_payload(nrf52_payload_t const * p_payload);
+nrf52_error_t radio_read_rx_payload(nrf52_payload_t * p_payload);
+nrf52_error_t radio_start_tx(void);
+nrf52_error_t radio_start_rx(void);
+nrf52_error_t radio_stop_rx(void);
+nrf52_error_t radio_flush_tx(void);
+nrf52_error_t radio_flush_rx(void);
+nrf52_error_t radio_pop_tx(void);
+nrf52_error_t radio_set_base_address_0(uint8_t const * p_addr);
+nrf52_error_t radio_set_base_address_1(uint8_t const * p_addr);
+nrf52_error_t radio_set_prefixes(uint8_t const * p_prefixes, uint8_t num_pipes);
+nrf52_error_t radio_set_prefix(uint8_t pipe, uint8_t prefix);
+
+#endif /* NRF52_RADIO_H_ */