Propeller Signal Generator

Have a look in the OBEX for the latest version!
http://obex.parallax.com/objects/872/
Version 1.2 features:
* Runs in a single cog
* Uses "duty dac"
* 1.25 MHz sample rate
* 6 waveform types (Saw, Triangle, Square, Noise, Sine, User)
* High resolution pulse width
* Outputs both normal and inverted signal at the same time
* Synchronization output on extra pin
* Signal can be damped in steps of 6 dB
* All parameters can be changed on the fly
Original first post below
http://obex.parallax.com/objects/872/
Version 1.2 features:
* Runs in a single cog
* Uses "duty dac"
* 1.25 MHz sample rate
* 6 waveform types (Saw, Triangle, Square, Noise, Sine, User)
* High resolution pulse width
* Outputs both normal and inverted signal at the same time
* Synchronization output on extra pin
* Signal can be damped in steps of 6 dB
* All parameters can be changed on the fly
Original first post below
I needed a signal generator that had a small footprint and a high sample rate; So I made one!
Features:
* Uses "duty dac"
* 600 kHz sample rate
* 4 waveform types (Saw, Triangle, Square, Noise)
* High resolution pulse width
* Outputs both normal and inverted signal at the same time
* Signal can be damped in steps of 6 dB
* All parameters can be changed on the fly
Comments
Propeller to the rescue!
I can do all kinds of mathematical calculations on jitter, "frequency exactness", linearity, resolution, SNR... etc
But I would like to have some real measurments on the performance..... pretty please!
You have got all this equipment to do good measurements (and the knowledge of course)
Many of these unknowns depends on the analog output stage... RC filter, OP... etc... etc...
And of course, the "duty DAC" is FAAAAR from good enough for high bandwidth signals. And of course, there's all kinds of "digital artifacts" generated inside the Prop. But let's say that we would want "optimal" performance for audio stuff. Then we would like to have a low pass filter with an extremely steep curve (48 dB or more) with a cut off of ~20 kHz to get rid of "duty noise". We would also like to have a high performance OP. and overall good components.
I want measurments of how well the "optimal" duty DAC circuit performs in combination with my PSG:
* Resolution for audio signals
* SNR (different pins and cogs... etc)
* Jitter
* Linearity
* Frequency exactness (in lack of better words)
*
(BTW, I think I can reach a sample rate of around 1 Mhz (80 Mhz prop) with some optimisations)
I am now experimenting with sample player. dithered, non dithered, noise shaped etc. Normal "duty DAC" gives 1812 clock cycles between two samples @44.1 kHz, and it means about 10bit DAC
And it means that lower bits of 16-bit signal causes peaks here and there - in accoustic band. All of this interferes with Propeller's internal work currents, so this noise depends on what cog is used and don't know what else. I tested my sample player without dithering with coginit instead of cognew, every cog gives different result. The best was using cog0 with playing routine and high numbered cogs for background work.
One remedium for it is this high frequency - 1 MHz in reality - white noise dither
Maybe, as someone wrote in my "sample player" topic, noise shaping and oversampling can give better results.
Another idea I want to try is using video generator for audio. This can be clocked with pll - something about 200 MHz instead of 80 MHz - I have to try it.
Btw, using the vide generator for audio has been done before and the bandwidth is great, but the resolution is.... Smile (8 bits at most). Maybe we could use two cogs/video generators, one taking care of the 8 MSBs and the other taking care of the LSBs?! Of course 16 pins will be needed and it might be hard to synchronize the two video generators. Just a thought!
Time to run bst and start testing
org 0 ' //////////////////////Initialization///////////////////////////////////////////////////////////////////////////////////////// init mov dira, ditherOutputMask mov ctra,#0 movi ctra,#%0_00001_111 'pll div 1 mov frqa,frqa_val ' mov vscl,vscl_val 'init video generator mov vcfg,vcfg_val mov bufptr2,par loop mov ptr,bufptr add ptr,bufcnt 'pointer to next sample rdword lsample,ptr 'get left sample add lsample,offset 'convert to unsigned add ptr, #2 rdword rsample,ptr and lsample,ffff add rsample,offset and rsample,ffff add bufcnt,#4 and bufcnt,bufmask wrlong bufcnt,bufptr2 mov t3,rsample shr t3,#4 and t3,mask5 mov t4,ffffffff shr t4,t3 mov t1,rsample shr t1,#9 add t1,#1 mov t2,#129 sub t2,t1 shl t1,#5 shl t2,#5 add t2,vscl_val0 add t1,vscl_val0 mov outa, zero mov vscl,t1 p11 waitvid zero,zero mov vscl,vscl_val waitvid ffffffff,t4 mov vscl,t2 p12 waitvid ffffffff,ffffffff mov outa,mask1 jmp #loop ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ' Data ' ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// leftTaps long $A4_00_00_80 ' Left LFSR taps. rightTaps long $80_A0_10_00 ' Right LFSR taps. leftLFSR long 1 ' Initial value. rightLFSR long 1 ' Initial value. ' //////////////////////Configuration Settings///////////////////////////////////////////////////////////////////////////////// ditherLeftCounterSetup long 0 ditherRightCounterSetup long 0 ditherOutputMask long 0 leftDitherShift long 0 rightDitherShift long 0 ' //////////////////////Addresses////////////////////////////////////////////////////////////////////////////////////////////// ditherLeftAddress long 0 ditherRightAddress long 0 ' //////////////////////Run Time Variables///////////////////////////////////////////////////////////////////////////////////// lsample long 0 rsample long 0 ls2 long 0 rs2 long 0 bufmask long %0000_0000_0000_0000_0000_0111_1111_1111 ptr long 0 temp long 0 bufptr long 0 bufcnt long 0 bufptr2 long 0 offset long $8000 ffff long %1111_1111_1111_1111 mask1 long %0000_0000_0000_0000_0000_1100_0000_0000 mask5 long 31 ffffffff long $ffffffff zero long 0 vscl_val long $00001020 '1 clock/pixel, 32 clock/frame, std vscl for display vscl_val0 long $00001000 t1 long 0 t2 long 0 t3 long 0 t4 long 0 vcfg_val long $200002ff frqa_val long $25000000 fit 496
(1) this is another chip
(2) $1? Where to buy???
(3) they are not suitable for a signal generator described in this topic - too low frequency
Wolfson Microelectronics WM8762, WM8759, WM8766... I2S bus, 24 bit stereo sigma-delta, upto 192kHz sample rate. Available from Farnell.
[ Also don't forget R-2R resistor ladder converter! ]
Sure, but roughly the same incremental cost as a single COG.
AK4386ETP-E2 at digikey is 52c/1k and 94c/100+
No problem - For higher frequencies, you can still use any prop pin you like.
For square wave clocks, direct connect would likely be best.
At Audio frequencies, which is where a lot of the focus is, then the cheap DAC gives far better noise/distortion, and can use the Sine LUT in Prop, for low distortion sine.
-Phil
http://obex.parallax.com/objects/872/
* Sample rate increased to 1.25 MHz (Because I hate jitter and aliasing)
* Code size decreased by 10% (It's just 39 rows of assembly now)
--tom
That all depends on your preference. A pot would be super simple using this circuit...
http://www.rayslogic.com/propeller/programming/adc.htm
Your input signal would be the wiper on a potentiometer.
You'd sample the potentiometer and equate various voltage values as settings for your home brew signal generator.
Personally, I'd prefer to use a handful of buttons plus the rotary encoder, but the pot method is super simple.
Try this: http://www.qsl.net/dl4yhf/spectra1.html
It is excellent.
Regards
Richard
Guess I'm olde fashioned, I still like the smooth response of a real knob.
Question: the 1n notation on the caps?
1n = 1 nanofarad = 1 x 10^-9 F = .001 x 10^-6 F = .001 uF
I like the feel of real knobs too. I hear ya.
I've been thinking of a project with, lets say, 8 knobs that will control various parameters of the software running in the Prop.
The RC circuit takes 2 pins, which may not be a big deal.
Excluding preferences for feel, to interface these 8 knobs (pots or encoders) with the Prop, which circuit would have the fewest number of components?
--tom
And I must warn that the audio out port on the Propeller Demo Board isn't an option at all. Even after some modifications of the RC filter the signal still is severely "distorted".
It seems like the OP/headphone circuit has a very nonlinear frequency response. I would guess that some kind of loudness filter is applied.
So what does a 1 nano farad cap look like?
-Phil
Goodness me!
*rummages through parts drawers*
He's right, by jove!
I've got drawers full of the brown ones http://www.futurlec.com/Capacitors/C001UC.shtml but my favorites are the green shiny ones http://www.futurlec.com/Capacitors/C001U250M.shtml
@Cluso, wasn't there a discussion a while back about how the propeller demo board has the wrong filter caps? http://elmicro.com/files/parallax/propdemod_schematic.pdf
10nf and 10k gives 1591hz according to http://sim.okawa-denshi.jp/en/CRtool.php Would 10nf and 1k be better?
--tom
I added a front end to your signal generator. Knobs, switches etc all work with a touchscreen. You can dial up approx the right frequency with the slider knob, then adjust with the +1 and -1 buttons for the exact frequency.
CON _clkmode = xtal1 + pll16x ' use crystal x 16 _xinfreq = 5_000_000 OBJ tch: "Touch" ' touchscreen driver ' F : "FloatMath" 'From Parallax Inc. v1.0 Q : "SPIN_TrigPack" 'v2.0 CompElit Ltd. psg : "PropellerSignalGenerator" ' thanks to Ahle2 VAR PUB Main tch.BeginProgram Function tch.SetWarmBoot ' clears screen, sets a warm boot and reboots PUB Function | xval,yval,org_x,org_y,pointx,pointy,r,theta,a,switchx,switchy,frequency,font,range,slider,xslider,yslider,waveform psg.start(21, 23) ' two audio pins (not next to each other so less crosstalk) frequency := 440 range := 1 ' x10, x100 etc slider := 44 ' 0-255 xslider := 26 ' position of the slider yslider := 144 waveform := 21 ' waveform SAW = 18, TRIANGLE = 21, SQUARE = 24, NOISE = 27, tch.ClearRam ' clear all but file 0 and 1 tch.StringDim(10) ' file 2 = strings if needed tch.SDBMPtoRam(string("function.bmp")) ' file 3 tch.SDBMPtoRam(string("rot1.bmp")) ' file 4 tch.SDBMPtoRam(string("rot2.bmp")) ' file 5 tch.SDBMPtoRam(string("rot3.bmp")) ' file 6 tch.SDBMPtoRam(string("rot4.bmp")) ' file 7 font := tch.Loadfont(string("DSdigGr.ifn")) ' file 8 tch.SDBMPtoRam(string("SlideP.bmp")) ' file 9 slider tch.SDBMPtoRam(string("synknbp.bmp")) ' file 10 slider knob tch.SDBMPtoRam(string("synoffP.bmp")) ' file 11 switch off tch.SDBMPtoRam(string("synonP.bmp")) ' file 12 slider knob tch.DrawBMPRam(3,0,0) ' draw file 3 DrawSlider(xslider,yslider,slider,9) LCDPrintFrequency(frequency,font) ' clear the little LCD display and print frequency RedrawSwitches(waveform) ' redraw the switches switchx := 138 ' centre of the switch, not the top left switchy := 171 ' switch bitmap is 60x60 tch.DrawBMPRam(4,switchx-30,switchy-30) ' draw file 4 tch.SelectSPIGroup ' talk to the spi touchscreen repeat yval := tch.TouchYPercent ' decode yval 0-100% xval := tch.TouchXPercent ' decode xval 0-100% if (xval <> 255) and (yval <> 255) ' valid keypress xval := (xval*24)/10 ' 0-240, use portrait mode yval := (yval*32)/10 ' 0-320 'tch.hex(xval,2) 'tch.hex(yval,2) org_x := q.Qval(switchx) ' origin x of the switch org_y := q.Qval(switchy) ' origin y pointx := q.Qval(xval) ' point touched pointy := q.Qval(yval) ' point touched r := q.Qradius(pointx - org_x, pointy - org_y) 'tch.text(Q.QvalToStr(r)) ' debugging theta := Q.Deg_ATAN2(pointx - org_x, org_y - pointy) ' pointy and orgy swapped as 0,0 is top left of the screenpolar coordinates with correct sign 'tch.text(Q.QvalToStr(theta)) theta += q.Qval(180) ' rotate 180 degrees and remove negative numbers, 0=9 o'clock, 90=6 o'clock, 180 = 3 o'clock, 270 = 12 o'clock 'tch.hex(r >> 16,2) if (r >> 16) < 47 ' touch close to the switch case (theta >> 16) 0..90: tch.DrawBMPRam(4,switchx-30,switchy-30) ' draw switch position 1 range := 1 91..180:tch.DrawBMPRam(7,switchx-30,switchy-30) ' draw switch position 4 range := 1000 181..270:tch.DrawBMPRam(6,switchx-30,switchy-30) ' draw switch position 3 range := 100 271..359:tch.DrawBMPRam(5,switchx-30,switchy-30) ' draw switch position 2 range := 10 frequency := RecalcFrequency(slider,range,frequency,font,waveform) ' redraw the display 'tch.text(Q.QvalToStr(theta)) ' debugging if xval >60 and xval <119 and yval >220 and yval <250 ' -1 frequency button frequency +=1 LCDPrintFrequency(frequency,font) if xval >120 and xval <180 and yval >220 and yval <250 ' +1 frequency button frequency -=1 LCDPrintFrequency(frequency,font) if xval > xslider and xval <60 and yval >yslider and yval <212 ' slider 26,144 slider := yval - yslider ' position on slider slider := slider * 256 ' 69 pixels high convert to 0-255 slider := slider / 69 if slider > 255 slider := 255 if slider < 0 slider := 0 tch.SelectMemGroup DrawSlider(xslider,yslider,slider,9) ' redraw this slider frequency := RecalcFrequency(slider,range,frequency,font,waveform) ' redraw the display if yval >58 and yval < 114 ' one of the 4 switches case xval 25..59:waveform := 18 ' sawtooth 77..110:waveform := 21 ' triangle 129..162:waveform := 24 ' square 181..214:waveform := 27 ' noise RecalcFrequency(slider,range,frequency,font,waveform) ' refresh display and output sound RedrawSwitches(waveform) if xval >190 and xval <240 and yval>0 and yval <40 ' off button return tch.pause1ms(100) ' debounce delay tch.SelectSPIGroup PUB DrawSlider(x,y,value,filen) ' value is 0-255. assumes bitmaps in ram tch.DrawBMPRam(filen,x,y) ' draw background tch.DrawBMPRam(filen+1,x+5,y+((value*100)/450)) ' draw the knob PUB LCDPrintFrequency(frequency,font) tch.ClearRectangle(41,265,197,307,tch.GetBackFontColor) ' clear an area the same color as the font background tch.SetCursor(46,267) tch.StringStr(0,frequency) ' store frequency to string zero tch.StringPrint(font,0) PUB RecalcFrequency(slider,range,frequency,font,waveform) frequency := slider * 4 * range ' slider 0-255, first range is 0-1000 Hz LCDPrintFrequency(frequency,font) if waveform <> 24 ' not square psg.setParameters(waveform, frequency, 1, 0) ' output the sound else psg.setParameters(waveform, frequency, 1, 1<<31) ' square 50% result := frequency PUB RedrawSwitches(waveform) ' SAW = 18, TRIANGLE = 21, SQUARE = 24, NOISE = 27 tch.SelectMemGroup tch.DrawBMPRam(11,25,59) ' draw all switches off first tch.DrawBMPRam(11,77,59) tch.DrawBMPRam(11,129,59) tch.DrawBMPRam(11,181,59) case waveform 18:tch.DrawBMPRam(12,25,59) ' switch on 21:tch.DrawBMPRam(12,77,59) 24:tch.DrawBMPRam(12,129,59) 27:tch.DrawBMPRam(12,181,59)
A modified C3 or El Jugador gives a much better result than a modified Demo Board.
That's extremely cool, I wish I had your board to play around with!