added initial config

This commit is contained in:
Ayzen
2026-02-17 22:20:47 +03:00
parent 4066899530
commit 8ca2fe39d0
7 changed files with 225 additions and 126 deletions

Binary file not shown.

View File

@ -3,6 +3,7 @@ import json
import math
import socket
import subprocess
import time
import device_interaction as dev
@ -23,6 +24,14 @@ INITIAL_TEMPERATURE_2 = 28.9 # Set initial temperature for Laser 2 in Celsius: f
INITIAL_CURRENT_1 = 33 # 64.0879 max # Set initial current for Laser 1, in mA
INITIAL_CURRENT_2 = 35 # 64.0879 max # Set initial current for Laser 2, in mA
AD9833_FREQ_DEFAULT_KHZ = 125.0
AD9833_MCLK_DEFAULT_MHZ = 20.0
DS1809_MAX_STEP = 63
DS1809_DEFAULT_STEP = 0
DS1809_INIT_HOME_PULSES = 64
DS1809_INIT_PULSE_MS = 2
DS1809_INIT_STARTUP_DELAY_S = 0.35
#### ---- Functions
def start_task(prt):
@ -73,6 +82,38 @@ def shorten(i):
return "{:.2f}".format(round(i, 2))
def clamp_int(value, min_value, max_value):
if value < min_value:
return min_value
if value > max_value:
return max_value
return value
def format_ds1809_status(step):
return f"{step}/{DS1809_MAX_STEP} шагов"
def initialize_ds1809_position(prt, default_step):
default_step = clamp_int(int(default_step), 0, DS1809_MAX_STEP)
# Give the STM32 side a short startup margin before DS1809 pulse traffic.
time.sleep(DS1809_INIT_STARTUP_DELAY_S)
dev.send_ds1809_pulse(prt, uc=False, dc=True,
count=DS1809_INIT_HOME_PULSES,
pulse_ms=DS1809_INIT_PULSE_MS)
current_step = 0
if default_step > 0:
dev.send_ds1809_pulse(prt, uc=True, dc=False,
count=default_step,
pulse_ms=DS1809_INIT_PULSE_MS)
current_step = default_step
return current_step
def set_initial_params():
params = {}
params['Temp_1'] = INITIAL_TEMPERATURE_1 # Initial temperature for Laser 1
@ -108,9 +149,11 @@ def set_initial_params():
params['RampSramMode'] = False
params['RampSramSamples'] = ''
params['RampSramAmp'] = ''
params['Ad9833Freq'] = ''
params['Ad9833Mclk'] = '25'
params['Ad9833Freq'] = str(AD9833_FREQ_DEFAULT_KHZ)
params['Ad9833Mclk'] = str(AD9833_MCLK_DEFAULT_MHZ)
params['Ad9833Triangle'] = True
params['DS1809Step'] = DS1809_DEFAULT_STEP
params['DS1809Status'] = format_ds1809_status(DS1809_DEFAULT_STEP)
return params
def update_data_lists():
@ -139,12 +182,29 @@ if __name__ == "__main__":
# dev.request_state(prt)
dev.send_control_parameters(prt, params)
saved_data.append(dev.request_data(prt))
draw_data.append(saved_data[0])
window = gui.setup_gui(params)
axes_signs = gui.sign_axes(window)
ds1809_step = initialize_ds1809_position(prt, params['DS1809Step'])
params['DS1809Step'] = ds1809_step
params['DS1809Status'] = format_ds1809_status(ds1809_step)
window['-DS1809Status-'].update(params['DS1809Status'])
initial_data = None
for _ in range(20):
initial_data = dev.request_data(prt)
if isinstance(initial_data, dict):
break
time.sleep(0.05)
if not isinstance(initial_data, dict):
print('Error: initial DATA packet not received. Closing program...')
exit(1)
saved_data.append(initial_data)
draw_data.append(initial_data)
current_and_temperature_settings_available = True
disableStartButton = False
@ -492,22 +552,33 @@ if __name__ == "__main__":
sram_mode=sram_mode, sram_samples=sram_samples,
sram_amplitude=sram_amplitude)
elif event == '-StartRamp9833-':
freq_hz = parse_optional_float(values.get('-AD9833Freq-'))
freq_khz = parse_optional_float(values.get('-AD9833Freq-'))
freq_hz = int(round(freq_khz * 1000.0)) if freq_khz is not None else None
mclk_mhz = parse_optional_float(values.get('-AD9833Mclk-'))
mclk_hz = mclk_mhz * 1e6 if mclk_mhz is not None else None
triangle = values.get('-AD9833Triangle-', True)
dev.start_ad9833_ramp(prt, freq_hz=freq_hz, mclk_hz=mclk_hz, triangle=triangle, enable=True)
elif event == '-DS1809UC-':
dev.send_ds1809_pulse(prt, uc=True, dc=False)
dev.send_ds1809_pulse(prt, uc=True, dc=False, count=1, pulse_ms=DS1809_INIT_PULSE_MS)
ds1809_step = clamp_int(ds1809_step + 1, 0, DS1809_MAX_STEP)
params['DS1809Step'] = ds1809_step
params['DS1809Status'] = format_ds1809_status(ds1809_step)
window['-DS1809Status-'].update(params['DS1809Status'])
elif event == '-DS1809DC-':
dev.send_ds1809_pulse(prt, uc=False, dc=True)
dev.send_ds1809_pulse(prt, uc=False, dc=True, count=1, pulse_ms=DS1809_INIT_PULSE_MS)
ds1809_step = clamp_int(ds1809_step - 1, 0, DS1809_MAX_STEP)
params['DS1809Step'] = ds1809_step
params['DS1809Status'] = format_ds1809_status(ds1809_step)
window['-DS1809Status-'].update(params['DS1809Status'])
elif event == '-StopCycle-':
window['-StopCycle-'].update(disabled = True)
current_and_temperature_settings_available = True
stop_task(prt)
elif event == TIMEOUT_KEY:
data = dev.request_data(prt)
if not isinstance(data, dict):
continue
update_data_lists()
window['-TOUT_1-'].update(gui.READ_TEMPERATURE_TEXT+' 1: '+shorten(data['Temp_1'])+' C')

View File

@ -27,7 +27,7 @@ AD9102_SRAM_HOLD_DEFAULT = 1
AD9102_SRAM_AMP_DEFAULT = 8191
AD9833_FLAG_ENABLE = 0x0001
AD9833_FLAG_TRIANGLE = 0x0002
AD9833_MCLK_HZ_DEFAULT = 25_000_000
AD9833_MCLK_HZ_DEFAULT = 20_000_000
DS1809_FLAG_UC = 0x0001
DS1809_FLAG_DC = 0x0002
DS1809_PULSE_MS_DEFAULT = 2
@ -222,13 +222,15 @@ def get_STATE(prt):
def get_DATA(prt):
''' Get decoded state of the device in byte format (426 bytes).
''' Get decoded state of the device in byte format (30 bytes).
'''
print("Received "+str(prt.inWaiting())+" bytes.\n")
if prt.inWaiting()!=GET_DATA_TOTAL_LENGTH:
print("Error. Couldn't get DATA data.")
print("receiven data len:", prt.inWaiting())
if prt.inWaiting() > 0:
print("Flushing input data:", prt.read(prt.inWaiting()))
return None
out_bytes = prt.read(GET_DATA_TOTAL_LENGTH)

View File

@ -1,118 +1,96 @@
import time
from datetime import datetime
import device_commands as cmd
#### ---- Constants
WAIT_AFTER_SEND = 0.15 # Wait after sending command, before requesting input (in seconds).
WAIT_AFTER_SEND = 0.15 # Wait after sending command before requesting input (s).
#### ---- High-level port commands
'''
def create_port_connection():
prt = None
for port, _, _ in sorted(cmd.list_ports.comports()):
try:
prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
cmd.open_port(prt)
reset_port_settings(prt)
except:
prt.close()
continue
break
return prt
'''
def create_port_connection():
prt = None
print()
ports = []
for port, _,_ in sorted(cmd.list_ports.comports()):
ports.append(port)
#ONLY FOR LINUX!!!
have_ttyUSB = False
USB_ports = []
for port in ports:
if "USB" in port:
USB_ports.append(port)
if len(USB_ports):
ports = USB_ports
# print("ports:", ports)
ports = [port for port, _, _ in sorted(cmd.list_ports.comports())]
# Linux-only preference: use USB UART ports first.
usb_ports = [port for port in ports if "USB" in port]
if usb_ports:
ports = usb_ports
# for port, _, _ in sorted(cmd.list_ports.comports()):
for port in ports:
try:
print("PORT:", port)
prt = cmd.setup_port_connection(port=port, baudrate=115200, timeout_sec=1)
cmd.open_port(prt)
reset_port_settings(prt)
except:
prt.close()
return prt
except Exception:
if prt is not None:
try:
prt.close()
except Exception:
pass
continue
break
return prt
return None
def _print_state_reply(state_bytes):
if state_bytes is None:
return False
# def setup_connection():
# prt = cmd.setup_port_connection()
# cmd.open_port(prt)
# return prt
status = state_bytes.hex()
print("Received: STATE. State status:", cmd.decode_STATE(status), "(" + cmd.flipfour(status) + ")")
print("")
return True
def reset_port_settings(prt):
# Reset port settings and check status
cmd.send_DEFAULT_ENABLE(prt)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
return _print_state_reply(cmd.get_STATE(prt))
def request_state(prt):
# Request data
cmd.send_STATE(prt)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
return _print_state_reply(cmd.get_STATE(prt))
def send_control_parameters(prt, params):
# Send control parameters
hexstring = cmd.encode_Input(params)
cmd.send_DECODE_ENABLE(prt,hexstring)
cmd.send_DECODE_ENABLE(prt, hexstring)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
else:
print("")
return _print_state_reply(cmd.get_STATE(prt))
def send_task_command(prt, sending_param):
# Send task command (TASK_ENABLE state in firmware)
hexstring = cmd.create_TaskEnableCommand(sending_param)
cmd.send_TASK_ENABLE(prt,hexstring)
cmd.send_TASK_ENABLE(prt, hexstring)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
else:
print("")
return _print_state_reply(cmd.get_STATE(prt))
def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None, pat_period_base=None, dac_clk_hz=None, triangle=True, sram_mode=False, sram_samples=None, sram_hold=None, sram_amplitude=None):
def start_ramp_max(
prt,
freq_hz=None,
duty=None,
saw_step=None,
pat_period=None,
pat_period_base=None,
dac_clk_hz=None,
triangle=True,
sram_mode=False,
sram_samples=None,
sram_hold=None,
sram_amplitude=None,
):
# Start AD9102 sawtooth with configurable frequency/duty or SRAM ramp mode
if sram_mode:
if sram_hold is None:
@ -121,9 +99,14 @@ def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None,
if dac_clk_hz is None:
dac_clk_hz = cmd.AD9102_DAC_CLK_HZ
sram_samples = cmd.calc_sram_samples_for_freq(freq_hz, dac_clk_hz, sram_hold)
hexstring = cmd.create_AD9102_ramp_command(enable=True, triangle=triangle, sram_mode=True,
sram_samples=sram_samples, sram_hold=sram_hold,
sram_amplitude=sram_amplitude)
hexstring = cmd.create_AD9102_ramp_command(
enable=True,
triangle=triangle,
sram_mode=True,
sram_samples=sram_samples,
sram_hold=sram_hold,
sram_amplitude=sram_amplitude,
)
else:
if pat_period_base is None:
pat_period_base = cmd.AD9102_PAT_PERIOD_BASE_DEFAULT
@ -137,74 +120,113 @@ def start_ramp_max(prt, freq_hz=None, duty=None, saw_step=None, pat_period=None,
pat_period = cmd.calc_pat_period_for_duty(saw_step, duty, pat_period_base, triangle)
if pat_period is None:
pat_period = cmd.AD9102_PAT_PERIOD_DEFAULT
hexstring = cmd.create_AD9102_ramp_command(saw_step, pat_period, pat_period_base,
enable=True, triangle=triangle)
hexstring = cmd.create_AD9102_ramp_command(
saw_step,
pat_period,
pat_period_base,
enable=True,
triangle=triangle,
)
cmd.send_AD9102(prt, hexstring)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
else:
print("")
return _print_state_reply(cmd.get_STATE(prt))
def start_ad9833_ramp(prt, freq_hz=None, mclk_hz=None, triangle=True, enable=True):
if freq_hz is None:
freq_hz = 0.0
hexstring = cmd.create_AD9833_ramp_command(freq_hz=freq_hz, mclk_hz=mclk_hz,
enable=enable, triangle=triangle)
hexstring = cmd.create_AD9833_ramp_command(
freq_hz=freq_hz,
mclk_hz=mclk_hz,
enable=enable,
triangle=triangle,
)
cmd.send_AD9833(prt, hexstring)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
else:
print("")
return _print_state_reply(cmd.get_STATE(prt))
def _wait_for_min_bytes(prt, expected_len, timeout_s, poll_s=0.01):
deadline = time.time() + timeout_s
while time.time() < deadline:
waiting = prt.inWaiting()
if waiting >= expected_len:
return True
time.sleep(poll_s)
return prt.inWaiting() >= expected_len
def send_ds1809_pulse(prt, uc=False, dc=False, count=1, pulse_ms=None):
if count is None or count <= 0:
count = 1
if pulse_ms is None or pulse_ms <= 0:
pulse_ms = cmd.DS1809_PULSE_MS_DEFAULT
hexstring = cmd.create_DS1809_pulse_command(uc=uc, dc=dc, count=count, pulse_ms=pulse_ms)
cmd.send_DS1809(prt, hexstring)
time.sleep(WAIT_AFTER_SEND)
status = cmd.get_STATE(prt).hex()
if status is not None:
print("Received: STATE. State status:", cmd.decode_STATE(status), "("+cmd.flipfour(status)+")")
print("")
else:
print("")
# Firmware blocks while pulsing DS1809 lines: wait pulse train + safe margin.
pulse_train_time = (2.0 * float(count) * float(pulse_ms)) / 1000.0
time.sleep(max(WAIT_AFTER_SEND, pulse_train_time + 0.35))
# Then poll shortly for STATE bytes; this avoids early read (0 bytes) on startup.
_wait_for_min_bytes(prt, expected_len=2, timeout_s=0.8)
return _print_state_reply(cmd.get_STATE(prt))
def request_data(prt):
# Request data
cmd.send_TRANS_ENABLE(prt)
time.sleep(WAIT_AFTER_SEND)
data = cmd.get_DATA(prt).hex()
data_dict = []
if data is not None:
data_dict = cmd.decode_DATA(data)
return data_dict
data_bytes = cmd.get_DATA(prt)
if data_bytes is None:
return None
return cmd.decode_DATA(data_bytes.hex())
def print_data(data):
def shorten(i):
return str(round(i, 2))
print("Data from device (time: "+datetime.now().strftime("%H:%M:%S:%f")+"):")
print("Message Header:", data['Header'], " Message ID:", data['Message_ID'])
print("Photodiode Current 1 ("+str(len(data['I1']))+" values):", \
shorten(data['I1']), shorten(data['I1'][1]), "...", \
shorten(data['I1']), shorten(data['I1'][-1]), "mA")
print("Photodiode Current 2 ("+str(len(data['I2']))+" values):", \
shorten(data['I2']), shorten(data['I2'][1]), "...", \
shorten(data['I2']), shorten(data['I2'][-1]), "mA")
print("Laser Temperature 1:", shorten(data['Temp_1']), "C")
print("Laser Temperature 2:", shorten(data['Temp_2']), "C")
print("Temperature of external thermistor 1:", shorten(data['Temp_Ext_1']), "C")
print("Temperature of external thermistor 2:", shorten(data['Temp_Ext_2']), "C")
print("Voltages 3V3: "+shorten(data['MON_3V3'])+"V 5V1: "+shorten(data['MON_5V1'])+ \
"V 5V2: "+shorten(data['MON_5V2'])+"V 7V0: "+shorten(data['MON_7V0'])+"V.")
print("Data from device (time: " + datetime.now().strftime("%H:%M:%S:%f") + "):")
print("Message Header:", data["Header"], " Message ID:", data["Message_ID"])
print(
"Photodiode Current 1 (" + str(len(data["I1"])) + " values):",
shorten(data["I1"]),
shorten(data["I1"][1]),
"...",
shorten(data["I1"]),
shorten(data["I1"][-1]),
"mA",
)
print(
"Photodiode Current 2 (" + str(len(data["I2"])) + " values):",
shorten(data["I2"]),
shorten(data["I2"][1]),
"...",
shorten(data["I2"]),
shorten(data["I2"][-1]),
"mA",
)
print("Laser Temperature 1:", shorten(data["Temp_1"]), "C")
print("Laser Temperature 2:", shorten(data["Temp_2"]), "C")
print("Temperature of external thermistor 1:", shorten(data["Temp_Ext_1"]), "C")
print("Temperature of external thermistor 2:", shorten(data["Temp_Ext_2"]), "C")
print(
"Voltages 3V3: "
+ shorten(data["MON_3V3"])
+ "V 5V1: "
+ shorten(data["MON_5V1"])
+ "V 5V2: "
+ shorten(data["MON_5V2"])
+ "V 7V0: "
+ shorten(data["MON_7V0"])
+ "V."
)
def close_connection(prt):
cmd.close_port(prt)

6
gui.py
View File

@ -51,13 +51,14 @@ SET_RAMP_SRAM_MODE_TEXT = 'SRAM режим'
SET_RAMP_SRAM_SAMPLES_TEXT = 'SRAM точки (samples):'
SET_RAMP_SRAM_AMP_TEXT = 'SRAM амплитуда (%):'
SET_AD9833_SECTION_TEXT = 'Настройка пилы (AD9833)'
SET_AD9833_FREQ_TEXT = 'Частота AD9833 (Гц):'
SET_AD9833_FREQ_TEXT = 'Частота AD9833 (кГц):'
SET_AD9833_MCLK_TEXT = 'MCLK AD9833 (МГц):'
SET_AD9833_TRI_TEXT = 'Треугольник AD9833'
SET_AD9833_BUTTON_TEXT = 'Пила AD9833'
SET_DS1809_SECTION_TEXT = 'DS1809 (UC/DC)'
SET_DS1809_UC_BUTTON_TEXT = 'UC импульс'
SET_DS1809_DC_BUTTON_TEXT = 'DC импульс'
SET_DS1809_STATUS_TEXT = 'Позиция DS1809:'
GRAPH_POINTS_NUMBER = 100 # Number of most recent data points shown on charts
@ -290,6 +291,9 @@ def setup_gui(params):
[sg.Button(SET_DS1809_UC_BUTTON_TEXT, key='-DS1809UC-', disabled_button_color=("Gray22", "Blue")),
sg.Button(SET_DS1809_DC_BUTTON_TEXT, key='-DS1809DC-', disabled_button_color=("Gray22", "Blue"))],
[sg.Text(SET_DS1809_STATUS_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
sg.Text(params.get('DS1809Status', '--'), key='-DS1809Status-', size=(20,1))],
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
[sg.Button(SET_START_BUTTON_TEXT, key='-StartCycle-', disabled_button_color=("Gray22", "Blue"), disabled=True), sg.Button(SET_STOP_BUTTON_TEXT, disabled_button_color=("Gray22", "Blue"), key='-StopCycle-', disabled=True), sg.Button(SET_RAMP_BUTTON_TEXT, key='-StartRamp-', disabled_button_color=("Gray22", "Blue")), sg.Button(SET_AD9833_BUTTON_TEXT, key='-StartRamp9833-', disabled_button_color=("Gray22", "Blue"))]]