From 43185af232c8689fb2082b14625fa5abc894eb92 Mon Sep 17 00:00:00 2001 From: Theodore Ateba Date: Wed, 21 Feb 2018 20:43:19 +0000 Subject: AVR: add DAC low level driver for XMEGA128A4U. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@11531 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/AVR/XMEGA/LLD/DACv1/driver.mk | 9 + os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c | 259 ++++++++++++++++++++++++ os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.h | 268 +++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 os/hal/ports/AVR/XMEGA/LLD/DACv1/driver.mk create mode 100644 os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c create mode 100644 os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.h (limited to 'os/hal/ports') diff --git a/os/hal/ports/AVR/XMEGA/LLD/DACv1/driver.mk b/os/hal/ports/AVR/XMEGA/LLD/DACv1/driver.mk new file mode 100644 index 000000000..547b86b98 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/DACv1/driver.mk @@ -0,0 +1,9 @@ +ifeq ($(USE_SMART_BUILD),yes) +ifneq ($(findstring HAL_USE_DAC TRUE,$(HALCONF)),) +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c +endif +else +PLATFORMSRC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c +endif + +PLATFORMINC += $(CHIBIOS)/os/hal/ports/AVR/XMEGA/LLD/DACv1 diff --git a/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c b/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c new file mode 100644 index 000000000..805c9c732 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.c @@ -0,0 +1,259 @@ +/* + ChibiOS - Copyright (C) 2016..2018 Theodore Ateba + + 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_dac_lld.c + * @brief AVR DAC subsystem low level driver source. + * + * @addtogroup DAC + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__) + +/*==========================================================================*/ +/* Driver local definitions. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver exported variables. */ +/*==========================================================================*/ + +/** @brief DAC1 driver identifier.*/ +#if (AVR_DAC_USE_DAC1 == TRUE) || defined(__DOXYGEN__) +DACDriver DACD1; +#endif + +/*==========================================================================*/ +/* Driver local variables. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver local functions. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver interrupt handlers. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver exported functions. */ +/*==========================================================================*/ + +/** + * @brief Check if channel data register is empty. + * + * @detail This function return the status of the datt register empty flag + * for the selected channel in the given DAC module. This can be used + * to get the status of the register before writing a new value to it. + * + * @param[in] dac pointer to DAC module register section + * @param[in] channel pelected channel in the DAC module, either CH0 or CH1 + * + * @retval dacStatus True if data register is empty + * @retval dacStatus False if data register is not empty + */ +static bool dac_is_channel_data_empty(DACDriver *dacp) { + + bool dacStatus = (dacp->dacblock->STATUS & + (dacp->config->ch ? DAC_CH1DRE_bm : DAC_CH0DRE_bm)); + return dacStatus; +} + +/** + * @brief Configure the DAC trigger mode. + * + * @param[in] dacp pointer to the DAC driver object + * @param[in] channel channel on wich the trigger must be configure + * @param[in] tm trigger mode to use for the DAC configuration + */ +static void dac_set_trigger_mode(DACDriver *dacp) { + + if (dacp->config->ch == DAC_CHANNEL0) { + if (dacp->config->tm == DAC_TRIG_ON_DATAREG) + dacp->dacblock->CTRLB &= ~(DAC_CH0TRIG_bm); + else + dacp->dacblock->CTRLB |= (DAC_CH0TRIG_bm); + } + else { + if (dacp->config->tm == DAC_TRIG_ON_DATAREG) + dacp->dacblock->CTRLB &= ~(DAC_CH1TRIG_bm); + else + dacp->dacblock->CTRLB |= (DAC_CH1TRIG_bm); + } +} + +/** + * @brief Configure the DAC operation (Single/Dual). + * @note In single channel operation, the DAC conversion block is always + * connected the data register of the channel 0. + * + * @param[in] dacp pointer to the DAC driver object + * @param[in] om dac operation mode + */ +static void dac_set_operation_mode(DACDriver *dacp) { + + if (dacp->config->om == DAC_OPMODE_SINGLE) + dacp->dacblock->CTRLB = (dacp->dacblock->CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_SINGLE_gc; + else + dacp->dacblock->CTRLB = (dacp->dacblock->CTRLB & ~DAC_CHSEL_gm ) | DAC_CHSEL_DUAL_gc; +} + +/** + * @brief Configure the DAC to accept Left or Right ajusted value. + * + * @param[in] dacp pointer to the DAC driver object + * @param[in] da data ajustment. + */ +static void dac_set_ajusted_mode(DACDriver *dacp) { + + dacp->dacblock->CTRLC = (dacp->dacblock->CTRLC & ~(DAC_LEFTADJ_bm)) | + (dacp->config->da ? DAC_LEFTADJ_bm : 0x00); +} + +/** + * @brief Configure the DAC voltage reference. + * + * @param[in] dacp pointer to the DAC driver object + * @param[in] vr voltage reference. + */ +static void dac_set_voltage_ref(DACDriver *dacp) { + + dacp->dacblock->CTRLC = (dacp->dacblock->CTRLC & ~(DAC_REFSEL_gm)) | + dacp->config->vr; +} + +/** + * @brief Low level DAC driver initialization. + * + * @notapi + */ +void dac_lld_init(void) { + +#if AVR_DAC_USE_DAC1 == TRUE + dacObjectInit(&DACD1); +#endif +} + +/** + * @brief Configures and activates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_start(DACDriver *dacp) { + + /* If the driver is in DAC_STOP state then a full initialization is + required. */ + if (dacp->state == DAC_STOP) { + dac_set_trigger_mode(dacp); + dac_set_operation_mode(dacp); + dac_set_ajusted_mode(dacp); + dac_set_voltage_ref(dacp); + + /* Enabling the DAC peripheral. */ +#if AVR_DAC_USE_DAC1 == TRUE + if (&DACD1 == dacp) { + dacp->dacblock->CTRLA |= DAC_ENABLE_bm; + } +#endif + } +} + +/** + * @brief Deactivates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_stop(DACDriver *dacp) { + + /* If in ready state then disables the DAC clock. */ + if (dacp->state == DAC_READY) { + /* Disabling DAC.*/ +#if AVR_DAC_USE_DAC1 == TRUE + if (&DACD1 == dacp) { + dacp->dacblock->CTRLA &= ~DAC_ENABLE_bm; + } +#endif + } +} + +/** + * @brief Outputs a value directly on a DAC channel. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] channel DAC channel number + * @param[in] sample value to be output + * + * @api + */ +void dac_lld_put_channel(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample) { + + if (channel == DAC_CHANNEL0) { + dacp->dacblock->CH0DATA = sample; + } + else { + dacp->dacblock->CH1DATA = sample; + } +} + +/** + * @brief Starts a DAC conversion. + * @details Starts an asynchronous conversion operation. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_start_conversion(DACDriver *dacp) { + + /* FIXME: For the moment just the single mode is implemented. + * FIXME: There is a buffer to output: All sample must be send to the DAC. + */ + while (dacp->depth >= 0) { + if (dac_is_channel_data_empty(dacp)) { + dacp->dacblock->CH0DATA = *dacp->samples; + dacp->depth--; + } + } +} + +/** + * @brief Stops an ongoing conversion. + * @details This function stops the currently ongoing conversion and returns + * the driver in the @p DAC_READY state. If there was no conversion + * being processed then the function does nothing. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @iclass + */ +void dac_lld_stop_conversion(DACDriver *dacp) { + + (void)dacp; + /* TODO: Must be implemented. */ +} + +#endif /* HAL_USE_DAC == TRUE */ + +/** @} */ diff --git a/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.h b/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.h new file mode 100644 index 000000000..5b22991c0 --- /dev/null +++ b/os/hal/ports/AVR/XMEGA/LLD/DACv1/hal_dac_lld.h @@ -0,0 +1,268 @@ +/* + ChibiOS - Copyright (C) 2016..2018 Theodore Ateba + + 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_dac_lld.h + * @brief AVR DAC subsystem low level driver header. + * + * @addtogroup DAC + * @{ + */ + +#ifndef HAL_DAC_LLD_H +#define HAL_DAC_LLD_H + +#if HAL_USE_DAC || defined(__DOXYGEN__) + +/*==========================================================================*/ +/* Driver constants. */ +/*==========================================================================*/ + +/** + * @brief Maximum number of DAC channels per unit. + */ +#define DAC_MAX_CHANNELS 2 + +/*==========================================================================*/ +/* Driver pre-compile time settings. */ +/*==========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief DAC1 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC1 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(AVR_DAC_USE_DAC1) || defined(__DOXYGEN__) +#define AVR_DAC_USE_DAC1 FALSE +#endif +/** @} */ + +/*==========================================================================*/ +/* Derived constants and error checks. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* Driver data structures and types. */ +/*==========================================================================*/ + +/** + * @brief DAC channel identifier. + */ +typedef enum { + DAC_CHANNEL0 = 0, /**< DAC channel 0. */ + DAC_CHANNEL1 = 1, /**< DAC channel 1. */ +} dacchan_t; + +/** + * @brief DAC channel selection. + */ +typedef enum { + DAC_CH_SEL0 = 0, /**< DAC single operation on channel 0. */ + DAC_CH_SEL1 = 1, /**< DAC single operation on channel 1. */ + DAC_CH_DUAL = 2, /**< DAC dual channel opration. */ +} dacchansel_t; + +/** + * @brief DAC voltage reference selection. + */ +typedef enum { + DAC_REFSEL_INT1V = 0, /**< DAC reference is the internal 1V. */ + DAC_REFSEL_AVCC = 1, /**< DAC reference is the analog voltage. */ + DAC_REFSEL_AREFA = 2, /**< DAC reference is the analog voltage on PORTA. */ + DAC_REFSEL_AREFB = 3, /**< DAC reference is the analog voltage on PORTB. */ +} dacrefsel_t; + +/** + * @brief DAC operation mode (Single/Dual). + */ +typedef enum { + DAC_OPMODE_SINGLE = 0, /**< DAC is use in single operation mode. */ + DAC_OPMODE_DUAL = 1, /**< DAC is use in dual operation mode. */ +} dacopmode_t; + +/** + * @brief DAC alignment mode(Right/Left). + */ +typedef enum { + DAC_AJUST_12BIT_RIGHT = 0, /**< DAC right ajusted value. */ + DAC_AJUST_12BIT_LEFT = 1, /**< DAC left ajusted value. */ +} dacajustmode_t; + +/** + * @brief Possible DAC trigger mode during conversion. + */ +typedef enum { + DAC_TRIG_ON_DATAREG = 0, /**< DAC triggered by data register. */ + DAC_TRIG_ON_EVENT = 1, /**< DAC triggered by incoming event system. */ +} dactrigmode_t; + +/** + * @brief Type of a DAC channel index. + */ +typedef uint8_t dacchannel_t; + +/** + * @brief Type of a structure representing an DAC driver. + */ +typedef struct DACDriver DACDriver; + +/** + * @brief Type representing a DAC sample. + */ +typedef uint16_t dacsample_t; + +/** + * @brief Possible DAC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + DAC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + DAC_ERR_UNDERFLOW = 1 /**< DAC overflow condition. */ +} dacerror_t; + +/** + * @brief DAC notification callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + * @param[in] buffer pointer to the next semi-buffer to be filled + * @param[in] n number of buffer rows available starting from @p buffer + * callback + */ +typedef void (*daccallback_t)(DACDriver *dacp, dacsample_t *buffer, size_t n); + +/** + * @brief DAC error callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + * callback + * @param[in] err DAC error code + */ +typedef void (*dacerrorcallback_t)(DACDriver *dacp, dacerror_t err); + +/** + * @brief DAC Conversion group structure. + */ +typedef struct { + /** + * @brief Number of DAC channels. + */ + uint32_t num_channels; + /** + * @brief Operation complete callback or @p NULL. + */ + daccallback_t end_cb; + /** + * @brief Error handling callback or @p NULL. + */ + dacerrorcallback_t error_cb; + /* End of the mandatory fields. */ +} DACConversionGroup; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + dacchan_t ch; /* DAC channel id. */ + dactrigmode_t tm; /* register write/event trigger. */ + dacopmode_t om; /* DAC operation mode. */ + dacajustmode_t da; /* DAC (left/rigth) ajustement. */ + dacrefsel_t vr; /* DAC voltage reference. */ + /* End of the mandatory fields. */ +} DACConfig; + +/** + * @brief Structure representing a DAC driver. + */ +struct DACDriver { + /** + * @brief Driver state. + */ + dacstate_t state; + /** + * @brief Conversion group. + */ + const DACConversionGroup *grpp; + /** + * @brief Samples buffer pointer. + */ + dacsample_t *samples; + /** + * @brief Samples buffer size. + */ + uint16_t depth; + /** + * @brief Current configuration data. + */ + const DACConfig *config; +#if DAC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* DAC_USE_WAIT */ +#if DAC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* DAC_USE_MUTUAL_EXCLUSION */ +#if defined(DAC_DRIVER_EXT_FIELDS) + DAC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields. */ + /** + * @brief Pointer to the DAC module block. + */ + DAC_t *dacblock; +}; + +/*==========================================================================*/ +/* Driver macros. */ +/*==========================================================================*/ + +/*==========================================================================*/ +/* External declarations. */ +/*==========================================================================*/ + +#if AVR_DAC_USE_DAC1 && !defined(__DOXYGEN__) +extern DACDriver DACD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void dac_lld_init(void); + void dac_lld_start(DACDriver *dacp); + void dac_lld_stop(DACDriver *dacp); + void dac_lld_put_channel(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample); + void dac_lld_start_conversion(DACDriver *dacp); + void dac_lld_stop_conversion(DACDriver *dacp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_DAC */ + +#endif /* HAL_DAC_LLD_H */ + +/** @} */ -- cgit v1.2.3