Close

Tone generation using PWM

A project log for 1Keyer

An iambic paddle controller for sending Morse code.

mark-vandewetteringMark VandeWettering 12/12/2016 at 07:110 Comments

The last piece of code I needed to write for this project was code to generate a 700Hz tone on command. There are a number of was I could do this, but I chose to use Timer1 in CTC (Clear Timer on Compare) mode.

The code isn't actually very complicated (quite tiny, in fact.) It mostly configures the Timer, and then enables an interrupt. Every 64 clock cycles, the timer counter register increments. When it reaches OCR1A, it triggers an interrupt, and the clock is reset to zero. We want to generate a 700Hz signal, which means that we need to generate flip from high to low at twice that rate. On each interrupt, we flip from high to low, and then back again.

I coded this up to toggle the LED pin (13) on the Arduino. It worked the very first time I coded it up. When I hooked up my Rigol oscilloscope, it showed a nice 701Hz square wave. It will be pretty easy to toggle it on and off (basically just set the TIMSK1 value to enable and disable the interrupt.

Over the next couple of days, I'll get all this code integrated together, and see if it really will work in just 1K. I'm optimistic.

#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>

// For my 1Keyer program, I wanted to generate a 700hz square wave
// on an auxillary pin, so I could use it as a "sidetone".  The 
// easiest way would seem to use "CTC" (Clear Timer Compare) mode.

ISR(TIMER1_COMPA_vect)
{
    // Every time we execute this interrupt, we are going 
    // to toggle the value of the output pin.
    PORTB ^= _BV(5) ;
}

#define CLOCK_PRESCALER         64
#define FREQ                    700
#define OVERFLOW_COUNT          (((F_CPU/CLOCK_PRESCALER)/(2*FREQ))-1)

int
main()
{
    // LED is on PB5, so configure it for output...
    DDRB |= _BV(5) ;
    // And set it high.  
    PORTB |= _BV(5) ;

    TCCR1A = 0 ;
    // CTC mode, clock divide by 64...
    TCCR1B = _BV(WGM12) | _BV(CS11) | _BV(CS10) ;
    OCR1A = OVERFLOW_COUNT ;

    // Turn on the overflow interrupt...
    TIMSK1 |= _BV(OCIE1A) ;

    sei() ;

    // Turn on interrupts, and the pin will chug away
    // at 700Hz.    

    for (;;) ;
}

Discussions