3
\$\begingroup\$

I'm a little bit confuse, and hope that someone here can point me out what I'm missing... I'm using an Atmega8-16PU, Flashed the fuse with :

avrdude -b 19200 -c avrisp -P /dev/tty.usbserial-A9IHTRVJ -p atmega8 -U lfuse:w:0xe4:m -U hfuse:w:0xd9:m

which means.. according to http://www.engbedded.com/fusecalc/ that I'm using int. osc 8Mhz. In my code, I override OSCAL to OSCCAL = 0xFF; in order to work at its max frequency.

nevertheless, in a for loop (also tried with a 'while'), I'm only toggling a pin LED1_STATE^=(1 << LED1_PIN); and I'm measuring 1.6Mhz ...

I was thinking that AVR where able to execute one instruction per system clock... here it seems that the FCPU is divided by 4..


I also tried using Timer0 and its overflow interrupt with ;

    TCNT0 = 254; 
    TIMSK |= (1<<TOIE0); // enable interrupt
    TCCR0 = 0x01; //prescaler = 1 
    sei();


ISR(TIMER0_OVF_vect){
    LED1_STATE ^=(1<<LED1_PIN);
    TCNT0 = 254;
}

and here the signal output 200Khz ....

can someone help me figure it out what is going on ? I am not enough considering the necessary 'instructions' time ? Many thanks internet

Regards

\$\endgroup\$
5
  • 3
    \$\begingroup\$ It would educational for you to disassemble the compiled code (with the loop) so you can see exactly what instructions are being executed. Remember that a single line in C is not a single instruction, and that loops incur overhead. \$\endgroup\$ – uint128_t Feb 11 '16 at 16:43
  • 1
    \$\begingroup\$ Also, if your output frequency is 1.6MHz, consider the implication of 8/1.6. This is telling you something. \$\endgroup\$ – uint128_t Feb 11 '16 at 16:45
  • \$\begingroup\$ well, I don't know how to disassemble code, but I guess : 1 instruction for the exclusive OR and 2x RJMP (2x2 = 4) total : 5 :) thanks \$\endgroup\$ – CrH Feb 11 '16 at 17:07
  • \$\begingroup\$ Also, most instructions take one clock second, but not all. There is a table in the datasheet listing a instruction summary including clock cycles. Definitely disassembling is very educational. If you are developing on Linux I have a generic makefile that includes a disassembly listing. git.linformatronics.nl/gitweb/?p=makefile;a=summary With interrupts you may be surprised about the overhead. \$\endgroup\$ – jippie Feb 11 '16 at 17:08
  • \$\begingroup\$ From that disassembled code, it looks like your loop has complied into: Read port, xor, write port, jump. Jump takes two clock cycles, the others each take one, for a total of five. The first three institutions are not part of the loop. Since that toggles the state of the pin once in 5 operations, it takes ten operations to toggle it twice. So by setting OSCCAL=0xFF you've got a 16MHz internal clock. The datasheet recommends against that. \$\endgroup\$ – Jack B Feb 11 '16 at 18:17
1
\$\begingroup\$

thanks to the jippie makefile, I was able to see the corresponding assembler code. In deed their are 6 asm instructions to do the while(1) and toggle.

 1e4:   8f ef           ldi r24, 0xFF   ; 255 <-- OSCAL = 0xFF (1 clk)
 1e6:   81 bf           out 0x31, r24   ; 49  <-- Switch pin voltage level (1clk)
 1e8:   92 e0           ldi r25, 0x02   ; 2   <-- ? (1clk)
 1ea:   85 b3           in  r24, 0x15   ; 21  <-- Read pins level (1clk)
 1ec:   89 27           eor r24, r25          <-- Excl. OR (1clk)
 1ee:   85 bb           out 0x15, r24   ; 21  <-- Switch pin voltage level (1clk)
 1f0:   fc cf           rjmp    .-8         ; 0x1ea <main+0x8> <-- Loop (2clk)

total : 8 clk ticks

Thanks

\$\endgroup\$
6
  • \$\begingroup\$ I'm not sure you noticed that the loop jumps back to 0x1ea in r24, 0x02, so there are 4 instructions in the loop consuming a total of 5 clocks per half period of the output square wave. 8MHz / 5 clk = 1.6MHz \$\endgroup\$ – jippie Feb 11 '16 at 19:04
  • \$\begingroup\$ Aaah rjmp .-8 ; 0x1ea <-- 0x1ea is the adress, right? \$\endgroup\$ – CrH Feb 11 '16 at 19:09
  • \$\begingroup\$ Right. -8 bytes, considering that the progam counter at the moment of the subtraction is pointing to the instruction following the rjmp, which results in 0x01f2 - 0x0008 = 0x1ea which is the address shown in the comments. \$\endgroup\$ – jippie Feb 11 '16 at 19:17
  • \$\begingroup\$ Actually I made one mistake, probably accounted for by you setting OSCAL to 255. The expected frequency at 8MHz clock (so without loading OSCAL) is 8MHz / ( 2 × 5 ) = 800kHz. 5 clock ticks per half cycle, 10 ticks per full cycle. \$\endgroup\$ – jippie Feb 11 '16 at 19:26
  • \$\begingroup\$ Absolutely, I measured 800Khz before 'overclocking' ... Many thanks to all of you for your answer. Exactly what I needed, and you gave me the tool and key to better understand. Really appreciate \$\endgroup\$ – CrH Feb 11 '16 at 19:37
1
\$\begingroup\$

Have a look at the assembly being generated, both of your cases will have some extra instructions you haven't thought about. In a while loop, for example, there will be an instruction to jump back to the beginning of the loop.

The timer approach is better, but you are going about it the wrong way. When the timer fires, you are calling a function. There are several instructions associated with that. Fortunately the 16-bit timers on ATmega* chips can do other things than trigger interrupts, they can also toggle pins directly. Try:

TCCR1B = 0x08 | 0x01; 
TCCR1A = 0x80; 
OCR1A = 1;
TCNT1 = 0;

This will cause timer1 to count to one, then toggle the associated output pin, then count to one and toggle it back. As written, you'll get a F_CPU/4 clock out, so twice as fast as what you have and without blocking the system up so you can't do anything else. You can probably get a F_CPU clock out by adjusting the fuses to put the master clock on one of the output pins.

Check the datasheet for which pin is the output - on most ATmegas you can choose one of two pins, you don't get to choose an arbitrary pin if you do it this way.

Here's a handy link explaining what some of those registers do: http://www.avrbeginners.net/architecture/timers/timers.html#tccrb


Now you've posted your disassembled while loop, we can see what was happening:

1e4:    8f ef ldi   r24, 0xFF   ; 255   Load the value 0xFF into a register
1e6:    81 bf out   0x31, r24   ; 49    Write that value to OSCCAL
1e8:    92 e0 ldi   r25, 0x02   ; 2     Load the value 0x02  into a register
1ea:    85 b3 in    r24, 0x15   ; 21    Read the pin state port into a register <-
1ec:    89 27 eor   r24, r25            XOR together the two registers           |
1ee:    85 bb out   0x15, r24   ; 21    Write the result to the pin state port   |
1f0:    fc cf rjmp  .-8 ;               Jump back to  ----------------------------

So your while loop includes the instructions: in, eor, out, rjump, which take 1+1+1+2=5 clock cycles, and toggle the pin once. So 10 cycles to toggle it twice. Since you're seeing 1.6MHz out, that implies that by setting OSCCAL to 0xFF you have obtained a clock speed of 16MHz. Note that although this works, the datasheet recommends against it.

\$\endgroup\$
1
  • \$\begingroup\$ don't worry. It was just a test. I would understand why I had a difference between the frequency I expected and the one I measured. It was a dummy test. Now I would like to understand how/where do you see the line, where rjmp return (21 ? ) \$\endgroup\$ – CrH Feb 11 '16 at 19:05

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.