Beispiel #1
0
 def init_pru(self):
     '''
     creates interface for pruss and irq
     '''
     self.IRQ = 2
     self.pruss = Icss('/dev/uio/pruss/module')
     self.irq = Uio("/dev/uio/pruss/irq%d" % self.IRQ, blocking=False)
     self.pruss.initialize(fill_memories=True)
Beispiel #2
0
#!/usr/bin/python3

from uio.ti.icss import Icss
from uio.device import Uio
import ctypes
import asyncio

loop = asyncio.get_event_loop()

EVENT0 = 16  # range 16..31
EVENT1 = 17  # range 16..31
IRQ = 2  # range 2..9

pruss = Icss("/dev/uio/pruss/module")
irq = Uio("/dev/uio/pruss/irq%d" % IRQ)
intc = pruss.intc
(core0, core1) = pruss.cores

pruss.initialize()

# clear and enable events and route them to our irq
for event in EVENT0, EVENT1:
    intc.ev_ch[event] = IRQ
    intc.ev_clear_one(event)
    intc.ev_enable_one(event)

# load program onto both cores
core0.load('fw/intc-test.bin')
core1.load('fw/intc-test.bin')

Beispiel #3
0
#!/usr/bin/python3
""" blinklaser1.py - test script for the Firestarter
blinks the laser 3 times with a period of 6 seconds
"""
from uio.ti.icss import Icss

pruss = Icss('/dev/uio/pruss/module')
pruss.initialize()
core = pruss.core0
core.load('off.bin')
core.run()
print('Waiting for core to halt')
while not core.halted:
    pass
Beispiel #4
0
#!/usr/bin/python3

from uio.ti.icss import Icss
from uio.device import Uio
import ctypes
import asyncio

loop = asyncio.get_event_loop()

EVENT0 = 16 # range 16..31
EVENT1 = 17 # range 16..31
IRQ = 2     # range 2..9

pruss = Icss( "/dev/uio/pruss/module" )
irq = Uio( "/dev/uio/pruss/irq%d" % IRQ )
intc = pruss.intc
(core0, core1) = pruss.cores

pruss.initialize()

# clear and enable events and route them to our irq
for event in EVENT0, EVENT1:
    intc.ev_ch[ event ] = IRQ
    intc.ev_clear_one( event )
    intc.ev_enable_one( event )

# load program onto both cores
core0.load( 'fw/intc-test.bin' )
core1.load( 'fw/intc-test.bin' )

# map and set parameters
Beispiel #5
0
#!/usr/bin/python3

from uio.ti.icss import Icss
from time import sleep

pruss = Icss("/dev/uio/pruss/module")
#core = pruss.core0

print(f'Activate UART')

pruss.uart.initialize(baudrate=9600)

#pruss.uart.io()
pruss.uart.io.write(b'CE\r')

message = pruss.uart.io.readline(newline=b'\r')

#message = message[0:len(message)-1]

print(message)

pruss.uart.io.close()
Beispiel #6
0
class Machine:
    def __init__(self, stepper=True, camera = False):
        if camera:
            from camera import Camera
            self.camera = Camera()
        self.loadconstants()
        self.position = [0, 0, 0]
        self.pindictionary()
        self.init_pru()
        self.currentdir = dirname(realpath(__file__))
        self.bin_folder = join(self.currentdir, 'binaries')
        self.laserchannels = 0
        self.configure_pins()
        if stepper:
            self.motor_spi = [self.init_stepper(label) for label in ['x','y','z']] 
        # digipot is used to set laser power
        self.digipot = I2C.get_i2c_device(0x28, busnum=2)


    def configure_pins(self):
        '''
        pins are configured via config-pin -f pinfile.bbio

        This relies on cape-universal. Cape-universal allows pins to be
        changed on the fly without reboot.
        '''
        path = join(self.currentdir, 'config-pin', 'firestarter.bbio')
        MyOut = subprocess.Popen(['config-pin', '-f', path ],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT)
        stdout, stderr = MyOut.communicate()
        if stdout or stderr:
            raise Exception("Configuring pins via config-pin failed")


    @property
    def laser_power(self):
        '''
        gets power set for laser driver chip can be in range [0-255] 
        '''
        return abs(self.digipot.readU8(0))


    @laser_power.setter
    def laser_power(self, value):
        '''
        set laser power to given value in range [0-255]
        for the laser driver chip. This does not turn on or off the laser.

        The laser power can be changed in two ways.
        First by using one or two channels. Second by settings a value between
        0-255 at the laser driver chip.
        '''
        if value < 0 or value > 255:
            raise Exception('Invalid laser power')
        self.digipot.write8(0, value)


    @property
    def laserchannels(self):
        '''
        returns the number of laser channels turned on

        channels can be [0,1,2]
        '''
        return self._laserchannels


    @laserchannels.setter
    def laserchannels(self, channels):
        '''
        sets 0, 1 or 2 channels on.
        
        The laser power can be changed in two ways.
        First by using one or two channels. Second by setting a value in 
        range [0-255] at the laser driver chip. Here the amount of channels 
        used is set.
        :param channels: number of channels to be turned max is 2. 
        '''
        channels = int(round(channels))
        if channels<0 or channels>2:
            raise Exception("Channels is not within set [0,1,2]")

        self.pruss.core0.load(join(self.bin_folder,
            'switch_laser.bin'))

        class Params( Structure ):
            _fields_ = [
                    ("power", c_uint32)
        ]
        variables = self.pruss.core0.dram.map(Params)
        variables.power = int(round(channels))
        self.pruss.core0.run()
        while not self.pruss.core0.halted:
            pass
        self._laserchannels = int(round(channels))


    def test_photodiode(self):
        """
        The laser is turned on. The polygon is spun at a rate of 1000 Hz
        for 5 seconds. The photodiode is measured for three seconds, if 
        high within these three seconds test is succesfull, otherwise 
        unsuccesfull. The laser is turned off.
        """
        GPIO.output(self.pins['prism_enable'], GPIO.LOW)
        self.pruss.core0.load(join(self.bin_folder,
                        'photodiodetest.bin'))
        self.pruss.core0.run()
        print("Waiting for core to halt")
        while not self.pruss.core0.halted:
            pass
        byte0 = self.pruss.core0.dram.map(c_uint32)
        return_value = int(byte0.value)
        if return_value == 0:
            print("Laser detected with photodiode, test successfull")
        elif return_value == 1:
            print("Photo diode connected but no signal")
        else:
            print("Photo diode not connected")
        GPIO.output(self.pins['prism_enable'], GPIO.HIGH)


    def test_polygonmotor(self):
        """
        spins the polygon at a rate of x Hz for 5 seconds
        """
        GPIO.output(self.pins['prism_enable'], GPIO.LOW)
        self.pruss.core0.load(join(self.bin_folder,
                        'spinpolygon.bin'))
        self.pruss.core0.run()
        print("Spinning polygon for 5 seconds")
        while not self.pruss.core0.halted:
            pass
        GPIO.output(self.pins['prism_enable'], GPIO.HIGH)


    def test_createline(self):
        """
        spins the polygon at a rate of x Hz for 5 seconds
        while laser channel 1 is enabled.
        As such a line is created without a phase lock loop.
        """
        GPIO.output(self.pins['prism_enable'], GPIO.LOW)
        self.pruss.core0.load(join(self.bin_folder,
                        'createline.bin'))
        self.pruss.core0.run()
        print("Turning on channel 1 and spinning polygon for 5 seconds")
        while not self.pruss.core0.halted:
            pass
        GPIO.output(self.pins['prism_enable'], GPIO.HIGH)


    def loadconstants(self):
        '''
        loads COMMANDS and Errors
        '''
        # commands accepted by scanhead also defined in laserscribe constants
        self.COMMANDS = ['CMD_EMPTY',
                'CMD_SCAN_DATA', 'CMD_SCAN_DATA_NO_SLED']
        self.COMMANDS += ['CMD_EXIT', 'CMD_DONE']
        self.COMMANDS = bidict(enumerate(self.COMMANDS))
        # errors accepted by scanhead also defined in laserscribe constants
        self.ERRORS = ['ERROR_NONE',
                'ERROR_DEBUG_BREAK', 'ERROR_MIRROR_SYNC']
        self.ERRORS += ['ERROR_TIME_OVERRUN']
        self.ERRORS = bidict(enumerate(self.ERRORS))

        #TODO: place this in a dictionary or a named tuple

        # key variables
        self.RPM = 2400                           # revolutions per minute 
        self.TICK_DELAY = 100                # cpu cycles in loop
        PRU_SPEED = 200E6                    # cpu cycles per second
        self.FACETS = 4                           # number of sides of prism
        self.TICKS_START = 4375              # laser starts in off state
        self.SCANLINE_DATA_SIZE = 790        # there are 8 pixels per byte 
                                             # so TICKS = 8*SCANLINE_DATA_SIZE
        # dependent variables
        self.TICKS_PER_PRISM_FACET = round(PRU_SPEED//  # ticks per prism facet
        (self.TICK_DELAY*(self.RPM*self.FACETS)/60)) 
        # time in seconds used to spinup prism
        self.SPINUP_TICKS = round(1.5 * PRU_SPEED / self.TICK_DELAY) 
        # time in seconds waited for laser stabilization
        self.MAX_WAIT_STABLE_TICKS = round(1.125 * PRU_SPEED / self.TICK_DELAY) 
        # ticks per half period of prism motor
        self.TICKS_HALF_PERIOD_MOTOR=int(round((self.TICKS_PER_PRISM_FACET*self.FACETS/6)/2))
        self.SCANLINE_HEADER_SIZE = 1        # each line starts with a self.COMMAND
        self.START_RINGBUFFER = 1            # the zero byte is an error code, ringbuffer starts at 1
        self.QUEUE_LEN = 8                   # length of the ringbuffer
        # length of an item in the ringbuffer in bytes
        self.SCANLINE_ITEM_SIZE = self.SCANLINE_HEADER_SIZE + self.SCANLINE_DATA_SIZE
        # the prism is spin up, it is determined if the jitter per prism is within a certain threshold
        self.JITTER_THRESH = round(self.TICKS_PER_PRISM_FACET / 400 )
        # after spinup the jitter allow is set smaller this leads to better results
        self.JITTER_ALLOW = round(self.TICKS_PER_PRISM_FACET / 3000 )

        self.STEPSPERMM = {'x': 76.2, 'y': 76.2, 'z': 1600}
        # These things are needed in interpolator
        self.SLED_SPEED = 1/self.STEPSPERMM['y']*(self.RPM*self.FACETS/60)
        #print("Sled speed is fixed at {} mm/s".format(SLED_SPEED))
        #print("Laser freq is {} Hz".format(200e6/TICK_DELAY))
        


    def init_stepper(self, drive='x', mA=600, microsteps=16, stealthchop=True):
        '''
        connects to stepper motor with current in miliamperes, number of microsteps
        and steatlchop, drive can be x, y or z.
        '''
        GPIO_0_BASE = 0x44E07000
        # pins order is x,y,z
        pindict = {'x':GPIO_0_BASE | 5,'y':GPIO_0_BASE | 30, 'z': GPIO_0_BASE | 26} 
        try:
            motor = steppers.TMC2130(pindict[drive])
        except KeyError:
            raise Exception("Invalid drive value")
        motor.begin()
        if motor.test_connection():
            raise Exception("Failed to connent to {} motor".format(drive))
        motor.rms_current(mA)
        motor.microsteps(microsteps)
        motor.toff(3)
        motor.stealthChop(stealthchop)
        return motor


    def init_pru(self):
        '''
        creates interface for pruss and irq
        '''
        self.IRQ = 2
        self.pruss = Icss('/dev/uio/pruss/module')
        self.irq = Uio("/dev/uio/pruss/irq%d" % self.IRQ, blocking=False)
        self.pruss.initialize(fill_memories=True)


    def pindictionary(self):
        '''
        creates dictionary for motor pins
        '''
        self.pins = {'x_dir': "P9_42",
                     'y_dir': "P8_15",
                     'z_dir': "P8_17",
                     'step_enable': "P9_12",
                     'prism_enable': "P9_23"}

        for key, value in self.pins.items():
            GPIO.setup(value, GPIO.OUT)


    def write_params(self, axis, distance, speed, core=0):
        '''
        writes parameters to pru0

        in moving distance defines a length
        in homing distance defines a limit

        :param axis; can be 'x', 'y' or 'z' used to determine steps per mm
        :param distance; distance in mm
        :param speed; speed in mm/s
        :param core; pru core map parameters to
        '''
        try:
            steps_per_mm = self.STEPSPERMM[axis]
        except KeyError:
            raise Exception("Axis {} invalid".format(axis))

        class Params( Structure ):
            _fields_ = [                         #1 steps moved in case of move
                    ("steps", c_uint32),         #1 max amount steps moved for home
                    ("halfperiodstep", c_uint32) #2 speed
        ]
        if core == 0:
            params = self.pruss.core0.dram.map(Params)
        else:
            params = self.pruss.core1.dram.map(Params)
        # round(np.float64(3.0)) -> 3.0, round(3.0) -> 3
        params.steps = int(round(distance * steps_per_mm))
        CPU_SPEED = 200E6
        INST_PER_LOOP = 2
        params.halfperiodstep = int(round(CPU_SPEED
            /(2*speed*steps_per_mm*INST_PER_LOOP)))


    def enable_steppers(self):
        '''
        enables stepper motors by setting enable pin to low
        '''
        GPIO.output(self.pins['step_enable'], GPIO.LOW)


    def disable_steppers(self):
        '''
        disables stepper motors by setting enable pin to high
        '''
        GPIO.output(self.pins['step_enable'], GPIO.HIGH)


    def home(self, direction='x', speed = 2):
        '''
        homes axis in direction at given speed
        
        :param direction; homing direction, x or y
        :param speed; homing speed in mm/s
        '''
        if direction == 'x':
            self.write_params('x', 300, speed, core=1)
            GPIO.output(self.pins['x_dir'], GPIO.LOW)
            self.pruss.core1.load(join(self.bin_folder,
                'home_x.bin'))
            self.pruss.core1.run()
            while not self.pruss.core1.halted:
                pass
            if self.pruss.core1.r10:
                raise Exception("Homing x failed")
        elif direction == 'y':
            self.write_params('y', 300, speed, core=0)
            GPIO.output(self.pins['y_dir'], GPIO.LOW)
            self.pruss.core0.load(join(self.bin_folder,
                'home_y.bin'))
            self.pruss.core0.run()
            while not self.pruss.core0.halted:
                pass
            if self.pruss.core0.r2:
                raise Exception("Homing y failed")
        elif direction == 'z':
            self.write_params('z', 300, speed, core=1)
            # home in the down direction
            GPIO.output(self.pins['z_dir'], GPIO.HIGH)
            self.pruss.core1.load(join(self.bin_folder,
                'home_z.bin'))
            self.pruss.core1.run()
            while not self.pruss.core1.halted:
                pass
            if self.pruss.core1.r10:
                raise Exception("Homing z failed")
        else:
            raise Exception("Direction invalid")
        # update current position, move so homing switch is disabled
        if direction == 'x':
            self.position[0] = 0
            self.move([40, self.position[1], self.position[2]], 4)
            self.position[0] = 0
        elif direction == 'y':
            self.position[1] = 0
            self.move([self.position[0], 10, self.position[2]], 4)
            self.position[1] = 0
        else: # must be z exception already cared in homing procedure
            self.position[2] = 0
            self.move([self.position[0], self.position[2], 10], 4)  
            self.position[2] = 0


    def move(self, target_position, speed = 2):
        '''
        moves axis into position at given speed

        :param target position; list, [x, y, z] in mm
        :param speed; homing speed in mm/s
        '''
        if target_position[0] < 0 or target_position[0] > 200:
            raise Exception('Target out of bounds')
        elif target_position[1] < 0 or target_position[1] > 200:
            raise Exception('Target out of bounds')
        elif target_position[2] < 0 or target_position[2] > 200:
            raise Exception('Target out of bounds')
        displacement = np.array(target_position) - np.array(self.position)
        
        if displacement[0]:
            direction = GPIO.HIGH if displacement[0] > 0 else GPIO.LOW
            GPIO.output(self.pins['x_dir'], direction)
            self.write_params('x', abs(displacement[0]), speed, core=1)
            self.pruss.core1.load(join(self.bin_folder, 'move_x.bin'))
            self.pruss.core1.run()
            while not self.pruss.core1.halted:
                pass

        if displacement[1]:
            direction = GPIO.HIGH if displacement[1] > 0 else GPIO.LOW
            GPIO.output(self.pins['y_dir'], direction)
            self.write_params('y', abs(displacement[1]), speed, core=0)
            self.pruss.core0.load(join(self.bin_folder, 'move_y.bin'))
            self.pruss.core0.run()
            while not self.pruss.core0.halted:
                pass

        if displacement[2]:
            direction = GPIO.LOW if displacement[2] > 0 else GPIO.HIGH
            GPIO.output(self.pins['z_dir'], direction)
            self.write_params('z', abs(displacement[2]), speed, core=1)
            self.pruss.core1.load(join(self.bin_folder, 'move_z.bin'))
            self.pruss.core1.run()
            while not self.pruss.core1.halted:
                pass

        self.position = target_position


    def enable_scanhead(self, singlefacet=False):
        '''
        enables scanhead, ensure scanhead is not above substrate
        :param singlefacet; if true singlefacet exposure enabled
        '''
        GPIO.output(self.pins['prism_enable'], GPIO.LOW)
        PRU0_ARM_INTERRUPT = 19
        self.pruss.intc.ev_ch[PRU0_ARM_INTERRUPT] = self.IRQ
        self.pruss.intc.ev_clear_one(PRU0_ARM_INTERRUPT)
        self.pruss.intc.ev_enable_one(PRU0_ARM_INTERRUPT)
        self.pruss.core0.load(join(self.bin_folder, './stabilizer.bin'))
        # map and set parameters
        class Variables( Structure ):
            _fields_ = [
                    ("ringbuffer_size", c_uint32),  
                    ("item_size", c_uint32),
                    ("start_sync_after", c_uint32),
                    ("global_time", c_uint32),
                    ("polygon_time", c_uint32),
                    ("wait_countdown", c_uint32),
                    ("hsync_time", c_uint32),
                    ("item_start", c_uint32),
                    ("item_pos", c_uint32),
                    ("state", c_uint16),
                    ("bit_loop", c_uint8),
                    ("last_hsync_bit", c_uint8),
                    ("single_facet", c_uint8),
                    ("current_facet", c_uint8),
                    ("ticks_half_period_motor", c_uint16),
                    ("low_thresh_prism", c_uint16),
                    ("high_thresh_prism", c_uint16),
                    ("ticks_start", c_uint16),
                    ("tick_delay", c_uint16),
                    ("spinup_ticks", c_uint32),
                    ("max_wait_stable_ticks", c_uint32)
                ]
        variables = self.pruss.core0.dram.map(Variables)
        if singlefacet:
            variables.single_facet = 1
        else:
            variables.single_facet = 0
        variables.item_size = self.SCANLINE_ITEM_SIZE
        variables.ringbuffer_size = self.SCANLINE_ITEM_SIZE * self.QUEUE_LEN
        variables.start_sync_after = self.TICKS_PER_PRISM_FACET - self.JITTER_ALLOW - 1
        variables.ticks_half_period_motor = self.TICKS_HALF_PERIOD_MOTOR
        variables.low_thresh_prism = self.TICKS_PER_PRISM_FACET - self.JITTER_THRESH
        variables.high_thresh_prism = self.TICKS_PER_PRISM_FACET + self.JITTER_THRESH
        variables.ticks_start = self.TICKS_START
        variables.tick_delay = self.TICK_DELAY
        variables.max_wait_stable_ticks = self.MAX_WAIT_STABLE_TICKS
        variables.spinup_ticks = self.SPINUP_TICKS
        self.pruss.core0.run()
        #TODO: add check polygon is enabled and stable
        # if you can't disable does not work
        sleep(4)


    def receive_command(self, byte = None, check = False):
        '''
        receives command at given offset

        :param byte; byte to read after trigger is received this is the command
        :param check; check wether the command equals command empty
        '''
        if byte == None:
            byte = self.START_RINGBUFFER

        self.pruss.intc.out_enable_one(self.IRQ) 
        # while loop is used in case irc_recv call is non-blocking
        while True:
            result = self.irq.irq_recv()
            if result:
                break
            else:
                sleep(1E-3)
        self.pruss.intc.ev_clear_one(self.pruss.intc.out_event[self.IRQ])
        self.pruss.intc.out_enable_one(self.IRQ)
        [command_index] = self.pruss.core0.dram.map(length = 1,
                                    offset = byte)

        if check:
            received_command = self.COMMANDS[command_index]
            if  received_command != 'CMD_EMPTY':
                error = ('Command not empty but {} '.format(received_command) +
                         'you could overwrite line not written to substrate yet.')
                raise Exception(error)
        
        return command_index


    def disable_scanhead(self):
        '''
        disables scanhead

        function sleeps to offset is START_RINGBUFFER
        '''
    
        data = [self.COMMANDS.inv['CMD_EXIT']]
        self.pruss.core0.dram.write(data, offset = self.START_RINGBUFFER)
        while not self.pruss.core0.halted:
            pass
        GPIO.output(self.pins['prism_enable'], GPIO.HIGH)


    def error_received(self):
        '''
        prints errors received and returns error index

        can be used to read out errors from the laser scanner
        '''
        ERROR_RESULT_POS = 0
        error_index = self.pruss.core0.dram.map(length = 1,
                offset = ERROR_RESULT_POS)[0]
        if error_index:
            try:
                print("Error received; {}".format(
                    self.ERRORS[error_index]))
            except IndexError:
                print("Error, error out of index")
        return error_index


    def expose(self, line_data, direction = True,
            multiplier = 1, move = False, takepicture = False):
        '''
        expose given line_data to substrate in given direction
        each line is exposed multiplier times.
        returns result of takepicture

        :param line_data; data to write to scanner, uint8
        :param multiplier; amount of times a line is exposed
        :param direction; direction of exposure,
                          True is postive y (away from home)
        :param move; if enabled moves stage
        :param takepicture; if enabled takes picture
        '''
        if line_data.dtype != np.uint8:
            raise Exception('Dtype must be uint8')
        if len(line_data) % self.SCANLINE_DATA_SIZE:
            raise Exception('Data invalid, length should be multilple'
                            + ' of scanline size')
        if (line_data.max() < 1 or line_data.max() > 255):
            raise Exception('Data invalid, values out of range.')
        if move:
            self.enable_steppers()
        else:
            self.disable_steppers()

        if direction:
            GPIO.output(self.pins['y_dir'], GPIO.HIGH)
        else:
            GPIO.output(self.pins['y_dir'], GPIO.LOW)
        
        byte = self.START_RINGBUFFER

        for scanline in range(0, len(line_data)//self.SCANLINE_DATA_SIZE):
            for counter in range(0, multiplier):
                self.receive_command(byte, True)
                # you start the picture where the laser is just off again
                if scanline == self.QUEUE_LEN and takepicture:
                    self.camera.get_spotinfo(wait=False)
                extra_data = list(line_data[scanline*self.SCANLINE_DATA_SIZE
                    :(scanline+1)*self.SCANLINE_DATA_SIZE])
                if counter == 0:
                    write_data = ([self.COMMANDS.inv['CMD_SCAN_DATA']] 
                    + extra_data)
                else:
                    write_data = ([self.COMMANDS.inv['CMD_SCAN_DATA_NO_SLED']] 
                    + extra_data)
                self.pruss.core0.dram.write(write_data, offset = byte)
                byte += self.SCANLINE_ITEM_SIZE
                if byte > self.SCANLINE_DATA_SIZE * self.QUEUE_LEN:
                    byte = self.START_RINGBUFFER

        if takepicture: 
            return self.camera.get_answer()
        
        return byte
Beispiel #7
0
class MainWindow(QtWidgets.QMainWindow):

    pruss = Icss("/dev/uio/pruss/module")
    ui = Ui_MainWindow()
    core_0 = pruss.core0
    core_1 = pruss.core1
    period = 10000
    position = None
    weight = None
    pen_weight = None
    pen_position = None
    timer = QTimer()
    position_line = None
    pruvars = None
    weight_line = None
    time_array = [0]
    position_array = [0]
    weight_array = [0]
    motor = Pwmss("/dev/uio/pwmss0").pwm
    maxloadposition = None
    minloadposition = None
    _running = None
    _last_timestamp_cycles = 0
    pru_thread = 0
    zero_offset = 0

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)
        self.ui.setupUi(self)
        self.ui.bttn_close.clicked.connect(self.CloseButton_on_click)
        self.ui.bttn_captureZero.clicked.connect(
            self.CaptureZeroButton_on_click)
        self.ui.bttn_setSpan.clicked.connect(self.SetSpanButton_on_click)
        self.ui.bttn_tareScale.clicked.connect(self.TareScaleButton_on_click)
        self.ui.bttn_pollSensors.clicked.connect(
            self.PollSensorsButton_on_click)
        self.ui.bttn_start.clicked.connect(self.StartButton_on_click)
        self.ui.bttn_reset.clicked.connect(self.ResetButton_on_click)
        self.ui.bttn_resetTare.clicked.connect(self.ResetTareButton_on_click)
        self.ui.bttn_systemZero.clicked.connect(self.SystemZeroButton_on_click)
        self.ui.bttn_stopRun.clicked.connect(self.StopRunButton_on_click)
        self.ui.bttn_setTopBottom.clicked.connect(self.SetTopBottom_on_click)
        self.ui.hs_motorControl.valueChanged.connect(
            self.MotorControlSlider_on_valuechange)
        self.ui.rb_forward.toggled.connect(
            lambda: self.FwdRadioButton_on_click(self.motor.ld_compare_a, self.
                                                 motor.ld_compare_b))
        self.ui.rb_reverse.toggled.connect(
            lambda: self.RevRadioButton_on_click(self.motor.ld_compare_a, self.
                                                 motor.ld_compare_b))
        self.ui.bttn_stopMotor.clicked.connect(self.StopMotorButton_on_click)
        self.ui.bttn_goTop.clicked.connect(self.GoTopButton_on_click)
        self.ui.bttn_goBottom.clicked.connect(self.GoBottomButton_on_click)
        self.ui.rb_forward.setChecked(True)
        self.ui.hs_motorControl.setMaximum(self.period)
        self.ui.hs_motorControl.setMinimum(0)
        self.ui.hs_motorControl.setSingleStep(1)
        self.ui.hs_motorControl.setValue(1)
        self.ui.tb_dutyCycle.setText("0%")
        self.ui.lcd_timer.setDigitCount(9)

        # setup plot
        pen_weight = pg.mkPen(color=(255, 255, 0), width=5)
        pen_position = pg.mkPen(color=(0, 0, 255), width=5)
        styles = {'color': 'y', 'font-size': '25px'}
        self.ui.graph.setLabel('left', 'Weight (g)', **styles)
        self.ui.graph.setLabel('bottom', 'Time (s)', **styles)
        self.position_line = self.ui.graph.plot([0], [0], pen=pen_position)
        self.weight_line = self.ui.graph.plot([0], [0], pen=pen_weight)

        #setup_pruss
        self.pruss.uart.initialize(baudrate=460800)
        self.core_0.load('decoder.bin')
        self.core_0.run()
        self.position = self.pruss.dram0.map(uint32)
        self.pruvars = self.pruss.dram2.map(PruVars)
        self._Init_EM100()
        self.motor.initialize(self.period, 1)

    def timestamp_cycles(self):
        """Returns the current timestamp in PRU cycles (integer)"""
        self._last_timestamp_cycles += (
            self.pruss.ecap.counter - self._last_timestamp_cycles) & 0xffffffff
        return self._last_timestamp_cycles

    def timestamp_ns(self):
        """Returns the current timestamp in nanoseconds (integer)"""
        return self.timestamp_cycles() * 5

    def timestamp_seconds(self):
        """Returns the current timestamp in seconds (floating-point)"""
        return self.timestamp_cycles() / 200e6

    def _TrimMessage(self, message, start, finish):
        return message[start - 1:finish - 1]

    def _Init_EM100(self):
        # set max value
        #self.pruss.uart.io.write(b'CM 1\r')
        #self.pruss.uart.io.readline(newline =b'\r')
        #self._OpenCalibration()
        #self.pruss.uart.io.write(bytes('CM 1 999999\r','ascii'))
        #self.pruss.uart.io.readline(newline =b'\r')

        # set min value
        #self.pruss.uart.io.write(b'CI 1\r')
        #self.pruss.uart.io.readline(newline =b'\r')
        #self._OpenCalibration()
        #self.pruss.uart.io.write(bytes('CI -999999\r','ascii'))
        #self.pruss.uart.io.readline(newline =b'\r')

        # set output to grams
        #self.pruss.uart.io.write(b'DP\r')
        #self.pruss.uart.io.readline(newline =b'\r')
        #self._OpenCalibration()
        #self.pruss.uart.io.write(bytes('DP 0\r','ascii'))
        #self.pruss.uart.io.readline(newline =b'\r')
        pass

    def _ChangeDirection(self):
        if (self.ui.rb_forward.isChecked()):
            self.ui.rb_reverse.setChecked(True)
            #self.RevRadioButton_on_click(self.motor.ld_compare_a,self.motor.ld_compare_b)
        else:
            self.ui.rb_forward.setChecked(True)
            #self.FwdRadioButton_on_click(self.motor.ld_compare_a,self.motor.ld_compare_b)

    def _OpenCalibration(self):
        self.pruss.uart.io.write(b'CE\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        calib_num = int(self._TrimMessage(response, 3, len(response)))
        self.pruss.uart.io.write(bytes('CE ' + str(calib_num) + '\r', 'ascii'))
        response = self.pruss.uart.io.readline(newline=b'\r').decode("ascii")
        #QMessageBox.about(self, "Response", response)

    def _UpdatePlot(self, time, weight, position):

        if len(self.time_array) > 100:
            self.time_array = self.time_array[
                1:]  # Remove the first y element.
            self.position_array = self.position_array[1:]
            self.weight_array = self.weight_array[1:]

        self.time_array.append(time)  # Add a new value 1 higher than the last.
        self.position_array.append(position)  # Add a new random value.
        self.weight_array.append(weight)

        self.position_line.setData(self.time_array, self.position_array)
        self.weight_line.setData(self.time_array, self.weight_array)

    def _UpdateGui(self, weight, position):
        time_point = self.timestamp_seconds()
        mm = int(self.timestamp_seconds() / 60)
        ss = int(time_point % 60)
        ms = int((time_point % 1) * 1000)
        time = '{}:{}:{}'.format(mm, ss, ms)
        angle = self._ConvertToAngle(position)
        if (angle >= 180):
            angle = 360 - angle
        self._UpdatePlot(time_point, weight, angle)
        print("{} {} {}".format(time, angle, weight))
        self.ui.lcd_timer.display(time)
        self.ui.lcd_weight.display(weight)
        self.ui.lcd_position.display(angle)

    def _ConvertToAngle(self, pos):
        p = float((pos + 1) % 1440)
        a = float(p * 360 / 1440)
        return a

    def _MotorGoToAngle(self, target):

        current_angle = self._ConvertToAngle(self.position.value)

        if (abs(target - current_angle) >= 180):
            self.motor.ld_compare_a = int(self.period / 8)
            self.motor.ld_compare_b = 0
        else:
            self.motor.ld_compare_a = 0
            self.motor.ld_compare_b = int(self.period / 8)

        while (abs(current_angle - target) > 0.001):
            sleep(0.001)
            togo = abs(target - current_angle)
            current_angle = self._ConvertToAngle(self.position.value)
        self.motor.ld_compare_a = 0
        self.motor.ld_compare_b = 0

    @pyqtSlot()
    def CloseButton_on_click(self):
        self.close()

    @pyqtSlot()
    def SetSpanButton_on_click(self):
        self.setEnabled(False)
        self.spanvalue = NumPadPopup()
        self.spanvalue.show()
        self.spanvalue.exec_()
        self.ui.tb_span.setText(str(self.spanvalue.result))
        self.setEnabled(True)
        span = int(self.ui.tb_span.text())
        self.pruss.uart.io.write(b'CG\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        self._OpenCalibration()
        self.pruss.uart.io.write(bytes('CG ' + str(span) + '\r', 'ascii'))
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        self._OpenCalibration()
        self.pruss.uart.io.write(b'CS\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        QMessageBox.about(self, "Response", response)

    @pyqtSlot()
    def SetTopBottom_on_click(self):
        self.motor.ld_compare_a = int(self.period / 8)
        self.motor.ld_compare_b = 0
        self.core_1.load('find_max.out')
        self.core_1.run()
        counter = 0
        max_weight = 0
        max_position = 0
        prev_weight = 0
        max_counter = 0
        desc_counter = 0
        while (1):
            angle = self._ConvertToAngle(self.pruvars.position)
            weight = self.pruvars.force
            print("Counter {} Max_Counter {} Descreasing Counter {}".format(
                counter, max_counter, desc_counter))
            if (weight >= prev_weight):
                desc_counter = 0
                print("weight increaseing new max weight set")
                if (weight > max_weight):
                    max_weight = weight
                    max_position = angle
                    max_counter = 0
                else:
                    print("weight increasing but below max weight")
                    max_counter = max_counter + 1
            else:
                max_counter = max_counter + 1
                if (desc_counter > 3):
                    print("Weight dscresing for too long changing direction")
                    self._ChangeDirection()
                    desc_counter = 0
                else:
                    print(
                        "Weight descrsing but not long enough to give up this way"
                    )
                    desc_counter = desc_counter + 1
            if (max_counter > 30 or counter > 500):
                break
            counter = counter + 1
            prev_weight = weight
            sleep(0.25)

        self.pruss.uart.io.write(
            b'FFV\r', discard=True,
            flush=True)  # interrupt continuous transmission
        sleep(0.1)  # make sure that response has been received
        self.pruss.uart.io.discard()  # discard response
        self.StopMotorButton_on_click()
        self.core_1.halt()
        self.ui.lcd_weight.display(self.pruvars.force)
        angle = self._ConvertToAngle(self.pruvars.position)
        self.maxloadposition = max_position
        self.minloadposition = abs(max_position - 180)
        self.GoBottomButton_on_click()
        self.zero_offset = self.position.value
        #self.ui.lcd_position.display(angle)

    @pyqtSlot()
    def GoTopButton_on_click(self):
        target_angle = self.maxloadposition
        self._MotorGoToAngle(target_angle)

    @pyqtSlot()
    def GoBottomButton_on_click(self):
        target_angle = self.minloadposition
        self._MotorGoToAngle(target_angle)

    @pyqtSlot()
    def TareScaleButton_on_click(self):
        self.pruss.uart.io.write(b'ST\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        QMessageBox.about(self, "Response", response)

    @pyqtSlot()
    def ResetTareButton_on_click(self):
        self.pruss.uart.io.write(b'RT\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        QMessageBox.about(self, "Response", response)

    @pyqtSlot()
    def SystemZeroButton_on_click(self):
        self.pruss.uart.io.write(b'SZ\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        QMessageBox.about(self, "Response", response)

    @pyqtSlot()
    def CaptureZeroButton_on_click(self):
        self._OpenCalibration()
        self.pruss.uart.io.write(b'CZ\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')

    @pyqtSlot()
    def PollSensorsButton_on_click(self):
        self.pruss.uart.io.write(b'GN\r')
        response = self.pruss.uart.io.readline(newline=b'\r').decode('ascii')
        print(response)
        weight = int(self._TrimMessage(response, 2, len(response)))
        self.ui.lcd_weight.display(weight)
        position = (self.position.value + 1) % 1440
        angle = float(position * 360 / 1440)
        self.ui.lcd_position.display(angle)

    @pyqtSlot()
    def StartButton_on_click(self):
        self.c = Communicate()
        self.core_1.load('record_run.out')
        self.last_timestamp_cycles = 0
        self.pruss.ecap.pwm.initialize(2**32)
        self.pru_thread = PollPRUThread(self.core_1, self.pruvars)
        self.pru_thread.c.newdata.connect(self._UpdateGui)
        self.pru_thread.start()

    @pyqtSlot()
    def StopMotorButton_on_click(self):
        self.motor.ld_compare_a = 0
        self.motor.ld_compare_b = 0
        self.ui.hs_motorControl.setValue(0)
        self.ui.tb_dutyCycle.setText("0%")

    @pyqtSlot()
    def FwdRadioButton_on_click(self, a, b):
        if (b > 0):
            self.motor.ld_compare_a = b
            self.motor.ld_compare_b = 0
        else:
            pass

    @pyqtSlot()
    def RevRadioButton_on_click(self, a, b):
        if (a > 0):
            self.motor.ld_compare_a = 0
            self.motor.ld_compare_b = a
        else:
            pass

    @pyqtSlot()
    def MotorControlSlider_on_valuechange(self):
        newSpeed = self.ui.hs_motorControl.value()
        if (self.ui.rb_forward.isChecked()):
            self.motor.ld_compare_a = newSpeed
            self.motor.ld_compare_b = 0
        else:
            self.motor.ld_compare_a = 0
            self.motor.ld_compare_b = newSpeed
        duty_cycle = int(newSpeed / self.period * 100)
        self.ui.tb_dutyCycle.setText("{}%".format(duty_cycle))

    @pyqtSlot()
    def ResetButton_on_click(self):
        self.ui.graph.clear()

    @pyqtSlot()
    def StopRunButton_on_click(self):
        self.pruss.uart.io.write(
            b'FFV\r', discard=True,
            flush=True)  # interrupt continuous transmission
        sleep(0.1)  # make sure that response has been received
        self.pruss.uart.io.discard()  # discard response
        print("stopping")
        self.pru_thread.quit()
        self.core_1.halt()