Author Topic: AVR ATmega32 Frequency Modulation - Quick start (Audio/tone/blockwave generator)  (Read 4756 times)

0 Members and 1 Guest are viewing this topic.

Offline RMS95

  • Contributor
  • Posts: 12
  • Country: nl
    • RMS95
Hi EEVBloggers,

Here is another tutorial, this time I will show you guys how to create a block-wave at a specific frequency. Connecting this to a speaker will give you a tone generator.
Since I saw someone having trouble to fix this, I decided to make a tutorial.
It works on an ATmega32, and with a little effort it works on most ATmega micro controllers with a 16 bit timer.
Though, after reading this article you really don't need this code anymore because you will know how it works and get it to work on any micro controller. =)
I refer to the following datasheet of the ATmega32a when using pagenumbers or registers:

So, how does it work?
The basic idea behind this is to create a PWM wave that has half the compare value(OCR) of the period (ICR).
A 50% PWM is actually already a block wave. Adapting the PWM counters limit results in adapting the frequency.
Code: [Select]
    ____    ____    ____
____    ____    ____
    ^ Start
        ^ Compare,  OCR1, set this to half ICR1 to get a block wave
            ^ Reset, ICR1
This will result in a block wave. By increasing or decreasing the maximum you can set different frequencies.

So, you have to set your micro controller to (fast) PWM mode, and set the limit to the period of the desired output. Then divide that by two for the compare value (or bitwise right shift by 1).

Put this code into your project if you don't want to make it yourself in the first place
Code: [Select]
/// Use this once to initialize, call only once.
void timer1_init()
/// Set PORTD5 as output
DDRD |= 0b00100000;

// You might want to initialize registers here instead.
/// Set to the given frequency
void timer1_set(int frequency)
/// Disable timer at 0Hz
if (frequency == 0)
TCCR1A = 0;
TCCR1B = 0;

/// Set settings registers
/* The settings you will be interested in:
WGM11|WGM12|WGM13 = FAST PWM mode ICR1 as top, see datasheet page 114
CS10 = No prescaler, you might want to change cs10-12 to create a slower acting generator. See datasheet page 115
TCCR1A |= (1<<WGM11)|(1<<COM1A1);
TCCR1B |= (1<<WGM12)|(1<<WGM13)|(1<<CS10);

/// Write frequency
frequency = F_CPU / frequency; // Note that 1 / F = t. F_CPU should be the timer frequency, which is in this case the same as F_CPU
ICR1 = frequency; // Set max to the period
OCR1A = frequency >> 1; // Set compare to half the period

Call the timer1_init() function at the start of your main.

Set the frequency
Whenever you want to set the frequency in Hz, use timer1_set(0); to stop the generator.

More info
I do strongly recommend reading the datasheet of your micro controller to check if you are using the best settings for your project instead of just using ctrl+c/v. =)
I tested this at the ATmega 32a, but I guess it will work on comparable devices such as the 16, 128, etc. without large modifications.
With some slight adapts it will work on most 16 bit PWM channels.

Depending on your demand you might want to set the TCCR registers at the initialization.

You might be interested in setting the prescaler as well if you need slower PWM.

You're all done!

Hope it helped you understanding block wave generation @ AVR!

PS: This is my second at last Quick Start for now, if you like to see another Quick Start about something related to micro controllers or software development contact me!

Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo