카테고리 없음2014. 9. 1. 11:59


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



Posted by 쿨한넘