From: Jan Michel Date: Fri, 16 Oct 2026 13:34:07 +0000 (+0200) Subject: add USB to step motor controller, GUI can show status X-Git-Url: https://jspc29.x-matter.uni-frankfurt.de/git/?a=commitdiff_plain;h=e7aa1f30ffca8a38211a0cb1e6e404c1473f6d6c;p=avr.git add USB to step motor controller, GUI can show status --- diff --git a/atmega32u4/stepmotorcontroller/gui.py b/atmega32u4/stepmotorcontroller/gui.py new file mode 100755 index 0000000..b13a802 --- /dev/null +++ b/atmega32u4/stepmotorcontroller/gui.py @@ -0,0 +1,232 @@ +#!/usr/bin/python3 + +import tkinter as tk +from tkinter import ttk +import serial +import threading +from pprint import pprint +import queue +import re +from tkinter import filedialog + + +# Configuration +PORT = '/dev/Xmatter_SMC' +BAUD_RATE = 9600 +input_queue = queue.Queue() +stop_event = threading.Event() + +STEP_SIZE = 0.3125 +CORRECTION_X = 1.001 +CORRECTION_Y = 1.001 + +def twos_complement(hexstr, bits): + value = int(hexstr, 16) + if value & (1 << (bits - 1)): + value -= 1 << bits + return value + + +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) + + labels = [ + "Position [steps]", "Position [um]", "Goto [steps]", "Goto [um]", + "Move by [steps]", "Move by [um]", "Stepsize", "Speed"] + names = ["PS","PU","GS","GU","MS","MU","SS","SP"] + self.entries = {} + + for row, label_text in enumerate(labels): + label = ttk.Label(root, text=label_text) + label.grid(row=row, column=0, padx=8, pady=5, sticky="w") + + entry1 = ttk.Entry(root) + entry1.grid(row=row, column=1, padx=5, pady=5, sticky="ew") + + if row < 6: + entry2 = ttk.Entry(root) + entry2.grid(row=row, column=2, padx=5, pady=5, sticky="ew") + + if row < 6: + self.entries[names[row]+"X"] = entry1 + self.entries[names[row]+"Y"] = entry2 + else: + self.entries[names[row]] = entry1 + + colors = ["red", "green", "green", "green", "green"] + colname = ["Active","X+","X-","Y+","Y-"]; + color_frame = tk.Frame(root) + color_frame.grid( + row=8, column=0, columnspan=3, + padx=8, pady=12, sticky="ew" + ) + + for col, color in enumerate(colors): + rectangle = tk.Canvas( + color_frame, + width=80, + height=30, + bg=color, + highlightthickness=0, + ) + rectangle.create_text( + 40, 15, # Center of 80 x 30 rectangle + text=colname[col], + fill="black", # Text color + font=("Arial", 10, "bold"), + ) + rectangle.grid(row=0, column=col, padx=5) + self.entries[colname[col]] = rectangle + + ttk.Button( + root, + text="Store", + style="Tiny.TButton", + command=lambda: self.storeposition() + ).grid(row=9, column=0) + ttk.Button( + root, + text="Save", + style="Tiny.TButton", + command=lambda: self.savelog() + + ).grid(row=10, column=0) + + text_box = tk.Text( + root, + height=10, + width=50, + wrap="word", # Wrap long lines + font=("Courier", 11) + ) + text_box.grid(row=9, column=1, columnspan=2, rowspan=2) + self.entries["store"] = text_box + + 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 + # --- Serial Setup --- + try: + self.ser = serial.Serial(PORT, BAUD_RATE, timeout=1) + except Exception as e: + 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 savelog(self): + filename = filedialog.asksaveasfilename( + title="Save text", + defaultextension=".txt", + filetypes=[("Text files", "*.txt"), ("All files", "*.*")] + ) + if filename: + with open(filename, "w", encoding="utf-8") as file: + file.write(self.entries["store"].get("1.0", "end-1c")) + + + def setbox(self, name, value, digits=4): + # if value is (int, float): + try: + if not value.is_integer(): + value = str(round(float(value),digits)) + except: + pass + # value = value + self.entries[name].delete(0, tk.END) + self.entries[name].insert(0,value) + + def update_display(self): + inputline = None + while True: + try: + inputline = input_queue.get_nowait() + except queue.Empty: + 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) + if m: + xs = twos_complement(m.group(1),32) + ys = twos_complement(m.group(2),32) + self.setbox("PSX",xs) + self.setbox("PSY",ys) + self.setbox("PUX",xs * STEP_SIZE * CORRECTION_X, 2) + self.setbox("PUY",ys * STEP_SIZE * CORRECTION_Y, 2) + m = re.search(r"S([01])([01])([01])([01])([01])",inputline) + if m: + if m.group(1) == '1': + self.entries["X-"].configure(bg="red") + else: + self.entries["X-"].configure(bg="green") + if m.group(2) == '1': + self.entries["X+"].configure(bg="red") + else: + self.entries["X+"].configure(bg="green") + if m.group(3) == '1': + self.entries["Y-"].configure(bg="red") + else: + self.entries["Y-"].configure(bg="green") + if m.group(4) == '1': + self.entries["Y+"].configure(bg="red") + else: + self.entries["Y+"].configure(bg="green") + if m.group(5) == '1': + self.entries["Active"].configure(bg="green") + else: + self.entries["Active"].configure(bg="red") + + + if not stop_event.is_set(): + root.after(50, self.update_display) + + + def read_serial(self): + while not stop_event.is_set(): + while self.ser.in_waiting > 0: + try: + # Read line and decode bytes to string + line = self.ser.readline().decode('utf-8').strip("\r\n").replace("\0","").replace(" ","") + if line: + # Update the UI from the thread safely + input_queue.put(line) + 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 close_window(): + stop_event.set() + root.destroy() + +if __name__ == "__main__": + root = tk.Tk() + root.protocol("WM_DELETE_WINDOW", close_window) + app = SerialGUI(root) + root.after(50, SerialGUI.update_display(app)) + root.mainloop() + diff --git a/atmega32u4/stepmotorcontroller/main.c b/atmega32u4/stepmotorcontroller/main.c index 5aff4af..78543cd 100644 --- a/atmega32u4/stepmotorcontroller/main.c +++ b/atmega32u4/stepmotorcontroller/main.c @@ -3,12 +3,13 @@ // #include #include "lcdlib/font.h" #include "lcdlib/lcd-color-graphic.h" -#include "twi.h" +// #include "twi.h" #include #include #include #include #include +#include void getdata(uint8_t buf); @@ -74,6 +75,7 @@ uint16_t time; uint8_t rxcnt = 0, txpoint = 0; uint8_t rxbuf[15]; uint8_t txbuf[15]; +uint8_t buffer[24]; color_t colors[16]; color_t topback; volatile uint8_t keypad[16] = { 0 }; @@ -130,9 +132,17 @@ uint32_t hex_to_int32(uint8_t* buf) return value; } -void send_answer_buf(uint8_t* b) { UCSR1B |= (1 << UDRIE1); } +// void send_answer_buf(uint8_t* b) { UCSR1B |= (1 << UDRIE1); } // activate transmit interupt "ISR(USART1_UDRE_vect)" + + +void send_string(uint8_t* buf, uint8_t len) +{ + usb_serial_write(buf, len); + usb_serial_flush_output(); +} + // compose response string void send_answer_hex(uint8_t* rxbuf, uint32_t v) { @@ -149,11 +159,13 @@ void send_answer_hex(uint8_t* rxbuf, uint32_t v) txbuf[10] = nib_to_hex(v, 0); txbuf[11] = '\n'; txbuf[12] = 0; - usb_serial_write(txbuf, 12); - usb_serial_flush_output(); + send_string(txbuf,12); + // usb_serial_write(txbuf, 12); + // usb_serial_flush_output(); // send_answer_buf(txbuf); } + // R0100000000 /***************************************************************** * Setting functions @@ -296,6 +308,56 @@ void init_spi_lcd() { } + +void updateSpeed(uint8_t speed) { + cli(); + OCR1A = speeds[speed]; + TCNT1 = 0; + sei(); + lcd_set_font(FONT_PROP_16, SPACING); + lcd_set_area_xy(280, 320, 48, 64); + LCD_MOVE_TO(48, 280); + lcd_putstr_P(PSTR("Sp ")); + lcd_put_short(speed); +} + +void updateStepsize(void) { + lcd_set_font(FONT_DIGITS_32, SPACING); + LCD_MOVE_TO(200, 16); + lcd_set_area_xy(16, 240, 200, 232); + lcd_put_long_align32_7(stepsize); + lcd_set_font(FONT_PROP_16, SPACING); +} + +void MoveAbsoluteX(int32_t pos) { + cli(); + M1_STEPS = 0; + if (M1_position < pos) { + DIRECTION_FORWARD_M1(); + M1_DIR = 1; + M1_STEPS = pos - M1_position; + } else { + DIRECTION_BACKWARD_M1(); + M1_DIR = 0; + M1_STEPS = M1_position - pos; + } + sei(); +} + +void MoveAbsoluteY(int32_t pos) { + cli(); + M2_STEPS = 0; + if (M2_position < pos) { + DIRECTION_FORWARD_M2(); + M2_DIR = 1; + M2_STEPS = pos - M2_position; + } else { + DIRECTION_BACKWARD_M2(); + M2_DIR = 0; + M2_STEPS = M2_position - pos; + } + sei(); +} /***************************************************************** * Communication *****************************************************************/ @@ -313,12 +375,50 @@ void getdata(uint8_t buf) if (regnumber == 1) { // Register 1: PIN send_answer_hex(&rxbuf[0], 0xabcd1234); } + if (regnumber == 2) { // Register 2: Switches (0,0,speed,act,yh,yl,xh,xl) + send_answer_hex(&rxbuf[0], M1_SWITCH_LOW + (M1_SWITCH_HIGH<<4) + (M2_SWITCH_LOW << 8) + (M2_SWITCH_HIGH << 12) + ((uint32_t)motor_active << 16)); + } + if (regnumber == 3) { // Register 3: Step size + send_answer_hex(&rxbuf[0], stepsize); + } + if (regnumber == 4) { // Register 4: Speed + send_answer_hex(&rxbuf[0], speed); + } + if (regnumber == 5) { // Register 5: Position X + send_answer_hex(&rxbuf[0], M1_position); + } + if (regnumber == 6) { // Register 6: Position Y + send_answer_hex(&rxbuf[0], M2_position); + } + } if (rxbuf[0] == 'W') { uint32_t value = hex_to_int32(&rxbuf[3]); if (regnumber == 0) { send_answer_hex(&rxbuf[0], value); } + if (regnumber == 3) { // Register 3: Stepsize W0300000190 + stepsize = hex_to_int32(&rxbuf[3]); + updateStepsize(); + send_answer_hex(&rxbuf[0], stepsize); + } + + if (regnumber == 4) { // Register 4: Speed W0400000007 + speed = hex_to_int(rxbuf[10]) & 0x7; + updateSpeed( speed ); + send_answer_hex(&rxbuf[0], speed); + } + if (regnumber == 5) { // Register 5: Move Absolute X W0500007895 + int32_t pos = hex_to_int32(&rxbuf[3]); + MoveAbsoluteX(pos); + send_answer_hex(&rxbuf[0], pos); + } + if (regnumber == 6) { // Register 6: Move Absolute Y W06FFFF7895 + int32_t pos = hex_to_int32(&rxbuf[3]); + MoveAbsoluteY(pos); + send_answer_hex(&rxbuf[0], pos); + } + } } } @@ -327,7 +427,8 @@ void getdata(uint8_t buf) rxcnt = 0; } } - +// R0200000000 +// R0300000000 // R0000000000 /***************************************************************** * Main function @@ -434,11 +535,7 @@ __attribute__((naked)) void main(void) lcd_use_foreground(colors[0]); lcd_putstr_xy_P(PSTR("<-->"), 32, 280); lcd_putstr_xy_P(PSTR("Sp 4"), 48, 280); - lcd_set_font(FONT_DIGITS_32, SPACING); - LCD_MOVE_TO(200, 46); - lcd_put_long(stepsize); - lcd_set_font(FONT_PROP_16, SPACING); - + updateStepsize(); uint16_t lasttime = 0; for (uint8_t i = 0; i < 16; i++) @@ -545,21 +642,15 @@ __attribute__((naked)) void main(void) } if (keypad[5]) { // 5, toggle single step mode cli(); - if(stepsize != 1) { - stored_stepsize = stepsize; - stepsize = 1; - } - else { - stepsize = stored_stepsize; - } + if (stepsize != 1) { + stored_stepsize = stepsize; + stepsize = 1; + } else { + stepsize = stored_stepsize; + } sei(); + updateStepsize(); - lcd_set_font(FONT_DIGITS_32, SPACING); - LCD_MOVE_TO(200, 16); - lcd_set_area_xy(16, 240, 200, 232); - lcd_put_long_align32_7(stepsize); - lcd_set_font(FONT_PROP_16, SPACING); - keypad[5] = 255; } if (keypad[6]) { // 8 @@ -676,67 +767,34 @@ __attribute__((naked)) void main(void) if (stepsize > 100000) stepsize = 100000; keypad[11] = 255; - lcd_set_font(FONT_DIGITS_32, SPACING); - LCD_MOVE_TO(200, 16); - lcd_set_area_xy(16, 240, 200, 232); - lcd_put_long_align32_7(stepsize); - lcd_set_font(FONT_PROP_16, SPACING); + updateStepsize(); } if (keypad[3] && keypad[3] != 255) { // * speed = inputbuffer & 0x7; inputbuffer = 0; negativeinput = 0; - cli(); - OCR1A = speeds[speed]; - TCNT1 = 0; - sei(); keypad[3] = 255; - lcd_set_font(FONT_PROP_16, SPACING); - lcd_set_area_xy(280, 320, 48, 64); - LCD_MOVE_TO(48, 280); - lcd_putstr_P(PSTR("Sp ")); - lcd_put_short(speed); + updateSpeed(speed); } if (keypad[14] && keypad[14] != 255) { // C if (motor_active) { - cli(); - M1_STEPS = 0; if (negativeinput) inputbuffer *= -1; - if (M1_position < inputbuffer) { - DIRECTION_FORWARD_M1(); - M1_DIR = 1; - M1_STEPS = inputbuffer - M1_position; - } else { - DIRECTION_BACKWARD_M1(); - M1_DIR = 0; - M1_STEPS = M1_position - inputbuffer; - } + MoveAbsoluteX(inputbuffer); inputbuffer = 0; negativeinput = 0; - sei(); } keypad[14] = 255; } if (keypad[15] && keypad[15] != 255) { // D if (motor_active) { - cli(); - M2_STEPS = 0; if (negativeinput) inputbuffer *= -1; - if (M2_position < inputbuffer) { - DIRECTION_FORWARD_M2(); - M2_DIR = 1; - M2_STEPS = inputbuffer - M2_position; - } else { - DIRECTION_BACKWARD_M2(); - M2_DIR = 0; - M2_STEPS = M2_position - inputbuffer; - } + MoveAbsoluteY(inputbuffer); inputbuffer = 0; negativeinput = 0; - sei(); } + keypad[15] = 255; } if (inputbuffer > 1000000) @@ -769,7 +827,39 @@ __attribute__((naked)) void main(void) lcd_set_font(FONT_PROP_16, SPACING); LCD_MOVE_TO(24, 0); + // } + //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); + + strncpy((char*)buffer, " ", 24); + buffer[0]='S'; + buffer[1]=M1_SWITCH_LOW?'1':'0'; + buffer[2]=M1_SWITCH_HIGH?'1':'0'; + buffer[3]=M2_SWITCH_LOW?'1':'0'; + buffer[4]=M2_SWITCH_HIGH?'1':'0'; + buffer[5]=motor_active?'1':'0'; + buffer[6]='\n'; + buffer[7] = 0; + send_string(buffer,8); + } + } + lasttime = time; + } +} +// 1 shunt voltage/ +// 2 bus voltage +// 3 power +// 4 current +// 5 calib 29789 -> 1/8mA // i2cTx[0]=1; //switch to voltage register // twi_writeTo(0x47, i2cTx, 1, 1, 1); // twi_readFrom(0x47, i2cRx, 2, 1); @@ -792,14 +882,5 @@ __attribute__((naked)) void main(void) // twi_writeTo(0x47, i2cTx, 1, 1, 1); // twi_readFrom(0x47, i2cRx, 2, 1); // lcd_put_int(((i2cRx[0]<<8)+i2cRx[1])/8); - } - } - lasttime = time; - } -} -// 1 shunt voltage -// 2 bus voltage -// 3 power -// 4 current -// 5 calib 29789 -> 1/8mA +