마땅히 새로운게 없어 살펴보고 있는 xmega.
쓰면서 느끼지만, 호화스러운 페리페럴들. 럭져리한 경차 같은 느낌이다. atmega와 유사하지만, 레지스터들이 상당히 많아서 효율좋은 코드가 가능할 것 같다.
프로세서는 역시 클럭 셋팅부터. application note avr1003을 참조...라기보다는 걍 거의 그대로 가져다 써서 테스트. 아래 코드로 네가지의 클럭셋팅을 순서대로 돌아가며 사용하였다. 조금 신경을 써야 할 부분은, 페리페럴 클럭들이다. CLKper2와 CLKper4는 고클럭세팅전에 반드시 낮춰놓고 나중에 다시 맞게 세팅해야한다.
/*
* avr1003_dons.c
*
* Created: 2014-08-28 오후 11:23:31
* Author: Don Quixote
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
/* The LED to use for visual feedback. */
#define LEDPORT PORTE
#define LEDMASK 0xFF
/* Which switches to listen to */
#define SWITCHPORT PORTD
#define SWITCHMASK 0xFF
/*! \brief Define the delay_us macro for GCC. */
#define delay_us(us) (_delay_us(us))
/* Definitions of macros. */
/*! \brief This macro enables the selected oscillator.
*
* \note Note that the oscillator cannot be used as a main system clock
* source without being enabled and stable first. Check the ready flag
* before using the clock. The function CLKSYS_IsReady( _oscSel )
* can be used to check this.
*
* \param _oscSel Bitmask of selected clock. Can be one of the following
* OSC_RC2MEN_bm, OSC_RC32MEN_bm, OSC_RC32KEN_bm, OSC_XOSCEN_bm,
* OSC_PLLEN_bm.
*/
#define CLKSYS_Enable(_oscSel) (OSC.CTRL |= (_oscSel))
/*! \brief This macro check if selected oscillator is ready.
*
* This macro will return non-zero if is is running, regardless if it is
* used as a main clock source or not.
*
* \param _oscSel Bitmask of selected clock. Can be one of the following
* OSC_RC2MEN_bm, OSC_RC32MEN_bm, OSC_RC32KEN_bm, OSC_XOSCEN_bm,
* OSC_PLLEN_bm.
*
* \return Non-zero if oscillator is ready and running.
*/
#define CLKSYS_IsReady(_oscSel) (OSC.STATUS & (_oscSel))
/*! \brief This macro will protect the following code from interrupts. */
#define AVR_ENTER_CRITICAL_REGION() uint8_t volatile saved_sreg = SREG; \
cli();
/*! \brief This macro must always be used in conjunction with AVR_ENTER_CRITICAL_REGION
* so the interrupts are enabled again.
*/
#define AVR_LEAVE_CRITICAL_REGION() SREG = saved_sreg;
void clk32m_setup(void);
void timer_setup(void);
void Port_setup(void);
void WaitForSwitches(void);
void CLKSYS_Prescalers_Config(CLK_PSADIV_t PSAfactor,
CLK_PSBCDIV_t PSBCfactor);
uint8_t CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_t clockSource);
uint8_t CLKSYS_Disable(uint8_t oscSel);
void CLKSYS_PLL_Config(OSC_PLLSRC_t clockSource, uint8_t factor);
void CCPWrite(volatile uint8_t* address, uint8_t value);
void clk_32m(void);
void clk_pll30m(void);
void clk_2m(void);
void clk_32k(void);
void clk_pll32m(void);
int main(void)
{
Port_setup();
timer_setup();
/* Enable low interrupt level in PMIC and enable global interrupts. */
PMIC.CTRL |= PMIC_MEDLVLEN_bm;
sei();
while (1) {
WaitForSwitches();
LEDPORT.OUT = ~0x01 & 0x0f;
clk_pll30m();
WaitForSwitches();
LEDPORT.OUT = ~0x02 & 0x0f;
clk_32m();
WaitForSwitches( );
LEDPORT.OUT = ~0x04 & 0x0f;
clk_pll32m();
WaitForSwitches();
LEDPORT.OUT = ~0x08 & 0x0f;
clk_2m();
}
}
/*! Just toggle LED(s) when interrupt occurs. */
ISR(TCC0_OVF_vect)
{
LEDPORT.OUTTGL = 0xf0;
}
void timer_setup(void)
{
/* Set up Timer/Counter 0 to work from CPUCLK/64, with period 10000 and
* enable overflow interrupt.
*/
TCC0.PER = 20000;
TCC0.CTRLA = ( TCC0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_DIV64_gc;
TCC0.INTCTRLA = ( TCC0.INTCTRLA & ~TC0_OVFINTLVL_gm )
| TC_OVFINTLVL_MED_gc;
}
void clk32m_setup(void)
{
// enable 32Mhz internal osc.
OSC.CTRL |= OSC_RC32MEN_bm;
// check whether the 32Mhz internal clock
while ((OSC.STATUS & OSC_RC32MRDY_bm) == 0);
}
/*
* setup LED port and switch port
*/
void Port_setup(void)
{
/* Set up user interface. */
LEDPORT.DIRSET = LEDMASK;
LEDPORT.OUTSET = 0xf7;
/* PORTC pin7 set to output */
PORTC.DIRSET = 0x80;
/* clkout on portc */
PORTCFG.CLKEVOUT |= 0x01;
SWITCHPORT.DIRCLR = SWITCHMASK; // set input
SWITCHPORT.PIN0CTRL = (SWITCHPORT.PIN0CTRL & ~PORT_OPC_gm)
| PORT_OPC_PULLUP_gc;
}
void waitBtnPressed(void)
{
while (1) {
if ((SWITCHPORT.IN & 0x01) == 0x00) {
_delay_ms(10);
if ((SWITCHPORT.IN & 0x01) == 0x00)
return;
}
}
}
void waitBtnRelesed(void)
{
while (1) {
if ((SWITCHPORT.IN & 0x01) == 0x01) {
_delay_ms(10);
if ((SWITCHPORT.IN & 0x01) == 0x01)
return;
}
}
}
/*! \brief This function waits for a button push and release before proceeding.
*/
void WaitForSwitches(void)
{
waitBtnPressed();
waitBtnRelesed();
/*
while ((SWITCHPORT.IN & 0x01) == 0x01);
_delay_ms(10);
while ((SWITCHPORT.IN & 0x01) == 0x00);
_delay_ms(10);
*/
}
/*! \brief This function changes the prescaler configuration.
*
* Change the configuration of the three system clock
* prescaler is one single operation. The user must make sure that
* the main CPU clock does not exceed recommended limits.
*
* \param PSAfactor Prescaler A division factor, OFF or 2 to 512 in
* powers of two.
* \param PSBCfactor Prescaler B and C division factor, in the combination
* of (1,1), (1,2), (4,1) or (2,2).
*/
void CLKSYS_Prescalers_Config(CLK_PSADIV_t PSAfactor,
CLK_PSBCDIV_t PSBCfactor)
{
uint8_t PSconfig = (uint8_t) PSAfactor | PSBCfactor;
CCPWrite(&CLK.PSCTRL, PSconfig);
}
/*! \brief This function selects the main system clock source.
*
* Hardware will disregard any attempts to select a clock source that is not
* enabled or not stable. If the change fails, make sure the source is ready
* and running and try again.
*
* \param clockSource Clock source to use as input for the system clock
* prescaler block.
*
* \return Non-zero if change was successful.
*/
uint8_t CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_t clockSource)
{
uint8_t clkCtrl = (CLK.CTRL & ~CLK_SCLKSEL_gm) | clockSource;
CCPWrite(&CLK.CTRL, clkCtrl);
clkCtrl = (CLK.CTRL & clockSource);
return clkCtrl;
}
/*! \brief This function disables the selected oscillator.
*
* This function will disable the selected oscillator if possible.
* If it is currently used as a main system clock source, hardware will
* disregard the disable attempt, and this function will return zero.
* If it fails, change to another main system clock source and try again.
*
* \param oscSel Bitmask of selected clock. Can be one of the following
* OSC_RC2MEN_bm, OSC_RC32MEN_bm, OSC_RC32KEN_bm,
* OSC_XOSCEN_bm, OSC_PLLEN_bm.
*
* \return Non-zero if oscillator was disabled successfully.
*/
uint8_t CLKSYS_Disable(uint8_t oscSel)
{
OSC.CTRL &= ~oscSel;
uint8_t clkEnabled = OSC.CTRL & oscSel;
return clkEnabled;
}
/*! \brief This function configures the internal high-frequency PLL.
*
* Configuration of the internal high-frequency PLL to the correct
* values. It is used to define the input of the PLL and the factor of
* multiplication of the input clock source.
*
* \note Note that the oscillator cannot be used as a main system clock
* source without being enabled and stable first. Check the ready flag
* before using the clock. The macro CLKSYS_IsReady( _oscSel )
* can be used to check this.
*
* \param clockSource Reference clock source for the PLL,
* must be above 0.4MHz.
* \param factor PLL multiplication factor, must be
* from 1 to 31, inclusive.
*/
void CLKSYS_PLL_Config(OSC_PLLSRC_t clockSource, uint8_t factor)
{
factor &= OSC_PLLFAC_gm;
OSC.PLLCTRL = (uint8_t)clockSource | (factor << OSC_PLLFAC_gp);
}
/*! \brief CCP write helper function written in assembly.
*
* This function is written in assembly because of the timecritial
* operation of writing to the registers.
*
* \param address A pointer to the address to write to.
* \param value The value to put in to the register.
*/
void CCPWrite(volatile uint8_t* address, uint8_t value)
{
AVR_ENTER_CRITICAL_REGION( );
volatile uint8_t* tmpAddr = address;
#ifdef RAMPZ
RAMPZ = 0;
#endif
asm volatile(
"movw r30, %0" "\n\t"
"ldi r16, %2" "\n\t"
"out %3, r16" "\n\t"
"st Z, %1" "\n\t"
:
: "r" (tmpAddr), "r" (value), "M" (CCP_IOREG_gc), "i" (&CCP)
: "r16", "r30", "r31"
);
AVR_LEAVE_CRITICAL_REGION( );
}
void clk_32m(void)
{
/* Enable internal 32 MHz ring oscillator and wait until it's
* stable. Divide clock by two with the prescaler C and set the
* 32 MHz ring oscillator as the main clock source. Wait for
* user input while the LEDs toggle.
*/
CLKSYS_Enable(OSC_RC32MEN_bm);
while (CLKSYS_IsReady(OSC_RC32MRDY_bm) == 0);
CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_RC32M_gc);
CLKSYS_Prescalers_Config(CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc);
CLKSYS_Disable(OSC_PLLEN_bm | OSC_XOSCEN_bm | OSC_RC32KEN_bm | OSC_RC2MEN_bm);
}
void clk_pll30m(void)
{
/* Configure PLL with the 2 MHz RC oscillator as source and
* multiply by 30 to get 60 MHz PLL clock and enable it. Wait
* for it to be stable and set prescaler C to divide by two
* to set the CPU clock to 30 MHz. Disable unused clock and
* wait for user input.
*/
CLKSYS_PLL_Config(OSC_PLLSRC_RC2M_gc, 30);
CLKSYS_Enable(OSC_PLLEN_bm);
while (CLKSYS_IsReady(OSC_PLLRDY_bm) == 0);
CLKSYS_Prescalers_Config(CLK_PSADIV_2_gc, CLK_PSBCDIV_2_2_gc);
CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_PLL_gc);
CLKSYS_Prescalers_Config(CLK_PSADIV_1_gc, CLK_PSBCDIV_1_2_gc);
CLKSYS_Disable(OSC_XOSCEN_bm | OSC_RC32KEN_bm | OSC_RC32MEN_bm);
}
void clk_pll32m(void)
{
CLKSYS_Enable(OSC_RC32MEN_bm);
while (CLKSYS_IsReady(OSC_RC32MRDY_bm) == 0);
CLKSYS_Prescalers_Config(CLK_PSADIV_2_gc, CLK_PSBCDIV_2_2_gc);
CLKSYS_PLL_Config(OSC_PLLSRC_RC32M_gc, 16);
CLKSYS_Enable(OSC_PLLEN_bm);
while (CLKSYS_IsReady(OSC_PLLRDY_bm) == 0);
CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_PLL_gc);
CLKSYS_Prescalers_Config(CLK_PSADIV_1_gc, CLK_PSBCDIV_2_2_gc);
CLKSYS_Disable(OSC_XOSCEN_bm | OSC_RC32KEN_bm | OSC_RC2MEN_bm);
}
void clk_2m(void)
{
/* Select 2 MHz RC oscillator as main clock source and diable
* unused clock.
*/
CLKSYS_Enable(OSC_RC2MEN_bm);
while (CLKSYS_IsReady(OSC_RC2MRDY_bm) == 0);
CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_RC2M_gc);
CLKSYS_Prescalers_Config(CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc);
CLKSYS_Disable(OSC_PLLEN_bm | OSC_XOSCEN_bm | OSC_RC32KEN_bm | OSC_RC32MEN_bm);
}
void clk_32k(void)
{
/* Enable internal 32 kHz calibrated oscillator and check for
* it to be stable and set prescaler A, B and C to none. Set
* the 32 kHz oscillator as the main clock source. Wait for
* user input while the LEDs toggle.
*/
CLKSYS_Enable(OSC_RC32KEN_bm);
while (CLKSYS_IsReady(OSC_RC32KRDY_bm) == 0);
CLKSYS_Main_ClockSource_Select(CLK_SCLKSEL_RC32K_gc);
CLKSYS_Disable(OSC_XOSCEN_bm | OSC_PLLEN_bm);
CLKSYS_Prescalers_Config(CLK_PSADIV_1_gc, CLK_PSBCDIV_1_1_gc);
}
