aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/ports/NRF5/LLD/TIMERv1
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/ports/NRF5/LLD/TIMERv1')
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/driver.mk13
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c361
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h264
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c749
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.h424
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.c328
-rw-r--r--os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h285
7 files changed, 2424 insertions, 0 deletions
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk b/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
new file mode 100644
index 0000000..333fe3c
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/driver.mk
@@ -0,0 +1,13 @@
+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_gpt_lld.c b/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c
new file mode 100644
index 0000000..aaff432
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c
@@ -0,0 +1,361 @@
+/*
+ ChibiOS - 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 TIMERv1/hal_gpt_lld.c
+ * @brief NRF5 GPT subsystem low level driver source.
+ *
+ * @addtogroup GPT
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_GPT || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define NRF5_TIMER_PRESCALER_NUM 10
+#define NRF5_TIMER_COMPARE_NUM 4
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief GPTD1 driver identifier.
+ * @note The driver GPTD1 allocates the complex timer TIM1 when enabled.
+ */
+#if NRF5_GPT_USE_TIMER0 || defined(__DOXYGEN__)
+GPTDriver GPTD1;
+#endif
+
+/**
+ * @brief GPTD2 driver identifier.
+ * @note The driver GPTD2 allocates the timer TIM2 when enabled.
+ */
+#if NRF5_GPT_USE_TIMER1 || defined(__DOXYGEN__)
+GPTDriver GPTD2;
+#endif
+
+/**
+ * @brief GPTD3 driver identifier.
+ * @note The driver GPTD3 allocates the timer TIM3 when enabled.
+ */
+#if NRF5_GPT_USE_TIMER2 || defined(__DOXYGEN__)
+GPTDriver GPTD3;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static uint8_t prescaler(uint16_t freq)
+{
+ uint8_t i;
+ static const gptfreq_t frequencies[] = {
+ NRF5_GPT_FREQ_16MHZ,
+ NRF5_GPT_FREQ_8MHZ,
+ NRF5_GPT_FREQ_4MHZ,
+ NRF5_GPT_FREQ_2MHZ,
+ NRF5_GPT_FREQ_1MHZ,
+ NRF5_GPT_FREQ_500KHZ,
+ NRF5_GPT_FREQ_250KHZ,
+ NRF5_GPT_FREQ_125KHZ,
+ NRF5_GPT_FREQ_62500HZ,
+ NRF5_GPT_FREQ_31250HZ,
+ };
+
+ for (i = 0; i < NRF5_TIMER_PRESCALER_NUM; i++)
+ if (freq == frequencies[i])
+ return i;
+
+ osalDbgAssert(FALSE, "invalid timer frequency");
+
+ return 0;
+}
+
+/**
+ * @brief Shared IRQ handler.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ */
+static void gpt_lld_serve_interrupt(GPTDriver *gptp) {
+
+ gptp->tim->EVENTS_COMPARE[gptp->cc_int] = 0;
+#if CORTEX_MODEL >= 4
+ (void)gptp->tim->EVENTS_COMPARE[gptp->cc_int];
+#endif
+ if (gptp->state == GPT_ONESHOT)
+ gptp->state = GPT_READY; /* Back in GPT_READY state. */
+ gptp->config->callback(gptp);
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if NRF5_GPT_USE_TIMER0
+/**
+ * @brief TIMER0 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector60) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_GPT_USE_TIMER0 */
+
+#if NRF5_GPT_USE_TIMER1
+/**
+ * @brief TIMER1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector64) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_GPT_USE_TIMER1 */
+
+#if NRF5_GPT_USE_TIMER2
+/**
+ * @brief TIMER2 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector68) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* NRF5_GPT_USE_TIMER2 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level GPT driver initialization.
+ *
+ * @notapi
+ */
+void gpt_lld_init(void) {
+
+#if NRF5_GPT_USE_TIMER0
+ /* Driver initialization.*/
+ GPTD1.tim = NRF_TIMER0;
+ gptObjectInit(&GPTD1);
+#endif
+
+#if NRF5_GPT_USE_TIMER1
+ /* Driver initialization.*/
+ GPTD2.tim = NRF_TIMER1;
+ gptObjectInit(&GPTD2);
+#endif
+
+#if NRF5_GPT_USE_TIMER2
+ /* Driver initialization.*/
+ GPTD3.tim = NRF_TIMER2;
+ gptObjectInit(&GPTD3);
+#endif
+}
+
+/**
+ * @brief Configures and activates the GPT peripheral.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_start(GPTDriver *gptp) {
+
+ NRF_TIMER_Type *tim = gptp->tim;
+
+ if (gptp->state == GPT_STOP) {
+ osalDbgAssert(gptp->cc_int < NRF5_TIMER_COMPARE_NUM,
+ "invalid capture/compare index");
+
+ tim->INTENSET = TIMER_INTENSET_COMPARE0_Msk << gptp->cc_int;
+#if NRF5_GPT_USE_TIMER0
+ if (&GPTD1 == gptp)
+ nvicEnableVector(TIMER0_IRQn, NRF5_GPT_TIMER0_IRQ_PRIORITY);
+#endif
+#if NRF5_GPT_USE_TIMER1
+ if (&GPTD2 == gptp)
+ nvicEnableVector(TIMER1_IRQn, NRF5_GPT_TIMER1_IRQ_PRIORITY);
+#endif
+#if NRF5_GPT_USE_TIMER2
+ if (&GPTD3 == gptp)
+ nvicEnableVector(TIMER2_IRQn, NRF5_GPT_TIMER2_IRQ_PRIORITY);
+#endif
+ }
+
+ /* Prescaler value calculation.*/
+ tim->PRESCALER = prescaler(gptp->config->frequency);
+
+ /* Timer configuration.*/
+ tim->MODE = TIMER_MODE_MODE_Timer << TIMER_MODE_MODE_Pos;
+
+ switch (gptp->config->resolution) {
+
+ case 8:
+ tim->BITMODE = TIMER_BITMODE_BITMODE_08Bit << TIMER_BITMODE_BITMODE_Pos;
+ break;
+
+ case 16:
+ tim->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
+ break;
+
+#if NRF5_GPT_USE_TIMER0
+ case 24:
+ tim->BITMODE = TIMER_BITMODE_BITMODE_24Bit << TIMER_BITMODE_BITMODE_Pos;
+ break;
+
+ case 32:
+ tim->BITMODE = TIMER_BITMODE_BITMODE_32Bit << TIMER_BITMODE_BITMODE_Pos;
+ break;
+#endif
+
+ default:
+ osalDbgAssert(FALSE, "invalid timer resolution");
+ break;
+ };
+}
+
+/**
+ * @brief Deactivates the GPT peripheral.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_stop(GPTDriver *gptp) {
+
+ if (gptp->state == GPT_READY) {
+ gptp->tim->TASKS_SHUTDOWN = 1;
+
+#if NRF5_GPT_USE_TIMER0
+ if (&GPTD1 == gptp)
+ nvicDisableVector(TIMER0_IRQn);
+#endif
+#if NRF5_GPT_USE_TIMER1
+ if (&GPTD2 == gptp)
+ nvicDisableVector(TIMER1_IRQn);
+#endif
+#if NRF5_GPT_USE_TIMER2
+ if (&GPTD3 == gptp)
+ nvicDisableVector(TIMER2_IRQn);
+#endif
+ gptp->tim->INTENCLR = TIMER_INTENSET_COMPARE0_Msk << gptp->cc_int;
+ }
+}
+
+/**
+ * @brief Starts the timer in continuous mode.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ * @param[in] interval period in ticks
+ *
+ * @notapi
+ */
+void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) {
+
+ NRF_TIMER_Type *tim = gptp->tim;
+
+ tim->TASKS_CLEAR = 1;
+ tim->CC[gptp->cc_int] = (uint32_t)(interval - 1); /* Time constant. */
+ if (gptp->state == GPT_ONESHOT)
+ gptp->tim->SHORTS = TIMER_SHORTS_COMPARE0_STOP_Msk << gptp->cc_int;
+ else if (gptp->state == GPT_CONTINUOUS)
+ gptp->tim->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Msk << gptp->cc_int;
+ tim->TASKS_START = 1;
+}
+
+/**
+ * @brief Stops the timer.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_stop_timer(GPTDriver *gptp) {
+
+ gptp->tim->TASKS_STOP = 1;
+}
+
+/**
+ * @brief Starts the timer in one shot mode and waits for completion.
+ * @details This function specifically polls the timer waiting for completion
+ * in order to not have extra delays caused by interrupt servicing,
+ * this function is only recommended for short delays.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ * @param[in] interval time interval in ticks
+ *
+ * @notapi
+ */
+void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) {
+
+ NRF_TIMER_Type *tim = gptp->tim;
+
+ tim->INTENCLR = (1UL << gptp->cc_int) << TIMER_INTENSET_COMPARE0_Pos;
+ tim->TASKS_CLEAR = 1;
+ tim->CC[gptp->cc_int] = (uint32_t)(interval - 1); /* Time constant. */
+ tim->TASKS_START = 1;
+ while (!(tim->INTENSET & (TIMER_INTENSET_COMPARE0_Msk << gptp->cc_int)))
+ ;
+ tim->INTENSET = TIMER_INTENSET_COMPARE0_Msk << gptp->cc_int;
+}
+
+/**
+ * @brief Returns the counter value of GPT peripheral.
+ * @pre The GPT unit must be running in continuous mode.
+ * @note The nature of the counter is not defined, it may count upward
+ * or downward, it could be continuously running or not.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @return The current counter value.
+ *
+ * @notapi
+ */
+gptcnt_t gpt_lld_get_counter(GPTDriver *gptp) {
+
+ gptp->tim->TASKS_CAPTURE[gptp->cc_get] = 1;
+ return gptp->tim->CC[gptp->cc_get];
+}
+
+#endif /* HAL_USE_GPT */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h b/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h
new file mode 100644
index 0000000..d362106
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h
@@ -0,0 +1,264 @@
+/*
+ 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 TIMERv1/gpt_lld.h
+ * @brief NRF5 GPT subsystem low level driver header.
+ *
+ * @addtogroup GPT
+ * @{
+ */
+
+#ifndef HAL_GPT_LLD_H
+#define HAL_GPT_LLD_H
+
+#if HAL_USE_GPT || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief GPTD1 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD1 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_GPT_USE_TIMER0) || defined(__DOXYGEN__)
+#define NRF5_GPT_USE_TIMER0 FALSE
+#endif
+
+/**
+ * @brief GPTD2 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD2 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_GPT_USE_TIMER1) || defined(__DOXYGEN__)
+#define NRF5_GPT_USE_TIMER1 FALSE
+#endif
+
+/**
+ * @brief GPTD3 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD3 is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(NRF5_GPT_USE_TIMER2) || defined(__DOXYGEN__)
+#define NRF5_GPT_USE_TIMER2 FALSE
+#endif
+
+/**
+ * @brief GPTD1 interrupt priority level setting.
+ */
+#if !defined(NRF5_GPT_TIMER0_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_GPT_TIMER0_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief GPTD2 interrupt priority level setting.
+ */
+#if !defined(NRF5_GPT_TIMER1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_GPT_TIMER1_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief GPTD3 interrupt priority level setting.
+ */
+#if !defined(NRF5_GPT_TIMER2_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define NRF5_GPT_TIMER2_IRQ_PRIORITY 3
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !NRF5_GPT_USE_TIMER0 && !NRF5_GPT_USE_TIMER1 && \
+ !NRF5_GPT_USE_TIMER2
+#error "GPT driver activated but no TIMER peripheral assigned"
+#endif
+
+#if NRF5_GPT_USE_TIMER0 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_GPT_TIMER0_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER0"
+#endif
+
+#if NRF5_GPT_USE_TIMER1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_GPT_TIMER1_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER1"
+#endif
+
+#if NRF5_GPT_USE_TIMER2 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_GPT_TIMER2_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIMER2"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief GPT frequency type.
+ */
+typedef enum {
+ NRF5_GPT_FREQ_31250HZ = 31250,
+ NRF5_GPT_FREQ_62500HZ = 62500,
+ NRF5_GPT_FREQ_125KHZ = 125000,
+ NRF5_GPT_FREQ_250KHZ = 250000,
+ NRF5_GPT_FREQ_500KHZ = 500000,
+ NRF5_GPT_FREQ_1MHZ = 1000000,
+ NRF5_GPT_FREQ_2MHZ = 2000000,
+ NRF5_GPT_FREQ_4MHZ = 4000000,
+ NRF5_GPT_FREQ_8MHZ = 8000000,
+ NRF5_GPT_FREQ_16MHZ = 16000000,
+} gptfreq_t;
+
+/**
+ * @brief GPT counter type.
+ */
+typedef uint32_t gptcnt_t;
+
+/**
+ * @brief Driver configuration structure.
+ * @note It could be empty on some architectures.
+ */
+typedef struct {
+ /**
+ * @brief Timer clock in Hz.
+ * @note The low level can use assertions in order to catch invalid
+ * frequency specifications.
+ */
+ gptfreq_t frequency;
+ /**
+ * @brief Timer callback pointer.
+ * @note This callback is invoked on GPT counter events.
+ * @note This callback can be set to @p NULL but in that case the
+ * one-shot mode cannot be used.
+ */
+ gptcallback_t callback;
+ /* End of the mandatory fields.*/
+ /**
+ * @brief The timer resolution in bits (8/16/24/32)
+ * @note The default value of this field is 16 bits
+ * @note The 24 and 32 bit modes are only valid for TIMER0
+ */
+ uint8_t resolution;
+} GPTConfig;
+
+/**
+ * @brief Structure representing a GPT driver.
+ */
+struct GPTDriver {
+ /**
+ * @brief Driver state.
+ */
+ gptstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const GPTConfig *config;
+#if defined(GPT_DRIVER_EXT_FIELDS)
+ GPT_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Pointer to the TIMERx registers block.
+ */
+ NRF_TIMER_Type *tim;
+ /**
+ * @brief Index of the TIMERx capture/compare register used for setting the
+ * interval between compare events.
+ */
+ uint8_t cc_int;
+ /**
+ * @brief Index of the TIMERx capture/compare register used for getting the
+ * current timer counter value.
+ */
+ uint8_t cc_get;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Changes the interval of GPT peripheral.
+ * @details This function changes the interval of a running GPT unit.
+ * @pre The GPT unit must be running in continuous mode.
+ * @post The GPT unit interval is changed to the new value.
+ * @note The function has effect at the next cycle start.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @param[in] interval new cycle time in timer ticks
+ *
+ * @notapi
+ */
+#define gpt_lld_change_interval(gptp, interval) \
+ ((gptp)->tim->CC[(gptp)->cc_int] = (uint32_t)((interval) - 1))
+
+/**
+ * @brief Returns the interval of GPT peripheral.
+ * @pre The GPT unit must be running in continuous mode.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @return The current interval.
+ *
+ * @notapi
+ */
+#define gpt_lld_get_interval(gptp) \
+ ((gptcnt_t)((gptp)->tim->CC[(gptp)->cc_int]) + 1)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if NRF5_GPT_USE_TIMER0 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD1;
+#endif
+
+#if NRF5_GPT_USE_TIMER1 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD2;
+#endif
+
+#if NRF5_GPT_USE_TIMER2 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD3;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void gpt_lld_init(void);
+ void gpt_lld_start(GPTDriver *gptp);
+ void gpt_lld_stop(GPTDriver *gptp);
+ void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period);
+ void gpt_lld_stop_timer(GPTDriver *gptp);
+ void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval);
+ gptcnt_t gpt_lld_get_counter(GPTDriver *gptp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_GPT */
+
+#endif /* HAL_GPT_LLD_H */
+
+/** @} */
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..bca8855
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_icu_lld.c
@@ -0,0 +1,749 @@
+/*
+ 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_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/TIMERv1/hal_st_lld.c b/os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.c
new file mode 100644
index 0000000..20aa6e0
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.c
@@ -0,0 +1,328 @@
+/*
+ ChibiOS - Copyright (C) 2015 Fabio Utzig
+ 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.
+*/
+
+/**
+ * @file TIMERv1/hal_st_lld.c
+ * @brief NRF5 ST subsystem low level driver source.
+ *
+ * @addtogroup ST
+ * @{
+ */
+
+#include "hal.h"
+
+#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__)
+#if NRF5_ST_USE_RTC0 == TRUE
+/**
+ * @brief System Timer vector (RTC0)
+ * @details This interrupt is used for system tick in periodic mode
+ * if selected with NRF5_ST_USE_RTC0
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector6C) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ NRF_RTC0->EVENTS_TICK = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_TICK;
+#endif
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#if NRF5_ST_USE_RTC1 == TRUE
+/**
+ * @brief System Timer vector (RTC1)
+ * @details This interrupt is used for system tick in periodic mode
+ * if selected with NRF5_ST_USE_RTC1
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector84) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ NRF_RTC1->EVENTS_TICK = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_TICK;
+#endif
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#if NRF5_ST_USE_TIMER0 == TRUE
+/**
+ * @brief System Timer vector. (TIMER0)
+ * @details This interrupt is used for system tick in periodic mode
+ * if selected with NRF5_ST_USE_TIMER0
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector60) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Clear timer compare event */
+ if (NRF_TIMER0->EVENTS_COMPARE[0] != 0) {
+ NRF_TIMER0->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_TIMER0->EVENTS_COMPARE[0];
+#endif
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+ }
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
+
+#if (OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING) || defined(__DOXYGEN__)
+#if NRF5_ST_USE_RTC0 == TRUE
+/**
+ * @brief System Timer vector (RTC0)
+ * @details This interrupt is used for freerunning mode (tick-less)
+ * if selected with NRF5_ST_USE_RTC0
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector6C) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ if (NRF_RTC0->EVENTS_COMPARE[0]) {
+ NRF_RTC0->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[0];
+#endif
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+ }
+
+#if OSAL_ST_RESOLUTION == 16
+ if (NRF_RTC0->EVENTS_COMPARE[1]) {
+ NRF_RTC0->EVENTS_COMPARE[1] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[1];
+#endif
+ NRF_RTC0->TASKS_CLEAR = 1;
+ }
+#endif
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#if NRF5_ST_USE_RTC1 == TRUE
+/**
+ * @brief System Timer vector (RTC1)
+ * @details This interrupt is used for freerunning mode (tick-less)
+ * if selected with NRF5_ST_USE_RTC1
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(Vector84) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ if (NRF_RTC1->EVENTS_COMPARE[0]) {
+ NRF_RTC1->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_COMPARE[0];
+#endif
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+ }
+
+#if OSAL_ST_RESOLUTION == 16
+ if (NRF_RTC1->EVENTS_COMPARE[1]) {
+ NRF_RTC1->EVENTS_COMPARE[1] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_COMPARE[1];
+#endif
+ NRF_RTC1->TASKS_CLEAR = 1;
+ }
+#endif
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level ST driver initialization.
+ *
+ * @notapi
+ */
+void st_lld_init(void) {
+#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING
+
+#if NRF5_ST_USE_RTC0 == TRUE
+ /* Using RTC with prescaler */
+ NRF_RTC0->TASKS_STOP = 1;
+ NRF_RTC0->PRESCALER = (NRF5_LFCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1;
+ NRF_RTC0->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk;
+ NRF_RTC0->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[0];
+#endif
+ NRF_RTC0->INTENSET = RTC_INTENSET_COMPARE0_Msk;
+#if OSAL_ST_RESOLUTION == 16
+ NRF_RTC0->CC[1] = 0x10000; /* 2^16 */
+ NRF_RTC0->EVENTS_COMPARE[1] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[1];
+#endif
+ NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
+ NRF_RTC0->INTENSET = RTC_INTENSET_COMPARE1_Msk;
+#endif
+ NRF_RTC0->TASKS_CLEAR = 1;
+
+ /* Start timer */
+ nvicEnableVector(RTC0_IRQn, NRF5_ST_PRIORITY);
+ NRF_RTC0->TASKS_START = 1;
+#endif /* NRF5_ST_USE_RTC0 == TRUE */
+
+#if NRF5_ST_USE_RTC1 == TRUE
+ /* Using RTC with prescaler */
+ NRF_RTC1->TASKS_STOP = 1;
+ NRF_RTC1->PRESCALER = (NRF5_LFCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1;
+ NRF_RTC1->EVTENCLR = RTC_EVTENSET_COMPARE0_Msk;
+ NRF_RTC1->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_COMPARE[0];
+#endif
+ NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE0_Msk;
+#if OSAL_ST_RESOLUTION == 16
+ NRF_RTC1->CC[1] = 0x10000; /* 2^16 */
+ NRF_RTC1->EVENTS_COMPARE[1] = 0;
+#if CORTEX_MODEL >= 4
+ NRF_RTC1->EVENTS_COMPARE[1];
+#endif
+ NRF_RTC1->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
+ NRF_RTC1->INTENSET = RTC_INTENSET_COMPARE1_Msk;
+#endif
+ NRF_RTC1->TASKS_CLEAR = 1;
+
+ /* Start timer */
+ nvicEnableVector(RTC1_IRQn, NRF5_ST_PRIORITY);
+ NRF_RTC1->TASKS_START = 1;
+#endif /* NRF5_ST_USE_RTC1 == TRUE */
+
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */
+
+#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC
+
+#if NRF5_ST_USE_RTC0 == TRUE
+ /* Using RTC with prescaler */
+ NRF_RTC0->TASKS_STOP = 1;
+ NRF_RTC0->PRESCALER = (NRF5_LFCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1;
+ NRF_RTC0->INTENSET = RTC_INTENSET_TICK_Msk;
+
+ /* Start timer */
+ nvicEnableVector(RTC0_IRQn, NRF5_ST_PRIORITY);
+ NRF_RTC0->TASKS_START = 1;
+#endif /* NRF5_ST_USE_RTC0 == TRUE */
+
+#if NRF5_ST_USE_RTC1 == TRUE
+ /* Using RTC with prescaler */
+ NRF_RTC1->TASKS_STOP = 1;
+ NRF_RTC1->PRESCALER = (NRF5_LFCLK_FREQUENCY / OSAL_ST_FREQUENCY) - 1;
+ NRF_RTC1->INTENSET = RTC_INTENSET_TICK_Msk;
+
+ /* Start timer */
+ nvicEnableVector(RTC1_IRQn, NRF5_ST_PRIORITY);
+ NRF_RTC1->TASKS_START = 1;
+#endif /* NRF5_ST_USE_RTC1 == TRUE */
+
+#if NRF5_ST_USE_TIMER0 == TRUE
+ NRF_TIMER0->TASKS_CLEAR = 1;
+
+ /*
+ * Using 32-bit mode with prescaler 1/16 configures this
+ * timer with a 1MHz clock, reducing power consumption.
+ */
+ NRF_TIMER0->BITMODE = TIMER_BITMODE_BITMODE_32Bit;
+ NRF_TIMER0->PRESCALER = 4;
+
+ /*
+ * Configure timer 0 compare capture 0 to generate interrupt
+ * and clear timer value when event is generated.
+ */
+ NRF_TIMER0->CC[0] = (1000000 / OSAL_ST_FREQUENCY) - 1;
+ NRF_TIMER0->SHORTS = 1;
+ NRF_TIMER0->INTENSET = TIMER_INTENSET_COMPARE0_Msk;
+
+ /* Start timer */
+ nvicEnableVector(TIMER0_IRQn, NRF5_ST_PRIORITY);
+ NRF_TIMER0->TASKS_START = 1;
+#endif /* NRF5_ST_USE_TIMER0 == TRUE */
+
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
+}
+
+#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */
+
+/** @} */
diff --git a/os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h b/os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h
new file mode 100644
index 0000000..c026d2b
--- /dev/null
+++ b/os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h
@@ -0,0 +1,285 @@
+/*
+ ChibiOS - Copyright (C) 2015 Fabio Utzig
+
+ 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 TIMERv1/st_lld.h
+ * @brief NRF5 ST subsystem low level driver header.
+ * @details This header is designed to be include-able without having to
+ * include other files from the HAL.
+ *
+ * @addtogroup ST
+ * @{
+ */
+
+#ifndef HAL_ST_LLD_H
+#define HAL_ST_LLD_H
+
+#include "halconf.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Use RTC0 to generates system ticks
+ *
+ * @note Avoid using RTC0, as PPI has pre-programmed channels on it
+ * that can be used to control RADIO or TIMER0
+ */
+#if !defined(NRF5_ST_USE_RTC0) || defined(__DOXYGEN__)
+#define NRF5_ST_USE_RTC0 FALSE
+#endif
+
+/**
+ * @brief Use RTC1 to generates system ticks
+ */
+#if !defined(NRF5_ST_USE_RTC1) || defined(__DOXYGEN__)
+#define NRF5_ST_USE_RTC1 TRUE
+#endif
+
+/**
+ * @brief Use TIMER0 to generates system ticks
+ *
+ * @note Avoid using TIMER0 as it will draw more current
+ */
+#if !defined(NRF5_ST_USE_TIMER0) || defined(__DOXYGEN__)
+#define NRF5_ST_USE_TIMER0 FALSE
+#endif
+
+/**
+ * @brief ST interrupt priority level setting.
+ */
+#if !defined(NRF5_ST_PRIORITY) || defined(__DOXYGEN__)
+#if !defined(SOFTDEVICE_PRESENT)
+#define NRF5_ST_PRIORITY CORTEX_MAX_KERNEL_PRIORITY
+#else
+#define NRF5_ST_PRIORITY 1
+#endif
+#endif
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if OSAL_ST_MODE != OSAL_ST_MODE_NONE
+#if (NRF5_ST_USE_TIMER0 == TRUE) && (NRF5_GPT_USE_TIMER0 == TRUE)
+#error "TIMER0 already used by GPT driver"
+#endif
+
+#if (NRF5_ST_USE_RTC0 == FALSE) && \
+ (NRF5_ST_USE_RTC1 == FALSE) && \
+ (NRF5_ST_USE_TIMER0 == FALSE)
+#error "One clock source is needed, enable one (RTC0, RTC1, or TIMER0)"
+#endif
+
+#if ((NRF5_ST_USE_RTC0 == TRUE ? 1 : 0) + \
+ (NRF5_ST_USE_RTC1 == TRUE ? 1 : 0) + \
+ (NRF5_ST_USE_TIMER0 == TRUE ? 1 : 0)) > 1
+#error "Only one clock source can be used (RTC0, RTC1, or TIMER0)"
+#endif
+
+#if defined(SOFTDEVICE_PRESENT)
+#if NRF5_ST_USE_RTC0 == TRUE
+#error "RTC0 cannot be used for system ticks when SOFTDEVICE present"
+#endif
+
+#if NRF5_ST_USE_TIMER0 == TRUE
+#error "TIMER0 cannot be used for system ticks when SOFTDEVICE present"
+#endif
+
+#if NRF5_ST_PRIORITY != 1
+#error "ST priority must be 1 when SOFTDEVICE present"
+#endif
+
+#endif /* defined(SOFTDEVICE_PRESENT) */
+#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */
+
+#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING
+#if defined(CH_CFG_ST_TIMEDELTA) && (CH_CFG_ST_TIMEDELTA < 5)
+#error "CH_CFG_ST_TIMEDELTA is too low"
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+#error "Freeruning (tick-less) mode not supported with TIMER, use RTC"
+#endif
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */
+
+#if !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ST_PRIORITY)
+#error "Invalid IRQ priority assigned to ST driver"
+#endif
+
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void st_lld_init(void);
+#ifdef __cplusplus
+}
+#endif
+
+/*===========================================================================*/
+/* Driver inline functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Returns the time counter value.
+ *
+ * @return The counter value.
+ *
+ * @notapi
+ */
+static inline systime_t st_lld_get_counter(void) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ return (systime_t)NRF_RTC0->COUNTER;
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ return (systime_t)NRF_RTC1->COUNTER;
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+ return (systime_t)0;
+#endif
+}
+
+/**
+ * @brief Starts the alarm.
+ * @note Makes sure that no spurious alarms are triggered after
+ * this call.
+ *
+ * @param[in] abstime the time to be set for the first alarm
+ *
+ * @notapi
+ */
+static inline void st_lld_start_alarm(systime_t abstime) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ NRF_RTC0->CC[0] = abstime;
+ NRF_RTC0->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[0];
+#endif
+ NRF_RTC0->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ NRF_RTC1->CC[0] = abstime;
+ NRF_RTC1->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_COMPARE[0];
+#endif
+ NRF_RTC1->EVTENSET = RTC_EVTENSET_COMPARE0_Msk;
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+ (void)abstime;
+#endif
+}
+
+/**
+ * @brief Stops the alarm interrupt.
+ *
+ * @notapi
+ */
+static inline void st_lld_stop_alarm(void) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ NRF_RTC0->EVTENCLR = RTC_EVTENCLR_COMPARE0_Msk;
+ NRF_RTC0->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC0->EVENTS_COMPARE[0];
+#endif
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ NRF_RTC1->EVTENCLR = RTC_EVTENCLR_COMPARE0_Msk;
+ NRF_RTC1->EVENTS_COMPARE[0] = 0;
+#if CORTEX_MODEL >= 4
+ (void)NRF_RTC1->EVENTS_COMPARE[0];
+#endif
+#endif
+}
+
+/**
+ * @brief Sets the alarm time.
+ *
+ * @param[in] abstime the time to be set for the next alarm
+ *
+ * @notapi
+ */
+static inline void st_lld_set_alarm(systime_t abstime) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ NRF_RTC0->CC[0] = abstime;
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ NRF_RTC1->CC[0] = abstime;
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+ (void)abstime;
+#endif
+}
+
+/**
+ * @brief Returns the current alarm time.
+ *
+ * @return The currently set alarm time.
+ *
+ * @notapi
+ */
+static inline systime_t st_lld_get_alarm(void) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ return (systime_t)NRF_RTC0->CC[0];
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ return (systime_t)NRF_RTC1->CC[0];
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+ return (systime_t)0;
+#endif
+}
+
+/**
+ * @brief Determines if the alarm is active.
+ *
+ * @return The alarm status.
+ * @retval false if the alarm is not active.
+ * @retval true is the alarm is active
+ *
+ * @notapi
+ */
+static inline bool st_lld_is_alarm_active(void) {
+#if NRF5_ST_USE_RTC0 == TRUE
+ return NRF_RTC0->EVTEN & RTC_EVTEN_COMPARE0_Msk;
+#endif
+#if NRF5_ST_USE_RTC1 == TRUE
+ return NRF_RTC1->EVTEN & RTC_EVTEN_COMPARE0_Msk;
+#endif
+#if NRF5_ST_USE_TIMER0 == TRUE
+ return false;
+#endif
+}
+
+#endif /* HAL_ST_LLD_H */
+
+/** @} */