Go Down

Topic: Programming the hardware timers - advanced level (Read 1 time) previous topic - next topic

fungus

HI,

I'm trying to output a square wave on an Arduino pin using a hardware timer. I'm using eight bit timer (timer 0) but I can't seem to get it to work like in the datasheet. I want the highest clock rate possible from the chip.

Here's my timer setup code:

Code: [Select]

    // Timer 0 -> Output on OC0A
    TCCR0A = _BV(WGM01)   // CTC mode
            |_BV(COM0A0); // Toggle OC0A output on every match
    OCR0A = 4;            // Reset when it reaches this value
    TCCR0B = _BV(CS00);   // Start timer, no prescale


I think that should toggle the ouput pin OC0A every 4 clock cycles, giving a nice square wave.

What I actually get is something like a PWM wave with a very short low pulse, like this (the purple line):



If I increase the value in OCR0A it makes the low part of the pulse wider, but a value of 3 or less makes it disappear (at least on my 'scope...)

Questions:
a) If I'm toggling a pin high/low based on a regular event (anything at all!), how can I get anything but a nice square wave (50% duty cycle)?
b) The datasheet says I can put zero in OCR0A when I want maximum frequency output. Why does the minimum useful value seem to be 4?
No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

fungus

Update: It is working properly, but the wave coming out of the Arduino is really nasty/ringy and the digital input on my 'scope wasn't seeing it properly.

I tried looking at it in analogue and on a different 'scope and the signals are there, they're just horrible.

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Nick Gammon

Using your code in a working sketch, thus:

Code: [Select]

void setup ()
{
  pinMode (6,OUTPUT);
  // Timer 0 -> Output on OC0A
  TCCR0A = _BV(WGM01)   // CTC mode
          |_BV(COM0A0); // Toggle OC0A output on every match
  OCR0A = 4;            // Reset when it reaches this value
  TCCR0B = _BV(CS00);   // Start timer, no prescale
}
void loop () { }


I get 1.6 MHz frequency (which you expect). The ringing isn't too bad for that frequency on my scope:



The above capture was with the probe set to x10.






The above is with a x1 probe.



Quote
... signals are there, they're just horrible ...


Can you post what you have?
http://www.gammon.com.au/electronics

Nick Gammon

Quote
b) The datasheet says I can put zero in OCR0A when I want maximum frequency output. Why does the minimum useful value seem to be 4?


You can use zero which gives a frequency of 8 MHz (because it takes two toggles per cycle):



I've used a similar bit of code to generate clock pulses for programming other processors so it can't be too dirty. The ringing I suspect is partly an artifact of the measuring. This is only a 60 MHz scope.
http://www.gammon.com.au/electronics

fungus

#4
Sep 23, 2012, 10:43 am Last Edit: Sep 23, 2012, 12:26 pm by Nick Gammon Reason: 1

I've used a similar bit of code to generate clock pulses for programming other processors so it can't be too dirty. The ringing I suspect is partly an artifact of the measuring. This is only a 60 MHz scope.


Here's captures with 2, 1 and 0 in OCR0A.







I'm using a DSO quad to look at it so the bandwidth is a lot less than 60MHz. I also hooked up an old green screen 'scope and the results were similar (though maybe 50% a less ringy).

I'm working with a bare chip in a breadboard so suppose the ringing could be due to the breadboard as well as the 'scopes. FWIW I looked at the output from an Arduino Uno and it was a little bit better.

Whatever, the important thing right now is that I'm not going crazy and the timer works as specified. I can get on with the rest of software.
No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

fungus

One more thing I found...this caused me a real headache!

If I write the code like this it works:

Code: [Select]
    // Timer 0 -> Output on OC0A
    TCCR0A = _BV(WGM01)   // CTC mode
            |_BV(COM0A0); // Toggle OC0A output on every match
    OCR0A = 4;            // Reset when it reaches this value
    TCCR0B = _BV(CS00);   // Start timer, no prescale


If I write the code like this, it doesn't:

Code: [Select]
    // Timer 0 -> Output on OC0A
    OCR0A = 4;            // Reset when it reaches this value
    TCCR0A = _BV(WGM01)   // CTC mode
            |_BV(COM0A0); // Toggle OC0A output on every match
    TCCR0B = _BV(CS00);   // Start timer, no prescale


I switched the two lines around when I was cleaning up the code a bit. It took me a whole day to figure out what had happened.
No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Nick Gammon

I've noticed that before, that the place you change OCR0A can affect results. I can't see any real explanation in the datasheet, except for page 108 where it says under CTC mode "Update of OCRx at ... Immediate".

But in your non-working sequence it isn't yet in CTC mode (you update OCR0A and then change to CTC mode) so somehow it loses that update.
http://www.gammon.com.au/electronics

Nick Gammon

In some modes (which may be the mode in place at the start) the update of OCR0A is buffered. Now how that affects things I'm not sure, except maybe this:


  • It starts in a buffered mode (eg. one of the PWM modes) because of init()

  • You change OCR0A but it goes into the buffer, ready for when the count ticks over (TOP or BOTTOM)

  • You change modes to CTC mode almost immediately

  • Since it is now in a non-buffered mode the (planned) copy from the buffer to the real register is lost

  • The old value of OCR0A is now retained

http://www.gammon.com.au/electronics

Nick Gammon

I have a mention of this problem on my page about timers:

http://www.gammon.com.au/forum/?id=11504

Near the bottom where I try out all the modes is this line:

Quote
Also note that you need to set the counter after starting the timer (as shown above) or you get rather strange results like this ...
http://www.gammon.com.au/electronics

fungus


But in your non-working sequence it isn't yet in CTC mode (you update OCR0A and then change to CTC mode) so somehow it loses that update.


My theory is that it's because the register is double-buffered in PWM mode but not in CTC mode.

The value is lost if you write it while the timer is in PWM mode then switch to CTC mode before it gets copied to the real register.
No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

Go Up