git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@6502 35acf78f-673a-0410-8e92-d51de3d6d3f4

master
pcirillo 2013-11-21 12:18:16 +00:00
parent 5d284d0f61
commit 10fcc2b9f6
2 changed files with 0 additions and 1121 deletions

View File

@ -1,592 +0,0 @@
/*
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;
ADCD1.adc_tagp->WTISR.R = isr;
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;
ADCD2.adc_tagp->WTISR.R = isr;
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;
/* Sets peripheral clock.*/
halSPCSetPeripheralClockMode(SPC5_ADC0_PCTL, SPC5_ADC_ADC0_START_PCTL);
ADCD1.adc_tagp->MCR.B.ADCLKSEL = SPC5_ADC_ADC0_CLK_FREQUENCY;
halSPCSetPeripheralClockMode(SPC5_ADC0_PCTL, SPC5_ADC_ADC0_STOP_PCTL);
#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;
/* Sets peripheral clock.*/
halSPCSetPeripheralClockMode(SPC5_ADC1_PCTL, SPC5_ADC_ADC1_START_PCTL);
ADCD2.adc_tagp->MCR.B.ADCLKSEL = SPC5_ADC_ADC1_CLK_FREQUENCY;
halSPCSetPeripheralClockMode(SPC5_ADC1_PCTL, SPC5_ADC_ADC1_STOP_PCTL);
#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);
halSPCSetPeripheralClockMode(SPC5_ADC0_PCTL,
SPC5_ADC_ADC0_START_PCTL);
}
#endif /* SPC5_ADC_USE_ADC0 */
#if SPC5_ADC_USE_ADC1
if (&ADCD2 == adcp) {
adcp->adc_dma_channel = edmaChannelAllocate(&adc_adc1_dma_config);
halSPCSetPeripheralClockMode(SPC5_ADC1_PCTL,
SPC5_ADC_ADC1_START_PCTL);
}
#endif /* SPC5_ADC_USE_ADC1 */
osalDbgAssert((adcp->adc_dma_channel != EDMA_ERROR),
"adc_lld_start(), #1", "DMA channel cannot be allocated");
/* Sets ADC Normal Mode.*/
adcp->adc_tagp->MCR.B.PWDN = 0;
/* 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 deactive watchdog threshold interrupts.*/
#if SPC5_ADC_NTRESHOLD == 4
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;
/* Disables the watchdog interrupts if any.*/
adcp->adc_tagp->CIMR[0].R = 0;
/* Clears watchdog interrupts if any.*/
adcp->adc_tagp->WTISR.R = 0xFFFF;
#elif SPC5_ADC_NTRESHOLD == 16
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->THRHLR_2[0].R = 0;
adcp->adc_tagp->THRHLR_2[1].R = 0;
adcp->adc_tagp->THRHLR_2[2].R = 0;
adcp->adc_tagp->THRHLR_2[3].R = 0;
adcp->adc_tagp->THRHLR_2[4].R = 0;
adcp->adc_tagp->THRHLR_2[5].R = 0;
adcp->adc_tagp->THRHLR_2[6].R = 0;
adcp->adc_tagp->THRHLR_2[7].R = 0;
adcp->adc_tagp->THRHLR_2[8].R = 0;
adcp->adc_tagp->THRHLR_2[9].R = 0;
adcp->adc_tagp->THRHLR_2[10].R = 0;
adcp->adc_tagp->THRHLR_2[11].R = 0;
adcp->adc_tagp->CWSEL[0].R = 0;
adcp->adc_tagp->CWSEL[1].R = 0;
adcp->adc_tagp->CWENR[0].R = 0;
adcp->adc_tagp->AWORR[0].R = 0;
adcp->adc_tagp->WTIMR.R = 0;
/* Disables the watchdog interrupts if any.*/
adcp->adc_tagp->CIMR[0].R = 0;
/* Clears watchdog interrupts if any.*/
adcp->adc_tagp->WTISR.R = 0xFFFFFFFF;
#endif
/* Deactive 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) {
uint32_t ch_mask;
uint8_t i;
/* Setting up DMA TCD parameters.*/
edmaChannelSetup(adcp->adc_dma_channel, /* channel. */
((uint8_t *)&adcp->adc_tagp->CDR[adcp->grpp->init_channel].R) + 2, /* 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. */
/* Active DMA.*/
adcp->adc_tagp->DMAE.R = ADC_DMAE_DMAEN;
/* TODO: make the number of WD registers a parameter in the registry, modify
the configuration structure.*/
/* Sets thresholds values and active watchdog threshold interrupts if any.*/
#if SPC5_ADC_NTRESHOLD == 4
for (i = 0; i < SPC5_ADC_NTRESHOLD; i++) {
switch (adcp->grpp->thresholds[i].threshold_mode){
case ADC_THRHLR_DISABLED:
break;
case ADC_THRHLR_HIGHER:
/* Sets threshold registers.*/
adcp->adc_tagp->TRC[i].R = (1U << 15) | adcp->grpp->thresholds[i].adc_ch;
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = 1U << (4U + i);
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
case ADC_THRHLR_LOWER:
/* Sets threshold registers.*/
adcp->adc_tagp->TRC[i].R = (1U << 15) | adcp->grpp->thresholds[i].adc_ch;
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = 1U << i;
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
case ADC_THRHLR_BOTH_HL:
/* Sets threshold registers.*/
adcp->adc_tagp->TRC[i].R = (1U << 15) | adcp->grpp->thresholds[i].adc_ch;
adcp->adc_tagp->THRHLR[i].B.THRL = adcp->grpp->thresholds[i].low_threshold_value;
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = (1U << i) | (1U << (4U + i));
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
}
}
#elif SPC5_ADC_NTRESHOLD == 16
for (i = 0; i < SPC5_ADC_NTRESHOLD; i++) {
switch (adcp->grpp->thresholds[i].threshold_mode){
case ADC_THRHLR_DISABLED:
break;
case ADC_THRHLR_HIGHER:
/* Sets threshold registers.*/
if (adcp->grpp->thresholds[i].adc_ch > 7U) {
adcp->adc_tagp->CWSEL[1].R = i << ((adcp->grpp->thresholds[i].adc_ch - 8U) * 4U);
} else {
adcp->adc_tagp->CWSEL[0].R = i << (adcp->grpp->thresholds[i].adc_ch * 4U);
}
adcp->adc_tagp->CWENR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
if (i > 4U) {
adcp->adc_tagp->THRHLR_2[i - 4U].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
} else {
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
}
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = 1U << (1U + i * 2U);
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
case ADC_THRHLR_LOWER:
/* Sets threshold registers.*/
if (adcp->grpp->thresholds[i].adc_ch > 7U) {
adcp->adc_tagp->CWSEL[1].R = i << ((adcp->grpp->thresholds[i].adc_ch - 8U) * 4U);
} else {
adcp->adc_tagp->CWSEL[0].R = i << (adcp->grpp->thresholds[i].adc_ch * 4U);
}
adcp->adc_tagp->CWENR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
if (i > 4U) {
adcp->adc_tagp->THRHLR_2[i - 4U].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
} else {
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
}
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = 1U << (i * 2U);
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
case ADC_THRHLR_BOTH_HL:
/* Sets threshold registers.*/
if (adcp->grpp->thresholds[i].adc_ch > 7U) {
adcp->adc_tagp->CWSEL[1].R = i << ((adcp->grpp->thresholds[i].adc_ch - 8U) * 4U);
} else {
adcp->adc_tagp->CWSEL[0].R = i << (adcp->grpp->thresholds[i].adc_ch * 4U);
}
adcp->adc_tagp->CWENR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
if (i > 4U) {
adcp->adc_tagp->THRHLR_2[i - 4U].B.THRL = adcp->grpp->thresholds[i].low_threshold_value;
adcp->adc_tagp->THRHLR_2[i - 4U].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
} else {
adcp->adc_tagp->THRHLR[i].B.THRL = adcp->grpp->thresholds[i].low_threshold_value;
adcp->adc_tagp->THRHLR[i].B.THRH = adcp->grpp->thresholds[i].high_threshold_value;
}
/* Active interrupts.*/
adcp->adc_tagp->WTIMR.R = (1U << (1U + i * 2U)) | (1U << (i * 2U));
adcp->adc_tagp->CIMR[0].R = 1U << adcp->grpp->thresholds[i].adc_ch;
break;
}
}
#endif
/* Active ADC channels for the conversion and sets the ADC DMA channels.*/
ch_mask = ((1 << adcp->grpp->num_channels) - 1) << adcp->grpp->init_channel;
adcp->adc_tagp->NCMR[0].R = ch_mask;
adcp->adc_tagp->DMAR[0].R = ch_mask;
/* 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 */
/** @} */

View File

@ -1,529 +0,0 @@
/*
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 || SPC5_HAS_ADC1 || defined(__DOXYGEN__)
#define ADC_CHN_AN0 0U
#define ADC_CHN_AN1 1U
#define ADC_CHN_AN2 2U
#define ADC_CHN_AN3 3U
#define ADC_CHN_AN4 4U
#define ADC_CHN_AN5 5U
#define ADC_CHN_AN6 6U
#define ADC_CHN_AN7 7U
#define ADC_CHN_AN8 8U
#define ADC_CHN_AN9 9U
#define ADC_CHN_AN10 10U
#define ADC_CHN_AN11 11U
#define ADC_CHN_AN12 12U
#define ADC_CHN_AN13 13U
#define ADC_CHN_AN14 14U
#define ADC_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 thresholds.
*/
typedef enum {
ADC_THRHLR_DISABLED = 0, /**< Watchdog threshold disabled. */
ADC_THRHLR_HIGHER = 1, /**< Watchdog higher threshold enabled. */
ADC_THRHLR_LOWER = 2, /**< Watchdog lower threshold enabled. */
ADC_THRHLR_BOTH_HL = 3, /**< Watchdog higher and lower thresholds enabled.*/
} adcthrhlr_t;
/**
* @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 ADC threshold structure.
*/
typedef struct {
/**
* @brief ADC channel watchdog mode selection.
*/
adcthrhlr_t threshold_mode;
/**
* @brief ADC channel selection.
*/
uint16_t adc_ch;
/**
* @brief ADC channel high threshold value.
*/
uint16_t high_threshold_value;
/**
* @brief ADC channel low threshold value.
*/
uint16_t low_threshold_value;
} ADCThrhlr;
/**
* @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 Threshold configuration data.
*/
#if SPC5_ADC_NTRESHOLD == 4
ADCThrhlr thresholds[4];
#elif SPC5_ADC_NTRESHOLD == 16
ADCThrhlr thresholds[16];
#endif
/**
* @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;
} ADCConversionGroup;
/**
* @brief Driver configuration structure.
* @note Empty in this implementation can be ignored.
*/
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 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_ */
/** @} */