From 1d3984c7a36aa83f9a4b6627e7ea924de806fbe0 Mon Sep 17 00:00:00 2001 From: pcirillo Date: Fri, 15 Nov 2013 09:24:59 +0000 Subject: [PATCH] git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@6478 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/platforms/SPC5xx/ADC_v1/adc_lld.c | 480 +++++++++++++++++++++ os/hal/platforms/SPC5xx/ADC_v1/adc_lld.h | 525 +++++++++++++++++++++++ 2 files changed, 1005 insertions(+) create mode 100644 os/hal/platforms/SPC5xx/ADC_v1/adc_lld.c create mode 100644 os/hal/platforms/SPC5xx/ADC_v1/adc_lld.h diff --git a/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.c b/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.c new file mode 100644 index 000000000..25168d909 --- /dev/null +++ b/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.c @@ -0,0 +1,480 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file SPC5xx/ADC_v1/adc_lld.c + * @brief ADC Driver subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/* Some forward declarations.*/ +static void adc_serve_adc_irq(edma_channel_t channel, void *p); +static void adc_serve_dma_error_irq(edma_channel_t channel, + void *p, + uint32_t esr); + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief ADC1 driver identifier. + */ +#if SPC5_ADC_USE_ADC0 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +#if SPC5_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if SPC5_ADC_USE_ADC0 || defined(__DOXYGEN__) +/** + * @brief DMA configuration for ADC0. + */ +static const edma_channel_config_t adc_adc0_dma_config = { + SPC5_ADC_ADC0_DMA_CH_ID, + SPC5_ADC0_DMA_DEV_ID, + SPC5_ADC_ADC0_DMA_IRQ_PRIO, + adc_serve_adc_irq, adc_serve_dma_error_irq, &ADCD1 +}; +#endif /* SPC5_ADC_USE_ADC0 */ + +#if SPC5_ADC_USE_ADC1 || defined(__DOXYGEN__) +/** + * @brief DMA configuration for ADC1. + */ +static const edma_channel_config_t adc_adc1_dma_config = { + SPC5_ADC_ADC1_DMA_CH_ID, + SPC5_ADC1_DMA_DEV_ID, + SPC5_ADC_ADC1_DMA_IRQ_PRIO, + adc_serve_adc_irq, adc_serve_dma_error_irq, &ADCD2 +}; +#endif /* SPC5_ADC_USE_ADC1 */ + +/*===========================================================================*/ +/* Driver local functions and macros. */ +/*===========================================================================*/ + +/** + * @brief Unsigned two's complement. + * + * @param[in] n the value to be complemented + * + * @notapi + */ +#define CPL2(n) ((~(uint32_t)(n)) + 1) + +/** + * @brief Shared ISR for ADC events. + * + * @param[in] channel the channel number + * @param[in] p parameter for the registered function + * + * @notapi + */ +static void adc_serve_adc_irq(edma_channel_t channel, void *p) { + ADCDriver *adcp = (ADCDriver *)p; + edma_tcd_t *tcdp = edmaGetTCD(channel); + + if (adcp->grpp != NULL) { + if ((tcdp->word[5] >> 16) != (tcdp->word[7] >> 16)) { + /* Half transfer processing.*/ + _adc_isr_half_code(adcp); + } + else { + /* Re-starting DMA channel if in circular mode.*/ + if (adcp->grpp->circular) { + edmaChannelStart(adcp->adc_dma_channel); + } + + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + } +} + +/** + * @brief Shared ISR for DMA error events. + * + * @param[in] channel the channel number + * @param[in] p parameter for the registered function + * @param[in] esr content of the ESR register + * + * @notapi + */ +static void adc_serve_dma_error_irq(edma_channel_t channel, + void *p, + uint32_t esr) { + ADCDriver *adcp = (ADCDriver *)p; + + (void)channel; + (void)esr; + + _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); +} + +/** + * @brief ADC ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] isr content of the ISR register + */ +static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) { + + /* It could be a spurious interrupt caused by overflows after DMA disabling, + just ignore it in this case.*/ + if (adcp->grpp != NULL) { + if (isr & ADC_ISR_AWD0_LT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD0_LT); + } + if (isr & ADC_ISR_AWD1_LT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD1_LT); + } + if (isr & ADC_ISR_AWD2_LT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD2_LT); + } + if (isr & ADC_ISR_AWD3_LT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD3_LT); + } + if (isr & ADC_ISR_AWD0_HT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD0_HT); + } + if (isr & ADC_ISR_AWD1_HT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD1_HT); + } + if (isr & ADC_ISR_AWD2_HT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD2_HT); + } + if (isr & ADC_ISR_AWD3_HT) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD3_HT); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if SPC5_ADC_USE_ADC0 +#if !defined(SPC5_ADC0_WD_HANDLER) +#error "SPC5_ADC0_WD_HANDLER not defined" +#endif +/** + * @brief ADC0 Watch Dog interrupt handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(SPC5_ADC0_WD_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + isr = ADCD1.adc_tagp->WTISR.R; + + adc_lld_serve_interrupt(&ADCD1, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* SPC5_ADC_USE_ADC0 */ + +#if SPC5_ADC_USE_ADC1 +#if !defined(SPC5_ADC1_WD_HANDLER) +#error "SPC5_ADC1_WD_HANDLER not defined" +#endif +/** + * @brief ADC1 Watch Dog interrupt handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(SPC5_ADC1_WD_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADCD2.adc_tagp->WTISR.R; + + adc_lld_serve_interrupt(&ADCD2, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* SPC5_ADC_USE_ADC1 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if SPC5_ADC_USE_ADC0 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc_dma_channel = EDMA_ERROR; + ADCD1.adc_tagp = &SPC5_ADC_0; +#endif /* SPC5_ADC_USE_ADC0 */ + +#if SPC5_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD2); + ADCD2.adc_dma_channel = EDMA_ERROR; + ADCD2.adc_tagp = &SPC5_ADC_1; +#endif /* SPC5_ADC_USE_ADC1 */ + +#if SPC5_ADC_USE_ADC0 + INTC.PSR[SPC5_ADC0_EOC_NUMBER].R = SPC5_ADC_ADC0_EOC_PRIORITY; + INTC.PSR[SPC5_ADC0_ER_NUMBER].R = SPC5_ADC_ADC0_ER_PRIORITY; + INTC.PSR[SPC5_ADC0_WD_NUMBER].R = SPC5_ADC_ADC0_WD_PRIORITY; +#endif + +#if SPC5_ADC_USE_ADC1 + INTC.PSR[SPC5_ADC1_EOC_NUMBER].R = SPC5_ADC_ADC1_EOC_PRIORITY; + INTC.PSR[SPC5_ADC1_ER_NUMBER].R = SPC5_ADC_ADC1_ER_PRIORITY; + INTC.PSR[SPC5_ADC1_WD_NUMBER].R = SPC5_ADC_ADC1_WD_PRIORITY; +#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 SPC5_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + adcp->adc_dma_channel = edmaChannelAllocate(&adc_adc0_dma_config); + } +#endif /* SPC5_ADC_USE_ADC0 */ + +#if SPC5_ADC_USE_ADC1 + if (&ADCD2 == adcp) { + adcp->adc_dma_channel = edmaChannelAllocate(&adc_adc1_dma_config); + } +#endif /* SPC5_ADC_USE_ADC1 */ + + osalDbgAssert((adcp->adc_dma_channel != EDMA_ERROR), + "adc_lld_start(), #1", "DMA channel cannot be allocated"); + + /* Configures the peripheral.*/ + + /* Sets ADC0 Clock.*/ +#if SPC5_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + halSPCSetPeripheralClockMode(SPC5_ADC0_PCTL, + SPC5_ADC_ADC0_START_PCTL); + } +#endif + + /* Sets ADC1 Clock.*/ +#if SPC5_ADC_USE_ADC1 + if (&ADCD2 == adcp) { + halSPCSetPeripheralClockMode(SPC5_ADC1_PCTL, + SPC5_ADC_ADC1_START_PCTL); + } +#endif + + /* Sets ADC Normal Mode.*/ + adcp->adc_tagp->MCR.B.PWDN = 0; + + /* Sets analog clock.*/ + if (adcp->config->clock == HALF_PERIPHERAL_SET_CLOCK_FREQUENCY) { + adcp->adc_tagp->MCR.B.ADCLKSEL = 0; + } else if (adcp->config->clock == PERIPHERAL_SET_CLOCK_FREQUENCY) { + adcp->adc_tagp->MCR.B.ADCLKSEL = 1; + } + + /* Sets MCR Register.*/ + adcp->adc_tagp->MCR.R = ADC_MCR_OWREN | ADC_MCR_MODE; + } +} + +/** + * @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.*/ + + /* Releases the allocated DMA channel.*/ + edmaChannelRelease(adcp->adc_dma_channel); + + /* Clears thresholds’ values and deactives watchdog threshold interrupts.*/ + if (adcp->grpp->wtimr != 0) { + adcp->adc_tagp->TRC[0].R = 0; + adcp->adc_tagp->TRC[1].R = 0; + adcp->adc_tagp->TRC[2].R = 0; + adcp->adc_tagp->TRC[3].R = 0; + adcp->adc_tagp->THRHLR[0].R = 0; + adcp->adc_tagp->THRHLR[1].R = 0; + adcp->adc_tagp->THRHLR[2].R = 0; + adcp->adc_tagp->THRHLR[3].R = 0; + adcp->adc_tagp->WTIMR.R = 0; + } + + /* Deactives ADC channels and the ADC DMA channels.*/ + adcp->adc_tagp->NCMR[0].R = 0; + adcp->adc_tagp->DMAR[0].R = 0; + + /* Puts the ADC Peripheral in Power-Down Mode.*/ + adcp->adc_tagp->MCR.B.PWDN = 1U; + + /* Disables the peripheral.*/ +#if SPC5_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + halSPCSetPeripheralClockMode(SPC5_ADC0_PCTL, + SPC5_ADC_ADC0_STOP_PCTL); + } +#endif + +#if SPC5_ADC_USE_ADC1 + if (&ADCD2 == adcp) { + halSPCSetPeripheralClockMode(SPC5_ADC1_PCTL, + SPC5_ADC_ADC1_STOP_PCTL); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + uint8_t i; + + //osalDbgAssert(adcp->grpp->num_channels*2 >= adcp->depth, + // "adc_lld_start_conversion(), #1", "too many elements"); + + /* Active DMA.*/ + adcp->adc_tagp->DMAE.R = ADC_DMAE_DMAEN; + + /* Setting up DMA TCD parameters.*/ + edmaChannelSetup(adcp->adc_dma_channel, /* channel. */ + adcp->adc_tagp->CDR[adcp->grpp->init_channel].B.CDATA, /* src. */ + adcp->samples, /* dst. */ + 4, /* soff, advance by four. */ + 2, /* doff, advance by two. */ + 1, /* ssize, 16 bits transfers.*/ + 1, /* dsize, 16 bits transfers.*/ + 2, /* nbytes, always two. */ + (uint32_t)adcp->grpp->num_channels * + (uint32_t)adcp->depth, /* iter. */ + CPL2((uint32_t)adcp->grpp->num_channels * 4), /* slast. */ + CPL2((uint32_t)adcp->grpp->num_channels * + (uint32_t)adcp->depth * + sizeof(adcsample_t)), /* dlast. */ + EDMA_TCD_MODE_DREQ | EDMA_TCD_MODE_INT_END | + ((adcp->depth > 1) ? EDMA_TCD_MODE_INT_HALF: 0)); /* mode. */ + + /* Sets thresholds’ values and active watchdog threshold interrupts if any.*/ + if (adcp->grpp->wtimr != 0) { + adcp->adc_tagp->TRC[0].R = adcp->grpp->trcr[0]; + adcp->adc_tagp->TRC[1].R = adcp->grpp->trcr[1]; + adcp->adc_tagp->TRC[2].R = adcp->grpp->trcr[2]; + adcp->adc_tagp->TRC[3].R = adcp->grpp->trcr[3]; + adcp->adc_tagp->THRHLR[0].R = adcp->grpp->thrhlr[0]; + adcp->adc_tagp->THRHLR[1].R = adcp->grpp->thrhlr[1]; + adcp->adc_tagp->THRHLR[2].R = adcp->grpp->thrhlr[2]; + adcp->adc_tagp->THRHLR[3].R = adcp->grpp->thrhlr[3]; + adcp->adc_tagp->WTIMR.R = adcp->grpp->wtimr; + } + + /* Active ADC channels for the conversion and sets the ADC DMA channels.*/ + for (i = adcp->grpp->init_channel; i <= adcp->grpp->final_channel; i++) { + adcp->adc_tagp->NCMR[0].R |= 1U << i; + adcp->adc_tagp->DMAR[0].R |= 1U << i; + } + + /* Sets ADC conversion timing register.*/ + adcp->adc_tagp->CTR[0].R = adcp->grpp->ctr; + + /* Starting DMA channels.*/ + edmaChannelStart(adcp->adc_dma_channel); + + /* Starts conversion.*/ + adcp->adc_tagp->MCR.B.NSTART = 1U; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + /* Stop DMA channel.*/ + edmaChannelStop(adcp->adc_dma_channel); + + /* Stops conversion.*/ + adcp->adc_tagp->MCR.B.NSTART = 0; + + /* Disables Interrupts and DMA.*/ + adcp->adc_tagp->WTIMR.R = 0; + adcp->adc_tagp->DMAE.R = ADC_DMAE_DMAEN; + + /* Clears all Interrupts.*/ + adcp->adc_tagp->WTISR.R = 0; +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.h b/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.h new file mode 100644 index 000000000..b08c0f1a1 --- /dev/null +++ b/os/hal/platforms/SPC5xx/ADC_v1/adc_lld.h @@ -0,0 +1,525 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file SPC5xx/ADC_v1/adc_lld.h + * @brief ADC Driver subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef _ADC_LLD_H_ +#define _ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +#include "spc5_adc.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Analog channel identifiers + * @{ + */ +#if SPC5_HAS_ADC0 || defined(__DOXYGEN__) +#define ADC0_CHN_AN0 0U +#define ADC0_CHN_AN1 1U +#define ADC0_CHN_AN2 2U +#define ADC0_CHN_AN3 3U +#define ADC0_CHN_AN4 4U +#define ADC0_CHN_AN5 5U +#define ADC0_CHN_AN6 6U +#define ADC0_CHN_AN7 7U +#define ADC0_CHN_AN8 8U +#define ADC0_CHN_AN9 9U +#define ADC0_CHN_AN10 10U +#define ADC0_CHN_AN11 11U +#define ADC0_CHN_AN12 12U +#define ADC0_CHN_AN13 13U +#define ADC0_CHN_AN14 14U +#define ADC0_CHN_AN15 15U +#endif + +#if SPC5_HAS_ADC1 || defined(__DOXYGEN__) +#define ADC1_CHN_AN0 0U +#define ADC1_CHN_AN1 1U +#define ADC1_CHN_AN2 2U +#define ADC1_CHN_AN3 3U +#define ADC1_CHN_AN4 4U +#define ADC1_CHN_AN5 5U +#define ADC1_CHN_AN6 6U +#define ADC1_CHN_AN7 7U +#define ADC1_CHN_AN8 8U +#define ADC1_CHN_AN9 9U +#define ADC1_CHN_AN10 10U +#define ADC1_CHN_AN11 11U +#define ADC1_CHN_AN12 12U +#define ADC1_CHN_AN13 13U +#define ADC1_CHN_AN14 14U +#define ADC1_CHN_AN15 15U +#endif +/** @} */ + +/** + * @name ADC MCR register definitions + * @{ + */ +#define ADC_MCR_OWREN (1U << 31) +#define ADC_MCR_WLSIDE (1U << 30) +#define ADC_MCR_MODE (1U << 29) +#define ADC_MCR_NSTART (1U << 24) +#define ADC_MCR_JTRGEN (1U << 22) +#define ADC_MCR_JEDGE (1U << 21) +#define ADC_MCR_JSTART (1U << 20) +#define ADC_MCR_CTUEN (1U << 17) +#define ADC_MCR_ADCLKSEL (1U << 8) +#define ADC_MCR_ABORTCHAIN (1U << 7) +#define ADC_MCR_ABORT (1U << 6) +#define ADC_MCR_ACKO (1U << 5) +#define ADC_MCR_PWDN (1U << 0) +/** @} */ + +/** + * @name ADC MSR register definitions + * @{ + */ +#define ADC_MSR_NSTART (1U << 24) +#define ADC_MSR_JABORT (1U << 23) +#define ADC_MSR_JSTART (1U << 20) +#define ADC_MSR_CTUSTART (1U << 16) +#define ADC_MSR_CHADDR (1U << 9) +#define ADC_MSR_ACKO (1U << 5) +#define ADC_MSR_ADCSTATUS (1U << 0) +/** @} */ + +/** + * @name ADC ISR register definitions + * @{ + */ +#define ADC_ISR_EOCTU (1U << 4) +#define ADC_ISR_JEOC (1U << 3) +#define ADC_ISR_JECH (1U << 2) +#define ADC_ISR_EOC (1U << 1) +#define ADC_ISR_ECH (1U << 0) +/** @} */ + +/** + * @name ADC IMR register definitions + * @{ + */ +#define ADC_IMR_MSKEOCTU (1U << 4) +#define ADC_IMR_MSKJEOC (1U << 3) +#define ADC_IMR_MSKJECH (1U << 2) +#define ADC_IMR_MSKEOC (1U << 1) +#define ADC_IMR_MSKECH (1U << 0) +/** @} */ + +/** + * @name ADC DMAE register definitions + * @{ + */ +#define ADC_DMAE_DCLR (1U << 1) +#define ADC_DMAE_DMAEN (1U << 0) +/** @} */ + +/** + * @name ADC CDR register definitions + * @{ + */ +#define ADC_CDR_VALID (1U << 19) +#define ADC_CDR_OVERW (1U << 18) +#define ADC_CDR_RESULT (1U << 16) +#define ADC_CDR_CDATA_LEFT (1U << 6) +#define ADC_CDR_CDATA_RIGHT (1U << 0) +/** @} */ + +/** + * @name ADC Wathdog ISR definitions + * @{ + */ +#define ADC_ISR_AWD3_HT (1U << 7) +#define ADC_ISR_AWD2_HT (1U << 6) +#define ADC_ISR_AWD1_HT (1U << 5) +#define ADC_ISR_AWD0_HT (1U << 4) +#define ADC_ISR_AWD3_LT (1U << 3) +#define ADC_ISR_AWD2_LT (1U << 2) +#define ADC_ISR_AWD1_LT (1U << 1) +#define ADC_ISR_AWD0_LT (1U << 0) +/** @} */ + + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +#if SPC5_HAS_ADC0 || defined(__DOXYGEN__) +/** + * @brief ADCD1 driver enable switch. + * @details If set to @p TRUE the support for ADC0 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC0) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC0 FALSE +#endif + +/** + * @brief ADC0 EOC interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC0_EOC_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_EOC_PRIORITY 12 +#endif + +/** + * @brief ADC0 ER interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC0_ER_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_ER_PRIORITY 12 +#endif + +/** + * @brief ADC0 WD interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC0_WD_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_WD_PRIORITY 12 +#endif + +/** + * @brief ADC0 DMA IRQ priority. + */ +#if !defined(SPC5_ADC_ADC0_DMA_IRQ_PRIO) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_DMA_IRQ_PRIO 12 +#endif + +/** + * @brief ADC0 peripheral configuration when started. + * @note The default configuration is 1 (always run) in run mode and + * 2 (only halt) in low power mode. The defaults of the run modes + * are defined in @p hal_lld.h. + */ +#if !defined(SPC5_ADC_ADC0_START_PCTL) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_START_PCTL (SPC5_ME_PCTL_RUN(1) | \ + SPC5_ME_PCTL_LP(2)) +#endif + +/** + * @brief ADC0 peripheral configuration when stopped. + * @note The default configuration is 0 (never run) in run mode and + * 0 (never run) in low power mode. The defaults of the run modes + * are defined in @p hal_lld.h. + */ +#if !defined(SPC5_ADC_ADC0_STOP_PCTL) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC0_STOP_PCTL (SPC5_ME_PCTL_RUN(0) | \ + SPC5_ME_PCTL_LP(0)) +#endif +#endif + +#if SPC5_HAS_ADC1 || defined(__DOXYGEN__) +/** + * @brief ADCD2 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(SPC5_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define SPC5_ADC_USE_ADC1 FALSE +#endif + +/** + * @brief ADC1 EOC interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC1_EOC_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_EOC_PRIORITY 12 +#endif + +/** + * @brief ADC1 ER interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC1_ER_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_ER_PRIORITY 12 +#endif + +/** + * @brief ADC1 WD interrupt priority level setting. + */ +#if !defined(SPC5_ADC_ADC1_WD_PRIORITY) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_WD_PRIORITY 12 +#endif + +/** + * @brief ADC1 DMA IRQ priority. + */ +#if !defined(SPC5_ADC_ADC1_DMA_IRQ_PRIO) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_DMA_IRQ_PRIO 12 +#endif + +/** + * @brief ADC1 peripheral configuration when started. + * @note The default configuration is 1 (always run) in run mode and + * 2 (only halt) in low power mode. The defaults of the run modes + * are defined in @p hal_lld.h. + */ +#if !defined(SPC5_ADC_ADC1_START_PCTL) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_START_PCTL (SPC5_ME_PCTL_RUN(1) | \ + SPC5_ME_PCTL_LP(2)) +#endif + +/** + * @brief ADC1 peripheral configuration when stopped. + * @note The default configuration is 0 (never run) in run mode and + * 0 (never run) in low power mode. The defaults of the run modes + * are defined in @p hal_lld.h. + */ +#if !defined(SPC5_ADC_ADC1_STOP_PCTL) || defined(__DOXYGEN__) +#define SPC5_ADC_ADC1_STOP_PCTL (SPC5_ME_PCTL_RUN(0) | \ + SPC5_ME_PCTL_LP(0)) +#endif +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !SPC5_HAS_ADC0 +#error "ADC0 not present in the selected device" +#endif + +#if !SPC5_HAS_ADC1 +#error "ADC1 not present in the selected device" +#endif + +#if !SPC5_ADC_USE_ADC0 && !SPC5_ADC_USE_ADC1 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC clock frequency. + */ +typedef enum { + HALF_PERIPHERAL_SET_CLOCK_FREQUENCY = 0, /**< ADC clock frequency is half Peripheral Set Clock frequency. */ + PERIPHERAL_SET_CLOCK_FREQUENCY = 1 /**< ADC clock frequency is equal to Peripheral Set Clock frequency. */ +} adc_clock; + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_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_AWD0_HT = 1, /**< Watchdog 0 triggered Higher Threshold. */ + ADC_ERR_AWD0_LT = 2, /**< Watchdog 0 triggered Lower Threshold. */ + ADC_ERR_AWD1_HT = 3, /**< Watchdog 1 triggered Higher Threshold. */ + ADC_ERR_AWD1_LT = 4, /**< Watchdog 1 triggered Lower Threshold. */ + ADC_ERR_AWD2_HT = 5, /**< Watchdog 2 triggered Higher Threshold. */ + ADC_ERR_AWD2_LT = 6, /**< Watchdog 2 triggered Lower Threshold. */ + ADC_ERR_AWD3_HT = 7, /**< Watchdog 3 triggered Higher Threshold. */ + ADC_ERR_AWD3_LT = 8, /**< Watchdog 3 triggered Lower Threshold. */ +} adcerror_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 ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + * @note Implementations may extend this structure to contain more, + * architecture dependent, fields. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool_t 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; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief ADC WTIMR register initialization data. + */ + uint32_t wtimr; + /** + * @brief ADC TRCx register initialization data. + */ + uint32_t trcr[4]; + /** + * @brief ADC THRHLRx register initialization data. + */ + uint32_t thrhlr[4]; + /** + * @brief ADC CTR0 register initialization data. + */ + uint32_t ctr; + /** + * @brief Only the conversion of contiguous channels is implemented. + * Specify initial and final conversion channels. + */ + /** + * @brief ADC Initial conversion channel. + */ + uint32_t init_channel; + /** + * @brief ADC Final conversion channel. + */ + uint32_t final_channel; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Analog clock frequency. + */ + adc_clock clock; +} 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 EDMA channel used for the ADC. + */ + edma_channel_t adc_dma_channel; + /** + * @brief Pointer to the ADCx registers block. + */ + volatile struct spc5_adc *adc_tagp; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if SPC5_ADC_USE_ADC0 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#if SPC5_ADC_USE_ADC1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD2; +#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 /* _ADC_LLD_H_ */ + +/** @} */