Unstable clock on ATXMEGA128A4

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

Hello,

I used following code for using external 16Mhz Crystal and setup Timer to generate signal to be 1Mhz:

 

 

#define F_CPU 32000000UL

#include <avr/io.h>
#include <avr/interrupt.h>

volatile int32_t CPU_count=0;

ISR(TCC0_OVF_vect)
{

    CPU_count++;
}

 

void micros(uint32_t doba)
{
    int32_t start_time,end_time;
    start_time=CPU_count;
    end_time=start_time+doba;

    while(end_time>CPU_count);
}

 

int crystal_init_32Mhz(void)
{

       OSC.XOSCCTRL = OSC_FRQRANGE_12TO16_gc | OSC_XOSCSEL_XTAL_16KCLK_gc;

       OSC.CTRL |= OSC_XOSCEN_bm;
       while (!(OSC.STATUS & OSC_XOSCRDY_bm));

       CCP = CCP_IOREG_gc;
       CLK.PSCTRL = CLK_PSADIV_1_gc | CLK_PSBCDIV_1_1_gc;

 

      //PLL      
       OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | (OSC_PLLFAC_gm & 2);
       OSC.CTRL |= OSC_PLLEN_bm;
       while (!(OSC.STATUS & OSC_PLLRDY_bm))  ;
       CCP = CCP_IOREG_gc;
       CLK.CTRL = CLK_SCLKSEL_gm & CLK_SCLKSEL_PLL_gc;

       OSC.CTRL &= ~(OSC_RC2MEN_bm | OSC_RC32MEN_bm | OSC_RC32KEN_bm);

return 0;    
}

 

int timer_1Mhz(void)
{
 

TCC0_CTRLB = TC_WGMODE_SS_gc;

TCC0_CTRLA = TC_CLKSEL_DIV1_gc;

TCC0_PER   = 31;// sets top for period of 1  usec

TCC0_INTCTRLA = TC_OVFINTLVL_LO_gc;

sei();

PMIC_CTRL |= PMIC_LOLVLEN_bm;

micros(1000);

return 0;    
}

 

int main(void)
{

     crystal_init_32Mhz();
     timer_1Mhz();
      
    
    //Pin 32 direction
    PORTE_DIR |= 4;
    micros(10000);
    
    
        
    while (1)
    {
    PORTE_OUT |= 4;
    micros(500);
    PORTE_OUT &= 0b11111011;
    micros(500);
    }
}

 

 

My expectation is generate 1uS impulse, which will serve as source for function micros();. I connected it to osciloscope, but i got uneven signal (as per attachment). It is because of instability of clock?

Crystal used is 16Mhz,+/-10bpm, CL=7pF. Also attaching details from PCB. Processor is supply with 3.3V.

 

C13- 10pF, C11 - 12pF.

Crystal location

 

Connection with Osciloscope is grounded via 10k resistor.

 

And signal from Osciloscope. Scale is 500uS.

Uneven signal from pin.

 

Appreciate some hints, which can forward me toward resolving an issue.

 

Thx

 

Jozef

 

Last Edited: Mon. Jun 3, 2019 - 02:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why is F_CPU showing 32 MHz even though it is 16 MHz operating?
ISR occurs every 16 clocks, but it is not in time.
Perform atomic processing to obtain CPU_count.
There are more ...

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

I'm sorry. I overlooked the use of PLL.

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

How sure are you about your scope's ability to resolve signals at that timebase?

 

Another way to measure "accuracy" would be to take a long view of things. Maybe set the code to increment CPU_count from 1 to a million or whatever. Take an IO line high and the start (or pulse it) then low (or second pulse) at the end. Measure the time between the two events - how close to an accurate measurement of 1,000,000 (or however many) events is that?

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

Please explain exactly what you want to do.
.
It is fairly simple to set up a specific Xmega clock. There is little point in providing clocks that you do'nt use.
I use 2MHz RC with the PLL to produce 8, 10, 12...60, 62MHz
Yes, the Xmega runs fine at 62MHz.
.
If you want accuracy an external crystal is best.
.
Oh, a Logic Analyser or Digital scope has a finite sampling time. A narrow or wide pulse is not unusual.

Last Edited: Mon. Jun 3, 2019 - 03:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Dawson,

I will try to test, hopefully will resolve it. I must admit, my scope is 20Mhz USB based, maybe precision is in question.

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

Hi David,

I'm trying to get precise clock for 1Mhz signal, want to used as time base for different purposes (one of it is control of servo). Since first time I used timer as well as external clock, just want to be sure,

that what I'm seeing on scope is not result of wrong design  but rather instrument limitation.

 

Thx

 

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

There are more basic issues.

 

This interrupt occurs every 32 clocks, but this interrupt processing does not finish within 32 clocks.
The advance of the count will be slower than 1 us.

ISR(TCC0_OVF_vect)
{
    CPU_count++;
}

 

It takes 4 instructions to get the CPU_count, but if the CPU_count is updated due to an interrupt on the way, the obtained value will be invalid. Interrupts must be disabled during acquisition.
Furthermore, the while statement does not work properly if CPU_count overflows.
It should be as follows.

void micros(uint32_t doba)
{
    int32_t start_time, end_time;

    cli();
    start_time=CPU_count;
    sei();
    while(1)
    {
        cli();
        end_time = CPU_count;
        sei();
        if (end_time - start_time >= doba)    break;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Remarkable.. Thx you kabasan. this will help me a lot.. Small question, if I can ask, i'm new in Atmel studio, where I can find out how many instructions each command will take?

 

Thx

 

J.

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


jozefvelky wrote:
, where I can find out how many instructions each command will take?

In the fine data sheet!

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Run Start Debugging and Break (Alt + F5) in simulator mode in Atmel studio.
Open Disassembly (Alt + F8) and set breakpoints at the beginning and end of the ISR.
This will tell you the number of clocks consumed by the Cycle Counter at each breakpoint.
The result is 60 clocks, but also adds interrupt response and return.

 

 

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

I'm confused... (Again...)

 

You stated you wanted to set up a Timer/Counter for a 1 MHz signal.

And you stated you wanted a 1 uSec pulse.

 

That gives a signal that is always high???

 

I also think you want to generate a signal the is present on an I/O pin to drive an external device.

 

Does the exact width of the 1 MHz pulse matter?

 

I don't read C, but I see interrupts being discussed.

 

Why are you using interrupts at all for this?

 

Would it be possible to set up a Timer/Counter in PWM mode to generate a signal on an I/O pin at exactly 1 MHz?

 

This would run independently of the processor core, and would not have any ISR overhead or jitter.

 

For a relatively fast signal, (1 MHz), for an AVR I'd run the XMega at 32 MHz for the extra processing clocks if you are using interrupts.

For a hardware only solution that actually doesn't matter.

 

If the Clock is 32 MHz, set the Pre-Scaler to Divide by 1 for a 32 MHz clock into the T/C module.

 

Set the period to 32 for a 1 MHz signal period.

(Or perhaps to 32 - 1 to count for roll over?)

 

Then set CCx for whatever pulse width duration you want for the signal on the I/O pin.

 

Now the I/O pin will generate a 1 Mhz pulse train without any processor core intervention.

 

JC  

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

Thx Jim for new information. I missed these last chapters on the end.

 

J.

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

Regarding Atmel Studio, these are information I was looking for. I was aware of simulation, but never went deeper into. Is making my life easier.

 

Thx again.

 

 

J.

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

Hello JC,

I understand that my explanation for problem doesn't have clarity. My terminus technicus need some adjustment...

Regarding needs for my project, I'm controlling servo, which is impulse 20ms long and depend how many uS is signal high, that is controlling it. Usually is between 700-2500 uS. That is main reason for counter.

I can setup TC to generate it, but is good only for 1 impulse, so each next impulse will be different. I was seeing as possible complication setup timer each impulse so I thought simpler will be just hold signal high so many uS (hence need for 1uS measurment).

Idea was to setup TC the way that will run with 1uS increment. I will read counter  to determine how many uS passed. When I browse internet for solution how to measure time I found one mentioned before and stick with.

Thank you for pointing me to proper direction.

 

J.

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

This is a supplement.
xmega has many great TCs. It is not difficult to realize an accurate 1us tick if you use it well.

One TC divides 32 MHz into 1 MHz, and the output is counted by two cascaded TCs. This does not require CPU resources or interrupts.

 

 

 

Try replacing your two functions. And ISR is no longer needed.
I believe you can get a relatively stable waveform.

 

void timer_1Mhz(void){
    EVSYS.CH0MUX = EVSYS_CHMUX_TCD0_OVF_gc;
    EVSYS.CH1MUX = EVSYS_CHMUX_TCC0_OVF_gc;

    TCC0.PER = 0xFFFF;
    TCC1.PER = 0xFFFF;
    TCC0.CTRLD = TC_EVACT_CAPT_gc | TC_EVSEL_CH2_gc;
    TCC1.CTRLD = TC_EVACT_CAPT_gc | TC_EVSEL_CH2_gc | TC1_EVDLY_bm;
    TCC0.CTRLB = TC0_CCAEN_bm | TC_WGMODE_NORMAL_gc;
    TCC1.CTRLB = TC1_CCAEN_bm | TC_WGMODE_NORMAL_gc;
    TCC0.CTRLA = TC_CLKSEL_EVCH0_gc;
    TCC1.CTRLA = TC_CLKSEL_EVCH1_gc;

    TCD0.PER = 31;
    TCD0.CTRLB = TC_WGMODE_NORMAL_gc;
    TCD0.CTRLA = TC_CLKSEL_DIV1_gc;
}

void micros(uint32_t doba){
    uint32_t start_time,end_time;

    EVSYS.STROBE = EVSYS_CHMUX2_bm;
    start_time = ((uint32_t)TCC1.CCA << 16) + TCC0.CCA;
    while(1){
        EVSYS.STROBE = EVSYS_CHMUX2_bm;
        end_time = ((uint32_t)TCC1.CCA << 16) + TCC0.CCA - start_time;
        if (end_time >= doba)   break;
    }
}

 

I don't know if this is the way it should or how it should be.

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

Thx kabasan, it resolved my issue.  Apparently problem was in software area.