aboutsummaryrefslogtreecommitdiffstats
path: root/os/hal/platforms/LPC17xx/adc_lld.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/hal/platforms/LPC17xx/adc_lld.c')
-rw-r--r--os/hal/platforms/LPC17xx/adc_lld.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/os/hal/platforms/LPC17xx/adc_lld.c b/os/hal/platforms/LPC17xx/adc_lld.c
new file mode 100644
index 000000000..2363dd12f
--- /dev/null
+++ b/os/hal/platforms/LPC17xx/adc_lld.c
@@ -0,0 +1,284 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
+ LPC17xx ADC driver - Copyright (C) 2013 Marcin Jokel
+
+ 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 LPC17xx/adc_lld.c
+ * @brief LPC17xx ADC subsystem low level driver source.
+ * @note Values in samples buffer are from DR register.
+ * To get ADC values make conversion (DR >> 6) & 0x03FF.
+ * @addtogroup ADC
+ * @{
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#if HAL_USE_ADC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief ADC1 driver identifier.*/
+ADCDriver ADCD1;
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+#if LPC17xx_ADC_USE_DMA
+/**
+ * @brief Common IRQ handler.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ */
+static void adc_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) {
+ (void) flags;
+
+ adcp->num++;
+ if (adcp->num == 1) {
+ dmaChannelSrcAddr(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.srcaddr);
+ dmaChannelDstAddr(LPC17xx_ADC_DMA_CHANNEL, \
+ &adcp->samples[adcp->nsamples/2]);
+ dmaChannelControl(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.control);
+ dmaChannelConfig(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.config);
+ _adc_isr_half_code(adcp);
+
+ }
+ else {
+ adcp->num = 0;
+ if (adcp->grpp->circular == TRUE) {
+ dmaChannelSrcAddr(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.srcaddr);
+ dmaChannelDstAddr(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.dstaddr);
+ dmaChannelControl(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.control);
+ dmaChannelConfig(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.config);
+ }
+ _adc_isr_full_code(adcp);
+ }
+}
+#endif
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/**
+ * @brief ADC interrupt handler.
+ *
+ * @isr
+ */
+CH_IRQ_HANDLER(Vector98) {
+ uint32_t status;
+ uint32_t n;
+ uint8_t i;
+
+ CH_IRQ_PROLOGUE();
+ status = LPC_ADC->STAT;
+
+ n = ADCD1.num;
+
+ /* Note, an overrun may occur only in burst mode, if one or more
+ conversions was (were) lost. */
+ if ((status & ADC0STAT_OVERRUN_MASK)) {
+ if (ADCD1.grpp != NULL)
+ _adc_isr_error_code(&ADCD1, ADC_ERR_OVERRUN);
+ }
+ else {
+
+ status = status & ADC0STAT_DONE_MASK;
+ for (i = 0; i < ADC_MAX_CHANNELS; i++) {
+ if (status & (0x01 << i)) {
+ ADCD1.samples[n] = LPC_ADC->DR[i];
+ n++;
+ }
+ }
+
+ if (n == (ADCD1.nsamples / 2)) {
+ _adc_isr_half_code(&ADCD1);
+ }
+
+ if (n == ADCD1.nsamples) {
+ n = 0;
+ _adc_isr_full_code(&ADCD1);
+ }
+ }
+ ADCD1.num = n;
+ CH_IRQ_EPILOGUE();
+}
+
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level ADC driver initialization.
+ *
+ * @notapi
+ */
+void adc_lld_init(void) {
+
+ /* Driver initialization.*/
+ adcObjectInit(&ADCD1);
+ ADCD1.adc = LPC_ADC;
+
+#if LPC17xx_ADC_USE_DMA
+ nvicDisableVector(ADC_IRQn);
+#else
+ nvicEnableVector(ADC_IRQn, CORTEX_PRIORITY_MASK(LPC17xx_ADC_IRQ_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 in stopped state then enables the ADC. */
+ if (adcp->state == ADC_STOP) {
+ LPC_SC->PCONP |= (1UL << 12); /* Enable ADC power */
+
+#if LPC17xx_ADC_USE_DMA
+ dmaChannelAllocate(LPC17xx_ADC_DMA_CHANNEL, \
+ (lpc17xx_dmaisr_t)adc_serve_dma_interrupt, \
+ (void *)adcp);
+#endif
+ }
+}
+
+/**
+ * @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.*/
+ if (adcp->state == ADC_READY) {
+ adcp->adc->CR = 0; /* Clear PDN bit */
+ LPC_SC->PCONP &= ~(1UL << 12); /* Disable ADC power */
+
+#if LPC17xx_ADC_USE_DMA
+ dmaChannelRelease(LPC17xx_ADC_DMA_CHANNEL);
+#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 dummy;
+ uint32_t cr;
+ uint8_t i;
+#if LPC17xx_ADC_USE_DMA
+ uint8_t ch;
+#endif
+
+ cr = adcp->grpp->cr0;
+ adcp->num = 0;
+ adcp->nsamples = adcp->depth * adcp->grpp->num_channels;
+
+ for (i = 0; i < ADC_MAX_CHANNELS; i++) {
+ dummy = adcp->adc->DR[i]; /* Clear all DONE and OVERRUN flags. */
+ }
+
+#if LPC17xx_ADC_USE_DMA
+ ch = 0;
+ for (i = 0; i < 8; i++) {
+ if (cr & (1UL << i)) {
+ ch = i; /* Get enabled channel number */
+ break;
+ }
+ }
+
+ /* DMA configuration */
+ adcp->adc_dma_cfg.srcaddr = (uint32_t)&adcp->adc->DR[ch];
+ adcp->adc_dma_cfg.dstaddr = (uint32_t)&adcp->samples[0];
+ adcp->adc_dma_cfg.lli = 0;
+ adcp->adc_dma_cfg.control =
+ DMA_CTRL_TRANSFER_SIZE(adcp->nsamples/2) |
+ DMA_CTRL_SRC_BSIZE_1 |
+ DMA_CTRL_DST_BSIZE_1 |
+ DMA_CTRL_SRC_WIDTH_WORD |
+ DMA_CTRL_DST_WIDTH_WORD |
+ DMA_CTRL_SRC_NOINC |
+ DMA_CTRL_DST_INC |
+ DMA_CTRL_PROT1_USER |
+ DMA_CTRL_PROT2_NONBUFF |
+ DMA_CTRL_PROT3_NONCACHE |
+ DMA_CTRL_INT;
+
+ adcp->adc_dma_cfg.config =
+ DMA_CFG_CH_ENABLE |
+ DMA_CFG_SRC_PERIPH(DMA_ADC) |
+ DMA_CFG_TTYPE_P2M |
+ DMA_CFG_IE |
+ DMA_CFG_ITC;
+
+ dmaChannelSrcAddr(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.srcaddr);
+ dmaChannelDstAddr(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.dstaddr);
+ dmaChannelControl(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.control);
+ dmaChannelConfig(LPC17xx_ADC_DMA_CHANNEL, adcp->adc_dma_cfg.config);
+#endif
+
+ /* ADC configuration and conversion start. */
+ adcp->adc->INTEN = adcp->grpp->inten; /* Set ADC interrupt on selected channels */
+ adcp->adc->CR = (cr & 0x0000FFFF) | ((LPC17xx_ADC_CLKDIV - 1) << 8) | AD0CR_PDN;
+ adcp->adc->CR |= cr & 0xFFFF0000;
+
+}
+
+/**
+ * @brief Stops an ongoing conversion.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_stop_conversion(ADCDriver *adcp) {
+
+#if LPC17xx_ADC_USE_DMA
+ dmaChannelDisable(LPC17xx_ADC_DMA_CHANNEL);
+#endif
+ adcp->adc->CR &= ~(AD0CR_MODE_BURST | AD0CR_START_MASK); /* Disable ADC conversion. */
+ adcp->adc->INTEN = 0; /* Mask ADC interrupts. */
+}
+
+
+#endif /* HAL_USE_ADC */
+
+/** @} */