From ca35947f8132f1879b2706ecceb82c112bfe0f71 Mon Sep 17 00:00:00 2001 From: "Konstantin K. Oblaukhov" Date: Tue, 20 Nov 2018 09:52:01 +0700 Subject: Reorganize NRF5x driver files. --- os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.c | 227 ++++++++++++ os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.h | 229 ++++++++++++ os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.c | 168 +++++++++ os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.h | 139 ++++++++ os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.c | 158 +++++++++ os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.h | 351 ++++++++++++++++++ os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.c | 492 ++++++++++++++++++++++++++ os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.h | 334 +++++++++++++++++ os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.c | 300 ++++++++++++++++ os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.h | 390 ++++++++++++++++++++ os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.c | 169 +++++++++ os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.h | 159 +++++++++ os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.c | 389 ++++++++++++++++++++ os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.h | 238 +++++++++++++ os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c | 361 +++++++++++++++++++ os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h | 264 ++++++++++++++ os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.c | 328 +++++++++++++++++ os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h | 285 +++++++++++++++ os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.c | 467 ++++++++++++++++++++++++ os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.h | 232 ++++++++++++ os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.c | 343 ++++++++++++++++++ os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.h | 155 ++++++++ os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.c | 157 ++++++++ os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.h | 143 ++++++++ os/hal/ports/NRF5/LLD/hal_gpt_lld.c | 361 ------------------- os/hal/ports/NRF5/LLD/hal_gpt_lld.h | 264 -------------- os/hal/ports/NRF5/LLD/hal_i2c_lld.c | 467 ------------------------ os/hal/ports/NRF5/LLD/hal_i2c_lld.h | 232 ------------ os/hal/ports/NRF5/LLD/hal_pal_lld.c | 158 --------- os/hal/ports/NRF5/LLD/hal_pal_lld.h | 351 ------------------ os/hal/ports/NRF5/LLD/hal_qei_lld.c | 300 ---------------- os/hal/ports/NRF5/LLD/hal_qei_lld.h | 390 -------------------- os/hal/ports/NRF5/LLD/hal_rng_lld.c | 169 --------- os/hal/ports/NRF5/LLD/hal_rng_lld.h | 159 --------- os/hal/ports/NRF5/LLD/hal_serial_lld.c | 343 ------------------ os/hal/ports/NRF5/LLD/hal_serial_lld.h | 155 -------- os/hal/ports/NRF5/LLD/hal_spi_lld.c | 389 -------------------- os/hal/ports/NRF5/LLD/hal_spi_lld.h | 238 ------------- os/hal/ports/NRF5/LLD/hal_st_lld.c | 328 ----------------- os/hal/ports/NRF5/LLD/hal_st_lld.h | 285 --------------- os/hal/ports/NRF5/LLD/hal_wdg_lld.c | 157 -------- os/hal/ports/NRF5/LLD/hal_wdg_lld.h | 143 -------- os/hal/ports/NRF5/NRF51822/hal_adc_lld.c | 227 ------------ os/hal/ports/NRF5/NRF51822/hal_adc_lld.h | 229 ------------ os/hal/ports/NRF5/NRF51822/hal_ext_lld.c | 168 --------- os/hal/ports/NRF5/NRF51822/hal_ext_lld.h | 139 -------- os/hal/ports/NRF5/NRF51822/hal_pwm_lld.c | 492 -------------------------- os/hal/ports/NRF5/NRF51822/hal_pwm_lld.h | 334 ----------------- 48 files changed, 6478 insertions(+), 6478 deletions(-) create mode 100644 os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.c create mode 100644 os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.h create mode 100644 os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.c create mode 100644 os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.h create mode 100644 os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.c create mode 100644 os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.h create mode 100644 os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.c create mode 100644 os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.h create mode 100644 os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.c create mode 100644 os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.h create mode 100644 os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.c create mode 100644 os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.h create mode 100644 os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.c create mode 100644 os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.h create mode 100644 os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.c create mode 100644 os/hal/ports/NRF5/LLD/TIMERv1/hal_gpt_lld.h create mode 100644 os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.c create mode 100644 os/hal/ports/NRF5/LLD/TIMERv1/hal_st_lld.h create mode 100644 os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.c create mode 100644 os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.h create mode 100644 os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.c create mode 100644 os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.h create mode 100644 os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.c create mode 100644 os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_gpt_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_gpt_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_i2c_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_i2c_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_pal_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_pal_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_qei_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_qei_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_rng_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_rng_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_serial_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_serial_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_spi_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_spi_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_st_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_st_lld.h delete mode 100644 os/hal/ports/NRF5/LLD/hal_wdg_lld.c delete mode 100644 os/hal/ports/NRF5/LLD/hal_wdg_lld.h delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_adc_lld.c delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_adc_lld.h delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_ext_lld.c delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_ext_lld.h delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_pwm_lld.c delete mode 100644 os/hal/ports/NRF5/NRF51822/hal_pwm_lld.h diff --git a/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.c b/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.c new file mode 100644 index 0000000..e1d8bc6 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.c @@ -0,0 +1,227 @@ +/* + 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 ADCv1/adc_lld.c + * @brief NRF51 ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ +#define ADC_CHANNEL_MASK 0x7 + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if NRF5_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void adc_lld_config_next_channel(ADCDriver *adcp, uint32_t config) { + + /* Default to all analog input pins disabled */ + config &= ~ADC_CONFIG_PSEL_Msk; + + if (adcp->grpp->channel_mask) { + /* Skip to the next channel */ + while (((1 << adcp->current_channel) & adcp->grpp->channel_mask) == 0) + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + config |= (((1 << adcp->current_channel) << ADC_CONFIG_PSEL_Pos) & ADC_CONFIG_PSEL_Msk); + } + + /* Setup analog input pin select and user config values */ + adcp->adc->CONFIG = config; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_ADC_USE_ADC1 || defined(__DOXYGEN__) +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector5C) { + + ADCDriver *adcp = &ADCD1; + NRF_ADC_Type *adc = adcp->adc; + bool more = true; + + OSAL_IRQ_PROLOGUE(); + + /* Clear the ADC event */ + adc->EVENTS_END = 0; + + /* Read the sample into the buffer */ + adcp->samples[adcp->current_index++] = adc->RESULT; + + /* At the end of the buffer then we may be finished */ + if (adcp->current_index == adcp->number_of_samples) { + _adc_isr_full_code(adcp); + + adcp->current_index = 0; + + /* We are never finished in circular mode */ + more = adcp->grpp->circular; + } + + if (more) { + + /* Signal half completion in circular mode. */ + if (adcp->grpp->circular && + (adcp->current_index == (adcp->number_of_samples / 2))) { + + _adc_isr_half_code(adcp); + } + + /* Skip to the next channel */ + adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; + adc_lld_config_next_channel(adcp, adcp->adc->CONFIG); + adcp->adc->TASKS_START = 1; + } else { + adc_lld_stop_conversion(adcp); + } + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if NRF5_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc = NRF_ADC; +#endif +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* If in stopped state then configures and enables the ADC. */ + if (adcp->state == ADC_STOP) { +#if NRF5_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + + adcp->adc->INTENSET = ADC_INTENSET_END_Enabled << ADC_INTENSET_END_Pos; + nvicEnableVector(ADC_IRQn, NRF5_ADC_IRQ_PRIORITY); + } +#endif /* NRF5_ADC_USE_ADC1 */ + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock and analog part.*/ + if (adcp->state == ADC_READY) { + +#if NRF5_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + + nvicDisableVector(ADC_IRQn); + adcp->adc->INTENCLR = ADC_INTENCLR_END_Clear << ADC_INTENCLR_END_Pos; + adc_lld_stop_conversion(adcp); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + + NRF_ADC_Type *adc = adcp->adc; + + adcp->number_of_samples = adcp->depth * adcp->grpp->num_channels; + adcp->current_index = 0; + + /* At least one sample must be configured */ + osalDbgAssert(adcp->number_of_samples, "must configure at least one sample"); + + /* Skip to the next channel */ + adcp->current_channel = 0; + adc_lld_config_next_channel(adcp, adcp->grpp->cfg); + + /* Enable and start the conversion */ + adc->ENABLE = ADC_ENABLE_ENABLE_Enabled << ADC_ENABLE_ENABLE_Pos; + adc->TASKS_START = 1; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + NRF_ADC_Type *adc = adcp->adc; + + adc->TASKS_STOP = 1; + adc->ENABLE = ADC_ENABLE_ENABLE_Disabled << ADC_ENABLE_ENABLE_Pos; +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.h b/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.h new file mode 100644 index 0000000..4e185d5 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/ADCv1/hal_adc_lld.h @@ -0,0 +1,229 @@ +/* + 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 ADCv1/adc_lld.h + * @brief NRF51 ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef HAL_ADC_LLD_H +#define HAL_ADC_LLD_H + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define NRF5_ADC_USE_ADC1 FALSE +#endif + +/** + * @brief ADC interrupt priority level setting. + */ +#if !defined(NRF5_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_ADC_IRQ_PRIORITY 2 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !NRF5_ADC_USE_ADC1 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +#if NRF5_ADC_USE_ADC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ADC_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC1" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint8_t adc_channels_num_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + * @note The use of this configuration structure requires knowledge of + * STM32 ADC cell registers interface, please refer to the STM32 + * reference manual for details. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /* End of the mandatory fields.*/ + /** + * @brief Bitmask of channels for ADC conversion. + */ + uint32_t channel_mask; + /** + * @brief ADC CONFIG register details. + * @note All the required bits must be defined into this field. + */ + uint32_t cfg; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + uint32_t dummy; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADCx registers block. + */ + NRF_ADC_Type *adc; + /** + * @brief Number of samples expected. + */ + size_t number_of_samples; + /** + * @brief Current position in the buffer. + */ + size_t current_index; + /** + * @brief Current channel index into group channel_mask. + */ + size_t current_channel; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if NRF5_ADC_USE_ADC1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* HAL_ADC_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.c b/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.c new file mode 100644 index 0000000..f38fa0c --- /dev/null +++ b/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.c @@ -0,0 +1,168 @@ +/* + 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 GPIOTEv1/ext_lld.c + * @brief NRF51 EXT subsystem low level driver source. + * + * @addtogroup EXT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +#include "hal_ext_lld_isr.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief EXTD1 driver identifier. + */ +EXTDriver EXTD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + + /* Driver initialization.*/ + extObjectInit(&EXTD1); +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { + + unsigned i; + + ext_lld_exti_irq_enable(); + + /* Configuration of automatic channels.*/ + for (i = 0; i < EXT_MAX_CHANNELS; i++) { + uint32_t config = 0; + uint32_t pad = (extp->config->channels[i].mode & EXT_MODE_GPIO_MASK) + >> EXT_MODE_GPIO_OFFSET; + + if (extp->config->channels[i].mode & EXT_CH_MODE_BOTH_EDGES) + config |= (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos); + else if (extp->config->channels[i].mode & EXT_CH_MODE_RISING_EDGE) + config |= (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos); + else + config |= (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos); + + config |= (pad << GPIOTE_CONFIG_PSEL_Pos); + + NRF_GPIOTE->CONFIG[i] = config; + NRF_GPIOTE->EVENTS_PORT = 0; + NRF_GPIOTE->EVENTS_IN[i] = 0; + + if (extp->config->channels[i].mode & EXT_CH_MODE_AUTOSTART) + ext_lld_channel_enable(extp, i); + else + ext_lld_channel_disable(extp, i); + } +} + +/** + * @brief Deactivates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_stop(EXTDriver *extp) { + + unsigned i; + + (void)extp; + ext_lld_exti_irq_disable(); + + for (i = 0; i < EXT_MAX_CHANNELS; i++) + NRF_GPIOTE->CONFIG[i] = 0; + + NRF_GPIOTE->INTENCLR = + (GPIOTE_INTENCLR_IN3_Msk | GPIOTE_INTENCLR_IN2_Msk | + GPIOTE_INTENCLR_IN1_Msk | GPIOTE_INTENCLR_IN0_Msk); +} + +/** + * @brief Enables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be enabled + * + * @notapi + */ +void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { + + uint32_t config = NRF_GPIOTE->CONFIG[channel] & ~GPIOTE_CONFIG_MODE_Msk; + + (void)extp; + config |= (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos); + + NRF_GPIOTE->CONFIG[channel] = config; + NRF_GPIOTE->INTENSET = (1 << channel); +} + +/** + * @brief Disables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be disabled + * + * @notapi + */ +void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { + + (void)extp; + NRF_GPIOTE->CONFIG[channel] &= ~GPIOTE_CONFIG_MODE_Msk; + NRF_GPIOTE->INTENCLR = (1 << channel); +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.h b/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.h new file mode 100644 index 0000000..8a93b6d --- /dev/null +++ b/os/hal/ports/NRF5/LLD/GPIOTEv1/hal_ext_lld.h @@ -0,0 +1,139 @@ +/* + 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 GPIOTEv1/ext_lld.h + * @brief NRF51 EXT subsystem low level driver header. + * + * @addtogroup EXT + * @{ + */ + +#ifndef HAL_EXT_LLD_H +#define HAL_EXT_LLD_H + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Available number of EXT channels. + */ +#define EXT_MAX_CHANNELS 4 +#define EXT_MODE_GPIO_MASK 0xF8 /**< @brief Pad field mask. */ +#define EXT_MODE_GPIO_OFFSET 3 /**< @brief Pad field offset. */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief EXT channel identifier. + */ +typedef uint32_t expchannel_t; + +/** + * @brief Type of an EXT generic notification callback. + * + * @param[in] extp pointer to the @p EXPDriver object triggering the + * callback + */ +typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); + +/** + * @brief Channel configuration structure. + */ +typedef struct { + /** + * @brief Channel mode. + */ + uint32_t mode; + /** + * @brief Channel callback. + * @details In the STM32 implementation a @p NULL callback pointer is + * valid and configures the channel as an event sources instead + * of an interrupt source. + */ + extcallback_t cb; +} EXTChannelConfig; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Channel configurations. + */ + EXTChannelConfig channels[EXT_MAX_CHANNELS]; + /* End of the mandatory fields.*/ +} EXTConfig; + +/** + * @brief Structure representing an EXT driver. + */ +struct EXTDriver { + /** + * @brief Driver state. + */ + extstate_t state; + /** + * @brief Current configuration data. + */ + const EXTConfig *config; + /* End of the mandatory fields.*/ +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern EXTDriver EXTD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void ext_lld_init(void); + void ext_lld_start(EXTDriver *extp); + void ext_lld_stop(EXTDriver *extp); + void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); + void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_EXT */ + +#endif /* HAL_EXT_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.c b/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.c new file mode 100644 index 0000000..9cfad8d --- /dev/null +++ b/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.c @@ -0,0 +1,158 @@ +/* + 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 GPIOv1/hal_pal_lld.c + * @brief NRF5 PAL subsystem low level driver source. + * + * @addtogroup PAL + * @{ + */ + +#include "osal.h" +#include "hal.h" + +#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +void _pal_lld_setpadmode(ioportid_t port, uint8_t pad, iomode_t mode) +{ + (void)port; + osalDbgAssert(pad < PAL_IOPORTS_WIDTH, "pal_lld_setpadmode() - invalid pad"); + + switch (mode) { + case PAL_MODE_RESET: + case PAL_MODE_UNCONNECTED: + IOPORT1->PIN_CNF[pad] = + (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | + (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | + (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | + (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | + (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos); + break; + case PAL_MODE_INPUT: + case PAL_MODE_INPUT_ANALOG: + IOPORT1->PIN_CNF[pad] = + (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | + (GPIO_PIN_CNF_DRIVE_S0S1 << 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); + break; + case PAL_MODE_INPUT_PULLUP: + IOPORT1->PIN_CNF[pad] = + (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | + (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | + (GPIO_PIN_CNF_PULL_Pullup << 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); + break; + case PAL_MODE_INPUT_PULLDOWN: + IOPORT1->PIN_CNF[pad] = + (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | + (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | + (GPIO_PIN_CNF_PULL_Pulldown << 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); + break; + case PAL_MODE_OUTPUT_PUSHPULL: + IOPORT1->PIN_CNF[pad] = + (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | + (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | + (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | + (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | + (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + break; + case PAL_MODE_OUTPUT_OPENDRAIN: + IOPORT1->PIN_CNF[pad] = + (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_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | + (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); + break; + default: + osalDbgAssert(FALSE, "invalid pal mode"); + break; + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief NRF5 I/O ports configuration. + * + * @param[in] config the NRF5 ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) +{ + uint8_t i; + + for (i = 0; i < TOTAL_GPIO_PADS; i++) { + pal_lld_setpadmode(IOPORT1, i, config->pads[i]); + } +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) +{ + uint8_t i; + + for (i = 0; i < TOTAL_GPIO_PADS; i++, mask >>= 1) { + if (mask & 1) { + pal_lld_setpadmode(port, i, mode); + } + } +} + +#endif /* HAL_USE_PAL == TRUE */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.h b/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.h new file mode 100644 index 0000000..4bb14e2 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/GPIOv1/hal_pal_lld.h @@ -0,0 +1,351 @@ +/* + 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 GPIOv1/hal_pal_lld.h + * @brief NRF5 PAL subsystem low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef HAL_PAL_LLD_H +#define HAL_PAL_LLD_H + +#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +#define TOTAL_GPIO_PADS 32 + +/** + * @name Port related definitions + * @{ + */ +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 32U + +/** + * @brief Whole port mask. + * @brief This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFFU) +/** @} */ + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint32_t)(pad))) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((ioportid_t)(IOPORT1)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint32_t)(line)) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE ((ioline_t)-1) +/** @} */ + +/** + * @brief Generic I/O ports static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialized the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + uint32_t pads[TOTAL_GPIO_PADS]; +} PALConfig; + +/** + * @brief Digital I/O port sized unsigned type. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Digital I/O modes. + */ +typedef uint8_t iomode_t; + +/** + * @brief Type of an I/O line. + */ +typedef uint32_t ioline_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef NRF_GPIO_Type *ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/*===========================================================================*/ + +/** + * @brief First I/O port identifier. + * @details Low level drivers can define multiple ports, it is suggested to + * use this naming convention. + */ +#if NRF_SERIES == 51 +#define IOPORT1 NRF_GPIO +#else +#define IOPORT1 NRF_P0 +#endif + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief Low level PAL subsystem initialization. + * + * @param[in] config architecture-dependent ports configuration + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads the physical I/O port states. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) (IOPORT1->IN) + +/** + * @brief Reads the output latch. + * @details The purpose of this function is to read back the latched output + * value. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) (IOPORT1->OUT) + +/** + * @brief Writes a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) (IOPORT1->OUT = (bits)) + +/** + * @brief Sets a bits mask on a I/O port. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) (IOPORT1->OUTSET = (bits)) + + +/** + * @brief Clears a bits mask on a I/O port. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) (IOPORT1->OUTCLR = (bits)) + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Reads a logical state from an I/O pad. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @return The logical state. + * @retval PAL_LOW low logical state. + * @retval PAL_HIGH high logical state. + * + * @notapi + */ +#define pal_lld_readpad(port, pad) \ + ((IOPORT1->IN & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW) + +/** + * @brief Writes a logical state on an output pad. + * @note This function is not meant to be invoked directly by the + * application code. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +#define pal_lld_writepad(port, pad, bit) \ + do { \ + (void)port; \ + if (bit == PAL_HIGH) \ + IOPORT1->OUTSET = ((uint32_t) 1 << pad); \ + else \ + IOPORT1->OUTCLR = ((uint32_t) 1 << pad); \ + } while (false) + +/** + * @brief Sets a pad logical state to @p PAL_HIGH. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_setpad(port, pad) (IOPORT1->OUTSET = (uint32_t) 1 << (pad)) + +/** + * @brief Clears a pad logical state to @p PAL_LOW. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_clearpad(port, pad) (IOPORT1->OUTCLR = (uint32_t) 1 << (pad)) + +/** + * @brief Toggles a pad logical state. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_togglepad(port, pad) \ + do { \ + uint8_t bit = (IOPORT1->OUT >> (pad)) & 1; \ + if (bit) \ + IOPORT1->OUTCLR = 1 << (pad); \ + else \ + IOPORT1->OUTSET = 1 << (pad); \ + } while (0) + +/** + * @brief Pad mode setup. + * @details This function programs a pad with the specified mode. + * @note The @ref PAL provides a default software implementation of this + * functionality, implement this function if can optimize it by using + * special hardware functionalities or special coding. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] mode pad mode + * + * @notapi + */ +#define pal_lld_setpadmode(port, pad, mode) _pal_lld_setpadmode(port, pad, mode) + +#if !defined(__DOXYGEN__) +extern const PALConfig pal_default_config; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); + void _pal_lld_setpadmode(ioportid_t port, + uint8_t pad, + iomode_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL == TRUE */ + +#endif /* HAL_PAL_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.c b/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.c new file mode 100644 index 0000000..d5201b8 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.c @@ -0,0 +1,492 @@ +/* + ChibiOS/HAL - 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. +*/ + +/** + * @file PWMv1/hal_pwm_lld.c + * @brief NRF51 PWM subsystem low level driver source. + * + * @note Using the method described in nrf51-pwm-library to correctly + * handle toggling of the pin with GPIOTE when changing period. + * It means it is generally unsafe to use GPIOTE with a period + * less than (2 * PWM_GPIOTE_DECISION_TIME / 16MHz) + * + * @addtogroup PWM + * @{ + */ + +#include "hal.h" + +#if HAL_USE_PWM || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define PWM_GPIOTE_PPI_CC 3 +#define PWM_GPIOTE_DECISION_TIME 160 + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief PWMD1 driver identifier. + * @note The driver PWMD1 allocates the timer TIMER0 when enabled. + */ +#if NRF5_PWM_USE_TIMER0 || defined(__DOXYGEN__) +PWMDriver PWMD1; +#endif + +/** + * @brief PWMD2 driver identifier. + * @note The driver PWMD2 allocates the timer TIMER1 when enabled. + */ +#if NRF5_PWM_USE_TIMER1 || defined(__DOXYGEN__) +PWMDriver PWMD2; +#endif + +/** + * @brief PWMD3 driver identifier. + * @note The driver PWMD3 allocates the timer TIMER2 when enabled. + */ +#if NRF5_PWM_USE_TIMER2 || defined(__DOXYGEN__) +PWMDriver PWMD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint8_t pwm_margin_by_prescaler[] = { + (PWM_GPIOTE_DECISION_TIME + 0) >> 0, + (PWM_GPIOTE_DECISION_TIME + 1) >> 1, + (PWM_GPIOTE_DECISION_TIME + 3) >> 2, + (PWM_GPIOTE_DECISION_TIME + 7) >> 3, + (PWM_GPIOTE_DECISION_TIME + 15) >> 4, + (PWM_GPIOTE_DECISION_TIME + 31) >> 5, + (PWM_GPIOTE_DECISION_TIME + 63) >> 6, + (PWM_GPIOTE_DECISION_TIME + 127) >> 7, + (PWM_GPIOTE_DECISION_TIME + 255) >> 8, + (PWM_GPIOTE_DECISION_TIME + 511) >> 9 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void pwm_lld_serve_interrupt(PWMDriver *pwmp) { + uint8_t channel; + /* Deal with PWM channels + */ + for (channel = 0 ; channel < pwmp->channels ; channel++) { + if (pwmp->timer->EVENTS_COMPARE[channel]) { + pwmp->timer->EVENTS_COMPARE[channel] = 0; + + if (pwmp->config->channels[channel].callback != NULL) { + pwmp->config->channels[channel].callback(pwmp); + } + } + } + + /* Deal with PWM period + */ + if (pwmp->timer->EVENTS_COMPARE[pwmp->channels]) { + pwmp->timer->EVENTS_COMPARE[pwmp->channels] = 0; + + if (pwmp->config->callback != NULL) { + pwmp->config->callback(pwmp); + } + } +} + +static inline +bool pwm_within_safe_margins(PWMDriver *pwmp, uint32_t timer, uint32_t width) { + const uint32_t margin = pwm_margin_by_prescaler[pwmp->timer->PRESCALER]; + return (width <= margin) + ? ((width <= timer) && (timer < (pwmp->period + width - margin))) + : ((width <= timer) || (timer < (width - margin))); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_PWM_USE_TIMER0 +/** + * @brief TIMER0 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector60) { + OSAL_IRQ_PROLOGUE(); + pwm_lld_serve_interrupt(&PWMD1); + OSAL_IRQ_EPILOGUE(); +} +#endif /* NRF5_PWM_USE_TIMER0 */ + +#if NRF5_PWM_USE_TIMER1 +/** + * @brief TIMER1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector64) { + OSAL_IRQ_PROLOGUE(); + pwm_lld_serve_interrupt(&PWMD2); + OSAL_IRQ_EPILOGUE(); +} +#endif /* NRF5_PWM_USE_TIMER1 */ + +#if NRF5_PWM_USE_TIMER2 +/** + * @brief TIMER2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector68) { + OSAL_IRQ_PROLOGUE(); + pwm_lld_serve_interrupt(&PWMD3); + OSAL_IRQ_EPILOGUE(); +} +#endif /* NRF5_PWM_USE_TIMER2 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level PWM driver initialization. + * + * @notapi + */ +void pwm_lld_init(void) { + +#if NRF5_PWM_USE_TIMER0 + pwmObjectInit(&PWMD1); + PWMD1.channels = PWM_CHANNELS; + PWMD1.timer = NRF_TIMER0; +#endif + +#if NRF5_PWM_USE_TIMER1 + pwmObjectInit(&PWMD2); + PWMD2.channels = PWM_CHANNELS; + PWMD2.timer = NRF_TIMER1; +#endif + +#if NRF5_PWM_USE_TIMER2 + pwmObjectInit(&PWMD3); + PWMD3.channels = PWM_CHANNELS; + PWMD3.timer = NRF_TIMER2; +#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 */ + uint16_t psc_ratio = NRF5_HFCLK_FREQUENCY / pwmp->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 16bits timer (only TIMER0 support 32bits) */ + pwmp->timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit; + pwmp->timer->MODE = TIMER_MODE_MODE_Timer; + + /* With clear shortcuts for period */ + pwmp->timer->SHORTS = + 0x1UL << (TIMER_SHORTS_COMPARE0_CLEAR_Pos + pwmp->channels); + + /* Disable and reset interrupts for compare events */ + pwmp->timer->INTENCLR = (TIMER_INTENCLR_COMPARE0_Msk | + TIMER_INTENCLR_COMPARE1_Msk | + TIMER_INTENCLR_COMPARE2_Msk | + TIMER_INTENCLR_COMPARE3_Msk ); + pwmp->timer->EVENTS_COMPARE[0] = 0; + pwmp->timer->EVENTS_COMPARE[1] = 0; + pwmp->timer->EVENTS_COMPARE[2] = 0; + pwmp->timer->EVENTS_COMPARE[3] = 0; + + /* Set prescaler */ + pwmp->timer->PRESCALER = psc_value; + + /* Set period */ + pwmp->timer->CC[pwmp->channels] = pwmp->period; + + /* Clear everything */ + pwmp->timer->TASKS_CLEAR = 1; + + /* Enable interrupt */ +#if NRF5_PWM_USE_TIMER0 + if (&PWMD1 == pwmp) { + nvicEnableVector(TIMER0_IRQn, NRF5_PWM_TIMER0_PRIORITY); + } +#endif + +#if NRF5_PWM_USE_TIMER1 + if (&PWMD2 == pwmp) { + nvicEnableVector(TIMER1_IRQn, NRF5_PWM_TIMER1_PRIORITY); + } +#endif + +#if NRF5_PWM_USE_TIMER2 + if (&PWMD3 == pwmp) { + nvicEnableVector(TIMER2_IRQn, NRF5_PWM_TIMER2_PRIORITY); + } +#endif + + /* Start timer */ + pwmp->timer->TASKS_START = 1; +} + +/** + * @brief Deactivates the PWM peripheral. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_stop(PWMDriver *pwmp) { + pwmp->timer->TASKS_SHUTDOWN = 1; + +#if NRF5_PWM_USE_TIMER0 + if (&PWMD1 == pwmp) { + nvicDisableVector(TIMER0_IRQn); + } +#endif + +#if NRF5_PWM_USE_TIMER1 + if (&PWMD2 == pwmp) { + nvicDisableVector(TIMER1_IRQn); + } +#endif + +#if NRF5_PWM_USE_TIMER2 + if (&PWMD3 == pwmp) { + nvicDisableVector(TIMER2_IRQn); + } +#endif +} + +/** + * @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) { +#if NRF5_PWM_USE_GPIOTE_PPI + const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel]; + const uint8_t gpiote_channel = cfg_channel->gpiote_channel; + const uint8_t *ppi_channel = cfg_channel->ppi_channel; + + uint32_t outinit; + switch(cfg_channel->mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW : outinit = GPIOTE_CONFIG_OUTINIT_Low; break; + case PWM_OUTPUT_ACTIVE_HIGH: outinit = GPIOTE_CONFIG_OUTINIT_High; break; + case PWM_OUTPUT_DISABLED : /* fall-through */ + default : goto no_output_config; + } + + /* Deal with corner case: 0% and 100% */ + if ((width <= 0) || (width >= pwmp->period)) { + /* Disable GPIOTE/PPI task */ + NRF_GPIOTE->CONFIG[gpiote_channel] = GPIOTE_CONFIG_MODE_Disabled; + NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); + /* Set Line */ + palWriteLine(cfg_channel->ioline, + ((width <= 0) ^ + ((cfg_channel->mode & PWM_OUTPUT_MASK) == PWM_OUTPUT_ACTIVE_HIGH))); + + /* Really doing PWM */ + } else { + const uint32_t gpio_pin = PAL_PAD(cfg_channel->ioline); + const uint32_t polarity = GPIOTE_CONFIG_POLARITY_Toggle; + + /* Program tasks (one for duty cycle, one for periode) */ + NRF_PPI->CH[ppi_channel[0]].EEP = + (uint32_t)&pwmp->timer->EVENTS_COMPARE[channel]; + NRF_PPI->CH[ppi_channel[0]].TEP = + (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; + NRF_PPI->CH[ppi_channel[1]].EEP = + (uint32_t)&pwmp->timer->EVENTS_COMPARE[pwmp->channels]; + NRF_PPI->CH[ppi_channel[1]].TEP = + (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; + NRF_PPI->CHENSET = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); + + /* Something Old, something New */ + const uint32_t old_width = pwmp->timer->CC[channel]; + const uint32_t new_width = width; + + /* Check GPIOTE state */ + const bool gpiote = (NRF_GPIOTE->CONFIG[gpiote_channel] & + GPIOTE_CONFIG_MODE_Msk) != GPIOTE_CONFIG_MODE_Disabled; + + /* GPIOTE is currently running */ + if (gpiote) { + uint32_t current; + while (true) { + pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; + current = pwmp->timer->CC[PWM_GPIOTE_PPI_CC]; + + if (pwm_within_safe_margins(pwmp, current, old_width) && + pwm_within_safe_margins(pwmp, current, new_width)) + break; + } + if (((old_width <= current) && (current < new_width)) || + ((new_width <= current) && (current < old_width))) { + NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; + } + + /* GPIOTE need to be restarted */ + } else { + /* Create GPIO Task */ + NRF_GPIOTE->CONFIG[gpiote_channel] = + (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) | + ((gpio_pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PSEL_Msk )| + ((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk)| + ((outinit << GPIOTE_CONFIG_OUTINIT_Pos ) & GPIOTE_CONFIG_OUTINIT_Msk ); + + pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; + if (pwmp->timer->CC[PWM_GPIOTE_PPI_CC] > width) + NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; + } + } + + no_output_config: +#endif + + pwmp->timer->CC[channel] = width; +} + +/** + * @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) { + pwmp->timer->CC[channel] = 0; +#if NRF5_PWM_USE_GPIOTE_PPI + const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel]; + switch(cfg_channel->mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + case PWM_OUTPUT_ACTIVE_HIGH: { + const uint8_t gpiote_channel = cfg_channel->gpiote_channel; + const uint8_t *ppi_channel = cfg_channel->ppi_channel; + NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); + NRF_GPIOTE->CONFIG[gpiote_channel] = GPIOTE_CONFIG_MODE_Disabled; + break; + } + case PWM_OUTPUT_DISABLED: + default: + break; + } +#endif +} + +/** + * @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) { + pwmp->timer->INTENSET = + 0x1UL << (TIMER_INTENSET_COMPARE0_Pos + pwmp->channels); +} + +/** + * @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->timer->INTENCLR = + 0x1UL << (TIMER_INTENCLR_COMPARE0_Pos + pwmp->channels); +} + +/** + * @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) { + pwmp->timer->INTENSET = + 0x1UL << (TIMER_INTENSET_COMPARE0_Pos + 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) { + pwmp->timer->INTENCLR = + 0x1UL << (TIMER_INTENCLR_COMPARE0_Pos + channel); +} + +#endif /* HAL_USE_PWM */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.h b/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.h new file mode 100644 index 0000000..616e61a --- /dev/null +++ b/os/hal/ports/NRF5/LLD/PWMv1/hal_pwm_lld.h @@ -0,0 +1,334 @@ +/* + ChibiOS/HAL - 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. +*/ + +/** + * @file PWMv1/hal_pwm_lld.h + * @brief NRF51 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. + */ +#if NRF5_PWM_USE_GPIOTE_PPI +#define PWM_CHANNELS 2 +#else +#define PWM_CHANNELS 3 +#endif + +#define PWM_FREQUENCY_16MHZ 16000000 /** @brief 16MHz */ +#define PWM_FREQUENCY_8MHZ 8000000 /** @brief 8MHz */ +#define PWM_FREQUENCY_4MHZ 4000000 /** @brief 4MHz */ +#define PWM_FREQUENCY_2MHZ 2000000 /** @brief 2MHz */ +#define PWM_FREQUENCY_1MHZ 1000000 /** @brief 1MHz */ +#define PWM_FREQUENCY_500KHZ 500000 /** @brief 500kHz */ +#define PWM_FREQUENCY_250KHZ 250000 /** @brief 250kHz */ +#define PWM_FREQUENCY_125KHZ 125000 /** @brief 125kHz */ +#define PWM_FREQUENCY_62500HZ 62500 /** @brief 62500Hz */ +#define PWM_FREQUENCY_31250HZ 31250 /** @brief 31250Hz */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ + +/** + * @brief TIMER0 as driver implementation + */ +#if !defined(NRF5_PWM_USE_TIMER0) +#define NRF5_PWM_USE_TIMER0 FALSE +#endif + +/** + * @brief TIMER1 as driver implementation + */ +#if !defined(NRF5_PWM_USE_TIMER1) +#define NRF5_PWM_USE_TIMER1 FALSE +#endif + +/** + * @brief TIMER2 as driver implementation + */ +#if !defined(NRF5_PWM_USE_TIMER2) +#define NRF5_PWM_USE_TIMER2 FALSE +#endif + +/** + * @brief TIMER0 interrupt priority level setting. + */ +#if !defined(NRF5_PWM_TIMER0_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_PWM_TIMER0_PRIORITY 3 +#endif + +/** + * @brief TIMER1 interrupt priority level setting. + */ +#if !defined(NRF5_PWM_TIMER1_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_PWM_TIMER1_PRIORITY 3 +#endif + +/** + * @brief TIMER2 interrupt priority level setting. + */ +#if !defined(NRF5_PWM_TIMER2_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_PWM_TIMER2_PRIORITY 3 +#endif + +/** + * @brief Allow driver to use GPIOTE/PPI to control PAL line + */ +#if !defined(NRF5_PWM_USE_GPIOTE_PPI) +#define NRF5_PWM_USE_GPIOTE_PPI TRUE +#endif + +/** @} */ + +/*===========================================================================*/ +/* Configuration checks. */ +/*===========================================================================*/ + +#if !NRF5_PWM_USE_TIMER0 && !NRF5_PWM_USE_TIMER1 && !NRF5_PWM_USE_TIMER2 +#error "PWM driver activated but no TIMER peripheral assigned" +#endif + +#if (NRF5_ST_USE_TIMER0 == TRUE) && (NRF5_PWM_USE_TIMER0 == TRUE) +#error "TIMER0 used for ST and PWM" +#endif + +#if NRF5_PWM_USE_TIMER0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER0_PRIORITY) +#error "Invalid IRQ priority assigned to TIMER0" +#endif + +#if NRF5_PWM_USE_TIMER1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER1_PRIORITY) +#error "Invalid IRQ priority assigned to TIMER1" +#endif + +#if NRF5_PWM_USE_TIMER2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER2_PRIORITY) +#error "Invalid IRQ priority assigned to TIMER2" +#endif + + +/*===========================================================================*/ +/* 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.*/ + +#if NRF5_PWM_USE_GPIOTE_PPI || defined(__DOXYGEN__) + /** + * @brief PAL line to toggle. + * @note Only used if mode is PWM_OUTPUT_HIGH or PWM_OUTPUT_LOW. + * @note When NRF5_PWM_USE_GPIOTE_PPI is used and channel enabled, + * it wont be possible to access this PAL line using the PAL + * driver. + */ + ioline_t ioline; + + /** + * @brief Unique GPIOTE channel to use. (1 channel) + * @note Only 4 GPIOTE channels are available on nRF51. + */ + uint8_t gpiote_channel; + + /** + * @brief Unique PPI channels to use. (2 channels) + * @note Only 16 PPI channels are available on nRF51 + * (When Softdevice is enabled, only channels 0-7 are available) + */ + uint8_t ppi_channel[2]; +#endif +} 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. + */ + uint32_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 TIMER registers block. + */ + NRF_TIMER_Type *timer; +}; + +/*===========================================================================*/ +/* 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)->timer->CC[(pwmp)->channels] = ((period) - 1); \ + } while(0) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if NRF5_PWM_USE_TIMER0 || defined(__DOXYGEN__) +extern PWMDriver PWMD1; +#endif +#if NRF5_PWM_USE_TIMER1 || defined(__DOXYGEN__) +extern PWMDriver PWMD2; +#endif +#if NRF5_PWM_USE_TIMER2 || 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/QDECv1/hal_qei_lld.c b/os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.c new file mode 100644 index 0000000..f0c24c9 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.c @@ -0,0 +1,300 @@ +/* + ChibiOS - Copyright (C) 2016..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. +*/ + +/** + * @file QDECv1/hal_qei_lld.c + * @brief NRF5 QEI subsystem low level driver. + * + * @addtogroup QEI + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_QEI == TRUE) || defined(__DOXYGEN__) + + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief QEID1 driver identifier. + */ +#if NRF5_QEI_USE_QDEC0 || defined(__DOXYGEN__) +QEIDriver QEID1; +#endif + + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Common IRQ handler. + * + * @param[in] qeip pointer to an QEIDriver + */ +static void serve_interrupt(QEIDriver *qeip) { + NRF_QDEC_Type *qdec = qeip->qdec; + +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + /* Accumulator overflowed + */ + if (qdec->EVENTS_ACCOF) { + qdec->EVENTS_ACCOF = 0; +#if CORTEX_MODEL >= 4 + (void)qdec->EVENTS_ACCOF; +#endif + + qeip->overflowed++; + if (qeip->config->overflowed_cb) + qeip->config->overflowed_cb(qeip); + } +#endif + + /* Report ready + */ + if (qdec->EVENTS_REPORTRDY) { + qdec->EVENTS_REPORTRDY = 0; +#if CORTEX_MODEL >= 4 + (void)qdec->EVENTS_REPORTRDY; +#endif + + /* Read (and clear counters due to shortcut) */ + int16_t acc = ( int16_t)qdec->ACCREAD; + uint16_t accdbl = (uint16_t)qdec->ACCDBLREAD; + + /* Inverse direction if requested */ + if (qeip->config->dirinv) + acc = -acc; // acc is [-1024..+1023], its okay on int16_t + + /* Adjust counter */ + qeiAdjustI(qeip, acc); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_QEI_USE_QDEC0 == TRUE +/** + * @brief Quadrature decoder vector (QDEC) + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector88) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&QEID1); + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level QEI driver initialization. + * + * @notapi + */ +void qei_lld_init(void) { + +#if NRF5_QEI_USE_QDEC0 == TRUE + /* Driver initialization.*/ + qeiObjectInit(&QEID1); + QEID1.qdec = NRF_QDEC; +#endif +} + +/** + * @brief Configures and activates the QEI peripheral. + * + * @param[in] qeip pointer to the @p QEIDriver object + * + * @notapi + */ +void qei_lld_start(QEIDriver *qeip) { + NRF_QDEC_Type *qdec = qeip->qdec; + const QEIConfig *cfg = qeip->config; + + if (qeip->state == QEI_STOP) { + /* Set Pins */ + palSetLineMode(cfg->phase_a, PAL_MODE_INPUT); + palSetLineMode(cfg->phase_b, PAL_MODE_INPUT); +#if NRF5_QEI_USE_LED == TRUE + if (cfg->led != PAL_NOLINE) { + palSetLineMode(cfg->led, PAL_MODE_INPUT); + } +#endif + + /* Set interrupt masks and enable interrupt */ +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + qdec->INTENSET = QDEC_INTENSET_REPORTRDY_Msk | + QDEC_INTENSET_ACCOF_Msk; +#else + qdec->INTENSET = QDEC_INTENSET_REPORTRDY_Msk; +#endif +#if NRF5_QEI_USE_QDEC0 == TRUE + if (&QEID1 == qeip) { + nvicEnableVector(QDEC_IRQn, NRF5_QEI_QDEC0_IRQ_PRIORITY); + } +#endif + + /* Select pin for Phase A and Phase B */ +#if NRF_SERIES == 51 + qdec->PSELA = PAL_PAD(cfg->phase_a); + qdec->PSELB = PAL_PAD(cfg->phase_b); +#else + qdec->PSEL.A = PAL_PAD(cfg->phase_a); + qdec->PSEL.B = PAL_PAD(cfg->phase_b); +#endif + /* Select (optional) pin for LED, and configure it */ +#if NRF5_QEI_USE_LED == TRUE +#if NRF_SERIES == 51 + qdec->PSELLED = PAL_PAD(cfg->led); +#else + qdec->PSEL.LED = PAL_PAD(cfg->led); +#endif + qdec->LEDPOL = ((cfg->led_polarity == QEI_LED_POLARITY_LOW) + ? QDEC_LEDPOL_LEDPOL_ActiveLow + : QDEC_LEDPOL_LEDPOL_ActiveHigh) + << QDEC_LEDPOL_LEDPOL_Pos; + qdec->LEDPRE = cfg->led_warming; +#else +#if NRF_SERIES == 51 + qdec->PSELLED = (uint32_t)-1; +#else + qdec->PSEL.LED = (uint32_t)-1; +#endif +#endif + + /* Set sampling resolution and debouncing */ + qdec->SAMPLEPER = cfg->resolution; + qdec->DBFEN = (cfg->debouncing ? QDEC_DBFEN_DBFEN_Enabled + : QDEC_DBFEN_DBFEN_Disabled) + << QDEC_DBFEN_DBFEN_Pos; + + /* Define minimum sampling before reporting + and create shortcut to clear accumulation */ + qdec->REPORTPER = cfg->report; + qdec->SHORTS = QDEC_SHORTS_REPORTRDY_READCLRACC_Msk; + + /* Enable peripheric */ + qdec->ENABLE = 1; + } + + /* Initially state is stopped, events cleared */ + qdec->TASKS_STOP = 1; + qdec->EVENTS_SAMPLERDY = 0; + qdec->EVENTS_REPORTRDY = 0; + qdec->EVENTS_ACCOF = 0; +#if CORTEX_MODEL >= 4 + (void)qdec->EVENTS_SAMPLERDY; + (void)qdec->EVENTS_REPORTRDY; + (void)qdec->EVENTS_ACCOF; +#endif +} + +/** + * @brief Deactivates the QEI peripheral. + * + * @param[in] qeip pointer to the @p QEIDriver object + * + * @notapi + */ +void qei_lld_stop(QEIDriver *qeip) { + + NRF_QDEC_Type *qdec = qeip->qdec; + const QEIConfig *cfg = qeip->config; + + if (qeip->state == QEI_READY) { + qdec->TASKS_STOP = 1; + qdec->ENABLE = 0; + + /* Unset interrupt masks and disable interrupt */ +#if NRF5_QEI_USE_QDEC0 == TRUE + if (&QEID1 == qeip) { + nvicDisableVector(QDEC_IRQn); + } +#endif +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + qdec->INTENCLR = QDEC_INTENCLR_REPORTRDY_Msk | + QDEC_INTENCLR_ACCOF_Msk; +#else + qdec->INTENCLR = QDEC_INTENCLR_REPORTRDY_Msk; +#endif + + /* Return pins to reset state */ + palSetLineMode(cfg->phase_a, PAL_MODE_RESET); + palSetLineMode(cfg->phase_b, PAL_MODE_RESET); +#if NRF5_QEI_USE_LED == TRUE + if (cfg->led != PAL_NOLINE) { + palSetLineMode(cfg->led, PAL_MODE_RESET); + } +#endif + } +} + +/** + * @brief Enables the input capture. + * + * @param[in] qeip pointer to the @p QEIDriver object + * + * @notapi + */ +void qei_lld_enable(QEIDriver *qeip) { +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + qeip->overflowed = 0; +#endif + + qeip->qdec->EVENTS_SAMPLERDY = 0; + qeip->qdec->EVENTS_REPORTRDY = 0; + qeip->qdec->EVENTS_ACCOF = 0; +#if CORTEX_MODEL >= 4 + (void)qeip->qdec->EVENTS_SAMPLERDY; + (void)qeip->qdec->EVENTS_REPORTRDY; + (void)qeip->qdec->EVENTS_ACCOF; +#endif + qeip->qdec->TASKS_START = 1; +} + +/** + * @brief Disables the input capture. + * + * @param[in] qeip pointer to the @p QEIDriver object + * + * @notapi + */ +void qei_lld_disable(QEIDriver *qeip) { + qeip->qdec->TASKS_STOP = 1; +} + + +#endif /* HAL_USE_QEI */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.h b/os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.h new file mode 100644 index 0000000..f4db11e --- /dev/null +++ b/os/hal/ports/NRF5/LLD/QDECv1/hal_qei_lld.h @@ -0,0 +1,390 @@ +/* + ChibiOS - Copyright (C) 2016..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. +*/ + +/** + * @file QDECv1/hal_qei_lld.h + * @brief NRF5 QEI subsystem low level driver header. + * + * @note Not tested with LED pin + * + * @note Pins are configured as input with no pull. + * + * @addtogroup QEI + * @{ + */ + +#ifndef HAL_QEI_LLD_H +#define HAL_QEI_LLD_H + +#if (HAL_USE_QEI == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief For LED active on LOW + */ +#define QEI_LED_POLARITY_LOW 0 + +/** + * @brief For LED active on HIGH + */ +#define QEI_LED_POLARITY_HIGH 1 + +/** + * @brief Mininum usable value for defining counter underflow + */ +#define QEI_COUNT_MIN (-2147483648) + +/** + * @brief Maximum usable value for defining counter overflow + */ +#define QEI_COUNT_MAX ( 2147483647) + + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief LED control enable switch. + * @details If set to @p TRUE the support for LED control + * is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_QEI_USE_LED) || defined(__DOXYGEN__) +#define NRF5_QEI_USE_LED FALSE +#endif + +/** + * @brief Accumulator overflow notification enable switch. + * @details If set to @p TRUE the support for accumulator overflow + * is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_QEI_USE_ACC_OVERFLOWED_CB) || defined(__DOXYGEN__) +#define NRF5_QEI_USE_ACC_OVERFLOWED_CB FALSE +#endif + +/** + * @brief QEID1 driver enable switch. + * @details If set to @p TRUE the support for QEID1 is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_QEI_USE_QDEC0) || defined(__DOXYGEN__) +#define NRF5_QEI_USE_QDEC0 FALSE +#endif + +/** + * @brief QEID interrupt priority level setting for QDEC0. + */ +#if !defined(NRF5_QEI_QDEC0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_QEI_QDEC0_IRQ_PRIORITY 2 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if NRF5_QEI_USE_QDEC0 == FALSE +#error "Requesting QEI driver, but no QDEC peripheric attached" +#endif + +#if NRF5_QEI_USE_QDEC0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_QEI_QDEC0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to QDEC0" +#endif + + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief QEI count mode. + */ +typedef enum { + QEI_MODE_QUADRATURE = 0, /**< Quadrature encoder mode. */ +} qeimode_t; + +/** + * @brief QEI resolution. + */ +typedef enum { + QEI_RESOLUTION_128us = 0x00UL, /**< 128us sample period. */ + QEI_RESOLUTION_256us = 0x01UL, /**< 256us sample period. */ + QEI_RESOLUTION_512us = 0x02UL, /**< 512us sample period. */ + QEI_RESOLUTION_1024us = 0x03UL, /**< 1024us sample period. */ + QEI_RESOLUTION_2048us = 0x04UL, /**< 2048us sample period. */ + QEI_RESOLUTION_4096us = 0x05UL, /**< 4096us sample period. */ + QEI_RESOLUTION_8192us = 0x06UL, /**< 8192us sample period. */ + QEI_RESOLUTION_16384us = 0x07UL, /**< 16384us sample period. */ +} qeiresolution_t; + +/** + * @brief Clusters of samples. + */ +typedef enum { + QEI_REPORT_10 = 0x00UL, /**< 10 samples per report. */ + QEI_REPORT_40 = 0x01UL, /**< 40 samples per report. */ + QEI_REPORT_80 = 0x02UL, /**< 80 samples per report. */ + QEI_REPORT_120 = 0x03UL, /**< 120 samples per report. */ + QEI_REPORT_160 = 0x04UL, /**< 160 samples per report. */ + QEI_REPORT_200 = 0x05UL, /**< 200 samples per report. */ + QEI_REPORT_240 = 0x06UL, /**< 240 samples per report. */ + QEI_REPORT_280 = 0x07UL, /**< 280 samples per report. */ +} qeireport_t; + +/** + * @brief QEI direction inversion. + */ +typedef enum { + QEI_DIRINV_FALSE = 0, /**< Do not invert counter direction. */ + QEI_DIRINV_TRUE = 1, /**< Invert counter direction. */ +} qeidirinv_t; + +/** + * @brief QEI counter type. + */ +typedef int16_t qeicnt_t; + +/** + * @brief QEI delta type. + */ +typedef int16_t qeidelta_t; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Count mode. + */ + qeimode_t mode; + /** + * @brief Resolution. + */ + qeiresolution_t resolution; + /** + * @brief Direction inversion. + */ + qeidirinv_t dirinv; + /** + * @brief Handling of counter overflow/underflow + * + * @details When overflow occurs, the counter value is updated + * according to: + * - QEI_OVERFLOW_DISCARD: + * discard the update value, counter doesn't change + * - QEI_OVERFLOW_MINMAX + * counter will be updated to reach min or max + * - QEI_OVERFLOW_WRAP: + * counter value will wrap around + */ + qeioverflow_t overflow; + /** + * @brief Min count value. + * + * @note If min == max, then QEI_COUNT_MIN is used. + */ + qeicnt_t min; + /** + * @brief Max count value. + * + * @note If min == max, then QEI_COUNT_MAX is used. + */ + qeicnt_t max; + /** + * @brief Notify of value change + * + * @note Called from ISR context. + */ + qeicallback_t notify_cb; + /** + * @brief Notify of overflow + * + * @note Overflow notification is performed after + * value changed notification. + * @note Called from ISR context. + */ + void (*overflow_cb)(QEIDriver *qeip, qeidelta_t delta); + /* End of the mandatory fields.*/ + /** + * @brief Line for reading Phase A + */ + ioline_t phase_a; + /** + * @brief Line for reading Phase B + */ + ioline_t phase_b; +#if (NRF5_QEI_USE_LED == TRUE) || defined(__DOXYGEN__) + /** + * @brief Line used to control LED + * + * @note If LED is not controlled by MCU, you need to use the + * PAL_NOLINE value. + */ + ioline_t led; + /** + * @brief Period in µs the LED is switched on prior to sampling. + * + * @details LED warming is expressed in micro-seconds and value + * is [0..511] + * + * @note 31µs is the recommanded default. + * + * @note If debouncing is activated, LED is always on for the + * whole sampling period (aka: resolution) + */ + uint16_t led_warming; + /** + * @brief LED polarity to used (when LED is controlled by MCU) + */ + uint8_t led_polarity; +#endif + /** + * @brief Activate debouncing filter + * + * @note If LED is controlled by MCU, the led_warming is ignored and, + * LED is always on for the whole sampling period (aka: resolution) + */ + bool debouncing; + /** + * @brief Number of samples per report + * + * @details Default to QEI_REPORT_10 + */ + qeireport_t report; +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + /** + * @brief Notify of internal accumulator overflowed + * (ie: MCU discarding samples) + * + * @note Called from ISR context. + */ + qeicallback_t overflowed_cb; +#endif +} QEIConfig; + +/** + * @brief Structure representing an QEI driver. + */ +struct QEIDriver { + /** + * @brief Driver state. + */ + qeistate_t state; + /** + * @brief Last count value. + */ + qeicnt_t last; + /** + * @brief Current configuration data. + */ + const QEIConfig *config; +#if defined(QEI_DRIVER_EXT_FIELDS) + QEI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Counter + */ + qeicnt_t count; +#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE + /** + * @brief Number of time the MCU discarded updates due to + * accumulator overflow + */ + uint32_t overflowed; +#endif + /** + * @brief Pointer to the QDECx registers block. + */ + NRF_QDEC_Type *qdec; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the counter value. + * + * @param[in] qeip pointer to the @p QEIDriver object + * @return The current counter value. + * + * @notapi + */ +#define qei_lld_get_count(qeip) ((qeip)->count) + + +/** + * @brief Set the counter value. + * + * @param[in] qeip pointer to the @p QEIDriver object + * @param[in] value counter value + * + * @notapi + */ +#define qei_lld_set_count(qeip, value) \ + if ((qeip)->count != ((qeicnt_t)value)) { \ + (qeip)->count = value; \ + if ((qeip)->config->notify_cb) \ + (qeip)->config->notify_cb(qeip); \ + } while(0) + + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if NRF5_QEI_USE_QDEC0 && !defined(__DOXYGEN__) +extern QEIDriver QEID1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void qei_lld_init(void); + void qei_lld_start(QEIDriver *qeip); + void qei_lld_stop(QEIDriver *qeip); + void qei_lld_enable(QEIDriver *qeip); + void qei_lld_disable(QEIDriver *qeip); + qeidelta_t qei_lld_adjust_count(QEIDriver *qeip, qeidelta_t delta); +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* To be moved in hal_qei */ +/*===========================================================================*/ + +void qeiSetCount(QEIDriver *qeip, qeicnt_t value); +qeidelta_t qeiAdjust(QEIDriver *qeip, qeidelta_t delta); + +#endif /* HAL_USE_QEI */ + +#endif /* HAL_QEI_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.c b/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.c new file mode 100644 index 0000000..bf9382c --- /dev/null +++ b/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.c @@ -0,0 +1,169 @@ +/* + RNG for ChibiOS - 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. +*/ + +/** + * @file RNGv1/hal_rng_lld.c + * @brief NRF5 RNG subsystem low level driver source. + * + * @addtogroup RNG + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_RNG == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/** + * @brief RNG default configuration. + */ +static const RNGConfig default_config = { + .digital_error_correction = 1, +}; + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief RNGD1 driver identifier.*/ +#if NRF5_RNG_USE_RNG0 || defined(__DOXYGEN__) +RNGDriver RNGD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level RNG driver initialization. + * + * @notapi + */ +void rng_lld_init(void) { + rngObjectInit(&RNGD1); + RNGD1.rng = NRF_RNG; + RNGD1.irq = RNG_IRQn; +} + +/** + * @brief Configures and activates the RNG peripheral. + * + * @param[in] rngp pointer to the @p RNGDriver object + * + * @notapi + */ +void rng_lld_start(RNGDriver *rngp) { + NRF_RNG_Type *rng = rngp->rng; + + /* If not specified, set default configuration */ + if (rngp->config == NULL) + rngp->config = &default_config; + + /* Configure digital error correction */ + if (rngp->config->digital_error_correction) + rng->CONFIG |= RNG_CONFIG_DERCEN_Msk; + else + rng->CONFIG &= ~RNG_CONFIG_DERCEN_Msk; + + /* Clear pending events */ + rng->EVENTS_VALRDY = 0; +#if CORTEX_MODEL >= 4 + (void)rng->EVENTS_VALRDY; +#endif + + /* Set interrupt mask */ + rng->INTENSET = RNG_INTENSET_VALRDY_Msk; + + /* Start */ + rng->TASKS_START = 1; +} + + +/** + * @brief Deactivates the RNG peripheral. + * + * @param[in] rngp pointer to the @p RNGDriver object + * + * @notapi + */ +void rng_lld_stop(RNGDriver *rngp) { + NRF_RNG_Type *rng = rngp->rng; + + /* Stop peripheric */ + rng->TASKS_STOP = 1; +} + + +/** + * @brief Write random bytes; + * + * @param[in] rngp pointer to the @p RNGDriver object + * @param[in] n size of buf in bytes + * @param[in] buf @p buffer location + * + * @notapi + */ +msg_t rng_lld_write(RNGDriver *rngp, uint8_t *buf, size_t n, + systime_t timeout) { + NRF_RNG_Type *rng = rngp->rng; + size_t i; + + for (i = 0 ; i < n ; i++) { + /* Wait for byte ready + * It take about 677µs to generate a new byte, not sure if + * forcing a context switch will be a benefit + */ + while (rng->EVENTS_VALRDY == 0) { + /* Sleep and wakeup on ARM event (interrupt) */ + SCB->SCR |= SCB_SCR_SEVONPEND_Msk; + __SEV(); + __WFE(); + __WFE(); + } + + /* Read byte */ + buf[i] = (char)rng->VALUE; + + /* Mark as read */ + rng->EVENTS_VALRDY = 0; +#if CORTEX_MODEL >= 4 + (void)rng->EVENTS_VALRDY; +#endif + + /* Clear interrupt so we can wake up again */ + nvicClearPending(rngp->irq); + } + return MSG_OK; +} + +#endif /* HAL_USE_RNG */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.h b/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.h new file mode 100644 index 0000000..a74f6ef --- /dev/null +++ b/os/hal/ports/NRF5/LLD/RNGv1/hal_rng_lld.h @@ -0,0 +1,159 @@ +/* + RNG for ChibiOS - 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. +*/ + +/** + * @file RNGv1/hal_rng_lld.h + * @brief NRF5 RNG subsystem low level driver header. + * + * @addtogroup RNG + * @{ + */ + +#ifndef HAL_RNG_LLD_H +#define HAL_RNG_LLD_H + +#if (HAL_USE_RNG == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief RNGD1 driver enable switch. + * @details If set to @p TRUE the support for RNGD1 is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_RNG_USE_RNG0) || defined(__DOXYGEN__) +#define NRF5_RNG_USE_RNG0 FALSE +#endif + +/** + * @brief RNG interrupt priority level setting for RNG0. + */ +#if !defined(NRF5_RNG_RNG0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_RNG_RNG0_IRQ_PRIORITY 3 +#endif + + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if NRF5_RNG_USE_RNG0 == FALSE +#error "Requesting RNG driver, but no RNG peripheric attached" +#endif + +#if NRF5_RNG_USE_RNG0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_RNG_RNG0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to RNG0" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an RNG driver. + */ +typedef struct RNGDriver RNGDriver; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /* End of the mandatory fields.*/ + /** + * @brief Activate the digital error correction + * + * @details A digital corrector algorithm is employed to remove any + * bias toward '1' or '0'. Disabling it offers a substantial + * speed advantage, but may result in a statistical distribution + * that is not perfectly uniform. + * + * @note For nRF51, on average, it take 167µs to get a byte without + * digitial error correction and 677µs with, but no garantee + * is made on the necessary time to generate one byte. + */ + uint8_t digital_error_correction:1; +} RNGConfig; + + +/** + * @brief Structure representing an RNG driver. + */ +struct RNGDriver { + /** + * @brief Driver state. + */ + rngstate_t state; + /** + * @brief Current configuration data. + */ + const RNGConfig *config; +#if RNG_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* RNG_USE_MUTUAL_EXCLUSION */ + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the RNGx registers block. + */ + NRF_RNG_Type *rng; + /** + * @brief IRQ number + */ + uint32_t irq; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if NRF5_RNG_USE_RNG0 && !defined(__DOXYGEN__) +extern RNGDriver RNGD1; +#endif /* NRF5_RNG_USE_RNG0 */ + +#ifdef __cplusplus +extern "C" { +#endif + void rng_lld_init(void); + void rng_lld_start(RNGDriver *rngp); + void rng_lld_stop(RNGDriver *rngp); + msg_t rng_lld_write(RNGDriver *rngp, uint8_t *buf, size_t n, + systime_t timeout); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_RNG */ + +#endif /* HAL_RNG_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.c b/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.c new file mode 100644 index 0000000..95bb9b7 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.c @@ -0,0 +1,389 @@ +/* + 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 SPIv1/hal_spi_lld.c + * @brief NRF5 low level SPI driver code. + * + * @addtogroup SPI + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +#if NRF5_SPI_USE_SPI0 || defined(__DOXYGEN__) +/** @brief SPI1 driver identifier.*/ +SPIDriver SPID1; +#endif + +#if NRF5_SPI_USE_SPI1 || defined(__DOXYGEN__) +/** @brief SPI2 driver identifier.*/ +SPIDriver SPID2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Preloads the transmit FIFO. + * + * @param[in] spip pointer to the @p SPIDriver object + */ +static void port_fifo_preload(SPIDriver *spip) { + NRF_SPI_Type *port = spip->port; + + if (spip->txcnt > 0 && spip->txptr != NULL) + port->TXD = *(uint8_t *)spip->txptr++; + else + port->TXD = 0xFF; + spip->txcnt--; +} + +#if defined(__GNUC__) +__attribute__((noinline)) +#endif +/** + * @brief Common IRQ handler. + * + * @param[in] spip pointer to the @p SPIDriver object + */ +static void serve_interrupt(SPIDriver *spip) { + NRF_SPI_Type *port = spip->port; + + // Clear SPI READY event flag + port->EVENTS_READY = 0; +#if CORTEX_MODEL >= 4 + (void)port->EVENTS_READY; +#endif + + if (spip->rxptr != NULL) { + *(uint8_t *)spip->rxptr++ = port->RXD; + } + else { + (void)port->RXD; + if (--spip->rxcnt == 0) { + osalDbgAssert(spip->txcnt == 0, "counter out of synch"); + /* Stops the IRQ sources.*/ + spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); + /* Portable SPI ISR code defined in the high level driver, note, it is + a macro.*/ + _spi_isr_code(spip); + return; + } + } + if (spip->txcnt > 0) { + port_fifo_preload(spip); + } + else { + spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); + /* Portable SPI ISR code defined in the high level driver, note, it is + a macro.*/ + _spi_isr_code(spip); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_SPI_USE_SPI0 || defined(__DOXYGEN__) +/** + * @brief SPI0 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector4C) { + + CH_IRQ_PROLOGUE(); + serve_interrupt(&SPID1); + CH_IRQ_EPILOGUE(); +} +#endif +#if NRF5_SPI_USE_SPI1 || defined(__DOXYGEN__) +/** + * @brief SPI1 interrupt handler. + * + * @isr + */ +CH_IRQ_HANDLER(Vector50) { + + CH_IRQ_PROLOGUE(); + serve_interrupt(&SPID2); + CH_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SPI driver initialization. + * + * @notapi + */ +void spi_lld_init(void) { + +#if NRF5_SPI_USE_SPI0 + spiObjectInit(&SPID1); + SPID1.port = NRF_SPI0; +#endif +#if NRF5_SPI_USE_SPI1 + spiObjectInit(&SPID2); + SPID2.port = NRF_SPI1; +#endif +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_start(SPIDriver *spip) { + uint32_t config; + + if (spip->state == SPI_STOP) { +#if NRF5_SPI_USE_SPI0 + if (&SPID1 == spip) + nvicEnableVector(SPI0_TWI0_IRQn, NRF5_SPI_SPI0_IRQ_PRIORITY); +#endif +#if NRF5_SPI_USE_SPI1 + if (&SPID2 == spip) + nvicEnableVector(SPI1_TWI1_IRQn, NRF5_SPI_SPI1_IRQ_PRIORITY); +#endif + } + + config = spip->config->lsbfirst ? + (SPI_CONFIG_ORDER_LsbFirst << SPI_CONFIG_ORDER_Pos) : + (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos); + + switch (spip->config->mode) { + case 1: + config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos); + config |= (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); + break; + case 2: + config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos); + config |= (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); + break; + case 3: + config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos); + config |= (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); + break; + default: + config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos); + config |= (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); + break; + } + + /* Configuration.*/ + spip->port->CONFIG = config; +#if NRF_SERIES == 51 + spip->port->PSELSCK = spip->config->sckpad; + spip->port->PSELMOSI = spip->config->mosipad; + spip->port->PSELMISO = spip->config->misopad; +#else + spip->port->PSEL.SCK = spip->config->sckpad; + spip->port->PSEL.MOSI = spip->config->mosipad; + spip->port->PSEL.MISO = spip->config->misopad; +#endif + spip->port->FREQUENCY = spip->config->freq; + spip->port->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos); + + /* clear events flag */ + spip->port->EVENTS_READY = 0; +#if CORTEX_MODEL >= 4 + (void)spip->port->EVENTS_READY; +#endif +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_stop(SPIDriver *spip) { + + if (spip->state != SPI_STOP) { + spip->port->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos); + spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); +#if NRF5_SPI_USE_SPI0 + if (&SPID1 == spip) + nvicDisableVector(SPI0_TWI0_IRQn); +#endif +#if NRF5_SPI_USE_SPI1 + if (&SPID2 == spip) + nvicDisableVector(SPI1_TWI1_IRQn); +#endif + } +} + +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_select(SPIDriver *spip) { + + palClearPad(IOPORT1, spip->config->sspad); +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_unselect(SPIDriver *spip) { + + palSetPad(IOPORT1, spip->config->sspad); +} + +/** + * @brief Ignores data on the SPI bus. + * @details This function transmits a series of idle words on the SPI bus and + * ignores the received data. This function can be invoked even + * when a slave select signal has not been yet asserted. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * + * @notapi + */ +void spi_lld_ignore(SPIDriver *spip, size_t n) { + + spip->rxptr = NULL; + spip->txptr = NULL; + spip->rxcnt = spip->txcnt = n; + port_fifo_preload(spip); + spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + + spip->rxptr = rxbuf; + spip->txptr = txbuf; + spip->rxcnt = spip->txcnt = n; + port_fifo_preload(spip); + spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { + + spip->rxptr = NULL; + spip->txptr = txbuf; + spip->rxcnt = spip->txcnt = n; + port_fifo_preload(spip); + spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { + + spip->rxptr = rxbuf; + spip->txptr = NULL; + spip->rxcnt = spip->txcnt = n; + port_fifo_preload(spip); + spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); +} + +/** + * @brief Exchanges one frame using a polled wait. + * @details This synchronous function exchanges one frame using a polled + * synchronization method. This function is useful when exchanging + * small amount of data on high speed channels, usually in this + * situation is much more efficient just wait for completion using + * polling than suspending the thread waiting for an interrupt. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] frame the data frame to send over the SPI bus + * @return The received data frame from the SPI bus. + */ +uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { + + spip->port->TXD = (uint8_t)frame; + while (spip->port->EVENTS_READY == 0) + ; + spip->port->EVENTS_READY = 0; +#if CORTEX_MODEL >= 4 + (void)spip->port->EVENTS_READY; +#endif + return spip->port->RXD; +} + +#endif /* HAL_USE_SPI */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.h b/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.h new file mode 100644 index 0000000..1c6d858 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/SPIv1/hal_spi_lld.h @@ -0,0 +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 SPIv1/hal_spi_lld.h + * @brief NRF5 low level SPI driver header. + * + * @addtogroup SPI + * @{ + */ + +#ifndef HAL_SPI_LLD_H +#define HAL_SPI_LLD_H + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief SPI0 interrupt priority level setting. + */ +#if !defined(NRF5_SPI_SPI0_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_SPI_SPI0_IRQ_PRIORITY 3 +#endif + +/** + * @brief SPI1 interrupt priority level setting. + */ +#if !defined(NRF5_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_SPI_SPI1_IRQ_PRIORITY 3 +#endif + +/** + * @brief Overflow error hook. + * @details The default action is to stop the system. + */ +#if !defined(NRF5_SPI_SPI_ERROR_HOOK) || defined(__DOXYGEN__) +#define NRF5_SPI_SPI_ERROR_HOOK() chSysHalt() +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !NRF5_SPI_USE_SPI0 && !NRF5_SPI_USE_SPI1 +#error "SPI driver activated but no SPI peripheral assigned" +#endif + +#if NRF5_SPI_USE_SPI0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SPI_SPI0_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI0" +#endif + +#if NRF5_SPI_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SPI_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an SPI driver. + */ +typedef struct SPIDriver SPIDriver; + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicallback_t)(SPIDriver *spip); + +/** + * @brief SPI frequency + */ +typedef enum { + NRF5_SPI_FREQ_125KBPS = (SPI_FREQUENCY_FREQUENCY_K125 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_250KBPS = (SPI_FREQUENCY_FREQUENCY_K250 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_500KBPS = (SPI_FREQUENCY_FREQUENCY_K500 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_1MBPS = (SPI_FREQUENCY_FREQUENCY_M1 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_2MBPS = (SPI_FREQUENCY_FREQUENCY_M2 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_4MBPS = (SPI_FREQUENCY_FREQUENCY_M4 << SPI_FREQUENCY_FREQUENCY_Pos), + NRF5_SPI_FREQ_8MBPS = (SPI_FREQUENCY_FREQUENCY_M8 << SPI_FREQUENCY_FREQUENCY_Pos), +} spifreq_t; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief Operation complete callback or @p NULL. + */ + spicallback_t end_cb; + /** + * @brief The frequency of the SPI peripheral + */ + spifreq_t freq; + /** + * @brief The SCK pad + */ + uint16_t sckpad; + /** + * @brief The MOSI pad + */ + uint16_t mosipad; + /** + * @brief The MOSI pad + */ + uint16_t misopad; + /* End of the mandatory fields.*/ + /** + * @brief The chip select line pad number. + */ + uint16_t sspad; + /** + * @brief Shift out least significant bit first + */ + uint8_t lsbfirst; + /** + * @brief SPI mode + */ + uint8_t mode; +} SPIConfig; + +/** + * @brief Structure representing a SPI driver. + */ +struct SPIDriver { + /** + * @brief Driver state. + */ + spistate_t state; + /** + * @brief Current configuration data. + */ + const SPIConfig *config; +#if SPI_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* SPI_USE_WAIT */ +#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) +#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#elif CH_CFG_USE_SEMAPHORES + semaphore_t semaphore; +#endif +#endif /* SPI_USE_MUTUAL_EXCLUSION */ +#if defined(SPI_DRIVER_EXT_FIELDS) + SPI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the SPI port. + */ + NRF_SPI_Type *port; + /** + * @brief Number of bytes yet to be received. + */ + uint32_t rxcnt; + /** + * @brief Receive pointer or @p NULL. + */ + void *rxptr; + /** + * @brief Number of bytes yet to be transmitted. + */ + uint32_t txcnt; + /** + * @brief Transmit pointer or @p NULL. + */ + const void *txptr; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if NRF5_SPI_USE_SPI0 && !defined(__DOXYGEN__) +extern SPIDriver SPID1; +#endif +#if NRF5_SPI_USE_SPI1 && !defined(__DOXYGEN__) +extern SPIDriver SPID2; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void spi_lld_init(void); + void spi_lld_start(SPIDriver *spip); + void spi_lld_stop(SPIDriver *spip); + void spi_lld_select(SPIDriver *spip); + void spi_lld_unselect(SPIDriver *spip); + void spi_lld_ignore(SPIDriver *spip, size_t n); + void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); + void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); + uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI */ + +#endif /* HAL_SPI_LLD_H */ + +/** @} */ 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_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 */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.c b/os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.c new file mode 100644 index 0000000..9f75906 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.c @@ -0,0 +1,467 @@ +/* + 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 TWIv1/hal_i2c_lld.c + * @brief NRF5 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 I2C_IRQ_NUM SPI0_TWI0_IRQn +#define I2C_IRQ_PRI NRF5_I2C_I2C0_IRQ_PRIORITY +#elif NRF5_I2C_USE_I2C1 +#define I2C_IRQ_NUM SPI1_TWI1_IRQn +#define I2C_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 + +uint8_t tx_resume_count; +uint8_t rx_resume_count; +uint8_t stop_count; + +/*===========================================================================*/ +/* 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; + int 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); +} + +static inline void i2c_setup_shortcut(I2CDriver *i2cp) +{ + uint32_t rxbytes = i2cp->rxbytes; + uint32_t txbytes = i2cp->txbytes; + + osalDbgAssert(rxbytes + txbytes, "transfer must be greater than zero"); + + if (txbytes > 1 || (!txbytes && rxbytes > 1)) + i2cp->i2c->SHORTS = TWI_SHORTS_BB_SUSPEND_Enabled << TWI_SHORTS_BB_SUSPEND_Pos; + else if (((txbytes == 1) && !rxbytes) || ((rxbytes == 1) && !txbytes)) + i2cp->i2c->SHORTS = TWI_SHORTS_BB_STOP_Enabled << TWI_SHORTS_BB_STOP_Pos; + else + i2cp->i2c->SHORTS = 0; +} + +#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 serve_interrupt(I2CDriver *i2cp) { + + NRF_TWI_Type *i2c = i2cp->i2c; + + if(i2c->EVENTS_TXDSENT) { + + i2c->EVENTS_TXDSENT = 0; +#if CORTEX_MODEL >= 4 + (void)i2c->EVENTS_TXDSENT; +#endif + + if(--i2cp->txbytes) { + + i2c->TXD = *i2cp->txptr++; + i2c_setup_shortcut(i2cp); + i2c->TASKS_RESUME = 1; + tx_resume_count++; + } + else if (i2cp->rxbytes) { + + i2c_setup_shortcut(i2cp); + i2c->TASKS_STARTRX = 1; + } + } + if(i2c->EVENTS_RXDREADY) { + + i2c->EVENTS_RXDREADY = 0; +#if CORTEX_MODEL >= 4 + (void)i2c->EVENTS_RXDREADY; +#endif + + *i2cp->rxptr++ = i2c->RXD; + + if(--i2cp->rxbytes) { + i2c_setup_shortcut(i2cp); + i2c->TASKS_RESUME = 1; + rx_resume_count++; + } + } + if(i2c->EVENTS_ERROR) { + + uint32_t err = i2c->ERRORSRC; + i2c->EVENTS_ERROR = 0; +#if CORTEX_MODEL >= 4 + (void)i2c->EVENTS_ERROR; +#endif + if (err & TWI_ERRORSRC_OVERRUN_Msk) + i2cp->errors |= I2C_OVERRUN; + if (err & (TWI_ERRORSRC_ANACK_Msk | TWI_ERRORSRC_DNACK_Msk)) + i2cp->errors |= I2C_ACK_FAILURE; + + i2c->TASKS_STOP = 1; + _i2c_wakeup_error_isr(i2cp); + } else if(i2c->EVENTS_STOPPED) { + + stop_count++; + i2c->EVENTS_STOPPED = 0; +#if CORTEX_MODEL >= 4 + (void)i2c->EVENTS_STOPPED; +#endif + _i2c_wakeup_isr(i2cp); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_I2C_USE_I2C0 || defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(Vector4C) { + + OSAL_IRQ_PROLOGUE(); + serve_interrupt(&I2CD1); + OSAL_IRQ_EPILOGUE(); +} + +#endif + +#if NRF5_I2C_USE_I2C1 || defined(__DOXYGEN__) + +OSAL_IRQ_HANDLER(Vector50) { + + OSAL_IRQ_PROLOGUE(); + 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_TWI0; +#endif + +#if NRF5_I2C_USE_I2C1 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = NRF_TWI1; +#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_TWI_Type *i2c = i2cp->i2c; + const I2CConfig *cfg = i2cp->config; + + if (i2cp->state != I2C_STOP) + return; + + i2c_clear_bus(i2cp); + + IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF; + IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF; + + i2c->EVENTS_RXDREADY = 0; + i2c->EVENTS_TXDSENT = 0; +#if CORTEX_MODEL >= 4 + (void)i2c->EVENTS_RXDREADY; + (void)i2c->EVENTS_TXDSENT; +#endif +#if NRF_SERIES == 51 + i2c->PSELSCL = cfg->scl_pad; + i2c->PSELSDA = cfg->sda_pad; +#else + i2c->PSEL.SCL = cfg->scl_pad; + i2c->PSEL.SDA = cfg->sda_pad; +#endif + + switch (cfg->clock) { + case 100000: + i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos; + break; + case 250000: + i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K250 << TWI_FREQUENCY_FREQUENCY_Pos; + break; + case 400000: + i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K400 << TWI_FREQUENCY_FREQUENCY_Pos; + break; + default: + osalDbgAssert(0, "invalid I2C frequency"); + break; + }; + + nvicEnableVector(I2C_IRQ_NUM, I2C_IRQ_PRI); + + i2c->INTENSET = TWI_INTENSET_TXDSENT_Msk | TWI_INTENSET_STOPPED_Msk | + TWI_INTENSET_ERROR_Msk | TWI_INTENSET_RXDREADY_Msk; + + i2c->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_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_TWI_Type *i2c = i2cp->i2c; + const I2CConfig *cfg = i2cp->config; + + if (i2cp->state != I2C_STOP) { + + i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; + + i2c->INTENCLR = TWI_INTENSET_TXDSENT_Msk | TWI_INTENSET_STOPPED_Msk | + TWI_INTENSET_ERROR_Msk | TWI_INTENSET_RXDREADY_Msk; + + nvicDisableVector(I2C_IRQ_NUM); + + 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_TWI_Type *i2c = i2cp->i2c; + + (void)timeout; + msg_t msg; + + i2cp->errors = I2C_NO_ERROR; + i2cp->addr = addr; + + i2cp->txptr = txbuf; + i2cp->txbytes = txbytes; + + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; + + i2c->ADDRESS = addr; + + tx_resume_count = 0; + rx_resume_count = 0; + stop_count = 0; + + if (i2cp->txbytes) { + + i2c->TXD = *i2cp->txptr++; + i2c_setup_shortcut(i2cp); + i2c->TASKS_STARTTX = 1; + } else if (i2cp->rxbytes) { + + i2c_setup_shortcut(i2cp); + 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. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @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. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @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/TWIv1/hal_i2c_lld.h b/os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.h new file mode 100644 index 0000000..7a4050b --- /dev/null +++ b/os/hal/ports/NRF5/LLD/TWIv1/hal_i2c_lld.h @@ -0,0 +1,232 @@ +/* + 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 TWIv1/hal_i2c_lld.h + * @brief NRF5 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. */ +/*===========================================================================*/ + +#define STATE_STOP 0x00 +#define STATE_SEND 0x01 +#define STATE_RECV 0x02 +#define STATE_DUMMY 0x03 + +/*===========================================================================*/ +/* 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 Type used to control the ISR state machine. */ +typedef uint8_t intstate_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__) +#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#elif CH_CFG_USE_SEMAPHORES + semaphore_t semaphore; +#endif +#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 Pointer to the buffer with data to send. */ + const uint8_t *txptr; + /* @brief Number of bytes of data to send. */ + size_t txbytes; + /* @brief Pointer to the buffer to put received data. */ + uint8_t *rxptr; + /* @brief Number of bytes of data to receive. */ + size_t rxbytes; + /* @brief Tracks current ISR state. */ + intstate_t intstate; + /* @brief Low-level register access. */ + NRF_TWI_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/LLD/UARTv1/hal_serial_lld.c b/os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.c new file mode 100644 index 0000000..76ba0e0 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.c @@ -0,0 +1,343 @@ +/* + 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 UARTv1/hal_serial_lld.c + * @brief NRF5 serial subsystem low level driver source. + * + * @addtogroup SERIAL + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) + +#if NRF_SERIES == 51 +#include "nrf51.h" +#elif NRF_SERIES == 52 +#include "nrf52.h" +#define UART0_IRQn UARTE0_UART0_IRQn +#endif + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART1 serial driver identifier.*/ +#if (NRF5_SERIAL_USE_UART0 == TRUE) || defined(__DOXYGEN__) +SerialDriver SD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief Driver default configuration. + */ +static const SerialConfig default_config = { + .speed = 38400, + .tx_pad = NRF5_SERIAL_PAD_DISCONNECTED, + .rx_pad = NRF5_SERIAL_PAD_DISCONNECTED, +#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) + .rts_pad = NRF5_SERIAL_PAD_DISCONNECTED, + .cts_pad = NRF5_SERIAL_PAD_DISCONNECTED, +#endif +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/* + * @brief Maps a baudrate speed to a BAUDRATE register value. + */ + +/** + * @brief Common UART configuration. + * + */ +static void configure_uart(const SerialConfig *config) +{ + uint32_t speed = UART_BAUDRATE_BAUDRATE_Baud250000; + + switch (config->speed) { + case 1200: speed = UART_BAUDRATE_BAUDRATE_Baud1200; break; + case 2400: speed = UART_BAUDRATE_BAUDRATE_Baud2400; break; + case 4800: speed = UART_BAUDRATE_BAUDRATE_Baud4800; break; + case 9600: speed = UART_BAUDRATE_BAUDRATE_Baud9600; break; + case 14400: speed = UART_BAUDRATE_BAUDRATE_Baud14400; break; + case 19200: speed = UART_BAUDRATE_BAUDRATE_Baud19200; break; + case 28800: speed = UART_BAUDRATE_BAUDRATE_Baud28800; break; + case 38400: speed = UART_BAUDRATE_BAUDRATE_Baud38400; break; + case 57600: speed = UART_BAUDRATE_BAUDRATE_Baud57600; break; + case 76800: speed = UART_BAUDRATE_BAUDRATE_Baud76800; break; + case 115200: speed = UART_BAUDRATE_BAUDRATE_Baud115200; break; + case 230400: speed = UART_BAUDRATE_BAUDRATE_Baud230400; break; + case 250000: speed = UART_BAUDRATE_BAUDRATE_Baud250000; break; + case 460800: speed = UART_BAUDRATE_BAUDRATE_Baud460800; break; + case 921600: speed = UART_BAUDRATE_BAUDRATE_Baud921600; break; + case 1000000: speed = UART_BAUDRATE_BAUDRATE_Baud1M; break; + default: osalDbgAssert(0, "invalid baudrate"); break; + }; + + /* Configure PINs mode */ + if (config->tx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { + palSetPadMode(IOPORT1, config->tx_pad, PAL_MODE_OUTPUT_PUSHPULL); + } + if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { + palSetPadMode(IOPORT1, config->rx_pad, PAL_MODE_INPUT); + } +#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) + if (config->rts_pad != NRF5_SERIAL_PAD_DISCONNECTED) { + palSetPadMode(IOPORT1, config->rts_pad, PAL_MODE_OUTPUT_PUSHPULL); + } + if (config->cts_pad != NRF5_SERIAL_PAD_DISCONNECTED) { + palSetPadMode(IOPORT1, config->cts_pad, PAL_MODE_INPUT); + } +#endif + + /* Select PINs used by UART */ + NRF_UART0->PSELTXD = config->tx_pad; + NRF_UART0->PSELRXD = config->rx_pad; +#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) + NRF_UART0->PSELRTS = config->rts_pad; + NRF_UART0->PSELCTS = config->cts_pad; +#else + NRF_UART0->PSELRTS = NRF5_SERIAL_PAD_DISCONNECTED; + NRF_UART0->PSELCTS = NRF5_SERIAL_PAD_DISCONNECTED; +#endif + + /* Set baud rate */ + NRF_UART0->BAUDRATE = speed; + + /* Set config */ + NRF_UART0->CONFIG = (UART_CONFIG_PARITY_Excluded << UART_CONFIG_PARITY_Pos); + + /* Adjust flow control */ +#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) + if ((config->rts_pad < TOTAL_GPIO_PADS) || + (config->cts_pad < TOTAL_GPIO_PADS)) { + NRF_UART0->CONFIG |= UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos; + } else { + NRF_UART0->CONFIG &= ~(UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos); + } +#else + NRF_UART0->CONFIG &= ~(UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos); +#endif + + /* Enable UART and clear events */ + NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled; + NRF_UART0->EVENTS_RXDRDY = 0; + NRF_UART0->EVENTS_TXDRDY = 0; +#if CORTEX_MODEL >= 4 + (void)NRF_UART0->EVENTS_RXDRDY; + (void)NRF_UART0->EVENTS_TXDRDY; +#endif + + if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { + while (NRF_UART0->EVENTS_RXDRDY != 0) { + (void)NRF_UART0->RXD; + } + } +} + + +/** + * @brief Driver output notification. + */ +#if NRF5_SERIAL_USE_UART0 || defined(__DOXYGEN__) +static void notify1(io_queue_t *qp) +{ + SerialDriver *sdp = &SD1; + + (void)qp; + + if (NRF_UART0->PSELTXD == NRF5_SERIAL_PAD_DISCONNECTED) + return; + + if (!sdp->tx_busy) { + msg_t b = oqGetI(&sdp->oqueue); + + if (b < Q_OK) { + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + NRF_UART0->TASKS_STOPTX = 1; + return; + } + sdp->tx_busy = 1; + NRF_UART0->TASKS_STARTTX = 1; + NRF_UART0->TXD = b; + } +} +#endif + + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_SERIAL_USE_UART0 || defined(__DOXYGEN__) +OSAL_IRQ_HANDLER(Vector48) { + + OSAL_IRQ_PROLOGUE(); + + SerialDriver *sdp = &SD1; + uint32_t isr = NRF_UART0->INTENSET; + + if ((NRF_UART0->EVENTS_RXDRDY != 0) && (isr & UART_INTENSET_RXDRDY_Msk)) { + // Clear UART RX event flag + NRF_UART0->EVENTS_RXDRDY = 0; +#if CORTEX_MODEL >= 4 + (void)NRF_UART0->EVENTS_RXDRDY; +#endif + + osalSysLockFromISR(); + if (iqIsEmptyI(&sdp->iqueue)) + chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE); + if (iqPutI(&sdp->iqueue, NRF_UART0->RXD) < Q_OK) + chnAddFlagsI(sdp, SD_OVERRUN_ERROR); + osalSysUnlockFromISR(); + } + + if ((NRF_UART0->EVENTS_TXDRDY != 0) && (isr & UART_INTENSET_TXDRDY_Msk)) { + msg_t b; + + // Clear UART TX event flag. + NRF_UART0->EVENTS_TXDRDY = 0; +#if CORTEX_MODEL >= 4 + (void)NRF_UART0->EVENTS_TXDRDY; +#endif + + osalSysLockFromISR(); + b = oqGetI(&sdp->oqueue); + osalSysUnlockFromISR(); + + if (b < Q_OK) { + osalSysLockFromISR(); + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + osalSysUnlockFromISR(); + NRF_UART0->TASKS_STOPTX = 1; + sdp->tx_busy = 0; + } else { + sdp->tx_busy = 1; + NRF_UART0->TXD = b; + } + } + + /* TODO: Error handling for EVENTS_ERROR */ + if ((NRF_UART0->EVENTS_ERROR != 0) && (isr & UART_INTENSET_ERROR_Msk)) { + // Clear UART ERROR event flag. + NRF_UART0->EVENTS_ERROR = 0; +#if CORTEX_MODEL >= 4 + (void)NRF_UART0->EVENTS_ERROR; +#endif + } + + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level serial driver initialization. + * + * @notapi + */ +void sd_lld_init(void) { + +#if NRF5_SERIAL_USE_UART0 == TRUE + sdObjectInit(&SD1, NULL, notify1); +#endif +} + +/** + * @brief Low level serial driver configuration and (re)start. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration. + * If this parameter is set to @p NULL then a default + * configuration is used. + * + * @notapi + */ +void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { + + if (config == NULL) + config = &default_config; + + osalDbgAssert( + (config->rx_pad < TOTAL_GPIO_PADS) || (config->tx_pad < TOTAL_GPIO_PADS), + "must configure at least an RX or TX pad"); + + if (sdp->state == SD_STOP) { + +#if NRF5_SERIAL_USE_UART0 == TRUE + if (sdp == &SD1) { + configure_uart(config); + + // Enable UART interrupt + NRF_UART0->INTENCLR = (uint32_t)-1; + NRF_UART0->INTENSET = UART_INTENSET_ERROR_Msk; + if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) + NRF_UART0->INTENSET |= UART_INTENSET_RXDRDY_Msk; + if (config->tx_pad != NRF5_SERIAL_PAD_DISCONNECTED) + NRF_UART0->INTENSET |= UART_INTENSET_TXDRDY_Msk; + + nvicEnableVector(UART0_IRQn, NRF5_SERIAL_UART0_PRIORITY); + + if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) + NRF_UART0->TASKS_STARTRX = 1; + } +#endif + + } +} + +/** + * @brief Low level serial driver stop. + * @details De-initializes the USART, stops the associated clock, resets the + * interrupt vector. + * + * @param[in] sdp pointer to a @p SerialDriver object + * + * @notapi + */ +void sd_lld_stop(SerialDriver *sdp) { + + if (sdp->state == SD_READY) { + +#if NRF5_SERIAL_USE_UART0 == TRUE + if (&SD1 == sdp) { + nvicDisableVector(UART0_IRQn); + NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Disabled; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL == TRUE */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.h b/os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.h new file mode 100644 index 0000000..9c76777 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/UARTv1/hal_serial_lld.h @@ -0,0 +1,155 @@ +/* + 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 UARTv1/hal_serial_lld.h + * @brief NRF5 serial subsystem low level driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef HAL_SERIAL_LLD_H +#define HAL_SERIAL_LLD_H + +#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name PLATFORM configuration options + * @{ + */ +/** + * @brief SD flow control enable switch. + * @details If set to @p TRUE the support for hardware flow control + * is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_SERIAL_USE_HWFLOWCTRL) || defined(__DOXYGEN__) +#define NRF5_SERIAL_USE_HWFLOWCTRL FALSE +#endif + +/** + * @brief SD1 driver enable switch. + * @details If set to @p TRUE the support for SD1 is included. + * @note The default is @p FALSE. + */ +#if !defined(NRF5_SERIAL_USE_UART0) || defined(__DOXYGEN__) +#define NRF5_SERIAL_USE_UART0 FALSE +#endif + +/** + * @brief UART0 interrupt priority level setting. + */ +#if !defined(NRF5_SERIAL_UART0_PRIORITY) || defined(__DOXYGEN__) +#define NRF5_SERIAL_UART0_PRIORITY 3 +#endif + +/* Value indicating that no pad is connected to this UART register. */ +#define NRF5_SERIAL_PAD_DISCONNECTED 0xFFFFFFFFU +#define NRF5_SERIAL_INVALID_BAUDRATE 0xFFFFFFFFU + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if NRF5_SERIAL_USE_UART0 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SERIAL_UART0_PRIORITY) +#error "Invalid IRQ priority assigned to UART0" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief NRF51 Serial Driver configuration structure. + * @details An instance of this structure must be passed to @p sdStart() + * in order to configure and start a serial driver operations. + * @note This structure content is architecture dependent, each driver + * implementation defines its own version and the custom static + * initializers. + */ +typedef struct { + /** + * @brief Bit rate. + */ + uint32_t speed; + /* End of the mandatory fields.*/ + uint32_t tx_pad; + uint32_t rx_pad; +#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) + uint32_t rts_pad; + uint32_t cts_pad; +#endif +} SerialConfig; + +/** + * @brief @p SerialDriver specific data. + */ +#define _serial_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + sdstate_t state; \ + /* Input queue.*/ \ + input_queue_t iqueue; \ + /* Output queue.*/ \ + output_queue_t oqueue; \ + /* Input circular buffer.*/ \ + uint8_t ib[SERIAL_BUFFERS_SIZE]; \ + /* Output circular buffer.*/ \ + uint8_t ob[SERIAL_BUFFERS_SIZE]; \ + /* 1 if port is busy transmitting, 0 otherwise. */ \ + uint8_t tx_busy; \ + /* End of the mandatory fields.*/ \ + thread_t *thread; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if (NRF5_SERIAL_USE_UART0 == TRUE) && !defined(__DOXYGEN__) +extern SerialDriver SD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sd_lld_init(void); + void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); + void sd_lld_stop(SerialDriver *sdp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SERIAL == TRUE */ + +#endif /* HAL_SERIAL_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.c b/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.c new file mode 100644 index 0000000..7bd2966 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.c @@ -0,0 +1,157 @@ +/* + ChibiOS - 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. +*/ + +/** + * @file WDTv1/hal_wdg_lld.c + * @brief NRF5 Watchdog Driver subsystem low level driver source template. + * + * @addtogroup WDG + * @{ + */ + +#include "hal.h" + +#if HAL_USE_WDG || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define RELOAD_REQUEST_VALUE 0x6E524635 + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +WDGDriver WDGD1; + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if WDG_USE_TIMEOUT_CALLBACK == TRUE +/** + * @brief Watchdog vector. + * @details This interrupt is used when watchdog timeout. + * + * @note Only 2 cycles at NRF5_LFCLK_FREQUENCY are available + * to they good bye. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector80) { + + OSAL_IRQ_PROLOGUE(); + osalSysLockFromISR(); + + /* Notify */ + if (WDGD1.config->callback) + WDGD1.config->callback(); + + /* Wait for reboot */ + while (1) { /* */ } + + osalSysUnlockFromISR(); + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level WDG driver initialization. + * + * @notapi + */ +void wdg_lld_init(void) { + WDGD1.state = WDG_STOP; + WDGD1.wdt = NRF_WDT; +} + +/** + * @brief Configures and activates the WDG peripheral. + * + * @note Once started there is no way out. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @notapi + */ +void wdg_lld_start(WDGDriver *wdgp) { + osalDbgAssert((wdgp->state == WDG_STOP), + "This WDG driver cannot be restarted once activated"); + + /* Generate interrupt on timeout */ +#if WDG_USE_TIMEOUT_CALLBACK == TRUE + wdgp->wdt->INTENSET = WDT_INTENSET_TIMEOUT_Msk; +#endif + + /* When to pause? (halt, sleep) */ + uint32_t config = 0; + if (!wdgp->config->pause_on_sleep) + config |= WDT_CONFIG_SLEEP_Msk; + if (!wdgp->config->pause_on_halt) + config |= WDT_CONFIG_HALT_Msk; + wdgp->wdt->CONFIG = config; + + /* Timeout in milli-seconds */ + uint64_t tout = (NRF5_LFCLK_FREQUENCY * wdgp->config->timeout_ms / 1000) - 1; + osalDbgAssert(tout <= 0xFFFFFFFF, "watchdog timout value exceeded"); + wdgp->wdt->CRV = (uint32_t)tout; + + /* Reload request (using RR0) */ + wdgp->wdt->RREN = WDT_RREN_RR0_Msk; + + /* Say your prayers, little one. */ + wdgp->wdt->TASKS_START = 1; +} + +/** + * @brief Deactivates the WDG peripheral. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @api + */ +void wdg_lld_stop(WDGDriver *wdgp) { + (void)wdgp; + osalDbgAssert(false, "This WDG driver cannot be stopped once activated"); +} + +/** + * @brief Reloads WDG's counter. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @notapi + */ +void wdg_lld_reset(WDGDriver * wdgp) { + wdgp->wdt->RR[0] = RELOAD_REQUEST_VALUE; +} + +#endif /* HAL_USE_WDG */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.h b/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.h new file mode 100644 index 0000000..c478919 --- /dev/null +++ b/os/hal/ports/NRF5/LLD/WDTv1/hal_wdg_lld.h @@ -0,0 +1,143 @@ +/* + ChibiOS - 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. +*/ + +/** + * @file WDTv1/hal_wdg_lld.h + * @brief NRF5 Watchdog Driver subsystem low level driver header template. + * + * @addtogroup WDG + * @{ + */ + +#ifndef HAL_WDG_LLD_H +#define HAL_WDG_LLD_H + +#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define WDG_MAX_TIMEOUT_MS \ + ((uint32_t)(0xFFFFFFFFu * 1000 / NRF5_LFCLK_FREQUENCY)) + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ + +/** + * @brief WDG driver implement timeout callback. + * @note The default is @p FALSE. + */ +#if !defined(WDG_USE_TIMEOUT_CALLBACK) || defined(__DOXYGEN__) +#define WDG_USE_TIMEOUT_CALLBACK FALSE +#endif +/** @} */ + + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an WDG driver. + */ +typedef struct WDGDriver WDGDriver; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + struct { + /** + * @brief Pause watchdog while the CPU is sleeping + */ + uint8_t pause_on_sleep : 1; + /** + * @brief Pause watchdog while the CPU is halted by the debugger + */ + uint8_t pause_on_halt : 1; + }; + /** + * + */ + uint32_t timeout_ms; +#if WDG_USE_TIMEOUT_CALLBACK == TRUE + /** + * @brief Notification callback when watchdog timedout + * + * @note About 2 cycles at NRF5_LFCLK_FREQUENCY are available + * before automatic reboot. + * + */ + void (*callback)(void); +#endif +} WDGConfig; + + + +/** + * @brief Structure representing an WDG driver. + */ +struct WDGDriver { + /** + * @brief Driver state. + */ + wdgstate_t state; + /** + * @brief Current configuration data. + */ + const WDGConfig *config; + /* End of the mandatory fields.*/ + NRF_WDT_Type *wdt; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +extern WDGDriver WDGD1; + +#ifdef __cplusplus +extern "C" { +#endif + void wdg_lld_init(void); + void wdg_lld_start(WDGDriver *wdgp); + void wdg_lld_stop(WDGDriver *wdgp); + void wdg_lld_reset(WDGDriver *wdgp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_WDG == TRUE */ + +#endif /* HAL_WDG_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_gpt_lld.c b/os/hal/ports/NRF5/LLD/hal_gpt_lld.c deleted file mode 100644 index 20dbcef..0000000 --- a/os/hal/ports/NRF5/LLD/hal_gpt_lld.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - 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 NRF5/LLD/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/hal_gpt_lld.h b/os/hal/ports/NRF5/LLD/hal_gpt_lld.h deleted file mode 100644 index 4173a3a..0000000 --- a/os/hal/ports/NRF5/LLD/hal_gpt_lld.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - 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/LLD/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/hal_i2c_lld.c b/os/hal/ports/NRF5/LLD/hal_i2c_lld.c deleted file mode 100644 index fefca0c..0000000 --- a/os/hal/ports/NRF5/LLD/hal_i2c_lld.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - 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/LLD/hal_i2c_lld.c - * @brief NRF5 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 I2C_IRQ_NUM SPI0_TWI0_IRQn -#define I2C_IRQ_PRI NRF5_I2C_I2C0_IRQ_PRIORITY -#elif NRF5_I2C_USE_I2C1 -#define I2C_IRQ_NUM SPI1_TWI1_IRQn -#define I2C_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 - -uint8_t tx_resume_count; -uint8_t rx_resume_count; -uint8_t stop_count; - -/*===========================================================================*/ -/* 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; - int 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); -} - -static inline void i2c_setup_shortcut(I2CDriver *i2cp) -{ - uint32_t rxbytes = i2cp->rxbytes; - uint32_t txbytes = i2cp->txbytes; - - osalDbgAssert(rxbytes + txbytes, "transfer must be greater than zero"); - - if (txbytes > 1 || (!txbytes && rxbytes > 1)) - i2cp->i2c->SHORTS = TWI_SHORTS_BB_SUSPEND_Enabled << TWI_SHORTS_BB_SUSPEND_Pos; - else if (((txbytes == 1) && !rxbytes) || ((rxbytes == 1) && !txbytes)) - i2cp->i2c->SHORTS = TWI_SHORTS_BB_STOP_Enabled << TWI_SHORTS_BB_STOP_Pos; - else - i2cp->i2c->SHORTS = 0; -} - -#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 serve_interrupt(I2CDriver *i2cp) { - - NRF_TWI_Type *i2c = i2cp->i2c; - - if(i2c->EVENTS_TXDSENT) { - - i2c->EVENTS_TXDSENT = 0; -#if CORTEX_MODEL >= 4 - (void)i2c->EVENTS_TXDSENT; -#endif - - if(--i2cp->txbytes) { - - i2c->TXD = *i2cp->txptr++; - i2c_setup_shortcut(i2cp); - i2c->TASKS_RESUME = 1; - tx_resume_count++; - } - else if (i2cp->rxbytes) { - - i2c_setup_shortcut(i2cp); - i2c->TASKS_STARTRX = 1; - } - } - if(i2c->EVENTS_RXDREADY) { - - i2c->EVENTS_RXDREADY = 0; -#if CORTEX_MODEL >= 4 - (void)i2c->EVENTS_RXDREADY; -#endif - - *i2cp->rxptr++ = i2c->RXD; - - if(--i2cp->rxbytes) { - i2c_setup_shortcut(i2cp); - i2c->TASKS_RESUME = 1; - rx_resume_count++; - } - } - if(i2c->EVENTS_ERROR) { - - uint32_t err = i2c->ERRORSRC; - i2c->EVENTS_ERROR = 0; -#if CORTEX_MODEL >= 4 - (void)i2c->EVENTS_ERROR; -#endif - if (err & TWI_ERRORSRC_OVERRUN_Msk) - i2cp->errors |= I2C_OVERRUN; - if (err & (TWI_ERRORSRC_ANACK_Msk | TWI_ERRORSRC_DNACK_Msk)) - i2cp->errors |= I2C_ACK_FAILURE; - - i2c->TASKS_STOP = 1; - _i2c_wakeup_error_isr(i2cp); - } else if(i2c->EVENTS_STOPPED) { - - stop_count++; - i2c->EVENTS_STOPPED = 0; -#if CORTEX_MODEL >= 4 - (void)i2c->EVENTS_STOPPED; -#endif - _i2c_wakeup_isr(i2cp); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_I2C_USE_I2C0 || defined(__DOXYGEN__) - -OSAL_IRQ_HANDLER(Vector4C) { - - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&I2CD1); - OSAL_IRQ_EPILOGUE(); -} - -#endif - -#if NRF5_I2C_USE_I2C1 || defined(__DOXYGEN__) - -OSAL_IRQ_HANDLER(Vector50) { - - OSAL_IRQ_PROLOGUE(); - 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_TWI0; -#endif - -#if NRF5_I2C_USE_I2C1 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = NRF_TWI1; -#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_TWI_Type *i2c = i2cp->i2c; - const I2CConfig *cfg = i2cp->config; - - if (i2cp->state != I2C_STOP) - return; - - i2c_clear_bus(i2cp); - - IOPORT1->PIN_CNF[cfg->scl_pad] = I2C_PIN_CNF; - IOPORT1->PIN_CNF[cfg->sda_pad] = I2C_PIN_CNF; - - i2c->EVENTS_RXDREADY = 0; - i2c->EVENTS_TXDSENT = 0; -#if CORTEX_MODEL >= 4 - (void)i2c->EVENTS_RXDREADY; - (void)i2c->EVENTS_TXDSENT; -#endif -#if NRF_SERIES == 51 - i2c->PSELSCL = cfg->scl_pad; - i2c->PSELSDA = cfg->sda_pad; -#else - i2c->PSEL.SCL = cfg->scl_pad; - i2c->PSEL.SDA = cfg->sda_pad; -#endif - - switch (cfg->clock) { - case 100000: - i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K100 << TWI_FREQUENCY_FREQUENCY_Pos; - break; - case 250000: - i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K250 << TWI_FREQUENCY_FREQUENCY_Pos; - break; - case 400000: - i2c->FREQUENCY = TWI_FREQUENCY_FREQUENCY_K400 << TWI_FREQUENCY_FREQUENCY_Pos; - break; - default: - osalDbgAssert(0, "invalid I2C frequency"); - break; - }; - - nvicEnableVector(I2C_IRQ_NUM, I2C_IRQ_PRI); - - i2c->INTENSET = TWI_INTENSET_TXDSENT_Msk | TWI_INTENSET_STOPPED_Msk | - TWI_INTENSET_ERROR_Msk | TWI_INTENSET_RXDREADY_Msk; - - i2c->ENABLE = TWI_ENABLE_ENABLE_Enabled << TWI_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_TWI_Type *i2c = i2cp->i2c; - const I2CConfig *cfg = i2cp->config; - - if (i2cp->state != I2C_STOP) { - - i2c->ENABLE = TWI_ENABLE_ENABLE_Disabled << TWI_ENABLE_ENABLE_Pos; - - i2c->INTENCLR = TWI_INTENSET_TXDSENT_Msk | TWI_INTENSET_STOPPED_Msk | - TWI_INTENSET_ERROR_Msk | TWI_INTENSET_RXDREADY_Msk; - - nvicDisableVector(I2C_IRQ_NUM); - - 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_TWI_Type *i2c = i2cp->i2c; - - (void)timeout; - msg_t msg; - - i2cp->errors = I2C_NO_ERROR; - i2cp->addr = addr; - - i2cp->txptr = txbuf; - i2cp->txbytes = txbytes; - - i2cp->rxptr = rxbuf; - i2cp->rxbytes = rxbytes; - - i2c->ADDRESS = addr; - - tx_resume_count = 0; - rx_resume_count = 0; - stop_count = 0; - - if (i2cp->txbytes) { - - i2c->TXD = *i2cp->txptr++; - i2c_setup_shortcut(i2cp); - i2c->TASKS_STARTTX = 1; - } else if (i2cp->rxbytes) { - - i2c_setup_shortcut(i2cp); - 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. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @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. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @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/hal_i2c_lld.h b/os/hal/ports/NRF5/LLD/hal_i2c_lld.h deleted file mode 100644 index 578d69b..0000000 --- a/os/hal/ports/NRF5/LLD/hal_i2c_lld.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - 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/LLD/hal_i2c_lld.h - * @brief NRF5 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. */ -/*===========================================================================*/ - -#define STATE_STOP 0x00 -#define STATE_SEND 0x01 -#define STATE_RECV 0x02 -#define STATE_DUMMY 0x03 - -/*===========================================================================*/ -/* 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 Type used to control the ISR state machine. */ -typedef uint8_t intstate_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__) -#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#elif CH_CFG_USE_SEMAPHORES - semaphore_t semaphore; -#endif -#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 Pointer to the buffer with data to send. */ - const uint8_t *txptr; - /* @brief Number of bytes of data to send. */ - size_t txbytes; - /* @brief Pointer to the buffer to put received data. */ - uint8_t *rxptr; - /* @brief Number of bytes of data to receive. */ - size_t rxbytes; - /* @brief Tracks current ISR state. */ - intstate_t intstate; - /* @brief Low-level register access. */ - NRF_TWI_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/LLD/hal_pal_lld.c b/os/hal/ports/NRF5/LLD/hal_pal_lld.c deleted file mode 100644 index 21e4b0b..0000000 --- a/os/hal/ports/NRF5/LLD/hal_pal_lld.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - 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 NRF5/LLD/hal_pal_lld.c - * @brief NRF5 PAL subsystem low level driver source. - * - * @addtogroup PAL - * @{ - */ - -#include "osal.h" -#include "hal.h" - -#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -void _pal_lld_setpadmode(ioportid_t port, uint8_t pad, iomode_t mode) -{ - (void)port; - osalDbgAssert(pad < PAL_IOPORTS_WIDTH, "pal_lld_setpadmode() - invalid pad"); - - switch (mode) { - case PAL_MODE_RESET: - case PAL_MODE_UNCONNECTED: - IOPORT1->PIN_CNF[pad] = - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos); - break; - case PAL_MODE_INPUT: - case PAL_MODE_INPUT_ANALOG: - IOPORT1->PIN_CNF[pad] = - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << 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); - break; - case PAL_MODE_INPUT_PULLUP: - IOPORT1->PIN_CNF[pad] = - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_PULL_Pullup << 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); - break; - case PAL_MODE_INPUT_PULLDOWN: - IOPORT1->PIN_CNF[pad] = - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_PULL_Pulldown << 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); - break; - case PAL_MODE_OUTPUT_PUSHPULL: - IOPORT1->PIN_CNF[pad] = - (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) | - (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) | - (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) | - (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); - break; - case PAL_MODE_OUTPUT_OPENDRAIN: - IOPORT1->PIN_CNF[pad] = - (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_Disconnect << GPIO_PIN_CNF_INPUT_Pos) | - (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos); - break; - default: - osalDbgAssert(FALSE, "invalid pal mode"); - break; - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief NRF5 I/O ports configuration. - * - * @param[in] config the NRF5 ports configuration - * - * @notapi - */ -void _pal_lld_init(const PALConfig *config) -{ - uint8_t i; - - for (i = 0; i < TOTAL_GPIO_PADS; i++) { - pal_lld_setpadmode(IOPORT1, i, config->pads[i]); - } -} - -/** - * @brief Pads mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * - * @param[in] port the port identifier - * @param[in] mask the group mask - * @param[in] mode the mode - * - * @notapi - */ -void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode) -{ - uint8_t i; - - for (i = 0; i < TOTAL_GPIO_PADS; i++, mask >>= 1) { - if (mask & 1) { - pal_lld_setpadmode(port, i, mode); - } - } -} - -#endif /* HAL_USE_PAL == TRUE */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_pal_lld.h b/os/hal/ports/NRF5/LLD/hal_pal_lld.h deleted file mode 100644 index 745afd3..0000000 --- a/os/hal/ports/NRF5/LLD/hal_pal_lld.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - 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 NRF5/LLD/hal_pal_lld.h - * @brief NRF5 PAL subsystem low level driver header. - * - * @addtogroup PAL - * @{ - */ - -#ifndef HAL_PAL_LLD_H -#define HAL_PAL_LLD_H - -#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Unsupported modes and specific modes */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* I/O Ports Types and constants. */ -/*===========================================================================*/ - -#define TOTAL_GPIO_PADS 32 - -/** - * @name Port related definitions - * @{ - */ -/** - * @brief Width, in bits, of an I/O port. - */ -#define PAL_IOPORTS_WIDTH 32U - -/** - * @brief Whole port mask. - * @brief This macro specifies all the valid bits into a port. - */ -#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFFU) -/** @} */ - -/** - * @name Line handling macros - * @{ - */ -/** - * @brief Forms a line identifier. - * @details A port/pad pair are encoded into an @p ioline_t type. The encoding - * of this type is platform-dependent. - */ -#define PAL_LINE(port, pad) \ - ((ioline_t)((uint32_t)(pad))) - -/** - * @brief Decodes a port identifier from a line identifier. - */ -#define PAL_PORT(line) \ - ((ioportid_t)(IOPORT1)) - -/** - * @brief Decodes a pad identifier from a line identifier. - */ -#define PAL_PAD(line) \ - ((uint32_t)(line)) - -/** - * @brief Value identifying an invalid line. - */ -#define PAL_NOLINE ((ioline_t)-1) -/** @} */ - -/** - * @brief Generic I/O ports static initializer. - * @details An instance of this structure must be passed to @p palInit() at - * system startup time in order to initialized the digital I/O - * subsystem. This represents only the initial setup, specific pads - * or whole ports can be reprogrammed at later time. - * @note Implementations may extend this structure to contain more, - * architecture dependent, fields. - */ -typedef struct { - uint32_t pads[TOTAL_GPIO_PADS]; -} PALConfig; - -/** - * @brief Digital I/O port sized unsigned type. - */ -typedef uint32_t ioportmask_t; - -/** - * @brief Digital I/O modes. - */ -typedef uint8_t iomode_t; - -/** - * @brief Type of an I/O line. - */ -typedef uint32_t ioline_t; - -/** - * @brief Port Identifier. - * @details This type can be a scalar or some kind of pointer, do not make - * any assumption about it, use the provided macros when populating - * variables of this type. - */ -typedef NRF_GPIO_Type *ioportid_t; - -/*===========================================================================*/ -/* I/O Ports Identifiers. */ -/*===========================================================================*/ - -/** - * @brief First I/O port identifier. - * @details Low level drivers can define multiple ports, it is suggested to - * use this naming convention. - */ -#if NRF_SERIES == 51 -#define IOPORT1 NRF_GPIO -#else -#define IOPORT1 NRF_P0 -#endif - -/*===========================================================================*/ -/* Implementation, some of the following macros could be implemented as */ -/* functions, if so please put them in pal_lld.c. */ -/*===========================================================================*/ - -/** - * @brief Low level PAL subsystem initialization. - * - * @param[in] config architecture-dependent ports configuration - * - * @notapi - */ -#define pal_lld_init(config) _pal_lld_init(config) - -/** - * @brief Reads the physical I/O port states. - * - * @param[in] port port identifier - * @return The port bits. - * - * @notapi - */ -#define pal_lld_readport(port) (IOPORT1->IN) - -/** - * @brief Reads the output latch. - * @details The purpose of this function is to read back the latched output - * value. - * - * @param[in] port port identifier - * @return The latched logical states. - * - * @notapi - */ -#define pal_lld_readlatch(port) (IOPORT1->OUT) - -/** - * @brief Writes a bits mask on a I/O port. - * - * @param[in] port port identifier - * @param[in] bits bits to be written on the specified port - * - * @notapi - */ -#define pal_lld_writeport(port, bits) (IOPORT1->OUT = (bits)) - -/** - * @brief Sets a bits mask on a I/O port. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] bits bits to be ORed on the specified port - * - * @notapi - */ -#define pal_lld_setport(port, bits) (IOPORT1->OUTSET = (bits)) - - -/** - * @brief Clears a bits mask on a I/O port. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] bits bits to be cleared on the specified port - * - * @notapi - */ -#define pal_lld_clearport(port, bits) (IOPORT1->OUTCLR = (bits)) - -/** - * @brief Pads group mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * @note Programming an unknown or unsupported mode is silently ignored. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset group bit offset within the port - * @param[in] mode group mode - * - * @notapi - */ -#define pal_lld_setgroupmode(port, mask, offset, mode) \ - _pal_lld_setgroupmode(port, mask << offset, mode) - -/** - * @brief Reads a logical state from an I/O pad. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @return The logical state. - * @retval PAL_LOW low logical state. - * @retval PAL_HIGH high logical state. - * - * @notapi - */ -#define pal_lld_readpad(port, pad) \ - ((IOPORT1->IN & ((uint32_t) 1 << pad)) ? PAL_HIGH : PAL_LOW) - -/** - * @brief Writes a logical state on an output pad. - * @note This function is not meant to be invoked directly by the - * application code. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] bit logical value, the value must be @p PAL_LOW or - * @p PAL_HIGH - * - * @notapi - */ -#define pal_lld_writepad(port, pad, bit) \ - do { \ - (void)port; \ - if (bit == PAL_HIGH) \ - IOPORT1->OUTSET = ((uint32_t) 1 << pad); \ - else \ - IOPORT1->OUTCLR = ((uint32_t) 1 << pad); \ - } while (false) - -/** - * @brief Sets a pad logical state to @p PAL_HIGH. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * - * @notapi - */ -#define pal_lld_setpad(port, pad) (IOPORT1->OUTSET = (uint32_t) 1 << (pad)) - -/** - * @brief Clears a pad logical state to @p PAL_LOW. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * - * @notapi - */ -#define pal_lld_clearpad(port, pad) (IOPORT1->OUTCLR = (uint32_t) 1 << (pad)) - -/** - * @brief Toggles a pad logical state. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * - * @notapi - */ -#define pal_lld_togglepad(port, pad) \ - do { \ - uint8_t bit = (IOPORT1->OUT >> (pad)) & 1; \ - if (bit) \ - IOPORT1->OUTCLR = 1 << (pad); \ - else \ - IOPORT1->OUTSET = 1 << (pad); \ - } while (0) - -/** - * @brief Pad mode setup. - * @details This function programs a pad with the specified mode. - * @note The @ref PAL provides a default software implementation of this - * functionality, implement this function if can optimize it by using - * special hardware functionalities or special coding. - * @note Programming an unknown or unsupported mode is silently ignored. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] mode pad mode - * - * @notapi - */ -#define pal_lld_setpadmode(port, pad, mode) _pal_lld_setpadmode(port, pad, mode) - -#if !defined(__DOXYGEN__) -extern const PALConfig pal_default_config; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void _pal_lld_init(const PALConfig *config); - void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode); - void _pal_lld_setpadmode(ioportid_t port, - uint8_t pad, - iomode_t mode); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_PAL == TRUE */ - -#endif /* HAL_PAL_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_qei_lld.c b/os/hal/ports/NRF5/LLD/hal_qei_lld.c deleted file mode 100644 index d3b99cd..0000000 --- a/os/hal/ports/NRF5/LLD/hal_qei_lld.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - ChibiOS - Copyright (C) 2016..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. -*/ - -/** - * @file NRF51/hal_qei_lld.c - * @brief NRF51 QEI subsystem low level driver. - * - * @addtogroup QEI - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_QEI == TRUE) || defined(__DOXYGEN__) - - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief QEID1 driver identifier. - */ -#if NRF5_QEI_USE_QDEC0 || defined(__DOXYGEN__) -QEIDriver QEID1; -#endif - - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Common IRQ handler. - * - * @param[in] qeip pointer to an QEIDriver - */ -static void serve_interrupt(QEIDriver *qeip) { - NRF_QDEC_Type *qdec = qeip->qdec; - -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - /* Accumulator overflowed - */ - if (qdec->EVENTS_ACCOF) { - qdec->EVENTS_ACCOF = 0; -#if CORTEX_MODEL >= 4 - (void)qdec->EVENTS_ACCOF; -#endif - - qeip->overflowed++; - if (qeip->config->overflowed_cb) - qeip->config->overflowed_cb(qeip); - } -#endif - - /* Report ready - */ - if (qdec->EVENTS_REPORTRDY) { - qdec->EVENTS_REPORTRDY = 0; -#if CORTEX_MODEL >= 4 - (void)qdec->EVENTS_REPORTRDY; -#endif - - /* Read (and clear counters due to shortcut) */ - int16_t acc = ( int16_t)qdec->ACCREAD; - uint16_t accdbl = (uint16_t)qdec->ACCDBLREAD; - - /* Inverse direction if requested */ - if (qeip->config->dirinv) - acc = -acc; // acc is [-1024..+1023], its okay on int16_t - - /* Adjust counter */ - qeiAdjustI(qeip, acc); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_QEI_USE_QDEC0 == TRUE -/** - * @brief Quadrature decoder vector (QDEC) - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector88) { - - OSAL_IRQ_PROLOGUE(); - serve_interrupt(&QEID1); - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level QEI driver initialization. - * - * @notapi - */ -void qei_lld_init(void) { - -#if NRF5_QEI_USE_QDEC0 == TRUE - /* Driver initialization.*/ - qeiObjectInit(&QEID1); - QEID1.qdec = NRF_QDEC; -#endif -} - -/** - * @brief Configures and activates the QEI peripheral. - * - * @param[in] qeip pointer to the @p QEIDriver object - * - * @notapi - */ -void qei_lld_start(QEIDriver *qeip) { - NRF_QDEC_Type *qdec = qeip->qdec; - const QEIConfig *cfg = qeip->config; - - if (qeip->state == QEI_STOP) { - /* Set Pins */ - palSetLineMode(cfg->phase_a, PAL_MODE_INPUT); - palSetLineMode(cfg->phase_b, PAL_MODE_INPUT); -#if NRF5_QEI_USE_LED == TRUE - if (cfg->led != PAL_NOLINE) { - palSetLineMode(cfg->led, PAL_MODE_INPUT); - } -#endif - - /* Set interrupt masks and enable interrupt */ -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - qdec->INTENSET = QDEC_INTENSET_REPORTRDY_Msk | - QDEC_INTENSET_ACCOF_Msk; -#else - qdec->INTENSET = QDEC_INTENSET_REPORTRDY_Msk; -#endif -#if NRF5_QEI_USE_QDEC0 == TRUE - if (&QEID1 == qeip) { - nvicEnableVector(QDEC_IRQn, NRF5_QEI_QDEC0_IRQ_PRIORITY); - } -#endif - - /* Select pin for Phase A and Phase B */ -#if NRF_SERIES == 51 - qdec->PSELA = PAL_PAD(cfg->phase_a); - qdec->PSELB = PAL_PAD(cfg->phase_b); -#else - qdec->PSEL.A = PAL_PAD(cfg->phase_a); - qdec->PSEL.B = PAL_PAD(cfg->phase_b); -#endif - /* Select (optional) pin for LED, and configure it */ -#if NRF5_QEI_USE_LED == TRUE -#if NRF_SERIES == 51 - qdec->PSELLED = PAL_PAD(cfg->led); -#else - qdec->PSEL.LED = PAL_PAD(cfg->led); -#endif - qdec->LEDPOL = ((cfg->led_polarity == QEI_LED_POLARITY_LOW) - ? QDEC_LEDPOL_LEDPOL_ActiveLow - : QDEC_LEDPOL_LEDPOL_ActiveHigh) - << QDEC_LEDPOL_LEDPOL_Pos; - qdec->LEDPRE = cfg->led_warming; -#else -#if NRF_SERIES == 51 - qdec->PSELLED = (uint32_t)-1; -#else - qdec->PSEL.LED = (uint32_t)-1; -#endif -#endif - - /* Set sampling resolution and debouncing */ - qdec->SAMPLEPER = cfg->resolution; - qdec->DBFEN = (cfg->debouncing ? QDEC_DBFEN_DBFEN_Enabled - : QDEC_DBFEN_DBFEN_Disabled) - << QDEC_DBFEN_DBFEN_Pos; - - /* Define minimum sampling before reporting - and create shortcut to clear accumulation */ - qdec->REPORTPER = cfg->report; - qdec->SHORTS = QDEC_SHORTS_REPORTRDY_READCLRACC_Msk; - - /* Enable peripheric */ - qdec->ENABLE = 1; - } - - /* Initially state is stopped, events cleared */ - qdec->TASKS_STOP = 1; - qdec->EVENTS_SAMPLERDY = 0; - qdec->EVENTS_REPORTRDY = 0; - qdec->EVENTS_ACCOF = 0; -#if CORTEX_MODEL >= 4 - (void)qdec->EVENTS_SAMPLERDY; - (void)qdec->EVENTS_REPORTRDY; - (void)qdec->EVENTS_ACCOF; -#endif -} - -/** - * @brief Deactivates the QEI peripheral. - * - * @param[in] qeip pointer to the @p QEIDriver object - * - * @notapi - */ -void qei_lld_stop(QEIDriver *qeip) { - - NRF_QDEC_Type *qdec = qeip->qdec; - const QEIConfig *cfg = qeip->config; - - if (qeip->state == QEI_READY) { - qdec->TASKS_STOP = 1; - qdec->ENABLE = 0; - - /* Unset interrupt masks and disable interrupt */ -#if NRF5_QEI_USE_QDEC0 == TRUE - if (&QEID1 == qeip) { - nvicDisableVector(QDEC_IRQn); - } -#endif -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - qdec->INTENCLR = QDEC_INTENCLR_REPORTRDY_Msk | - QDEC_INTENCLR_ACCOF_Msk; -#else - qdec->INTENCLR = QDEC_INTENCLR_REPORTRDY_Msk; -#endif - - /* Return pins to reset state */ - palSetLineMode(cfg->phase_a, PAL_MODE_RESET); - palSetLineMode(cfg->phase_b, PAL_MODE_RESET); -#if NRF5_QEI_USE_LED == TRUE - if (cfg->led != PAL_NOLINE) { - palSetLineMode(cfg->led, PAL_MODE_RESET); - } -#endif - } -} - -/** - * @brief Enables the input capture. - * - * @param[in] qeip pointer to the @p QEIDriver object - * - * @notapi - */ -void qei_lld_enable(QEIDriver *qeip) { -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - qeip->overflowed = 0; -#endif - - qeip->qdec->EVENTS_SAMPLERDY = 0; - qeip->qdec->EVENTS_REPORTRDY = 0; - qeip->qdec->EVENTS_ACCOF = 0; -#if CORTEX_MODEL >= 4 - (void)qeip->qdec->EVENTS_SAMPLERDY; - (void)qeip->qdec->EVENTS_REPORTRDY; - (void)qeip->qdec->EVENTS_ACCOF; -#endif - qeip->qdec->TASKS_START = 1; -} - -/** - * @brief Disables the input capture. - * - * @param[in] qeip pointer to the @p QEIDriver object - * - * @notapi - */ -void qei_lld_disable(QEIDriver *qeip) { - qeip->qdec->TASKS_STOP = 1; -} - - -#endif /* HAL_USE_QEI */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_qei_lld.h b/os/hal/ports/NRF5/LLD/hal_qei_lld.h deleted file mode 100644 index 85c96a5..0000000 --- a/os/hal/ports/NRF5/LLD/hal_qei_lld.h +++ /dev/null @@ -1,390 +0,0 @@ -/* - ChibiOS - Copyright (C) 2016..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. -*/ - -/** - * @file NRF51/hal_qei_lld.h - * @brief NRF51 QEI subsystem low level driver header. - * - * @note Not tested with LED pin - * - * @note Pins are configured as input with no pull. - * - * @addtogroup QEI - * @{ - */ - -#ifndef HAL_QEI_LLD_H -#define HAL_QEI_LLD_H - -#if (HAL_USE_QEI == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief For LED active on LOW - */ -#define QEI_LED_POLARITY_LOW 0 - -/** - * @brief For LED active on HIGH - */ -#define QEI_LED_POLARITY_HIGH 1 - -/** - * @brief Mininum usable value for defining counter underflow - */ -#define QEI_COUNT_MIN (-2147483648) - -/** - * @brief Maximum usable value for defining counter overflow - */ -#define QEI_COUNT_MAX ( 2147483647) - - - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief LED control enable switch. - * @details If set to @p TRUE the support for LED control - * is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_QEI_USE_LED) || defined(__DOXYGEN__) -#define NRF5_QEI_USE_LED FALSE -#endif - -/** - * @brief Accumulator overflow notification enable switch. - * @details If set to @p TRUE the support for accumulator overflow - * is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_QEI_USE_ACC_OVERFLOWED_CB) || defined(__DOXYGEN__) -#define NRF5_QEI_USE_ACC_OVERFLOWED_CB FALSE -#endif - -/** - * @brief QEID1 driver enable switch. - * @details If set to @p TRUE the support for QEID1 is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_QEI_USE_QDEC0) || defined(__DOXYGEN__) -#define NRF5_QEI_USE_QDEC0 FALSE -#endif - -/** - * @brief QEID interrupt priority level setting for QDEC0. - */ -#if !defined(NRF5_QEI_QDEC0_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_QEI_QDEC0_IRQ_PRIORITY 2 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if NRF5_QEI_USE_QDEC0 == FALSE -#error "Requesting QEI driver, but no QDEC peripheric attached" -#endif - -#if NRF5_QEI_USE_QDEC0 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_QEI_QDEC0_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to QDEC0" -#endif - - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief QEI count mode. - */ -typedef enum { - QEI_MODE_QUADRATURE = 0, /**< Quadrature encoder mode. */ -} qeimode_t; - -/** - * @brief QEI resolution. - */ -typedef enum { - QEI_RESOLUTION_128us = 0x00UL, /**< 128us sample period. */ - QEI_RESOLUTION_256us = 0x01UL, /**< 256us sample period. */ - QEI_RESOLUTION_512us = 0x02UL, /**< 512us sample period. */ - QEI_RESOLUTION_1024us = 0x03UL, /**< 1024us sample period. */ - QEI_RESOLUTION_2048us = 0x04UL, /**< 2048us sample period. */ - QEI_RESOLUTION_4096us = 0x05UL, /**< 4096us sample period. */ - QEI_RESOLUTION_8192us = 0x06UL, /**< 8192us sample period. */ - QEI_RESOLUTION_16384us = 0x07UL, /**< 16384us sample period. */ -} qeiresolution_t; - -/** - * @brief Clusters of samples. - */ -typedef enum { - QEI_REPORT_10 = 0x00UL, /**< 10 samples per report. */ - QEI_REPORT_40 = 0x01UL, /**< 40 samples per report. */ - QEI_REPORT_80 = 0x02UL, /**< 80 samples per report. */ - QEI_REPORT_120 = 0x03UL, /**< 120 samples per report. */ - QEI_REPORT_160 = 0x04UL, /**< 160 samples per report. */ - QEI_REPORT_200 = 0x05UL, /**< 200 samples per report. */ - QEI_REPORT_240 = 0x06UL, /**< 240 samples per report. */ - QEI_REPORT_280 = 0x07UL, /**< 280 samples per report. */ -} qeireport_t; - -/** - * @brief QEI direction inversion. - */ -typedef enum { - QEI_DIRINV_FALSE = 0, /**< Do not invert counter direction. */ - QEI_DIRINV_TRUE = 1, /**< Invert counter direction. */ -} qeidirinv_t; - -/** - * @brief QEI counter type. - */ -typedef int16_t qeicnt_t; - -/** - * @brief QEI delta type. - */ -typedef int16_t qeidelta_t; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Count mode. - */ - qeimode_t mode; - /** - * @brief Resolution. - */ - qeiresolution_t resolution; - /** - * @brief Direction inversion. - */ - qeidirinv_t dirinv; - /** - * @brief Handling of counter overflow/underflow - * - * @details When overflow occurs, the counter value is updated - * according to: - * - QEI_OVERFLOW_DISCARD: - * discard the update value, counter doesn't change - * - QEI_OVERFLOW_MINMAX - * counter will be updated to reach min or max - * - QEI_OVERFLOW_WRAP: - * counter value will wrap around - */ - qeioverflow_t overflow; - /** - * @brief Min count value. - * - * @note If min == max, then QEI_COUNT_MIN is used. - */ - qeicnt_t min; - /** - * @brief Max count value. - * - * @note If min == max, then QEI_COUNT_MAX is used. - */ - qeicnt_t max; - /** - * @brief Notify of value change - * - * @note Called from ISR context. - */ - qeicallback_t notify_cb; - /** - * @brief Notify of overflow - * - * @note Overflow notification is performed after - * value changed notification. - * @note Called from ISR context. - */ - void (*overflow_cb)(QEIDriver *qeip, qeidelta_t delta); - /* End of the mandatory fields.*/ - /** - * @brief Line for reading Phase A - */ - ioline_t phase_a; - /** - * @brief Line for reading Phase B - */ - ioline_t phase_b; -#if (NRF5_QEI_USE_LED == TRUE) || defined(__DOXYGEN__) - /** - * @brief Line used to control LED - * - * @note If LED is not controlled by MCU, you need to use the - * PAL_NOLINE value. - */ - ioline_t led; - /** - * @brief Period in µs the LED is switched on prior to sampling. - * - * @details LED warming is expressed in micro-seconds and value - * is [0..511] - * - * @note 31µs is the recommanded default. - * - * @note If debouncing is activated, LED is always on for the - * whole sampling period (aka: resolution) - */ - uint16_t led_warming; - /** - * @brief LED polarity to used (when LED is controlled by MCU) - */ - uint8_t led_polarity; -#endif - /** - * @brief Activate debouncing filter - * - * @note If LED is controlled by MCU, the led_warming is ignored and, - * LED is always on for the whole sampling period (aka: resolution) - */ - bool debouncing; - /** - * @brief Number of samples per report - * - * @details Default to QEI_REPORT_10 - */ - qeireport_t report; -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - /** - * @brief Notify of internal accumulator overflowed - * (ie: MCU discarding samples) - * - * @note Called from ISR context. - */ - qeicallback_t overflowed_cb; -#endif -} QEIConfig; - -/** - * @brief Structure representing an QEI driver. - */ -struct QEIDriver { - /** - * @brief Driver state. - */ - qeistate_t state; - /** - * @brief Last count value. - */ - qeicnt_t last; - /** - * @brief Current configuration data. - */ - const QEIConfig *config; -#if defined(QEI_DRIVER_EXT_FIELDS) - QEI_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Counter - */ - qeicnt_t count; -#if NRF5_QEI_USE_ACC_OVERFLOWED_CB == TRUE - /** - * @brief Number of time the MCU discarded updates due to - * accumulator overflow - */ - uint32_t overflowed; -#endif - /** - * @brief Pointer to the QDECx registers block. - */ - NRF_QDEC_Type *qdec; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Returns the counter value. - * - * @param[in] qeip pointer to the @p QEIDriver object - * @return The current counter value. - * - * @notapi - */ -#define qei_lld_get_count(qeip) ((qeip)->count) - - -/** - * @brief Set the counter value. - * - * @param[in] qeip pointer to the @p QEIDriver object - * @param[in] value counter value - * - * @notapi - */ -#define qei_lld_set_count(qeip, value) \ - if ((qeip)->count != ((qeicnt_t)value)) { \ - (qeip)->count = value; \ - if ((qeip)->config->notify_cb) \ - (qeip)->config->notify_cb(qeip); \ - } while(0) - - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if NRF5_QEI_USE_QDEC0 && !defined(__DOXYGEN__) -extern QEIDriver QEID1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void qei_lld_init(void); - void qei_lld_start(QEIDriver *qeip); - void qei_lld_stop(QEIDriver *qeip); - void qei_lld_enable(QEIDriver *qeip); - void qei_lld_disable(QEIDriver *qeip); - qeidelta_t qei_lld_adjust_count(QEIDriver *qeip, qeidelta_t delta); -#ifdef __cplusplus -} -#endif - -/*===========================================================================*/ -/* To be moved in hal_qei */ -/*===========================================================================*/ - -void qeiSetCount(QEIDriver *qeip, qeicnt_t value); -qeidelta_t qeiAdjust(QEIDriver *qeip, qeidelta_t delta); - -#endif /* HAL_USE_QEI */ - -#endif /* HAL_QEI_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_rng_lld.c b/os/hal/ports/NRF5/LLD/hal_rng_lld.c deleted file mode 100644 index 9712150..0000000 --- a/os/hal/ports/NRF5/LLD/hal_rng_lld.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - RNG for ChibiOS - 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. -*/ - -/** - * @file NRF5/LLD/hal_rng_lld.c - * @brief NRF5 RNG subsystem low level driver source. - * - * @addtogroup RNG - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_RNG == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/** - * @brief RNG default configuration. - */ -static const RNGConfig default_config = { - .digital_error_correction = 1, -}; - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief RNGD1 driver identifier.*/ -#if NRF5_RNG_USE_RNG0 || defined(__DOXYGEN__) -RNGDriver RNGD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level RNG driver initialization. - * - * @notapi - */ -void rng_lld_init(void) { - rngObjectInit(&RNGD1); - RNGD1.rng = NRF_RNG; - RNGD1.irq = RNG_IRQn; -} - -/** - * @brief Configures and activates the RNG peripheral. - * - * @param[in] rngp pointer to the @p RNGDriver object - * - * @notapi - */ -void rng_lld_start(RNGDriver *rngp) { - NRF_RNG_Type *rng = rngp->rng; - - /* If not specified, set default configuration */ - if (rngp->config == NULL) - rngp->config = &default_config; - - /* Configure digital error correction */ - if (rngp->config->digital_error_correction) - rng->CONFIG |= RNG_CONFIG_DERCEN_Msk; - else - rng->CONFIG &= ~RNG_CONFIG_DERCEN_Msk; - - /* Clear pending events */ - rng->EVENTS_VALRDY = 0; -#if CORTEX_MODEL >= 4 - (void)rng->EVENTS_VALRDY; -#endif - - /* Set interrupt mask */ - rng->INTENSET = RNG_INTENSET_VALRDY_Msk; - - /* Start */ - rng->TASKS_START = 1; -} - - -/** - * @brief Deactivates the RNG peripheral. - * - * @param[in] rngp pointer to the @p RNGDriver object - * - * @notapi - */ -void rng_lld_stop(RNGDriver *rngp) { - NRF_RNG_Type *rng = rngp->rng; - - /* Stop peripheric */ - rng->TASKS_STOP = 1; -} - - -/** - * @brief Write random bytes; - * - * @param[in] rngp pointer to the @p RNGDriver object - * @param[in] n size of buf in bytes - * @param[in] buf @p buffer location - * - * @notapi - */ -msg_t rng_lld_write(RNGDriver *rngp, uint8_t *buf, size_t n, - systime_t timeout) { - NRF_RNG_Type *rng = rngp->rng; - size_t i; - - for (i = 0 ; i < n ; i++) { - /* Wait for byte ready - * It take about 677µs to generate a new byte, not sure if - * forcing a context switch will be a benefit - */ - while (rng->EVENTS_VALRDY == 0) { - /* Sleep and wakeup on ARM event (interrupt) */ - SCB->SCR |= SCB_SCR_SEVONPEND_Msk; - __SEV(); - __WFE(); - __WFE(); - } - - /* Read byte */ - buf[i] = (char)rng->VALUE; - - /* Mark as read */ - rng->EVENTS_VALRDY = 0; -#if CORTEX_MODEL >= 4 - (void)rng->EVENTS_VALRDY; -#endif - - /* Clear interrupt so we can wake up again */ - nvicClearPending(rngp->irq); - } - return MSG_OK; -} - -#endif /* HAL_USE_RNG */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_rng_lld.h b/os/hal/ports/NRF5/LLD/hal_rng_lld.h deleted file mode 100644 index 5c56be2..0000000 --- a/os/hal/ports/NRF5/LLD/hal_rng_lld.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - RNG for ChibiOS - 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. -*/ - -/** - * @file NRF5/LLD/hal_rng_lld.h - * @brief NRF5 RNG subsystem low level driver header. - * - * @addtogroup RNG - * @{ - */ - -#ifndef HAL_RNG_LLD_H -#define HAL_RNG_LLD_H - -#if (HAL_USE_RNG == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief RNGD1 driver enable switch. - * @details If set to @p TRUE the support for RNGD1 is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_RNG_USE_RNG0) || defined(__DOXYGEN__) -#define NRF5_RNG_USE_RNG0 FALSE -#endif - -/** - * @brief RNG interrupt priority level setting for RNG0. - */ -#if !defined(NRF5_RNG_RNG0_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_RNG_RNG0_IRQ_PRIORITY 3 -#endif - - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if NRF5_RNG_USE_RNG0 == FALSE -#error "Requesting RNG driver, but no RNG peripheric attached" -#endif - -#if NRF5_RNG_USE_RNG0 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_RNG_RNG0_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to RNG0" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an RNG driver. - */ -typedef struct RNGDriver RNGDriver; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /* End of the mandatory fields.*/ - /** - * @brief Activate the digital error correction - * - * @details A digital corrector algorithm is employed to remove any - * bias toward '1' or '0'. Disabling it offers a substantial - * speed advantage, but may result in a statistical distribution - * that is not perfectly uniform. - * - * @note For nRF51, on average, it take 167µs to get a byte without - * digitial error correction and 677µs with, but no garantee - * is made on the necessary time to generate one byte. - */ - uint8_t digital_error_correction:1; -} RNGConfig; - - -/** - * @brief Structure representing an RNG driver. - */ -struct RNGDriver { - /** - * @brief Driver state. - */ - rngstate_t state; - /** - * @brief Current configuration data. - */ - const RNGConfig *config; -#if RNG_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* RNG_USE_MUTUAL_EXCLUSION */ - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the RNGx registers block. - */ - NRF_RNG_Type *rng; - /** - * @brief IRQ number - */ - uint32_t irq; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if NRF5_RNG_USE_RNG0 && !defined(__DOXYGEN__) -extern RNGDriver RNGD1; -#endif /* NRF5_RNG_USE_RNG0 */ - -#ifdef __cplusplus -extern "C" { -#endif - void rng_lld_init(void); - void rng_lld_start(RNGDriver *rngp); - void rng_lld_stop(RNGDriver *rngp); - msg_t rng_lld_write(RNGDriver *rngp, uint8_t *buf, size_t n, - systime_t timeout); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_RNG */ - -#endif /* HAL_RNG_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_serial_lld.c b/os/hal/ports/NRF5/LLD/hal_serial_lld.c deleted file mode 100644 index 42091e8..0000000 --- a/os/hal/ports/NRF5/LLD/hal_serial_lld.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - 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 NRF5/LLD/hal_serial_lld.c - * @brief NRF5 serial subsystem low level driver source. - * - * @addtogroup SERIAL - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) - -#if NRF_SERIES == 51 -#include "nrf51.h" -#elif NRF_SERIES == 52 -#include "nrf52.h" -#define UART0_IRQn UARTE0_UART0_IRQn -#endif - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USART1 serial driver identifier.*/ -#if (NRF5_SERIAL_USE_UART0 == TRUE) || defined(__DOXYGEN__) -SerialDriver SD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** - * @brief Driver default configuration. - */ -static const SerialConfig default_config = { - .speed = 38400, - .tx_pad = NRF5_SERIAL_PAD_DISCONNECTED, - .rx_pad = NRF5_SERIAL_PAD_DISCONNECTED, -#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) - .rts_pad = NRF5_SERIAL_PAD_DISCONNECTED, - .cts_pad = NRF5_SERIAL_PAD_DISCONNECTED, -#endif -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/* - * @brief Maps a baudrate speed to a BAUDRATE register value. - */ - -/** - * @brief Common UART configuration. - * - */ -static void configure_uart(const SerialConfig *config) -{ - uint32_t speed = UART_BAUDRATE_BAUDRATE_Baud250000; - - switch (config->speed) { - case 1200: speed = UART_BAUDRATE_BAUDRATE_Baud1200; break; - case 2400: speed = UART_BAUDRATE_BAUDRATE_Baud2400; break; - case 4800: speed = UART_BAUDRATE_BAUDRATE_Baud4800; break; - case 9600: speed = UART_BAUDRATE_BAUDRATE_Baud9600; break; - case 14400: speed = UART_BAUDRATE_BAUDRATE_Baud14400; break; - case 19200: speed = UART_BAUDRATE_BAUDRATE_Baud19200; break; - case 28800: speed = UART_BAUDRATE_BAUDRATE_Baud28800; break; - case 38400: speed = UART_BAUDRATE_BAUDRATE_Baud38400; break; - case 57600: speed = UART_BAUDRATE_BAUDRATE_Baud57600; break; - case 76800: speed = UART_BAUDRATE_BAUDRATE_Baud76800; break; - case 115200: speed = UART_BAUDRATE_BAUDRATE_Baud115200; break; - case 230400: speed = UART_BAUDRATE_BAUDRATE_Baud230400; break; - case 250000: speed = UART_BAUDRATE_BAUDRATE_Baud250000; break; - case 460800: speed = UART_BAUDRATE_BAUDRATE_Baud460800; break; - case 921600: speed = UART_BAUDRATE_BAUDRATE_Baud921600; break; - case 1000000: speed = UART_BAUDRATE_BAUDRATE_Baud1M; break; - default: osalDbgAssert(0, "invalid baudrate"); break; - }; - - /* Configure PINs mode */ - if (config->tx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { - palSetPadMode(IOPORT1, config->tx_pad, PAL_MODE_OUTPUT_PUSHPULL); - } - if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { - palSetPadMode(IOPORT1, config->rx_pad, PAL_MODE_INPUT); - } -#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) - if (config->rts_pad != NRF5_SERIAL_PAD_DISCONNECTED) { - palSetPadMode(IOPORT1, config->rts_pad, PAL_MODE_OUTPUT_PUSHPULL); - } - if (config->cts_pad != NRF5_SERIAL_PAD_DISCONNECTED) { - palSetPadMode(IOPORT1, config->cts_pad, PAL_MODE_INPUT); - } -#endif - - /* Select PINs used by UART */ - NRF_UART0->PSELTXD = config->tx_pad; - NRF_UART0->PSELRXD = config->rx_pad; -#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) - NRF_UART0->PSELRTS = config->rts_pad; - NRF_UART0->PSELCTS = config->cts_pad; -#else - NRF_UART0->PSELRTS = NRF5_SERIAL_PAD_DISCONNECTED; - NRF_UART0->PSELCTS = NRF5_SERIAL_PAD_DISCONNECTED; -#endif - - /* Set baud rate */ - NRF_UART0->BAUDRATE = speed; - - /* Set config */ - NRF_UART0->CONFIG = (UART_CONFIG_PARITY_Excluded << UART_CONFIG_PARITY_Pos); - - /* Adjust flow control */ -#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) - if ((config->rts_pad < TOTAL_GPIO_PADS) || - (config->cts_pad < TOTAL_GPIO_PADS)) { - NRF_UART0->CONFIG |= UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos; - } else { - NRF_UART0->CONFIG &= ~(UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos); - } -#else - NRF_UART0->CONFIG &= ~(UART_CONFIG_HWFC_Enabled << UART_CONFIG_HWFC_Pos); -#endif - - /* Enable UART and clear events */ - NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Enabled; - NRF_UART0->EVENTS_RXDRDY = 0; - NRF_UART0->EVENTS_TXDRDY = 0; -#if CORTEX_MODEL >= 4 - (void)NRF_UART0->EVENTS_RXDRDY; - (void)NRF_UART0->EVENTS_TXDRDY; -#endif - - if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) { - while (NRF_UART0->EVENTS_RXDRDY != 0) { - (void)NRF_UART0->RXD; - } - } -} - - -/** - * @brief Driver output notification. - */ -#if NRF5_SERIAL_USE_UART0 || defined(__DOXYGEN__) -static void notify1(io_queue_t *qp) -{ - SerialDriver *sdp = &SD1; - - (void)qp; - - if (NRF_UART0->PSELTXD == NRF5_SERIAL_PAD_DISCONNECTED) - return; - - if (!sdp->tx_busy) { - msg_t b = oqGetI(&sdp->oqueue); - - if (b < Q_OK) { - chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); - NRF_UART0->TASKS_STOPTX = 1; - return; - } - sdp->tx_busy = 1; - NRF_UART0->TASKS_STARTTX = 1; - NRF_UART0->TXD = b; - } -} -#endif - - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_SERIAL_USE_UART0 || defined(__DOXYGEN__) -OSAL_IRQ_HANDLER(Vector48) { - - OSAL_IRQ_PROLOGUE(); - - SerialDriver *sdp = &SD1; - uint32_t isr = NRF_UART0->INTENSET; - - if ((NRF_UART0->EVENTS_RXDRDY != 0) && (isr & UART_INTENSET_RXDRDY_Msk)) { - // Clear UART RX event flag - NRF_UART0->EVENTS_RXDRDY = 0; -#if CORTEX_MODEL >= 4 - (void)NRF_UART0->EVENTS_RXDRDY; -#endif - - osalSysLockFromISR(); - if (iqIsEmptyI(&sdp->iqueue)) - chnAddFlagsI(sdp, CHN_INPUT_AVAILABLE); - if (iqPutI(&sdp->iqueue, NRF_UART0->RXD) < Q_OK) - chnAddFlagsI(sdp, SD_OVERRUN_ERROR); - osalSysUnlockFromISR(); - } - - if ((NRF_UART0->EVENTS_TXDRDY != 0) && (isr & UART_INTENSET_TXDRDY_Msk)) { - msg_t b; - - // Clear UART TX event flag. - NRF_UART0->EVENTS_TXDRDY = 0; -#if CORTEX_MODEL >= 4 - (void)NRF_UART0->EVENTS_TXDRDY; -#endif - - osalSysLockFromISR(); - b = oqGetI(&sdp->oqueue); - osalSysUnlockFromISR(); - - if (b < Q_OK) { - osalSysLockFromISR(); - chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); - osalSysUnlockFromISR(); - NRF_UART0->TASKS_STOPTX = 1; - sdp->tx_busy = 0; - } else { - sdp->tx_busy = 1; - NRF_UART0->TXD = b; - } - } - - /* TODO: Error handling for EVENTS_ERROR */ - if ((NRF_UART0->EVENTS_ERROR != 0) && (isr & UART_INTENSET_ERROR_Msk)) { - // Clear UART ERROR event flag. - NRF_UART0->EVENTS_ERROR = 0; -#if CORTEX_MODEL >= 4 - (void)NRF_UART0->EVENTS_ERROR; -#endif - } - - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level serial driver initialization. - * - * @notapi - */ -void sd_lld_init(void) { - -#if NRF5_SERIAL_USE_UART0 == TRUE - sdObjectInit(&SD1, NULL, notify1); -#endif -} - -/** - * @brief Low level serial driver configuration and (re)start. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] config the architecture-dependent serial driver configuration. - * If this parameter is set to @p NULL then a default - * configuration is used. - * - * @notapi - */ -void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { - - if (config == NULL) - config = &default_config; - - osalDbgAssert( - (config->rx_pad < TOTAL_GPIO_PADS) || (config->tx_pad < TOTAL_GPIO_PADS), - "must configure at least an RX or TX pad"); - - if (sdp->state == SD_STOP) { - -#if NRF5_SERIAL_USE_UART0 == TRUE - if (sdp == &SD1) { - configure_uart(config); - - // Enable UART interrupt - NRF_UART0->INTENCLR = (uint32_t)-1; - NRF_UART0->INTENSET = UART_INTENSET_ERROR_Msk; - if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) - NRF_UART0->INTENSET |= UART_INTENSET_RXDRDY_Msk; - if (config->tx_pad != NRF5_SERIAL_PAD_DISCONNECTED) - NRF_UART0->INTENSET |= UART_INTENSET_TXDRDY_Msk; - - nvicEnableVector(UART0_IRQn, NRF5_SERIAL_UART0_PRIORITY); - - if (config->rx_pad != NRF5_SERIAL_PAD_DISCONNECTED) - NRF_UART0->TASKS_STARTRX = 1; - } -#endif - - } -} - -/** - * @brief Low level serial driver stop. - * @details De-initializes the USART, stops the associated clock, resets the - * interrupt vector. - * - * @param[in] sdp pointer to a @p SerialDriver object - * - * @notapi - */ -void sd_lld_stop(SerialDriver *sdp) { - - if (sdp->state == SD_READY) { - -#if NRF5_SERIAL_USE_UART0 == TRUE - if (&SD1 == sdp) { - nvicDisableVector(UART0_IRQn); - NRF_UART0->ENABLE = UART_ENABLE_ENABLE_Disabled; - } -#endif - } -} - -#endif /* HAL_USE_SERIAL == TRUE */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_serial_lld.h b/os/hal/ports/NRF5/LLD/hal_serial_lld.h deleted file mode 100644 index 741a40a..0000000 --- a/os/hal/ports/NRF5/LLD/hal_serial_lld.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - 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 NRF5/LLD/hal_serial_lld.h - * @brief NRF5 serial subsystem low level driver header. - * - * @addtogroup SERIAL - * @{ - */ - -#ifndef HAL_SERIAL_LLD_H -#define HAL_SERIAL_LLD_H - -#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name PLATFORM configuration options - * @{ - */ -/** - * @brief SD flow control enable switch. - * @details If set to @p TRUE the support for hardware flow control - * is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_SERIAL_USE_HWFLOWCTRL) || defined(__DOXYGEN__) -#define NRF5_SERIAL_USE_HWFLOWCTRL FALSE -#endif - -/** - * @brief SD1 driver enable switch. - * @details If set to @p TRUE the support for SD1 is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_SERIAL_USE_UART0) || defined(__DOXYGEN__) -#define NRF5_SERIAL_USE_UART0 FALSE -#endif - -/** - * @brief UART0 interrupt priority level setting. - */ -#if !defined(NRF5_SERIAL_UART0_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_SERIAL_UART0_PRIORITY 3 -#endif - -/* Value indicating that no pad is connected to this UART register. */ -#define NRF5_SERIAL_PAD_DISCONNECTED 0xFFFFFFFFU -#define NRF5_SERIAL_INVALID_BAUDRATE 0xFFFFFFFFU - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if NRF5_SERIAL_USE_UART0 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SERIAL_UART0_PRIORITY) -#error "Invalid IRQ priority assigned to UART0" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief NRF51 Serial Driver configuration structure. - * @details An instance of this structure must be passed to @p sdStart() - * in order to configure and start a serial driver operations. - * @note This structure content is architecture dependent, each driver - * implementation defines its own version and the custom static - * initializers. - */ -typedef struct { - /** - * @brief Bit rate. - */ - uint32_t speed; - /* End of the mandatory fields.*/ - uint32_t tx_pad; - uint32_t rx_pad; -#if (NRF5_SERIAL_USE_HWFLOWCTRL == TRUE) - uint32_t rts_pad; - uint32_t cts_pad; -#endif -} SerialConfig; - -/** - * @brief @p SerialDriver specific data. - */ -#define _serial_driver_data \ - _base_asynchronous_channel_data \ - /* Driver state.*/ \ - sdstate_t state; \ - /* Input queue.*/ \ - input_queue_t iqueue; \ - /* Output queue.*/ \ - output_queue_t oqueue; \ - /* Input circular buffer.*/ \ - uint8_t ib[SERIAL_BUFFERS_SIZE]; \ - /* Output circular buffer.*/ \ - uint8_t ob[SERIAL_BUFFERS_SIZE]; \ - /* 1 if port is busy transmitting, 0 otherwise. */ \ - uint8_t tx_busy; \ - /* End of the mandatory fields.*/ \ - thread_t *thread; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if (NRF5_SERIAL_USE_UART0 == TRUE) && !defined(__DOXYGEN__) -extern SerialDriver SD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sd_lld_init(void); - void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); - void sd_lld_stop(SerialDriver *sdp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SERIAL == TRUE */ - -#endif /* HAL_SERIAL_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_spi_lld.c b/os/hal/ports/NRF5/LLD/hal_spi_lld.c deleted file mode 100644 index 2c6ec91..0000000 --- a/os/hal/ports/NRF5/LLD/hal_spi_lld.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - 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/LLD/hal_spi_lld.c - * @brief NRF5 low level SPI driver code. - * - * @addtogroup SPI - * @{ - */ - -#include "hal.h" - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -#if NRF5_SPI_USE_SPI0 || defined(__DOXYGEN__) -/** @brief SPI1 driver identifier.*/ -SPIDriver SPID1; -#endif - -#if NRF5_SPI_USE_SPI1 || defined(__DOXYGEN__) -/** @brief SPI2 driver identifier.*/ -SPIDriver SPID2; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Preloads the transmit FIFO. - * - * @param[in] spip pointer to the @p SPIDriver object - */ -static void port_fifo_preload(SPIDriver *spip) { - NRF_SPI_Type *port = spip->port; - - if (spip->txcnt > 0 && spip->txptr != NULL) - port->TXD = *(uint8_t *)spip->txptr++; - else - port->TXD = 0xFF; - spip->txcnt--; -} - -#if defined(__GNUC__) -__attribute__((noinline)) -#endif -/** - * @brief Common IRQ handler. - * - * @param[in] spip pointer to the @p SPIDriver object - */ -static void serve_interrupt(SPIDriver *spip) { - NRF_SPI_Type *port = spip->port; - - // Clear SPI READY event flag - port->EVENTS_READY = 0; -#if CORTEX_MODEL >= 4 - (void)port->EVENTS_READY; -#endif - - if (spip->rxptr != NULL) { - *(uint8_t *)spip->rxptr++ = port->RXD; - } - else { - (void)port->RXD; - if (--spip->rxcnt == 0) { - osalDbgAssert(spip->txcnt == 0, "counter out of synch"); - /* Stops the IRQ sources.*/ - spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); - /* Portable SPI ISR code defined in the high level driver, note, it is - a macro.*/ - _spi_isr_code(spip); - return; - } - } - if (spip->txcnt > 0) { - port_fifo_preload(spip); - } - else { - spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); - /* Portable SPI ISR code defined in the high level driver, note, it is - a macro.*/ - _spi_isr_code(spip); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_SPI_USE_SPI0 || defined(__DOXYGEN__) -/** - * @brief SPI0 interrupt handler. - * - * @isr - */ -CH_IRQ_HANDLER(Vector4C) { - - CH_IRQ_PROLOGUE(); - serve_interrupt(&SPID1); - CH_IRQ_EPILOGUE(); -} -#endif -#if NRF5_SPI_USE_SPI1 || defined(__DOXYGEN__) -/** - * @brief SPI1 interrupt handler. - * - * @isr - */ -CH_IRQ_HANDLER(Vector50) { - - CH_IRQ_PROLOGUE(); - serve_interrupt(&SPID2); - CH_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SPI driver initialization. - * - * @notapi - */ -void spi_lld_init(void) { - -#if NRF5_SPI_USE_SPI0 - spiObjectInit(&SPID1); - SPID1.port = NRF_SPI0; -#endif -#if NRF5_SPI_USE_SPI1 - spiObjectInit(&SPID2); - SPID2.port = NRF_SPI1; -#endif -} - -/** - * @brief Configures and activates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_start(SPIDriver *spip) { - uint32_t config; - - if (spip->state == SPI_STOP) { -#if NRF5_SPI_USE_SPI0 - if (&SPID1 == spip) - nvicEnableVector(SPI0_TWI0_IRQn, NRF5_SPI_SPI0_IRQ_PRIORITY); -#endif -#if NRF5_SPI_USE_SPI1 - if (&SPID2 == spip) - nvicEnableVector(SPI1_TWI1_IRQn, NRF5_SPI_SPI1_IRQ_PRIORITY); -#endif - } - - config = spip->config->lsbfirst ? - (SPI_CONFIG_ORDER_LsbFirst << SPI_CONFIG_ORDER_Pos) : - (SPI_CONFIG_ORDER_MsbFirst << SPI_CONFIG_ORDER_Pos); - - switch (spip->config->mode) { - case 1: - config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos); - config |= (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); - break; - case 2: - config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos); - config |= (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); - break; - case 3: - config |= (SPI_CONFIG_CPOL_ActiveHigh << SPI_CONFIG_CPOL_Pos); - config |= (SPI_CONFIG_CPHA_Trailing << SPI_CONFIG_CPHA_Pos); - break; - default: - config |= (SPI_CONFIG_CPOL_ActiveLow << SPI_CONFIG_CPOL_Pos); - config |= (SPI_CONFIG_CPHA_Leading << SPI_CONFIG_CPHA_Pos); - break; - } - - /* Configuration.*/ - spip->port->CONFIG = config; -#if NRF_SERIES == 51 - spip->port->PSELSCK = spip->config->sckpad; - spip->port->PSELMOSI = spip->config->mosipad; - spip->port->PSELMISO = spip->config->misopad; -#else - spip->port->PSEL.SCK = spip->config->sckpad; - spip->port->PSEL.MOSI = spip->config->mosipad; - spip->port->PSEL.MISO = spip->config->misopad; -#endif - spip->port->FREQUENCY = spip->config->freq; - spip->port->ENABLE = (SPI_ENABLE_ENABLE_Enabled << SPI_ENABLE_ENABLE_Pos); - - /* clear events flag */ - spip->port->EVENTS_READY = 0; -#if CORTEX_MODEL >= 4 - (void)spip->port->EVENTS_READY; -#endif -} - -/** - * @brief Deactivates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_stop(SPIDriver *spip) { - - if (spip->state != SPI_STOP) { - spip->port->ENABLE = (SPI_ENABLE_ENABLE_Disabled << SPI_ENABLE_ENABLE_Pos); - spip->port->INTENCLR = (SPI_INTENCLR_READY_Clear << SPI_INTENCLR_READY_Pos); -#if NRF5_SPI_USE_SPI0 - if (&SPID1 == spip) - nvicDisableVector(SPI0_TWI0_IRQn); -#endif -#if NRF5_SPI_USE_SPI1 - if (&SPID2 == spip) - nvicDisableVector(SPI1_TWI1_IRQn); -#endif - } -} - -/** - * @brief Asserts the slave select signal and prepares for transfers. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_select(SPIDriver *spip) { - - palClearPad(IOPORT1, spip->config->sspad); -} - -/** - * @brief Deasserts the slave select signal. - * @details The previously selected peripheral is unselected. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_unselect(SPIDriver *spip) { - - palSetPad(IOPORT1, spip->config->sspad); -} - -/** - * @brief Ignores data on the SPI bus. - * @details This function transmits a series of idle words on the SPI bus and - * ignores the received data. This function can be invoked even - * when a slave select signal has not been yet asserted. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be ignored - * - * @notapi - */ -void spi_lld_ignore(SPIDriver *spip, size_t n) { - - spip->rxptr = NULL; - spip->txptr = NULL; - spip->rxcnt = spip->txcnt = n; - port_fifo_preload(spip); - spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); -} - -/** - * @brief Exchanges data on the SPI bus. - * @details This asynchronous function starts a simultaneous transmit/receive - * operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be exchanged - * @param[in] txbuf the pointer to the transmit buffer - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf) { - - spip->rxptr = rxbuf; - spip->txptr = txbuf; - spip->rxcnt = spip->txcnt = n; - port_fifo_preload(spip); - spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); -} - -/** - * @brief Sends data over the SPI bus. - * @details This asynchronous function starts a transmit operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to send - * @param[in] txbuf the pointer to the transmit buffer - * - * @notapi - */ -void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { - - spip->rxptr = NULL; - spip->txptr = txbuf; - spip->rxcnt = spip->txcnt = n; - port_fifo_preload(spip); - spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); -} - -/** - * @brief Receives data from the SPI bus. - * @details This asynchronous function starts a receive operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to receive - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { - - spip->rxptr = rxbuf; - spip->txptr = NULL; - spip->rxcnt = spip->txcnt = n; - port_fifo_preload(spip); - spip->port->INTENSET = (SPI_INTENCLR_READY_Enabled << SPI_INTENCLR_READY_Pos); -} - -/** - * @brief Exchanges one frame using a polled wait. - * @details This synchronous function exchanges one frame using a polled - * synchronization method. This function is useful when exchanging - * small amount of data on high speed channels, usually in this - * situation is much more efficient just wait for completion using - * polling than suspending the thread waiting for an interrupt. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] frame the data frame to send over the SPI bus - * @return The received data frame from the SPI bus. - */ -uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { - - spip->port->TXD = (uint8_t)frame; - while (spip->port->EVENTS_READY == 0) - ; - spip->port->EVENTS_READY = 0; -#if CORTEX_MODEL >= 4 - (void)spip->port->EVENTS_READY; -#endif - return spip->port->RXD; -} - -#endif /* HAL_USE_SPI */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_spi_lld.h b/os/hal/ports/NRF5/LLD/hal_spi_lld.h deleted file mode 100644 index afad5ab..0000000 --- a/os/hal/ports/NRF5/LLD/hal_spi_lld.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - 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 NRF/LLD/hal_spi_lld.h - * @brief NRF5 low level SPI driver header. - * - * @addtogroup SPI - * @{ - */ - -#ifndef HAL_SPI_LLD_H -#define HAL_SPI_LLD_H - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @brief SPI0 interrupt priority level setting. - */ -#if !defined(NRF5_SPI_SPI0_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_SPI_SPI0_IRQ_PRIORITY 3 -#endif - -/** - * @brief SPI1 interrupt priority level setting. - */ -#if !defined(NRF5_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_SPI_SPI1_IRQ_PRIORITY 3 -#endif - -/** - * @brief Overflow error hook. - * @details The default action is to stop the system. - */ -#if !defined(NRF5_SPI_SPI_ERROR_HOOK) || defined(__DOXYGEN__) -#define NRF5_SPI_SPI_ERROR_HOOK() chSysHalt() -#endif - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if !NRF5_SPI_USE_SPI0 && !NRF5_SPI_USE_SPI1 -#error "SPI driver activated but no SPI peripheral assigned" -#endif - -#if NRF5_SPI_USE_SPI0 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SPI_SPI0_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI0" -#endif - -#if NRF5_SPI_USE_SPI1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_SPI_SPI1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI1" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an SPI driver. - */ -typedef struct SPIDriver SPIDriver; - -/** - * @brief SPI notification callback type. - * - * @param[in] spip pointer to the @p SPIDriver object triggering the - * callback - */ -typedef void (*spicallback_t)(SPIDriver *spip); - -/** - * @brief SPI frequency - */ -typedef enum { - NRF5_SPI_FREQ_125KBPS = (SPI_FREQUENCY_FREQUENCY_K125 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_250KBPS = (SPI_FREQUENCY_FREQUENCY_K250 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_500KBPS = (SPI_FREQUENCY_FREQUENCY_K500 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_1MBPS = (SPI_FREQUENCY_FREQUENCY_M1 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_2MBPS = (SPI_FREQUENCY_FREQUENCY_M2 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_4MBPS = (SPI_FREQUENCY_FREQUENCY_M4 << SPI_FREQUENCY_FREQUENCY_Pos), - NRF5_SPI_FREQ_8MBPS = (SPI_FREQUENCY_FREQUENCY_M8 << SPI_FREQUENCY_FREQUENCY_Pos), -} spifreq_t; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief Operation complete callback or @p NULL. - */ - spicallback_t end_cb; - /** - * @brief The frequency of the SPI peripheral - */ - spifreq_t freq; - /** - * @brief The SCK pad - */ - uint16_t sckpad; - /** - * @brief The MOSI pad - */ - uint16_t mosipad; - /** - * @brief The MOSI pad - */ - uint16_t misopad; - /* End of the mandatory fields.*/ - /** - * @brief The chip select line pad number. - */ - uint16_t sspad; - /** - * @brief Shift out least significant bit first - */ - uint8_t lsbfirst; - /** - * @brief SPI mode - */ - uint8_t mode; -} SPIConfig; - -/** - * @brief Structure representing a SPI driver. - */ -struct SPIDriver { - /** - * @brief Driver state. - */ - spistate_t state; - /** - * @brief Current configuration data. - */ - const SPIConfig *config; -#if SPI_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif /* SPI_USE_WAIT */ -#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) -#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#elif CH_CFG_USE_SEMAPHORES - semaphore_t semaphore; -#endif -#endif /* SPI_USE_MUTUAL_EXCLUSION */ -#if defined(SPI_DRIVER_EXT_FIELDS) - SPI_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the SPI port. - */ - NRF_SPI_Type *port; - /** - * @brief Number of bytes yet to be received. - */ - uint32_t rxcnt; - /** - * @brief Receive pointer or @p NULL. - */ - void *rxptr; - /** - * @brief Number of bytes yet to be transmitted. - */ - uint32_t txcnt; - /** - * @brief Transmit pointer or @p NULL. - */ - const void *txptr; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if NRF5_SPI_USE_SPI0 && !defined(__DOXYGEN__) -extern SPIDriver SPID1; -#endif -#if NRF5_SPI_USE_SPI1 && !defined(__DOXYGEN__) -extern SPIDriver SPID2; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void spi_lld_init(void); - void spi_lld_start(SPIDriver *spip); - void spi_lld_stop(SPIDriver *spip); - void spi_lld_select(SPIDriver *spip); - void spi_lld_unselect(SPIDriver *spip); - void spi_lld_ignore(SPIDriver *spip, size_t n); - void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf); - void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); - void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); - uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SPI */ - -#endif /* HAL_SPI_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_st_lld.c b/os/hal/ports/NRF5/LLD/hal_st_lld.c deleted file mode 100644 index 8e42029..0000000 --- a/os/hal/ports/NRF5/LLD/hal_st_lld.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - 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 NRF5/LLD/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/hal_st_lld.h b/os/hal/ports/NRF5/LLD/hal_st_lld.h deleted file mode 100644 index 93c2abb..0000000 --- a/os/hal/ports/NRF5/LLD/hal_st_lld.h +++ /dev/null @@ -1,285 +0,0 @@ -/* - 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 NRF5/LLD/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 */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_wdg_lld.c b/os/hal/ports/NRF5/LLD/hal_wdg_lld.c deleted file mode 100644 index 35c079f..0000000 --- a/os/hal/ports/NRF5/LLD/hal_wdg_lld.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - ChibiOS - 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. -*/ - -/** - * @file NRF5/LLD/hal_wdg_lld.c - * @brief NRF5 Watchdog Driver subsystem low level driver source template. - * - * @addtogroup WDG - * @{ - */ - -#include "hal.h" - -#if HAL_USE_WDG || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define RELOAD_REQUEST_VALUE 0x6E524635 - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -WDGDriver WDGD1; - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if WDG_USE_TIMEOUT_CALLBACK == TRUE -/** - * @brief Watchdog vector. - * @details This interrupt is used when watchdog timeout. - * - * @note Only 2 cycles at NRF5_LFCLK_FREQUENCY are available - * to they good bye. - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector80) { - - OSAL_IRQ_PROLOGUE(); - osalSysLockFromISR(); - - /* Notify */ - if (WDGD1.config->callback) - WDGD1.config->callback(); - - /* Wait for reboot */ - while (1) { /* */ } - - osalSysUnlockFromISR(); - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level WDG driver initialization. - * - * @notapi - */ -void wdg_lld_init(void) { - WDGD1.state = WDG_STOP; - WDGD1.wdt = NRF_WDT; -} - -/** - * @brief Configures and activates the WDG peripheral. - * - * @note Once started there is no way out. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @notapi - */ -void wdg_lld_start(WDGDriver *wdgp) { - osalDbgAssert((wdgp->state == WDG_STOP), - "This WDG driver cannot be restarted once activated"); - - /* Generate interrupt on timeout */ -#if WDG_USE_TIMEOUT_CALLBACK == TRUE - wdgp->wdt->INTENSET = WDT_INTENSET_TIMEOUT_Msk; -#endif - - /* When to pause? (halt, sleep) */ - uint32_t config = 0; - if (!wdgp->config->pause_on_sleep) - config |= WDT_CONFIG_SLEEP_Msk; - if (!wdgp->config->pause_on_halt) - config |= WDT_CONFIG_HALT_Msk; - wdgp->wdt->CONFIG = config; - - /* Timeout in milli-seconds */ - uint64_t tout = (NRF5_LFCLK_FREQUENCY * wdgp->config->timeout_ms / 1000) - 1; - osalDbgAssert(tout <= 0xFFFFFFFF, "watchdog timout value exceeded"); - wdgp->wdt->CRV = (uint32_t)tout; - - /* Reload request (using RR0) */ - wdgp->wdt->RREN = WDT_RREN_RR0_Msk; - - /* Say your prayers, little one. */ - wdgp->wdt->TASKS_START = 1; -} - -/** - * @brief Deactivates the WDG peripheral. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @api - */ -void wdg_lld_stop(WDGDriver *wdgp) { - (void)wdgp; - osalDbgAssert(false, "This WDG driver cannot be stopped once activated"); -} - -/** - * @brief Reloads WDG's counter. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @notapi - */ -void wdg_lld_reset(WDGDriver * wdgp) { - wdgp->wdt->RR[0] = RELOAD_REQUEST_VALUE; -} - -#endif /* HAL_USE_WDG */ - -/** @} */ diff --git a/os/hal/ports/NRF5/LLD/hal_wdg_lld.h b/os/hal/ports/NRF5/LLD/hal_wdg_lld.h deleted file mode 100644 index 109b67e..0000000 --- a/os/hal/ports/NRF5/LLD/hal_wdg_lld.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - ChibiOS - 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. -*/ - -/** - * @file NRF5/LLD/hal_wdg_lld.h - * @brief NRF5 Watchdog Driver subsystem low level driver header template. - * - * @addtogroup WDG - * @{ - */ - -#ifndef HAL_WDG_LLD_H -#define HAL_WDG_LLD_H - -#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define WDG_MAX_TIMEOUT_MS \ - ((uint32_t)(0xFFFFFFFFu * 1000 / NRF5_LFCLK_FREQUENCY)) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ - -/** - * @brief WDG driver implement timeout callback. - * @note The default is @p FALSE. - */ -#if !defined(WDG_USE_TIMEOUT_CALLBACK) || defined(__DOXYGEN__) -#define WDG_USE_TIMEOUT_CALLBACK FALSE -#endif -/** @} */ - - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an WDG driver. - */ -typedef struct WDGDriver WDGDriver; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - struct { - /** - * @brief Pause watchdog while the CPU is sleeping - */ - uint8_t pause_on_sleep : 1; - /** - * @brief Pause watchdog while the CPU is halted by the debugger - */ - uint8_t pause_on_halt : 1; - }; - /** - * - */ - uint32_t timeout_ms; -#if WDG_USE_TIMEOUT_CALLBACK == TRUE - /** - * @brief Notification callback when watchdog timedout - * - * @note About 2 cycles at NRF5_LFCLK_FREQUENCY are available - * before automatic reboot. - * - */ - void (*callback)(void); -#endif -} WDGConfig; - - - -/** - * @brief Structure representing an WDG driver. - */ -struct WDGDriver { - /** - * @brief Driver state. - */ - wdgstate_t state; - /** - * @brief Current configuration data. - */ - const WDGConfig *config; - /* End of the mandatory fields.*/ - NRF_WDT_Type *wdt; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -extern WDGDriver WDGD1; - -#ifdef __cplusplus -extern "C" { -#endif - void wdg_lld_init(void); - void wdg_lld_start(WDGDriver *wdgp); - void wdg_lld_stop(WDGDriver *wdgp); - void wdg_lld_reset(WDGDriver *wdgp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_WDG == TRUE */ - -#endif /* HAL_WDG_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_adc_lld.c b/os/hal/ports/NRF5/NRF51822/hal_adc_lld.c deleted file mode 100644 index 6c0f2c6..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_adc_lld.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - 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 NRF51Fx22/adc_lld.c - * @brief NRF51Fx22 ADC subsystem low level driver source. - * - * @addtogroup ADC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ -#define ADC_CHANNEL_MASK 0x7 - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief ADC1 driver identifier.*/ -#if NRF5_ADC_USE_ADC1 || defined(__DOXYGEN__) -ADCDriver ADCD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void adc_lld_config_next_channel(ADCDriver *adcp, uint32_t config) { - - /* Default to all analog input pins disabled */ - config &= ~ADC_CONFIG_PSEL_Msk; - - if (adcp->grpp->channel_mask) { - /* Skip to the next channel */ - while (((1 << adcp->current_channel) & adcp->grpp->channel_mask) == 0) - adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; - config |= (((1 << adcp->current_channel) << ADC_CONFIG_PSEL_Pos) & ADC_CONFIG_PSEL_Msk); - } - - /* Setup analog input pin select and user config values */ - adcp->adc->CONFIG = config; -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_ADC_USE_ADC1 || defined(__DOXYGEN__) -/** - * @brief ADC interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector5C) { - - ADCDriver *adcp = &ADCD1; - NRF_ADC_Type *adc = adcp->adc; - bool more = true; - - OSAL_IRQ_PROLOGUE(); - - /* Clear the ADC event */ - adc->EVENTS_END = 0; - - /* Read the sample into the buffer */ - adcp->samples[adcp->current_index++] = adc->RESULT; - - /* At the end of the buffer then we may be finished */ - if (adcp->current_index == adcp->number_of_samples) { - _adc_isr_full_code(adcp); - - adcp->current_index = 0; - - /* We are never finished in circular mode */ - more = adcp->grpp->circular; - } - - if (more) { - - /* Signal half completion in circular mode. */ - if (adcp->grpp->circular && - (adcp->current_index == (adcp->number_of_samples / 2))) { - - _adc_isr_half_code(adcp); - } - - /* Skip to the next channel */ - adcp->current_channel = (adcp->current_channel + 1) & ADC_CHANNEL_MASK; - adc_lld_config_next_channel(adcp, adcp->adc->CONFIG); - adcp->adc->TASKS_START = 1; - } else { - adc_lld_stop_conversion(adcp); - } - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ADC driver initialization. - * - * @notapi - */ -void adc_lld_init(void) { - -#if NRF5_ADC_USE_ADC1 - /* Driver initialization.*/ - adcObjectInit(&ADCD1); - ADCD1.adc = NRF_ADC; -#endif -} - -/** - * @brief Configures and activates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start(ADCDriver *adcp) { - - /* If in stopped state then configures and enables the ADC. */ - if (adcp->state == ADC_STOP) { -#if NRF5_ADC_USE_ADC1 - if (&ADCD1 == adcp) { - - adcp->adc->INTENSET = ADC_INTENSET_END_Enabled << ADC_INTENSET_END_Pos; - nvicEnableVector(ADC_IRQn, NRF5_ADC_IRQ_PRIORITY); - } -#endif /* NRF5_ADC_USE_ADC1 */ - } -} - -/** - * @brief Deactivates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop(ADCDriver *adcp) { - - /* If in ready state then disables the ADC clock and analog part.*/ - if (adcp->state == ADC_READY) { - -#if NRF5_ADC_USE_ADC1 - if (&ADCD1 == adcp) { - - nvicDisableVector(ADC_IRQn); - adcp->adc->INTENCLR = ADC_INTENCLR_END_Clear << ADC_INTENCLR_END_Pos; - adc_lld_stop_conversion(adcp); - } -#endif - } -} - -/** - * @brief Starts an ADC conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start_conversion(ADCDriver *adcp) { - - NRF_ADC_Type *adc = adcp->adc; - - adcp->number_of_samples = adcp->depth * adcp->grpp->num_channels; - adcp->current_index = 0; - - /* At least one sample must be configured */ - osalDbgAssert(adcp->number_of_samples, "must configure at least one sample"); - - /* Skip to the next channel */ - adcp->current_channel = 0; - adc_lld_config_next_channel(adcp, adcp->grpp->cfg); - - /* Enable and start the conversion */ - adc->ENABLE = ADC_ENABLE_ENABLE_Enabled << ADC_ENABLE_ENABLE_Pos; - adc->TASKS_START = 1; -} - -/** - * @brief Stops an ongoing conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop_conversion(ADCDriver *adcp) { - - NRF_ADC_Type *adc = adcp->adc; - - adc->TASKS_STOP = 1; - adc->ENABLE = ADC_ENABLE_ENABLE_Disabled << ADC_ENABLE_ENABLE_Pos; -} - -#endif /* HAL_USE_ADC */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_adc_lld.h b/os/hal/ports/NRF5/NRF51822/hal_adc_lld.h deleted file mode 100644 index 2ee30ac..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_adc_lld.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - 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 NRF51x22/adc_lld.h - * @brief NRF51x22 ADC subsystem low level driver header. - * - * @addtogroup ADC - * @{ - */ - -#ifndef HAL_ADC_LLD_H -#define HAL_ADC_LLD_H - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief ADC1 driver enable switch. - * @details If set to @p TRUE the support for ADC1 is included. - * @note The default is @p FALSE. - */ -#if !defined(NRF5_ADC_USE_ADC1) || defined(__DOXYGEN__) -#define NRF5_ADC_USE_ADC1 FALSE -#endif - -/** - * @brief ADC interrupt priority level setting. - */ -#if !defined(NRF5_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_ADC_IRQ_PRIORITY 2 -#endif - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if !NRF5_ADC_USE_ADC1 -#error "ADC driver activated but no ADC peripheral assigned" -#endif - -#if NRF5_ADC_USE_ADC1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_ADC_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC1" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ADC sample data type. - */ -typedef uint16_t adcsample_t; - -/** - * @brief Channels number in a conversion group. - */ -typedef uint8_t adc_channels_num_t; - -/** - * @brief Type of a structure representing an ADC driver. - */ -typedef struct ADCDriver ADCDriver; - -/** - * @brief ADC notification callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] buffer pointer to the most recent samples data - * @param[in] n number of buffer rows available starting from @p buffer - */ -typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @brief Conversion group configuration structure. - * @details This implementation-dependent structure describes a conversion - * operation. - * @note The use of this configuration structure requires knowledge of - * STM32 ADC cell registers interface, please refer to the STM32 - * reference manual for details. - */ -typedef struct { - /** - * @brief Enables the circular buffer mode for the group. - */ - bool circular; - /** - * @brief Number of the analog channels belonging to the conversion group. - */ - adc_channels_num_t num_channels; - /** - * @brief Callback function associated to the group or @p NULL. - */ - adccallback_t end_cb; - /* End of the mandatory fields.*/ - /** - * @brief Bitmask of channels for ADC conversion. - */ - uint32_t channel_mask; - /** - * @brief ADC CONFIG register details. - * @note All the required bits must be defined into this field. - */ - uint32_t cfg; -} ADCConversionGroup; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - uint32_t dummy; -} ADCConfig; - -/** - * @brief Structure representing an ADC driver. - */ -struct ADCDriver { - /** - * @brief Driver state. - */ - adcstate_t state; - /** - * @brief Current configuration data. - */ - const ADCConfig *config; - /** - * @brief Current samples buffer pointer or @p NULL. - */ - adcsample_t *samples; - /** - * @brief Current samples buffer depth or @p 0. - */ - size_t depth; - /** - * @brief Current conversion group pointer or @p NULL. - */ - const ADCConversionGroup *grpp; -#if ADC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif -#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* ADC_USE_MUTUAL_EXCLUSION */ -#if defined(ADC_DRIVER_EXT_FIELDS) - ADC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the ADCx registers block. - */ - NRF_ADC_Type *adc; - /** - * @brief Number of samples expected. - */ - size_t number_of_samples; - /** - * @brief Current position in the buffer. - */ - size_t current_index; - /** - * @brief Current channel index into group channel_mask. - */ - size_t current_channel; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if NRF5_ADC_USE_ADC1 && !defined(__DOXYGEN__) -extern ADCDriver ADCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void adc_lld_init(void); - void adc_lld_start(ADCDriver *adcp); - void adc_lld_stop(ADCDriver *adcp); - void adc_lld_start_conversion(ADCDriver *adcp); - void adc_lld_stop_conversion(ADCDriver *adcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ADC */ - -#endif /* HAL_ADC_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_ext_lld.c b/os/hal/ports/NRF5/NRF51822/hal_ext_lld.c deleted file mode 100644 index 47736c7..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_ext_lld.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - 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 NRF51822/ext_lld.c - * @brief NRF51822 EXT subsystem low level driver source. - * - * @addtogroup EXT - * @{ - */ - -#include "hal.h" - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -#include "hal_ext_lld_isr.h" - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief EXTD1 driver identifier. - */ -EXTDriver EXTD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level EXT driver initialization. - * - * @notapi - */ -void ext_lld_init(void) { - - /* Driver initialization.*/ - extObjectInit(&EXTD1); -} - -/** - * @brief Configures and activates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_start(EXTDriver *extp) { - - unsigned i; - - ext_lld_exti_irq_enable(); - - /* Configuration of automatic channels.*/ - for (i = 0; i < EXT_MAX_CHANNELS; i++) { - uint32_t config = 0; - uint32_t pad = (extp->config->channels[i].mode & EXT_MODE_GPIO_MASK) - >> EXT_MODE_GPIO_OFFSET; - - if (extp->config->channels[i].mode & EXT_CH_MODE_BOTH_EDGES) - config |= (GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos); - else if (extp->config->channels[i].mode & EXT_CH_MODE_RISING_EDGE) - config |= (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos); - else - config |= (GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos); - - config |= (pad << GPIOTE_CONFIG_PSEL_Pos); - - NRF_GPIOTE->CONFIG[i] = config; - NRF_GPIOTE->EVENTS_PORT = 0; - NRF_GPIOTE->EVENTS_IN[i] = 0; - - if (extp->config->channels[i].mode & EXT_CH_MODE_AUTOSTART) - ext_lld_channel_enable(extp, i); - else - ext_lld_channel_disable(extp, i); - } -} - -/** - * @brief Deactivates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_stop(EXTDriver *extp) { - - unsigned i; - - (void)extp; - ext_lld_exti_irq_disable(); - - for (i = 0; i < EXT_MAX_CHANNELS; i++) - NRF_GPIOTE->CONFIG[i] = 0; - - NRF_GPIOTE->INTENCLR = - (GPIOTE_INTENCLR_IN3_Msk | GPIOTE_INTENCLR_IN2_Msk | - GPIOTE_INTENCLR_IN1_Msk | GPIOTE_INTENCLR_IN0_Msk); -} - -/** - * @brief Enables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be enabled - * - * @notapi - */ -void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { - - uint32_t config = NRF_GPIOTE->CONFIG[channel] & ~GPIOTE_CONFIG_MODE_Msk; - - (void)extp; - config |= (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos); - - NRF_GPIOTE->CONFIG[channel] = config; - NRF_GPIOTE->INTENSET = (1 << channel); -} - -/** - * @brief Disables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be disabled - * - * @notapi - */ -void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { - - (void)extp; - NRF_GPIOTE->CONFIG[channel] &= ~GPIOTE_CONFIG_MODE_Msk; - NRF_GPIOTE->INTENCLR = (1 << channel); -} - -#endif /* HAL_USE_EXT */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_ext_lld.h b/os/hal/ports/NRF5/NRF51822/hal_ext_lld.h deleted file mode 100644 index 37ae721..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_ext_lld.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - 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 NRF51822/ext_lld.h - * @brief NRF51822 GPIOTE subsystem low level driver header. - * - * @addtogroup EXT - * @{ - */ - -#ifndef HAL_EXT_LLD_H -#define HAL_EXT_LLD_H - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Available number of EXT channels. - */ -#define EXT_MAX_CHANNELS 4 -#define EXT_MODE_GPIO_MASK 0xF8 /**< @brief Pad field mask. */ -#define EXT_MODE_GPIO_OFFSET 3 /**< @brief Pad field offset. */ -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief EXT channel identifier. - */ -typedef uint32_t expchannel_t; - -/** - * @brief Type of an EXT generic notification callback. - * - * @param[in] extp pointer to the @p EXPDriver object triggering the - * callback - */ -typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); - -/** - * @brief Channel configuration structure. - */ -typedef struct { - /** - * @brief Channel mode. - */ - uint32_t mode; - /** - * @brief Channel callback. - * @details In the STM32 implementation a @p NULL callback pointer is - * valid and configures the channel as an event sources instead - * of an interrupt source. - */ - extcallback_t cb; -} EXTChannelConfig; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Channel configurations. - */ - EXTChannelConfig channels[EXT_MAX_CHANNELS]; - /* End of the mandatory fields.*/ -} EXTConfig; - -/** - * @brief Structure representing an EXT driver. - */ -struct EXTDriver { - /** - * @brief Driver state. - */ - extstate_t state; - /** - * @brief Current configuration data. - */ - const EXTConfig *config; - /* End of the mandatory fields.*/ -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern EXTDriver EXTD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void ext_lld_init(void); - void ext_lld_start(EXTDriver *extp); - void ext_lld_stop(EXTDriver *extp); - void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); - void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_EXT */ - -#endif /* HAL_EXT_LLD_H */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.c b/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.c deleted file mode 100644 index e2b4b6b..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - ChibiOS/HAL - 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. -*/ - -/** - * @file hal_pwm_lld.c - * @brief NRF51 PWM subsystem low level driver source. - * - * @note Using the method described in nrf51-pwm-library to correctly - * handle toggling of the pin with GPIOTE when changing period. - * It means it is generally unsafe to use GPIOTE with a period - * less than (2 * PWM_GPIOTE_DECISION_TIME / 16MHz) - * - * @addtogroup PWM - * @{ - */ - -#include "hal.h" - -#if HAL_USE_PWM || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define PWM_GPIOTE_PPI_CC 3 -#define PWM_GPIOTE_DECISION_TIME 160 - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief PWMD1 driver identifier. - * @note The driver PWMD1 allocates the timer TIMER0 when enabled. - */ -#if NRF5_PWM_USE_TIMER0 || defined(__DOXYGEN__) -PWMDriver PWMD1; -#endif - -/** - * @brief PWMD2 driver identifier. - * @note The driver PWMD2 allocates the timer TIMER1 when enabled. - */ -#if NRF5_PWM_USE_TIMER1 || defined(__DOXYGEN__) -PWMDriver PWMD2; -#endif - -/** - * @brief PWMD3 driver identifier. - * @note The driver PWMD3 allocates the timer TIMER2 when enabled. - */ -#if NRF5_PWM_USE_TIMER2 || defined(__DOXYGEN__) -PWMDriver PWMD3; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const uint8_t pwm_margin_by_prescaler[] = { - (PWM_GPIOTE_DECISION_TIME + 0) >> 0, - (PWM_GPIOTE_DECISION_TIME + 1) >> 1, - (PWM_GPIOTE_DECISION_TIME + 3) >> 2, - (PWM_GPIOTE_DECISION_TIME + 7) >> 3, - (PWM_GPIOTE_DECISION_TIME + 15) >> 4, - (PWM_GPIOTE_DECISION_TIME + 31) >> 5, - (PWM_GPIOTE_DECISION_TIME + 63) >> 6, - (PWM_GPIOTE_DECISION_TIME + 127) >> 7, - (PWM_GPIOTE_DECISION_TIME + 255) >> 8, - (PWM_GPIOTE_DECISION_TIME + 511) >> 9 -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void pwm_lld_serve_interrupt(PWMDriver *pwmp) { - uint8_t channel; - /* Deal with PWM channels - */ - for (channel = 0 ; channel < pwmp->channels ; channel++) { - if (pwmp->timer->EVENTS_COMPARE[channel]) { - pwmp->timer->EVENTS_COMPARE[channel] = 0; - - if (pwmp->config->channels[channel].callback != NULL) { - pwmp->config->channels[channel].callback(pwmp); - } - } - } - - /* Deal with PWM period - */ - if (pwmp->timer->EVENTS_COMPARE[pwmp->channels]) { - pwmp->timer->EVENTS_COMPARE[pwmp->channels] = 0; - - if (pwmp->config->callback != NULL) { - pwmp->config->callback(pwmp); - } - } -} - -static inline -bool pwm_within_safe_margins(PWMDriver *pwmp, uint32_t timer, uint32_t width) { - const uint32_t margin = pwm_margin_by_prescaler[pwmp->timer->PRESCALER]; - return (width <= margin) - ? ((width <= timer) && (timer < (pwmp->period + width - margin))) - : ((width <= timer) || (timer < (width - margin))); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if NRF5_PWM_USE_TIMER0 -/** - * @brief TIMER0 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector60) { - OSAL_IRQ_PROLOGUE(); - pwm_lld_serve_interrupt(&PWMD1); - OSAL_IRQ_EPILOGUE(); -} -#endif /* NRF5_PWM_USE_TIMER0 */ - -#if NRF5_PWM_USE_TIMER1 -/** - * @brief TIMER1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector64) { - OSAL_IRQ_PROLOGUE(); - pwm_lld_serve_interrupt(&PWMD2); - OSAL_IRQ_EPILOGUE(); -} -#endif /* NRF5_PWM_USE_TIMER1 */ - -#if NRF5_PWM_USE_TIMER2 -/** - * @brief TIMER2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(Vector68) { - OSAL_IRQ_PROLOGUE(); - pwm_lld_serve_interrupt(&PWMD3); - OSAL_IRQ_EPILOGUE(); -} -#endif /* NRF5_PWM_USE_TIMER2 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level PWM driver initialization. - * - * @notapi - */ -void pwm_lld_init(void) { - -#if NRF5_PWM_USE_TIMER0 - pwmObjectInit(&PWMD1); - PWMD1.channels = PWM_CHANNELS; - PWMD1.timer = NRF_TIMER0; -#endif - -#if NRF5_PWM_USE_TIMER1 - pwmObjectInit(&PWMD2); - PWMD2.channels = PWM_CHANNELS; - PWMD2.timer = NRF_TIMER1; -#endif - -#if NRF5_PWM_USE_TIMER2 - pwmObjectInit(&PWMD3); - PWMD3.channels = PWM_CHANNELS; - PWMD3.timer = NRF_TIMER2; -#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 */ - uint16_t psc_ratio = NRF5_HFCLK_FREQUENCY / pwmp->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 16bits timer (only TIMER0 support 32bits) */ - pwmp->timer->BITMODE = TIMER_BITMODE_BITMODE_16Bit; - pwmp->timer->MODE = TIMER_MODE_MODE_Timer; - - /* With clear shortcuts for period */ - pwmp->timer->SHORTS = - 0x1UL << (TIMER_SHORTS_COMPARE0_CLEAR_Pos + pwmp->channels); - - /* Disable and reset interrupts for compare events */ - pwmp->timer->INTENCLR = (TIMER_INTENCLR_COMPARE0_Msk | - TIMER_INTENCLR_COMPARE1_Msk | - TIMER_INTENCLR_COMPARE2_Msk | - TIMER_INTENCLR_COMPARE3_Msk ); - pwmp->timer->EVENTS_COMPARE[0] = 0; - pwmp->timer->EVENTS_COMPARE[1] = 0; - pwmp->timer->EVENTS_COMPARE[2] = 0; - pwmp->timer->EVENTS_COMPARE[3] = 0; - - /* Set prescaler */ - pwmp->timer->PRESCALER = psc_value; - - /* Set period */ - pwmp->timer->CC[pwmp->channels] = pwmp->period; - - /* Clear everything */ - pwmp->timer->TASKS_CLEAR = 1; - - /* Enable interrupt */ -#if NRF5_PWM_USE_TIMER0 - if (&PWMD1 == pwmp) { - nvicEnableVector(TIMER0_IRQn, NRF5_PWM_TIMER0_PRIORITY); - } -#endif - -#if NRF5_PWM_USE_TIMER1 - if (&PWMD2 == pwmp) { - nvicEnableVector(TIMER1_IRQn, NRF5_PWM_TIMER1_PRIORITY); - } -#endif - -#if NRF5_PWM_USE_TIMER2 - if (&PWMD3 == pwmp) { - nvicEnableVector(TIMER2_IRQn, NRF5_PWM_TIMER2_PRIORITY); - } -#endif - - /* Start timer */ - pwmp->timer->TASKS_START = 1; -} - -/** - * @brief Deactivates the PWM peripheral. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_stop(PWMDriver *pwmp) { - pwmp->timer->TASKS_SHUTDOWN = 1; - -#if NRF5_PWM_USE_TIMER0 - if (&PWMD1 == pwmp) { - nvicDisableVector(TIMER0_IRQn); - } -#endif - -#if NRF5_PWM_USE_TIMER1 - if (&PWMD2 == pwmp) { - nvicDisableVector(TIMER1_IRQn); - } -#endif - -#if NRF5_PWM_USE_TIMER2 - if (&PWMD3 == pwmp) { - nvicDisableVector(TIMER2_IRQn); - } -#endif -} - -/** - * @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) { -#if NRF5_PWM_USE_GPIOTE_PPI - const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel]; - const uint8_t gpiote_channel = cfg_channel->gpiote_channel; - const uint8_t *ppi_channel = cfg_channel->ppi_channel; - - uint32_t outinit; - switch(cfg_channel->mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW : outinit = GPIOTE_CONFIG_OUTINIT_Low; break; - case PWM_OUTPUT_ACTIVE_HIGH: outinit = GPIOTE_CONFIG_OUTINIT_High; break; - case PWM_OUTPUT_DISABLED : /* fall-through */ - default : goto no_output_config; - } - - /* Deal with corner case: 0% and 100% */ - if ((width <= 0) || (width >= pwmp->period)) { - /* Disable GPIOTE/PPI task */ - NRF_GPIOTE->CONFIG[gpiote_channel] = GPIOTE_CONFIG_MODE_Disabled; - NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); - /* Set Line */ - palWriteLine(cfg_channel->ioline, - ((width <= 0) ^ - ((cfg_channel->mode & PWM_OUTPUT_MASK) == PWM_OUTPUT_ACTIVE_HIGH))); - - /* Really doing PWM */ - } else { - const uint32_t gpio_pin = PAL_PAD(cfg_channel->ioline); - const uint32_t polarity = GPIOTE_CONFIG_POLARITY_Toggle; - - /* Program tasks (one for duty cycle, one for periode) */ - NRF_PPI->CH[ppi_channel[0]].EEP = - (uint32_t)&pwmp->timer->EVENTS_COMPARE[channel]; - NRF_PPI->CH[ppi_channel[0]].TEP = - (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; - NRF_PPI->CH[ppi_channel[1]].EEP = - (uint32_t)&pwmp->timer->EVENTS_COMPARE[pwmp->channels]; - NRF_PPI->CH[ppi_channel[1]].TEP = - (uint32_t)&NRF_GPIOTE->TASKS_OUT[gpiote_channel]; - NRF_PPI->CHENSET = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); - - /* Something Old, something New */ - const uint32_t old_width = pwmp->timer->CC[channel]; - const uint32_t new_width = width; - - /* Check GPIOTE state */ - const bool gpiote = (NRF_GPIOTE->CONFIG[gpiote_channel] & - GPIOTE_CONFIG_MODE_Msk) != GPIOTE_CONFIG_MODE_Disabled; - - /* GPIOTE is currently running */ - if (gpiote) { - uint32_t current; - while (true) { - pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; - current = pwmp->timer->CC[PWM_GPIOTE_PPI_CC]; - - if (pwm_within_safe_margins(pwmp, current, old_width) && - pwm_within_safe_margins(pwmp, current, new_width)) - break; - } - if (((old_width <= current) && (current < new_width)) || - ((new_width <= current) && (current < old_width))) { - NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; - } - - /* GPIOTE need to be restarted */ - } else { - /* Create GPIO Task */ - NRF_GPIOTE->CONFIG[gpiote_channel] = - (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) | - ((gpio_pin << GPIOTE_CONFIG_PSEL_Pos ) & GPIOTE_CONFIG_PSEL_Msk )| - ((polarity << GPIOTE_CONFIG_POLARITY_Pos) & GPIOTE_CONFIG_POLARITY_Msk)| - ((outinit << GPIOTE_CONFIG_OUTINIT_Pos ) & GPIOTE_CONFIG_OUTINIT_Msk ); - - pwmp->timer->TASKS_CAPTURE[PWM_GPIOTE_PPI_CC] = 1; - if (pwmp->timer->CC[PWM_GPIOTE_PPI_CC] > width) - NRF_GPIOTE->TASKS_OUT[gpiote_channel] = 1; - } - } - - no_output_config: -#endif - - pwmp->timer->CC[channel] = width; -} - -/** - * @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) { - pwmp->timer->CC[channel] = 0; -#if NRF5_PWM_USE_GPIOTE_PPI - const PWMChannelConfig *cfg_channel = &pwmp->config->channels[channel]; - switch(cfg_channel->mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW: - case PWM_OUTPUT_ACTIVE_HIGH: { - const uint8_t gpiote_channel = cfg_channel->gpiote_channel; - const uint8_t *ppi_channel = cfg_channel->ppi_channel; - NRF_PPI->CHENCLR = ((1 << ppi_channel[0]) | (1 << ppi_channel[1])); - NRF_GPIOTE->CONFIG[gpiote_channel] = GPIOTE_CONFIG_MODE_Disabled; - break; - } - case PWM_OUTPUT_DISABLED: - default: - break; - } -#endif -} - -/** - * @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) { - pwmp->timer->INTENSET = - 0x1UL << (TIMER_INTENSET_COMPARE0_Pos + pwmp->channels); -} - -/** - * @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->timer->INTENCLR = - 0x1UL << (TIMER_INTENCLR_COMPARE0_Pos + pwmp->channels); -} - -/** - * @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) { - pwmp->timer->INTENSET = - 0x1UL << (TIMER_INTENSET_COMPARE0_Pos + 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) { - pwmp->timer->INTENCLR = - 0x1UL << (TIMER_INTENCLR_COMPARE0_Pos + channel); -} - -#endif /* HAL_USE_PWM */ - -/** @} */ diff --git a/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.h b/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.h deleted file mode 100644 index 2cad6e7..0000000 --- a/os/hal/ports/NRF5/NRF51822/hal_pwm_lld.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - ChibiOS/HAL - 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. -*/ - -/** - * @file hal_pwm_lld.h - * @brief NRF51 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. - */ -#if NRF5_PWM_USE_GPIOTE_PPI -#define PWM_CHANNELS 2 -#else -#define PWM_CHANNELS 3 -#endif - -#define PWM_FREQUENCY_16MHZ 16000000 /** @brief 16MHz */ -#define PWM_FREQUENCY_8MHZ 8000000 /** @brief 8MHz */ -#define PWM_FREQUENCY_4MHZ 4000000 /** @brief 4MHz */ -#define PWM_FREQUENCY_2MHZ 2000000 /** @brief 2MHz */ -#define PWM_FREQUENCY_1MHZ 1000000 /** @brief 1MHz */ -#define PWM_FREQUENCY_500KHZ 500000 /** @brief 500kHz */ -#define PWM_FREQUENCY_250KHZ 250000 /** @brief 250kHz */ -#define PWM_FREQUENCY_125KHZ 125000 /** @brief 125kHz */ -#define PWM_FREQUENCY_62500HZ 62500 /** @brief 62500Hz */ -#define PWM_FREQUENCY_31250HZ 31250 /** @brief 31250Hz */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ - -/** - * @brief TIMER0 as driver implementation - */ -#if !defined(NRF5_PWM_USE_TIMER0) -#define NRF5_PWM_USE_TIMER0 FALSE -#endif - -/** - * @brief TIMER1 as driver implementation - */ -#if !defined(NRF5_PWM_USE_TIMER1) -#define NRF5_PWM_USE_TIMER1 FALSE -#endif - -/** - * @brief TIMER2 as driver implementation - */ -#if !defined(NRF5_PWM_USE_TIMER2) -#define NRF5_PWM_USE_TIMER2 FALSE -#endif - -/** - * @brief TIMER0 interrupt priority level setting. - */ -#if !defined(NRF5_PWM_TIMER0_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_PWM_TIMER0_PRIORITY 3 -#endif - -/** - * @brief TIMER1 interrupt priority level setting. - */ -#if !defined(NRF5_PWM_TIMER1_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_PWM_TIMER1_PRIORITY 3 -#endif - -/** - * @brief TIMER2 interrupt priority level setting. - */ -#if !defined(NRF5_PWM_TIMER2_PRIORITY) || defined(__DOXYGEN__) -#define NRF5_PWM_TIMER2_PRIORITY 3 -#endif - -/** - * @brief Allow driver to use GPIOTE/PPI to control PAL line - */ -#if !defined(NRF5_PWM_USE_GPIOTE_PPI) -#define NRF5_PWM_USE_GPIOTE_PPI TRUE -#endif - -/** @} */ - -/*===========================================================================*/ -/* Configuration checks. */ -/*===========================================================================*/ - -#if !NRF5_PWM_USE_TIMER0 && !NRF5_PWM_USE_TIMER1 && !NRF5_PWM_USE_TIMER2 -#error "PWM driver activated but no TIMER peripheral assigned" -#endif - -#if (NRF5_ST_USE_TIMER0 == TRUE) && (NRF5_PWM_USE_TIMER0 == TRUE) -#error "TIMER0 used for ST and PWM" -#endif - -#if NRF5_PWM_USE_TIMER0 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER0_PRIORITY) -#error "Invalid IRQ priority assigned to TIMER0" -#endif - -#if NRF5_PWM_USE_TIMER1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER1_PRIORITY) -#error "Invalid IRQ priority assigned to TIMER1" -#endif - -#if NRF5_PWM_USE_TIMER2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(NRF5_PWM_TIMER2_PRIORITY) -#error "Invalid IRQ priority assigned to TIMER2" -#endif - - -/*===========================================================================*/ -/* 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.*/ - -#if NRF5_PWM_USE_GPIOTE_PPI || defined(__DOXYGEN__) - /** - * @brief PAL line to toggle. - * @note Only used if mode is PWM_OUTPUT_HIGH or PWM_OUTPUT_LOW. - * @note When NRF5_PWM_USE_GPIOTE_PPI is used and channel enabled, - * it wont be possible to access this PAL line using the PAL - * driver. - */ - ioline_t ioline; - - /** - * @brief Unique GPIOTE channel to use. (1 channel) - * @note Only 4 GPIOTE channels are available on nRF51. - */ - uint8_t gpiote_channel; - - /** - * @brief Unique PPI channels to use. (2 channels) - * @note Only 16 PPI channels are available on nRF51 - * (When Softdevice is enabled, only channels 0-7 are available) - */ - uint8_t ppi_channel[2]; -#endif -} 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. - */ - uint32_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 TIMER registers block. - */ - NRF_TIMER_Type *timer; -}; - -/*===========================================================================*/ -/* 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)->timer->CC[(pwmp)->channels] = ((period) - 1); \ - } while(0) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if NRF5_PWM_USE_TIMER0 || defined(__DOXYGEN__) -extern PWMDriver PWMD1; -#endif -#if NRF5_PWM_USE_TIMER1 || defined(__DOXYGEN__) -extern PWMDriver PWMD2; -#endif -#if NRF5_PWM_USE_TIMER2 || 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_ */ - -/** @} */ -- cgit v1.2.3