# Commits

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

# File software/Gcode.py

• 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 '''
-        print self.message + " = " +self.answer
+        #print self.message + " = " +self.answer

''' Set a new answer other than 'ok'  '''

# File software/Path.py

• 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

• 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.running     = True                                 # Yes, we are running
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()
-                    #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():
-                print "_do_work done"
+                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)
+
+
+                    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)
+
+                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

• 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:
-
-        with open("/sys/class/uio/uio0/maps/map2/size", "r") as f:
-
-        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_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 '''
(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

• 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

• 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):