EmbeddedRelated.com
Forums

sine routines

Started by CAFxX January 14, 2004
CBFalconer <cbfalconer@yahoo.com> writes:
> [snip] > I was thinking of some Tchebychev polynomials or ratio of > polynomials I used 25 years ago for 4.5 digit accuracy. I have > the source somewhere for the 8080 code I developed for them, which > took about 10 millisec on a 2 Mhz 8080, as I recall. Floating > multiply was in the order of 300 uSec. and divide headed for 1 > millisec.
I suspect these weren't in (IEEE) floating point. I would guess that IEEE float multiply would take closer to 300 msec. than 300 usec.
"Everett M. Greene" wrote:
> CBFalconer <cbfalconer@yahoo.com> writes: > > [snip] > > I was thinking of some Tchebychev polynomials or ratio of > > polynomials I used 25 years ago for 4.5 digit accuracy. I have > > the source somewhere for the 8080 code I developed for them, which > > took about 10 millisec on a 2 Mhz 8080, as I recall. Floating > > multiply was in the order of 300 uSec. and divide headed for 1 > > millisec. > > I suspect these weren't in (IEEE) floating point. I would > guess that IEEE float multiply would take closer to 300 > msec. than 300 usec.
No, they used a 16 bit significand normalized with a hidden left bit. The routines operated entirely in registers, for speed and reentrancy. An earlier version of the system was published in DDJ in the late 70's or so. Accuracy was 4.7 decimal digits, and was quite sufficient for my purposes (medical instrumentation). It was actually better for most things than Microsofts Basic fp, which was 32 bits, because I rounded properly and they truncated. IIRC the whole code module, including i/o, fit into 2k, and was an order of magnitude faster than anything then available, except hardware. At any rate applications such as solving a least square fit of calibration data to a 3rd order polynomial and applying goodness criteria (possibly dropping input points) produced a barely detectable hesitation. Re/Calibration was automatic when the operator fed in a suitable set of standards, controlled by reserved identification numbers. -- Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net) Available for consulting/temporary embedded and systems. <http://cbfalconer.home.att.net> USE worldnet address!
"CAFxX" <cafxx@n-side.it> wrote in message
news:bu634c$7g9$1@lacerta.tiscalinet.it...
> what i need to do is to generate a custom-frequency sine wave to be > outputted at CD-quality frequency (44100 samples/second) to a DAC. > If possible i'd prefer working with 16 bit integers since float needs > cpu-time-expensive libraries. > thanks to all of you for your replies.
If your hardware design is not set in stone at this point, add two 27512 64Kx8 EPROMs, and a handful of glue logic. Feed the address pins with two 8-bit latches that can WRITE from the 8051. Use some glue logic to take a pulse from the processor, do a read cycle on the EPROMs, and latch the results into two other 8-bit latches that you can READ from the 8051. Now burn the EPROMs with sin(address), and you have a dedicated hardware sin(x) lookup table that will be scary fast compared to any algorithm for computing sin(x). It works something like this: poke(msb(x), SIN_TABLE_ADDR_MSB); poke(lsb(x), SIN_TABLE_ADDR_LSB); pulse(SIN_TABLE_TRIGGER); sinx = peek(SIN_TABLE_DATA_MSB) << 8 | peek(SIN_TABLE_DATA_LSB); This is basically how the Yamaha DX7 synthesizer generated frequency-modulated audio sinewaves. The trick is realizing that you can hook EPROM (and RAM, for that matter) address pins to other things besides microprocessor address pins.
"John R. Strohm" <strohm@airmail.net> wrote in message
news:bu82f6$9mc@library2.airnews.net...
> "CAFxX" <cafxx@n-side.it> wrote in message > news:bu634c$7g9$1@lacerta.tiscalinet.it... > > what i need to do is to generate a custom-frequency sine wave to be > > outputted at CD-quality frequency (44100 samples/second) to a DAC. > > If possible i'd prefer working with 16 bit integers since float needs > > cpu-time-expensive libraries. > > thanks to all of you for your replies. > > If your hardware design is not set in stone at this point, add two 27512
64Kx8
> EPROMs, and a handful of glue logic. Feed the address pins with two 8-bit > latches that can WRITE from the 8051. Use some glue logic to take a pulse
from
> the processor, do a read cycle on the EPROMs, and latch the results into
two
> other 8-bit latches that you can READ from the 8051. > > Now burn the EPROMs with sin(address), and you have a dedicated hardware
sin(x)
> lookup table that will be scary fast compared to any algorithm for
computing
> sin(x).
Nice idea, but you also need a DA converter. Now go take a look at the AD9833: A full DDS capable of outputting DC to 12.5MHz in 0.1Hz steps, sine, square and triangle. And if you clock it with 'only' 1MHz, you get DC to 500kHz in 0.004Hz resolution. And all this in a 10 pin package for a whopping $9.30 at Digikey.... Meindert
"CAFxX" <cafxx@n-side.it> wrote in message news:<bu6k2g$s4s$1@lacerta.tiscalinet.it>...
> ok i understood that i have to use a lookup table. before asking some more > question i just want to explain what i am really trying to do. > it will simply be a sort of musical keyboard (a synth). it takes the input > of 24 keys (using 3 of the four raw i/o ports of the 8052) and sends 16 bit > samples to the dac (even if it accepts only 24 bits samples - i simply leave > the LS 8 all low). > what i'm wondering here is how to implement the lookup table (actually the > problem is that the sine function generate variable frequency sine waves) > i mean: > > short sine (unsigned short frequency) { > return sin[(counter % frequency) / frequency]; > } > > (note that long is 4 byte in sdcc) to make sure of having a *decent* > resolution without using long and float (int and short are the same) values > in the calculation what can i do? > i thought of something like > return sin[((counter % frequency) << 10) / frequency]; > (at least 10) but i think this will mess up everything. or not?
Please don't top post. If I understand you correctly, you just want to generate 1-4 sine waves, of varying frequencies and amplitudes, then add then together and send them out your DAC, right? To generate a continuous sine wave, just ought to be able to use Bresenham's circle drawing algorithm and output the X (or Y) coordinate as you pop around the circle you're "drawing." Very fast and you can do it all with modest resolution integer math. (The technique is closely related to Bresenham's line drawing algorithm - just Google for either).
CAFxX <cafxx@n-side.it> wrote:
> ok i understood that i have to use a lookup table. before asking some more > question i just want to explain what i am really trying to do. > it will simply be a sort of musical keyboard (a synth). it takes the input > of 24 keys (using 3 of the four raw i/o ports of the 8052) and sends 16 bit > samples to the dac (even if it accepts only 24 bits samples - i simply leave > the LS 8 all low). > what i'm wondering here is how to implement the lookup table (actually the > problem is that the sine function generate variable frequency sine waves) > i mean:
> short sine (unsigned short frequency) { > return sin[(counter % frequency) / frequency]; > }
Careful here, you're getting mixed up with the various frequencies involved in this. A sampled signal being played back at a different frequency involves *three* frequencies, or even four, depending on how flexible you want to be: *) the stored sample's sampling rate : r_in *) the playback sampling rate : r_out *) the frequency you're trying to output: f_out *) the original frequency of the sampled: f_in For numeric examples, I'll assume r_in = r_out = 44100 Hz f_in = 1 Hz From these, the computations go as follows: N := r_in / f_in gives the length of the sampling table for one complete cycle of the wave, and thus the table index after which you would wrap around. dt := 1/r_out is the time step of the output --- the time between two writes to your DAC. In this time, you've covered a fraction of dp := f_out * dt = f_out / r_out or your output wave. Since we know that one complete cycle of that wave is one pass through the table, the step size in the table has to be di := N * dp = N * f_out / r_out = (r_in * f_out) / (r_out * f_in) Assuming r_in = r_out, this becomes di = f_out / f_in The frequency actually output will not be accurate to more than f_in anyway, so there's no point trying to treat f_out as a fractional number. Using this, all computations are in integers: N = r_in / f_in; sine_table[N] = { filled by precomputation... } i = 0; assert(f_out * 2 < r_out); // pay respect to Mr. Myquist... forever { to_DAC(sine_table[i]); i = (i + f_out) % N; ... wait until time 1/r_out has passed ... } If you need frequency accuracy better than 1 Hz, you must use a smaller f_in, but that will obviously make your table larger. If you try to use non-integer multiples of f_in as f_out, you'll get noisy output, i.e. more than one harmonic on output. -- Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de) Even if all the snow were burnt, ashes would remain.
"Meindert Sprang" <mhsprang@NOcustomSPAMware.nl> wrote in message
news:40079955$1@news.nb.nu...
> "John R. Strohm" <strohm@airmail.net> wrote in message > news:bu82f6$9mc@library2.airnews.net... > > "CAFxX" <cafxx@n-side.it> wrote in message > > news:bu634c$7g9$1@lacerta.tiscalinet.it... > > > what i need to do is to generate a custom-frequency sine wave to be > > > outputted at CD-quality frequency (44100 samples/second) to a DAC. > > > If possible i'd prefer working with 16 bit integers since float needs > > > cpu-time-expensive libraries. > > > thanks to all of you for your replies. > > > > If your hardware design is not set in stone at this point, add two 27512 > 64Kx8 > > EPROMs, and a handful of glue logic. Feed the address pins with two 8-bit > > latches that can WRITE from the 8051. Use some glue logic to take a pulse > from > > the processor, do a read cycle on the EPROMs, and latch the results into > two > > other 8-bit latches that you can READ from the 8051. > > > > Now burn the EPROMs with sin(address), and you have a dedicated hardware > sin(x) > > lookup table that will be scary fast compared to any algorithm for > computing > > sin(x). > > Nice idea, but you also need a DA converter. > Now go take a look at the AD9833: A full DDS capable of outputting DC to > 12.5MHz in 0.1Hz steps, sine, square and triangle. And if you clock it with > 'only' 1MHz, you get DC to 500kHz in 0.004Hz resolution. > And all this in a 10 pin package for a whopping $9.30 at Digikey....
The original poster already has a DAC. If all you want is sine/square/triangle waves, that chip will do it. If you want to do more interesting things, it won't. But I think I'm going to take a longer look at it, for some radio projects I have in mind.
In comp.arch.embedded, "CAFxX" <cafxx@n-side.it> wrote:

>ok i understood that i have to use a lookup table. before asking some more >question i just want to explain what i am really trying to do. >it will simply be a sort of musical keyboard (a synth). it takes the input >of 24 keys (using 3 of the four raw i/o ports of the 8052) and sends 16 bit >samples to the dac (even if it accepts only 24 bits samples - i simply leave >the LS 8 all low). >what i'm wondering here is how to implement the lookup table (actually the >problem is that the sine function generate variable frequency sine waves)
This Is Not A Problem. Use a phase accumulator running at 44.1kHz. Here's some scratch code: unsigned_int_24_bits phase; // yes, three bytes for each. unsigned_int_24_bits frequency; unsigned_int_24_bits freq_lookup [24] = { 123445, // (SWAG approximate values) 126999, // These should each be the frequency for 131111, // the corresponding key. ... }; int sin_lookup [256]; // this table is 256 points along a complete sine wave of 360 degrees. phase = 0; // init phase (not even neccesary) frequency = freq_lookup [note]; // set output frequency based on note interrrupt isr_for_44100Hz_interrupt (void); { phase += frequency; output_sample_to_DAC = sin_lookup [phase >> 16]; } // end ISR routine This routine is just about that simple. When the frequency variable is half its maximum value, the output will be (if the phase doesn't start out at zero, but we can ignore that for this calculation) two points at 22,050Hz. Thus the ratio between Hertz and the number in the frequency variable (or specifically freq_lookup) is: 22050 / 2^23 = 0.0026285648345947265625 Thus, if you put a 1 in the frequnecy variable, you will get a sine wave with 0.0026 Hz output. Let's take the reciprocal of that for easier conversion the other way: 380.435736961451247165532879818594 Let's call it 380.436. To generate an "A" at 440Hz, use the number: 380.436 * 440 = 167391.84 or just round it to 167392. The next note up is A# at 2^(1/12)*440 or 466.16Hz. 380.436 * 466.16 = 177344.04576 which when rounded becomes 177344. If you follow the code carefully you may notice that at frequencies below 44100/256 or 172Hz, some samples will have sine table entries repeated, and at higher frequencies some entries in the sine table will be skipped. Again, this is not a problem, unless you're making a "real" music synthesizer for use by "real" musicians, or you're using this for a lab instrument that needs a pure-as-possible sine wave at arbitrary frequencies. Another neat thing about using a lookup table in this manner is that you can change the waveshape just by putting it into the lookup table. Square, triangle and sawtooth immediately come to mind, and again, skipping or repeating samples at different frequencies isn't a problem except in truly critical applications.
>i mean: > >short sine (unsigned short frequency) { > return sin[(counter % frequency) / frequency]; >} > >(note that long is 4 byte in sdcc) to make sure of having a *decent* >resolution without using long and float (int and short are the same) values >in the calculation what can i do? >i thought of something like > return sin[((counter % frequency) << 10) / frequency]; >(at least 10) but i think this will mess up everything. or not? > >"Hans-Bernhard Broeker" <broeker@physik.rwth-aachen.de> ha scritto nel >messaggio news:bu65lp$1ls$1@nets3.rz.RWTH-Aachen.DE... >> CAFxX <cafxx@n-side.it> wrote: >> > what i need to do is to generate a custom-frequency sine wave to be >> > outputted at CD-quality frequency (44100 samples/second) to a DAC. >> > If possible i'd prefer working with 16 bit integers since float needs >> > cpu-time-expensive libraries. >> >> Setting aside the recommendable option of replacing the whole kaboodle >> you're planning now with a special-purpose chip (a digitally >> controlled oscillator), there's still no reason to actually evaluate a >> sine function for this. >> >> A table should be perfectly adequate --- subsample it in steps through >> the table whose size is governed by the quotient of your target >> frequency and the frequency of the tabulated signal. E.g. if you have >> a sine table of (effectively) 44100 samples for a 1 Hz wave, a 1 kHz >> wave would use every 1000th sample from that table, and wrap around at >> the 45th iteration to 45*1000 - 44100 = 900. You'll want to handle >> non-integer step sizes, too --- either using fixed-point fractional >> arithmetic, or by Bresenham's algorithm. >> >> The actual table should of course store only one quadrant of the wave >> (0 .. 90 degrees, typically), to keep it short. >> >> -- >> Hans-Bernhard Broeker (broeker@physik.rwth-aachen.de) >> Even if all the snow were burnt, ashes would remain. >
----- http://mindspring.com/~benbradley
"CAFxX" <cafxx@n-side.it> wrote in message
news:bu49k1$125$1@lacerta.tiscalinet.it...
> does anybody know where to find some FAST sine routines to be run on an > 8052? sdcc's math.h is just an empty file... just an #error directive!
thank
> you in advance.
I am sorry, terribly busy, but you can get there from here (I had to) circles ( and sines, fit for your purpose) using only add and subtract X = 100: y = 0 FOR i = 1 TO 1000 PRINT X, y X = X - y / 256 y = y + X / 256 NEXT If you cannot do it I will dig out the Scamp (INS8060) code, eventually. David F. Cox
hi, this is the simplification of the compound angle formulas, it works very
well and can be quite quick but not as fast as a lookup table.