from tkinter import *
import serial
import serial.tools.list_ports
import codecs
import math
import time
from math import floor
root = Tk()
csr_acquire = IntVar()
csr_trigger = IntVar()
st_trig = IntVar()
st_ff = IntVar()
st_fe = IntVar()
st_rxfe = IntVar()
g_s = IntVar()
deb_var = IntVar()
class Application(Frame):
""" Create the window and populate with widgets """
def __init__(self,parent):
""" initializes the frame """
Frame.__init__(self,parent,background="white")
#
# frame 1 holds the QUIT and status line
#
self.frame1 = Frame(parent)
self.frame1.grid(row=0,column=0,sticky=W)
self.frame1.config(highlightthickness=1,highlightbackground="black")
#
# frame 2 holds the fetching of 16 bit words
#
self.frame2 = Frame(parent)
self.frame2.grid(row=1,column=0,sticky=W)
self.frame2.config(highlightthickness=1,highlightbackground="black")
#
# frame 3 holds the setting and verifying of 8-bit parameters
#
self.frame3 = Frame(parent)
self.frame3.grid(row=2,column=0,sticky=W)
self.frame3.config(highlightthickness=1,highlightbackground="black")
#
# frame 4 is for reading data from the data fifo
#
self.frame4 = Frame(parent)
self.frame4.grid(row=3,column=0,sticky=W)
self.frame4.config(highlightthickness=1,highlightbackground="black")
#
# frame 5 holds the voltmeter
#
self.frame5 = Frame(parent)
self.frame5.grid(row=4,column=0,sticky=W)
self.frame5.config(highlightthickness=1,highlightbackground="black")
self.parent = parent
self.debugit = False
self.canvas_width = 500
self.canvas_height = 500
self.grid()
self.create_widgets()
self.isopen = 0
self.openPort()
self.voltages = []
self.adcs = []
def create_widgets(self):
self.buttonQ = Button(self.frame1, text="Quit")
self.buttonQ["command"] = self.quitit
self.buttonQ.grid(row=0,column=0, sticky=W)
self.cdebug = Checkbutton(self.frame1,text="Debug?",variable=deb_var)
self.cdebug.grid(row=0,column=1,sticky=W)
self.slabel = Label(self.frame1, text="Status:")
self.slabel.grid(row=1, column=0, sticky=W)
self.status = Text(self.frame1,height=1,width=40)
self.status.grid(row=1, column=1, sticky=W)
self.status.delete("1.0",END)
self.rlabel = Label(self.frame1, text="Result:")
self.rlabel.grid(row=2, column=0, sticky=W)
self.answer = Text(self.frame1,height=1, width=40)
self.answer.grid(row=2,column=1,sticky=W)
self.answer.delete("1.0",END)
#
# set up the fetch buttons to get 16 bit words
# firmware version, test vector from switches,
# and the ADC value present
#
self.fetchlabel = Label(self.frame2, text="Fetch:")
self.fetchlabel.grid(row=0, column=1, sticky=W)
self.buttonV = Button(self.frame2,text="Version")
self.buttonV.grid(row=0,column=2, sticky=W)
self.buttonV["command"] = lambda: self.getdata16(0xa)
self.buttonS = Button(self.frame2,text="Test")
self.buttonS.grid(row=0,column=3, sticky=W)
self.buttonS["command"] = lambda: self.getdata16(0xc)
self.buttonD = Button(self.frame2,text="ADC Data")
self.buttonD.grid(row=0,column=4, sticky=W)
self.buttonD["command"] = lambda: self.getdata16(8)
self.text16 = Text(self.frame2, height=1, width=10)
self.text16.grid(row=0, column=5)
#
# next, set 8-bit parameters
#
# status register:
#
row = 0
self.setlabel = Label(self.frame3, text="Status Register:")
self.setlabel.grid(row=row, column=0, sticky=W)
self.buttonRS = Button(self.frame3, text="Read",
command=self.readStatus)
self.buttonRS.grid(row=row, column=1, sticky=W)
self.RStext = Text(self.frame3, height=1, width=10)
self.RStext.grid(row=row, column=2, sticky=W)
row = row + 1
self.sglobal = Checkbutton(self.frame3,text="Start",variable=g_s)
self.sglobal.grid(row=row,column=1,sticky=W)
self.strigger = Checkbutton(self.frame3,text="Trigger Ena",variable=st_trig)
self.strigger.grid(row=row,column=2,sticky=W)
self.sfifofull = Checkbutton(self.frame3,text="ADC Fifo Full",variable=st_ff)
self.sfifofull.grid(row=row,column=3,sticky=W)
self.sfifoempty = Checkbutton(self.frame3,text="ADC Fifo Empty",variable=st_fe)
self.sfifoempty.grid(row=row,column=4,sticky=W)
row = row + 1
self.rxfifoempty = Checkbutton(self.frame3,text="RX Fifo Empty",variable=st_rxfe)
self.rxfifoempty.grid(row=row,column=4,sticky=W)
#
# control register:
#
row = row + 1
self.csrlabel = Label(self.frame3, text="Control Register:")
self.csrlabel.grid(row=row, column=0, sticky=W)
self.csrread = Button(self.frame3, text="Read",command=self.readCSR)
self.csrread.grid(row=row, column=1, sticky=W)
self.csrtext = Text(self.frame3, height=1, width=6)
self.csrtext.grid(row=row, column=2, sticky=W)
row = row + 1
self.buttonSC = Button(self.frame3, text="Set",
command=self.setCSR)
self.buttonSC.grid(row=row, column=1, sticky=W)
self.radacq = Checkbutton(self.frame3, text="Acquire", variable=csr_acquire)
self.radacq.grid(row=row,column=2,sticky=W)
self.radtrig = Checkbutton(self.frame3, text="Trigger", variable=csr_trigger)
self.radtrig.grid(row=row,column=3,sticky=W)
#
# threshold register
#
row = row + 1
self.thlabel = Label(self.frame3, text="Threshold Register:")
self.thlabel.grid(row=row, column=0, sticky=W)
self.buttonST = Button(self.frame3, text="Set",
command=self.setThreshold)
self.buttonST.grid(row=row, column=1, sticky=W)
self.STtext = Text(self.frame3, height=1, width=10)
self.STtext.grid(row=row,column=2, sticky=W)
self.STtext.insert("1.0","80")
self.buttonRT = Button(self.frame3, text="Read",
command=self.readThreshold)
self.buttonRT.grid(row=row, column=3, sticky=W)
self.RTstext = Text(self.frame3, height=1, width=10)
self.RTstext.grid(row=row, column=4, sticky=W)
#
# prefill register - how many data points to save before trigger
#
row = row + 1
self.blabel = Label(self.frame3, text="Prefill Register:")
self.blabel.grid(row=row, column=0, sticky=W)
self.buttonSB = Button(self.frame3, text="Set",
command=self.setPrefill)
self.buttonSB.grid(row=row, column=1, sticky=W)
self.SBtext = Text(self.frame3, height=1, width=10)
self.SBtext.grid(row=row,column=2, sticky=W)
self.SBtext.insert("1.0","60")
self.buttonRB = Button(self.frame3, text="Read",
command=self.readPrefill)
self.buttonRB.grid(row=row, column=3, sticky=W)
self.RBstext = Text(self.frame3, height=1, width=10)
self.RBstext.grid(row=row, column=4, sticky=W)
#
# read data from the fifo
#
self.dlabel = Label(self.frame4, text="Fifo Data:")
self.dlabel.grid(row=0, column=0, sticky=W)
self.buttonD = Button(self.frame4, text="Read 1",
command=self.read1Data)
self.buttonD.grid(row=0, column=1, sticky=W)
self.dtext = Text(self.frame4, height=1, width=8)
self.dtext.grid(row=0, column=2, sticky=W)
self.buttonAll = Button(self.frame4, text="Read till Empty",
command=self.readAllData)
self.buttonAll.grid(row=0,column=3, sticky=W)
self.buttonPlot = Button(self.frame4, text="Plot",
command=self.plotAll)
self.buttonPlot.grid(row=0, column=4, sticky=W)
self.buttonreplot = Button(self.frame4, text="Replot",
command=self.rePlot)
self.buttonreplot.grid(row=1, column=4, sticky=W)
self.textLow = Text(self.frame4,height=1,width=6)
self.textLow.grid(row=1,column=5,sticky=W)
self.textLow.insert("1.0",0)
self.textHi = Text(self.frame4,height=1,width=6)
self.textHi.grid(row=1,column=6,sticky=W)
self.textHi.insert("1.0",65535)
self.C = Canvas(self.frame5,bg="white",
height=self.canvas_height,width=self.canvas_width)
self.C.grid(row=0,column=0,columnspan=5)
def plotAll(self):
self.debugit = deb_var.get() == 1
self.plot()
def readAllData(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("readAllData:")
#
# read until fifo empty but not more than 1024
#
n = 1
self.voltages.clear()
self.adcs.clear()
for i in range(0,65535):
idata = self.fiforead()
if idata == 0:
print(" timeout reading n="+str(n))
break
adc = idata >> 4
self.adcs.append(adc)
volt = 0.244e-3 * adc
self.voltages.append(volt)
last = idata & 1
empty = (idata >> 1) & 1
start = (idata >> 2) & 1
triggered = (idata >> 3) & 1
if triggered == 1:
if self.debugit: print(" "+str(n)+" data="+hex(idata)+" ADC="+hex(adc)+" empty="+str(empty)+" TRIGGERED!")
else:
if self.debugit: print(" "+str(n)+" data="+hex(idata)+" ADC="+hex(adc)+" empty="+str(empty))
if last == 1:
if self.debugit: print("last word! "+hex(idata))
break
n += 1
# if n%1000 == 0:
# print(".",end="")
if self.debugit:
print("")
print(" read "+str(n)+" times")
self.plot()
def read1Data(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("read1Data:")
#
# read 1 data point (16 bits, 2 bytes) from the fifo
#
idata = self.fiforead()
if self.debugit: print(" data read back: "+hex(idata))
last = idata & 1
empty = (idata >> 1) & 1
start = (idata >> 2) & 1
triggered = (idata >> 3) & 1
if self.debugit: print("triggered="+str(triggered)+" start="+str(start)+" empty="+str(empty)+" last="+str(last))
self.dtext.delete(1.0,END)
self.dtext.insert(1.0,hex(idata))
def fiforead(self):
#
# 1st write 0x8 indicating read fifo
#
haddr = 0x10
if self.debugit: print(" writing "+hex(haddr))
sendb = haddr.to_bytes(1,"little")
self.ser.write(sendb)
#
# read 2 bytes
#
rdata = self.ser.read(2)
idata = int.from_bytes(rdata,byteorder="little")
if len(rdata) == 0:
return 0
else:
return idata
def write_to_address(self,addr,value):
if self.debugit: print("write_to_address:")
#
# this function does 3 writes to the FPGA:
# the first is a byte of instructions
# the 2nd is the lower byte of the data word
# the 3rd is the upper byte of the data word
#
if self.isopen == 0:
self.status.insert(END,"You MUST open a port first!\n Doing it for you...\n")
self.openPort()
#
# write addr first, tells the FPGA to get ready for value,
# which comes 2nd and 3rd. addr and value are integers
#
haddr = addr
if self.debugit: print(" writing "+hex(value)+" to address "+hex(addr))
self.status.insert(END," sending write command to address "+hex(addr))
sendb = haddr.to_bytes(1,"little")
self.ser.write(sendb)
#
# send the data you want to write
#
value_low = value & 0xFF
if self.debugit: print(" sending "+hex(value_low))
sendb = value_low.to_bytes(1,"little")
if self.debugit: print(" in bytes is "+str(sendb))
self.ser.write(sendb)
value_high = value >> 8
if self.debugit: print(" sending "+hex(value_high))
sendb = value_high.to_bytes(1,"little")
if self.debugit: print(" in bytes is "+str(sendb))
self.ser.write(sendb)
def read_from_address(self,addr):
if self.isopen == 0:
self.status.insert(END,"You MUST open a port first!\n Doing it for you...\n")
self.openPort()
if self.debugit: print("read_from_address:")
if self.debugit: print(" write to address "+hex(addr))
self.status.delete(1.0,END)
self.status.insert(END,"write command to address 0x"+hex(addr))
sendb = addr.to_bytes(1,"little")
#
# write the instruction
#
self.ser.write(sendb)
#
# now read the result
#
tdata = self.ser.read(2)
if self.debugit: print(" value read back in bytes: "+str(tdata))
idata = int.from_bytes(tdata,byteorder="little")
if self.debugit: print(" value read back in hex: "+hex(idata))
if len(tdata) == 0:
return 0
else:
return idata
def readCSR(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("readCSR")
haddr = 0x0
idata = self.read_from_address(haddr)
self.csrtext.delete(1.0,END)
self.csrtext.insert(1.0,hex(idata))
if idata & 1 > 0:
csr_acquire.set(1)
else:
csr_acquire.set(0)
if idata & 2 > 0:
csr_trigger.set(1)
else:
csr_trigger.set(0)
def setCSR(self):
self.debugit = deb_var.get() == 1
#
# this function will take the bits from the checkboxes and
# overwrite CSR
#
if self.debugit: print("setCSR:")
if self.isopen == 0:
self.status.insert(END,"You MUST open a port first!\n Doing it for you...\n")
self.openPort()
#
# so far we only have 2 bits, but we could easily have more
#
bit0 = csr_acquire.get()
bit1 = csr_trigger.get()
if self.debugit: print(" bit 0 "+str(bit0))
if self.debugit: print(" bit 1 "+str(bit1))
#
# write the data
#
value = bit0 + 2*bit1
if self.debugit: print(" value = "+hex(value))
haddr = 1
self.write_to_address(haddr,value)
def write_csr(self,which_bit,bit_val):
if self.debugit: print("write_csr:")
#
# first read CSR
#
addr = 0x0
if self.debugit: print(" bit="+str(which_bit)+" and bit value = "+str(bit_val))
value = self.read_from_address(addr)
if value == 0:
print("TIMEOUT!!!")
if self.debugit: print(" reading from address 0x22 returns "+hex(value))
self.status.insert(END," CSR value read is "+str(value)+"\n")
vor = 2**which_bit
if bit_val == 1:
value_new = value | vor
else:
value_new = value & ~vor
self.status.insert(END," sending "+hex(value_new)+"\n")
#
# now write to csr, address 3
#
addr = 3
self.write_to_address(addr,value_new)
def readStatus(self):
self.debugit = deb_var.get() == 1
#
# write to 0x2, then read back 2 byte status register
#
if self.debugit: print("readStatus:")
haddr = 0x2
idata = self.read_from_address(haddr)
if idata == 0:
print("TIMEOUT!!!")
if self.debugit: print(" status value read back is "+hex(idata))
self.RStext.delete(1.0,END)
self.RStext.insert(1.0,hex(idata))
#
# check the bits. here is the latest:
# bit 0 global_start enabled
# 1 trigger enabled
# 2 rx_fifo_empty
# 3 data_fifo_empty
# 4 data_fifo_full
# 5 triggered
if idata & 1 > 0:
g_s.set(1)
else:
g_s.set(0)
if idata & 2 > 0:
st_trig.set(1)
else:
st_trig.set(0)
if idata & 8 > 0:
st_fe.set(1)
else:
st_fe.set(0)
if idata & 16 > 0:
st_ff.set(1)
else:
st_ff.set(0)
if idata & 4 > 1:
st_rxfe.set(1)
else:
st_rxfe.set(0)
def setThreshold(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("setThreshold:")
haddr = 3
thresh = self.STtext.get("1.0",END).strip('\n')
thresh_int = int(thresh,16)
if self.debugit: print(" sending threshold "+thresh)
self.write_to_address(haddr,thresh_int)
def readThreshold(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("readThreshold:")
haddr = 0x4
idata = self.read_from_address(haddr)
if idata == 0:
print("TIMEOUT!!!")
if self.debugit: print(" threshold value read back from address "+hex(haddr)+" is "+hex(idata))
self.RTstext.delete(1.0,END)
self.RTstext.insert(1.0,hex(idata))
def setPrefill(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("setPrefill:")
haddr = 5
prefill = self.SBtext.get("1.0",END).strip('\n')
prefill_int = int(prefill,16)
self.write_to_address(haddr,prefill_int)
def readPrefill(self):
self.debugit = deb_var.get() == 1
if self.debugit: print("readPrefill:")
haddr = 0x6
idata = self.read_from_address(haddr)
if idata == 0:
print("TIMEOUT!!!")
if self.debugit: print(" prefill value read back from address "+hex(haddr)+" is "+hex(idata))
self.RBstext.delete(1.0,END)
self.RBstext.insert(1.0,hex(idata))
def quitit(self):
print("That's all folks!")
quit()
def openPort(self):
if self.isopen == 1:
self.status.insert(END,"Port is already open!\n")
self.ser.close()
#return
#
# defaults
#
port = "/dev/ttyS0"
sbaud = "1000000"
baud = int(sbaud)
timeout = 5
if self.debugit: print("port="+port+" baud="+sbaud)
self.ser = serial.Serial(port,sbaud,timeout=timeout)
if self.ser.isOpen():
self.status.insert(END,self.ser.name + " is now open\n")
print(self.ser.name + " is now open...")
self.isopen = 1
else:
self.status.insert(END,self.ser.name + " is NOT open!!!\n")
print("sorry, problem trying to open port "+port+"\n")
def getdata16(self,which):
self.debugit = deb_var.get() == 1
#
# fetches 1 of the 16 bit words
#
#
# first check to see if any port has been opened or not
#
if self.isopen == 0:
self.status.insert(END,"Sorry but you MUST open a port first!")
return
#
# fetch what?
#
addr = which;
if which == 0:
self.status.insert(END,"??? invalid address!!!")
return
#
# send the command
#
if self.debugit: print("sending "+str(addr))
self.status.delete(1.0,END)
self.status.insert(1.0,"Sending...")
sendb = addr.to_bytes(1,"little")
self.ser.write(sendb)
self.status.insert(END,"done!")
#
# now wait a short time and then look for input
#
nbytes = 2
self.status.insert(END," Waiting...")
#time.sleep(0.1)
tdata = self.ser.read(nbytes)
ld = len(tdata)
if self.debugit: print(ld)
if ld > 0:
#
# flag input has arrived and print out in hex
#
noinput = 0
idata = int.from_bytes(tdata,byteorder="little")
self.text16.delete(1.0,END)
self.text16.insert(1.0,hex(idata))
if self.debugit: print("idata1: "+str(idata))
self.status.insert(END," done!")
self.answer.delete(1.0,END)
#
# check to see if this is the ADC data, then make the display of voltage
#
if addr == 8:
val = idata/16
if self.debugit: print("idata: ",str(val))
#
# now calculate the votage read
#
voltage = val * 0.244E-3
self.answer.delete(1.0,END)
svolt = "{:.2f}".format(voltage)
#svolt = str(round("{:.2f}".format(voltage)
self.answer.insert(1.0,hex(int(val)) + " = " +svolt+" volts")
self.display(voltage)
else:
self.answer.insert(1.0,hex(idata))
else:
self.status.insert(END," timeout!")
def rePlot(self):
#
# get low and high fifo bins to plot
#
self.C.delete("all")
xlow = int(self.textLow.get("1.0",END).strip('\n'))
xhi = int(self.textHi.get("1.0",END).strip('\n'))
self.plotPoints(xlow,xhi,False)
def plot(self):
print("drawing plot...")
self.C.delete("all")
nmax =len(self.voltages)
self.plotPoints(0,65535,False)
print(" done!")
def plotPoints(self,xlow,xhigh,connect):
#
# xoff and yoff are the offsets for the plot in the canvas
#
xoff = 50
yoff = 50
voltmax = 1.0
if len(self.voltages) == 0:
print(" no data to plot!")
return
width = self.canvas_width
height = self.canvas_height
pw = width - 2*xoff
ph = height - 2*yoff
self.C.create_rectangle(xoff,yoff,width-xoff,height-yoff)
n = 0
radius = 1
xold = 0
yold = 0
#
# display the y axis, voltage
#
self.C.create_text(xoff-20,20,text="Voltage")
self.C.create_text(xoff-15,height-yoff,text="0.0")
self.C.create_text(xoff-15,height-yoff-0.25*ph,text="0.25")
self.C.create_text(xoff-15,height-yoff-0.5*ph,text="0.5")
self.C.create_text(xoff-15,height-yoff-0.75*ph,text="0.75")
self.C.create_text(xoff-15,height-yoff-ph,text="1.0")
#
# display x axis, time. note that delta-t is 1us
#
# make 10 intervals, so 11 ticks starting at 0
#
nvolts = xhigh-xlow #len(self.voltages)
ticky = 10
ninterval = 10
deltax = (self.canvas_width-2*xoff)/ninterval
xtick = xoff
timeScale = "ms"
if nvolts < 1001:
dscale = 1.0*nvolts/ninterval
timeScale = "us"
else:
dscale = 1.0*nvolts/(1000*ninterval)
scale = 0.0
for i in range(0,ninterval):
self.C.create_line(xtick,height-yoff-ticky/2,xtick,height-yoff+ticky/2)
sscale = str( floor(scale*10)/10 )
self.C.create_text(xtick-5,height-yoff/2,text=sscale)
scale += dscale
xtick += deltax
self.C.create_text(self.canvas_width-20,height-yoff/2,text=timeScale)
for i in range(xlow,xhigh):
volt = self.voltages[i]
y = height - yoff - volt*ph/voltmax
x = xoff + n*pw/nvolts
x0 = x - radius
x1 = x + radius
y0 = y - radius
y1 = y + radius
self.C.create_oval(x0,y0,x1,y1)
if n == 0:
xold = x
yold = y
else:
if connect: self.C.create_line(x,y,xold,yold)
xold = x
yold = y
n += 1
def display(self,volts):
self.C.delete("all")
#
# draw a circle centered at half the canvas width and height
# with radius circle_radius
#
circle_radius = 100
circle_center_x = self.canvas_width/2
circle_center_y = circle_radius + 50
print(str(circle_radius))
print(str(circle_center_x))
print(str(circle_center_y))
left_x = circle_center_x - 100
right_x = circle_center_x + 100
left_y = circle_center_y - 100
right_y = circle_center_y + 100
self.C.create_oval(left_x,left_y,right_x,right_y)
#
# voltage goes from 0 to 1, so scale the angle of
# the meter line
#
angle = math.pi * volts
linex = circle_radius * math.cos(angle)
liney = circle_radius * math.sin(angle)
x0 = circle_center_x
y0 = circle_center_y
self.C.create_line(x0,y0,x0-linex,y0-liney)
for i in range(0,11):
val = float(i)/10
sval = str(val)
ang = math.pi * val
x1 = x0 - (circle_radius+10) * math.cos(ang)
y1 = y0 - (circle_radius+10) * math.sin(ang)
self.C.create_text(x1,y1,text=sval)
def main():
# modify the window
root.title("Python/BASYS3 DAQ")
root.wm_title("Python/BASYS3 DAQ")
root.geometry("600x900+500+100")
root.update()
#create the frame that holds other widgets
app = Application(root)
#kick off event loop
root.mainloop()
if __name__ == '__main__':
main()