ad9102 prestored saw done
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -44,6 +44,31 @@ def get_float(values, strId):
|
|||||||
window['-StartCycle-'].update(disabled = True)
|
window['-StartCycle-'].update(disabled = True)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def parse_optional_int(value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
s = str(value).strip()
|
||||||
|
if s == "":
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return int(s, 0)
|
||||||
|
except Exception:
|
||||||
|
print(f"Invalid integer value: {s}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def parse_optional_float(value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
s = str(value).strip()
|
||||||
|
if s == "":
|
||||||
|
return None
|
||||||
|
s = s.replace(",", ".")
|
||||||
|
try:
|
||||||
|
return float(s)
|
||||||
|
except Exception:
|
||||||
|
print(f"Invalid float value: {s}")
|
||||||
|
return None
|
||||||
|
|
||||||
def shorten(i):
|
def shorten(i):
|
||||||
return "{:.2f}".format(round(i, 2))
|
return "{:.2f}".format(round(i, 2))
|
||||||
|
|
||||||
@ -73,6 +98,13 @@ def set_initial_params():
|
|||||||
params['Delta_Current_2'] = 0.05
|
params['Delta_Current_2'] = 0.05
|
||||||
params['Delta_Time'] = 50
|
params['Delta_Time'] = 50
|
||||||
params['Tau'] = 10
|
params['Tau'] = 10
|
||||||
|
params['RampFreq'] = ''
|
||||||
|
params['RampDuty'] = ''
|
||||||
|
params['RampSawStep'] = ''
|
||||||
|
params['RampPatPeriod'] = ''
|
||||||
|
params['RampPatBase'] = 2
|
||||||
|
params['RampDacClk'] = ''
|
||||||
|
params['RampTriangle'] = True
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def update_data_lists():
|
def update_data_lists():
|
||||||
@ -423,6 +455,23 @@ if __name__ == "__main__":
|
|||||||
params['Iset_2'] = float(values['-InputI2-'])
|
params['Iset_2'] = float(values['-InputI2-'])
|
||||||
dev.send_control_parameters(prt, params)
|
dev.send_control_parameters(prt, params)
|
||||||
#print(sending_param)
|
#print(sending_param)
|
||||||
|
elif event == '-StartRamp-':
|
||||||
|
freq_hz = parse_optional_float(values.get('-RampFreq-'))
|
||||||
|
duty = parse_optional_float(values.get('-RampDuty-'))
|
||||||
|
if duty is not None:
|
||||||
|
if duty > 1.0:
|
||||||
|
duty = duty / 100.0
|
||||||
|
if duty <= 0:
|
||||||
|
duty = None
|
||||||
|
saw_step = parse_optional_int(values.get('-RampSawStep-'))
|
||||||
|
pat_period = parse_optional_int(values.get('-RampPatPeriod-'))
|
||||||
|
pat_period_base = parse_optional_int(values.get('-RampPatBase-'))
|
||||||
|
dac_clk_mhz = parse_optional_float(values.get('-RampDacClk-'))
|
||||||
|
dac_clk_hz = dac_clk_mhz * 1e6 if dac_clk_mhz is not None else None
|
||||||
|
triangle = values.get('-RampTriangle-', True)
|
||||||
|
dev.start_ramp_max(prt, freq_hz=freq_hz, duty=duty, saw_step=saw_step,
|
||||||
|
pat_period=pat_period, pat_period_base=pat_period_base,
|
||||||
|
dac_clk_hz=dac_clk_hz, triangle=triangle)
|
||||||
elif event == '-StopCycle-':
|
elif event == '-StopCycle-':
|
||||||
window['-StopCycle-'].update(disabled = True)
|
window['-StopCycle-'].update(disabled = True)
|
||||||
current_and_temperature_settings_available = True
|
current_and_temperature_settings_available = True
|
||||||
@ -466,4 +515,3 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
dev.close_connection(prt)
|
dev.close_connection(prt)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,13 @@ GET_DATA_TOTAL_LENGTH = 30 # Total number of bytes when getting DATA
|
|||||||
SEND_PARAMS_TOTAL_LENGTH = 30 # Total number of bytes when sending parameters
|
SEND_PARAMS_TOTAL_LENGTH = 30 # Total number of bytes when sending parameters
|
||||||
TASK_ENABLE_COMMAND_LENGTH = 32 # Total number of bytes when sending TASK_ENABLE command
|
TASK_ENABLE_COMMAND_LENGTH = 32 # Total number of bytes when sending TASK_ENABLE command
|
||||||
|
|
||||||
|
AD9833_CMD_TOTAL_LENGTH = 10 # Total bytes when sending AD9102 saw command
|
||||||
|
AD9833_CMD_HEADER = "8888"
|
||||||
|
AD9102_SAW_STEP_DEFAULT = 1
|
||||||
|
AD9102_PAT_PERIOD_DEFAULT = 0xFFFF
|
||||||
|
AD9102_PAT_PERIOD_BASE_DEFAULT = 0x02
|
||||||
|
AD9102_DAC_CLK_HZ = None # set to actual DAC clock if you want freq->SAW_STEP conversion
|
||||||
|
|
||||||
class TaskType(IntEnum):
|
class TaskType(IntEnum):
|
||||||
Manual = 0x00
|
Manual = 0x00
|
||||||
ChangeCurrentLD1 = 0x01
|
ChangeCurrentLD1 = 0x01
|
||||||
@ -148,6 +155,17 @@ def send_STATE(prt):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def send_AD9833(prt, bytestring):
|
||||||
|
''' Start/stop AD9833 output with triangle (ramp) mode (0x8888 + ...).
|
||||||
|
Expected device answer: STATE.
|
||||||
|
'''
|
||||||
|
if len(bytestring) != AD9833_CMD_TOTAL_LENGTH:
|
||||||
|
print("Error. Wrong parameter string for AD9833 command.")
|
||||||
|
return None
|
||||||
|
prt.write(bytestring)
|
||||||
|
print("Sent: AD9833 ramp command.")
|
||||||
|
|
||||||
|
|
||||||
# ---- Getting data
|
# ---- Getting data
|
||||||
|
|
||||||
|
|
||||||
@ -262,6 +280,76 @@ def create_TaskEnableCommand(sending_param):
|
|||||||
|
|
||||||
return bytearray.fromhex(data)
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calc_saw_step_for_freq(freq_hz: float, dac_clk_hz: float, triangle: bool):
|
||||||
|
if freq_hz <= 0 or dac_clk_hz is None or dac_clk_hz <= 0:
|
||||||
|
return AD9102_SAW_STEP_DEFAULT
|
||||||
|
n = 2 if triangle else 1
|
||||||
|
step = int(round(dac_clk_hz / (freq_hz * n * 16384.0)))
|
||||||
|
if step < 1:
|
||||||
|
step = 1
|
||||||
|
if step > 63:
|
||||||
|
step = 63
|
||||||
|
return step
|
||||||
|
|
||||||
|
|
||||||
|
def calc_pat_period_for_duty(saw_step: int, duty: float, pat_period_base: int, triangle: bool):
|
||||||
|
if duty is None or duty <= 0 or duty > 1.0:
|
||||||
|
return AD9102_PAT_PERIOD_DEFAULT
|
||||||
|
n = 2 if triangle else 1
|
||||||
|
base_cycles = 16 if pat_period_base == 0 else pat_period_base
|
||||||
|
ramp_cycles = n * 16384 * max(1, min(63, saw_step))
|
||||||
|
pat_period = int(round(ramp_cycles / (duty * base_cycles)))
|
||||||
|
if pat_period < 1:
|
||||||
|
pat_period = 1
|
||||||
|
if pat_period > 0xFFFF:
|
||||||
|
pat_period = 0xFFFF
|
||||||
|
return pat_period
|
||||||
|
|
||||||
|
|
||||||
|
def create_AD9833_ramp_command(saw_step: int = None,
|
||||||
|
pat_period: int = None,
|
||||||
|
pat_period_base: int = None,
|
||||||
|
enable: bool = True,
|
||||||
|
triangle: bool = True):
|
||||||
|
if saw_step is None:
|
||||||
|
saw_step = AD9102_SAW_STEP_DEFAULT
|
||||||
|
if pat_period is None:
|
||||||
|
pat_period = AD9102_PAT_PERIOD_DEFAULT
|
||||||
|
if pat_period_base is None:
|
||||||
|
pat_period_base = AD9102_PAT_PERIOD_BASE_DEFAULT
|
||||||
|
if saw_step < 1:
|
||||||
|
saw_step = 1
|
||||||
|
if saw_step > 63:
|
||||||
|
saw_step = 63
|
||||||
|
if pat_period < 0:
|
||||||
|
pat_period = 0
|
||||||
|
if pat_period > 0xFFFF:
|
||||||
|
pat_period = 0xFFFF
|
||||||
|
if pat_period_base < 0:
|
||||||
|
pat_period_base = 0
|
||||||
|
if pat_period_base > 0x0F:
|
||||||
|
pat_period_base = 0x0F
|
||||||
|
|
||||||
|
flags = 0
|
||||||
|
if enable:
|
||||||
|
flags |= 0x0001
|
||||||
|
if triangle:
|
||||||
|
flags |= 0x0002
|
||||||
|
|
||||||
|
param0 = ((pat_period_base & 0x0F) << 8) | (saw_step & 0xFF)
|
||||||
|
crc_word = flags ^ param0 ^ pat_period
|
||||||
|
|
||||||
|
data = flipfour(AD9833_CMD_HEADER) # Word 0 (header)
|
||||||
|
data += flipfour(int_to_hex(flags))
|
||||||
|
data += flipfour(int_to_hex(param0))
|
||||||
|
data += flipfour(int_to_hex(pat_period))
|
||||||
|
data += flipfour(int_to_hex(crc_word))
|
||||||
|
|
||||||
|
return bytearray.fromhex(data)
|
||||||
|
|
||||||
|
|
||||||
def encode_Input(params):
|
def encode_Input(params):
|
||||||
|
|
||||||
if params is None:
|
if params is None:
|
||||||
@ -295,24 +383,38 @@ def encode_Input(params):
|
|||||||
|
|
||||||
def decode_STATE(state):
|
def decode_STATE(state):
|
||||||
st = flipfour(state)
|
st = flipfour(state)
|
||||||
if st == '0000':
|
if st is None or len(st) != 4:
|
||||||
|
return "Error: invalid STATE length."
|
||||||
|
|
||||||
|
hi = int(st[0:2], 16)
|
||||||
|
lo = int(st[2:4], 16)
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
if lo & 0x01:
|
||||||
|
errors.append("SD Card reading/writing error (SD_ERR)")
|
||||||
|
if lo & 0x02:
|
||||||
|
errors.append("Command error (UART_ERR)")
|
||||||
|
if lo & 0x04:
|
||||||
|
errors.append("Wrong parameter value error (UART_DECODE_ERR)")
|
||||||
|
if lo & 0x08:
|
||||||
|
errors.append("Laser 1: TEC driver overheat (TEC1_ERR)")
|
||||||
|
if lo & 0x10:
|
||||||
|
errors.append("Laser 2: TEC driver overheat (TEC2_ERR)")
|
||||||
|
if lo & 0x20:
|
||||||
|
errors.append("Resetting system error (DEFAULT_ERR)")
|
||||||
|
if lo & 0x40:
|
||||||
|
errors.append("File deletion error (REMOVE_ERR)")
|
||||||
|
if lo & 0x80:
|
||||||
|
errors.append("AD9102 status check failed (AD9102_ERR)")
|
||||||
|
|
||||||
|
if not errors:
|
||||||
status = "All ok."
|
status = "All ok."
|
||||||
elif st == '0001':
|
|
||||||
status = "SD Card reading/writing error (SD_ERR)."
|
|
||||||
elif st == '0002':
|
|
||||||
status = "Command error (UART_ERR)."
|
|
||||||
elif st == '0004':
|
|
||||||
status = "Wrong parameter value error (UART_DECODE_ERR)."
|
|
||||||
elif st == '0008':
|
|
||||||
status = "Laser 1: TEC driver overheat (TEC1_ERR)."
|
|
||||||
elif st == '0010':
|
|
||||||
status = "Laser 2: TEC driver overheat (TEC2_ERR)."
|
|
||||||
elif st == '0020':
|
|
||||||
status = "Resetting system error (DEFAULT_ERR)."
|
|
||||||
elif st == '0040':
|
|
||||||
status = "File deletion error (REMOVE_ERR)."
|
|
||||||
else:
|
else:
|
||||||
status = "Unknown or reserved error."
|
status = "; ".join(errors)
|
||||||
|
|
||||||
|
if hi != 0:
|
||||||
|
status += f" | AD9102_PAT_STATUS=0x{hi:02X}"
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
|
||||||
@ -342,6 +444,3 @@ def decode_DATA(dh):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -110,6 +110,33 @@ def send_task_command(prt, sending_param):
|
|||||||
else:
|
else:
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
# Start AD9102 sawtooth with configurable frequency/duty (via SAW_STEP and PAT_PERIOD)
|
||||||
|
if pat_period_base is None:
|
||||||
|
pat_period_base = cmd.AD9102_PAT_PERIOD_BASE_DEFAULT
|
||||||
|
if saw_step is None and freq_hz is not None:
|
||||||
|
if dac_clk_hz is None:
|
||||||
|
dac_clk_hz = cmd.AD9102_DAC_CLK_HZ
|
||||||
|
saw_step = cmd.calc_saw_step_for_freq(freq_hz, dac_clk_hz, triangle)
|
||||||
|
if saw_step is None:
|
||||||
|
saw_step = cmd.AD9102_SAW_STEP_DEFAULT
|
||||||
|
if pat_period is None and duty is not 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_AD9833_ramp_command(saw_step, pat_period, pat_period_base, enable=True, 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("")
|
||||||
|
|
||||||
|
|
||||||
def request_data(prt):
|
def request_data(prt):
|
||||||
# Request data
|
# Request data
|
||||||
cmd.send_TRANS_ENABLE(prt)
|
cmd.send_TRANS_ENABLE(prt)
|
||||||
|
|||||||
35
gui.py
35
gui.py
@ -38,6 +38,16 @@ SET_TEXT_WIDTH_NEW = 40
|
|||||||
SET_START_BUTTON_TEXT = 'Пуск'
|
SET_START_BUTTON_TEXT = 'Пуск'
|
||||||
SET_STOP_BUTTON_TEXT = 'Стоп'
|
SET_STOP_BUTTON_TEXT = 'Стоп'
|
||||||
|
|
||||||
|
SET_RAMP_BUTTON_TEXT = 'Пила'
|
||||||
|
SET_RAMP_SECTION_TEXT = 'Настройка пилы (AD9102)'
|
||||||
|
SET_RAMP_FREQ_TEXT = 'Частота (Гц):'
|
||||||
|
SET_RAMP_DUTY_TEXT = 'Скважность (%):'
|
||||||
|
SET_RAMP_SAWSTEP_TEXT = 'SAW_STEP (1-63):'
|
||||||
|
SET_RAMP_PATPERIOD_TEXT = 'PAT_PERIOD (1-65535):'
|
||||||
|
SET_RAMP_PATBASE_TEXT = 'PAT_PERIOD_BASE (0-15):'
|
||||||
|
SET_RAMP_DACCLK_TEXT = 'DAC clk (МГц):'
|
||||||
|
SET_RAMP_TRI_TEXT = 'Треугольник'
|
||||||
|
|
||||||
|
|
||||||
GRAPH_POINTS_NUMBER = 100 # Number of most recent data points shown on charts
|
GRAPH_POINTS_NUMBER = 100 # Number of most recent data points shown on charts
|
||||||
GRAPH_CANVAS_SIZE = (0, 0)
|
GRAPH_CANVAS_SIZE = (0, 0)
|
||||||
@ -195,9 +205,32 @@ def setup_gui(params):
|
|||||||
[sg.Text(SET_TAU_T_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
[sg.Text(SET_TAU_T_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
sg.Input(params['Tau'], size=(SET_INPUT_WIDTH,1), key='-InputTau-', disabled=True, disabled_readonly_background_color="Gray")],
|
sg.Input(params['Tau'], size=(SET_INPUT_WIDTH,1), key='-InputTau-', disabled=True, disabled_readonly_background_color="Gray")],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_SECTION_TEXT, size=(SET_TEXT_WIDTH_NEW,1))],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_FREQ_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampFreq', ''), size=(SET_INPUT_WIDTH,1), key='-RampFreq-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_DUTY_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampDuty', ''), size=(SET_INPUT_WIDTH,1), key='-RampDuty-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_SAWSTEP_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampSawStep', ''), size=(SET_INPUT_WIDTH,1), key='-RampSawStep-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_PATPERIOD_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampPatPeriod', ''), size=(SET_INPUT_WIDTH,1), key='-RampPatPeriod-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_PATBASE_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampPatBase', ''), size=(SET_INPUT_WIDTH,1), key='-RampPatBase-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_DACCLK_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Input(params.get('RampDacClk', ''), size=(SET_INPUT_WIDTH,1), key='-RampDacClk-')],
|
||||||
|
|
||||||
|
[sg.Text(SET_RAMP_TRI_TEXT, size=(SET_TEXT_WIDTH_NEW,1)),
|
||||||
|
sg.Checkbox('', default=bool(params.get('RampTriangle', True)), key='-RampTriangle-')],
|
||||||
|
|
||||||
[sg.HSeparator(pad=H_SEPARATOR_PAD)],
|
[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_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"))]]
|
||||||
|
|
||||||
|
|
||||||
layout = [[sg.Column(layout_input_col1, pad=(0,0)), sg.VSeparator(pad=(4,0)), sg.Column(layout_input_col2, pad=(0,0)), sg.VSeparator(pad=(4,0)), sg.Column(layout_input_col3, pad=(0,0))],
|
layout = [[sg.Column(layout_input_col1, pad=(0,0)), sg.VSeparator(pad=(4,0)), sg.Column(layout_input_col2, pad=(0,0)), sg.VSeparator(pad=(4,0)), sg.Column(layout_input_col3, pad=(0,0))],
|
||||||
|
|||||||
4
run
4
run
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
#reset generator PCB
|
#reset generator PCB
|
||||||
pinctrl set 26 op dl # drive PCB NRST LOW -> reset stm32
|
#pinctrl set 26 op dl # drive PCB NRST LOW -> reset stm32
|
||||||
pinctrl set 26 op dh # turn stm32 back ON
|
#pinctrl set 26 op dh # turn stm32 back ON
|
||||||
|
|
||||||
source .venv/bin/activate
|
source .venv/bin/activate
|
||||||
python3 _device_main.py
|
python3 _device_main.py
|
||||||
|
|||||||
Reference in New Issue
Block a user