Generating 40kHz square wave

Hi, new here and probably being a bit dim but I’ve Googled and done all the obvious stuff and I cannot figure out how to program the PWM to get a 40kHz signal to drive an ultrasonic sender for a distance measurement device.

I’ve got the example below running an giving me 8kHz but how do I scale this to get 40kHz. Also on my ATMega1280 the output is on pin 13, not pin 6 which this example code suggests (I guess the code is for a different processor). I’ve read the datasheet but it just went in and fell straight out again. Any help appreciated.

#include <avr/io.h>

int main(void)
{
    DDRD |= (1 << DDD6);
    // PD6 is now an output

    OCR0A = 128;
    // set PWM for 50% duty cycle


    TCCR0A |= (1 << COM0A1);
    // set none-inverting mode

    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // set fast PWM Mode

    TCCR0B |= (1 << CS01);
    // set prescaler to 8 and starts PWM


    while (1);
    {
        // we have a working Fast PWM
    }
}

You need a pwm mode that allows you to set the top.

You need a pwm mode that allows you to set the top.

Was the any more to your post, dhenry?

You need to carefully read Nick Gammon's web site, I'd suggest starting here:

Nick Gammon's description of Timers and Counters

PWM frequency on the Playground: http://playground.arduino.cc//Code/PwmFrequency

Please keep in mind that changing the PWM frequency changes the Atmega's timers and disrupts the normal operation of many functions that rely on time (delay(), millis(), Servo library).

dhenry: You need a pwm mode that allows you to set the top.

Which mode is that?

One of my sketches on the page below generates 50 KHz:

http://www.gammon.com.au/forum/?id=11504&reply=6#reply6

By changing this line you should be able to get 40 KHz:

const long frequency = 50000L;  // Hz

Yep, it does, looks pretty accurate too:

Sketch:

const byte LED = 3;  // Timer 2 "B" output: OC2B

const long frequency = 40000L;  // Hz

void setup() 
 {
  pinMode (LED, OUTPUT);

  TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1); // fast PWM, clear OC2B on compare
  TCCR2B = _BV (WGM22) | _BV (CS21);         // fast PWM, prescaler of 8
  OCR2A =  ((F_CPU / 8) / frequency) - 1;    // zero relative  
  OCR2B = ((OCR2A + 1) / 2) - 1;             // 50% duty cycle
  }  // end of setup

void loop() { }

Thanks for the replies, I'll give that a go

Sorry to resurrect an old(ish) thread but what is the best way to stop and start this timer? I am using it for ultrasonic distance measurement so I need to send out a short ping and then listen for the echo. The ping needs to be short - is it best to just set the pin to input from output or do I need to stop the PWM, if so how?

Turning the pin to input is probably the simple way to stop it.

The thing that actually starts the timer is the CS21 part of this line (the clock source):

  TCCR2B = _BV (WGM22) | _BV (CS21);         // fast PWM, prescaler of 8

So the above line starts it. This would stop it:

  TCCR2B = _BV (WGM22) ;         // fast PWM, timer off

The full doc gives all the registers and commands, 55 pages of answers for those who ain'ts gots:

http://www.atmel.com/search.aspx?q=+ATMEGA328P&as_q=inmeta%3Aasset_type~Datasheets%2Binmeta%3Aproduct_family%3DAtmel%2520AVR%25208%252D%2520and%252032%252Dbit&dnavs=inmeta%3Aasset_type~Datasheets%2Binmeta%3Aproduct_family%3DAtmel%2520AVR%25208%252D%2520and%252032%252Dbit&filter=0&btnG=Search&client=support_frontend&proxystylesheet=support_frontend&output=xml_no_dtd&getfields=*&oe=UTF-8&ie=UTF-8&ud=1&num=1000&sort=date%3AD%3AL%3Ad1&lr=lang_en&exclude_apps=1&site=en_collection

BTW that's in thanks to your code in reply #7 Nick.

Thanks for the replies

Apologies for reviving this thread again, but I am now trying to use a similar bit of code to generate a 1kHz signal, however it is fine down to about 8kHz but it won't go below that. I suspect that it is due to the fast PWM but I have read the datasheet and I haven't got a clue how to stop using the Fast PWM.

Any help greatly appreciated.

Why use PWM at all? Just set up a timer and interrupt to change the pin state.

I'd like to use PWM because this is not all that the Arduino is doing and accuracy could be compromised if a bit-banging approach got interrupted.

PWM uses a timer that if you change PWM speed will affect some other operations. OTOH if you use a timer to drive a very short interrupt, the effect will be less to none. Worst case, your square wave edge will be off a few usecs but that is the case with PWM. Best case, you make it work both ways just to see which is more accurate.

I'm still not clear on what you are suggesting? Suppose in the main loop I want to do a Serial.print would this not affect the timing of the timer interrupt? Would the Serial.print complete or would it be interrupted by the timer?

Are you using the arrival of serial data sent from Arduino to time anything? I hope not!

You do a print command and what happens? This is as much as I can piece together, there are others on the board that can take it right down to the code and the metal:

With UNO there is the 328P that you program and the 8U2 to get to USB. Your text goes to the 8U2 via the serial RX pin at maximum 115200 bits per second (including start and stop bits) until that buffer is full when Serial fills its own buffer on the 328P. The 8U2 may empty faster than it can fill, or the PC may be busy now and then in which case hello Serial buffer. If you print long strings then you have more chance to find out and have serial output hang your sketch up, likely a fraction of a millisecond.

So let's suppose that we manage to not overload serial. Our timer interrupt will always run and flip the state of our output pin on time. Our ISR will not print. It may set a flag that the sketch can use to eventually print (do you plan on printing once every millisecond? then eventually is right then instead) but serial i/o should be down near the lowest priority of your sketch.

IMO the high priority is getting that square wave. And I would do it the same way at 50 kHz.

Of course if I'm wrong about any of this I would like to know soonest!

Ok, thanks, I'll may be give it a try.

No I'm not triggering on serial data. More the other way round, generate the square wave, wait for a response and then record the time between signal and response. It's useful to be able to Serial.print after the start of the square wave to provide feed back that things are running.