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
This commit is contained in:
Theodore Ateba 2018-02-21 20:43:19 +00:00
parent d59ad8c78d
commit 43185af232
3 changed files with 536 additions and 0 deletions

View File

@ -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

View File

@ -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 */
/** @} */

View File

@ -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 */
/** @} */