Xmega32a4u USB HID using external 16MHz crystal

Go To Last Post
16 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

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

Hi, i have similar problem with setting the oscillator. When I access the registers directly (effectively going behind ASF's back) I can set the clock but doing so breaks most of the modules in ASF. (TCs and USART baud rates for example are 16 times faster, etc.)

 

I tried to set BOARD_XOSC_HZ but that doesn't seem to help at all.

 

When I'm creating the project I choose ASF Board Project and select ATxmega32A4U / "User Board template - XMEGA AU".

From the ASF Wizard I add CPU specific features (driver) & System Clock Control (Service)

 

Which headers should I edit to set the oscillator properly? (project Structure in attachment!)

 

Attachment(s): 

Last Edited: Wed. Dec 6, 2017 - 03:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WHat's wrong with using the ASF to configure the clock again?

 

Actually I can answer that, it disables all your peripheral clocks. I normally comment that bit out but the actual main CPU and USB clock setting stuff works just fine IME.

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

I'm just getting started with ASF and now I'm kind of confused... how do I enable the peripheral clocks then? 

 

sysclk_enable_usb()

sysclk_enable_module()

sysclk_enable_peripheral_clock()

 

Are these what I'm looking for?

 

EDIT: Or should I delete this part from sysclk.c:

 

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

 

(this doesn't seem to be the nicely abstracted solution I was looking for) frown

Last Edited: Wed. Dec 6, 2017 - 10:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@sukuwc said:

how do I enable the peripheral clocks then? 

When you use ASF code to initialize the peripherals, the clocks are turned on for you.

Or should I delete this part from sysclk.c:

That will enable the clocks to all peripherals, which will consume more power than just enabling the peripherals you are using.

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

I just delete that code you cited, yeah. The ASF will enable any clocks it needs, it's only your own code that is affected.

 

99 times out of 100 when a peripheral doesn't work it's because I forgot to enable its clock again. I don't bother turning them off on USB powered devices, because it only saves a few microamps max.

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

I'd be happy to let the ASF modules enable the peripheral clocks, but why does this not work then?

I thought ioport_init() would enable the clocks needed for the GPIO but that does not seem to be the case.

However, if i remove the code from ASF that disables all the peripheral clocks then everything works just fine...

#include <asf.h>

int main (void)
{

	sysclk_init();

	board_init();

	ioport_init();

	ioport_set_pin_dir(IOPORT_CREATE_PIN(PORTA,0), IOPORT_DIR_OUTPUT);
	
	while(1){
		
		ioport_set_pin_high(IOPORT_CREATE_PIN(PORTA,0));
		_delay_ms(1);
		ioport_set_pin_low(IOPORT_CREATE_PIN(PORTA,0));
		_delay_ms(1);
		
		
        }
}

 

 

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

There is no power reduction bit(s) for IOPORTs:

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

And if one looks through the ASF layers you will find that ioport_init() generates no code:

 

ioport.h:

static inline void ioport_init(void)
{
	arch_ioport_init();
}

xmega/ioport.h

__always_inline static void arch_ioport_init(void)
{
}

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

Thanks for the helpful reply! cheers.