Cappels' project pages
Return to HOME
A 1 Khz Digital Sine Wave Signal
The fundamental digital sine wave
signal generator, with the frequency fixed at 1 kHz.
AVR controllers equipped with an 8 bit I/O port.
1 Khz From a Quartz Crystal
• 1 V P-P Sine Wave Calibrated Output
• 1 KHz Square Wave Output
• 900 mv Inverted Sine Wave Output (Uncalibrated)
of those resistors above the micro controller form the R-2R DAC.
Download the WINAVR main source for the 2313 version of the firmware: 2313sine.c
the complete zipped WINAVR project for the 2313 version of the
Download the WINAVR main source for the 8515 version of the firmware: 8515sine080210B.c
Download the complete zipped WINAVR project for the 2313 version
of the firmwre: 1_KHz_sine_wave_8515.zip
Find updates at www.projects.cappels.org
If found myself in need of a 1 KHz signal source for an experiments. My
function/sweep generator was needed as a pulse generator for the same
experiment, so I went though my junk box, looking for circuits from
long ago that might fill the need, but found nothing useful.
I had a board with an AT90S8515 and an 8 bit resistor ladder network on
it, which serves as a DAC (digital to analog converter), so I thought
"This will be easy." and sat down to write the code. Since I wrote it
in C rather than assembly, writing the code took much longer than I
expected, but in the end, I managed to get it to do what I wanted it to
do, and I fixed the typographical errors.
The little generator with the AT90S8515 worked well enough for that
experiment, that I decided to build up a version that uses the
ATTINY2313 or AT90S2313 (I tested the firmware on both chips) so the 1
kHz sine wave generator would be available for some future experiments
that I am planning.
At this point, you might wonder at my choice of an AT90S2313 instead of
the ATTINY2313. I still have lots of AT90S2313's and intend to use them
where I can. There is not much of a market for obsolete controllers.
Code is provided for the ATTINY2313/AT90S2313 and the
ATMEGA8515/AT90S8515. The ATTINY2313 hex file was tested on both the
ATTINY2313 and the AT90S2312, while the AT90S8515 hex file was only
tested on an AT90S8515. The AT90S8515 hex file is fully expected to run
on the ATMEGA8515 without any problems, provided that the ATMEGA8515 is
operated in the AT90S8515 compatibility mode (see below).
It should be a (nearly) trivial exercise to re-target the code to
compile on any AVR controller with an 8 bit I/O port.
The circuit has three outputs.
Two sine wave outputs and one scope sync output.
I would like to explain the new
technologies developed for this circuit, but there are no new
technologies involved. The
firmware sends 8 bit values corresponding to 32 different values
per cycle of the firmware, and this appears on an output port, to which
is connected an 8 bit DAC. The output of the DAC. Since the sine wave
frequency is 1 kHz, a new 8 bit value is sent to the output port every
1/32,00 seconds (every 31.25 microseconds).
This is the simpler circuit. No
calibrated output and no scope sync output.
The DAC is made up of 9 each 20 k Ohm and 7 each 10 k Ohm
resistors, connected as an R-2R ladder network. The output of the DAC
shunted by a voltage divider, whose resistance is 2.8 k Ohms, which
simultaneously decreases the amplitude of the the sine wave to about
900 millivolts. Without the voltage divider (6.8k and 4.7K, the output
would be nearly 5 volts peak-to-peak.) An integrated circuit DAC
would have worked just fine, and I have some laying around, but
somehow, it was more satisfying to make my own DAC with resistors.
Take note, that I used 5% resistors in the DAC, and if the resistors
had been at the edge of their specification, the monotonicity would
have been about 4 /12 bits. Fortunately, the resistors that I have
bothered to measure are much closer to their specified values than
this. Even more important, is that in this application, particularly
when only generating a relatively coarse 32 values sine wave, a little
error in monotonicity only results in as slight increase in harmonic
content, and the low pass filter takes care of it.
Spurious signals in the output were about 40 db below the 1 kHz
carrier in the original circuit, but a I added a capacitor to the 2313
circuit that is also in parallel with the output
of the DAC forms an approximately 1.5 kHZ single pole low pass filter,
which is enough to bring the spurious signals to about 50 db below the
1 kHz sine wave, while not affecting the amplitude of the 1 kHz signal
much at all.
The reason its nice that the low pass filter does not affect the
amplitude of the 1 kHz signal is because this reduces the amplitude's
dependency on the corner frequency of the filter. The capacitor could
drift quite a bit and not affect the amplitude of the 1 kHz signal much
because its not affecting the amplitude much in the first place. If I
had placed the corner frequency much lower, it would have more of an
affect. As it happens, the .047 uf capacitor that I used in the filter
is made with polypropylene, which has a temperature coefficient of
about 0.02% per degree C around room temperature.
A voltage follower buffers the signal, which is AC coupled to the
output through a 100 uf capacitor. A second output from the buffer
drives an inverting amplifier that has a gain adjustment. The second
amplifier can be adjusted to provide a calibrated 1 volt P-P output.
The most significant bit from the DAC output is a 1 kHz square wave.
Its phase slightly leads that of the sine wave outputs because of the
low pass filter on the sine wave. The 1 kHz square wave is useful for
triggering my oscilloscope.
A simpler version, shown below, before I got carried away adding the
output with the calibrated amplitude and the square wave output is
Just to give you an idea of what the
ATMEGA8515/AT908515 version would look like, here, below, is a picture
of the board I used for the first signal generator. Please not that the
circuitry located above the controller, except for the crystal, is left
over from other projects.
Most of the components above the
micro controller are not used in this project,
and a just left over from
This is essentially the same as
the ATTINY2313 circuit.
This board was originally used in a
software phase locked loop, running AttoBasic
and it will likely be used for other projects in the future.
The schematic above does not show the power supply, but I think it
should be easy enough to come up with your own, or adapt the power
supply from the ATTINY2313/AT90S2313 circuit. If you are going to use
the ATMEGA8515, please remember to set the 8515 compatibility mode. See
the example for the STK-500 below.
The image above shows the Fuses
panel on the STK-500.
My only cautions for construction are to be careful about noise pickup
in the R-2R ladder network, and of course, put the 4 Mhz crystal in the
load capacitors close to the controller. The accuracy of the crystal
determines the accuracy of the 1 kHz sine wave output.
You can look at the firmware as being made of three parts:
Initialization, the main loop, and the timer interrupt routine.
During initialization, the I/O ports are initialized. Significantly,
8 bit port is set up as an output so it can drive the DAC.
Also during initialization, a 32 value into an array is loaded with
values corresponding to 32 values in a sine wave. I could have gotten
by with just 8 values representing 90 degrees, and played some tricks
make them into 32 steps -fairly common practice, but why bother when
there is plenty of RAM in the AVR controller? The reason I used
an array in RAM instead of just using the constants in program memory
is because its nice and fast, and if I was lucky, I could get by
without having to be very careful about execution speed.
The 8 bit timer are set up during initialization to cause an interrupt
every 1/32,000 of a second, and then just before the main loop,
interrupts are enabled.
Here, the 8515 differs from the 2313 version. In the case of the 8515
version of the code, the controller does nothing but idle in a do
nothing loop while waiting for an interrupt. In the case of the 2313
code, the micro controller sleeps and waits for an interrupt within a
The reason for the 2313 code sleeping is that I feel it is a better
practice because the time to service the interrupt will be consistent,
and thus, there would be much less chance for jitter in the interrupt
latency time, and there fore less chance for jitter in the output
I looked at cycle-to-cycle jitter with a digital oscilloscope, with a
resolution of 250 nanoseconds per division, and did not see any jitter
with either the 8515 version of the code nor with the 2313 version of
During the timer interrupt routine, the timer is reloaded for the next
timing interval, which is a total of 1/32,000 second. The actual number
loaded into the timer is smaller because of all of the overhead
involved in waking from sleep, going to the interrupt vector, and
saving registers, etc. before even starting to reload the timer
The intertupt routine fetches the next value from the array the pointer
incremented. If the pointer then points past the
32nd value in the array, it is set back to the first value.
That's all there is to the firmware itself.
The code was compiled with AVR-GCC 3.4.6. The 2313 version of the
firmware was compiled with the AT90S2313 as the target and the
resulting hex file was tested on both the AT90S2313 and the ATTINY2313.
IF YOU FIND THIS PROJECT ON AN
INTERNET DOMAIN OTHER THAN CAPPELS.ORG, PLEASE SEND EMAIL TO ME.
Contents ©2008 Richard Cappels All Rights Reserved. Find updates
First posted in February, 2008. Revised
March, 2008, March 2012.
You can send email to me at
"(at)" with "@" before mailing.
presented on this page is for personal, nonprofit educational and
use only. This material (including object files) is copyrighted by
Cappels and may not be republished or used directly for commercial
For commercial license, click
and intellectual property notice
(Summary: No warranties, use these pages at your
own risk. You may use the information provided here for personal and
educational purposes but you may not republish or use this information
for any commercial purpose without explicit permission.) I neither
express nor imply any warranty for the quality, fitness for any
particular purpose or user, or freedom from patents or other
restrictions on the rights of use of any
software, firmware, hardware, design, service,information, or advice
mentioned,or made reference to in these pages. By utilizing or relying
on software, firmware, hardware, design, service,information, or advice
provided, mentioned, or made reference to in these pages, the user
takes responsibility to assume all risk and associated with said
activity and hold Richard Cappels harmless in the event of any loss or
expense associated with said activity. The contents of this web site,
unless otherwise noted, is copyrighted by Richard
Cappels. Use of information presented on this site for personal,
educational and noncommercial use is encouraged, but unless explicitly
with respect to particular material, the material itself may not be
or used directly for commercial purposes. For the purposes of this
copying binary data resulting from program files, including assembly
code and object (hex) files into semiconductor memories for personal,
educational or other noncommercial use is not considered republishing.
desiring to use any material published in this pages for commercial
should contact the respective copyright holder(s).