## DDS信号发生器 * [[https://www.eetree.cn/project/detail/580|参考项目介绍]], 源自Instruable上的一个DDS项目,已经分享在电子森林项目页面。{{ :awgpico.png |}} * [[https://www.instructables.com/Poor-Mans-Waveform-Generator-Based-on-RP2040-Raspb/|Poor Man's Waveform Generator Based on RP2040 Raspberry-pi-Pico Board]],这是一个改进的带有UI的任意波形发生器的案例说明,采用了[[https://github.com/peterhinch/micropython-micro-gui|MicroGUI]]作为控制界面,支持8bit/10bit DAC。 {{ :awg2040.jpg |}} ### 基于MicroGUI的信号发生器 * [[microgui_dds]] # Arbitrary waveform generator for Rasberry Pi Pico # Requires 8-bit R2R DAC on pins 0-7. Works for R=1kOhm # Achieves 125Msps when running 125MHz clock # Rolf Oldeman, 13/2/2021. CC BY-NC-SA 4.0 licence # tested with rp2-pico-20210205-unstable-v1.14-8-g1f800cac3.uf2 from machine import Pin,mem32 from rp2 import PIO, StateMachine, asm_pio from array import array from utime import sleep from math import pi,sin,exp,sqrt,floor from uctypes import addressof from random import random fclock=125000000 #clock frequency of the pico DMA_BASE=0x50000000 CH0_READ_ADDR =DMA_BASE+0x000 CH0_WRITE_ADDR =DMA_BASE+0x004 CH0_TRANS_COUNT=DMA_BASE+0x008 CH0_CTRL_TRIG =DMA_BASE+0x00c CH0_AL1_CTRL =DMA_BASE+0x010 CH1_READ_ADDR =DMA_BASE+0x040 CH1_WRITE_ADDR =DMA_BASE+0x044 CH1_TRANS_COUNT=DMA_BASE+0x048 CH1_CTRL_TRIG =DMA_BASE+0x04c CH1_AL1_CTRL =DMA_BASE+0x050 PIO0_BASE =0x50200000 PIO0_TXF0 =PIO0_BASE+0x10 PIO0_SM0_CLKDIV=PIO0_BASE+0xc8 #state machine that just pushes bytes to the pins @asm_pio(out_init=(PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH,PIO.OUT_HIGH), out_shiftdir=PIO.SHIFT_RIGHT, autopull=True, pull_thresh=32) def stream(): out(pins,8) sm = StateMachine(0, stream, freq=125000000, out_base=Pin(0)) sm.active(1) #2-channel chained DMA. channel 0 does the transfer, channel 1 reconfigures p=array('I',[0]) #global 1-element array def startDMA(ar,nword): #first disable the DMAs to prevent corruption while writing mem32[CH0_AL1_CTRL]=0 mem32[CH1_AL1_CTRL]=0 #setup first DMA which does the actual transfer mem32[CH0_READ_ADDR]=addressof(ar) mem32[CH0_WRITE_ADDR]=PIO0_TXF0 mem32[CH0_TRANS_COUNT]=nword IRQ_QUIET=0x1 #do not generate an interrupt TREQ_SEL=0x00 #wait for PIO0_TX0 CHAIN_TO=1 #start channel 1 when done RING_SEL=0 RING_SIZE=0 #no wrapping INCR_WRITE=0 #for write to array INCR_READ=1 #for read from array DATA_SIZE=2 #32-bit word transfer HIGH_PRIORITY=1 EN=1 CTRL0=(IRQ_QUIET<<21)|(TREQ_SEL<<15)|(CHAIN_TO<<11)|(RING_SEL<<10)|(RING_SIZE<<9)|(INCR_WRITE<<5)|(INCR_READ<<4)|(DATA_SIZE<<2)|(HIGH_PRIORITY<<1)|(EN<<0) mem32[CH0_AL1_CTRL]=CTRL0 #setup second DMA which reconfigures the first channel p[0]=addressof(ar) mem32[CH1_READ_ADDR]=addressof(p) mem32[CH1_WRITE_ADDR]=CH0_READ_ADDR mem32[CH1_TRANS_COUNT]=1 IRQ_QUIET=0x1 #do not generate an interrupt TREQ_SEL=0x3f #no pacing CHAIN_TO=0 #start channel 0 when done RING_SEL=0 RING_SIZE=0 #no wrapping INCR_WRITE=0 #single write INCR_READ=0 #single read DATA_SIZE=2 #32-bit word transfer HIGH_PRIORITY=1 EN=1 CTRL1=(IRQ_QUIET<<21)|(TREQ_SEL<<15)|(CHAIN_TO<<11)|(RING_SEL<<10)|(RING_SIZE<<9)|(INCR_WRITE<<5)|(INCR_READ<<4)|(DATA_SIZE<<2)|(HIGH_PRIORITY<<1)|(EN<<0) mem32[CH1_CTRL_TRIG]=CTRL1 def setupwave(buf,f,w): div=fclock/(f*maxnsamp) # required clock division for maximum buffer size if div<1.0: #can't speed up clock, duplicate wave instead dup=int(1.0/div) nsamp=int((maxnsamp*div*dup+0.5)/4)*4 #force multiple of 4 clkdiv=1 else: #stick with integer clock division only clkdiv=int(div)+1 nsamp=int((maxnsamp*div/clkdiv+0.5)/4)*4 #force multiple of 4 dup=1 #fill the buffer for isamp in range(nsamp): buf[isamp]=max(0,min(255,int(256*eval(w,dup*(isamp+0.5)/nsamp)))) #set the clock divider clkdiv_int=min(clkdiv,65535) clkdiv_frac=0 #fractional clock division results in jitter mem32[PIO0_SM0_CLKDIV]=(clkdiv_int<<16)|(clkdiv_frac<<8) #start DMA startDMA(buf,int(nsamp/4)) #evaluate the content of a wave def eval(w,x): m,s,p=1.0,0.0,0.0 if 'phasemod' in w.__dict__: p=eval(w.phasemod,x) if 'mult' in w.__dict__: m=eval(w.mult,x) if 'sum' in w.__dict__: s=eval(w.sum,x) x=x*w.replicate-w.phase-p x=x-floor(x) #reduce x to 0.0-1.0 range v=w.func(x,w.pars) v=v*w.amplitude*m v=v+w.offset+s return v #some common waveforms. combine with sum,mult,phasemod def sine(x,pars): return sin(x*2*pi) def pulse(x,pars): #risetime,uptime,falltime if x10=gaussian return sum([random()-0.5 for _ in range(pars[0])])*sqrt(12/pars[0]) #make buffers for the waveform. #large buffers give better results but are slower to fill maxnsamp=4096 #must be a multiple of 4. miximum size is 65536 wavbuf={} wavbuf[0]=bytearray(maxnsamp) wavbuf[1]=bytearray(maxnsamp) ibuf=0 #empty class just to attach properties to class wave: pass wave1=wave() wave1.amplitude=0.5 wave1.offset=0.5 wave1.phase=0.0 wave1.replicate=1 wave1.func=sine wave1.pars=[] #step through frequencies for freq in (1e5,2e5,5e5,1e6,2e6,5e6,1e7,2e7): setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 freq=2e5 #step through amplitudes freq=2e5 for amp in (0.1,0.2,0.3,0.4,0.5): wave1.amplitude=amp setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #step through offsets wave1.amplitude=0.4 for offs in (0.4,0.5,0.6,0.7,0.8): wave1.offset=offs setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #pulse: step through duty cycle wave1.amplitude=1.0 wave1.offset=0.0 wave1.func=pulse wave1.pars=[0.0,0.5,0.0] for duty in (0.5,0.4,0.3,0.2,0.1,0.05,0.02,0.01): wave1.pars[1]=duty setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #pulse: step through rise time,fall time for rise in (0.02,0.05,0.10,0.20): wave1.pars=[rise,0.1,0.0] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 for fall in (0.02,0.05,0.10,0.20): wave1.pars=[0.2,0.1,fall] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #pulse: step from saw to triangle to saw wave1.pars=[0.0,0.0,1.0] for x in (0.00,0.17,0.33,0.50,0.67,0.83,1.00): wave1.pars=[x,0,1-x] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #gaussian: step through widths wave1.func=gaussian for width in (0.15,0.1,0.05,0.02,0.01): wave1.pars=[width] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #sinc: step through widths wave1.func=sinc wave1.amplitude=0.5 wave1.offset=0.2 for width in (0.020,0.015,0.010,0.005,0.004,0.003): wave1.pars=[width] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #exponential: step through widths wave1.func=exponential wave1.amplitude=1.0 wave1.offset=0.0 for width in (0.20,0.10,0.05,0.02): wave1.pars=[width] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # reverse exponential: step through widths wave1.replicate=-1 for width in (0.20,0.10,0.05,0.02): wave1.pars=[width] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # noise: step through quality wave1.replicate=1 wave1.func=noise wave1.amplitude=0.2 wave1.offset=0.5 for n in (1,2,5,10,20): wave1.pars=[n] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # summation: build up a square wave from sines wi={} for i in range(6): wi[i]=wave() wi[i].amplitude=0.5/(2*i+1) wi[i].offset=0.5*(i==0) wi[i].phase=0.0 wi[i].replicate=2*i+1 wi[i].func=sine wi[i].pars=[] if i>0: wi[i-1].sum=wi[i] setupwave(wavbuf[ibuf],freq,wi[0]); ibuf=(ibuf+1)%2 #summation: double pulse wave1=wave() wave1.amplitude=1.0 wave1.offset=0.0 wave1.phase=0.0 wave1.replicate=1 wave1.func=pulse wave1.pars=[0.0,0.1,0.0] wave2=wave() wave2.amplitude=0.4 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=pulse wave2.pars=[0.1,0.1,0.1] wave1.sum=wave2 for delay in (0.15,0.20,0.25,0.30,0.35): wave2.phase=delay setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #summation: gauss + noise wave1=wave() wave1.amplitude=-0.5 wave1.offset=0.75 wave1.phase=0.0 wave1.replicate=1 wave1.func=gaussian wave1.pars=[0.05] wave2=wave() wave2.amplitude=0.1 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=noise wave2.pars=[4] wave1.sum=wave2 for amp in (0.01,0.02,0.03,0.04,0.05): wave2.amplitude=amp setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 #summation: exponential + gaussians wave1=wave() wave1.amplitude=0.8 wave1.offset=0.0 wave1.phase=0.0 wave1.replicate=1 wave1.func=exponential wave1.pars=[0.2] wave2=wave() wave2.amplitude=0.2 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=gaussian wave2.pars=[0.05] wave1.sum=wave2 for r in (1,2,3,4,5): wave2.replicate=r setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # product: sine * exponential wave1=wave() wave1.amplitude=0.4 wave1.offset=0.3 wave1.phase=0.0 wave1.replicate=10 wave1.func=sine wave1.pars=[] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=exponential wave2.pars=[0.2] wave1.mult=wave2 for width in (0.05,0.10,0.15,0.20,0.25): wave2.pars=[width] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # product: sine * pulse wave1=wave() wave1.amplitude=0.4 wave1.offset=0.4 wave1.phase=0.0 wave1.replicate=10 wave1.func=sine wave1.pars=[] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=pulse wave2.pars=[0.0,0.1,0.0] wave1.mult=wave2 for width in (0.05,0.10,0.15,0.20,0.25,0.30): wave2.pars=[0.0,width,0.0] setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # product: gauss * sine wave1=wave() wave1.amplitude=0.4 wave1.offset=0.4 wave1.phase=0.0 wave1.replicate=1 wave1.func=sine wave1.pars=[] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=20 wave2.func=gaussian wave2.pars=[0.1] wave1.mult=wave2 for r in (2,5,10,20): wave2.replicate=r setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # product: pulse * pulse wave1=wave() wave1.amplitude=0.4 wave1.offset=0.4 wave1.phase=0.0 wave1.replicate=1 wave1.func=pulse wave1.pars=[0.5,0.0,0.5] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=20 wave2.func=pulse wave2.pars=[0.0,0.5,0.0] wave1.mult=wave2 for r in (2,5,10,20): wave2.replicate=r setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # modulate: sine with sine - small beta wave1=wave() wave1.amplitude=0.5 wave1.offset=0.5 wave1.phase=0.0 wave1.replicate=10 wave1.func=sine wave1.pars=[] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=sine wave2.pars=[] wave1.phasemod=wave2 for amp in (0.1,0.2,0.5,1.0): wave2.amplitude=amp setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # modulate: sine with sine - large beta wave1=wave() wave1.amplitude=0.5 wave1.offset=0.5 wave1.phase=0.0 wave1.replicate=1 wave1.func=sine wave1.pars=[] wave2=wave() wave2.amplitude=1.0 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=sine wave2.pars=[] wave1.phasemod=wave2 for amp in (0.2,0.5,1.0,2.0,5.0,10.0): wave2.amplitude=amp setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2 # modulate: pulse with pulse wave1=wave() wave1.amplitude=1.0 wave1.offset=0.0 wave1.phase=0.0 wave1.replicate=10 wave1.func=pulse wave1.pars=[0.0,0.5,0.0] wave2=wave() wave2.amplitude=0.5 wave2.offset=0.0 wave2.phase=0.0 wave2.replicate=1 wave2.func=pulse wave2.pars=[0.5,0.0,0.5] wave1.phasemod=wave2 for amp in (0.1,0.2,0.5,1.0): wave2.amplitude=amp setupwave(wavbuf[ibuf],freq,wave1); ibuf=(ibuf+1)%2