40 Hz Square wave with 8kHz tone

Hi @wdunne

If you're using an Arduino Uno, then timer 1 is the only timer capable of generating a precise 40Hz and 8kHz signal. As it can't do both simultaneously, it's possible to use timer 1 to generate the 8kHz PWM signal at 50% duty-cycle then use the micros() function in the loop to time the 12.5ms gate interval. On the Arudino Uno the micros() function is accurate to 4us.

Other AVR Arduinos such as the Mega, Micro and Leonardo have an additional 16-bit timers that could be used to gate the 8kHz signal more accurately.

Gating is also a bit problematic on the Uno (or any other AVR microcontroller), since if timer 1 generates its 8kHz PWM using phase and frequency correct mode, the first and last pulses of the 8kHz burst the are truncated to 25% duty-cycle. I'm not sure if this is an issue for your application?

The workaround is to use fast PWM mode, however this introduces another problem in that this mode can't output a 0% duty-cycle. When the OCR1A register is set to 0 there's always a tiny slither of a pulse at the beginning of the timer's cycle. This can be overcome, but requires additional code to manually pull the output to 0% duty-cycle.

Here's a code example that uses the easier first method with phase and frequency correct PWM. This generates an 8kHz signal on digital pin D9 using timer 1 PWM mode and gates it using a 12.5ms interval using the micros() function:

// Set-up hadware PWM on the Arduino UNO at 8kHz on digital pin D9, gated by 12.5ms switch
unsigned long previousTime;

void setup() { 
  pinMode(9, OUTPUT);                         // Set digital pin 9 (D9) to an output
  TCCR1A = _BV(COM1A1);                       // Enable the PWM output on digital pin 9
  TCCR1B = _BV(WGM13) | _BV(CS10);            // Set phase and frequency correct PWM and no prescaler on timer 1
  ICR1 = 999;                                 // Set the PWM frequency to 8kHz: 16MHz / (2 * 8000) - 1
  OCR1A = 500;                                // Set duty-cycle to 50%: (999 + 1) / 2 = 500
  previousTime = micros();                    // Intialise the previous time
}

void loop() {
  unsigned long currentTime = micros();       // Update the current time
  static bool toggle = false;                 // Define the toggle variable
  
  if (currentTime - previousTime >= 12500)    // Time 12.5ms intervals
  {
    OCR1A = toggle ? 500 : 0;                 // Set timer1's duty-cycle to either 50% or 0%
    toggle = !toggle;                         // Flip the toggle
    previousTime = currentTime;               // Update the previous time
  }
}