18

The Arduino IDE has many built-in commands to produce PWM outputs but directly setting the timer registers gives you much more flexibility and power. Below I show how to configure the 8-bit Timer/Counter2 on the ATmega328 (Ardunio UNO) to generate a 40 kHz square wave on Arduino digital pin 11. Why 40 kHz? I want to use it to drive a 40 kHz ultrasonic transducer for a project I am working on. First I will show you the code then explain what each line does. At the end I will discuss how you could set the clock prescaler to slow the frequency to as little as about 31 Hz.

void startTransducer()
{
  TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS20);
  OCR2A = B11000111; // 199, so timer2 counts from 0 to 199 (200 cycles at 16 MHz)
}

void setup()
{
pinMode(11, OUTPUT);
startTransducer();
}

void loop()
{
}

The _BV(XXX) function sets the XXX bit of whatever register you are working with to one. It is defined by the following C macro buried somewhere in the libraries used by the compiler:

#define _BV(bit) (1 << (bit)) 

So, using bit-wise "or" operations, the first two lines in my startTransducer() function sets the TCCR2A register (Timer/Counter Control Register A for Timer/Counter 2) and the TCCR2B register (Timer/Counter Control Register B for Timer/Counter 2) to the following 8-bit binary numbers:

TCCR2A = B01000011;
TCCR2B = B00001001;

The TCCR2A and TCCR2B 8-bit registers have the following structures:

TCCR2A - [COM2A1, COM2A0, COM2B1, COM2B0, reserved, reserved, WGM21, WGM20]
TCCR2B - [FOC2A, FOC2B, reserved, reserved, WGM22, CS22, CS21, CS20]

Now for the explanation. Setting the COM2Ax bits (Compare Output Mode bits) to 01 sets timer/counter2 to toggle OC2A (i.e., Arduino digital pin 11) from high to low when the counter matches the 8-bit value stored in OCR2A (Output Compare Register A on Counter/Timer 2). Setting the CS2x bits to 001 allows the counter to increment every clock cycle. Setting these bits to other values slows the counter rate using the prescaler (see below). Setting the WGM2x bits (Waveform Generation Mode bits) to 111 sets the timer/counter to Fast PWM mode with OCR2A setting the top value. More explicitly, this makes the timer count from zero to whatever value is stored in the OCR2A register. When this top value is reached, the timer resets to zero. Of course, since the timer is 8-bit, the counter can count from zero to a maximum of 11111111 or decimal 255.

The last line of the function sets the value of OCR2A to decimal 199 so the timer will count from 0 to 199 and toggle the output on pin 11 every 200 clock cycles. Since the clock is running at 16 MHz on the Arduino Uno, a quick calculation shows that the output toggles every 200/16,000,000 = 0.0000125 s. Thus a complete cycle of the output (from high to low and back to high) takes 0.000025 s creating a square wave output with a frequency of 40,000 Hz.

Reducing the Counter Rate using the Clock Prescaler

If you want the counter to count slower (e.g., if you want lower frequency square waves) then you can set the CS2x bits to use the microcontroller's prescaler so the counter doesn't update every clock cycle but ever 8 cycles (set the bits to 010), 32 cycles (011), 64 cycles (100), 128 cycles (101), 256 cycles (110), or every 1024 cycles (111).


If you have enjoyed this entry. Please feel free to bookmark it using your favorite social bookmarking site

18 Responses so far

  1. Sou says:

    Hi,
    i am an engineering student and i am working on a smilar project using Arduino.
    I wonder if you could show me how to make the signal stop after a period of time, because i need to send it for limited periods of time.

    • Jeff says:

      I use the built-in Arduino delayMicroseconds() or delay() functions in order to control the duration of a signal. For example, I will have a function called startTransducer to initiate the signal and stopTransducer to stop the signal then I will calculate how long I want the signal to be generated and put something like the following in my loop.


      startTransducer();
      delayMicroseconds(400); // wait for 400 microseconds
      stopTransducer();

      By default, Arduino uses Timer0 for these built in timing functions so you can’t also be using Timer0 to generate your signal. I hope this helps. It doesn’t give you incredibly fine control over the signal duration but it has worked well for me.

      By the way, all you have to do to stop a timer is write zeros to the appropriate timer/counter control register. For example, my stopTransducer function looks like this.


      void stopTransducer()
      {
      TCCR2A = 0;
      TCCR2B = 0; // I think this is all that is needed since setting the CS bits to zero stops the timer.
      }

  2. Johannes Kiefer says:

    Hey, so i am a student who works with ultrasonic sound measurement systems and build some sound generator with an atmel.

    There is an old chip named ne 555 wich could help to reduce the need for Timer 2. The chip is just a simple oszillator with an control input to turn it on or off or to use pwm control on a fixed frequency.

    Very simple to set up, just 3 resistor and 1 capacitor is needed.

    maybe this is helpfull

    Johannes

    • Jeff says:

      Thanks for the tip. I am familiar with the 555 and may give this a try. One benefit of using the MCU is that it gives your very precise control over the signal frequency which may not be obtainable using the 555 since the tolerances of the resistors and capacitors that control the duty cycle are often larger than 1%. I think another drawback of the 555 is that it is hard to obtain a duty cycle close to 50% unless one of the resistors is much much much smaller then the other one.

  3. samt says:

    Hi,
    First of all, thanks for great article!

    I am starting a project using 40kHz transducer. with Arduino. But I’m only a hobbyist. Here is my question:
    I wonder if I could use two or three receivers to read signal from one transmitter? So I could track the position of the transmitter by calculating the distance between each of the receivers and transmitter by measuring the time it takes for sound to be received by each of the sensors(receivers).

    Thanks for your kindness in advance.
    Sam

    • Jeff says:

      Sam,

      You can do what you are suggesting but the Arduino probably isn’t powerful enough to do it. You would need one timer to generate the ultrasonic pulse and two 16 bit timers (probably) for the other two transducers to record the arrival time of the pulse. You would also need two comparators to detect the arrival time via threshold crossing. The ATmega328 has only one 16 bit timer and one comparator onboard. You probably would need a more feature-rich MCU. I don’t know anything about the more beefed up Arduino models based on the ATmega2560 but I know the ATmega2560 has more onboard timers. I hope this helps

      Jeff

  4. Redemption says:

    Hi I want to get 40khz frequency signal from the pwm pin. Is this possible? Thanks

    • Jeff says:

      I translated your comment as best I could into English. Using analogWrite(127) will give you a square wave output with a 50% duty cycle but the frequency will be 31,250 Hz if using Arduino Uno pins 3, 9, 10, or 11 and 62,500 Hz if using pins 5 or 6. The only way to get more control over the frequency of outputs is the use a library that allows you to change the PWM frequencies. Truly fine control is only achievable by setting the timer registers.

  5. sang says:

    I would like to generate 22khz frequency but I calculated that it will need output at 363.6363 clock cycle. Is that even doable? Also, does delayMicrosconds take half(.5ms) value?

    • sang says:

      I think I found the answer, need to use CS2x bit. But what is the value that I will need to set for CS2x and the clock cycle? Also, does .5ms delay possible?

      • Jeff says:

        Yes, you would have to set the CS2X bits to further divide the counter with different prescalers since timer 2 is only 8-bits. Sorry, I forgot about this when I responded to your first question. I was thinking about timer 1 which is a 16-bit timer. I am sorry for the long delay getting back to you. As a professor, this is a super busy time of year.

    • Jeff says:

      Yes, you would have to change OCR2A to a value corresponding to the number of clock cycles (at 16MHz) equal to a time of one-half of 1/22.2kHz. I calculate a value of 360 but check my work. See the part of the post quoted bellow for more details.

      The last line of the function sets the value of OCR2A to decimal 199 so the timer will count from 0 to 199 and toggle the output on pin 11 every 200 clock cycles. Since the clock is running at 16 MHz on the Arduino Uno, a quick calculation shows that the output toggles every 200/16,000,000 = 0.0000125 s. Thus a complete cycle of the output (from high to low and back to high) takes 0.000025 s creating a square wave output with a frequency of 40,000 Hz.

      Also, delayMicroseconds only takes integers as an argument.

  6. marlon says:

    Hi, im doing a project that uses an ultrasonic transducer to receive a high frequency sound for example a bat, and i was wondering if you had any advice in how i can build a circuit to detect high frequency sounds using the arduino.
    Thanks

    • Jeff says:

      That is cool. You should be able to detect bat calls using one of the 40kHz ultrasonic transducers I used in this guide. I believe bats emit sounds from about 10 kHz (still audible) to about 100 kHz (not audible). I have no idea what frequency they use most often or which frequency they emit with the most power. I suppose the idea would be to match the frequency of the ultrasonic transducer used to the frequency the bats emit with the most energy (power). Either way you will probably have to pass the received signal through several stages of amplification (and probably band pass filters) before it is usable. This is an awesome idea. If you are successful please come back and post a follow up and a link to your finished project if available. Good luck.

  7. Brian says:

    Hi, I just found this post and thank you for the concise explanation of the timer functions. I have a 40khz project to measure wind speed using sealed transducers. I am planning on using a bridged MAX232 to pump up the signal and drive the transducer using 2 pins (1 hi/ 1 low). Is it possible to drive 2 pins using the timer and split the output such that we drive one pin hi for 12.5us and then 12.5us low?
    Thank you in advance for any help you can provide.

    • Jeff says:

      How about using an external comparator that takes the timer’s output as the inverting input and the non-inverting input is about 2.5V. Then when the timer is high the comparator’s output will be low and vice versa.

  8. Rob says:

    Will this code work for the arduino Mega

    • Jeff says:

      Yeah, but it may need some tweaks to work with the special function registers for one of the Mega’s 16-bit timers. Check the data sheet.

Leave a Comment