# Arbitrary Waveform Generator for Raspberry Pi Pico
# Requires 8-bit R2R DAC on pins 0-7. Works for R=2kOhm
# 20Hz to 1MHz sine, sawtooth and triangular waveforms
# front end code and design by G Nicholson 9/12/2023
# wave generation by code from Rolf Oldeman, 7/2/2021. CC BY-NC-SA 4.0 licence
from machine import Pin,mem32,I2C
from rp2 import PIO, StateMachine, asm_pio
from array import array
from utime import sleep
from math import sin,pi
import _thread
import machine
# Oled display
from ssd1306 import SSD1306_I2C
# Oled display 0.91″
sda=machine.Pin(20)
scl=machine.Pin(21)
i2c = I2C(0, sda=Pin(20), scl=Pin(21))
oled = SSD1306_I2C(128, 32, i2c)
oled.fill(0)
oled.show()
#set GPIO pins
fRange_a = Pin(16, Pin.IN, Pin.PULL_UP)
fRange_c = Pin(17, Pin.IN, Pin.PULL_UP)
outType_a = Pin(18, Pin.IN, Pin.PULL_UP)
outType_c = Pin(19, Pin.IN, Pin.PULL_UP)
GO =Pin(15, Pin.IN, Pin.PULL_UP)
led_onboard=machine.Pin(25,machine.Pin.OUT)
potentiometer=machine.ADC(26)
#intial settings
setFreq=1
outType=1
setFreq=2000
def read_freqControl():
global potentiometer,setFreq, fRange_a, fRange_c, outType_a, outType_c, outType
sleep(0.3)
print(setFreq)
setFreq=int((potentiometer.read_u16() / 65535) *100)
if fRange_a.value() == 1 and fRange_c.value()==0:
setFreq = (setFreq*10000)
if fRange_a.value() == 0 and fRange_c.value()==1:
setFreq = (setFreq*1000000)
if fRange_a.value() == 1 and fRange_c.value()==1:
setFreq = (setFreq*100000)
if outType_a.value() == 0:
outType= 3
if outType_c.value() == 0:
outType= 1
if outType_a.value()==1 and outType_c.value()==1:
outType = 2
while True:
read_freqControl()
if setFreq <=2000:
setFreq=2000
oled.fill(0)
oled.text(“Freq”,5,1,1)
oled.text(“Wave”,5,24,1)
oled.text(str(int(setFreq/100)), 50,1, 1)
oled.text(“Hertz”, 50,12, 1)
if outType ==1:
oled.text(“Sine”, 50,24,1)
if outType ==2:
oled.text(“Saw”, 50,24,1)
if outType ==3:
oled.text(“Tri”, 50,24,1)
oled.show()
if GO.value() == 1:
print(“loop”)
else:
break
oled.text(“ON”, 100,24, 1)
oled.show()
#sig gen code
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
PIO0_BASE =0x50200000
PIO0_BASE_TXF0=PIO0_BASE+0x10
#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=setFreq, out_base=Pin(0))
sm.active(1)
#2-channel chained DMA. channel 0 does the transfer, channel 1 reconfigures
p_ar=array(‘I’,[0]) #global 1-element array
@micropython.viper
def startDMA(ar,nword):
p=ptr32(ar)
mem32[CH0_READ_ADDR]=p
mem32[CH0_WRITE_ADDR]=PIO0_BASE_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
p_ar[0]=p
mem32[CH1_READ_ADDR]=ptr(p_ar)
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
#setup waveform. frequency is 125MHz/nsamp
def setup_waveform():
global nsamp,wave,outType
nsamp=100 #must be a multiple of 4
wave=array(“I”,[0]*nsamp)
for isamp in range(nsamp):
if outType==1:
val=128+127*sin((isamp+0.5)*2*pi/nsamp) #sine wave
if outType==2:
val=isamp*255/nsamp #sawtooth
if outType==3:
val=abs(255-isamp*510/nsamp) #triangle
if outType==4:
val=int(isamp/20)*20*255/nsamp #stairs
wave[int(isamp/4)]+=(int(val)<<((isamp%4)*8))
setup_waveform()
#start
startDMA(wave,int(nsamp/4))
#processor free to do anything else
led_onboard.value(1)
sleep(2)
while True:
if GO.value() == 1:
print(“End”)
else:
machine.reset()