Hadn't built many beginner projects and I thought it was time to make an R2/R ladder DAC. Alright.
Built the networks, 6 in total = 2^6=64 steps, and wired an op amp in a voltage follower configuration in a moment (I had great trouble last time finding what pin was where last time, so great!)
Figured I'd just use an Arduino I had on hand, for binary output.
--------------------Functions!----------------------------
sawtooth: i=0;i<2^5;i++; PORTD = i
sawtooth inverse: i=2^6;i>0;i--; PORTD = i
triangle: both of the above (clever!)
square: on and off...
sine wave: ugly table of integers, alright.
(ps: nice picture of what it looked like before I got insane below:
)
---------------------------------------------------
Now to the good stuff: It can pump out clean waves @ 1-10kHz with uS delays each step, or dirty but fast ones @ ~30-60kHz with no delays. Now how do I ...say...order it "Give me a 4kHz sawtooth!"? ...or 4.5kHz sine?
----------------------------------------------------
I'm sometimes poor at math but it greatly interests me, so I just spewed out crazy possibilities:
10kHz = 1000uS, sawtooth loop takes 330 cycles (=330uS/16MHz=20.625uS, verified on delayed timebase and AVRStudio's simulator to match! Yay!)
So... (1000uS - 20.625uS) / 2^5 = 30.606uS per loop iteration (of i) required delay to create roughly 1000kHz sawtooth.
Okay...
so...
for(int i=0;i<resolution;i++) {
PORTD = i;
_delay_us(30);
}
Uh oh... 606nS * 64 = ~39uS skew
So ...there's no _delay_ns? okay... make one!
void _delay_ns(int delay) {
for(int i=0; i < delay; i+=1/F_CPU*1e9) {
asm volatile("NOP"::);
}
}
Assuming NOP is one cycle...say F_CPU is 16MHz = 62.5nS granularity?, but 0.062uS is better than 1uS.
So... I calculate exactly what I need this time...of course this is depending on 330 cycles to be static
float delay_per_cycle = ((1.0/freq) - (330.0/F_CPU)) / 64 * 1000000.0;
int delay_per_cycle_us = (int)delay_per_cycle;
int delay_per_cycle_ns = (int)((delay_per_cycle - delay_per_cycle_us) * 1000);
I'm too tired to verify if that is correct, or if this even works on 8 bits+avrgcc...but okay.
for(int x = 0; x < 2^6; x++) {
PORTB = x;
_delay_us(delay_per_cycle_us);
_delay_ns(delay_per_cycle_ns);
}
Bam! Now my simulator test seems to make this a ~200-300Hz signal rather than 1kHz, might be due to math errors somewhere, as I am not sure how to check what numbers are in AVR studio...guess I'll have to figure out the debugger a little better....
But is this insane?
How does a DDS control frequency when it's pumping out the wave form, from I assume EEPROM (which I technically am doing for some of it), but this way I can at least create functions based on...functions, such as my for loops, although I have to calculate its total cycles each time.
I'd be upset if I spent 5 hours on this project and no one thought it was crazy, or at least interesting, for a beginner (I've never touched AVR studio or PORTD before!). :-)
EDIT: I realised the loop had a greater delay than the nop! explains the lag, and brings me to ~300nS granularity for that delay, just as I read using _delay_us(float<1 here) et. al. has...so ideas of a better way would be neatAlexander.