마땅히 새로운게 없어 살펴보고 있는 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); }