Go Down

Topic: Audio out using PCM (synth trial runs) (Read 2 times) previous topic - next topic

jamisnemo

Hello everyone!

In advance, sorry for all this text. I want to make a tutorial out of this...

I got an Arduino for Christmas with the hopes of making a subtractive synth out of it. In a perfect world I would like to have at least 2 oscillators (perhaps using PWM on outputs 9 and 10 on the board?) and be able to select each oscillator's wave form individually from a choice of a square, a triangle, a sawtooth or a sine wave.

I was able to make some progress using the piezo speaker example but that method is really only capable of producing square waves. I could of course convert the outputted square wave to whatever form I wanted externally (along with the phasing and stereo effect I was able to get working) but that's not really not clean enough for me. Along with requiring external signal processing, I couldn't get the square wave frequency range wide enough for the application.

So I want to move onto using Pulse Width Modulation to create the oscillators. I tried starting with a triangle wave. (it's hard to program a sine wave without a sin function.... anyone know how math.h is ever supposed to fit in ram?? or maybe even create a useable sine table??? ... anyways). I looked around and couldn't find any clear tutorials of how to do this so I figured I'd put what I figured out here... sorry if this info's repeated or if it's not exactly how it should be done.


The REAL problem is that I need to filter out the PWM's carrier signal to leave me with a clean analog waveform. This means I need a low-pass filter with a cutoff frequency above (hopefully) 20k Hz but below the PWM's carrier frequency. As stated on the analogWrite reference page, "The frequency of the PWM signal is approximately 30769 Hz".

After a few google searches I found this page that discusses how to make a low-pass filter that will be sufficient:
http://ww1.microchip.com/downloads/en/AppNotes/00538c.pdf

It mentions that a good cut off frequency for the filter should be at least 2 times greater then the highest frequency that you want to output. In (hi-fi?) audio applications, 20k Hz is usually the highest you ever need to pass.

20k Hz * 2 = 40k Hz > 30.769k Hz

By calculating this out, it seems, at first glance, that we are out of luck. The cut off frequency will still leave the carrier signal in the line. Not good. Maybe we won't have the Fundamental harmonic in there (higher then 30.769... calculate it out if you want...) but the carrier signal (though out of the audible range) is still going to be there.

But we can get around this. We are able to change the carrier frequencies of Arduino's PWM outputs. Looking around on the forums I found a post hinting at how we change it. (tell me if the link doesn't work...):
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1152547089/2

I've copied the relevant code snippet into this post to put it all in one place:

Code: [Select]
#define TIMER_CLK_STOP            0x00      ///< Timer Stopped
#define TIMER_CLK_DIV1            0x01      ///< Timer clocked at F_CPU
#define TIMER_CLK_DIV8            0x02      ///< Timer clocked at F_CPU/8
#define TIMER_CLK_DIV64            0x03      ///< Timer clocked at F_CPU/64
#define TIMER_CLK_DIV256            0x04      ///< Timer clocked at F_CPU/256
#define TIMER_CLK_DIV1024            0x05      ///< Timer clocked at F_CPU/1024

void Setup()
{
[...]

TCCR1B = (TCCR1B & ~TIMER_PRESCALE_MASK) | TIMER_CLK_DIV8);

[...]
}


Really we can set the frequency by using:

Code: [Select]
void Setup()
{
[...]

TCCR1B = 0x03;  //or whatever setting we want to use...

[...]
}


We want the PWM carrier frequency to be higher then the default. The default I found (after trying all the options listed) seems to be 0x03.


This is where I start to get a bit confused. If 0x03 is the default value of TCCR1B and this value corresponds to "Timer clocked at F_CPU/64" AND if this means the PWM's carrier signal is at around 30.769 kHz then, (unless I'm reading it wrong... and doing my math wrong... which I probably did...):

F_CPU = 30.769 kHz *64 = 1969.216 kHz = 1.969 MHz  != 16 mHz ATMega8 clock

What is going on here? Shouldn't it equal out? What does F_CPU really stand for?....


And thus my final question is this: What value of TCCR1B should I use to get the PWM's carrier frequency above 40 kHz? Or is it there already and I'm just not calculating this right?

I'd love to see a table in the reference page about the corresponding frequencies...


Thanks a lot for the help,
Sorry for all the text, I really couldn't find any real audio tutorials for Arduino and would love to get some synth tutorials out there.

~jamis

mellis

The smaller the prescale factor, the higher the frequency, so to achieve maximum frequency, you want to set the low three bit of TCCR1B to 0x01.  You shouldn't simply assign a value to TCCR1B, as it will write to the other bits (they might happen to already be 0, but it's not good to rely on that).  

F_CPU is the speed of the clock running the chip: 16 MHz.  The frequency of the PWM wave, however, is determined by the timer overflows which happen every 256 increments of the timer.  And the current frequency of the PWMs is around 1 KHz.  So: 1 KHz * 64 * 256 = ~16,000 KHz = 16 MHz.  

The reason this sort of thing isn't in the reference is because it's more advanced than we think is appropriate for the Arduino (like anything that involves messing around with registers and bit manipulation).  A method for setting the PWM frequency, however, has been a frequent request, and I hope to find a simple way to provide that functionality.  Suggestions welcome.  Also, consider writing these issues up for the Arduino playground so that more advanced users can take advantage of the information.

tateu

hey jamisnemo. i'm interested in your code to generate square and triangular signals. i don't care too much about the higher freqs so i'd be fine with the ~31k freq of the pwm and a low pass at 30k. if you don't mind sendin your code, you can email me at tateu@yaoo.com.
i would like to generate audio from the arduino, eventually, my goal is to do some ADC on some audio on analog pins, screw with it and output it back with pwm. i've looked a bit into generating sq signals but got nowhere so far.
thanks in advance for your help.
tateu.

jamisnemo

Well I've run into some issues with this project that will probably require an external chip to solve.

The reason that most computers have a dedicated sound processing card/chip in addition to the cpu is because the logic code needed to create different wave forms can't be run quick enough directly from the cpu itself.

I ran into the problem with the Arduino and cant really find an easy fix. Because the processor only runs at 16 or 20 MHz, it's hard to have accurate frequency control over any oscillator code you write.

For example, if you setup the "PlayMelody" tutorial (http://www.arduino.cc/en/Tutorial/PlayMelody) and use a musical tuner to check the frequencies (such as the 'a' at 440 hertz) you'll find that the frequencies are lower then they should be. The A given in the code doesn't actually give a frequency of 440. This is due to the time delay given while the rest of the code runs each loop. This could be fixed in the PlayMelody tutorial because it's playing a tune that's coded in.

However, the method shown in the PlayMelody tutorial can't be used for realtime frequency generation. The amount of time spent by the Arduino's CPU when executing the analogRead() function puts a HUGE kink in how high the frequency can be.

I set up some code that would switch one of the analog outputs from high to low on every alternating loop. The highest frequency I got (using a software oscilloscope) was rather high. I believe it was in the range of 34k hertz (above the human hearing).

As soon as I added the code to read in a value from an analog input, a delay was added into each loop that made the highest frequency square wave peak out at about 148 hertz (not anywhere near high enough).

I'll redo the experiment and get the exact numbers after I sign up for classes this afternoon but I know for a fact that the numbers aren't high enough for any real time signal generation using the equipment the Arduino comes with alone.


However, for those of us that would like to make synthesizers using the Arduino, there do exist frequency generator chips! A friend sent me this link last night (http://www.makezine.com/blog/archive/2007/01/how_to_make_a_f_2.html) which hints at two such chips (the Maxim MAX038 and the Exar XR2206). I'm currently looking for some samples of either chip to test in conjunction with the Arduino.

Another possible option is to find a "broken" game system from the NES generation. The synth sounds used in many video games is quite tasty for some styles of music and it may be feasible to use a chip from one of those systems.

I'll post again when I find a chip that'll do the job I need.

Tateu: sorry but the code I have doesn't really produce reliable waveforms. I can, however, try to draw up a circuit for a low pass filter along with the formulas needed for the component values.

Anyone know of a good circuit design software...?

~jamis

tateu

hey jamis. thanks for your reply and info. keep posting what you find. i think this subject is very interesting.
for the design soft, everybody seems to favor eagle's pcb. it's free:
http://www.cadsoft.de/freeware.htm

Go Up