Xmega32a4u USB HID using external 16MHz crystal

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi All, we are currently developing a program using the ASF example for USB HID. This example uses the internal 32MHz oscillator and adjusts the calibration to be 48MHz needed for USB. The code is below.

 

Is is possible to use an external crystal to do the same job?

 


void sysclk_init(void)
{
	//uint8_t *reg = (uint8_t *)&PR.PRGEN;
	uint8_t i;
#ifdef CONFIG_OSC_RC32_CAL
	uint16_t cal;
	/* avoid Cppcheck Warning */
	UNUSED(cal);
#endif
	bool need_rc2mhz = false;

	/* Turn off all peripheral clocks that can be turned off. */
	for (i = 0; i <= SYSCLK_PORT_F; i++) {
	//	*(reg++) = 0xff;
	}

	/* Set up system clock prescalers if different from defaults */
	if ((CONFIG_SYSCLK_PSADIV != SYSCLK_PSADIV_1)
			|| (CONFIG_SYSCLK_PSBCDIV != SYSCLK_PSBCDIV_1_1)) {
		sysclk_set_prescalers(CONFIG_SYSCLK_PSADIV,
				CONFIG_SYSCLK_PSBCDIV);
	}
#if (CONFIG_OSC_RC32_CAL==48000000UL)
	MSB(cal) = nvm_read_production_signature_row(
			nvm_get_production_signature_row_offset(USBRCOSC));
	LSB(cal) = nvm_read_production_signature_row(
			nvm_get_production_signature_row_offset(USBRCOSCA));
	/*
	* If a device has an uncalibrated value in the
	* production signature row (early sample part), load a
	* sane default calibration value.
	*/
	if (cal == 0xFFFF) {
		cal = 0x2340;
	}
	osc_user_calibration(OSC_ID_RC32MHZ,cal);
#endif
	/*
	 * Switch to the selected initial system clock source, unless
	 * the default internal 2 MHz oscillator is selected.
	 */
	if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_RC2MHZ) {
		need_rc2mhz = true;
	} else {
		switch (CONFIG_SYSCLK_SOURCE) {
		case SYSCLK_SRC_RC32MHZ:
			osc_enable(OSC_ID_RC32MHZ);
			osc_wait_ready(OSC_ID_RC32MHZ);
#ifdef CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC
			if (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC
					!= OSC_ID_USBSOF) {
				osc_enable(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
				osc_wait_ready(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
			}
			osc_enable_autocalibration(OSC_ID_RC32MHZ,
					CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
#endif
			break;

		case SYSCLK_SRC_RC32KHZ:
			osc_enable(OSC_ID_RC32KHZ);
			osc_wait_ready(OSC_ID_RC32KHZ);
			break;

		case SYSCLK_SRC_XOSC:
			osc_enable(OSC_ID_XOSC);
			osc_wait_ready(OSC_ID_XOSC);
			break;

#ifdef CONFIG_PLL0_SOURCE
		case SYSCLK_SRC_PLL:
			if (CONFIG_PLL0_SOURCE == PLL_SRC_RC2MHZ) {
				need_rc2mhz = true;
			}
			pll_enable_config_defaults(0);
			break;
#endif
#if XMEGA_E
		case SYSCLK_SRC_RC8MHZ:
			osc_enable(OSC_ID_RC8MHZ);
			osc_wait_ready(OSC_ID_RC8MHZ);
			break;
#endif
		default:
			//unhandled_case(CONFIG_SYSCLK_SOURCE);
			return;
		}

		ccp_write_io((uint8_t *)&CLK.CTRL, CONFIG_SYSCLK_SOURCE);
		Assert(CLK.CTRL == CONFIG_SYSCLK_SOURCE);
	}

	if (need_rc2mhz) {
#ifdef CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC
		osc_enable(CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC);
		osc_wait_ready(CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC);
		osc_enable_autocalibration(OSC_ID_RC2MHZ,
				CONFIG_OSC_AUTOCAL_RC2MHZ_REF_OSC);
#endif
	} else {
		osc_disable(OSC_ID_RC2MHZ);
	}

#ifdef CONFIG_RTC_SOURCE
	sysclk_rtcsrc_enable(CONFIG_RTC_SOURCE);
#endif
}

 

Electronic System Design
http://www.esdn.com.au

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know about the 16MHz crystal.   A 32kHz "watch crystal" can be used with DFLL.  The internal 32kHz RC osc. and be used as well, but you may have to adjust it if the factory calibration is off.  In any case, using the 32 MHz RC osc. requires DFLL because the 32 MHz osc. is just a plain old inaccurate wobbly RC osc.

 

The built in 32kHz RC osc. is very good for an RC osc. but of course not as good as a crystal.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You will be able to use a 16MHz crystal as a USB clock source on the route shown below.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does HID use slow or full speed USB?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using the ASF then check config/conf_clock.h. You need to enable  the PLL with the 16MHz crystal as source and 3x multiplier to give 48MHz. Then set the PLL as the source for the USB clock.

 

// PLL config, 48MHz
#define CONFIG_PLL0_SOURCE          PLL_SRC_XOSC
#define CONFIG_PLL0_MUL             3
#define CONFIG_PLL0_DIV             1

// External oscillator frequency range
#define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16

// USB clock
#define CONFIG_USBCLK_SOURCE                USBCLK_SRC_PLL

// CPU clock, 16MHz
#define CONFIG_SYSCLK_SOURCE				SYSCLK_SRC_XOSC
#define CONFIG_SYSCLK_PSADIV				SYSCLK_PSADIV_1
#define CONFIG_SYSCLK_PSBCDIV				SYSCLK_PSBCDIV_1_1

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

Does HID use slow or full speed USB?

 

Either.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks mojo-chan. That is just what we needed. We are using the USB in full speed - the slow speed had some issues from memory.

 

Here is the code below which replaces the ASF function sysclk_init();

 

\// PLL config, 48MHz
#define CONFIG_PLL0_SOURCE          PLL_SRC_XOSC
#define CONFIG_PLL0_MUL             3
#define CONFIG_PLL0_DIV             1

// External oscillator frequency range
#define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16

// USB clock
#define CONFIG_USBCLK_SOURCE                USBCLK_SRC_PLL

// CPU clock, 16MHz
#define CONFIG_SYSCLK_SOURCE				SYSCLK_SRC_XOSC
#define CONFIG_SYSCLK_PSADIV				SYSCLK_PSADIV_2
#define CONFIG_SYSCLK_PSBCDIV				SYSCLK_PSBCDIV_1_2

void setup_48MHz_clock(void);
void setup_48MHz_clock(void)
{
    CCP=0xD8;    // Enable the clock source
    OSC.XOSCCTRL = XOSC_RANGE_12TO16|7; // enable 14MHz

    CCP=0xD8;    // Enable the clock source
    OSC.CTRL = OSC_XOSCEN_bm; // enable
    while ((OSC.STATUS & OSC_XOSCRDY_bm)==0) {} // wait for it to become stable

    OSC.PLLCTRL = PLL_SRC_XOSC | CONFIG_PLL0_MUL ; // 0x20 = PLLDIV

    OSC.CTRL |= OSC_PLLEN_bm ; // enable the PLL...
    while( (OSC.STATUS & OSC_PLLRDY_bm) == 0 ){} // wait until it's stable

    CCP=0xD8;
    //CLK.CTRL = CLK_SCLKSEL_XOSC_gc;
    CLK.CTRL = CLK_SCLKSEL_PLL_gc;	// The System clock is now  PLL (16Mhz / 2)
	
	if ((CONFIG_SYSCLK_PSADIV != SYSCLK_PSADIV_1)
			|| (CONFIG_SYSCLK_PSBCDIV != SYSCLK_PSBCDIV_1_1)) {
	//			CLK.PSCTRL
		sysclk_set_prescalers(CONFIG_SYSCLK_PSADIV,CONFIG_SYSCLK_PSBCDIV);	
			}
}

 

Electronic System Design
http://www.esdn.com.au