DDS信号发生器

基于MicroGUI的信号发生器

# 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 x<pars[0]: return x/pars[0]
    if x<pars[0]+pars[1]: return 1.0
    if x<pars[0]+pars[1]+pars[2]: return 1.0-(x-pars[0]-pars[1])/pars[2]
    return 0.0
def gaussian(x,pars):
    return exp(-((x-0.5)/pars[0])**2)
def sinc(x,pars):
    if x==0.5: return 1.0
    else: return sin((x-0.5)/pars[0])/((x-0.5)/pars[0])
def exponential(x,pars):
    return exp(-x/pars[0])
def noise(x,pars): #p0=quality: 1=uniform >10=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