From: Jan Michel Date: Mon, 19 Oct 2026 07:31:10 +0000 (+0200) Subject: add automatic save on reset X-Git-Url: https://jspc29.x-matter.uni-frankfurt.de/git/?a=commitdiff_plain;h=HEAD;p=avr.git add automatic save on reset --- diff --git a/atmega32u4/stepmotorcontroller/Keyboard.odt b/atmega32u4/stepmotorcontroller/Keyboard.odt index f9c6d5c..1fe0787 100644 Binary files a/atmega32u4/stepmotorcontroller/Keyboard.odt and b/atmega32u4/stepmotorcontroller/Keyboard.odt differ diff --git a/atmega32u4/stepmotorcontroller/gui.py b/atmega32u4/stepmotorcontroller/gui.py index b13a802..6f130cb 100755 --- a/atmega32u4/stepmotorcontroller/gui.py +++ b/atmega32u4/stepmotorcontroller/gui.py @@ -26,17 +26,29 @@ def twos_complement(hexstr, bits): value -= 1 << bits return value +def is_integer(n): + try: + float(n) + except ValueError: + return False + else: + return float(n).is_integer() + +def select_all(event): + event.widget.select_range(0, tk.END) + event.widget.icursor(tk.END) + return "break" class SerialGUI: def __init__(self, root): self.root = root self.root.title("Step Motor Controller") - self.root.columnconfigure(1, weight=1) - self.root.columnconfigure(2, weight=1) - + # self.root.columnconfigure(1, weight=1) + # self.root.columnconfigure(2, weight=1) + self.root.columnconfigure(3, weight=1) labels = [ - "Position [steps]", "Position [um]", "Goto [steps]", "Goto [um]", - "Move by [steps]", "Move by [um]", "Stepsize", "Speed"] + "Position [steps]", "Position [µm]", "Goto [steps]", "Goto [µm]", + "Move by [steps]", "Move by [µm]", "Stepsize", "Speed"] names = ["PS","PU","GS","GU","MS","MU","SS","SP"] self.entries = {} @@ -44,11 +56,17 @@ class SerialGUI: label = ttk.Label(root, text=label_text) label.grid(row=row, column=0, padx=8, pady=5, sticky="w") - entry1 = ttk.Entry(root) + entry1 = ttk.Entry(root, width=20) + for b in ["",""]: entry1.bind(b, lambda event, sn=names[row]+"X": self.valuechanged(sn)) + for b in ["",""]: entry1.bind(b, lambda event, sn=names[row]+"X": self.valuechanged(sn,1)) + for b in ["",""]: entry1.bind(b,select_all) entry1.grid(row=row, column=1, padx=5, pady=5, sticky="ew") if row < 6: - entry2 = ttk.Entry(root) + entry2 = ttk.Entry(root, width=20) + for b in ["",""]: entry2.bind(b, lambda event, sn=names[row]+"Y": self.valuechanged(sn)) + for b in ["",""]: entry2.bind(b, lambda event, sn=names[row]+"Y": self.valuechanged(sn,1)) + for b in ["",""]: entry2.bind(b,select_all) entry2.grid(row=row, column=2, padx=5, pady=5, sticky="ew") if row < 6: @@ -61,7 +79,7 @@ class SerialGUI: colname = ["Active","X+","X-","Y+","Y-"]; color_frame = tk.Frame(root) color_frame.grid( - row=8, column=0, columnspan=3, + row=8, column=0, columnspan=4, padx=8, pady=12, sticky="ew" ) @@ -79,39 +97,55 @@ class SerialGUI: fill="black", # Text color font=("Arial", 10, "bold"), ) + if col==0: + rectangle.bind("", lambda event: self.activate()) rectangle.grid(row=0, column=col, padx=5) self.entries[colname[col]] = rectangle + ttk.Button( + color_frame, + text="Clear", + style="Tiny.TButton", + command=lambda: self.clearcoordinates() + ).grid(row=0, column=5) + ttk.Button( root, text="Store", style="Tiny.TButton", command=lambda: self.storeposition() ).grid(row=9, column=0) + ttk.Button( + root, + text="Goto", + style="Tiny.TButton", + command=lambda: self.gotoline() + ).grid(row=10, column=0) ttk.Button( root, text="Save", style="Tiny.TButton", command=lambda: self.savelog() - - ).grid(row=10, column=0) + ).grid(row=11, column=0) text_box = tk.Text( root, height=10, - width=50, + width=80, wrap="word", # Wrap long lines - font=("Courier", 11) + font=("Courier", 11), ) - text_box.grid(row=9, column=1, columnspan=2, rowspan=2) + text_box.bind("", lambda event: self.gotoline()) + text_box.grid(row=9, column=1, columnspan=3, rowspan=3) self.entries["store"] = text_box + self.entries["store"].insert("end","X (step) Y (step) X (µm) Y (µm) Notes\n") - label = ttk.Label(root, text="Raw Input") - label.grid(row=11, column=0, padx=8, pady=5, sticky="w") - entry1 = ttk.Entry(root) - entry1.grid(row=11, column=1, columnspan=2, padx=5, pady=5, sticky="ew") + # label = ttk.Label(root, text="Raw Input") + # label.grid(row=11, column=0, padx=8, pady=5, sticky="w") + # entry1 = ttk.Entry(root) + # entry1.grid(row=11, column=1, columnspan=2, padx=5, pady=5, sticky="ew") - self.entries["raw"] = entry1 + # self.entries["raw"] = entry1 # --- Serial Setup --- try: self.ser = serial.Serial(PORT, BAUD_RATE, timeout=1) @@ -119,16 +153,103 @@ class SerialGUI: self.display_var.set(f"Error: Could not open {PORT}\n{e}") return - # Start a background thread to read serial data without freezing the GUI self.thread = threading.Thread(target=self.read_serial, daemon=True) self.thread.start() - def storeposition(self): - xs = self.entries["PSX"].get() - xu = self.entries["PUX"].get() - ys = self.entries["PSY"].get() - yu = self.entries["PUY"].get() - self.entries["store"].insert("end",f"{xs} {ys}\t{xu} {yu}\n") + def valuechanged(self, field, invert=0): + if field == "SSX": + value = self.entries["SS"].get() + if is_integer(value): + value = int(value) & 0x7fffffff + self.send_data("W03%08x\n" % value) + if field == "SPX": + value = self.entries["SP"].get() + if is_integer(value): + value = int(value) & 0x7 + self.send_data("W04%08x\n" % value) + if field == "GSX": + value = self.entries["GSX"].get() + if is_integer(value): + value = int(value) & 0xffffffff + self.send_data("W05%08x\n" % value) + if field == "GSY": + value = self.entries["GSY"].get() + if is_integer(value): + value = int(value) & 0xffffffff + self.send_data("W06%08x\n" % value) + if field == "GUX": + value = self.entries["GUX"].get() + if is_integer(value): + value = round((float(value))/STEP_SIZE/CORRECTION_X) & 0xffffffff + self.send_data("W05%08x\n" % value) + if field == "GUY": + value = self.entries["GUY"].get() + if is_integer(value): + value = round((float(value))/STEP_SIZE/CORRECTION_Y) & 0xffffffff + self.send_data("W06%08x\n" % value) + if field == "MSX": + value = self.entries["MSX"].get() + if is_integer(value): + value = int(value) + if invert: value = -value + value = value & 0xffffffff + self.send_data("W07%08x\n" % value) + if field == "MSY": + value = self.entries["MSY"].get() + if is_integer(value): + value = int(value) + if invert: value = -value + value = value & 0xffffffff + self.send_data("W08%08x\n" % value) + if field == "MUX": + value = self.entries["MUX"].get() + if is_integer(value): + value = float(value) + if invert: value = -value + value = round(value/STEP_SIZE/CORRECTION_X) & 0xffffffff + self.send_data("W07%08x\n" % value) + if field == "MUY": + value = self.entries["MUY"].get() + if is_integer(value): + value = float(value) + if invert: value = -value + value = round(value/STEP_SIZE/CORRECTION_Y) & 0xffffffff + self.send_data("W08%08x\n" % value) + + def clearcoordinates(self): + xs = int(self.entries["PSX"].get()) + ys = int(self.entries["PSY"].get()) + + self.send_data("W0A00000000\n") + self.entries["store"].see(tk.END) + + + def activate(self): + if self.entries["Active"].cget("bg") == "green": + self.send_data("W0900000000\n") + else: + self.send_data("W0900000001\n") + + def gotoline(self): + line = self.entries["store"].get("insert linestart", "insert lineend") + m = re.search(r"^\s*([\-0-9]+)\s*([\-0-9]+)",line) + if m: + value = int(m.group(1)) & 0xffffffff + self.send_data("W05%08x\n" % value) + value = int(m.group(2)) & 0xffffffff + self.send_data("W06%08x\n" % value) + + def storeposition(self, text=""): + xs = int(self.entries["PSX"].get()) + xu = float(self.entries["PUX"].get()) + ys = int(self.entries["PSY"].get()) + yu = float(self.entries["PUY"].get()) + s = "" + if text != "": + s = " # " + text + # self.entries["store"].insert("end",f"{xs} {ys}\t{xu} {yu}\n") + self.entries["store"].insert("end","%8i %8i %11.2f %11.2f %s\n" %(xs,ys,xu,yu,s)) + self.entries["store"].see(tk.END) def savelog(self): filename = filedialog.asksaveasfilename( @@ -161,8 +282,8 @@ class SerialGUI: break if inputline is not None: # print(inputline) - self.setbox("raw",inputline) - m = re.search(r"P([0-9a-fA-F]+),([0-9a-fA-F]+)",inputline) + # self.setbox("raw",inputline) + m = re.search(r"P([0-9a-fA-F]+),([0-9a-fA-F]+)([SR]?)",inputline) if m: xs = twos_complement(m.group(1),32) ys = twos_complement(m.group(2),32) @@ -170,6 +291,10 @@ class SerialGUI: self.setbox("PSY",ys) self.setbox("PUX",xs * STEP_SIZE * CORRECTION_X, 2) self.setbox("PUY",ys * STEP_SIZE * CORRECTION_Y, 2) + if m.group(3) == 'S': + self.storeposition() + if m.group(3) == 'R': + self.storeposition("# reset") m = re.search(r"S([01])([01])([01])([01])([01])",inputline) if m: if m.group(1) == '1': @@ -210,15 +335,15 @@ class SerialGUI: except Exception: pass - # def send_data(self): - # data = self.entry.get() - # if data: - # try: - # # Add newline character and send as bytes - # self.ser.write((data + '\n').encode('utf-8')) - # self.entry.delete(0, tk.END) # Clear input field - # except Exception as e: - # print(f"Send Error: {e}") + def send_data(self, data): + if data: + try: + # Add newline character and send as bytes + self.ser.write((data + '\n').encode('utf-8')) + except Exception as e: + print(f"Send Error: {e}") + + def close_window(): stop_event.set() root.destroy() diff --git a/atmega32u4/stepmotorcontroller/main.c b/atmega32u4/stepmotorcontroller/main.c index 78543cd..cfd6272 100644 --- a/atmega32u4/stepmotorcontroller/main.c +++ b/atmega32u4/stepmotorcontroller/main.c @@ -75,7 +75,7 @@ uint16_t time; uint8_t rxcnt = 0, txpoint = 0; uint8_t rxbuf[15]; uint8_t txbuf[15]; -uint8_t buffer[24]; +uint8_t buffer[26]; color_t colors[16]; color_t topback; volatile uint8_t keypad[16] = { 0 }; @@ -358,6 +358,85 @@ void MoveAbsoluteY(int32_t pos) { } sei(); } + +void MoveRelativeX(int32_t pos) { + cli(); + if (motor_active) { + if (pos > 0) { + DIRECTION_FORWARD_M1(); + M1_DIR = 1; + M1_STEPS = pos; + } + else{ + DIRECTION_BACKWARD_M1(); + M1_DIR = 0; + M1_STEPS = -pos; + } + } + sei(); +} + +void MoveRelativeY(int32_t pos) { + cli(); + if (motor_active) { + if (pos > 0) { + DIRECTION_FORWARD_M2(); + M2_DIR = 1; + M2_STEPS = pos; + } + else{ + DIRECTION_BACKWARD_M2(); + M2_DIR = 0; + M2_STEPS = -pos; + } + } + sei(); +} + +void activate(uint8_t state) { + if (state == 0) { + motor_active = 0; + DISABLE_M1(); + DISABLE_M2(); + DISABLE_POWER(); + M1_STEPS = 0; + M2_STEPS = 0; + lcd_use_foreground(colors[5]); + lcd_putstr_xy_P(PSTR("off"), 16, 280); + lcd_use_foreground(colors[0]); + } else { + motor_active = 1; + ENABLE_M1(); + ENABLE_M2(); + ENABLE_POWER(); + lcd_use_foreground(colors[3]); + lcd_putstr_xy_P(PSTR("ON "), 16, 280); + lcd_use_foreground(colors[0]); + } +} + +void sendPosition(uint8_t c) { + strncpy((char*)buffer, " ", 24); + buffer[0]='P'; + ltoa(M1_position, (char*)buffer+1, 16); + buffer[11]=','; + ltoa(M2_position, (char*)buffer+12, 16); + buffer[22]=c; + buffer[23]='\n'; + buffer[24] = 0; + send_string(buffer,25); +} + + +void clearcoordinates(void) { + cli(); + M1_position = 0; + M2_position = 0; + sendPosition('R'); + sei(); +} + + /***************************************************************** * Communication *****************************************************************/ @@ -418,7 +497,25 @@ void getdata(uint8_t buf) MoveAbsoluteY(pos); send_answer_hex(&rxbuf[0], pos); } - + if (regnumber == 7) { // Register 7: Move Relative X W0700000064 + int32_t pos = hex_to_int32(&rxbuf[3]); + MoveRelativeX(pos); + send_answer_hex(&rxbuf[0], pos); + } + if (regnumber == 8) { // Register 8: Move Relative Y W08FFFFFF95 + int32_t pos = hex_to_int32(&rxbuf[3]); + MoveRelativeY(pos); + send_answer_hex(&rxbuf[0], pos); + } + if (regnumber == 9) { // Register 9: Activate W0900000001 W0900000000 + uint8_t t = hex_to_int(rxbuf[10]) & 1; + activate(t); + send_answer_hex(&rxbuf[0], t); + } + if (regnumber == 0xa) { // Register 10: Clear W0A00000000 + clearcoordinates(); + send_answer_hex(&rxbuf[0], 1); + } } } } @@ -573,25 +670,10 @@ __attribute__((naked)) void main(void) lcd_set_font(FONT_PROP_16, SPACING); if (keypad[12] && keypad[12] != 255) { // A - if (motor_active) { - motor_active = 0; - DISABLE_M1(); - DISABLE_M2(); - DISABLE_POWER(); - M1_STEPS = 0; - M2_STEPS = 0; - lcd_use_foreground(colors[5]); - lcd_putstr_xy_P(PSTR("off"), 16, 280); - lcd_use_foreground(colors[0]); - } else { - motor_active = 1; - ENABLE_M1(); - ENABLE_M2(); - ENABLE_POWER(); - lcd_use_foreground(colors[3]); - lcd_putstr_xy_P(PSTR("ON "), 16, 280); - lcd_use_foreground(colors[0]); - } + if (motor_active) + activate(0); + else + activate(1); keypad[12] = 255; } if (keypad[13] && keypad[13] != 255) { // B @@ -611,10 +693,7 @@ __attribute__((naked)) void main(void) // Cursor Keys if (!numlock) { if (keypad[14] && keypad[14] != 255) { // C - cli(); - M1_position = 0; - M2_position = 0; - sei(); + clearcoordinates(); keypad[14] = 255; } if (keypad[15] && keypad[15] != 255) { // D @@ -622,7 +701,7 @@ __attribute__((naked)) void main(void) M1_STEPS = 0; M2_STEPS = 0; sei(); - keypad[14] = 255; + keypad[15] = 255; } if (keypad[4]) { // 2 @@ -640,7 +719,7 @@ __attribute__((naked)) void main(void) if (!(keystate & (1 << 4))) keypad[4] = 255; } - if (keypad[5]) { // 5, toggle single step mode + if (keypad[5] && keypad[5] != 255) { // 5, toggle single step mode cli(); if (stepsize != 1) { stored_stepsize = stepsize; @@ -653,6 +732,12 @@ __attribute__((naked)) void main(void) keypad[5] = 255; } + if (keypad[7] && keypad[7] != 255) { // 0, send position + cli(); + sendPosition('S'); + sei(); + keypad[7] = 255; + } if (keypad[6]) { // 8 if (keypad[6] < 20 || keypad[6] > 40) { cli(); @@ -830,14 +915,7 @@ __attribute__((naked)) void main(void) // } //output position to USB // if ((time & 0x7) == 0) { - strncpy((char*)buffer, " ", 24); - buffer[0]='P'; - ltoa(M1_position, (char*)buffer+1, 16); - buffer[11]=','; - ltoa(M2_position, (char*)buffer+12, 16); - buffer[22]='\n'; - buffer[23] = 0; - send_string(buffer,24); + sendPosition(' '); strncpy((char*)buffer, " ", 24); buffer[0]='S';