From 7fa948e7c62692cbff2bd4d6f58f7a1a56940ecc Mon Sep 17 00:00:00 2001 From: Konstantin Oblaukhov Date: Sun, 12 Jan 2020 13:34:28 +0700 Subject: [PATCH] Driver for new ADC (SAADC) in NRF52 devices. --- os/hal/ports/NRF5/LLD/ADCv2/driver.mk | 9 + os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c | 222 ++++++++++++++++++++++ os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.h | 168 ++++++++++++++++ os/hal/ports/NRF5/NRF52832/platform.mk | 1 + 4 files changed, 400 insertions(+) create mode 100644 os/hal/ports/NRF5/LLD/ADCv2/driver.mk create mode 100644 os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c create mode 100644 os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.h diff --git a/os/hal/ports/NRF5/LLD/ADCv2/driver.mk b/os/hal/ports/NRF5/LLD/ADCv2/driver.mk new file mode 100644 index 00000000..2797439e --- /dev/null +++ b/os/hal/ports/NRF5/LLD/ADCv2/driver.mk @@ -0,0 +1,9 @@ +ifeq ($(USE_SMART_BUILD),yes) +ifneq ($(findstring HAL_USE_ADC TRUE,$(HALCONF)),) +PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c +endif +else +PLATFORMSRC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c +endif + +PLATFORMINC_CONTRIB += ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/ADCv2 diff --git a/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c b/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c new file mode 100644 index 00000000..589bfdfc --- /dev/null +++ b/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.c @@ -0,0 +1,222 @@ +/* + Copyright (C) 2020 Konstantin Oblaukhov + + 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_adc_lld.c + * @brief NRF5 ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_ADC == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief ADC1 driver identifier. + */ +#if (NRF5_ADC_USE_ADC1 == TRUE) || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if NRF5_ADC_USE_ADC1 || defined(__DOXYGEN__) +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(Vector5C) { + + ADCDriver *adcp = &ADCD1; + NRF_SAADC_Type *adc = adcp->adc; + const ADCConversionGroup *grpp = adcp->grpp; + + OSAL_IRQ_PROLOGUE(); + + if (adc->EVENTS_RESULTDONE) { + adc->EVENTS_RESULTDONE = 0; + adcp->ch_counter++; + + if (adcp->ch_counter == grpp->num_channels) { + adcp->counter++; + adcp->ch_counter = 0; + + if (grpp->circular & + (adcp->counter == adcp->depth / 2)) + _adc_isr_half_code(adcp) + + if ((adcp->counter < adcp->depth) && !grpp->external) { + adc->TASKS_SAMPLE = 1; + } + } + } + + if (adc->EVENTS_END) { + adc->EVENTS_END = 0; + _adc_isr_full_code(adcp); + + if (grpp->circular) { + adcp->counter = 0; + adcp->ch_counter = 0; + adc->TASKS_START = 1; + if (!grpp->external) + adc->TASKS_SAMPLE = 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 == TRUE + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc = NRF_SAADC; +#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 (adcp->state == ADC_STOP) { + /* Enables the peripheral.*/ +#if NRF5_ADC_USE_ADC1 == TRUE + if (&ADCD1 == adcp) { + adcp->adc->INTEN = SAADC_INTEN_END_Msk | SAADC_INTEN_RESULTDONE_Msk; + nvicEnableVector(SAADC_IRQn, NRF5_ADC_IRQ_PRIORITY); + } +#endif + } + /* Configures the peripheral.*/ + +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + if (adcp->state == ADC_READY) { + /* Resets the peripheral.*/ + + /* Disables the peripheral.*/ +#if NRF5_ADC_USE_ADC1 == TRUE + if (&ADCD1 == adcp) { + nvicDisableVector(SAADC_IRQn); + adcp->adc->INTEN = 0; + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + NRF_SAADC_Type *adc = adcp->adc; + const ADCConversionGroup *grpp = adcp->grpp; + + adc->RESOLUTION = grpp->resolution; + adc->OVERSAMPLE = grpp->oversample; + adc->SAMPLERATE = grpp->samplerate; + + for (int i = 0; i < grpp->num_channels; i++) { + adc->CH[i].PSELP = grpp->channels[i].pselp; + adc->CH[i].PSELN = grpp->channels[i].pseln; + adc->CH[i].CONFIG = grpp->channels[i].config; + } + for (int i = grpp->num_channels; i < NRF5_ADC_MAX_CHANNELS; i++) { + adc->CH[i].PSELP = 0; + adc->CH[i].PSELN = 0; + } + + adc->RESULT.PTR = (uint32_t)adcp->samples; + adc->RESULT.MAXCNT = adcp->depth * grpp->num_channels; + + adcp->counter = 0; + adcp->ch_counter = 0; + + adc->ENABLE = SAADC_ENABLE_ENABLE_Enabled << SAADC_ENABLE_ENABLE_Pos; + adc->TASKS_START = 1; + + if (!grpp->external) + adc->TASKS_SAMPLE = 1; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + NRF_SAADC_Type *adc = adcp->adc; + + adc->TASKS_STOP = 1; + adc->ENABLE = SAADC_ENABLE_ENABLE_Disabled << SAADC_ENABLE_ENABLE_Pos; +} + +#endif /* HAL_USE_ADC == TRUE */ + +/** @} */ diff --git a/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.h b/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.h new file mode 100644 index 00000000..73e4b67c --- /dev/null +++ b/os/hal/ports/NRF5/LLD/ADCv2/hal_adc_lld.h @@ -0,0 +1,168 @@ +/* + Copyright (C) 2020 Konstantin Oblaukhov + + 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_adc_lld.h + * @brief NRF5 ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef HAL_ADC_LLD_H +#define HAL_ADC_LLD_H + +#if (HAL_USE_ADC == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name NRF5 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 (CORTEX_MAX_KERNEL_PRIORITY + 1) +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !NRF5_ADC_USE_ADC1 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +#if !defined(NRF5_ADC_MAX_CHANNELS) || defined(__DOXYGEN__) +#define NRF5_ADC_MAX_CHANNELS 8 +#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 Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1, /**< ADC overflow condition. */ +} adcerror_t; + +/** + * @brief ADC channel config. + */ +struct adc_lld_channel_config { + /* @brief ADC channel positive pin.*/ + uint8_t pselp; + /* @brief ADC channel negative pin.*/ + uint8_t pseln; + /* @brief ADC channel CONFIG register details.*/ + uint32_t config; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Low level fields of the ADC driver structure. + */ +#define adc_lld_driver_fields \ + /* @brief Pointer to the ADCx registers block.*/ \ + NRF_SAADC_Type *adc; \ + /* @brief Current sample counter.*/ \ + size_t counter; \ + /* @brief Current channel counter.*/ \ + size_t ch_counter + +/** + * @brief Low level fields of the ADC configuration structure. + */ +#define adc_lld_config_fields \ + /* Dummy configuration, it is not needed.*/ \ + uint32_t dummy + +/** + * @brief Low level fields of the ADC configuration structure. + */ +#define adc_lld_configuration_group_fields \ + /* @brief ADC sample is triggered by external source.*/ \ + bool external; \ + /* @brief ADC RESOLUTION register details.*/ \ + uint32_t resolution; \ + /* @brief ADC OVERSAMPLE register details.*/ \ + uint32_t oversample; \ + /* @brief ADC SAMPLERATE register details.*/ \ + uint32_t samplerate; \ + /* @brief ADC channel configurations.*/ \ + struct adc_lld_channel_config channels[NRF5_ADC_MAX_CHANNELS] + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if (NRF5_ADC_USE_ADC1 == TRUE) && !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 == TRUE */ + +#endif /* HAL_ADC_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/NRF5/NRF52832/platform.mk b/os/hal/ports/NRF5/NRF52832/platform.mk index 1e0a1afe..98f8897f 100644 --- a/os/hal/ports/NRF5/NRF52832/platform.mk +++ b/os/hal/ports/NRF5/NRF52832/platform.mk @@ -18,6 +18,7 @@ HALCONF := $(strip $(shell cat $(CONFDIR)/halconf.h $(CONFDIR)/halconf_community endif +include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/ADCv2/driver.mk include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/GPIOv1/driver.mk include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/UARTv1/driver.mk include ${CHIBIOS_CONTRIB}/os/hal/ports/NRF5/LLD/SPIv1/driver.mk