2

I need to obtain 200 kHz from Arduino Uno. I am using Timer0. I have configured it to run in normal mode and the Prescaler is set to clk/8 (the clock frequency of Arduino is 16 MHz). TCNT0 is set to 0xFB and the code toggles 2 pins of PORTB as timer overflow flag is set.

Here is my code (I am using Atmel Studio 7):

#include <avr/io.h>


int main(void)
{
   DDRB = 0x03;
   PORTB |= 0x02;
   while (1) 
   {
       TCNT0 = 0xFB;
       TCCR0A = 0X00;
       TCCR0B = 0x02;
       while ( (TIFR0 & 0x01) == 0 );
       PORTB ^= 0x03;
       TCCR1A = 0x00;
       TCCR1B = 0x00;
       TIFR0 = 0X01;
  }
}

When I check on the oscilloscope, it shows that the pulse is 166 kHz instead of 200 kHz. Although I can use Fast PWM Mode and use interrupts, I am just curious to know why it doesn't work? Did I make any mistake?

  • You don’t even need to use interrupts. Just have the timer toggle one of the two associated pins for you. – Gerben Jul 6 '18 at 17:21
3

That's simple enough - you forgot that everything takes time.

Your timeline is:

  1. Configure timer
  2. Set count
  3. Wait for count to overflow (33 ticks)
  4. Toggle pin
  5. Do something with T1 (why...?)
  6. Clear overflow bit
  7. Go back to 1.

Here's how that looks as assembly:

90: 3b ef           ldi r19, 0xFB
92: 42 e0           ldi r20, 0x02
94: 23 e0           ldi r18, 0x03
96: 91 e0           ldi r25, 0x01

while (1) {
98: 36 bd           out 0x26, r19   ; TCNT0 = 0xFB;
9a: 14 bc           out 0x24, r1    ; TCCR0A = 0X00;
9c: 45 bd           out 0x25, r20   ; TCCR0B = 0x02;

while ( (TIFR0 & 0x01) == 0 );
9e: a8 9b           sbis    0x15, 0
a0: fe cf           rjmp    .-4         ; 0x9e <setup+0xe>

PORTB ^= 0x03;
a2: 85 b1           in  r24, 0x05   
a4: 82 27           eor r24, r18
a6: 85 b9           out 0x05, r24   

a8: 10 92 80 00     sts 0x0080, r1
ac: 10 92 81 00     sts 0x0081, r1
b0: 95 bb           out 0x15, r25   

}
b2: f2 cf           rjmp    .-28

So assuming that the waiting for the timer overflow takes 8*4+ 1=33 clock cycles, and referencing the datasheet for how long each instruction takes, we can add up everything in the main while loop:

while (1) {
98: 36 bd           out 0x26, r19   ; 1
9a: 14 bc           out 0x24, r1    ; 1
9c: 45 bd           out 0x25, r20   ; 1

while ( (TIFR0 & 0x01) == 0 );
9e: a8 9b           sbis    0x15, 0 ; 2 (at end)
a0: fe cf           rjmp    .-4     ; 33 (while running)

PORTB ^= 0x03;
a2: 85 b1           in  r24, 0x05   ; 1
a4: 82 27           eor r24, r18    ; 1
a6: 85 b9           out 0x05, r24   ; 1

a8: 10 92 80 00     sts 0x0080, r1  ; 2
ac: 10 92 81 00     sts 0x0081, r1  ; 2
b0: 95 bb           out 0x15, r25   ; 1

}
b2: f2 cf           rjmp    .-28    ; 2

You can see that takes 48 clock cycles (We don't care about what is in the inner while except when it comes to finishing, and that's just the SBIS instruction). For 200kHz you want 40 clock cycles. You have 48. 16000000/48 = 333333. Divide by two for your pin toggling, and you get 166666Hz.

If you want to use this kind of method you need to ensure that the timer is completely free running and the overflow flag gets set at a regular interval, not just at 32 ticks after you start it.

You should be operating in CTC mode so it counts up from 0 to your target value and then resets back to 0 whilst setting the overflow flag. Your loop then looks like:

while (1) {
    if ((TIFR0 & 0x01) == 0x01) {
        PORTB ^= 0x03;
    }
}

All the timer configuration is done before the loop and it just runs and runs until you say otherwise.

  • Thank You, now I think I understood where I made mistake. Can you explain why waiting for the timer overflow takes 8*4+ 1=33 clock cycles?? – Prabhat Narang Jul 7 '18 at 2:53
  • @PrabhatNarang 8*4 to get to 0xFF, and the flag gets set on the next clock cycle. – Majenko Jul 7 '18 at 7:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.