Path: Home =>
Sine generators => Variable sine m324
||Tutorial for learning avr assembler language of|
AVR-single-chip-processors AT90S, ATtiny, ATmega, ATxmega
of ATMEL using practical examples.
Variable frequency sine generator m324
Variable frequency sine wave generator with ATmega324
If you need a variable frequency sine wave generator you can build that.
It has the following properties:
- 1x8 LCD for frequency display in Hz,
- two potentiometers to regulate the resolution and the delay
- active key to update the adjusted parameters, no automatic
- 8-bit R/2R network,
- selectible and configurable crystal,
- either 256, 128, 64, 32, 16 or 8 resolution steps,
- large 16-bit delay counter, usable for very low frequencies,
- very fast algorithm (minimum cycle length = 17 cycles).
This is what it needs.
The generator has
The ATmega324 was selected because it is one of the rare AVR types that
- the usual R/2R network with 8 bit resolution, attached to Port D,
- a crystal for exact timing, here: 16 MHz,
- a driver that protects the network and provides enough output power,
here a 741 operation amplifier (CA3140 or TL071 would also fit, but
provide less output power than the 741),
- an LC low-pass filter that rips off remaining rectangles,
- a standard six-pin ISP interface to program the controller within
The PA sub-type of the ATmega324 was selected because it is cheaper than
other sub-types, at least at my dealer.
- two complete ports with 8 bits each available, one for the LCD's
bi-directional data bus and one for the R/2R network, and
- the option to be clocked with an external crystal, and
- it has ADC channels for measuring the potentiometers, and
- it provides an external INT that can be configured to interrupt
on falling edges solely (INT2).
The LC network for filtering the higher rectangle frequencies produced
by the R/2R network depends from the frequency range that the generator
shall provide. If you want to operate the generator with smaller
resolutions, such as 8 or 16, to achieve higher frequencies or a finer
resolution, the clipping frequency has to be smaller than your desired
highest frequency, muliplied with the smallest resolution.
I built my one with L=3.3mH and C=150nF, the output capacitor is 470nF.
This is the PCB layout. Shown are half size pictures, clicking on these
displays the originals (download with right-click and Save file as ...).
Red dots in the component picture are 0.8mm holes, violett ones are 1.0mm.
The total size is 80-by-100mm (half Euro size). Note that I used a coil
with 22µH instead of the 10 that is listed in the schematic.
If you have Linux, you can work with tgif on the originals. The copper
side and all components in color layers are
here, the component plan is
Note that earlier versions of the schematic had the LCD control lines
on different port-pins.
When starting the controller up, it generates the selected frequency that
is to be defined in the software. If the key on PB2 is pressed, the two
variable resistors attached to ADC0 and ADC1 determine
The start-up frequency has to be defined in the source code.
Selecting the start-up frequency, you can either
This means entering the desired combination in the header of the
assembler source file, in the section Adjustable constants. For
all cases the Assembler determines the actual effective frequency
(in mHz), so you can decide whether the accuracy is good enough.
Just inspect the frequency at the end of the assembler listing in
the section Symbol list, if you use gavrasm or avr_sim for
assembling, other assemblers unfortunately find this kind of
information unnecessary, so you'll have to use hyper-intelligent
work-arounds to get this type of information.
- define the desired frequency (in mHz) and leave the selection of
the resolution and the delay constant for the Assembler, or
- the frequency and the resolution (256/128/64/32/16/8), for
which the Assembler calculates the delay constant, or
- the frequency and the delay constant (0 to 65535, 0 is 65536!),
for which the Assembler determines the necessary resolution, or
- define the resolution and the delay constant.
Note that the names of the constants cFreq, cFrequency,
cDel, cDelay, cRes and cResol have been
chosen willingly, even though they often have the same value in the
short and in the longer constant name. The longer names keep what
the user actually has defined, while cRes and cDel are the results
of calculations, if that is necessary. As in a 2-pass-Assembler the
user input will be lost if those names would be used in calculations,
I decided to introduce this convention in this section.
Changing the frequency with the potentiometers
If the user presses the key, the sine loop is immediately stopped
and the voltages on the two input pins ADC0 and ADC1 are measured.
The voltage on ADC0 linearly determines the resolution: the AD
result is divided into six sections, by first dividing the result
by 4 and then continuiesly subtracting 43 and shifting an adder
left, as long as no carry happens. The adder starts with 1.
The conversion of the ADC1 result to delay values is a little
bit different. The AD result is divided by 64 and one is added.
This number is multiplied with the original AD 16-bit result.
At maximum this yields 65,472. That limits the lowest frequency
range slightly (from 262,157 down to 261901, at 16 MHz
from 0.2384 to 0.2386 Hz).
This shows why it is recommendable to either use a 10-turn
or a log potentiometer: at small delay constants the frequency
changes more rapidly (see the division section below).
The new values come immediately in effect, if the debouncing
protection time of 250 ms has been absolved without
additional external interrupts.
This is the small LCD. The pins are shown from the upper (display)
side. The pins are in two rows and in distances of 2.54 mm
per pin, the PCB layout below is designed for that format.
As I have foreseen a very small LCD with 8 characters on one
single line, calculation and display of the frequency is a little
Calculating the frequency
The calculation is as follows. First, the clock frequency,
multiplied with 1,000 (for getting the result in mHz) and
divided by 256 is placed in a 64-bit register area. The adder,
that determines resolution, is shifted to the right and, as long
as no carry occurs, multiplies the 64-bit register area repeatedly
by two. The divident is then complete, its lower 32 bits hold
the divident and its upper 32 bits are for dividing.
The delay constant is written to a 32-bit wide register area. It
is multiplied by 4 and 13 is added. This will be the divisor.
The 64-bit divident representing the multiplied clock frequency is
then divided by the 32-bit divisor to yield a 32-bit result. The
result is the frequency in mHz.
The 32-bit integer is then converted to decimal ASCII, with the
decimal dot behind the Hz. This conversion uses a space in SRAM,
examples look like this:
0001234567.123 ; All three decimal digits are non-zero
0001234567.120 ; The last decimal digit is zero
0001234567.100 ; The last two decimal digits are zero
0001234567.000 ; All three decimal digits are zero
0123456789.123 ; The number is larger than 99,999
Formatting the displayed frequency
In the first step all zero digits in the mHz are removed. That
means that the second number in the above list will be shorter
by one digit, the third by two digits. If all mHz digits are
zero, those are removed together with the preceding decimal dot.
Removal is done by shifting all digits one, two or four
positions to the right and inserting ASCII zeroes to the left.
Then all preceding ASCII zeroes are replaced with blanks. If the
first non-blanked digit is too far to the left (the number has
more than eight digits) digits of the mHz behind the decimal dot
are removed (removed digits from 5 and above round the preceeding
The eight digits are then finally written to the LCD.
Testing the hardware
For diverse purposes I have developed a software which allows
to separately test the components of the sine generator. The
source code for that is available
here. If the LCD shall
also be tested, the include routines in
lcd.inc have to be placed
to the same folder.
in the section Debugging tests of the source code you
can determine which tests should be performed (the respective
test is set to Yes, only one test procedure can be
active, none or more than one force errors when assembling):
A hint: When I tested the hardware, the LCD did not work at
all. After many hours of searching I found the reason: the
pins of the LCD are very small, and the socket expected larger
ones. So at least one of the 14 pins had no contact. I replaced
the plug with a better one, and it worked. Never use unsuited
sockets, rather transfer them to the trash can.
- testR2R: On the R2R port the number is placed
that is determined with the parameter testR2ROut.
- testSine: The two parameters testSineRes
and testSineDly are used to generate a sine on the
R/2R port. Using 128 for testSineRes and 28 for testSineDly
brings a perfect 1.000 kHz sine to the R/2R network.
- testKey: The attached key is read. If open, the
R/2R voltages is set to 3.0 Volt, when closed it is
- testAdc0, testAdc1: This measures the voltage on
the two ADC inputs and adjusts the R/2R output to the
- testPortC: The port C (data port to the LCD)
is made output and all pins are pulled high.
- testLcd: The LCD is initialized and the text
"SineM324" is displayed.
The software for the sine generator
The software is, in assembler source format, available
here. Download it,
edit the respective parameters in the section Adjustable
constants, download the
lcd.inc and copy it into
the same folder. By either defining cOpAmp741 or cOpAmpCA3140
select the curve form. If you use a different opamp, you'll
have to use the sheet SineTable in LibreOffice Calc
file here to make
your own sine table and insert it and the end of the source
code, adding it with .ifdef cMyOwnOpAmp and .endif.
Place the activator with .equ cMyOwnOpAmp = Yes to
the adjustment section and disable the other two with
Then re-assemble and burn the hex code to the flash's memory.
Do not forget to change the following fuses:
- Disable JTAG, otherwise the LCD will not work properly.
- Disable CLKDIV8, otherwise your sine will be too slow.
- Select an external crystal as clock source, otherwise
it will be too slow.
This is what the generator produces with a resolution of
256 and a delay of 1: a sine wave with 3.676 kHz.
This is what results from a resolution of 256 and a desired
frequency of 1,000 Hz. Unfortunately the integer math
produces a frequency of 961.538 Hz as the nearest
possible. Not very accurate.
This is the result if you reduce the resolution down to 8.
The frequency is nearer to 1,000 Hz, but the sine
looks rather angled.
To the top of that page
©2020 by http://www.avr-asm-tutorial.net