Sometimes when tuning various electronic hardware, we need a simple signal generator with different waveforms and frequencies. One of the options is to buy a professional with variable gain professional coating and many additional functions. But if you are a hobbyist, you might want to build one. This small project is dedicated to making one of those signal generators.
AVR DDS signal generator consists of the following parts:
- Atmel Atmega8 8-bit microcontroller;
- Supply source and voltage regulator;
- 2×16 standard LCD and shift register 74HC164;
- Seven buttons;
- R-2R resistor leader for DAC;
- Three outputs: universal(OUT) from DAC, PWM, and pulses;
- Metal case;
- and microcontroller firmware.
Atmega8 microcontroller is a simplest of Atmega series. There is 8kb of FLASH program memory, and the maximum frequency is 16MHz, which is used to reach DDS generators maximum resolution at the maximum rate. For now, frequency is limited from 1 to 65535Hz with the minimal step of 1Hz.
DDS AVR generator is powered with a 9V battery. Voltage is reduced to 5V and stabilized by 7805 voltage regulator.
LCD is controlled using three wires through shift register 74HC164. So this register is used as serial to the parallel converter in order to save microcontroller pins. LCD is controlled in 8-bit mode.
AVR DDS Generator uses seven control buttons:
- Stop, which is a reset also;
- IP- increasing value;
- DOWN- decreasing value;
- Mode1 – signal selection button;
- Mode2 – signal properties;
- Freq – signal frequency multiplier selection.
AVR DDS signal generator uses R-2R digital-to-analog (DAC) converter. This is the simplest solution where resistors are connected in a ladder:
In this schematic, R=10kohm. By using 8 bits and 5V step, a value is about 18.5mV. This is enough for getting average-quality signals.
The generator has three outputs:
- Universal DAC output through the R-2R ladder;
- Impulse (SQ);
Universal output (OUT) is a signal output from DAC. This output forms various signals like sawtooth, sine, square, and triangle.
PWM channel is used to form for PWM signal output – directly from the timer.
SQ channel is an additional channel to form square pulses or second PWM signal. It will be implemented in the future.
The block chart has described the structure of AVD DDS signal generator. You can see the signal paths. Each block part is described in the specification.
Menu and program stages:
After the generator is switched on, the message is displayed in LCD: LCD SCREENS
â€žMode2â€œ button selects the step of frequency from 1 to 10000Hz by pressing it in series. “Up” and “Down” buttons change the frequency value by step value.
After the “Start” button is pressed – the last settings are saved in EEPROM, and signal generation is started.
The above functionality is implemented and tested. In future plans, there are few more signal generation modes. By pressing “Mode1”, you can see the following screens, which may change a little during implementation: LCD Screens1
Circuit diagram and PCB
PCB is single-sided, with some wiring done on the top side.
Circuit diagram explanation:
Microcontroller port pins are connected to:
- R-2R DAC is connected to D port pins 0…7;
- Shift register data line is connected to C port’s 0 pins;
- Shift registers synchronize line is connected to port C pin ;
- LCD screen’s E signal is controlled by C ports pin 2;
- The start button is connected to B port pin 0;
- The stop button is a reset button;
- Up button is connected to port B pin 3;
- Down button is connected to port B pin 4;
- Mode1 button is connected to C port pin 3;
- The mode2 button is connected to C port pin 4;
- Freq button is connected to C port pin 5;
- SQ signal is coming from AVR b port pin 2;
- PWM signal comes out from Port B pin 1.
Part list (exported from eagle): Parts List
In developing this AVR DDS signal generator, There were the following tools used:
- Eagle Cadsoft
- WinAVR 20060421
- Parts (Atmega8, PCB coated with a photoresistive layer, box,…)
- UV exposure unit
- PCB Etcher
- Developer, Etching solution
- Programmer AVR ISP
- Other tools…
Few words about firmware
Program is written in C language and can be compiled with WinAVR20060421 toolset. Programming is done by using AVR ISP and PonyProg programming software.
Program flow in a few words:
When AVR DDS generator is switched on:
- Initialization of LCD;
- Reading previous settings from EEPROM memory and displaying to LCD;
- Using buttons settings and signals can be changed. When the “Start” button is pressed, new settings are saved to EEPROM and signal generation starts.
- Note: After power up, you can press “Start” to start last saved signal generation immediately;
- Generator is stopped by pressing the “Stop” button which resets the generator. After Reset generator again loads its lasts configuration from EEPROM memory and is ready to start a new generation.
EEPROM memory stores following data: EEPROM data
- 0 – OUT_| ¯|_;
- 1 – OUT|/|/|;
- 2 – OUT|\|\|;
- 3 – OUT/\/\/\;
- 4 – OUT~~~~;
- 5 – OUT-NOISE;
- 20 – PWM-OCC;
- 21 – PWM-SINMDS;
- 22 – PWMSQSMDS;
- 23 – PWM-CMDS;
Frequency value is divided to 3 EEPROM bytes. Because max theoretical frequency is 16MHz which hexadecimal value is 0XF42400:
Duty value is from 1 to 99 in percents.
LCD_3w.h – LCD settings
LCD_3w.c it contains LCD control functions
- void sendByteToRegister(uint8_t);
- void LCDenableCommand(void);
- void LCDdisableCommand(void);
- void LCDenableData(void);
- void LCDdisableData(void);
- void LCDsendChar(uint8_t); //forms data ready to send to 74HC164
- void LCDsendCommand(uint8_t); //forms data ready to send to 74HC164
- void LCDinit(void);
- void LCDwritebyte(uint8_t, uint8_t);
- void LCDdefinechar(const uint8_t* ,uint8_t);
- void LCDclr(void);
- void LCDhome(void);
- void LCDstring(uint8_t*, uint8_t);
- void LCDGotoXY(uint8_t, uint8_t);
main.c – main program text where:
- signal tables are stored;
- messages to LCD are described
- EEPROM initialization is done;
- changing parameters are implemented;
- signal generation (using in-line ASM routines) is performed.
<h2 > Testing
For now testing is done by using frequency counter and Oscilloscope. In a frequency range of 1 to 65535Hz works OK. There can be greater frequency range programmed but with less resolution. I think for audio equipment testing there is more than enough frequencies.
As I mentioned earlier, this is a first trimmed version of AVR controlled generator. It has half of working functionality in many cases this can be enough. Main signals are generated and can be controlled. In the future, the functionality can be expanded to add PWM generation using timers. A generator generates signals as they are – directly from DAC. There is no variable gain regulation. To be a more interesting project there can be VGA amplifier used with feedback to control output signal voltage. The project source code is open to modify and expand functionality. If there will be some good modifications – feedback is always welcome.
You can download AVR DDS Documentation and AVR DDS Source files as they are.
More pictures from the project:
Download Eagle project files
Some software writing ideas were taken from Jespers Mini DDS.
clean wave! The PWM isnt used for now?
Did you write the sine-function? When i looked at the math.h of avr, it seemed empty with only two functions implemented.
The sin function is precalculated.
Now PWM isn’t implemented yet. For Sine wave and other signals I used look up tables – look at the source code.
It looks cool, but your DAC is too dependant on the input impedance of your test circuit. I would recomend an op-amp in a voltage follower configuration for your DAC output.
I know that Op Amp would be great to solve this problem. This is why this generator is called simple 🙂 In a future I will probably put variable gain operation amplifier and put some feed back to be able control voltage digitally and display it on LCD..
good site…but why not add a simple follower opamp?
As for the captcha, entering the number is hell on my Firefox box… . It always jumps back to the URL field…
Hi, I have a problem.
I have to design a DDS Generator. I work with a avr atmega88 at 18,432 Mhz for constant time-flags i used the Timer0 but I can’t reach more than 10000 Hz with ISR overflow interrupts. In your project you use another way of time-division (asm inline). Can you explain me some tipps how it works, cause i doesn’t unterstand the source well. Thanks
I used 24bit phase accumulator() which determines the signal resolution of frequency. The bigger frequency I am generating the smaller resolution I get.
If you would use 18,432 Mhz crystal, then your:
Then you can generate frequencies:
Accumulator value is updated according to your selected frequency, what means last byte of Accumulator addresses the value form lookup table.
I don’t know if I explained well, but you can see similar project here:
Great work, I’d be interested in building this.
Could you perhaps share the eagle board file, or a high res image?
I have added Eagle project files at the end of article. If you need you may modify or use for any purpose.
but I would like to undersand this
asm volatile( “eor r18, r18 ;r18
eor r18, r18
means R18 XOR R18 register R18 becomes zero. refer to AVR Assembly language documentation.
in fact that’s all the asm code that I don’t understand.
Could you explain it here (or by mail) ?
Is it possible to do it with inline asm (only in C)
you should read “without asm” (instead of “with asm”)
I have written blog post about this issue avr dds signal generator in-line asm explained
Might be helpful
Thanks a lot for the avr dds signal generator in-line asm explained
I have some suggestions
Why not using a DAC800 (very inexpensive DAC) ?
I see in you C code that you use a switch inside the loop.
That’s not a good idea as the condition (mode) is tested during each loop
a good solution for this was given to me by an opensource game creator.
Instead of using the integer variable mode you should use a pointer to function ptr_mode
You make a function for each part of your switch
Instead of assigning a value to the variable mode you can assign the adress of the function &mode_0 or &mode_1 … to the function pointer ptr_mode
So in the loop there is no switch (or if) but the call of the function
which call the “good” function mode_0() mode_1() …
That’s just a trick… but I think it can be usefull in such a case
PS: you can contact me at
I have been thinking about DAC800 or other similar chips. But this project was some sort of a good practice of embedded programming while implementing DDS algorithm. Performance is as good as would be with DAC800 but it works.
Bout programming style where you suggest to use function pointers is good suggestion i think. My code is messy and require clean up and optimisation, but i think won’t stick with it as there is another project in my mind. Anyway nice to to see your interest here. Hope to stay in touch.
this project doesnt work for me , it will show error message after showing splashes .
pleas fix software .
Could you provide more details what kind of errors you get and how do you program Atmega including fuses. Do you use same circuit parts and crystal? Because other were able to duplicate it without problems.
the error shown when the mode is graeter than 23 . atmega an crystal work well in other projects.
i am reading your source code, the error occurwhen mode is greater than 23 , and 23 doesn’t exists in switch cases and default case show error on lcd . im trying to fix it .
—another question .
why you use inline asm for generating signals ?! when start key pressed the program goes to signal generating loop and there isn’t any stop delay , could you write your code in C ?! i can help you to expand your program but i don’t have knowledge in asm .
shankimout, thank you for your analysis…appreciate that.
I understand that there are some glitches with cases in program loop. You are welcome to modify the code.
About inline ASM. One reason why I have used it is to get max performance. With ASM i can get 1 signal sample per 9 MCU clocks. I am using lookup tables with 256 samples per signal period. so one signal period takes 9*256=2304 MCU clock cycles. Using inline ASM i have better control of frequency. It is not dependant on compilation optimisation and other factors. But disadvantage of it that code is tied to MCU. I would welcome effective algorithm written in C that have pretty good performance comparing to ASM.
As I have mentioned this is a first version of generator and it has many shortcomings like there is no ability to regulate Output voltage, code in some places isn’t optimal. I have thought to make second version of generator whit Op amp on exit with controlling ADC, I should fix the stopping generator without resetting MCU. Other features aren’t implemented like PWM etc.
Any additional comments and suggestions are welcome as well.
thanx man , i fixed your firmware , it will works for me , and i added a while for up/down , now no need pressing up/down repleatly for decrease and increase frequency .
you can try , in simulator .
get it here : http://nilshare.com/file.php?file=890f961a31ac1705cc20c45f2fc6ac27
Thanks shankimout – You are fast 😉
I am placing you fixed version locally Firmware.rar
My friend Scienceprog, thank you very very much for your good work!
I order the components to build one of this good generator. I’m looking for something like this for a long time ago!
You said you’ll enhance the project with an output drive circuit with indication of output level signal on the LCD display, what is really great! I’ll “follow your steps”, can’t wait the time I’ll see it on your project page.
And if you permitt my friend, I’ll add to the “wish list” of your project a “Sweep” function with programmable start/stop sweep frequencies. This will be very useful to measure the response of virtually any device! Your equipment will be superb!!! Thanks again, friend!
Sweep is listed in feature listing of next generator. I didn’t think about frequency limits, but this is good idea.
Please explain to me how to make a parameter for setting timer and sinus value?
i mean how to make a sinus table?
Hello and nice work with your signal generator, I’m a fan of the AVR micros and actually I’m building your design on my breadboard for further test, but i have a problem trying to load the hex file in the atmega8, I have a program called bootloader installed in the flash memory that helps me to load any program trough serial to serial comm, but it says that the hex file is too large to load, actually the hex file you have is kind of big
Hey I waited for a response of you people and I have nothing, truly I’m disappointed because you are giving bad data and files of this signal generator, actually the hex file you give is not for an atmega8 and second it doesn’t work, so please, upload a new working hex file for a real atmega8, not for a simulation program
thank you very much for publishing your AVR DDS Application. It is exactly what I searched for. The Documentation is very good and there is no question open except one:
fres=(F_CPU/(clocks for one sample output))/2^(accumulator length) = 16000000/9/2^24=0.1059638129340278Hz.
That means, the more clocks for one sample output you need the better is the resolution?
I think the correct resolution is 1/0.1059638129340278 = 9,437184 Hz.
Everything is correct. Resolution of 0.1059638129340278Hz means smallest step frequency can be incremented or decremented. Look more carefully at formula – you can see that F_CPU is divided by ‘clocks for one sample output’ – what means more clocks per sample the lower resolution.
Don’t forget to check out second version of AVR DDS generator: https://scienceprog.com/avr-dds-signal-generator-v20/
Can you post configuration of fuse bits in Pony Prog? Please
I made this signal generator using the exact parts (metal film resistor, crystal, capacitors) and the first firmware but i have a problem. the output frequency is not correct (on lcd displace 65535 and the output frequency is 4210.