ArduinoFastSquare

From arrizza.org wiki

Jump to navigation Jump to search
Previous ⇦ ArduinoSOS Arduino ⇫ Up ArduinoInterrupts ⇨ Next

Overview

This project toggles a pin on the Arduino as fast as possible, roughly 2.0Mhz. My oscilloscope reported 1.898823 Mhz.

See Tools_and_other_Setup for some discussion about setting up an Oscilloscope

Git repository

https://bitbucket.org/arrizza/arduinofastsquare

git clone git@bitbucket.org:arrizza/arduinofastsquare.git

Setup

Set pin 10 to an output

Main Loop

Instead of using the normal pinWrite, we use the cbi and sbi macros. These take a pin id and either clear it (cbi) or set it (sbi). To get a uniform square wave a couple of NOPs (i.e. " "no-operation" instructions) are entered after the clear bit. These compensate for the hidden jump instruction generated by compiler due to the for(;;) loop call.

  for (;;)
    {
    // Clear Bit: set pin 10 low
    cbi(PORTB, PORTB4);

    // compensate for the jump instruction in the for() loop
    __asm__ ("nop"::);
    __asm__ ("nop"::);

    //Set Bit: set pin 10 high
    sbi(PORTB, PORTB4);
    }

Connecting to the Arduino Pins

This image shows the oscilloscope leads connected to pin 10 (red lead) and to ground (black lead).

Note I used a perforated board to mount the Arduino so it doesn't move around when I work with it. (In fact, this perforated board has two Arduinos mounted on it, so I can try out some communication code)

ArduinoFastSquare - board hookup.jpg

The Waveform

This image shows the waveform of the square wave. You can see it is nicely square, taking roughly 250ns (i.e 2 and a half horizontal grids) while it is high and roughly 250ns when it is low which corresponds to 1.98832Mhz. Note each horizontal grid is 100nS (see the "M 100ns" at the bottom of the screen).

Note also at this high frequency the square wave has some "ringing" when it it rises up and when it falls down. This is normal.

ArduinoFastSquare - waveform.jpg

I used "Autorange" on the Oscillopscope. It adjusted the horizontal range a bit for me. Now it shows the 250ns cycle a little more clearly - the "M 250ns" at the bottom of the screen indicates each horizontal grid is 250nS wide. Two of these form one cycle so 1/500nS = 2Mhz.

ArduinoFastSquare - waveform2.jpg

Notes

The Arduino's Atmel CPU runs at 16Mhz, but that doesn't mean that each instruction takes one clock cycle (i.e. 62.5 nanoseconds). Each instruction can take more than one clock cycle. In fact, there are some instructions that can sometimes take one cycle and sometimes take two cycles.

To find out what is going on, we need to see the exact opcodes and then go through the Atmel datasheet to find out how long each individual instruction takes.

Get the Generated Assembly Listing

To see the cbi, NOPs, the sbi and jump instructions, use avr-objdump

cd ~/projects/ArduinoFastSquare
cd cmake-build-debug/CMakeFiles/ArduinoFastSquare.dir

# you should see an obj file:
$ ls *.obj
ArduinoFastSquare_ArduinoFastSquare.cpp.obj

# invoke avr-objdump to:
#    -S intersperse the C++ source code in the listing
#    -d causes a dissassembly 
avr-objdump -S -d ArduinoFastSquare_ArduinoFastSquare.cpp.obj

The ATMEL Opcodes for the Main Loop

Unfortunately, the avr-objdump is not easy to read. I took out the "-S" and removed some of the extra listing to make it clearer for our purposes here.

00000000 <loop>:
   0:	2c 98       	cbi	0x05, 4	; 5
   2:	00 00       	nop
   4:	00 00       	nop
   6:	2c 9a       	sbi	0x05, 4	; 5
   8:	00 c0       	rjmp	.+0      	; 0xa <__zero_reg__+0x9>

Here you can clearly see the cbi, two NOPs, the sbi, all followed by a jump backwards to the top of the loop.

See ATmel's site

http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html

for the opcodes, the clock cycle count and specific details on how they work.

Timing Analysis of the Assembly

Using Atmel's datasheet to look up each individual opcode, the total cycle count is

cbi   => 2 clock cycles
NOP   => 1 clock cycle x 2 => 2 clock cycles
sbi   => 2 clock cycles
jump  => 2 clock cycles

total: 4 clocks high, 4 clocks low

# double-check our work!
16Mz / 8 clock cycles => 2Mhz

Can We Go Faster?

The other Jump instructions available on the ATMEL CPU also take 2 cycles and the Branch instructions (i.e. conditional jumps) take 2 cycles if the jump is taken. So at this point, it looks like this is the fastest square wave we can get with an Arduino.

What If It Wasn't a Square Wave?

But perhaps we can go a little faster if we take away the requirement for the wave to be square.

I tried taking away the NOPs to see the effect:

  • one NOP => the frequency jumped to 2.27Mhz (note: 16Mhz / 7 clock cycles => 2.285)
  • zero NOP => the frequency jumped again to 2.65Mhz (note: 16Mhz / 6 => 2.66Mhz)

These values make sense since the the waveform cycles that much faster without the NOPs. However, in both cases the waveform was not square. It was on longer than it was off.

Personal tools