Fastest switching speed of ATMEGA328

I’ve written a little sketch that will vary the duty cycle of a PWM signal on one of the digital outputs of the Uno based on the voltage at one of the ADC pins. I’m doing it on one of the digital pins because I want to switch at 100KHz for a SMPS. The value returned by the sampling of the ADC pin is used a feedback to determine the duty cycle of the output in useconds. The sketch all works fine apart from the fact I can’t seem to get it to switch higher than ~35KHz no matter what numbers I use. I realy need 100KHz minimum for this design but is it physically possible?

I’m sure the people here will easily work it out but…

The ADC value is returned between 0 and 1023 this is divided by 100 to give the period in uS. Then this is taken for the off time then it’s take away from 10.23 to give the on time. This gives me my feed back.

If I divide the value by 10 to give me a 100uS period then I will get a good 10KHz waveform but with the /100 I’ll only get 35KHz


//This program is used with the 328 as a SMPS controller.

// Constant declaraions.

const int pwmOut = 13;

// Variable declaraions.

float val = 0.0;
float valHigh = 0.0;
float valLow = 0.0;

// the setup routine runs once when you press reset:

void setup()

// initialize the IO.

pinMode(pwmOut, OUTPUT);

// the loop routine runs over and over again forever:

void loop()

val = analogRead(A0);

valLow = val / 100;

valHigh = 10.23 - valLow;

digitalWrite(pwmOut, HIGH); // turn the output high

delayMicroseconds(valHigh); // delay for valHigh

digitalWrite(pwmOut, LOW); // turn the output low

delayMicroseconds(valLow); // delay for valLow

The sketch all works fine apart from the fact I can't seem to get it to switch higher than ~35KHz no matter what numbers I use.

Get rid of floating point calcs and do use fast digitalwrite() from fat16lib's library.,150325.0.html You may consider the hw PWM as well..

analog Read() takes time.... 100us.

You can "schedule" the analog read and then a couple of loops later read the value returned. Have a look in the core code for inspiration

Get rid of floating point calcs and

use 2kB program memory look up table.

Lets take it from the top - "get rid of the floats".

val = analogRead(A0);  // this returns an integer, and is converted (needlessly) into a float
  valLow = val / 100;   // this is a float divide, but as the val can ony be a whole number
    //  (even though it is float) and 100 is an int, so if val is an int, then this is just an integer divide
    valHigh = 10.23 - valLow; // OK, this is a float constant. If valLow was an int, then it would be converted here to a float.
    digitalWrite(pwmOut, HIGH);   // turn the output high
    delayMicroseconds(valHigh);  //This can only take an int/long as the input so the ".23" is wasted/truncated away.

In short, declare your values as int and all will be well :)

Oh, you are you worried that you are missing on the decimals in the divide? But they can not be passed to the delay microseconds as it only takes WHOLE numbers. In fact it will truncate to the nearest 4 us seconds (ie. 5 is the same as 4, 7 is the same as 4, but 8 will indeed be 8.)

The fancy conversion table won't help, as long as you call analogRead() in each loop. That is 100 us or approx 1600 instructions so removing 20 or 100 instructions by dropping the floats or the single integer divide isn't going to be noticed.