mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-04 15:24:12 +01:00
221c10842e
(cherry-pick from Thibault Richard commit a1d6cb43a5
)
482 lines
12 KiB
C
482 lines
12 KiB
C
/* ----------------------------------------------------------------------------
|
|
* SAM Software Package License
|
|
* ----------------------------------------------------------------------------
|
|
* Copyright (c) 2011-2012, Atmel Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following condition is met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the disclaimer below.
|
|
*
|
|
* Atmel's name may not be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* DISCLAIMER: 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
|
|
* 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.
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "dacc.h"
|
|
|
|
/// @cond 0
|
|
/**INDENT-OFF**/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
/**INDENT-ON**/
|
|
/// @endcond
|
|
|
|
/**
|
|
* \defgroup sam_drivers_dacc_group Digital-to-Analog Converter Controller (DACC)
|
|
*
|
|
* \par Purpose
|
|
*
|
|
* Driver for the Digital-to-Analog Converter Controller. It provides access to the main
|
|
* features of the DAC controller.
|
|
*
|
|
* \par Usage
|
|
*
|
|
* -# DACC clock should be enabled before using it.
|
|
* - \ref pmc_enable_periph_clk() can be used to enable the clock.
|
|
* -# Reset DACC with \ref dacc_reset().
|
|
* -# If DACC can be enabled/disabled, uses \ref dacc_enable() and
|
|
* \ref dacc_disable().
|
|
* -# Initialize DACC timing with \ref dacc_set_timing() (different DAC
|
|
* peripheral may require different parameters).
|
|
* -# Write conversion data with \ref dacc_write_conversion_data().
|
|
* -# Configure trigger with \ref dacc_set_trigger()
|
|
* and \ref dacc_disable_trigger().
|
|
* -# Configure FIFO transfer mode with \ref dacc_set_transfer_mode().
|
|
* -# Control interrupts with \ref dacc_enable_interrupt(),
|
|
* \ref dacc_disable_interrupt(), \ref dacc_get_interrupt_mask() and
|
|
* \ref dacc_get_interrupt_status().
|
|
* -# DACC registers support write protect with \ref dacc_set_writeprotect()
|
|
* and \ref dacc_get_writeprotect_status().
|
|
* -# If the DACC can work with PDC, use \ref dacc_get_pdc_base() to get
|
|
* PDC register base for the DAC controller.
|
|
* -# If the DACC has several channels to process, the following functions can
|
|
* be used:
|
|
* - Enable/Disable TAG and select output channel selection by
|
|
* \ref dacc_set_channel_selection(),
|
|
* \ref dacc_enable_flexible_channel_selection().
|
|
* - Enable/disable channel by \ref dacc_enable_channel() /
|
|
* \ref dacc_disable_channel(), get channel status by
|
|
* \ref dacc_get_channel_status().
|
|
*
|
|
* \section dependencies Dependencies
|
|
* This driver does not depend on other modules.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
//! Max channel number
|
|
#if (SAM3N_SERIES)
|
|
# define MAX_CH_NB 0
|
|
#else
|
|
# define MAX_CH_NB 1
|
|
#endif
|
|
|
|
//! DACC Write Protect Key "DAC" in ASCII
|
|
#define DACC_WP_KEY (0x444143)
|
|
|
|
/**
|
|
* \brief Reset DACC.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*/
|
|
void dacc_reset(Dacc *p_dacc)
|
|
{
|
|
p_dacc->DACC_CR = DACC_CR_SWRST;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable trigger and set the trigger source.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_trigger Trigger source number.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_set_trigger(Dacc *p_dacc, uint32_t ul_trigger)
|
|
{
|
|
uint32_t mr = p_dacc->DACC_MR & (~(DACC_MR_TRGSEL_Msk));
|
|
#if (SAM3N_SERIES)
|
|
p_dacc->DACC_MR = mr
|
|
| DACC_MR_TRGEN
|
|
| ((ul_trigger << DACC_MR_TRGSEL_Pos) & DACC_MR_TRGSEL_Msk);
|
|
#else
|
|
p_dacc->DACC_MR = mr | DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(ul_trigger);
|
|
#endif
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable trigger (free run mode).
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*/
|
|
void dacc_disable_trigger(Dacc *p_dacc)
|
|
{
|
|
p_dacc->DACC_MR &= ~DACC_MR_TRGEN;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the transfer mode.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_mode Transfer mode configuration.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_set_transfer_mode(Dacc *p_dacc, uint32_t ul_mode)
|
|
{
|
|
if (ul_mode) {
|
|
#if (SAM3N_SERIES)
|
|
p_dacc->DACC_MR |= DACC_MR_WORD;
|
|
#else
|
|
p_dacc->DACC_MR |= DACC_MR_WORD_WORD;
|
|
#endif
|
|
} else {
|
|
#if (SAM3N_SERIES)
|
|
p_dacc->DACC_MR &= (~DACC_MR_WORD);
|
|
#else
|
|
p_dacc->DACC_MR &= (~DACC_MR_WORD_WORD);
|
|
#endif
|
|
}
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable DACC interrupts.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_interrupt_mask The interrupt mask.
|
|
*/
|
|
void dacc_enable_interrupt(Dacc *p_dacc, uint32_t ul_interrupt_mask)
|
|
{
|
|
p_dacc->DACC_IER = ul_interrupt_mask;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable DACC interrupts.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_interrupt_mask The interrupt mask.
|
|
*/
|
|
void dacc_disable_interrupt(Dacc *p_dacc, uint32_t ul_interrupt_mask)
|
|
{
|
|
p_dacc->DACC_IDR = ul_interrupt_mask;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the interrupt mask.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return The interrupt mask.
|
|
*/
|
|
uint32_t dacc_get_interrupt_mask(Dacc *p_dacc)
|
|
{
|
|
return p_dacc->DACC_IMR;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the interrupt status.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return The interrupt status.
|
|
*/
|
|
uint32_t dacc_get_interrupt_status(Dacc *p_dacc)
|
|
{
|
|
return p_dacc->DACC_ISR;
|
|
}
|
|
|
|
/**
|
|
* \brief Write data to conversion register.
|
|
*
|
|
* \note The \a ul_data could be output data or data with channel TAG when
|
|
* flexible mode is used.
|
|
*
|
|
* In flexible mode the 2 bits, DACC_CDR[13:12] which are otherwise unused,
|
|
* are employed to select the channel in the same way as with the USER_SEL
|
|
* field. Finally, if the WORD field is set, the 2 bits, DACC_CDR[13:12] are
|
|
* used for channel selection of the first data and the 2 bits,
|
|
* DACC_CDR[29:28] for channel selection of the second data.
|
|
*
|
|
* \see dacc_enable_flexible_selection()
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_data The data to be transferred to analog value.
|
|
*/
|
|
void dacc_write_conversion_data(Dacc *p_dacc, uint32_t ul_data)
|
|
{
|
|
p_dacc->DACC_CDR = ul_data;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable or disable write protect of DACC registers.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_enable 1 to enable, 0 to disable.
|
|
*/
|
|
void dacc_set_writeprotect(Dacc *p_dacc, uint32_t ul_enable)
|
|
{
|
|
if (ul_enable) {
|
|
p_dacc->DACC_WPMR = DACC_WPMR_WPKEY(DACC_WP_KEY)
|
|
| DACC_WPMR_WPEN;
|
|
} else {
|
|
p_dacc->DACC_WPMR = DACC_WPMR_WPKEY(DACC_WP_KEY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Get the write protect status.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return Write protect status.
|
|
*/
|
|
uint32_t dacc_get_writeprotect_status(Dacc *p_dacc)
|
|
{
|
|
return p_dacc->DACC_WPSR;
|
|
}
|
|
|
|
/**
|
|
* \brief Get PDC registers base address.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return DACC PDC register base address.
|
|
*/
|
|
Pdc *dacc_get_pdc_base(Dacc *p_dacc)
|
|
{
|
|
p_dacc = p_dacc;
|
|
return PDC_DACC;
|
|
}
|
|
|
|
#if (SAM3N_SERIES) || defined(__DOXYGEN__)
|
|
/**
|
|
* \brief Enable DACC.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*/
|
|
void dacc_enable(Dacc *p_dacc)
|
|
{
|
|
p_dacc->DACC_MR |= DACC_MR_DACEN;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable DACC.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
void dacc_disable(Dacc *p_dacc)
|
|
{
|
|
p_dacc->DACC_MR &= (~DACC_MR_DACEN);
|
|
}
|
|
|
|
/**
|
|
* \brief Set the DACC timing.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_startup Startup time selection.
|
|
* \param ul_clock_divider Clock divider for internal trigger.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_set_timing(Dacc *p_dacc, uint32_t ul_startup,
|
|
uint32_t ul_clock_divider)
|
|
{
|
|
uint32_t mr = p_dacc->DACC_MR
|
|
& ~(DACC_MR_STARTUP_Msk | DACC_MR_CLKDIV_Msk);
|
|
p_dacc->DACC_MR = mr | DACC_MR_STARTUP(ul_startup)
|
|
| DACC_MR_CLKDIV(ul_clock_divider);
|
|
return DACC_RC_OK;
|
|
}
|
|
#endif /* #if (SAM3N_SERIES) */
|
|
|
|
#if (SAM3S_SERIES) || (SAM3XA_SERIES) || (SAM4S_SERIES) || defined(__DOXYGEN__)
|
|
/**
|
|
* \brief Disable flexible (TAG) mode and select a channel for DAC outputs.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_channel Channel to select.
|
|
*
|
|
* \return \ref DACC_RC_OK if successful.
|
|
*/
|
|
uint32_t dacc_set_channel_selection(Dacc *p_dacc, uint32_t ul_channel)
|
|
{
|
|
uint32_t mr = p_dacc->DACC_MR & (~DACC_MR_USER_SEL_Msk);
|
|
if (ul_channel > MAX_CH_NB) {
|
|
return DACC_RC_INVALID_PARAM;
|
|
}
|
|
mr &= ~(DACC_MR_TAG);
|
|
mr |= ul_channel << DACC_MR_USER_SEL_Pos;
|
|
p_dacc->DACC_MR = mr;
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable the flexible channel selection mode (TAG).
|
|
*
|
|
* In this mode the 2 bits, DACC_CDR[13:12] which are otherwise unused, are
|
|
* employed to select the channel in the same way as with the USER_SEL field.
|
|
* Finally, if the WORD field is set, the 2 bits, DACC_CDR[13:12] are used
|
|
* for channel selection of the first data and the 2 bits, DACC_CDR[29:28]
|
|
* for channel selection of the second data.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*/
|
|
void dacc_enable_flexible_selection(Dacc *p_dacc)
|
|
{
|
|
p_dacc->DACC_MR |= DACC_MR_TAG;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the power save mode.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_sleep_mode Sleep mode configuration.
|
|
* \param ul_fast_wakeup_mode Fast wakeup mode configuration.
|
|
*
|
|
* \return \ref DACC_RC_OK if successful.
|
|
*/
|
|
uint32_t dacc_set_power_save(Dacc *p_dacc,
|
|
uint32_t ul_sleep_mode, uint32_t ul_fast_wakeup_mode)
|
|
{
|
|
if (ul_sleep_mode) {
|
|
p_dacc->DACC_MR |= DACC_MR_SLEEP;
|
|
} else {
|
|
p_dacc->DACC_MR &= (~DACC_MR_SLEEP);
|
|
}
|
|
if (ul_fast_wakeup_mode) {
|
|
p_dacc->DACC_MR |= DACC_MR_FASTWKUP;
|
|
} else {
|
|
p_dacc->DACC_MR &= (~DACC_MR_FASTWKUP);
|
|
}
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Set DACC timings.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_refresh Refresh period setting value.
|
|
* \param ul_maxs Max speed mode configuration.
|
|
* \param ul_startup Startup time selection.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_set_timing(Dacc *p_dacc,
|
|
uint32_t ul_refresh, uint32_t ul_maxs, uint32_t ul_startup)
|
|
{
|
|
uint32_t mr = p_dacc->DACC_MR
|
|
& (~(DACC_MR_REFRESH_Msk | DACC_MR_STARTUP_Msk));
|
|
mr |= DACC_MR_REFRESH(ul_refresh);
|
|
if (ul_maxs) {
|
|
mr |= DACC_MR_MAXS;
|
|
} else {
|
|
mr &= ~DACC_MR_MAXS;
|
|
}
|
|
mr |= (DACC_MR_STARTUP_Msk & ((ul_startup) << DACC_MR_STARTUP_Pos));
|
|
p_dacc->DACC_MR = mr;
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable DACC channel.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_channel The output channel to enable.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_enable_channel(Dacc *p_dacc, uint32_t ul_channel)
|
|
{
|
|
if (ul_channel > MAX_CH_NB)
|
|
return DACC_RC_INVALID_PARAM;
|
|
|
|
p_dacc->DACC_CHER = DACC_CHER_CH0 << ul_channel;
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable DACC channel.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_channel The output channel to disable.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_disable_channel(Dacc *p_dacc, uint32_t ul_channel)
|
|
{
|
|
if (ul_channel > MAX_CH_NB) {
|
|
return DACC_RC_INVALID_PARAM;
|
|
}
|
|
p_dacc->DACC_CHDR = DACC_CHDR_CH0 << ul_channel;
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the channel status.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return DACC channel status.
|
|
*/
|
|
uint32_t dacc_get_channel_status(Dacc *p_dacc)
|
|
{
|
|
return p_dacc->DACC_CHSR;
|
|
}
|
|
|
|
/**
|
|
* \brief Set the analog control value.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
* \param ul_analog_control Analog control configuration.
|
|
*
|
|
* \return \ref DACC_RC_OK for OK.
|
|
*/
|
|
uint32_t dacc_set_analog_control(Dacc *p_dacc, uint32_t ul_analog_control)
|
|
{
|
|
p_dacc->DACC_ACR = ul_analog_control;
|
|
return DACC_RC_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Get the analog control value.
|
|
*
|
|
* \param p_dacc Pointer to a DACC instance.
|
|
*
|
|
* \return Current setting of analog control.
|
|
*/
|
|
uint32_t dacc_get_analog_control(Dacc *p_dacc)
|
|
{
|
|
return p_dacc->DACC_ACR;
|
|
}
|
|
#endif /* (SAM3S_SERIES) || (SAM3XA_SERIES) */
|
|
|
|
//@}
|
|
|
|
/// @cond 0
|
|
/**INDENT-OFF**/
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
/**INDENT-ON**/
|
|
/// @endcond
|