From 16fc0455097487428c4a1b5759a18d470ebb6cc2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Sat, 27 Aug 2016 15:04:34 -0700 Subject: [PATCH] atmel-samd: Add direct write access to the DAC output. --- atmel-samd/Makefile | 6 +- atmel-samd/asf/sam0/drivers/dac/dac.h | 207 +++ .../asf/sam0/drivers/dac/dac_callback.h | 119 ++ .../asf/sam0/drivers/dac/dac_sam_d_c/dac.c | 779 ++++++++++ .../drivers/dac/dac_sam_d_c/dac_callback.c | 435 ++++++ .../drivers/dac/dac_sam_d_c/dac_feature.h | 634 ++++++++ .../drivers/dac/docimg/dac_block_diagram.svg | 218 +++ .../dac/docimg/dac_block_diagram_saml.svg | 1303 +++++++++++++++++ atmel-samd/modmachine.c | 2 + atmel-samd/mpconfigport.h | 2 + atmel-samd/mpdac.c | 152 ++ atmel-samd/mpdac.h | 27 + 12 files changed, 3883 insertions(+), 1 deletion(-) create mode 100644 atmel-samd/asf/sam0/drivers/dac/dac.h create mode 100644 atmel-samd/asf/sam0/drivers/dac/dac_callback.h create mode 100644 atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac.c create mode 100644 atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_callback.c create mode 100644 atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_feature.h create mode 100644 atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram.svg create mode 100644 atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram_saml.svg create mode 100644 atmel-samd/mpdac.c create mode 100644 atmel-samd/mpdac.h diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 6003c519cd..ef363ef43d 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -48,6 +48,8 @@ INC += $(addprefix -Iasf/sam0/,\ drivers/port \ drivers/system \ drivers/adc/adc_sam_d_r \ + drivers/dac \ + drivers/dac/dac_sam_d_c \ drivers/system/clock \ drivers/system/clock/clock_samd21_r21_da \ drivers/system/interrupt \ @@ -93,12 +95,13 @@ endif SRC_ASF = $(addprefix asf/sam0/,\ + drivers/adc/adc_sam_d_r/adc.c \ + drivers/dac/dac_sam_d_c/dac.c \ drivers/port/port.c \ drivers/sercom/sercom.c \ drivers/sercom/sercom_interrupt.c \ drivers/sercom/usart/usart.c \ drivers/sercom/usart/usart_interrupt.c \ - drivers/adc/adc_sam_d_r/adc.c \ drivers/system/clock/clock_samd21_r21_da/clock.c \ drivers/system/clock/clock_samd21_r21_da/gclk.c \ drivers/system/interrupt/system_interrupt.c \ @@ -114,6 +117,7 @@ SRC_C = \ main.c \ modmachine.c \ modutime.c \ + mpdac.c \ mphalport.c \ pin.c \ pin_named_pins.c \ diff --git a/atmel-samd/asf/sam0/drivers/dac/dac.h b/atmel-samd/asf/sam0/drivers/dac/dac.h new file mode 100644 index 0000000000..f26e08e5a0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/dac.h @@ -0,0 +1,207 @@ +/** + * \file + * + * \brief SAM Peripheral Digital-to-Analog Converter Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DAC_H_INCLUDED +#define DAC_H_INCLUDED + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * \addtogroup asfdoc_sam0_dac_group + * + * @{ + */ + +/** + * Define DAC features set according to different device families. + * @{ + */ +#if (SAMD21 || SAMD10 || SAMD11 || SAMDA1) +# define FEATURE_DAC_DATABUF_WRITE_PROTECTION +#endif +/**@}*/ + +#ifndef DAC_TIMEOUT +# define DAC_TIMEOUT 0xFFFF +#endif + +#if DAC_CALLBACK_MODE == true +# include + +/** Forward definition of the device instance. */ +struct dac_module; + +#if !defined(__DOXYGEN__) +extern struct dac_module *_dac_instances[DAC_INST_NUM]; +#endif + +/** Type definition for a DAC module callback function. */ +typedef void (*dac_callback_t)(uint8_t channel); + +/** Enum for the possible callback types for the DAC module. */ +enum dac_callback { + /** Callback type for when a DAC channel data empty condition occurs + * (requires event triggered mode) */ + DAC_CALLBACK_DATA_EMPTY, + + /** Callback type for when a DAC channel data underrun condition occurs + * (requires event triggered mode) */ + DAC_CALLBACK_DATA_UNDERRUN, + + /** Callback type for when a DAC channel write buffer job complete (requires + * event triggered mode) */ + DAC_CALLBACK_TRANSFER_COMPLETE, +#if !defined(__DOXYGEN__) + DAC_CALLBACK_N, +#endif +}; + +#endif + +#include + +/** + * \name Configuration and Initialization + * @{ + */ + +bool dac_is_syncing( + struct dac_module *const dev_inst); + +void dac_get_config_defaults( + struct dac_config *const config); + +enum status_code dac_init( + struct dac_module *const dev_inst, + Dac *const module, + struct dac_config *const config); + +void dac_reset( + struct dac_module *const dev_inst); + +void dac_enable( + struct dac_module *const dev_inst); + +void dac_disable( + struct dac_module *const dev_inst); + +void dac_enable_events( + struct dac_module *const module_inst, + struct dac_events *const events); + +void dac_disable_events( + struct dac_module *const module_inst, + struct dac_events *const events); + +/** @} */ + +/** + * \name Configuration and Initialization (Channel) + * @{ + */ + +void dac_chan_get_config_defaults( + struct dac_chan_config *const config); + +void dac_chan_set_config( + struct dac_module *const dev_inst, + const enum dac_channel channel, + struct dac_chan_config *const config); + +void dac_chan_enable( + struct dac_module *const dev_inst, + enum dac_channel channel); + +void dac_chan_disable( + struct dac_module *const dev_inst, + enum dac_channel channel); + +/** @} */ + +/** + * \name Channel Data Management + * @{ + */ + +enum status_code dac_chan_write( + struct dac_module *const dev_inst, + enum dac_channel channel, + const uint16_t data); + +enum status_code dac_chan_write_buffer_wait( + struct dac_module *const module_inst, + enum dac_channel channel, + uint16_t *buffer, + uint32_t length); + +/** @} */ + +/** + * \name Status Management + * @{ + */ +uint32_t dac_get_status( + struct dac_module *const module_inst); +void dac_clear_status( + struct dac_module *const module_inst, + uint32_t status_flags); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + + +#endif /* DAC_H_INCLUDED */ diff --git a/atmel-samd/asf/sam0/drivers/dac/dac_callback.h b/atmel-samd/asf/sam0/drivers/dac/dac_callback.h new file mode 100644 index 0000000000..0de73056b0 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/dac_callback.h @@ -0,0 +1,119 @@ +/** + * \file + * + * \brief SAM Digital-to-Analog Interrupt Driver + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DAC_CALLBACK_H_INCLUDED +#define DAC_CALLBACK_H_INCLUDED + +#include +#include "dac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup asfdoc_sam0_dac_group + * + * @{ + */ + +/** \name Callback Configuration and Initialization + * @{ + */ +enum status_code dac_chan_write_buffer_job( + struct dac_module *const module_inst, + const enum dac_channel channel, + uint16_t *buffer, + uint32_t buffer_size); + +enum status_code dac_chan_write_job( + struct dac_module *const module_inst, + const enum dac_channel channel, + uint16_t data); + +enum status_code dac_register_callback( + struct dac_module *const module, + const enum dac_channel channel, + const dac_callback_t callback, + const enum dac_callback type); + +enum status_code dac_unregister_callback( + struct dac_module *const module, + const enum dac_channel channel, + const enum dac_callback type); + +/** @} */ + +/** \name Callback Enabling and Disabling (Channel) + * @{ + */ + +enum status_code dac_chan_enable_callback( + struct dac_module *const module, + const enum dac_channel channel, + const enum dac_callback type); + +enum status_code dac_chan_disable_callback( + struct dac_module *const module, + const enum dac_channel channel, + const enum dac_callback type); + +enum status_code dac_chan_get_job_status( + struct dac_module *module_inst, + const enum dac_channel channel); + +void dac_chan_abort_job( + struct dac_module *module_inst, + const enum dac_channel channel); + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac.c b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac.c new file mode 100644 index 0000000000..aa73badd3a --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac.c @@ -0,0 +1,779 @@ +/** + * \file + * + * \brief SAM Peripheral Digital-to-Analog Converter Driver + * + * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#include "dac.h" +#include +#include + +/** + * \internal Writes a DAC configuration to the hardware module. + * + * Writes out a given configuration to the hardware module. + * + * \param[out] module_inst Pointer to the DAC software instance struct + * \param[in] config Pointer to the configuration struct + * + */ +static void _dac_set_config( + struct dac_module *const module_inst, + struct dac_config *const config) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(config); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + /* Set selected DAC output to be enabled when enabling the module */ + module_inst->output = config->output; + module_inst->start_on_event = false; + + uint32_t new_ctrla = 0; + uint32_t new_ctrlb = 0; + + /* Enable DAC in standby sleep mode if configured */ + if (config->run_in_standby) { + new_ctrla |= DAC_CTRLA_RUNSTDBY; + } + + /* Set reference voltage */ + new_ctrlb |= config->reference; + + /* Left adjust data if configured */ + if (config->left_adjust) { + new_ctrlb |= DAC_CTRLB_LEFTADJ; + } + +#ifdef FEATURE_DAC_DATABUF_WRITE_PROTECTION + /* Bypass DATABUF write protection if configured */ + if (config->databuf_protection_bypass) { + new_ctrlb |= DAC_CTRLB_BDWP; + } +#endif + + /* Voltage pump disable if configured */ + if (config->voltage_pump_disable) { + new_ctrlb |= DAC_CTRLB_VPD; + } + + /* Apply the new configuration to the hardware module */ + dac_module->CTRLA.reg = new_ctrla; + + while (dac_is_syncing(module_inst)) { + /* Wait until the synchronization is complete */ + } + + dac_module->CTRLB.reg = new_ctrlb; +} + +/** + * \brief Determines if the hardware module(s) are currently synchronizing to the bus. + * + * Checks to see if the underlying hardware peripheral module(s) are currently + * synchronizing across multiple clock domains to the hardware bus. This + * function can be used to delay further operations on a module until such time + * that it is ready, to prevent blocking delays for synchronization in the + * user application. + * + * \param[in] dev_inst Pointer to the DAC software instance struct + * + * \return Synchronization status of the underlying hardware module(s). + * + * \retval true If the module synchronization is ongoing + * \retval false If the module has completed synchronization + */ +bool dac_is_syncing( + struct dac_module *const dev_inst) +{ + /* Sanity check arguments */ + Assert(dev_inst); + + Dac *const dac_module = dev_inst->hw; + +#if (SAMC21) + if (dac_module->SYNCBUSY.reg) { +#else + if (dac_module->STATUS.reg & DAC_STATUS_SYNCBUSY) { +#endif + return true; + } + + return false; +} + +/** + * \brief Initializes a DAC configuration structure to defaults. + * + * Initializes a given DAC configuration structure to a set of + * known default values. This function should be called on any new + * instance of the configuration structures before being modified by the + * user application. + * + * The default configuration is as follows: + * \li 1V from internal bandgap reference + * \li Drive the DAC output to the VOUT pin + * \li Right adjust data + * \li GCLK generator 0 (GCLK main) clock source + * \li The output buffer is disabled when the chip enters STANDBY sleep + * mode + * + * \param[out] config Configuration structure to initialize to default values + */ +void dac_get_config_defaults( + struct dac_config *const config) +{ + /* Sanity check arguments */ + Assert(config); + + /* Default configuration values */ + config->reference = DAC_REFERENCE_INT1V; + config->output = DAC_OUTPUT_EXTERNAL; + config->left_adjust = false; +#ifdef FEATURE_DAC_DATABUF_WRITE_PROTECTION + config->databuf_protection_bypass = false; +#endif + config->voltage_pump_disable = false; + config->clock_source = GCLK_GENERATOR_0; + config->run_in_standby = false; +#if (SAMC21) + config->dither_mode = false; +#endif +} + +/** + * \brief Initialize the DAC device struct. + * + * Use this function to initialize the Digital to Analog Converter. Resets the + * underlying hardware module and configures it. + * + * \note The DAC channel must be configured separately. + * + * \param[out] module_inst Pointer to the DAC software instance struct + * \param[in] module Pointer to the DAC module instance + * \param[in] config Pointer to the config struct, created by the user + * application + * + * \return Status of initialization. + * \retval STATUS_OK Module initiated correctly + * \retval STATUS_ERR_DENIED If module is enabled + * \retval STATUS_BUSY If module is busy resetting + */ +enum status_code dac_init( + struct dac_module *const module_inst, + Dac *const module, + struct dac_config *const config) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module); + Assert(config); + + /* Initialize device instance */ + module_inst->hw = module; + + /* Turn on the digital interface clock */ +#if (SAMC21) + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, MCLK_APBCMASK_DAC); +#else + system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_DAC); +#endif + + /* Check if module is enabled. */ + if (module->CTRLA.reg & DAC_CTRLA_ENABLE) { + return STATUS_ERR_DENIED; + } + + /* Check if reset is in progress. */ + if (module->CTRLA.reg & DAC_CTRLA_SWRST) { + return STATUS_BUSY; + } + + /* Configure GCLK channel and enable clock */ + struct system_gclk_chan_config gclk_chan_conf; + system_gclk_chan_get_config_defaults(&gclk_chan_conf); + gclk_chan_conf.source_generator = config->clock_source; + system_gclk_chan_set_config(DAC_GCLK_ID, &gclk_chan_conf); + system_gclk_chan_enable(DAC_GCLK_ID); + + /* MUX the DAC VOUT pin */ + struct system_pinmux_config pin_conf; + system_pinmux_get_config_defaults(&pin_conf); + + /* Set up the DAC VOUT pin */ + pin_conf.mux_position = MUX_PA02B_DAC_VOUT; + pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; + pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; + system_pinmux_pin_set_config(PIN_PA02B_DAC_VOUT, &pin_conf); + + /* Write configuration to module */ + _dac_set_config(module_inst, config); + + /* Store reference selection for later use */ + module_inst->reference = config->reference; + +#if DAC_CALLBACK_MODE == true + for (uint8_t i = 0; i < DAC_CALLBACK_N; i++) { + module_inst->callback[i] = NULL; + }; + + _dac_instances[0] = module_inst; +#endif + + return STATUS_OK; +} + +/** + * \brief Resets the DAC module. + * + * This function will reset the DAC module to its power on default values and + * disable it. + * + * \param[in] module_inst Pointer to the DAC software instance struct + */ +void dac_reset( + struct dac_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + while (dac_is_syncing(module_inst)) { + /* Wait until the synchronization is complete */ + } + + /* Software reset the module */ + dac_module->CTRLA.reg |= DAC_CTRLA_SWRST; +} + +/** + * \brief Enable the DAC module. + * + * Enables the DAC interface and the selected output. If any internal reference + * is selected it will be enabled. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * + */ +void dac_enable( + struct dac_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + /* Enable selected output */ + dac_module->CTRLB.reg |= module_inst->output; + + while (dac_is_syncing(module_inst)) { + /* Wait until the synchronization is complete */ + } + + /* Enable the module */ + dac_module->CTRLA.reg |= DAC_CTRLA_ENABLE; + + /* Enable internal bandgap reference if selected in the configuration */ + if (module_inst->reference == DAC_REFERENCE_INT1V) { +#if (SAMC21) + system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_OUTPUT); + } + + if(dac_module->CTRLA.reg & DAC_CTRLA_ENABLE) { + while(! (dac_module->STATUS.reg & DAC_STATUS_READY)) { + }; + } +#else + system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_BANDGAP); + } + +#endif +} + +/** + * \brief Disable the DAC module. + * + * Disables the DAC interface and the output buffer. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * + */ +void dac_disable( + struct dac_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + /* Wait until the synchronization is complete */ + while (dac_is_syncing(module_inst)) { + }; + + /* Disbale interrupt */ + dac_module->INTENCLR.reg = DAC_INTENCLR_MASK; + /* Clear interrupt flag */ + dac_module->INTFLAG.reg = DAC_INTFLAG_MASK; + + /* Disable DAC */ + dac_module->CTRLA.reg &= ~DAC_CTRLA_ENABLE; +} + +/** + * \brief Enables a DAC event input or output. + * + * Enables one or more input or output events to or from the DAC module. See + * \ref dac_events "dac_events" for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Software instance for the DAC peripheral + * \param[in] events Struct containing flags of events to enable + */ +void dac_enable_events( + struct dac_module *const module_inst, + struct dac_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Dac *const dac_module = module_inst->hw; + + uint32_t event_mask = 0; + +#if(SAMC21) + /* Configure Enable Inversion of input event */ + if (events->generate_event_on_chan_falling_edge) { + event_mask |= DAC_EVCTRL_INVEI; + } +#endif + + /* Configure Buffer Empty event */ + if (events->generate_event_on_buffer_empty) { + event_mask |= DAC_EVCTRL_EMPTYEO; + } + + /* Configure Conversion Start event */ + if (events->on_event_start_conversion) { + event_mask |= DAC_EVCTRL_STARTEI; + module_inst->start_on_event = true; + } + + dac_module->EVCTRL.reg |= event_mask; +} + +/** + * \brief Disables a DAC event input or output. + * + * Disables one or more input or output events to or from the DAC module. See + * \ref dac_events "dac_events" for a list of events this module supports. + * + * \note Events cannot be altered while the module is enabled. + * + * \param[in] module_inst Software instance for the DAC peripheral + * \param[in] events Struct containing flags of events to disable + */ +void dac_disable_events( + struct dac_module *const module_inst, + struct dac_events *const events) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(events); + + Dac *const dac_module = module_inst->hw; + + uint32_t event_mask = 0; + + /* Configure Buffer Empty event */ + if (events->generate_event_on_buffer_empty) { + event_mask |= DAC_EVCTRL_EMPTYEO; + } + + /* Configure Conversion Start event */ + if (events->on_event_start_conversion) { + event_mask |= DAC_EVCTRL_STARTEI; + module_inst->start_on_event = false; + } + + dac_module->EVCTRL.reg &= ~event_mask; +} + +/** + * \brief Initializes a DAC channel configuration structure to defaults. + * + * Initializes a given DAC channel configuration structure to a set of + * known default values. This function should be called on any new + * instance of the configuration structures before being modified by the + * user application. + * + * The default configuration is as follows: + * \li Start Conversion Event Input enabled + * \li Start Data Buffer Empty Event Output disabled + * + * \param[out] config Configuration structure to initialize to default values + */ +void dac_chan_get_config_defaults( + struct dac_chan_config *const config) +{ + /* Sanity check arguments */ + Assert(config); +} + +/** + * \brief Writes a DAC channel configuration to the hardware module. + * + * Writes a given channel configuration to the hardware module. + * + * \note The DAC device instance structure must be initialized before calling + * this function. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel Channel to configure + * \param[in] config Pointer to the configuration struct + * + */ +void dac_chan_set_config( + struct dac_module *const module_inst, + const enum dac_channel channel, + struct dac_chan_config *const config) +{ + /* No channel support yet */ + UNUSED(channel); +} + +/** + * \brief Enable a DAC channel. + * + * Enables the selected DAC channel. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel Channel to enable + * + */ +void dac_chan_enable( + struct dac_module *const module_inst, + enum dac_channel channel) +{ + /* No channel support yet */ + UNUSED(channel); +} + +/** + * \brief Disable a DAC channel. + * + * Disables the selected DAC channel. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel Channel to disable + * + */ +void dac_chan_disable( + struct dac_module *const module_inst, + enum dac_channel channel) +{ + /* No channel support yet */ + UNUSED(channel); +} + +/** + * \brief Enable the output buffer. + * + * Enables the output buffer and drives the DAC output to the VOUT pin. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel DAC channel to alter + */ +void dac_chan_enable_output_buffer( + struct dac_module *const module_inst, + enum dac_channel channel) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* No channel support yet */ + UNUSED(channel); + + Dac *const dac_module = module_inst->hw; + + /* Enable output buffer */ + dac_module->CTRLB.reg |= DAC_OUTPUT_EXTERNAL; +} + +/** + * \brief Disable the output buffer. + * + * Disables the output buffer. + * + * \note The output buffer(s) should be disabled when a channel's output is not + * currently needed, as it will draw current even if the system is in + * sleep mode. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel DAC channel to alter + */ +void dac_chan_disable_output_buffer( + struct dac_module *const module_inst, + enum dac_channel channel) +{ + /* Sanity check arguments*/ + Assert(module_inst); + Assert(module_inst->hw); + + /* No channel support yet */ + UNUSED(channel); + + Dac *const dac_module = module_inst->hw; + + /* Disable output buffer */ + dac_module->CTRLB.reg &= ~DAC_OUTPUT_EXTERNAL; +} + +/** + * \brief Write to the DAC. + * + * This function writes to the DATA or DATABUF register. + * If the conversion is not event-triggered, the data will be written to + * the DATA register and the conversion will start. + * If the conversion is event-triggered, the data will be written to DATABUF + * and transferred to the DATA register and converted when a Start Conversion + * Event is issued. + * Conversion data must be right or left adjusted according to configuration + * settings. + * \note To be event triggered, the enable_start_on_event must be + * enabled in the configuration. + * + * \param[in] module_inst Pointer to the DAC software device struct + * \param[in] channel DAC channel to write to + * \param[in] data Conversion data + * + * \return Status of the operation. + * \retval STATUS_OK If the data was written + */ +enum status_code dac_chan_write( + struct dac_module *const module_inst, + enum dac_channel channel, + const uint16_t data) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* No channel support yet */ + UNUSED(channel); + + Dac *const dac_module = module_inst->hw; + + /* Wait until the synchronization is complete */ + while (dac_is_syncing(module_inst)) { + }; + + if (module_inst->start_on_event) { + /* Write the new value to the buffered DAC data register */ + dac_module->DATABUF.reg = data; + } else { + /* Write the new value to the DAC data register */ + dac_module->DATA.reg = data; + } + + return STATUS_OK; +} + +/** + * \brief Write to the DAC. + * + * This function converts a specific number of digital data. + * The conversion should be event-triggered, the data will be written to DATABUF + * and transferred to the DATA register and converted when a Start Conversion + * Event is issued. + * Conversion data must be right or left adjusted according to configuration + * settings. + * \note To be event triggered, the enable_start_on_event must be + * enabled in the configuration. + * + * \param[in] module_inst Pointer to the DAC software device struct + * \param[in] channel DAC channel to write to + * \param[in] buffer Pointer to the digital data write buffer to be converted + * \param[in] length Length of the write buffer + * + * \return Status of the operation. + * \retval STATUS_OK If the data was written or no data conversion required + * \retval STATUS_ERR_UNSUPPORTED_DEV The DAC is not configured as using event trigger + * \retval STATUS_BUSY The DAC is busy to convert + */ +enum status_code dac_chan_write_buffer_wait( + struct dac_module *const module_inst, + enum dac_channel channel, + uint16_t *buffer, + uint32_t length) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + /* No channel support yet */ + UNUSED(channel); + + Dac *const dac_module = module_inst->hw; + + /* Wait until the synchronization is complete */ + while (dac_is_syncing(module_inst)) { + }; + + /* Zero length request */ + if (length == 0) { + /* No data to be converted */ + return STATUS_OK; + } + +#if DAC_CALLBACK_MODE == true + /* Check if busy */ + if (module_inst->job_status == STATUS_BUSY) { + return STATUS_BUSY; + } +#endif + + /* Only support event triggered conversion */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + /* Blocks while buffer is being transferred */ + while (length--) { + /* Convert one data */ + dac_chan_write(module_inst, channel, buffer[length]); + + /* Wait until Transmit is complete or timeout */ + for (uint32_t i = 0; i <= DAC_TIMEOUT; i++) { + if (dac_module->INTFLAG.reg & DAC_INTFLAG_EMPTY) { + break; + } else if (i == DAC_TIMEOUT) { + return STATUS_ERR_TIMEOUT; + } + } + } + + return STATUS_OK; +} + +/** + * \brief Retrieves the current module status + * + * Checks the status of the module and returns it as a bitmask of status + * flags. + * + * \param[in] module_inst Pointer to the DAC software device struct + * + * \return Bitmask of status flags. + * + * \retval DAC_STATUS_CHANNEL_0_EMPTY Data has been transferred from DATABUF + * to DATA by a start conversion event + * and DATABUF is ready for new data + * \retval DAC_STATUS_CHANNEL_0_UNDERRUN A start conversion event has occurred + * when DATABUF is empty + * + */ +uint32_t dac_get_status( + struct dac_module *const module_inst) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + uint8_t intflags = dac_module->INTFLAG.reg; + uint32_t status_flags = 0; + + if (intflags & DAC_INTFLAG_EMPTY) { + status_flags |= DAC_STATUS_CHANNEL_0_EMPTY; + } + + if (intflags & DAC_INTFLAG_UNDERRUN) { + status_flags |= DAC_STATUS_CHANNEL_0_UNDERRUN; + } + + return status_flags; +} + +/** + * \brief Clears a module status flag + * + * Clears the given status flag of the module. + * + * \param[in] module_inst Pointer to the DAC software device struct + * \param[in] status_flags Bit mask of status flags to clear + * + */ +void dac_clear_status( + struct dac_module *const module_inst, + uint32_t status_flags) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + Dac *const dac_module = module_inst->hw; + + uint32_t intflags = 0; + + if (status_flags & DAC_STATUS_CHANNEL_0_EMPTY) { + intflags |= DAC_INTFLAG_EMPTY; + } + + if (status_flags & DAC_STATUS_CHANNEL_0_UNDERRUN) { + intflags |= DAC_INTFLAG_UNDERRUN; + } + + dac_module->INTFLAG.reg = intflags; +} diff --git a/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_callback.c b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_callback.c new file mode 100644 index 0000000000..d49fc09cbf --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_callback.c @@ -0,0 +1,435 @@ +/** + * \file + * + * \brief SAM Digital-to-Analog Interrupt Driver + * + * Copyright (C) 2013-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#include "dac.h" +#include "dac_callback.h" + +struct dac_module *_dac_instances[DAC_INST_NUM]; + +/** + * \brief Convert a specific number digital data to analog through DAC. + * + * This function will perform a conversion of specific number of digital data. + * The conversion should be event-triggered, the data will be written to DATABUF + * and transferred to the DATA register and converted when a Start Conversion + * Event is issued. + * Conversion data must be right or left adjusted according to configuration + * settings. + * \note To be event triggered, the enable_start_on_event must be + * enabled in the configuration. + * + * \param[in] module_inst Pointer to the DAC software device struct + * \param[in] channel DAC channel to write to + * \param[in] buffer Pointer to the digital data write buffer to be converted + * \param[in] length Size of the write buffer + * + * \return Status of the operation. + * \retval STATUS_OK If the data was written + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + * \retval STATUS_BUSY The DAC is busy to accept new job + */ +enum status_code dac_chan_write_buffer_job( + struct dac_module *const module_inst, + const enum dac_channel channel, + uint16_t *buffer, + uint32_t length) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + Assert(buffer); + + UNUSED(channel); + + Dac *const dac_module = module_inst->hw; + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + if(module_inst->remaining_conversions != 0 || + module_inst->job_status == STATUS_BUSY){ + return STATUS_BUSY; + } + + /* Wait until the synchronization is complete */ + while (dac_is_syncing(module_inst)) { + }; + + module_inst->job_status = STATUS_BUSY; + + module_inst->remaining_conversions = length; + module_inst->job_buffer = buffer; + module_inst->transferred_conversions = 0; + + /* Enable interrupt */ + system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_DAC); + dac_module->INTFLAG.reg = DAC_INTFLAG_UNDERRUN | DAC_INTFLAG_EMPTY; + dac_module->INTENSET.reg = DAC_INTENSET_UNDERRUN | DAC_INTENSET_EMPTY; + + return STATUS_OK; +} + +/** + * \brief Convert one digital data job. + * + * This function will perform a conversion of specfic number of digital data. + * The conversion is event-triggered, the data will be written to DATABUF + * and transferred to the DATA register and converted when a Start Conversion + * Event is issued. + * Conversion data must be right or left adjusted according to configuration + * settings. + * \note To be event triggered, the enable_start_on_event must be + * enabled in the configuration. + * + * \param[in] module_inst Pointer to the DAC software device struct + * \param[in] channel DAC channel to write to + * \param[in] data Digital data to be converted + * + * \return Status of the operation. + * \retval STATUS_OK If the data was written + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + * \retval STATUS_BUSY The DAC is busy to accept new job + */ +enum status_code dac_chan_write_job( + struct dac_module *const module_inst, + const enum dac_channel channel, + uint16_t data) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(module_inst->hw); + + UNUSED(channel); + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + if(module_inst->remaining_conversions != 0 || + module_inst->job_status == STATUS_BUSY){ + return STATUS_BUSY; + } + + dac_chan_write_buffer_job(module_inst, channel, &data, 1); + + return STATUS_OK; +} + +/** + * \brief Registers an asynchronous callback function with the driver. + * + * Registers an asynchronous callback with the DAC driver, fired when a callback + * condition occurs. + * + * \param[in,out] module_inst Pointer to the DAC software instance struct + * \param[in] callback Pointer to the callback function to register + * \param[in] channel Logical channel to register callback function + * \param[in] type Type of callback function to register + * + * \return Status of the registration operation. + * \retval STATUS_OK The callback was registered successfully + * \retval STATUS_ERR_INVALID_ARG If an invalid callback type was supplied + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + */ +enum status_code dac_register_callback( + struct dac_module *const module_inst, + const enum dac_channel channel, + const dac_callback_t callback, + const enum dac_callback type) +{ + /* Sanity check arguments */ + Assert(module_inst); + Assert(callback); + + UNUSED(channel); + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + if ((uint8_t)type < DAC_CALLBACK_N) { + module_inst->callback[(uint8_t)type] = callback; + return STATUS_OK; + } + + return STATUS_ERR_INVALID_ARG; +} + +/** + * \brief Unregisters an asynchronous callback function with the driver. + * + * Unregisters an asynchronous callback with the DAC driver, removing it + * from the internal callback registration table. + * + * \param[in,out] module_inst Pointer to the DAC software instance struct + * \param[in] channel Logical channel to unregister callback function + * \param[in] type Type of callback function to unregister + * + * \return Status of the de-registration operation. + * \retval STATUS_OK The callback was unregistered successfully + * \retval STATUS_ERR_INVALID_ARG If an invalid callback type was supplied + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + */ +enum status_code dac_unregister_callback( + struct dac_module *const module_inst, + const enum dac_channel channel, + const enum dac_callback type) +{ + /* Sanity check arguments */ + Assert(module_inst); + + UNUSED(channel); + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + if ((uint8_t)type < DAC_CALLBACK_N) { + module_inst->callback[(uint8_t)type] = NULL; + return STATUS_OK; + } + + return STATUS_ERR_INVALID_ARG; +} + +/** + * \brief Enables asynchronous callback generation for a given channel and type. + * + * Enables asynchronous callbacks for a given logical DAC channel and type. This + * must be called before a DAC channel will generate callback events. + * + * \param[in,out] dac_module Pointer to the DAC software instance struct + * \param[in] channel Logical channel to enable callback function + * \param[in] type Type of callback function callbacks to enable + * + * \return Status of the callback enable operation. + * \retval STATUS_OK The callback was enabled successfully + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + */ +enum status_code dac_chan_enable_callback( + struct dac_module *const module_inst, + const enum dac_channel channel, + const enum dac_callback type) +{ + /* Sanity check arguments */ + Assert(module_inst); + + UNUSED(channel); + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + module_inst->callback_enable[type] = true; + + return STATUS_OK; +} + +/** + * \brief Disables asynchronous callback generation for a given channel and type. + * + * Disables asynchronous callbacks for a given logical DAC channel and type. + * + * \param[in,out] dac_module Pointer to the DAC software instance struct + * \param[in] channel Logical channel to disable callback function + * \param[in] type Type of callback function callbacks to disable + * + * \return Status of the callback disable operation. + * \retval STATUS_OK The callback was disabled successfully + * \retval STATUS_ERR_UNSUPPORTED_DEV If a callback that requires event driven + * mode was specified with a DAC instance + * configured in non-event mode + */ +enum status_code dac_chan_disable_callback( + struct dac_module *const module_inst, + const enum dac_channel channel, + const enum dac_callback type) +{ + /* Sanity check arguments */ + Assert(module_inst); + + UNUSED(channel); + + /* DAC interrupts require it to be driven by events to work, fail if in + * unbuffered (polled) mode */ + if (module_inst->start_on_event == false) { + return STATUS_ERR_UNSUPPORTED_DEV; + } + + module_inst->callback_enable[type] = false; + + return STATUS_OK; +} + +/** \internal + * Internal handler for DAC module interrupts. + * + * \param[in] instance DAC instance number + */ +static void _dac_interrupt_handler(const uint8_t instance) +{ + struct dac_module *module = _dac_instances[instance]; + Dac *const dac_hw = module->hw; + + if (dac_hw->INTFLAG.reg & DAC_INTFLAG_UNDERRUN) { + dac_hw->INTFLAG.reg = DAC_INTFLAG_UNDERRUN; + + if ((module->callback) && + (module->callback_enable[DAC_CALLBACK_DATA_UNDERRUN])){ + module->callback[DAC_CALLBACK_DATA_UNDERRUN](0); + } + } + + if (dac_hw->INTFLAG.reg & DAC_INTFLAG_EMPTY) { + dac_hw->INTFLAG.reg = DAC_INTFLAG_EMPTY; + + /* If in a write buffer job */ + if (module->remaining_conversions) { + + /* Fill the data buffer with next data in write buffer */ + dac_hw->DATABUF.reg = + module->job_buffer[module->transferred_conversions++]; + + /* Write buffer size decrement */ + module->remaining_conversions --; + + /* If in a write buffer job and all the data are converted */ + if (module->remaining_conversions == 0) { + module->job_status = STATUS_OK; + + /* Disable interrupt */ + dac_hw->INTENCLR.reg = DAC_INTENCLR_EMPTY; + dac_hw->INTFLAG.reg = DAC_INTFLAG_EMPTY; + system_interrupt_disable(SYSTEM_INTERRUPT_MODULE_DAC); + + if ((module->callback) && + (module->callback_enable[DAC_CALLBACK_TRANSFER_COMPLETE])) { + module->callback[DAC_CALLBACK_TRANSFER_COMPLETE](0); + } + } + } + + if ((module->callback) && + (module->callback_enable[DAC_CALLBACK_DATA_EMPTY])) { + module->callback[DAC_CALLBACK_DATA_EMPTY](0); + } + } +} + +/** Handler for the DAC hardware module interrupt. */ +void DAC_Handler(void) +{ + _dac_interrupt_handler(0); +} + +/** + * \brief Gets the status of a job + * + * Gets the status of an ongoing or the last job. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel Logical channel to enable callback function + * + * \return Status of the job. + */ +enum status_code dac_chan_get_job_status( + struct dac_module *module_inst, + const enum dac_channel channel) +{ + /* Sanity check arguments */ + Assert(module_inst); + + UNUSED(channel); + + return module_inst->job_status; +} + +/** + * \brief Aborts an ongoing job + * + * Aborts an ongoing job. + * + * \param[in] module_inst Pointer to the DAC software instance struct + * \param[in] channel Logical channel to enable callback function + */ +void dac_chan_abort_job( + struct dac_module *module_inst, + const enum dac_channel channel) +{ + /* Sanity check arguments */ + Assert(module_inst); + + UNUSED(channel); + + /* Disable interrupt */ + module_inst->hw->INTFLAG.reg = DAC_INTFLAG_UNDERRUN | DAC_INTFLAG_EMPTY; + module_inst->hw->INTENCLR.reg = DAC_INTENCLR_UNDERRUN | DAC_INTENCLR_EMPTY; + + /* Mark job as aborted */ + module_inst->job_status = STATUS_ABORTED; + module_inst->remaining_conversions = 0; + +} diff --git a/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_feature.h b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_feature.h new file mode 100644 index 0000000000..ae7ce8d657 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/dac_sam_d_c/dac_feature.h @@ -0,0 +1,634 @@ +/** + * \file + * + * \brief SAM Peripheral Digital-to-Analog Converter Driver + * + * Copyright (C) 2012-2015 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ +#ifndef DAC_FEATURE_H_INCLUDED +#define DAC_FEATURE_H_INCLUDED + +/** + * \defgroup asfdoc_sam0_dac_group SAM Digital-to-Analog (DAC) Driver + * + * This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the conversion of + * digital values to analog voltage. The following driver API modes are covered + * by this manual: + * + * - Polled APIs + * \if DAC_CALLBACK_MODE + * - Callback APIs + * \endif + * + * The following peripheral is used by this module: + * - DAC (Digital-to-Analog Converter) + * + * The following devices can use this module: + * - Atmel | SMART SAM D20/D21 + * - Atmel | SMART SAM D10/D11 + * - Atmel | SMART SAM DA1 + * - Atmel | SMART SAM C21 + * + * The outline of this documentation is as follows: + * - \ref asfdoc_sam0_dac_prerequisites + * - \ref asfdoc_sam0_dac_module_overview + * - \ref asfdoc_sam0_dac_special_considerations + * - \ref asfdoc_sam0_dac_extra_info + * - \ref asfdoc_sam0_dac_examples + * - \ref asfdoc_sam0_dac_api_overview + * + * + * \section asfdoc_sam0_dac_prerequisites Prerequisites + * + * There are no prerequisites for this module. + * + * + * \section asfdoc_sam0_dac_module_overview Module Overview + * + * The Digital-to-Analog converter converts a digital value to analog voltage. + * The SAM DAC module has one channel with 10-bit resolution, + * and is capable of converting up to 350k samples per second (ksps). + * + * A common use of DAC is to generate audio signals by connecting the DAC + * output to a speaker, or to generate a reference voltage; either for an + * external circuit or an internal peripheral such as the Analog Comparator. + * + * After being set up, the DAC will convert new digital values written to the + * conversion data register (DATA) to an analog value either on the VOUT pin of + * the device, or internally for use as an input to the AC, ADC, and other analog + * modules. + * + * Writing the DATA register will start a new conversion. It is also possible + * to trigger the conversion from the event system. + * + * A simplified block diagram of the DAC can be seen in + * \ref asfdoc_sam0_dac_module_block_diagram "the figure below". + * + * \anchor asfdoc_sam0_dac_module_block_diagram + * \image html dac_block_diagram.svg "DAC Block Diagram" + * + * \subsection asfdoc_sam0_dac_conversion_range Conversion Range + * The conversion range is between GND and the selected voltage reference. + * Available voltage references are: + * \li AVCC voltage reference + * \li Internal 1V reference (INT1V) + * \li External voltage reference (AREF) + * + * \note Internal references will be enabled by the driver, but not disabled. + * Any reference not used by the application should be disabled by the application. + * + * The output voltage from a DAC channel is given as: + * \f[ + * V_{OUT} = \frac{DATA}{0x3FF} \times VREF + * \f] + * + * \subsection asfdoc_sam0_dac_conversion Conversion + * The digital value written to the conversion data register (DATA) will be + * converted to an analog value. + * Writing the DATA register will start a new conversion. + * It is also possible to write the conversion data to the DATABUF register, + * the writing of the DATA register can then be triggered from the event + * system, which will load the value from DATABUF to DATA. + * + * \subsection asfdoc_sam0_dac_analog_output Analog Output + * The analog output value can be output to either the VOUT pin or internally, + * but not both at the same time. + * + * \subsubsection asfdoc_sam0_dac_analog_output_external External Output + * The output buffer must be enabled in order to drive the DAC output to the + * VOUT pin. Due to the output buffer, the DAC has high drive strength, and is + * capable of driving both resistive and capacitive loads, as well as loads + * which combine both. + * + * \subsubsection asfdoc_sam0_dac_analog_output_internal Internal Output + * The analog value can be internally available for use as input to the + * AC or ADC modules. + * + * \subsection asfdoc_sam0_dac_events Events + * Events generation and event actions are configurable in the DAC. + * The DAC has one event line input and one event output: Start Conversion + * and Data Buffer Empty. + * + * If the Start Conversion input event is enabled in the module configuration, + * an incoming event will load data from the data buffer to the data register + * and start a new conversion. This method synchronizes conversions with + * external events (such as those from a timer module) and ensures regular and + * fixed conversion intervals. + * + * If the Data Buffer Empty output event is enabled in the module configuration, + * events will be generated when the DAC data buffer register becomes empty and + * new data can be loaded to the buffer. + * + * \note The connection of events between modules requires the use of the event + * driver to route output event of one module to the input event of another. + * For more information on event routing, refer to the documentation + * \ref asfdoc_sam0_events_group "SAM Event System (EVENTS) Driver". + * + * \subsection asfdoc_sam0_dac_data_adjust Left and Right Adjusted Values + * The 10-bit input value to the DAC is contained in a 16-bit register. This + * can be configured to be either left or right adjusted. In + * \ref asfdoc_sam0_dac_module_adj_modes "the figure below" both options are + * shown, and the position of the most (MSB) and the least (LSB) significant bits + * are indicated. The unused bits should always be written to zero. + * + * \anchor asfdoc_sam0_dac_module_adj_modes + * \dot + * digraph { + * subgraph cluster_right { + * msbl [label="MSB", shape=none, group="msbl"]; + * lsbl [label="LSB", shape=none]; + * node [shape=none]; + * color="white"; + * reg_left [label=< + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
1514131211109876543210
DATA[9:0]
+ * >]; + * msbl -> reg_left:msb:n; + * lsbl -> reg_left:lsb; + * label ="Left adjusted.\n"; + * } + * subgraph cluster_left { + * rankdir=TB; + * msb [label="MSB", shape=none]; + * lsb [label="LSB", shape=none]; + * color="white"; + * node [shape=none]; + * reg_right [label=< + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
1514131211109876543210
DATA[9:0]
+ * >]; + * msb -> reg_right:msb; + * lsb -> reg_right:lsb:n; + * label = "Right adjusted.\n"; + * graph [shape=none]; + * } + * } + * \enddot + * + * \subsection asfdoc_sam0_dac_clk_sources Clock Sources + * The clock for the DAC interface (CLK_DAC) is generated by the Power Manager. + * This clock is turned on by default, and can be enabled and disabled in the + * Power Manager. + * + * Additionally, an asynchronous clock source (GCLK_DAC) is required. + * These clocks are normally disabled by default. The selected clock source + * must be enabled in the Power Manager before it can be used by the DAC. + * The DAC core operates asynchronously from the user interface and + * peripheral bus. As a consequence, the DAC needs two clock cycles of both + * CLK_DAC and GCLK_DAC to synchronize the values written to some of the + * control and data registers. + * The oscillator source for the GCLK_DAC clock is selected in the System + * Control Interface (SCIF). + * + * \section asfdoc_sam0_dac_special_considerations Special Considerations + * + * \subsection asfdoc_sam0_dac_special_considerations_output_buffer Output Driver + * The DAC can only do conversions in Active or Idle modes. However, if the + * output buffer is enabled it will draw current even if the system is in + * sleep mode. Therefore, always make sure that the output buffer is not + * enabled when it is not needed, to ensure minimum power consumption. + * + * \subsection asfdoc_sam0_dac_special_considerations_conversion_time Conversion Time + * DAC conversion time is approximately 2.85µs. The user must ensure that new + * data is not written to the DAC before the last conversion is complete. + * Conversions should be triggered by a periodic event from a Timer/Counter or + * another peripheral. + * + * + * \section asfdoc_sam0_dac_extra_info Extra Information + * + * For extra information, see \ref asfdoc_sam0_dac_extra. This includes: + * - \ref asfdoc_sam0_dac_extra_acronyms + * - \ref asfdoc_sam0_dac_extra_dependencies + * - \ref asfdoc_sam0_dac_extra_errata + * - \ref asfdoc_sam0_dac_extra_history + * + * + * \section asfdoc_sam0_dac_examples Examples + * + * For a list of examples related to this driver, see + * \ref asfdoc_sam0_dac_exqsg. + * + * + * \section asfdoc_sam0_dac_api_overview API Overview + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +/** + * \name DAC Status Flags + * + * DAC status flags, returned by \ref dac_get_status() and cleared by + * \ref dac_clear_status(). + * @{ + */ + +/** Data Buffer Empty Channel 0 - Set when data is transferred from DATABUF + * to DATA by a start conversion event and DATABUF is ready for new data. + */ +#define DAC_STATUS_CHANNEL_0_EMPTY (1UL << 0) + +/** Under-run Channel 0 - Set when a start conversion event occurs when + * DATABUF is empty. + */ +#define DAC_STATUS_CHANNEL_0_UNDERRUN (1UL << 1) + +/** @} */ + +/** + * \brief DAC reference voltage enum. + * + * Enum for the possible reference voltages for the DAC. + */ +enum dac_reference { + /** 1V from the internal band-gap reference*/ + DAC_REFERENCE_INT1V = DAC_CTRLB_REFSEL(0), + /** Analog VCC as reference */ + DAC_REFERENCE_AVCC = DAC_CTRLB_REFSEL(1), + /** External reference on AREF */ + DAC_REFERENCE_AREF = DAC_CTRLB_REFSEL(2), +}; + +/** + * \brief DAC output selection enum. + * + * Enum for the DAC output selection. + */ +enum dac_output { + /** DAC output to VOUT pin */ + DAC_OUTPUT_EXTERNAL = DAC_CTRLB_EOEN, + /** DAC output as internal reference */ + DAC_OUTPUT_INTERNAL = DAC_CTRLB_IOEN, + /** No output */ + DAC_OUTPUT_NONE = 0, +}; + +/** + * \brief DAC channel selection enum. + * + * Enum for the DAC channel selection. + */ +enum dac_channel { + /** DAC output channel 0 */ + DAC_CHANNEL_0, +}; + +/** + * \brief DAC software device instance structure. + * + * DAC software instance structure, used to retain software state information + * of an associated hardware module instance. + * + * \note The fields of this structure should not be altered by the user + * application; they are reserved for module-internal use only. + */ +struct dac_module { +#if !defined(__DOXYGEN__) + /** DAC hardware module */ + Dac *hw; + /** DAC output selection */ + enum dac_output output; + /** Reference selection */ + enum dac_reference reference; + /** DAC event selection */ + bool start_on_event; +# if DAC_CALLBACK_MODE == true + /** Pointer to buffer used for ADC results */ + volatile uint16_t *job_buffer; + /** Remaining number of conversions in current job */ + volatile uint16_t remaining_conversions; + /** Transferred number of conversions in current job */ + volatile uint16_t transferred_conversions; + /** DAC callback enable */ + bool callback_enable[DAC_CALLBACK_N]; + /** DAC registered callback functions */ + dac_callback_t callback[DAC_CALLBACK_N]; + /** Holds the status of the ongoing or last conversion job */ + volatile enum status_code job_status; +# endif +#endif +}; + +/** + * \brief DAC configuration structure. + * + * Configuration structure for a DAC instance. This structure should be + * initialized by the \ref dac_get_config_defaults() + * function before being modified by the user application. + */ +struct dac_config { + /** Reference voltage */ + enum dac_reference reference; + /** Select DAC output */ + enum dac_output output; + /** Left adjusted data */ + bool left_adjust; + /** GCLK generator used to clock the peripheral */ + enum gclk_generator clock_source; +#ifdef FEATURE_DAC_DATABUF_WRITE_PROTECTION + /** Bypass DATABUF write protection */ + bool databuf_protection_bypass; +#endif + /** Voltage pump disable */ + bool voltage_pump_disable; + /** + * The DAC behaves as in normal mode when the chip enters STANDBY sleep + * mode + */ + bool run_in_standby; +#if (SAMC21) + /** Dither mode enable data */ + bool dither_mode; +#endif +}; + +/** + * \brief DAC event enable/disable structure. + * + * Event flags for the DAC module. This is used to enable and + * disable events via \ref dac_enable_events() and \ref dac_disable_events(). + */ +struct dac_events { + /** Start a new DAC conversion */ + bool on_event_start_conversion; + /** Enable event generation on data buffer empty */ + bool generate_event_on_buffer_empty; +#if (SAMC21) + /** Enable the falling edge of the input event for DAC1 */ + bool generate_event_on_chan_falling_edge; +#endif +}; + +/** + * \brief DAC channel configuration structure + * + * Configuration for a DAC channel. This structure should be initialized by the + * \ref dac_chan_get_config_defaults() function before being modified by the + * user application. + */ +struct dac_chan_config { +#if !defined(__DOXYGEN__) + /** Dummy value to ensure the struct has at least one member */ + uint8_t _dummy; +#endif +}; + +/** + * \name Configuration and Initialization (Channel) + * @{ + */ + +void dac_chan_enable_output_buffer( + struct dac_module *const dev_inst, + const enum dac_channel channel); + +void dac_chan_disable_output_buffer( + struct dac_module *const dev_inst, + const enum dac_channel channel); + +/** @} */ + +/** @} */ + +/** + * \page asfdoc_sam0_dac_extra Extra Information for DAC Driver + * + * \section asfdoc_sam0_dac_extra_acronyms Acronyms + * The table below presents the acronyms used in this module: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
AcronymDescription
ADCAnalog-to-Digital Converter
ACAnalog Comparator
DACDigital-to-Analog Converter
LSBLeast Significant Bit
MSBMost Significant Bit
DMADirect Memory Access
+ * + * + * \section asfdoc_sam0_dac_extra_dependencies Dependencies + * This driver has the following dependencies: + * + * - \ref asfdoc_sam0_system_pinmux_group "System Pin Multiplexer Driver" + * + * + * \section asfdoc_sam0_dac_extra_errata Errata + * There are no errata related to this driver. + * + * + * \section asfdoc_sam0_dac_extra_history Module History + * An overview of the module history is presented in the table below, with + * details on the enhancements and fixes made to the module since its first + * release. The current version of this corresponds to the newest version in + * the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Changelog
Add configuration for using 14-bit hardware dithering (SAMC21 support)
Added new configuration parameters \c databuf_protection_bypass, + * \c voltage_pump_disable. Added new callback functions + * \c dac_chan_write_buffer_wait, + * \c dac_chan_write_buffer_job, \c dac_chan_write_job, + * \c dac_get_job_status, \c dac_abort_job and new callback type + * \c DAC_CALLBACK_TRANSFER_COMPLETE for DAC conversion job
Initial Release
+ */ + +/** + * \page asfdoc_sam0_dac_exqsg Examples for DAC Driver + * + * This is a list of the available Quick Start guides (QSGs) and example + * applications for \ref asfdoc_sam0_dac_group. QSGs are simple examples with + * step-by-step instructions to configure and use this driver in a selection of + * use cases. Note that a QSG can be compiled as a standalone application or be + * added to the user application. + * + * - \subpage asfdoc_sam0_dac_basic_use_case + * \if DAC_CALLBACK_MODE + * - \subpage asfdoc_sam0_dac_basic_use_case_callback + * \endif + * \if DAC_DMA_USE_MODE_SELECTION + * - \subpage asfdoc_sam0_adc_dma_use_case_dac_in_dma + * \endif + * + * \if DAC_DMA_USE_MODE_SELECTION + * \page asfdoc_sam0_adc_dma_use_case_dac_in_dma Quick Start Guide for Using DMA with ADC/DAC + * For this examples, see + * \ref asfdoc_sam0_adc_dma_use_case + * \endif + * + * \page asfdoc_sam0_dac_document_revision_history Document Revision History + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Doc. Rev. + * Date + * Comments + *
42110E09/2015Add SAM C21 and SAM DA1 support
42110D12/2014Add SAM D10/D11 support
42110C01/2014Add SAM D21 support
42110B06/2013Added additional documentation on the event system. Corrected + * documentation typos.
42110A06/2013Initial document release
+ */ + +#ifdef __cplusplus +} +#endif + + +#endif /* DAC_FEATURE_H_INCLUDED */ + diff --git a/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram.svg b/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram.svg new file mode 100644 index 0000000000..0b064257ae --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram.svg @@ -0,0 +1,218 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram_saml.svg b/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram_saml.svg new file mode 100644 index 0000000000..0ed3f88e79 --- /dev/null +++ b/atmel-samd/asf/sam0/drivers/dac/docimg/dac_block_diagram_saml.svg @@ -0,0 +1,1303 @@ + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/atmel-samd/modmachine.c b/atmel-samd/modmachine.c index 1471ca9813..6a8256753c 100644 --- a/atmel-samd/modmachine.c +++ b/atmel-samd/modmachine.c @@ -32,6 +32,7 @@ #include "py/runtime.h" #include "adc.h" +#include "mpdac.h" #include "pin.h" #if MICROPY_PY_MACHINE @@ -39,6 +40,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&adc_type) }, + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&dac_type) }, { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, }; diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index 545b2c40d0..7682997ad6 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -56,6 +56,8 @@ #define MICROPY_PY_MACHINE (1) #define MICROPY_MODULE_WEAK_LINKS (1) #define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_HW_ENABLE_DAC (1) +#define MICROPY_ENABLE_FINALISER (1) // type definitions for the specific machine diff --git a/atmel-samd/mpdac.c b/atmel-samd/mpdac.c new file mode 100644 index 0000000000..5f3308b936 --- /dev/null +++ b/atmel-samd/mpdac.c @@ -0,0 +1,152 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +// +// #include "py/nlr.h" +#include "py/runtime.h" +// #include "timer.h" +#include "mpdac.h" +// #include "dma.h" +// #include "pin.h" +// #include "genhdr/pins.h" + +#include "asf/sam0/drivers/dac/dac.h" + +/// \moduleref machine +/// \class DAC - digital to analog conversion +/// +/// The DAC is used to output analog values (a specific voltage) on pin PA02 +/// (Arduino Zero A0, Feather MO Bluefruit A0). +/// The voltage will be between 0 and 3.3V. +/// +/// *This module will undergo changes to the API.* +/// +/// Example usage: +/// +/// from machine import DAC +/// +/// dac = DAC() # create DAC 1 on pin PA02 +/// dac.write(512) # write a value to the DAC (makes PA02 1.65V) +/// dac.write_mv(1650) # Also results in PA02 being 1.65V + +/******************************************************************************/ +// Micro Python bindings + +typedef struct _dac_obj_t { + mp_obj_base_t base; + struct dac_module dac_instance; +} dac_obj_t; + +// create the dac object + +/// \classmethod \constructor() +/// Construct a new DAC object for pin PA02. +STATIC mp_obj_t dac_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + // check arguments + mp_arg_check_num(n_args, n_kw, 0, MP_OBJ_FUN_ARGS_MAX, true); + + dac_obj_t *dac = m_new_obj(dac_obj_t); + dac->base.type = &dac_type; + + struct dac_config config_dac; + dac_get_config_defaults(&config_dac); + config_dac.reference = DAC_REFERENCE_AVCC; + enum status_code status = dac_init(&dac->dac_instance, DAC, &config_dac); + if (status != STATUS_OK) { + // Throw error. + return mp_const_none; + } + + struct dac_chan_config config_dac_chan; + dac_chan_get_config_defaults(&config_dac_chan); + dac_chan_set_config(&dac->dac_instance, DAC_CHANNEL_0, &config_dac_chan); + dac_chan_enable(&dac->dac_instance, DAC_CHANNEL_0); + + dac_enable(&dac->dac_instance); + + // return object + return dac; +} + +/// \method deinit() +/// Turn off the DAC, enable other use of pin. +STATIC mp_obj_t dac_del(mp_obj_t self_in) { + dac_obj_t *self = self_in; + + dac_disable(&self->dac_instance); + dac_chan_disable(&self->dac_instance, DAC_CHANNEL_0); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(dac_del_obj, dac_del); + +/// \method write(value) +/// Direct access to the DAC output (10 bit). +STATIC mp_obj_t dac_write(mp_obj_t self_in, mp_obj_t val) { + dac_obj_t *self = self_in; + + dac_chan_write(&self->dac_instance, DAC_CHANNEL_0, mp_obj_get_int(val)); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(dac_write_obj, dac_write); + +/// \method write_mv(value) +/// Direct access to the DAC output by specifying millivolts. +STATIC mp_obj_t dac_write_mv(mp_obj_t self_in, mp_obj_t val_mv) { + dac_obj_t *self = self_in; + // TODO(tannewt): Sanity check that the mv value is less than the reference + // voltage. + // Dividing by 3301 instead of 3300 ensures that val_mv of 3300 actually + // produces a value of 1023 instead of 1024 which is out of range of the + // DAC. + uint16_t val = ((uint32_t) mp_obj_get_int(val_mv)) * 1024 / 3301; + + dac_chan_write(&self->dac_instance, DAC_CHANNEL_0, val); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(dac_write_mv_obj, dac_write_mv); + +STATIC const mp_map_elem_t dac_locals_dict_table[] = { + // instance methods + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&dac_del_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&dac_write_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_write_mv), (mp_obj_t)&dac_write_mv_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(dac_locals_dict, dac_locals_dict_table); + +const mp_obj_type_t dac_type = { + { &mp_type_type }, + .name = MP_QSTR_DAC, + .make_new = dac_make_new, + .locals_dict = (mp_obj_t)&dac_locals_dict, +}; diff --git a/atmel-samd/mpdac.h b/atmel-samd/mpdac.h new file mode 100644 index 0000000000..931987cd95 --- /dev/null +++ b/atmel-samd/mpdac.h @@ -0,0 +1,27 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +extern const mp_obj_type_t dac_type;