From ffd9d3fd90ffe7f8a7f9d824fa3d9d8b6f33c196 Mon Sep 17 00:00:00 2001 From: marcoveeneman Date: Sun, 2 Jul 2017 17:11:10 +0200 Subject: Initial ADC driver and testhal application for TM4C123x. --- os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.c | 352 ++++++++++++++++++++++++++++++++ os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.h | 230 +++++++++++++++++++++ os/hal/ports/TIVA/LLD/uDMA/tiva_udma.h | 2 +- os/hal/ports/TIVA/TM4C123x/platform.mk | 1 + os/hal/ports/TIVA/TM4C129x/platform.mk | 1 + 5 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.c create mode 100644 os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.h (limited to 'os/hal') diff --git a/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.c b/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.c new file mode 100644 index 0000000..a175664 --- /dev/null +++ b/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.c @@ -0,0 +1,352 @@ +/* + Copyright (C) 2014..2017 Marco Veeneman + + 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 PLATFORM 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 ADC0 driver identifier.*/ +#if TIVA_ADC_USE_ADC0 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/** @brief ADC1 driver identifier.*/ +#if TIVA_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Common IRQ handler. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void serve_interrupt(ADCDriver *adcp) +{ + uint32_t dmachis = HWREG(UDMA_CHIS); + + if (dmachis & (1 << adcp->dmanr)) { + if (adcp->grpp->circular) { + tiva_udma_table_entry_t *pri = &udmaControlTable.primary[adcp->dmanr]; + tiva_udma_table_entry_t *alt = &udmaControlTable.alternate[adcp->dmanr]; + + if ((pri->chctl & UDMA_CHCTL_XFERMODE_M) == UDMA_CHCTL_XFERMODE_STOP) { + if (adcp->depth > 1) { + _adc_isr_half_code(adcp); + } + else { + _adc_isr_full_code(adcp); + } + + /* Reconfigure DMA for new (lower half) transfer */ + pri->chctl = adcp->prictl; + } + else if ((alt->chctl & UDMA_CHCTL_XFERMODE_M) == UDMA_CHCTL_XFERMODE_STOP) { + _adc_isr_full_code(adcp); + + /* Reconfigure DMA for new upper half transfer */ + alt->chctl = adcp->altctl; + } + } + else { + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if TIVA_ADC_USE_ADC0 +/** + * @brief ADC0 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(TIVA_ADC0_SEQ0_HANDLER) +{ + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&ADCD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if TIVA_ADC_USE_ADC1 +/** + * @brief ADC1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(TIVA_ADC1_SEQ0_HANDLER) +{ + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&ADCD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) +{ +#if TIVA_ADC_USE_ADC0 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc = ADC0_BASE; + ADCD1.dmanr = TIVA_ADC_ADC0_SS0_UDMA_CHANNEL; + ADCD1.chnmap = TIVA_ADC_ADC0_SS0_UDMA_MAPPING; +#endif + +#if TIVA_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD2); + ADCD2.adc = ADC1_BASE; + ADCD2.dmanr = TIVA_ADC_ADC1_SS0_UDMA_CHANNEL; + ADCD2.chnmap = TIVA_ADC_ADC1_SS0_UDMA_MAPPING; +#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 TIVA_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + bool b; + b = udmaChannelAllocate(adcp->dmanr); + osalDbgAssert(!b, "channel already allocated"); + + HWREG(SYSCTL_RCGCADC) |= (1 << 0); + + while (!(HWREG(SYSCTL_PRADC) & (1 << 0))) + ; + + /* Only sequencer 0 is supported */ + nvicEnableVector(TIVA_ADC0_SEQ0_NUMBER, TIVA_ADC0_SEQ0_PRIORITY); + } +#endif + +#if TIVA_ADC_USE_ADC1 + if (&ADCD2 == adcp) { + bool b; + b = udmaChannelAllocate(adcp->dmanr); + osalDbgAssert(!b, "channel already allocated"); + + HWREG(SYSCTL_RCGCADC) |= (1 << 1); + + while (!(HWREG(SYSCTL_PRADC) & (1 << 1))) + ; + + /* Only sequencer 0 is supported */ + nvicEnableVector(TIVA_ADC1_SEQ0_NUMBER, TIVA_ADC1_SEQ0_PRIORITY); + } +#endif + + HWREG(UDMA_CHMAP0 + (adcp->dmanr / 8) * 4) |= (adcp->chnmap << (adcp->dmanr % 8)); + } +} + +/** + * @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 TIVA_ADC_USE_ADC0 + if (&ADCD1 == adcp) { + + } +#endif + +#if TIVA_ADC_USE_ADC1 + if (&ADCD2 == adcp) { + + } +#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 adc = adcp->adc; + tiva_udma_table_entry_t *primary = &udmaControlTable.primary[adcp->dmanr]; + tiva_udma_table_entry_t *alternate = &udmaControlTable.alternate[adcp->dmanr]; + + /* Disable sample sequencer 0 */ + HWREG(adc + ADC_O_ACTSS) &= (1 << 0); + + /* Configure the sample sequencer 0 trigger */ + HWREG(adc + ADC_O_EMUX) = adcp->grpp->emux & 0xff; + + /* If pwm is used as trigger, select in which block the pwm generator is + located */ + if (adcp->grpp->emux >= 6 && adcp->grpp->emux <= 9) { + HWREG(adc + ADC_O_TSSEL) = 0; + } + + /* For each sample in the sample sequencer, select the input source */ + HWREG(adc + ADC_O_SSMUX0) = adcp->grpp->ssmux; + + /* Configure the sample control bits */ + HWREG(adc + ADC_O_SSCTL0) = adcp->grpp->ssctl | 0x44444444; /* Enforce IEn bits */ + + /* Primary source endpoint is the same for all transfers */ + primary->srcendp = (void *)(adcp->adc + ADC_O_SSFIFO0); + + /* Configure DMA */ + if ((adcp->grpp->circular) && (adcp->depth > 1)) { + /* Configure DMA in ping-pong mode. + Ping (1st half) is configured in the primary control structure. + Pong (2nd half) is configured in the alternate control structure. */ + + uint32_t ctl; + + /* configure the alternate source endpoint */ + alternate->srcendp = (void *)(adcp->adc + ADC_O_SSFIFO0); + + /* sample buffer is split in half, the upper half is used here */ + primary->dstendp = (void *)(adcp->samples + + (adcp->grpp->num_channels * adcp->depth / 2) - 1); + /* the lower half is used here */ + alternate->dstendp = (void *)(adcp->samples + + (adcp->grpp->num_channels * adcp->depth) - 1); + + ctl = UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_32 | + UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_NONE | + UDMA_CHCTL_ARBSIZE_1 | + UDMA_CHCTL_XFERSIZE(adcp->grpp->num_channels * adcp->depth / 2) | + UDMA_CHCTL_XFERMODE_PINGPONG; + + adcp->prictl = ctl; + adcp->altctl = ctl; + } + else { + /* Configure the DMA in basic mode. + This is used for both circular buffers with a depth of 1 and linear + buffers.*/ + primary->dstendp = (void *)(adcp->samples + + (adcp->grpp->num_channels * adcp->depth) - 1); + adcp->prictl = UDMA_CHCTL_DSTSIZE_32 | UDMA_CHCTL_DSTINC_32 | + UDMA_CHCTL_SRCSIZE_32 | UDMA_CHCTL_SRCINC_NONE | + UDMA_CHCTL_ARBSIZE_1 | + UDMA_CHCTL_XFERSIZE(adcp->grpp->num_channels * adcp->depth) | + UDMA_CHCTL_XFERMODE_BASIC; + adcp->altctl = UDMA_CHCTL_XFERMODE_STOP; + } + + /* Configure primary and alternate channel control fields */ + primary->chctl = adcp->prictl; + alternate->chctl = adcp->altctl; + + /* Configure DMA channel */ + dmaChannelPrimary(adcp->dmanr); + dmaChannelBurstOnly(adcp->dmanr); + dmaChannelPriorityDefault(adcp->dmanr); + dmaChannelEnableRequest(adcp->dmanr); + + /* Enable DMA channel */ + dmaChannelEnable(adcp->dmanr); + + /* Enable the sample sequencer */ + HWREG(adc + ADC_O_ACTSS) |= (1 << 0); + + /* Enable DMA on the sample sequencer, is this for 129x only?*/ + //HWREG(adc + ADC_O_ACTSS) |= (1 << 8); + + /* Start conversion if configured for CPU trigger */ + if ((adcp->grpp->emux & 0xff) == 0) { + HWREG(adc + ADC_O_PSSI) = ADC_PSSI_SS0; + } +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) +{ + uint32_t adc = adcp->adc; + + /* Stop ongoing DMA transfer */ + dmaChannelDisable(adcp->dmanr); + + /* Stop ongoing ADC conversion by disabling the active sample sequencer */ + HWREG(adc + ADC_O_ACTSS) &= ~(1 << 0); +} + +#endif /* HAL_USE_ADC == TRUE */ + +/** @} */ diff --git a/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.h b/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.h new file mode 100644 index 0000000..81916b8 --- /dev/null +++ b/os/hal/ports/TIVA/LLD/ADC/hal_adc_lld.h @@ -0,0 +1,230 @@ +/* + Copyright (C) 2014..2017 Marco Veeneman + + 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 PLATFORM 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 PLATFORM 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(PLATFORM_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define PLATFORM_ADC_USE_ADC1 FALSE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !defined(TIVA_UDMA_REQUIRED) +#define TIVA_UDMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint32_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_OVERFLOW = 1, /**< ADC overflow condition. */ + ADC_ERR_AWD = 2 /**< Analog watchdog triggered. */ +} 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 The use of this configuration structure requires knowledge of + * PLATFORM ADC cell registers interface, please refer to the PLATFORM + * 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; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + uint32_t emux; + uint32_t ssmux; + uint32_t ssctl; +} 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 == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if (ADC_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADC registers block. + */ + uint32_t adc; + uint8_t dmanr; + uint8_t chnmap; + uint32_t prictl; + uint32_t altctl; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if TIVA_ADC_USE_ADC0 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#if TIVA_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 == TRUE */ + +#endif /* HAL_ADC_LLD_H */ + +/** @} */ diff --git a/os/hal/ports/TIVA/LLD/uDMA/tiva_udma.h b/os/hal/ports/TIVA/LLD/uDMA/tiva_udma.h index cf90399..a473f6c 100644 --- a/os/hal/ports/TIVA/LLD/uDMA/tiva_udma.h +++ b/os/hal/ports/TIVA/LLD/uDMA/tiva_udma.h @@ -32,7 +32,7 @@ /** * @brief CHCTL XFERSIZE helper. */ -#define UDMA_CHCTL_XFERSIZE(n) ((n-1) << 4) +#define UDMA_CHCTL_XFERSIZE(n) (((n)-1) << 4) /*===========================================================================*/ /* Driver pre-compile time settings. */ diff --git a/os/hal/ports/TIVA/TM4C123x/platform.mk b/os/hal/ports/TIVA/TM4C123x/platform.mk index 2544696..de482d0 100644 --- a/os/hal/ports/TIVA/TM4C123x/platform.mk +++ b/os/hal/ports/TIVA/TM4C123x/platform.mk @@ -11,6 +11,7 @@ HALCONF := $(strip $(shell cat halconf.h | egrep -e "\#define")) endif # Drivers compatible with the platform. +include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/ADC/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/GPIO/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/GPTM/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/I2C/driver.mk diff --git a/os/hal/ports/TIVA/TM4C129x/platform.mk b/os/hal/ports/TIVA/TM4C129x/platform.mk index fb2dc4a..8e4c9fa 100644 --- a/os/hal/ports/TIVA/TM4C129x/platform.mk +++ b/os/hal/ports/TIVA/TM4C129x/platform.mk @@ -11,6 +11,7 @@ HALCONF := $(strip $(shell cat halconf.h | egrep -e "\#define")) endif # Drivers compatible with the platform. +include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/ADC/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/GPIO/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/GPTM/driver.mk include $(CHIBIOS_CONTRIB)/os/hal/ports/TIVA/LLD/I2C/driver.mk -- cgit v1.2.3