/** * \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 Atmel Support */ #ifndef XMEGA_OSC_H_INCLUDED #define XMEGA_OSC_H_INCLUDED #include #include /** * \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 */