Go Down

Topic: Maximum pin toggle speed (Read 20159 times) previous topic - next topic

#30
Jun 16, 2009, 06:53 pm Last Edit: Jun 17, 2009, 05:07 am by selfonlypath Reason: 1
Many many many thanks westfw.

I've tried & benchmarked delay_loop_1 and delay_loop_2 per your explanation in the other thread. Works fine as you predicted: 3N cycles for delay_loop_1 and 4 cycles for delay_loop_2.

I don't know if it is important but it seems delay_loop_1(0) or delay_loop_2(0) do not work giving extremely long return time but that is OK for my application which forces a non-negative value to not blow up my MOSFET.

It is really interesting to note that for(i=0;i<n;i++) NOP will give average 7 cycles per iteration if i UNSIGNED but average 8 cycles per iteration if i SIGNED as you mentionned.

Anyway, calling x unsigned or signed delay_loop_x(n) gives same running time plus I really save a lot of timing hence i'll be able to go PWM higher precision than using for(i=0;i<n;i++) NOP.

Amicalement, Albert

Quote
Code:
 cli();
 while (1) {
   PORTD |= 0x8;
   PORTD &= ~0x8;
 }

on the same board runs at 2.667MHz.  (This does produce the minimal sbi/cbi/rjmp loop that you'd expect, BTW.)
(so that's about a 20x penalty for the arduino library code; sounds about right: the overhead of abstracting IO to "pin number" is pretty substantial: a subroutine call, lookup table to get the port, another lookup table to get the bit, a third to check whether analogWrite is in use, and then less efficient instructions to access the port "indirectly")


I don't know if this is of any interest but i've lately figure out how to generate fast PWM, phase correct PWM using 8bits and 16 bits timers hence freeing CPU for my project.

Could be wrong but if setting a timer in fast PWM mode and TOP=OCRnA=2, you can reach max frequency of 16MHz/(1+TOP) hence 5.333MHz and OCRnB=1. There seems to be a specific auto-toggling mode in the timer providing a 50% PWM at 8MHz.

The advantage is ultra fast PWM and freeing CPU.

The disavantage is only limited timers in duamilanove, a bit more on mega so if one needs many pins fast toggling, the quoted method is the best.

westfw

If you have a sample sketch, I'll be happy to throw the output into my scope and measure it to make sure!

#33
Oct 18, 2009, 09:17 am Last Edit: Oct 18, 2009, 09:20 am by selfonlypath Reason: 1
Here is the sketch for a mega board or duamilove, don't know which you have but I have both which should generate 5.333MHz on Pin12
Code: [Select]
#include <util/delay_basic.h>

int outputPsuB = 12;  // Timer1-B

void setup()
{
// outputs via timer1
 pinMode(outputPsuB, OUTPUT);  // select Pin as ch-B

 TCCR1A = B00100011; // Fast PWM change at OCR1A
 TCCR1B = B11001;  // System clock
 OCR1A = 2; // 5.333 MHz
 OCR1B = 1; // 50% PWM
}

void loop()
{
// do what ever you want with full 100% CPU
}


If it works on your scope, you might then try OCR1A=5 and should get your initial case of 2.667MHz and choose different PW via OCR1B value from 0 to OCR1A !

This other sketch should give 8MHz toggling on Pin11 but only 50%PW that cannot be changed
Code: [Select]
#include <util/delay_basic.h>

int outputPsuA = 11;  // Timer1-A

void setup()
{
// outputs via timer1
 pinMode(outputPsuA, OUTPUT);  // select Pin as ch-A

 TCCR1A = B01000011; // Fast PWM change at OCR1A
 TCCR1B = B11001;  // System clock
 OCR1A = 0; // 8 MHz
}

void loop()
{
// do what ever you want with full 100% CPU
}


There are many other possibilities if you have a mega board

Paul Stoffregen

Quote
Using digitalWrite() on a non-PWM pin (4 instead of 3) runs about 148.4kHz instead of 106.8kHz:


I recently wrote a speed optimized digitalWrite() for Teensyduino.  It runs that loop at 223 kHz on non-PWM pins and 195 kHz on PWM pins.

If anyone's interested in porting it back to the Arduino core, it's available and open source.  Just run the installer, then look for it in pins_teensy.c inside the teensy_serial or teensy_hid directories.

westfw

Quote
Here is the sketch for a mega board or duamilove, don't know which you have but I have both which should generate 5.333MHz on Pin12

Ah.  The timer output to arduino pin mapping is different on Mega vs Diecimila; the signal shows up on pin 10 of the Diecimila.  Once I corrected for that I did indeed get 5.33MHz on pin 10.  Not a square wave, though; high for twice as long as it is low.

#36
Oct 23, 2009, 05:08 am Last Edit: Oct 23, 2009, 05:09 am by selfonlypath Reason: 1
Quote
The timer output to arduino pin mapping is different on Mega vs Diecimila; the signal shows up on pin 10 of the Diecimila.  Once I corrected for that I did indeed get 5.33MHz on pin 10.

Glad it worked on your side.  ;)

Yes about pin, there are a bit different depending diecemila / duamilanove or mega eventhough the core sketch I gave you works on both.

I suggest you try my second sketch to find out ultimate case of 8 MHz on pin9 of diecemila instead of pin 11 of mega.

This is very useful doc from mem arduino member http://spreadsheets.google.com/pub?key=rtHw_R6eVL140KS9_G8GPkA&gid=0

Quote
Not a square wave, though; high for twice as long as it is low.

I'm sure you know this already but to not create confusion, the timers really work at correct frequencies but what happens is the arduino drivers and/or Atmega drivers are not enough fast to generate sharp PW waves. You might want to plug an external fast speed driver IC then you'll get nice waveform on your scope.



westfw

The first example really does generate a non-square wave.  It ought to be obvious: 5.33 = 16/3, so it's a 3-cycle waveform.  Lacking any sort of clock multiplier, that's going to be two cycles in one state and one in the other state...  (or, you're counting from 0 to 2 and flipping the bit at 1, so depending on which edges are involved, we have one state for 0,1 and the other for 2, or one state for 0, and the other for 1,2...)

The second example did generate a 8Mhz square wave (on  pin 10 of the diecimila, I think.  I didn't check both 9 and 10.)  Interpret the code as "count from 0 to 0 and toggle the output each time you finish (ie toggle every cycle, so output = clock/w))

The timer-generated output signals are quite a bit less interesting than the software-controlled ones, IMO.  Although people seem to have used such things to help drive LCD displays in a jitter-free fashion...  Hmm.

About the first example, then you might want to try
- PWM, Phase & frequency correct
- PWM, Phase correct
because I gave you the code for fast PWM which goes higher freq but is less jitter stable.

For Fast PWM and OCR1A=3, timer1 frequency=16MHz/(1+TOP)=16MHz/3

For PWM phase and/or frequency correct, timer1 frequency is 16MHz/(2*TOP). Since your initial sofwtare controlled code was producing 2,667 MHz, you could choose OCR1A=TOP=2 in order to get 4MHz and setting OCR1B=1 to get 50% PWM. The phase and/or frequency correct does not provide maximum frequency but does provide very stable jitter due to its counting up-down timer construction.

Anyway, we're clearly pushing the limits of Atmega toggling outputs frequency  ;)

In any case, you need good external fast drivers otherwise signal distorsion will occur.


Circuitsville

Could you post the code?                Please?

The basic code I am using, from the Fade example, has a base frequency of about 500Hz.  To simplify and reduce the size of a lot of the inductors it would be nice to work at 50kHz.

bconley at circuitsvilleeng dot com

westfw

The code is what selfonlypath posted a while ago.  Note that this does NOT generate "very fast PWM signals"; it just uses the same PWM peripheral to generate a very fast waveform (2 bits of PWM.  OFF and ON)  To retain the 256-bit "analogWrite" capability, the overall frequency will need to be much lower.  Still, you can probably get something like 16kHz or 32kz just by changing the clock source in TCCR1B

#41
Oct 28, 2009, 05:00 am Last Edit: Oct 28, 2009, 05:38 am by selfonlypath Reason: 1
hey Circuitsville,

it took me a while to understand timers but then, it is a peace of cake to generate any PWM frequency from a few Hz up to hundreds of KHz or even more.

Here is another example showing the basic timer programmation (from post #5 to the end of thread):
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1252073278

please note you can have different ways to generate same frequency by playing with clock scaling, using a 8bit or 16bit Atmega timer along with TOP value but each way might have not same PW resolution.

TraumaPony

#42
Dec 24, 2009, 07:59 am Last Edit: Dec 24, 2009, 08:00 am by TraumaPony Reason: 1
Hi guys. I did some testing on 0017.

I modified turnOffPWM in wiring_digital.c, and profiled various configurations of inline/noninline, switch, if, and else if for the timer selection.

Here's the code that I tested with:

Code: [Select]
int unConstPwmPin =  3;      
int unConstPin = 2;
const int constPwmPin = 5;
const int constPin = 12;
int index;
long time;
void setup() {
 // set the digital pins as output:
 pinMode(unConstPwmPin, OUTPUT);      
 pinMode(constPwmPin, OUTPUT);      
 pinMode(unConstPin, OUTPUT);      
 pinMode(constPin, OUTPUT);      
 Serial.begin(9600);
}

void loop()
{
 int var = LOW;
 time = micros();
 for (index = 0; index < 10000; index++)
 {
   digitalWrite(unConstPwmPin, HIGH);
   digitalWrite(unConstPwmPin, LOW);
   digitalWrite(constPwmPin, HIGH);
   digitalWrite(constPwmPin, LOW);
   digitalWrite(unConstPin, HIGH);
   digitalWrite(unConstPin, LOW);
   digitalWrite(constPin, HIGH);
   digitalWrite(constPin, LOW);
   digitalWrite(unConstPwmPin, var);
   digitalWrite(unConstPwmPin, var);
   digitalWrite(constPwmPin, var);
   digitalWrite(constPwmPin, var);
   digitalWrite(unConstPin, var);
   digitalWrite(unConstPin, var);
   digitalWrite(constPin, var);
   digitalWrite(constPin, var);
   var = !var;
   digitalWrite(unConstPwmPin, var);
   digitalWrite(unConstPwmPin, var);
   digitalWrite(constPwmPin, var);
   digitalWrite(constPwmPin, var);
   digitalWrite(unConstPin, var);
   digitalWrite(unConstPin, var);
   digitalWrite(constPin, var);
   digitalWrite(constPin, var);
 }
 time = micros() - time;
 Serial.println(time);
 delay(1000);
}


And here are the results:

Noninline switch = 1004456
Inline switch = 1004452

Noninline if=1019544
Inline if=1019544

Noninline elseif = 1019544
Inline elseif = 1019544

For the inline switch, this works out to be approximately 4.1852166666666666666666666666667 (etc) microseconds per pin. Note that I'm treating PWM and non-PWM pins the same, to find the AVERAGE efficiency. This works out to be approximately 239kHz.

If I tried only constant non-PWM pins with consant inputs, the frequency is about 270 kHz, whereas plain if's is 274kHz. For non-constant non-PWM pins with non-constant inputs, switch gives 266kHz; if gives 271kHz.

For a constant PWM pin with constant inputs, using switch, the frequency is 218kHz; using if's, it's exactly the same. For non-constant pins with non-constant inputs, switch gives 215 kHz; for if's, 199kHz.

Paul Stoffregen

You should add "static" to the const variables.  The compiler will not truly treat them as compile time constants unless they are static, because even though they are const, they are visible to other compile units that could discard the const qualifier.  That forces the compiler to allocate them like any other variable.  Only with "static const" is it truly a constant.

Today that probably won't make any difference.  But when (if ever?) my digitalWrite optimization gets included in Arduino, it will make a HUGE difference.  That code is in issue #140.

http://code.google.com/p/arduino/issues/detail?id=140

Also, I ran this code on a Teensy (which also does not have that optimization as of teensyduino 0.8, but does have other optimizations I wrote earlier), and I get 584128.  That code is open source and you can get if from my website, if you're interested.  Just run the installer and look for it in the hardware directory.

I'm also working on some assembly optimizations that will further speed up the non-const cases.

I don't understand
Quote
and I get 584128.
, what does it mean 584218 ?

Go Up