Go Down

Topic: How to set XMEGA clock (Read 278 times) previous topic - next topic

Zigmund

Would anyone have an example code of how to change the clock source/speed on XMEGA processors? The MCU starts up with 2 MHz internal clock; let's say I want to switch to 32 MHz internal clock.

westfw


Zigmund

Oh, from the manuals I had an impression that they are all handling the clock the same way...

At the moment I am after 128A4U.

westfw

#3
Apr 21, 2017, 11:26 am Last Edit: Apr 21, 2017, 11:30 am by westfw
Quote
from the manuals I had an impression that they are all handling the clock the same way...
Could be.  But I would have had to look at all of them to be sure :-)
I was hoping it would be similar to one of the SAM clock systems, which I'm somewhat more familiar with, but apparently not...

You've probably noticed that the 128A4U datasheet is useless; what you need is the Xmega AU family datasheet
Using the internal 32MHz oscillator looks pretty easy:

Code: [Select]
   OSC->CTRL |= OSC_RC32MEN_bm;    // Enabled the internal 32MHz oscillator
   while ((OSC->STATUS & OSC_RC32MRDY_bm) == 0)
      ;  // wait for oscillator to finish starting.
   CPU_CCP = CCP_IOREG_gc;  // tickle the Configuration Change Protection Register
   CLK->CTRL = CLK_SCLKSEL_RC32M_gc;   // select the 32MHz oscillator as system clock.


(This is just a rough guess, and it might take some additional work.  In particular, the final clock setting has to happen within 4 clocks (or something like that) of the CCP write, which might take some additional care.  (oh - there's an avr-libc function "PROTECTED_WRITE" in avr/xmega.h that looks like it does the right thing!  How handy))

If you're using asf (somehow?), there's probably an ASF call to change the clock that uses 20x more code than the above snippet.  But it might be needed so that other ASF functions can figure out what the current clock speed is.

(and yes, it looks like all of the XMEGAs have the same clock system.  At least, the XMEGA-E (at the other end of the spectrum, for the Xmegas) seems to have the same stuff as the XMEGA-A)

Zigmund

Thanks for the code, a good starting point. It failed to compile, pointing to OSC->CTRL, with the comment "base operand of '->' has non-pointer type OCS_t (aka OCS_struct)'. Here I need to mention that I try really hard to not code in C++ whenever possible; working with pointers makes me sad :)

As for the range of devices, my impression that all XMEGAs share the clock setting came from "Atmel AVR1003: Using the XMEGA Clock System"

westfw

Quote
working with pointers makes me sad
That's not good.   In a nod to "modern practices", nearly all the peripherals are described as structures.
Hmm.  Looks like the individual symbols are dereferenced structures, which probably means all of my "->" should have just been "."  (PS: these are standard C pointers; nothing to do with C++)

It does look like there are old-fashioned per-register definitions as well:
Code: [Select]
   OSC_CTRL |= OSC_RC32MEN_bm;    // Enabled the internal 32MHz oscillator
   while ((OSC_STATUS & OSC_RC32MRDY_bm) == 0)
      ;  // wait for oscillator to finish starting.
   PROTECTED_WRITE(CLK_CTRL, CLK_SCLKSEL_RC32M_gc);  // select the 32MHz oscillator as system clock.



Zigmund

It compiles now, with one detail: PROTECTED_WRITE seems to need a leading underscore, as in _PROTECTED_WRITE (explained here)

I tried to time the execution of single analogRead(0), averaged over 1000 executons, and got 72 microseconds, both with and without the clock setting code, so I am guessing the clock did not change :(

westfw

analogRead() would usually be limited by the speed of the ADC, which has a separate clock.

Are you using an existing Xmega Core (if so, which one?  And which board?), or writing new code from scratch?

Zigmund

I'm using XmegaForArduino off GitHub. I like it because it is active, and it let's you use regular, updatable Arduino IDE, unlike, for instance, Xmegaduino. The board is my own, using 128A4U; It exposes all the MCU pins, and all the ports; I've offered my board pins file on the GitHub project issues forum.

You are probably right on AnalogRead not being good for looking up the MCU speed. How is digitalWrite instead? Like the following, in setup():

Code: [Select]

   OSC_CTRL |= OSC_RC32MEN_bm;
   while ((OSC_STATUS & OSC_RC32MRDY_bm) == 0);
   _PROTECTED_WRITE(CLK_CTRL, CLK_SCLKSEL_RC32M_gc);

   pinMode(LED_BUILTIN, OUTPUT);
   time0 = micros();
   for (int i=0; i<1000; i++)
   {
      digitalWrite(LED_BUILTIN, LOW);
   }
   newTime = micros();

   Serial.print(newTime-time0);


With this code the output is 3178, and I get the same with the first three lines commented out (funny it's 3116 when writing HIGH).

westfw

If you're using a "core", I'd strongly suspect that the clock is already set to run at the maximum possible speed, rather than the 2MHz "default."

Yeah: https://github.com/XMegaForArduino/arduino/blob/master/cores/xmega/wiring.c#L375
Set to the 32MHz clock, with automatic calibration from the (higher accuracy) 32kHz internal clock, I think.
I suppose that it's possible that this code doesn't work, somehow leaving the chip running at 2MHz.  Do you have actual evidence that the chip is not running "full speed"?

Zigmund

Oh, what a royal waste of time... You are right, I should have tried setting the clock to 32 kHz and 2 MHz too. Will do that in a bit. I started the whole clock inquiry because of a suspiciously slow writing to an I2C 7-seg display, where I can see the digits changing even when writing digits 0 to 9 at full speed, which was not the case when the display was connected to Mega or Nano. Will post in a bit the timing when setting the clock to 32 kHz and 2 MHz; the source of my I2C slow writing on XMega might be somewhere else.

westfw

Quote
what a royal waste of time
Oh, it's not that bad.  *I* learned something about the xMega clock system, so I have no complaints about the time I spent...  :-)

Zigmund

I (indirectly) confirmed that my XMEGA 128A4U is running at 32 MHz, without any code that deals with the clock, just based on XmegaForArduino cores. I run a simple code:

Code: [Select]
   unsigned long timeStart = micros();
   for (byte j=0; j<10; j++)
   {
      for (byte i=0; i<10; i++)
      {
         digitalWrite(LED_BUILTIN,HIGH;
         digitalWrite(LED_BUILTIN,LOW;
      }
   }
   unsigned long timeEnd = micros();
   Serial.println(timeEnd-timeStart);


The XMEGA returned 618 microseconds, while Mega2560, running at 16 MHz, did it in 1216 microseconds

The issue that originally brought me to question the clock was writing to the TWI port, which (loop of 100) on Mega2560 completes in 33 milliseconds, while on XMEGA takes almost 8 _seconds_, but reports (?????) 76 milliseconds. Don't reply here, I'll start another thread with that.

Go Up