]> jspc29.x-matter.uni-frankfurt.de Git - avr.git/commitdiff
add automatic save on reset master
authorJan Michel <michel@physik.uni-frankfurt.de>
Mon, 19 Oct 2026 07:31:10 +0000 (09:31 +0200)
committerJan Michel <michel@physik.uni-frankfurt.de>
Mon, 19 Oct 2026 07:31:10 +0000 (09:31 +0200)
atmega32u4/stepmotorcontroller/Keyboard.odt
atmega32u4/stepmotorcontroller/gui.py
atmega32u4/stepmotorcontroller/main.c

index f9c6d5c33d2b3a43c0326edf88053a296954d565..1fe07878f1f8b54a594d79cda04a2e6a64a8e87e 100644 (file)
Binary files a/atmega32u4/stepmotorcontroller/Keyboard.odt and b/atmega32u4/stepmotorcontroller/Keyboard.odt differ
index b13a8027c7e2ced1bb09e4e33e2b26f94d23da78..6f130cb4d8a6220d0c22e54e77c3df45194565d0 100755 (executable)
@@ -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 ["<Return>","<KP_Enter>"]: entry1.bind(b,   lambda event, sn=names[row]+"X": self.valuechanged(sn))
+            for b in ["<Shift-Return>","<Shift-KP_Enter>"]: entry1.bind(b,   lambda event, sn=names[row]+"X": self.valuechanged(sn,1))
+            for b in ["<Control-a>","<Double-Button-1>"]:  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 ["<Return>","<KP_Enter>"]: entry2.bind(b,   lambda event, sn=names[row]+"Y": self.valuechanged(sn))
+              for b in ["<Shift-Return>","<Shift-KP_Enter>"]: entry2.bind(b,   lambda event, sn=names[row]+"Y": self.valuechanged(sn,1))
+              for b in ["<Control-a>","<Double-Button-1>"]:  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("<Button-1>", 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("<Control-g>",   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()
index 78543cd004d4a4767ee37062df387bc058729421..cfd627218524ce2c22edaa906635428c641579bd 100644 (file)
@@ -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';