4

I have this code:

void setup() {
  pinMode(11, OUTPUT);
  TCCR2B = 0;
  TCNT2 = 0;
  TCCR2A = _BV(WGM21);
  TCCR2A |= _BV(COM2A0);
  OCR2A = 249;
}

void loop() {
  soundBuzzer();
}

void soundBuzzer() {
  TCCR2B |= _BV(CS21);
}

void silenceBuzzer() {
  TCCR2B &= ~_BV(CS21);
  TCNT2 = 0;
}

(Got it from here).

And I have an ultrasonic water atomizer that works in a frequency of 1.7 Mhz. Writing another number instead of 249 in the line "OCR2A = 249;", the frequency generated changes. With 249 it generates a frequency of 4 Khz. So, my question is: Which number do I have to write to get approximately 1.7 Mhz?

dda
  • 1,595
  • 1
  • 12
  • 17
Hackeitos
  • 41
  • 1
  • 3

1 Answers1

3

Expanding from my comment... any signal level generated by this kind of microcontroller can only last for an integer number of CPU cycles. In order to generate it, you first have to compute the timing characteristics of the desired signal in terms of CPU cycles:

period = 16 MHz ÷ 1.7 MHz = 9.412 cycles

The closest you can get is 9 cycles, which gives the frequency

frequency = 16 MHz ÷ 9 = 1.778 MHz

Assuming this is acceptably close to your target, you can configure a timer to do it. The example code you provided uses Timer 2 with the OC2A output set to toggle mode. This approach can only give periods which are an even number of cycles, thus I am using the regular “non-inverting fast PWM” mode instead in the solution below. In this mode, the pin OC2A cannot be used with a controlled frequency, then I changed it to OC2B (pin 3 on the Uno). Here is the code:

const uint8_t OUTPUT_PIN = 3;  // = OC2B
const uint8_t PERIOD = 9;      // 9 CPU cycles ~ 1.778 MHz

void setup()
{
    pinMode(OUTPUT_PIN, OUTPUT);
    TCCR2B = 0;           // stop timer
    TCNT2  = 0;           // reset timer
    TCCR2A = _BV(COM2B1)  // non-inverting PWM on OC2B
           | _BV(WGM20)   // fast PWM mode, TOP = OCR2A
           | _BV(WGM21);  // ...ditto
    TCCR2B = _BV(WGM22);  // ...ditto
    OCR2A = PERIOD - 1;
    OCR2B = PERIOD/2 - 1;
}

void soundBuzzer() {
    TCCR2B |= _BV(CS20);  // F_CPU / 1
}

void silenceBuzzer() {
    TCCR2B &= ~_BV(CS20);
    TCNT2 = 0;
}

The generated signal is slightly asymmetric: it stays HIGH during 4 cycles and LOW for 5 cycles. If you want a symmetric signal (50% duty cycle), you can set PERIOD to 10 (5 cycles HIGH and 5 cycles LOW), but then the frequency drops to 1.6 MHz.

Edit: You could also replace your Arduino Uno by a cheap ATtiny85 microcontroller, or maybe an Arduino-compatible dev board based on it, like the Trinket or the Digispark.

This microcontroller has a built-in PLL which allows it to run one of its timers at 64 MHz, even though the CPU is running much slower. A 64 MHz timer allows you to hit the target 1.7 MHz frequency to within 1%:

64 MHz ÷ 38 = 1.684 MHz
Edgar Bonet
  • 45,094
  • 4
  • 42
  • 81