1. Elias Bakken
  2. Replicape

Commits

Elias Bakken  committed 32d5ca9

This version is working, but still unstable. Making a tag for CircuitCo and others

  • Participants
  • Parent commits 9c9aa35
  • Branches Rev-A4
  • Tags v0.1

Comments (0)

Files changed (6)

File software/Gcode.py

View file
  • Ignore whitespace
     def removeTokenByLetter(self, letter):
          for i, token in enumerate(self.tokens):
             if token[0] == letter:
-                print token+" "+str(i)  
                 self.tokens.pop(i)
 
     def numTokens(self):
 
     ''' Get the result of the execution '''
     def getAnswer(self):
-        print self.message + " = " +self.answer
+        #print self.message + " = " +self.answer
         return self.answer
     
     ''' Set a new answer other than 'ok'  '''

File software/Path.py

View file
  • Ignore whitespace
     def get_max_speed(self):
         return (self.feed_rate/60.0)/1000.0
 
+    ''' Get the ratio for this axis '''
+    def get_axis_ratio(self, axis):
+        hyp     = self.get_length()    	                                # Calculate the ratio               
+        if hyp == 0.0:
+            return 1.0
+        return self.get_axis_length(axis)/hyp
+
     ''' Get the lowest speed along this segment '''
     def get_min_speed(self):
         return 0
 
+
     ''' Return the list of axes '''
     def get_axes(self):
         return self.axes

File software/Path_planner.py

View file
  • Ignore whitespace
 class Path_planner:
     ''' Init the planner '''
     def __init__(self, steppers):
-        self.steppers = steppers
-        self.pru = Pru()                                        # Make the PRU
-        self.paths = list()                                     # Make a list of paths
+        self.steppers    = steppers
+        self.pru         = Pru()                                # Make the PRU
+        self.paths       = list()                               # Make a list of paths
         self.current_pos = {"X":0.0, "Y":0.0, "Z":0.0, "E":0.0} # Current position in (x, y, z, e)
-        self.running = True                                     # Yes, we are running
-        self.t = Thread(target=self._do_work)                   # Make the thread
+        self.running     = True                                 # Yes, we are running
+        self.t           = Thread(target=self._do_work)         # Make the thread
         self.t.start()		                
 
     ''' Set the acceleration used '''
     ''' This loop pops a path, sends it to the PRU 
     and waits for an event '''
     def _do_work(self):
+        events_waiting = 0
         while self.running:
             if len(self.paths) > 0:
                 path = self.paths.pop(0)                            # Get the last path added
                 path.set_global_pos(self.current_pos.copy())               # Set the global position of the printer
-                for axis in path.get_axes():                        # Run through all the axes in the path
-                    if path.get_axis_length(axis) == 0:
-                        continue
-                    stepper = self.steppers[axis]                   # Get a handle of  the stepper
-                    data = self._make_data(path, axis)              # Generate the timing and pin data                         
-                    #if stepper.has_pru():                           # If this stepper has a PRU associated with it
-                    pru_num = stepper.get_pru()
-                    self.pru.add_data(data, 0)#pru_num) 
-                    #else:               
-                    #    stepper.add_data(data)                      # If not, let the stepper fix this.     
-
-                for axis in path.get_axes():                        
-                    self.steppers[axis].prepare_move()              # Make them start performing
-                for axis in path.get_axes():                        
-                    self.steppers[axis].start_move()                # Make them start performing
-
-                self.pru.commit_data()                              # Commit data to ddr 
-                self.pru.wait_for_event()                           # Wait for the PRU to finish execution 
-                for axis in path.get_axes():                        
-                    self.steppers[axis].end_move()                  # Join threads
-                print "_do_work done"
+                axes_added = 0
+                all_data = {}
+                slowest =  0
+                for axis in path.get_axes():                        # Run through all the axes in the path    
+                    stepper = self.steppers[axis]                   # Get a handle of  the stepper                    
+                    data = self._make_data(path, axis)
+                    if len(data[0]) > 0:
+                        all_data[axis] = data                       # Generate the timing and pin data                         
+                        slowest = max(slowest, sum(data[1]))   
+
+                for axis in all_data:                         
+                    packet = all_data[axis]                           
+                    delays = np.array(packet[1])
+                    diff = (slowest-sum(delays))/len(delays)
+                    for j, delay in enumerate(delays):
+                        delays[j] = max(delay+diff, 2.0/1000.0)   # min 2ms                     
+                    data = (packet[0], delays)                    
+                    while not self.pru.has_capacity_for(len(delays)*2):# Wait until the PRU has capacity for this chunk of data
+                        time.sleep(0.1)
+                    axes_added += self.pru.add_data(data)
+                    
+
+                if axes_added > 0:
+                    self.pru.commit_data()                             # Commit data to ddr 
+                    events_waiting += 1
+                    if events_waiting > 20:
+                        self.pru.wait_for_event(True)                       # Wait for the PRU to finish execution 
+                        events_waiting -= 1
+                '''
+                for axis in all_data: 
+                    packet = all_data[axis]                           
+                    delays = np.array(packet[1])
+                    diff = (slowest-sum(delays))/len(delays)
+                    for j, delay in enumerate(delays):
+                        delays[j] = max(delay+diff, 2.0/1000)                     
+                    data = (packet[0], delays)                    
+                    self.steppers[axis].add_data(data)
+
+                for axis in all_data:                            
+                    self.steppers[axis].prepare_move()
+                for axis in all_data:                            
+                    self.steppers[axis].start_move()
+                for axis in all_data:                            
+                    self.steppers[axis].end_move()
+               '''
+               
+            elif events_waiting > 1:
+                 self.pru.wait_for_event(True)                       # Wait for the PRU to finish execution
+                 events_waiting -= 1   
             else:
                 time.sleep(0.1)                                     # If there is no paths to execute, sleep. 
 
     ''' Make the data for the PRU or steppers '''
-    def _make_data(self, path, axis):        
-        vec         = path.get_axis_length(axis)                        # Total travel distance
-        stepper     = self.steppers[axis]
-        num_steps   = int(abs(vec) * stepper.get_steps_pr_meter())      # Number of steps to tick
+    def _make_data(self, path, axis):     
+        stepper         = self.steppers[axis]
+        steps_pr_meter  = stepper.get_steps_pr_meter()
+        vec             = path.get_axis_length(axis)                        # Total travel distance
+        num_steps       = int(abs(vec) * steps_pr_meter)                    # Number of steps to tick
+        if num_steps == 0:
+            return ([], [])
         step_pin    = stepper.get_step_pin()                            # Get the step pin
         dir_pin     = stepper.get_dir_pin()                             # Get the direction pin
         dir_pin     = 0 if vec < 0 else dir_pin                         # Disable the dir-pin if we are going backwards               
-        delays      = self._calculate_delays(path, axis)                # Make the delays
         pins        = [step_pin | dir_pin, dir_pin]*num_steps           # Make the pin states
 
-        if len(delays) > num_steps:                                     # If the delays is longer than the steps, something is wrong
-            delays = []
-        if num_steps == 1:
-            delays = [0]            
+        s           = abs(path.get_axis_length(axis))                   # Get the length of the vector
+        ratio       = path.get_axis_ratio(axis)
+
+        Vm = path.get_max_speed()*ratio				                    # The travelling speed in m/s
+        a  = self.acceleration*ratio    		                        # Accelleration in m/s/s
+        ds = 1.0/steps_pr_meter                                         # Delta S, distance in meters travelled pr step. 
+        u  = ratio*path.get_min_speed()                 	            # Minimum speed in m/s
+        tm = (Vm-u)/a					                                # Calculate the time for when max speed is met. 
+        sm = min(u*tm + 0.5*a*tm*tm, s/2.0)			                    # Calculate the distance traveled when max speed is met
+
+        distances       = list(np.arange(0, sm, ds))		            # Table of distances                       
+        timestamps      = [(-u+np.sqrt(2.0*a*ss+u*u))/a for ss in distances]# Make a table of times when a tick occurs   
+        delays          = np.diff(timestamps)/2.0			                # We are more interested in the delays pr second.         
+        delays = list(np.array([delays, delays]).transpose().flatten()) # Double the array so we have timings for up and down  
+  
         i_steps     = num_steps-len(delays)		                        # Find out how many delays are missing
-        i_delays    = delays[-1::]*i_steps*2		                    # Make the intermediate steps
+        i_delays    = [(ds/Vm)/2.0]*i_steps*2		                    # Make the intermediate steps
         delays      += i_delays+delays[::-1]                            # Add the missing delays. These are max_speed
-
-        td          = num_steps/stepper.get_steps_pr_meter()            # Calculate the actual travelled distance        
+        td          = num_steps/steps_pr_meter                          # Calculate the actual travelled distance        
         if vec < 0:                                                     # If the vector is negative, negate it.      
             td     *= -1.0
 
         path.set_travelled_distance(axis, td)                           # Set the travelled distance back in the path 
         self.current_pos[axis] += td                                    # Update the global position vector
-        #print "Axis: "+axis+",  Vector: "+str(vec)+",  Num_steps: "+str(num_steps)
-        #print "Global position: "+str(self.current_pos)
 
         return (pins, delays)                                           # return the pin states and the data
 
-    ''' Caluclate the delays for a path vector '''
-    def _calculate_delays(self, path, axis):
-        s       = abs(path.get_axis_length(axis))                             # Get the length of the vector
-        hyp     = path.get_length()    	                                # Calculate the ratio               
-        if hyp == 0.0:
-            ratio = 1
-        else:
-            ratio = s/hyp
-        Vm = path.get_max_speed()*ratio				                    # The travelling speed in m/s
-        a  = self.acceleration*ratio    		                        # Accelleration in m/s/s
-        ds = 1.0/self.steppers[axis].get_steps_pr_meter()               # Delta S, distance in meters travelled pr step. 
-        u  = ratio*path.get_min_speed()                 	            # Minimum speed in m/s
 
-        #print "Axis: "+str(axis)+", vec: "+str(s)+", len: "+str(path.get_length())+", ratio: "+str(ratio)
-        tm = (Vm-u)/a					                                # Calculate the time for when max speed is met. 
-        sm = u*tm + 0.5*a*tm*tm			                                # Calculate the distace travelled when max speed is met
-        if sm > s/2.0:                                                  # If the distance is too short, we do not reach full speed 
-            sm = s/2.0            
-        if ds > sm:                                                     # If the delta-step is too small, force it to become 2*ds
-            sm = 2.0*ds
-        
-        distances   = np.arange(0, sm, ds)		                        # Table of distances
-        timestamps  = [(-u+np.sqrt(2.0*a*s+u*u))/a for s in distances]  # Make a table of times, the time at which a tick occurs
-        delays      = np.diff(timestamps)/2.0			                # We are more interested in the delays pr second. 
-        delays      = np.array([delays, delays]).transpose().flatten()	# Double the array so we have timings for up and down
-        
-        return list(delays)
 
 
-  

File software/Pru.py

View file
  • Ignore whitespace
 
 class Pru:
     def __init__(self):
-        pru_hz 			    = 200*1000*1000		            # The PRU has a speed of 200 MHz
-        self.s_pr_inst 		= 1.0/pru_hz		            # I take it every instruction is a single cycle instruction
-        self.inst_pr_loop 	= 16				            # This is the minimum number of instructions needed to step. 
-        self.inst_pr_delay 	= 2					            # Every loop adds two instructions: i-- and i != 0            
-        self.pru_data       = [[], []]                      
-        with open("/sys/class/uio/uio0/maps/map2/addr", "r") as f:
-            ddr_addr = int(f.readline(), 16)
-
-        with open("/sys/class/uio/uio0/maps/map2/size", "r") as f:
-            ddr_size = int(f.readline(), 16)
-
-        ddr_offset     = ddr_addr-0x10000000
-        ddr_filelen    = ddr_size+0x10000000
+        pru_hz 			    = 200*1000*1000             # The PRU has a speed of 200 MHz
+        self.s_pr_inst 		= 1.0/pru_hz                # I take it every instruction is a single cycle instruction
+        self.inst_pr_loop 	= 16                        # This is the minimum number of instructions needed to step. 
+        self.inst_pr_delay 	= 2                         # Every loop adds two instructions: i-- and i != 0            
+        self.pru_data       = []      	    	        # This holds all data for one move (x,y,z,e1,e2)
+        self.ddr_used       = []                        # List of data lengths currently in DDR for execution
+        self.ddr_reserved   = 0        
+
+        pypruss.modprobe(0x40000)    			        # This only has to be called once pr boot
+        self.ddr_addr = pypruss.ddr_addr()
+        self.ddr_size = pypruss.ddr_size()              
+        print "The DDR memory reserved for the PRU is "+hex(self.ddr_size)+" and has addr "+hex(self.ddr_addr)
+
+        ddr_offset     		= self.ddr_addr-0x10000000  # The Python mmap function cannot accept unsigned longs. 
+        ddr_filelen    		= self.ddr_size+0x10000000
         self.DDR_START      = 0x10000000
-        self.DDR_END        = 0x10000000+ddr_size
+        self.DDR_END        = 0x10000000+self.ddr_size
         self.ddr_start      = self.DDR_START
 
-        with open("/dev/mem", "r+b") as f:	                # Open the memory device
+        with open("/dev/mem", "r+b") as f:	            # Open the memory device
             self.ddr_mem = mmap.mmap(f.fileno(), ddr_filelen, offset=ddr_offset) # mmap the right area            
             self.ddr_mem[self.ddr_start:self.ddr_start+4] = struct.pack('L', 0)  # Add a zero to the first reg to make it wait
-
-        pypruss.modprobe()							       	# This only has to be called once pr boot
-        pypruss.init()										# Init the PRU
-        pypruss.open(0)										# Open PRU event 0 which is PRU0_ARM_INTERRUPT
-        pypruss.pruintc_init()								# Init the interrupt controller
-        pypruss.pru_write_memory(0, 0, [ddr_addr])			# Put the ddr address in the first region 
+       
+        pypruss.init()						            # Init the PRU
+        pypruss.open(0)						            # Open PRU event 0 which is PRU0_ARM_INTERRUPT
+        pypruss.pruintc_init()					        # Init the interrupt controller
+        pypruss.pru_write_memory(0, 0, [self.ddr_addr])		# Put the ddr address in the first region 
         pypruss.exec_program(0, "../firmware/firmware_pru_0.bin")	# Load firmware "ddr_write.bin" on PRU 0
         print "PRU initialized"
 
     ''' Add some data to one of the PRUs '''
-    def add_data(self, data, pru_num):
+    def add_data(self, data):
         (pins, delays) = data                       	    # Get the data
         if len(pins) == 0:
-            return 
-        print "Adding data len for "+str(pru_num)+": "+str(len(pins))
+            return 0
+        #print "Adding pru data: "+str(len(pins))
         delays = map(self._sec_to_inst, delays)     	    # Convert the delays in secs to delays in instructions
         data = np.array([pins, delays])		        	    # Make a 2D matrix combining the ticks and delays
         data = list(data.transpose().flatten())     	    # Braid the data so every other item is a pin and delay
         
-        if len(self.pru_data[pru_num]) > 0:
-            self.pru_data[pru_num] = self._braid_data(data, self.pru_data[pru_num])
+        if len(self.pru_data) > 0:
+            self.pru_data = self._braid_data(data, self.pru_data)
         else:
-            self.pru_data[pru_num] = data
+            self.pru_data = data
+        self.ddr_reserved += len(data)		            # This amount is now reserved, waiting to be committed
+        return 1
+
+    ''' Check if the PRU has capacity for a chunk of data '''
+    def has_capacity_for(self, data_len):
+        cap = self.ddr_size-sum(self.ddr_used)-self.ddr_reserved
+        return (cap > data_len)
+    
+    ''' If there is little capacity, return true '''
+    def has_little_capacity(self):
+        cap = self.ddr_size-sum(self.ddr_used)-self.ddr_reserved
+        return cap < self.ddr_size/2
+
 
     ''' Commit the data to the DDR memory '''
     def commit_data(self):
-        data = struct.pack('L', len(self.pru_data[0])/2)	    # Data in string form
-        for reg in self.pru_data[0]:									
+        data = struct.pack('L', len(self.pru_data)/2)	    	# Data in string form
+        for reg in self.pru_data:									
             data += struct.pack('L', reg) 				        # Make the data, it needs to be a string
-        data += struct.pack('L', 0)                             # Add a terminating 0, this keeps it looping.
+        data += struct.pack('L', 0)                             # Add a terminating 0, this keeps the fw waiting for a new command.
 
         self.ddr_end = self.ddr_start+len(data)       
         if self.ddr_end > self.DDR_END:                         # If the data is too long, wrap it around to the start
             multiple = (self.DDR_END-self.ddr_start)%8          # Find a multiple of 8
             cut = self.DDR_END-self.ddr_start-multiple-4        # The cut must be done after a delay, so a multiple of 8 bytes +/-4
+
             first = struct.pack('L', cut/8)+data[4:cut]         # Update the loop count
             first += struct.pack('L', DDR_MAGIC)                # Add the magic number to force a reset of DDR memory counter
             self.ddr_mem[self.ddr_start:self.ddr_start+len(first)] = first  # Write the first part of the data to the DDR memory.
             self.ddr_end = self.DDR_START+len(second)           # Update the end counter
             self.ddr_mem[self.DDR_START:self.ddr_end] = second  # Write the second half of data to the DDR memory.
 
-            self.wait_for_event()                               # Must wait for event here  
+            self.wait_for_event(False)                          # Must wait for event here to sync 
             print "Wrapped"
         else:
             self.ddr_mem[self.ddr_start:self.ddr_end] = data    # Write the data to the DDR memory.
                 self.ddr_mem[self.DDR_START:self.DDR_START+4] = struct.pack('L', 0) # Terminate the next instruction
                 self.ddr_end = self.DDR_START+4                
                 print "wrapped due to insufficient DDR"
-
-        self.ddr_start = self.ddr_end-4                         # Update the start of ddr for next time 
-        self.pru_data = [[],[]]                                 # Reset the pru_data list since it has been commited         
+     
+        self.ddr_used.append(len(data)) 		            # update the amount of memory used 
+        self.ddr_reserved 	-= len(data)		            # Remove this amount from the reserved counter 
+        self.ddr_start 		 = self.ddr_end-4               # Update the start of ddr for next time 
+        self.pru_data 		 = []                           # Reset the pru_data list since it has been commited         
 
 
     ''' Wait for the PRU to finish '''                
-    def wait_for_event(self):
-        pypruss.wait_for_event(PRU_EVTOUT_0)				    # Wait a while for it to finish.
-        pypruss.clear_event(PRU0_ARM_INTERRUPT)				    # Clear the event 
-        pypruss.wait_for_event(PRU_EVTOUT_0)				    # Wait a while for it to finish.
-        pypruss.clear_event(PRU0_ARM_INTERRUPT)				    # Clear the event 
+    def wait_for_event(self, pop=True):
+        pypruss.wait_for_event(PRU_EVTOUT_0)			# Wait a while for it to finish.
+        pypruss.clear_event(PRU0_ARM_INTERRUPT)			# Clear the event 
+        pypruss.wait_for_event(PRU_EVTOUT_0)			# Wait a while for it to finish.
+        pypruss.clear_event(PRU0_ARM_INTERRUPT)			# Clear the event 
+        if pop:
+            self.ddr_used.pop(0)                        # Pop the first ddr memory amount         
 
     ''' Close shit up '''
     def close(self):
         ddr_mem.close()                                         # Close the memory 
         f.close()                                               # Close the file
         pypruss.pru_disable(0)                                  # Disable PRU 0, this is already done by the firmware
-        #pypruss.pru_disable(1)                                 # Disable PRU 0, this is already done by the firmware
         pypruss.exit()                                          # Exit, don't know what this does. 
         
 
         return int(inst_pr_step)			        # Make it an int
 
 
-    ''' Braid together the data from the two data sets'''
+    ''' Braid/merge together the data from the two data sets'''
     def _braid_data(self, data1, data2):
         braids = [data1[0] | data2[0]]
         del data1[0]

File software/Replicape.py

View file
  • Ignore whitespace
         self.steppers["Z"].setCurrentValue(1.0) # 2A
         self.steppers["Z"].setEnabled() 
         self.steppers["Z"].set_steps_pr_mm(149.25)
-        self.steppers["Z"].set_microstepping(0) 
+        self.steppers["Z"].set_microstepping(2) 
 
-        self.steppers["E"].setCurrentValue(1.0) # 2A        
+        self.steppers["E"].setCurrentValue(1.8) # 2A        
         self.steppers["E"].setEnabled()
         self.steppers["E"].set_steps_pr_mm(5.0)
         self.steppers["E"].set_microstepping(2)

File software/Smd.py

View file
  • Ignore whitespace
         bytes = []
         for smd in SMD.all_smds:	   
             bytes.append(smd.getState())
-        print "Updating registers with: "+str(bytes[::-1])
         spi2_1.writebytes(bytes[::-1])
 
     ''' Init'''
 	
     ''' Sets the SMD disabled '''
     def setDisabled(self):
-        print "setDisabled called"
         if self.enabled:
             self.state |= (1<<6)
             self.update()
             self.enabled = False
-            print "smd disabled, state = "+bin(self.state)
 
     '''Logic high to enable device, logic low to enter
     low-power sleep mode. Internal pulldown.'''
         (pins, delays) = data
         self.pins = pins
         self.delays = delays
-        print "steps: "+str(len(self.pins))+" delays "+str(len(self.delays))
+        #print "steps: "+str(len(self.pins))+" delays "+str(len(self.delays))
     
     ''' Prepare a move. But do not start the thread yet. '''
     def prepare_move(self):