# Simple function generator using microcontroller

Is it possible to make a function generator with a microcontroller (PIC18F4550 OR PICAXE20X2)?

I am thinking that for generating square waves it can be enough to make a pin output high and low on desired time frames. But how about a sinewave? Maybe it is possible with the same idea but putting a inductor or something at the output?

If you generaly think this can work I want to start doing it!

UPDATE First of all I have to thank all the answers, it really gave me some nice start points.

I have a rather good experience with PIC18F4550 as I did lots of small home project around this chip. So it would be my main choice. Here is a link to its datasheet

My main points is now as following: 1- Best way and mathematic approach for square waves 2- same thing for sine waves 3- Amplitude and frequency control

• Can you describe the application in more detail? Jul 18, 2011 at 17:15
• @endolith Well as a beginner I am just asking this question to see if it will be a real goal for me. The application I would like to have is a basic function generator which can generate atlease square and sine waves with variable frequenty and pulse width(for sq wave). Jul 18, 2011 at 22:49
• At what frequencies? Audio? MHz? It's certainly a realizable goal. Jul 18, 2011 at 23:02
• Well I am thinking about 1Hz to 5MHz but I am not sure about the maximum, that is if it is possible with a microcontroller? Jul 18, 2011 at 23:11
• @sean87 - Could you add links to the controllers' datasheets? We're trying to make users aware of the importance of this, especially for less common parts, so that others don't have to go searching for it and that everybody is sure to be talking about the same thing. Just trying to cultivate good habits. TIA Jul 19, 2011 at 8:57

I am thinking that for generating square waves it can be enough to make a pin output high and low on desired time frames.

That depends on your application. This is considered "naive" square wave synthesis, and doesn't produce a mathematically correct square wave. (It's equivalent to sampling an ideal mathematical function without putting it through an anti-aliasing filter first.)

This also applies to triangle waves, sawtooth waves, and anything else with harmonics above the Nyquist frequency.

It will often be "good enough" if you have many samples (or time frames) per cycle, but not otherwise. For example, if you generate a 10 kHz square wave with a 44.1 kHz sampling rate, it will look like this:

You can see that every few cycles are different lengths. The transitions can only occur on sample boundaries, but an actual square wave would transition at a time in between them. Practically, this results in lots of aliased harmonics below the square wave frequency, which you probably don't want, depending on your application. In audio applications, this sounds awful.

You can avoid this by generating a correct band-limited square wave in software, or by using a sampling frequency much higher than necessary for your signal.

Here's a comparison of the two methods on a 5 kHz square:

Simplistic:

Mathematically correct (generated with additive synthesis):

• If the output is passed through a good filter, a properly-computed anti-aliased squarewave will yield a signal indistinguishable from passing a perfect square wave of the correct frequency through the same filter. That having been said, one should note that the correct shape for the anti-aliased square wave should depend upon the ratio of the square wave frequency to the sampling rate. Jul 18, 2011 at 16:20
• @Olin: The question is about a general purpose function generator that can output sine waves. It's certainly not going into a logic gate. The output is not "being sampled to reconstruct an analog signal". The output is already sampled. Jul 18, 2011 at 16:23
• @Olin Lathrop: Even if the output from the square wave will drive a logic gate, adding extra analog stuff can result in a cleaner output waveform since it allows the output gate switching to occur at times between samples. Linear filtering may be overkill; it may in some cases be better to use an RC filter and compensate for its non-linear delays in software. For some applications, one can ignore such issues and simply tolerate a sample's worth of jitter. In others, though, jitter can be amazingly annoying. Jul 18, 2011 at 16:24
• Hey guys - This is getting a little busy and heated for a comment exchange. Can y'all tone it down a bit, and perhaps take it to chat? I've created a room specifically for this question here. Jul 18, 2011 at 22:00
• @Sean87: Amplitude in general is just a multiplication by a constant. Halve the amplitude by dividing the samples by 2. How to actually implement this depends if you're using PWM or a DAC or something else. Jul 19, 2011 at 0:42

Jesper developed a DDS (Direct Digital Synthesis) function generator around an AVR, controllable via EIA-232 and PC.
Schematic and assembly code are simple and available on his website, so you should be able to adapt them for the PIC. Or you can simply use the AVR version.

Yes, a PIC can be used to make a function generator, especially since you didn't specify a accuracy or frequency.

If you want a arbitrary digital pattern, you can put the serial data in a table and use a SPI port to update the output pin one bit at a time from hardware. The firmware only needs to reload the hardware every byte. For a little slower, this can be done in a periodic interrupt without the SPI hardware.

For making analog signals, the easiest is to low pass filter a PWM output. Once again, there is a speed versus resolution tradeoff. With the PIC 18 running at 10 MHz instruction clock, you can get 8 bit resolution at 39 kHz. Even with just a bunch of passive resistors and capacitors, this can make reasonable quality voice audio.

For a higher resolution*frequency tradeoff, you can use a external D/A converter.

Unless this function generator is for a specific application and the PIC 18 is good enough for that, I'd use a PIC 24H instead. Those can handle 16 bits at a time and run at 40 MHz instruction rate. That gives you a 4x better PWM resolution*frequency tradeoff. Some of the dsPICs also have one of the two special high frequency PWM modules. These sortof run off a 16x higher internal clock to get nearly 1ns pulse width resolution. Watch out for jitter and restrictions on the duty cycle though.

• Some of the dsPICs also have built-in DACs as well. Some give you nice 16-bit DACs for audio use. Jul 18, 2011 at 12:23

For a sine wave the best way is to use what is known as a Wave Table. This is basically a list of discrete values to pass to a DAC to generate a waveform.

It uses more storage memory than generating a sine mathematically, but is far simpler and more efficient from a processing point of view. Plus, it has the advantage that the waveform generated can be changed by changing the wave table data.

There are many ways of interfacing a PIC to a DAC, and there are many ways of making a DAC yourself. It all depends on the resolution you require for the final waveform, and the frequency ranges you are looking to generate, and how accurate you want the output.

• One note not mentioned in the Wikipedia article is that wavetables are often played at a rate other than one wavetable sample per output sample, there are a variety of means by which the rate can be scaled. The easiest is to index the table using the upper bits of a phase accumulator. This works, but will generate unawanted harmonic content extending down to half the sample rate minus the highest frequency present in the input signal. If your output sample rate is 44KHz and your desired signal is a 300Hz sine wave, not a problem. But... Jul 18, 2011 at 16:32
• ...if you're trying to output at 48Khz a waveform sampled at 44KHz that has harmonic content extendingto 20KHz , it's going to sound bad. Another approach is to use linear interpolation between each sample and the next one. This generally won't eliminate nasty harmonics, but will substantially attenuate them. Harmonic decomposition and reconstruction is the cleanest approach, but it's computationally expensive. Jul 18, 2011 at 16:34

To begin to get other waveforms, you first have to work out how to acheive more than the two levels out of pin than digital outputs provide. There is a type of integrated circuit called a Digital to Analogue converter which is specifically designed for this task. A recommended way to use a DAC would be to connect to it over a serial link such as I2C or SPI, which minimises the number of pins you need on the micro compared to a parallel input DAC. Once you have got that working, you can send the sinewave or whatever waveform using either numbers that you calculate on the fly using a sinewave formula or from a look up table. Note that the lookup table for the sinewave only has to contain one quarter cycle of data, the other parts of the waveforms are repeats or inversions. A formula calculation will be slower than a lookup table, so your choice here depends on desired output frequency. However, for triangle and sawtooth waveforms, the formula is so simple that it would not be worth bothering with a lookup table.

Another approach is to use a 1-bit DAC approach, as described in theory here, and in an interesting example which is PIC specific here which is designed for simulating speech, so would only be usable for low audio frequencies.

Whilst a filter is part of the solution, it would not be enough to simply add an inductor on the output pin, and that would not be recommended on a digital IO pin because it creates a flyback voltage when it is switched off which will damage the output transistors.

• You talk about DACs, but not about how to generate the waveform. Jul 18, 2011 at 11:32
• @Martin, in my experience SPI on almost every micro is easier to get working then ADCs, often very simple commands to have working. And yes, you really did not discuss a solution to the problem. Jul 18, 2011 at 11:43
• @Kortuk In my experience SPI, as it has no real standard, can be a real bitch to get going sometimes. Some systems need leading edge, some trailing edge, some idle high, some idle low - so while the commands to set it up may be simple enough, getting the commands correct for the specific combination of µC and slave device can be somewhat tricky. Jul 18, 2011 at 12:10
• @Kortuk And then you have the problem of devices that don't use multiples of 8 bits for their data. That can be even more daunting to the uninitiated. Jul 18, 2011 at 12:11
• @MattJenkins, I am referring to sending bytes is normally easy. yes, getting them to do what you want can be challenging based on the level of complexity of your device. Having a shared bus between chips where some need to be serviced in short time periods can take some work. Jul 18, 2011 at 12:32