You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
508 lines
13 KiB
C
508 lines
13 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Chip-specific oscillator management functions
|
|
*
|
|
* Copyright (c) 2010-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 <a href="http://www.atmel.com/design-support/">Atmel Support</a>
|
|
*/
|
|
#ifndef XMEGA_OSC_H_INCLUDED
|
|
#define XMEGA_OSC_H_INCLUDED
|
|
|
|
#include <compiler.h>
|
|
#include <board.h>
|
|
|
|
/**
|
|
* \weakgroup osc_group
|
|
*
|
|
* \section osc_group_errata Errata
|
|
* - Auto-calibration does not work on XMEGA A1 revision H and
|
|
* earlier.
|
|
* @{
|
|
*/
|
|
|
|
//! \name Oscillator identifiers
|
|
//@{
|
|
//! 2 MHz Internal RC Oscillator
|
|
#define OSC_ID_RC2MHZ OSC_RC2MEN_bm
|
|
//! 32 MHz Internal RC Oscillator
|
|
#define OSC_ID_RC32MHZ OSC_RC32MEN_bm
|
|
//! 32 KHz Internal RC Oscillator
|
|
#define OSC_ID_RC32KHZ OSC_RC32KEN_bm
|
|
//! External Oscillator
|
|
#define OSC_ID_XOSC OSC_XOSCEN_bm
|
|
#if XMEGA_E
|
|
//! 8 MHz Internal RC Oscillator
|
|
# define OSC_ID_RC8MHZ OSC_RC8MEN_bm
|
|
#endif
|
|
|
|
/**
|
|
* \brief Reference from USB Start Of Frame
|
|
* \note This cannot be enabled or disabled, but can be used as a reference for
|
|
* the autocalibration (DFLL).
|
|
*/
|
|
#define OSC_ID_USBSOF 0xff
|
|
//@}
|
|
|
|
//! \name External oscillator types
|
|
//@{
|
|
#define XOSC_TYPE_EXTERNAL 0 //!< External clock signal
|
|
#define XOSC_TYPE_32KHZ 2 //!< 32.768 kHz resonator on TOSC
|
|
#define XOSC_TYPE_XTAL 3 //!< 0.4 to 16 MHz resonator on XTAL
|
|
//@}
|
|
|
|
/**
|
|
* \def CONFIG_XOSC_32KHZ_LPM
|
|
* \brief Define for enabling Low Power Mode for 32 kHz external oscillator.
|
|
*/
|
|
#ifdef __DOXYGEN__
|
|
# define CONFIG_XOSC_32KHZ_LPM
|
|
#endif /* __DOXYGEN__ */
|
|
|
|
/**
|
|
* \def CONFIG_XOSC_STARTUP
|
|
* \brief Board-dependent value that determines the number of start-up cycles
|
|
* for external resonators, based on BOARD_XOSC_STARTUP_US. This is written to
|
|
* the two MSB of the XOSCSEL field of OSC.XOSCCTRL.
|
|
*
|
|
* \note This is automatically computed from BOARD_XOSC_HZ and
|
|
* BOARD_XOSC_STARTUP_US if it is not manually set.
|
|
*/
|
|
|
|
//! \name XTAL resonator start-up cycles
|
|
//@{
|
|
#define XOSC_STARTUP_256 0 //!< 256 cycle start-up time
|
|
#define XOSC_STARTUP_1024 1 //!< 1 k cycle start-up time
|
|
#define XOSC_STARTUP_16384 2 //!< 16 k cycle start-up time
|
|
//@}
|
|
|
|
/**
|
|
* \def CONFIG_XOSC_RANGE
|
|
* \brief Board-dependent value that sets the frequency range of the external
|
|
* oscillator. This is written to the FRQRANGE field of OSC.XOSCCTRL.
|
|
*
|
|
* \note This is automatically computed from BOARD_XOSC_HZ if it is not manually
|
|
* set.
|
|
*/
|
|
|
|
//! \name XTAL resonator frequency range
|
|
//@{
|
|
//! 0.4 to 2 MHz frequency range
|
|
#define XOSC_RANGE_04TO2 OSC_FRQRANGE_04TO2_gc
|
|
//! 2 to 9 MHz frequency range
|
|
#define XOSC_RANGE_2TO9 OSC_FRQRANGE_2TO9_gc
|
|
//! 9 to 12 MHz frequency range
|
|
#define XOSC_RANGE_9TO12 OSC_FRQRANGE_9TO12_gc
|
|
//! 12 to 16 MHz frequency range
|
|
#define XOSC_RANGE_12TO16 OSC_FRQRANGE_12TO16_gc
|
|
//@}
|
|
|
|
/**
|
|
* \def XOSC_STARTUP_TIMEOUT
|
|
* \brief Number of us to wait for XOSC to start
|
|
*
|
|
* This is the number of slow clock cycles corresponding to
|
|
* OSC0_STARTUP_VALUE with an additional 25% safety margin. If the
|
|
* oscillator isn't running when this timeout has expired, it is assumed
|
|
* to have failed to start.
|
|
*/
|
|
|
|
// If application intends to use XOSC.
|
|
#ifdef BOARD_XOSC_HZ
|
|
// Get start-up config for XOSC, if not manually set.
|
|
# ifndef CONFIG_XOSC_STARTUP
|
|
# ifndef BOARD_XOSC_STARTUP_US
|
|
# error BOARD_XOSC_STARTUP_US must be configured.
|
|
# else
|
|
//! \internal Number of start-up cycles for the board's XOSC.
|
|
# define BOARD_XOSC_STARTUP_CYCLES \
|
|
(BOARD_XOSC_HZ / 1000000 * BOARD_XOSC_STARTUP_US)
|
|
|
|
# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL)
|
|
# if (BOARD_XOSC_STARTUP_CYCLES > 16384)
|
|
# error BOARD_XOSC_STARTUP_US is too high for current BOARD_XOSC_HZ.
|
|
|
|
# elif (BOARD_XOSC_STARTUP_CYCLES > 1024)
|
|
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_16384
|
|
# define XOSC_STARTUP_TIMEOUT (16384*(1000000/BOARD_XOSC_HZ))
|
|
|
|
# elif (BOARD_XOSC_STARTUP_CYCLES > 256)
|
|
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_1024
|
|
# define XOSC_STARTUP_TIMEOUT (1024*(1000000/BOARD_XOSC_HZ))
|
|
|
|
# else
|
|
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_256
|
|
# define XOSC_STARTUP_TIMEOUT (256*(1000000/BOARD_XOSC_HZ))
|
|
# endif
|
|
# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */
|
|
# define CONFIG_XOSC_STARTUP 0
|
|
# endif
|
|
# endif /* BOARD_XOSC_STARTUP_US */
|
|
# endif /* CONFIG_XOSC_STARTUP */
|
|
|
|
// Get frequency range setting for XOSC, if not manually set.
|
|
# ifndef CONFIG_XOSC_RANGE
|
|
# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL)
|
|
# if (BOARD_XOSC_HZ < 400000)
|
|
# error BOARD_XOSC_HZ is below minimum frequency of 400 kHz.
|
|
|
|
# elif (BOARD_XOSC_HZ < 2000000)
|
|
# define CONFIG_XOSC_RANGE XOSC_RANGE_04TO2
|
|
|
|
# elif (BOARD_XOSC_HZ < 9000000)
|
|
# define CONFIG_XOSC_RANGE XOSC_RANGE_2TO9
|
|
|
|
# elif (BOARD_XOSC_HZ < 12000000)
|
|
# define CONFIG_XOSC_RANGE XOSC_RANGE_9TO12
|
|
|
|
# elif (BOARD_XOSC_HZ <= 16000000)
|
|
# define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16
|
|
|
|
# else
|
|
# error BOARD_XOSC_HZ is above maximum frequency of 16 MHz.
|
|
# endif
|
|
# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */
|
|
# define CONFIG_XOSC_RANGE 0
|
|
# endif
|
|
# endif /* CONFIG_XOSC_RANGE */
|
|
#endif /* BOARD_XOSC_HZ */
|
|
|
|
#ifndef __ASSEMBLY__
|
|
|
|
/**
|
|
* \internal
|
|
* \brief Enable internal oscillator \a id
|
|
*
|
|
* Do not call this function directly. Use osc_enable() instead.
|
|
*/
|
|
static inline void osc_enable_internal(uint8_t id)
|
|
{
|
|
irqflags_t flags;
|
|
|
|
Assert(id != OSC_ID_USBSOF);
|
|
|
|
flags = cpu_irq_save();
|
|
OSC.CTRL |= id;
|
|
#if (XMEGA_E && CONFIG_SYSCLK_RC8MHZ_LPM)
|
|
if(id == OSC_ID_RC8MHZ) {
|
|
OSC.CTRL |= OSC_RC8MLPM_bm;
|
|
}
|
|
#endif
|
|
cpu_irq_restore(flags);
|
|
}
|
|
|
|
#if defined(BOARD_XOSC_HZ) || defined(__DOXYGEN__)
|
|
|
|
/**
|
|
* \internal
|
|
* \brief Enable external oscillator \a id
|
|
*
|
|
* Do not call this function directly. Use osc_enable() instead. Also
|
|
* note that this function is only available if the board actually has
|
|
* an external oscillator crystal.
|
|
*/
|
|
static inline void osc_enable_external(uint8_t id)
|
|
{
|
|
irqflags_t flags;
|
|
|
|
Assert(id == OSC_ID_XOSC);
|
|
|
|
#ifndef CONFIG_XOSC_32KHZ_LPM
|
|
# if (XMEGA_E && (BOARD_XOSC_TYPE == XOSC_TYPE_EXTERNAL) && defined(CONFIG_XOSC_EXTERNAL_PC4))
|
|
OSC.XOSCCTRL = OSC_XOSCSEL4_bm;
|
|
# else
|
|
OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) |
|
|
CONFIG_XOSC_RANGE;
|
|
# endif
|
|
#else
|
|
OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) |
|
|
CONFIG_XOSC_RANGE | OSC_X32KLPM_bm;
|
|
#endif /* CONFIG_XOSC_32KHZ_LPM */
|
|
|
|
flags = cpu_irq_save();
|
|
OSC.CTRL |= id;
|
|
cpu_irq_restore(flags);
|
|
}
|
|
#else
|
|
|
|
static inline void osc_enable_external(uint8_t id)
|
|
{
|
|
Assert(false); // No external oscillator on the selected board
|
|
}
|
|
#endif
|
|
|
|
static inline void osc_disable(uint8_t id)
|
|
{
|
|
irqflags_t flags;
|
|
|
|
Assert(id != OSC_ID_USBSOF);
|
|
|
|
flags = cpu_irq_save();
|
|
OSC.CTRL &= ~id;
|
|
cpu_irq_restore(flags);
|
|
}
|
|
|
|
static inline void osc_enable(uint8_t id)
|
|
{
|
|
if (id != OSC_ID_XOSC) {
|
|
osc_enable_internal(id);
|
|
} else {
|
|
osc_enable_external(id);
|
|
}
|
|
}
|
|
|
|
static inline bool osc_is_ready(uint8_t id)
|
|
{
|
|
Assert(id != OSC_ID_USBSOF);
|
|
|
|
return OSC.STATUS & id;
|
|
}
|
|
|
|
//! \name XMEGA-Specific Oscillator Features
|
|
//@{
|
|
|
|
/**
|
|
* \brief Enable DFLL-based automatic calibration of an internal
|
|
* oscillator.
|
|
*
|
|
* The XMEGA features two Digital Frequency Locked Loops (DFLLs) which
|
|
* can be used to improve the accuracy of the 2 MHz and 32 MHz internal
|
|
* RC oscillators. The DFLL compares the oscillator frequency with a
|
|
* more accurate reference clock to do automatic run-time calibration of
|
|
* the oscillator.
|
|
*
|
|
* This function enables auto-calibration for either the 2 MHz or 32 MHz
|
|
* internal oscillator using either the 32.768 kHz calibrated internal
|
|
* oscillator or an external crystal oscillator as a reference. If the
|
|
* latter option is used, the crystal must be connected to the TOSC pins
|
|
* and run at 32.768 kHz.
|
|
*
|
|
* \param id The ID of the oscillator for which to enable
|
|
* auto-calibration:
|
|
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
|
|
* \param ref_id The ID of the oscillator to use as a reference:
|
|
* \arg \c OSC_ID_RC32KHZ or \c OSC_ID_XOSC for internal or external 32 kHz
|
|
* reference, respectively.
|
|
* \arg \c OSC_ID_USBSOF for 32 MHz only when USB is available and running.
|
|
*/
|
|
static inline void osc_enable_autocalibration(uint8_t id, uint8_t ref_id)
|
|
{
|
|
irqflags_t flags;
|
|
|
|
flags = cpu_irq_save();
|
|
switch (id) {
|
|
case OSC_ID_RC2MHZ:
|
|
#if !XMEGA_E
|
|
Assert((ref_id == OSC_ID_RC32KHZ) || (ref_id == OSC_ID_XOSC));
|
|
if (ref_id == OSC_ID_XOSC) {
|
|
osc_enable(OSC_ID_RC32KHZ);
|
|
OSC.DFLLCTRL |= OSC_RC2MCREF_bm;
|
|
} else {
|
|
OSC.DFLLCTRL &= ~(OSC_RC2MCREF_bm);
|
|
}
|
|
DFLLRC2M.CTRL |= DFLL_ENABLE_bm;
|
|
#endif
|
|
break;
|
|
|
|
case OSC_ID_RC32MHZ:
|
|
#if XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_E
|
|
Assert((ref_id == OSC_ID_RC32KHZ)
|
|
|| (ref_id == OSC_ID_XOSC)
|
|
# if !XMEGA_E
|
|
|| (ref_id == OSC_ID_USBSOF)
|
|
#endif
|
|
);
|
|
|
|
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_gm);
|
|
|
|
if (ref_id == OSC_ID_XOSC) {
|
|
osc_enable(OSC_ID_RC32KHZ);
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_XOSC32K_gc;
|
|
}
|
|
else if (ref_id == OSC_ID_RC32KHZ) {
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_RC32K_gc;
|
|
}
|
|
# if !XMEGA_E
|
|
else if (ref_id == OSC_ID_USBSOF) {
|
|
/*
|
|
* Calibrate 32MRC at 48MHz using USB SOF
|
|
* 48MHz / 1kHz = 0xBB80
|
|
*/
|
|
DFLLRC32M.COMP1 = 0x80;
|
|
DFLLRC32M.COMP2 = 0xBB;
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_USBSOF_gc;
|
|
}
|
|
# endif
|
|
#else
|
|
Assert((ref_id == OSC_ID_RC32KHZ) ||
|
|
(ref_id == OSC_ID_XOSC));
|
|
|
|
# if defined(OSC_RC32MCREF_gm)
|
|
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_gm);
|
|
# endif
|
|
|
|
if (ref_id == OSC_ID_XOSC) {
|
|
osc_enable(OSC_ID_RC32KHZ);
|
|
# if defined(OSC_RC32MCREF_gm)
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_XOSC32K_gc;
|
|
# else
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_bm;
|
|
# endif
|
|
}
|
|
else if (ref_id == OSC_ID_RC32KHZ) {
|
|
# if defined(OSC_RC32MCREF_gm)
|
|
OSC.DFLLCTRL |= OSC_RC32MCREF_RC32K_gc;
|
|
# else
|
|
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_bm);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
DFLLRC32M.CTRL |= DFLL_ENABLE_bm;
|
|
break;
|
|
|
|
default:
|
|
Assert(false);
|
|
break;
|
|
}
|
|
cpu_irq_restore(flags);
|
|
}
|
|
|
|
/**
|
|
* \brief Disable DFLL-based automatic calibration of an internal
|
|
* oscillator.
|
|
*
|
|
* \see osc_enable_autocalibration
|
|
*
|
|
* \param id The ID of the oscillator for which to disable
|
|
* auto-calibration:
|
|
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
|
|
*/
|
|
static inline void osc_disable_autocalibration(uint8_t id)
|
|
{
|
|
switch (id) {
|
|
case OSC_ID_RC2MHZ:
|
|
#if !XMEGA_E
|
|
DFLLRC2M.CTRL = 0;
|
|
#endif
|
|
break;
|
|
|
|
case OSC_ID_RC32MHZ:
|
|
DFLLRC32M.CTRL = 0;
|
|
break;
|
|
|
|
default:
|
|
Assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Load a specific calibration value for the specified oscillator.
|
|
*
|
|
* \param id The ID of the oscillator for which to disable
|
|
* auto-calibration:
|
|
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
|
|
* \param calib The specific calibration value required:
|
|
*
|
|
*/
|
|
static inline void osc_user_calibration(uint8_t id, uint16_t calib)
|
|
{
|
|
switch (id) {
|
|
case OSC_ID_RC2MHZ:
|
|
#if !XMEGA_E
|
|
DFLLRC2M.CALA=LSB(calib);
|
|
DFLLRC2M.CALB=MSB(calib);
|
|
#endif
|
|
break;
|
|
|
|
case OSC_ID_RC32MHZ:
|
|
DFLLRC32M.CALA=LSB(calib);
|
|
DFLLRC32M.CALB=MSB(calib);
|
|
break;
|
|
|
|
#if XMEGA_E
|
|
case OSC_ID_RC8MHZ:
|
|
OSC.RC8MCAL=LSB(calib);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
Assert(false);
|
|
break;
|
|
}
|
|
}
|
|
//@}
|
|
|
|
static inline uint32_t osc_get_rate(uint8_t id)
|
|
{
|
|
Assert(id != OSC_ID_USBSOF);
|
|
|
|
switch (id) {
|
|
case OSC_ID_RC2MHZ:
|
|
return 2000000UL;
|
|
|
|
case OSC_ID_RC32MHZ:
|
|
#ifdef CONFIG_OSC_RC32_CAL
|
|
return CONFIG_OSC_RC32_CAL;
|
|
#else
|
|
return 32000000UL;
|
|
#endif
|
|
|
|
case OSC_ID_RC32KHZ:
|
|
return 32768UL;
|
|
|
|
#ifdef BOARD_XOSC_HZ
|
|
case OSC_ID_XOSC:
|
|
return BOARD_XOSC_HZ;
|
|
#endif
|
|
|
|
default:
|
|
Assert(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif /* __ASSEMBLY__ */
|
|
|
|
//! @}
|
|
|
|
#endif /* XMEGA_OSC_H_INCLUDED */
|