MUFF WIGGLER Forum Index
 FAQ & Terms of UseFAQ & Terms Of Use   Wiggler RadioMW Radio   Muff Wiggler TwitterTwitter   Support the site @ PatreonPatreon 
 SearchSearch   RegisterSign up   Log inLog in 
WIGGLING 'LITE' IN GUEST MODE

Programming the AD9833 DDS with an AtMega328 (Arduino)
MUFF WIGGLER Forum Index -> Music Tech DIY  
Author Programming the AD9833 DDS with an AtMega328 (Arduino)
Grumble
I get questions on a regular basis about my modular synth, especially about the use of the AD9833 DDS from Analog Devices in combination with an Arduino.
My plan is to write a series of articles about this little wonder, add basic programs and schematics.
The idea is to show how to work with control voltages to change the frequency, or make a hard sync etc.

The AD9833 is a DDS, what stands for Direct Digital Synthesis, Analog Devices has a large number of these type of devices but I like this one the most because it's ease of programming and the low pin-count.
t
The basic diagram

There is one downside though: It's freaking small, so the hard part is not the programming, but the soldering of this chip to convert it to a more diy friendly format.
In order to do this I bought THESE pcb converters.
To get an idea how small the AD9833 really is, the regular SOIC is on the right hand side and the size of the AD9833 is on the right hand side...:

For programming I use C++ on Atmel Studio which can be free downloaded HERE.

(see code)
in the line: ad9833_send(AD_TRI);// set waveform shape the shape is chosen, in this case a triangle shape, but also a sine or square is possible (not simultaneous) and for the square the output will be VCC, but the sine and triangle will be typ. 650mV so they need some amplification and maybe level shifting.
The frequency is set here: ad9833_set_frequency(1500); 1500Hz in this example, but 1500.1Hz is also possible...
The lowest frequency when using the 16MHz from the Arduino as a master clock, will be 0.0596Hz which is also the accuracy for setting the frequency.
The maximum frequency is well in the MHz range, not suitable for audio but definitely very practical for clock frequency generation.
To use the clock frequency of the Arduino a fuse has to be set: in Atmel Studio this one is called LOW.CKOUT if this fuse is set, the 16MHz clock is output to port PB0

since the code keeps getting f*cked up, I added the code as an attachement
Grumble
In this second article about programming the AD9833 I added an input for modulation.
For modulation purposes I used a 3310B Function Generator from Hewlett Packard, but any sound source can be used, set to a voltage between 0 and 5 volt, I used the Analog 0 input (PC0) of the AtMega328p and added some lines of code to the previous code.
Also a sound clip is added to this page, where I use different wave forms and amplitudes to modulate the AD9833.
I added 10 to the Analog to Digital Converter input and made that equal to the frequency, so the frequency span is 10 - 1033Hz, since the ADC of the AtMega328 is 10bit (1024 levels)
guest
i have some code to force it to make saw and sine/saw (half-wave sine which resets) waveforms. its a bit of a hack, and only works with certain frequencies (16b settable, so a fair bit of frequencies). i can post it up if interested.
Grumble
Please do! I had the idea of resetting it to a certain phase, but it is not possible, it just can ADD to the phase and not set the phase.
Too bad d'oh!
guest
ok, here is the code:
http://www.openmusiclabs.com/files/dds_chip.ino

its really rough, and im not sure exactly what its setup to do at the moment. i would have cleaned it up a bit, but i took apart my hardware setup, so it would be a bit of effort at this point. here is the general gist:

0. preload the phase registers with 0 and 180 degrees.
1. setup TIMER1 to trigger an interrupt at 1/2 the period you want to produce.
2. setup the DDS chip for the period (frequency) you want.
3. reset TIMER1 and do a software reset of the DDS chip, one right after the other.
4. you can play around with the TIMER1 reset value to get the delay just right.
5. in the interrupt, toggle the phase between the 2 phase registers.

you can get higher than 16b resolution if you trigger on overflow and compare match, and count mutiples of overflows plus the compare match. to change frequency, you have to repeat the whole process (steps 1-3), but doesnt take very long at all. ideally interrupts should be blocked for step 3, although if any other interrupts are running, youll probably have problems anyways. i turned off the TIMSK0 interrupt to reduce jitter (this kills delay() and millis()). it is really difficult to do phase modulation in this manner, which is why i ultimately decided to go down a different route. i was concerned that i couldnt keep up with the phase accumulation in the interrupt routine. if i missed a cycle, it would sound like garbage from that point onwards.

also, playing around with the reset value of TIMER1 gives waveform modification.

for my application, i really needed a sawtooth that i had easier control over, so i got an ARM0+ and wrote a really quick loop that does DDS and outputs 10b to an R2R DAC. it works pretty well, allows for arbitrary waveshapes, and is a little bit cheaper.
Grumble

I managed to do some phase modulation on the AD9833, this is a short demo of phase modulation of the Analog Devices AD9833 DDS chip with an Arduino (Atmega328p).
First part is where the hard sync input frequency is varied, than the phase of the hard sync is modulated en than the frequentie of the ad9833 is modulated.
The last part all three variables are modulated.
The sync input is PD2 (INT0), the phase input is ADC0 (PC0) and the Frequency input to control the AD9833 is ADC1 (PC1).
The signal is very noisy, but that is because the wires go all over my desk.
guest
thats looking good.
Grumble
Thanks, it does, doesn’t it? hyper
There is one drawback though, the frequency of the AD9833 can only been set if an interrupt is received, or else there will be a lot of jitter.
When using the AD9833 as an LFO it is not a huge problem, but if the trigger comes in the audio range like the youtube flic, it is audible as (quite loud) noise.
guest
Grumble wrote:
There is one drawback though, the frequency of the AD9833 can only been set if an interrupt is received, or else there will be a lot of jitter.


im not sure i understand what you mean here.
Grumble
If a rising edge is received at INT0 there is a flag set, which is detected by polling (see the last cpp file) and when this flag is detected the phase is set and the output frequency of the AD9833. Since this takes the same time every time an interrupt is evoked, the output signal of the AD9833 is almost perfect in sync with the square wave at INT0 (like I wrote b4, this is only importand with trigger frequencies in audio range)
If I want to change the output frequency of the AD9833 asynchrone from the trigger input, there will be jitter caused by the processor reading the dac and setting the frequency of the AD9833 and the detection of the flag from the interrupt.

Does this makes any sence?
guest
oh. i see now.

you might be able to reduce that jitter if your frequency and phase values were pre-calculated (in main() the ADCs are sampled and values calculated continuously, with appropriate interrupt blocking around volatile variable changes), and then in INT0, the commands are sent with the most recent phase and frequency variables.
Grumble
That might work, going to try that. Thanks applause
guest
also, is your frequency calculation being done as a float? isnt that slower than just a long? id assume it would be, but im not actually sure.
Grumble
I had no idea, so I did some google wizardry...
Quote:
If you are memory constrained, then the reason to use float over double might be as simple as "it takes half as much space". But even if memory is not an issue, storing your data with float may be substantially faster. As I said, double takes twice the space over float, so that means it will take twice as long to allocate, initialize and copy your data if you use double. Moreover, if you are reading your data in an unpredictable fashion (random access) you will get increased cache misses with double, which will make it around 40% slower (based on the O(√N) rule of thumb and confirmed with benchmarks).

Source: http://www.ilikebigbits.com/blog/2017/6/1/float-or-double
This is about storing and retreiving data, I havent found a conclusive text about calculations though... There doesn’t seem to be a concensus about the fastest calculations using float or double.
guest
so, for most applications, double = double floating point, or 2x the length of a regular float. so what they are saying makes sense. but, for avrgcc, double is not defined. there is only float. in arduino, double is cast to float for the uno. the long, on the other hand, is not done in floating point, and is just a 32b integer, which is the same length as a float. so access will be the same, so it really boils down to whether fixed point math is faster or slower on the atmega processors. they might be the same, as the output ends up being an integer anyways.

in the long run you will probably want an exponential response, so all this will be moot as a lookup table is the best way of doing that.
guest
from the arduino website:

Quote:
Floating point math is also much slower than integer math in performing calculations, so should be avoided if, for example, a loop has to run at top speed for a critical timing function. Programmers often go to some lengths to convert floating point calculations to integer math to increase speed.


although thats a broad statement. im sure there are some maths that are faster due to the exponent nature of floats.
Grumble
To be honoust about it i'm not to good in math's ( actually suck about it d'oh! ) so with that kind of things I relay on other ppl's code ...

edit: I don’t use the Arduino ide, but Atmel Studio c++ instead and maybe it handles long and float differently?
Grumble
int main (void)
{
init();
while (1)
{
phaseshift = ((read_adc(0))<<2);//get phase CV (0 - 4095)
frequency=((read_adc(1))+0.1);//get frequency CV (0.1 - 1023.1)
cli();
ad9833_set_frequency(frequency);
sei();
}
}

ISR (INT0_vect)
{
ad9833_send(AD_RESET);
ad9833_send(AD_PHASE0 | phaseshift);//phase word for DDS
ad9833_send(AD_ZERO);//end reset
}

I have changed the code to this ^^
And now I can change the frequency of the AD9833 asynchronous of the trigger input for the hard-sync
thumbs up
guest
thats a great solution! is it possible to adjust the interrupt blocking so its just around the SPI data transfer portion of the frequency update? that would reduce the total time that the phase reset might have to wait during blocking.

i did a bit more reading on floats, as i have never used them before (ive done my own version of floats for some very specific things, but not quite the same). on the AVR its 1 sign bit, 7b of resolution, and 24b of exponent. so, for this application where you only need 28b of resolution, it is much better to use a long than a float. the float allows numbers to span a really wide range (3*10^38 -> 3*10^-38), and your 32b of representation is spread out over that whole range, so you dont get all the numbers. with a 32b long, its a one to one mapping, so you get all the numbers over the smaller range (0 -> 4*10^9).
Grumble
Quote:
is it possible to adjust the interrupt blocking so its just around the SPI data transfer portion of the frequency update? that would reduce the total time that the phase reset might have to wait during blocking.

I have looked into this a little and didn’t found it benificial regarding speed, but I’m not finished looking yet Mr. Green
MUFF WIGGLER Forum Index -> Music Tech DIY  
Page 1 of 1
Powered by phpBB © phpBB Group