From fd1095c50ac1fdb887df9c5fbb1eb3549a3612fd Mon Sep 17 00:00:00 2001 From: Ayzen Date: Tue, 3 Feb 2026 18:57:30 +0300 Subject: [PATCH] ad9102 prestored saw done --- __pycache__/device_commands.cpython-312.pyc | Bin 16929 -> 20462 bytes __pycache__/device_conversion.cpython-312.pyc | Bin 3849 -> 3828 bytes .../device_interaction.cpython-312.pyc | Bin 6717 -> 8090 bytes __pycache__/gui.cpython-312.pyc | Bin 16013 -> 18512 bytes _device_main.py | 50 ++++++- device_commands.py | 137 +++++++++++++++--- device_interaction.py | 27 ++++ gui.py | 35 ++++- run | 4 +- 9 files changed, 230 insertions(+), 23 deletions(-) diff --git a/__pycache__/device_commands.cpython-312.pyc b/__pycache__/device_commands.cpython-312.pyc index 32f04092652726e254056899b0c24dd2ce433830..3f7dcecc1aabe03a467ab6e4541ec4a9bd3a96fd 100644 GIT binary patch delta 6423 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 fU9n(J2Q2%D zmmh7c=hrO1+ksq2(xt_mrb{W7(n&gpX%7Ys2do1+0vh-g>l4Jg{<*b}ka}KS5+N=8 zV9AqV3}J%R2}i64hlc=kTTX+RP8Ynbif~vjSa@^EHkev#?;8@)OSRr?-T%a~NpKeOjZ8J}_t?L?oFm6^%-8^!+xSNg^mO0xeBW-IzXw1s(b7wbx~zpPtVpuGXpyo^%UPfW zX@WfQUpgDCL(uKzpE^4W5jB_oo$TP{u7NHLK({Ct(%KEe2BGtCAPzOFWaO;MG!_Tb zZmziwfQOr|TQK_>_e>aPvnWCg;Q+wH^PQTN7pg?B5^fH}W0MEsTaJ!`{UkW@@V`{HgR>KLukyF5z8%AaGvNju zVmR>7tx&kj+9OV@3r-IX&yFRwygm+t)BGo%h%NjFOE2^Ccd9Q9Vex$>Ge%5_>EVf) znXTE!aUlG2B>=0YO{dA~03R6Ie`}tuN}&1cXx~~Kf5}s4Jqb)6-npx=ip#YZy1qIT z2I)pRr{m+3b8-3L2&6L!^fUZ9Z^RL~ZRy*v*qL|zM4g#z|Njo0V$Cy&;Y6HKnEE12 z#ma>Jy8eOh3L!2&{h03QcYD%scG)Ce$t!T30XA%0Z^H2O2VfP+w zJVD4je>V^>#q6`w2n+mvprI{|%`*Ux__3p(1H*}7rh@4PA8I-)bSOo5Z*vX*sOjC~ zWnc(djs3d~o!}g|RRn%HR@_+C6lSkAuvO-{m zT(fHax6a^k^sc*8YUZ4rkYgyr6RZXYdeGpAUq~rgEv;r)Et0rkHj6L^5Hjm-tObxz zIh$n3B_`^&SeK`}0@feExKGw!=$eusN8a5H7GDES7t4PcTi-+YKEf+}d|%s&_)_i% zFA?G=R%FEf8V-E};dKO|$hZhYNWY0KJP$%v-6mqniJ({x4MOxK_;NY?5Gahs38K3S z`5VbNV286CV0Em2vF{e~<9U|C!)oh?ClCJb{nW%iAM&qIau(jlEYy72e91gxxy~gh+?(IdW6}1k{KFQ7*koWV|I8NRBU=WWDMxfck^eXT@Ir+kni9( zquVP)uQ#0U7)!!6AsFEwMt$Xx2h$?qd>gNbZ8N6zhx5&RZ!Eado%hXV&Suo}nIOE1 zDKPh(Qh0MWoH|&eQ0La12D?i)!(k7{b5dK*eW}!`VwAejpI^YF4){E|UqNHl^qHgb zyKUcUT8(a$IIhlJxwKK~yK38PF}bQ%SH?G+O-|RX*B-`0dgLZW1RazgH#>tmOTy`6Kt>=N{L8^)b;5%y%!xN9!V^3Y>%AzVaw z8zAIhcn^m>sOQ;hTpCq-K0q5vj!P*7-#>sWH4!iAxbXjI9X-yi~Bq5so9A zM8G3f9F`1EI&n_1s8G{2?4wEw)nQoB48s+-Rfh5-*7Jq^YfCC!UQ`CzZQxLmayL!< vxrxX4?TK`k&qX|U1El~h7UH|>@{q>6-C@#tml7Y~U=69d*X<=0!r}h_gX$dt diff --git a/__pycache__/device_conversion.cpython-312.pyc b/__pycache__/device_conversion.cpython-312.pyc index 0780389f816b29ea1c6ef935d38646073bddcf88..baa8aa647fd0921af07944c07adb7ec294b25fc9 100644 GIT binary patch delta 78 zcmeB_`y$J8nwOW00SKDzHe_z(NnkW~*U!k$P1R2=Ey^!Q)OSfO&MwI>&<{#X$;=PP e$S=vy%S?_BaCQRX_~QJulJdl&)XiHMLwN!HK^sf} delta 99 zcmew&+bPF$nwOW00SJuV|H#lqNDAC#DqnIDjmUy`4f znH(QnnpB)xl$MyB8ef!{l31kgo|>0hlvt8q6d&O1q#saOl98Vm?;h&8c|Kz(F94Hh BBG>=` diff --git a/__pycache__/device_interaction.cpython-312.pyc b/__pycache__/device_interaction.cpython-312.pyc index b45b308766f13c73ef1f08778d2639853ca5ed8d..708036d896ae9b051348cfb7139a4709c6b58e91 100644 GIT binary patch delta 1583 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 diff --git a/_device_main.py b/_device_main.py index a1e5f47..05dc422 100644 --- a/_device_main.py +++ b/_device_main.py @@ -44,6 +44,31 @@ def get_float(values, strId): window['-StartCycle-'].update(disabled = True) 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): return "{:.2f}".format(round(i, 2)) @@ -73,6 +98,13 @@ def set_initial_params(): params['Delta_Current_2'] = 0.05 params['Delta_Time'] = 50 params['Tau'] = 10 + params['RampFreq'] = '' + params['RampDuty'] = '' + params['RampSawStep'] = '' + params['RampPatPeriod'] = '' + params['RampPatBase'] = 2 + params['RampDacClk'] = '' + params['RampTriangle'] = True return params def update_data_lists(): @@ -423,6 +455,23 @@ if __name__ == "__main__": params['Iset_2'] = float(values['-InputI2-']) dev.send_control_parameters(prt, params) #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-': window['-StopCycle-'].update(disabled = True) current_and_temperature_settings_available = True @@ -466,4 +515,3 @@ if __name__ == "__main__": dev.close_connection(prt) - diff --git a/device_commands.py b/device_commands.py index 741743a..ef71d54 100644 --- a/device_commands.py +++ b/device_commands.py @@ -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 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): Manual = 0x00 ChangeCurrentLD1 = 0x01 @@ -148,6 +155,17 @@ def send_STATE(prt): 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 @@ -262,6 +280,76 @@ def create_TaskEnableCommand(sending_param): 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): if params is None: @@ -295,24 +383,38 @@ def encode_Input(params): def decode_STATE(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." - 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: - status = "Unknown or reserved error." + status = "; ".join(errors) + + if hi != 0: + status += f" | AD9102_PAT_STATUS=0x{hi:02X}" + return status @@ -342,6 +444,3 @@ def decode_DATA(dh): return data - - - diff --git a/device_interaction.py b/device_interaction.py index 6e2cb78..80f603b 100644 --- a/device_interaction.py +++ b/device_interaction.py @@ -110,6 +110,33 @@ def send_task_command(prt, sending_param): else: 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): # Request data cmd.send_TRANS_ENABLE(prt) diff --git a/gui.py b/gui.py index 36c469b..c0e1056 100644 --- a/gui.py +++ b/gui.py @@ -38,6 +38,16 @@ SET_TEXT_WIDTH_NEW = 40 SET_START_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_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.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.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))], diff --git a/run b/run index 3d0615b..08a5e29 100755 --- a/run +++ b/run @@ -1,7 +1,7 @@ #!/usr/bin/bash #reset generator PCB -pinctrl set 26 op dl # drive PCB NRST LOW -> reset stm32 -pinctrl set 26 op dh # turn stm32 back ON +#pinctrl set 26 op dl # drive PCB NRST LOW -> reset stm32 +#pinctrl set 26 op dh # turn stm32 back ON source .venv/bin/activate python3 _device_main.py