From c0e5822abbc1d496315a5817ad79b67bf75d79d0 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 11:35:48 +0100 Subject: [PATCH 01/50] Add files via upload --- sensor_ver5.py | 259 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 sensor_ver5.py diff --git a/sensor_ver5.py b/sensor_ver5.py new file mode 100644 index 0000000..5f9834b --- /dev/null +++ b/sensor_ver5.py @@ -0,0 +1,259 @@ +import time, math, gc +from array import array +from machine import Pin, SPI +from pyControl.hardware import * +''' +Changes: PAA5100JE does not have SROM ID, need to change way to interrupt queue +''' +def twos_comp(val, bits=16): + """compute the 2's complement of int value val""" + if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255 + val = val - (1 << bits) # compute negative value + return val # return positive value as is + + +class PAA5100JE(): + def __init__(self, + SPI_type: str, + reset: str = None, + cs: str = None, + sck: str = None, + mosi: str = None, + miso: str = None): + + # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' + SPIparams = {'baudrate': 9600, 'polarity': 0, 'phase': 1, + 'bits': 8, 'firstbit': machine.SPI.MSB} + if '1' in SPI_type: + self.spi = machine.SPI(1, **SPIparams) + + elif '2' in SPI_type: + self.spi = machine.SPI(2, **SPIparams) + + elif 'soft' in SPI_type.lower(): # Works for newer versions of micropython + self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + miso=machine.Pin(miso, mode=machine.Pin.IN), + **SPIparams + ) + + self.select = Digital_output(pin=CS, inverted=True) + self.reset = Digital_output(pin=reset, inverted=True) + + self.reset.off() + self.select.off() + + time.sleep_ms(50) # Motion delay after reset + + def read_register(self, addrs: int): + """ + addrs < 128 + """ + # ensure MSB=0 + addrs = addrs & 0x7f + addrs = addrs.to_bytes(1, 'little') + self.select.on() + self.spi.write(addrs) + time.sleep_us(2) # tSRAD + data = self.spi.read(1) + time.sleep_us(1) # tSCLK-NCS for read operation is 120ns + self.select.off() + time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS + return data + + def write_register(self, addrs: int, data: int): + """ + addrs < 128 + """ + # flip the MSB to 1: + addrs = addrs | 0x80 + addrs = addrs.to_bytes(1, 'little') + data = data.to_bytes(1, 'little') + self.select.on() + self.spi.write(addrs) + self.spi.write(data) + time.sleep_us(20) # tSCLK-NCS for write operation + self.select.off() + time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + + def send_values(self, values): + self.select.on() # Select the device + for value in values: + self.spi.write(value.to_bytes(2, 'big')) # Sending each value as 2 bytes + self.select.off() # Deselect the device + + def read_pos(self): + # write and read Motion register to lock the content of delta registers + self.write_register(0x02, 0x01) + self.read_register(0x02) + + delta_x_L = self.read_register(0x03) + delta_x_H = self.read_register(0x04) + delta_y_L = self.read_register(0x05) + delta_y_H = self.read_register(0x06) + + delta_x = delta_x_H + delta_x_L + delta_y = delta_y_H + delta_y_L + + delta_x = int.from_bytes(delta_x, 'little') + delta_y = int.from_bytes(delta_y, 'little') + + delta_x = twos_comp(delta_x) + delta_y = twos_comp(delta_y) + + return delta_x, delta_y + + def config(self): + # Need ID for interrupting queue when sending data to computer + self.select.value(0) + ID = self.flo.get_id() + time.sleep_us(50) + self.select.value(1) + time.sleep_ms(1) + + def shut_down(self, deinitSPI=True): + self.select.off() + time.sleep_ms(1) + self.select.on() + self.reset.off() + time.sleep_ms(60) + self.read_motion() + self.select.off() + time.sleep_ms(1) + if deinitSPI: + self.spi.deinit() + +class MotionDetector(): + def __init__(self, reset, cs1, cs2, + name='MotDet', threshold=1, calib_coef=1, + sampling_rate=100, event='motion'): + + # Create SPI objects + self.motSen_x = PAA5100JE('SPI2', reset, cs1) + self.motSen_y = PAA5100JE('SPI2', reset, cs2) + + self.motSen_x.config() + self.motSen_y.config() + print(f"Sensor X ID: {self.motSen_x.config()} | Sensor Y ID: {self.motSen_y.config()}") + + self._threshold = threshold + self.calib_coef = calib_coef + + # Motion sensor variables + self.x_buffer = bytearray(2) + self.y_buffer = bytearray(2) + self.x_buffer_mv = memoryview(self.x_buffer) + self.y_buffer_mv = memoryview(self.y_buffer) + self.delta_x_l = self.x_buffer_mv[0:1] + self.delta_x_h = self.x_buffer_mv[1:] + self.delta_y_l = self.y_buffer_mv[0:1] + self.delta_y_h = self.y_buffer_mv[1:] + + self.delta_x, self.delta_y = 0, 0 # accumulated position + self._delta_x, self._delta_y = 0, 0 # instantaneous position + self.x, self.y = 0, 0 # to be accessed from the task, unit=mm + + # Parent + Analog_input.__init__(self, pin=None, name=name + '-X', sampling_rate=int(sampling_rate), + threshold=threshold, rising_event=event, falling_event=None, + data_type='l') + self.data_chx = self.data_channel + self.data_chy = Data_channel(name + '-Y', sampling_rate, data_type='l') + self.crossing_direction = True # to conform to the Analog_input syntax + self.timestamp = fw.current_time + self.acquiring = False + + gc.collect() + time.sleep_ms(2) + + @property + def threshold(self): + # return the value in cms + return math.sqrt(self._threshold / 10) + + @threshold.setter + def threshold(self, new_threshold): + self._threshold = int((new_threshold)**2) * self.calib_coef + self.reset_delta() + + def reset_delta(self): + # reset the accumulated position data + self.delta_x, self.delta_y = 0, 0 + + def read_sample(self): + self.motSen_y.write_register_buff(b'\x82', b'\x01') + self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) + self._delta_y = twos_comp(int.from_bytes(self.y_buffer_mv, 'little')) + + self.motSen_x.write_register_buff(b'\x82', b'\x01') + self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) + self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) + self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) + self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) + self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) + self._delta_x = twos_comp(int.from_bytes(self.x_buffer_mv, 'little')) + + self.delta_y += self._delta_y + self.delta_x += self._delta_x + + disp = self.displacement() + + print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") + + def displacement(self): + # Calculate displacement using the change of coordinates with time + disp = math.sqrt(self._delta_x**2 + self._delta_y**2) + return disp + + def tilt_angle(self): + # Calculate tilt angle with respect to positive y axis (in degrees) + if self.delta_y == 0: + return 0 + angle = math.atan2(self.delta_x, self.delta_y) * 180 / math.pi + return angle + + def _timer_ISR(self, t): + # Read a sample to the buffer, update write index. + self.read_sample() + self.data_chx.put(self._delta_x) + self.data_chy.put(self._delta_y) + + if self.delta_x**2 + self.delta_y**2 >= self._threshold: + self.x = self.delta_x + self.y = self.delta_y + self.reset_delta() + self.timestamp = fw.current_time + interrupt_queue.put(self.ID) ### no self.ID (use timer/GPIO interrupt pin) + + def _stop_acquisition(self): + # Stop sampling analog input values. + self.timer.deinit() + self.data_chx.stop() + self.data_chy.stop() + self.motSen_x.shut_down(deinitSPI=False) + self.motSen_y.shut_down() + self.acquiring = False + self.reset_delta() + + def _start_acquisition(self): + # Start sampling analog input values. + self.timer.init(freq=self.data_chx.sampling_rate) + self.timer.callback(self._timer_ISR) + self.acquiring = True + + def record(self): + # Start streaming data to computer. + self.data_chx.record() + self.data_chy.record() + if not self.acquiring: + self._start_acquisition() + +# -------------------------------------------------------- +if __name__ == "__main__": + motion_sensor = MotionDetector(2, 17, 9) + while True: + motion_sensor.read_sample() \ No newline at end of file From 820e8317bd00940157f4036535dfa0716eae5fbd Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 11:38:44 +0100 Subject: [PATCH 02/50] Update and rename sensor_ver5.py to PAA5100JE.py --- sensor_ver5.py => PAA5100JE.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename sensor_ver5.py => PAA5100JE.py (96%) diff --git a/sensor_ver5.py b/PAA5100JE.py similarity index 96% rename from sensor_ver5.py rename to PAA5100JE.py index 5f9834b..8ec2c77 100644 --- a/sensor_ver5.py +++ b/PAA5100JE.py @@ -256,4 +256,4 @@ def record(self): if __name__ == "__main__": motion_sensor = MotionDetector(2, 17, 9) while True: - motion_sensor.read_sample() \ No newline at end of file + motion_sensor.read_sample() From 8de7d29ca4354bf263b0fa24f5ee2d6211ed7a6d Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 11:41:41 +0100 Subject: [PATCH 03/50] Update PAA5100JE.py --- PAA5100JE.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PAA5100JE.py b/PAA5100JE.py index 8ec2c77..fc3230b 100644 --- a/PAA5100JE.py +++ b/PAA5100JE.py @@ -106,7 +106,7 @@ def read_pos(self): def config(self): # Need ID for interrupting queue when sending data to computer self.select.value(0) - ID = self.flo.get_id() + ID = self.register(0x01) time.sleep_us(50) self.select.value(1) time.sleep_ms(1) From c3493b1a2ff5d5e304d31c364a69300471e97d69 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 12:18:56 +0100 Subject: [PATCH 04/50] Update PAA5100JE.py --- PAA5100JE.py | 54 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/PAA5100JE.py b/PAA5100JE.py index fc3230b..effae11 100644 --- a/PAA5100JE.py +++ b/PAA5100JE.py @@ -11,7 +11,6 @@ def twos_comp(val, bits=16): val = val - (1 << bits) # compute negative value return val # return positive value as is - class PAA5100JE(): def __init__(self, SPI_type: str, @@ -37,7 +36,7 @@ def __init__(self, **SPIparams ) - self.select = Digital_output(pin=CS, inverted=True) + self.select = Digital_output(pin=cs, inverted=True) self.reset = Digital_output(pin=reset, inverted=True) self.reset.off() @@ -102,15 +101,31 @@ def read_pos(self): delta_y = twos_comp(delta_y) return delta_x, delta_y - - def config(self): - # Need ID for interrupting queue when sending data to computer - self.select.value(0) - ID = self.register(0x01) - time.sleep_us(50) - self.select.value(1) - time.sleep_ms(1) + def read_register_buff(self, addrs: bytes, buff: bytes): + """ + addrs < 128 + """ + self.select.on() + self.spi.write(addrs) + utime.sleep_us(100) # tSRAD + self.spi.readinto(buff) + utime.sleep_us(1) # tSCLK-NCS for read operation is 120ns + self.select.off() + utime.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS + + def write_register_buff(self, addrs: bytes, data: bytes): + """ + addrs < 128 + """ + # flip the MSB to 1:... + self.select.on() + self.spi.write(addrs) + self.spi.write(data) + utime.sleep_us(20) # tSCLK-NCS for write operation + self.select.off() + utime.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + def shut_down(self, deinitSPI=True): self.select.off() time.sleep_ms(1) @@ -123,7 +138,7 @@ def shut_down(self, deinitSPI=True): if deinitSPI: self.spi.deinit() -class MotionDetector(): +class MotionDetector(Analog_input): def __init__(self, reset, cs1, cs2, name='MotDet', threshold=1, calib_coef=1, sampling_rate=100, event='motion'): @@ -131,10 +146,6 @@ def __init__(self, reset, cs1, cs2, # Create SPI objects self.motSen_x = PAA5100JE('SPI2', reset, cs1) self.motSen_y = PAA5100JE('SPI2', reset, cs2) - - self.motSen_x.config() - self.motSen_y.config() - print(f"Sensor X ID: {self.motSen_x.config()} | Sensor Y ID: {self.motSen_y.config()}") self._threshold = threshold self.calib_coef = calib_coef @@ -169,12 +180,7 @@ def __init__(self, reset, cs1, cs2, @property def threshold(self): # return the value in cms - return math.sqrt(self._threshold / 10) - - @threshold.setter - def threshold(self, new_threshold): - self._threshold = int((new_threshold)**2) * self.calib_coef - self.reset_delta() + return math.sqrt((self._threshold**2)* self.calib_coef / 10) def reset_delta(self): # reset the accumulated position data @@ -251,9 +257,3 @@ def record(self): self.data_chy.record() if not self.acquiring: self._start_acquisition() - -# -------------------------------------------------------- -if __name__ == "__main__": - motion_sensor = MotionDetector(2, 17, 9) - while True: - motion_sensor.read_sample() From 8040f8763a99b61d677d88e3ccdb1964cbbd07b9 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 13:10:44 +0100 Subject: [PATCH 05/50] Add files via upload --- devices/sensor_ver5.py | 265 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 devices/sensor_ver5.py diff --git a/devices/sensor_ver5.py b/devices/sensor_ver5.py new file mode 100644 index 0000000..893aa61 --- /dev/null +++ b/devices/sensor_ver5.py @@ -0,0 +1,265 @@ +import time, math, gc +from array import array +from machine import Pin, SPI +from pyControl.hardware import * +''' +Changes: PAA5100JE does not have SROM ID, need to change way to interrupt queue +''' +def twos_comp(val, bits=16): + """compute the 2's complement of int value val""" + if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255 + val = val - (1 << bits) # compute negative value + return val # return positive value as is + +class PAA5100JE(): + def __init__(self, + SPI_type: str, + reset: str = None, + cs: str = None, + sck: str = None, + mosi: str = None, + miso: str = None): + + # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' + SPIparams = {'baudrate': 9600, 'polarity': 0, 'phase': 1, + 'bits': 8, 'firstbit': machine.SPI.MSB} + if '1' in SPI_type: + self.spi = machine.SPI(1, **SPIparams) + + elif '2' in SPI_type: + self.spi = machine.SPI(2, **SPIparams) + + elif 'soft' in SPI_type.lower(): # Works for newer versions of micropython + self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + miso=machine.Pin(miso, mode=machine.Pin.IN), + **SPIparams + ) + + self.select = Digital_output(pin=cs, inverted=True) + self.reset = Digital_output(pin=reset, inverted=True) + + self.reset.off() + self.select.off() + + time.sleep_ms(50) # Motion delay after reset + + def read_register(self, addrs: int): + """ + addrs < 128 + """ + # ensure MSB=0 + addrs = addrs & 0x7f + addrs = addrs.to_bytes(1, 'little') + self.select.on() + self.spi.write(addrs) + time.sleep_us(2) # tSRAD + data = self.spi.read(1) + time.sleep_us(1) # tSCLK-NCS for read operation is 120ns + self.select.off() + time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS + return data + + def write_register(self, addrs: int, data: int): + """ + addrs < 128 + """ + # flip the MSB to 1: + addrs = addrs | 0x80 + addrs = addrs.to_bytes(1, 'little') + data = data.to_bytes(1, 'little') + self.select.on() + self.spi.write(addrs) + self.spi.write(data) + time.sleep_us(20) # tSCLK-NCS for write operation + self.select.off() + time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + + def send_values(self, values): + self.select.on() # Select the device + for value in values: + self.spi.write(value.to_bytes(2, 'big')) # Sending each value as 2 bytes + self.select.off() # Deselect the device + + def read_pos(self): + # write and read Motion register to lock the content of delta registers + self.write_register(0x02, 0x01) + self.read_register(0x02) + + delta_x_L = self.read_register(0x03) + delta_x_H = self.read_register(0x04) + delta_y_L = self.read_register(0x05) + delta_y_H = self.read_register(0x06) + + delta_x = delta_x_H + delta_x_L + delta_y = delta_y_H + delta_y_L + + delta_x = int.from_bytes(delta_x, 'little') + delta_y = int.from_bytes(delta_y, 'little') + + delta_x = twos_comp(delta_x) + delta_y = twos_comp(delta_y) + + return delta_x, delta_y + + def read_register_buff(self, addrs: bytes, buff: bytes): + """ + addrs < 128 + """ + self.select.on() + self.spi.write(addrs) + utime.sleep_us(100) # tSRAD + self.spi.readinto(buff) + utime.sleep_us(1) # tSCLK-NCS for read operation is 120ns + self.select.off() + utime.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS + + def write_register_buff(self, addrs: bytes, data: bytes): + """ + addrs < 128 + """ + # flip the MSB to 1:... + self.select.on() + self.spi.write(addrs) + self.spi.write(data) + utime.sleep_us(20) # tSCLK-NCS for write operation + self.select.off() + utime.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + + def shut_down(self, deinitSPI=True): + self.select.off() + time.sleep_ms(1) + self.select.on() + self.reset.off() + time.sleep_ms(60) + self.read_motion() + self.select.off() + time.sleep_ms(1) + if deinitSPI: + self.spi.deinit() + +class MotionDetector(Analog_input): + def __init__(self, reset, cs1, cs2, + name='MotDet', threshold=1, calib_coef=1, + sampling_rate=100, event='motion'): + + # Create SPI objects + self.motSen_x = PAA5100JE('SPI2', reset, cs1) + self.motSen_y = PAA5100JE('SPI2', reset, cs2) + + self._threshold = threshold + self.calib_coef = calib_coef + + # Motion sensor variables + self.x_buffer = bytearray(2) + self.y_buffer = bytearray(2) + self.x_buffer_mv = memoryview(self.x_buffer) + self.y_buffer_mv = memoryview(self.y_buffer) + self.delta_x_l = self.x_buffer_mv[0:1] + self.delta_x_h = self.x_buffer_mv[1:] + self.delta_y_l = self.y_buffer_mv[0:1] + self.delta_y_h = self.y_buffer_mv[1:] + + self.delta_x, self.delta_y = 0, 0 # accumulated position + self._delta_x, self._delta_y = 0, 0 # instantaneous position + self.x, self.y = 0, 0 # to be accessed from the task, unit=mm + + # Parent + Analog_input.__init__(self, pin=None, name=name + '-X', sampling_rate=int(sampling_rate), + threshold=threshold, rising_event=event, falling_event=None, + data_type='l') + self.data_chx = self.data_channel + self.data_chy = Data_channel(name + '-Y', sampling_rate, data_type='l') + self.crossing_direction = True # to conform to the Analog_input syntax + self.timestamp = fw.current_time + self.acquiring = False + + gc.collect() + time.sleep_ms(2) + + @property + def threshold(self): + # return the value in cms + return math.sqrt((self._threshold**2)* self.calib_coef / 10) + + def reset_delta(self): + # reset the accumulated position data + self.delta_x, self.delta_y = 0, 0 + + def read_sample(self): + self.motSen_y.write_register_buff(b'\x82', b'\x01') + self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) + self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) + self._delta_y = twos_comp(int.from_bytes(self.y_buffer_mv, 'little')) + + self.motSen_x.write_register_buff(b'\x82', b'\x01') + self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) + self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) + self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) + self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) + self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) + self._delta_x = twos_comp(int.from_bytes(self.x_buffer_mv, 'little')) + + self.delta_y += self._delta_y + self.delta_x += self._delta_x + + disp = self.displacement() + + print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") + + def displacement(self): + # Calculate displacement using the change of coordinates with time + disp = math.sqrt(self._delta_x**2 + self._delta_y**2) + return disp + + def tilt_angle(self): + # Calculate tilt angle with respect to positive y axis (in degrees) + if self.delta_y == 0: + return 0 + angle = math.atan2(self.delta_x, self.delta_y) * 180 / math.pi + return angle + + def _timer_ISR(self, t): + # Read a sample to the buffer, update write index. + self.read_sample() + self.data_chx.put(self._delta_x) + self.data_chy.put(self._delta_y) + + if self.delta_x**2 + self.delta_y**2 >= self._threshold: + self.x = self.delta_x + self.y = self.delta_y + self.reset_delta() + self.timestamp = fw.current_time + interrupt_queue.put(self.ID) ### no self.ID (use timer/GPIO interrupt pin) + + def _stop_acquisition(self): + # Stop sampling analog input values. + self.timer.deinit() + self.data_chx.stop() + self.data_chy.stop() + self.motSen_x.shut_down(deinitSPI=False) + self.motSen_y.shut_down() + self.acquiring = False + self.reset_delta() + + def _start_acquisition(self): + # Start sampling analog input values. + self.timer.init(freq=self.data_chx.sampling_rate) + self.timer.callback(self._timer_ISR) + self.acquiring = True + + def record(self): + # Start streaming data to computer. + self.data_chx.record() + self.data_chy.record() + if not self.acquiring: + self._start_acquisition() + +# -------------------------------------------------------- +if __name__ == "__main__": + motion_sensor = MotionDetector(2, 17, 9) + while True: + motion_sensor.read_sample() \ No newline at end of file From 4e9ca5260a5b526be40cd7410d15b4bfd5b874cf Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 13:11:08 +0100 Subject: [PATCH 06/50] Update and rename sensor_ver5.py to PAA5100JE.py --- devices/{sensor_ver5.py => PAA5100JE.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename devices/{sensor_ver5.py => PAA5100JE.py} (97%) diff --git a/devices/sensor_ver5.py b/devices/PAA5100JE.py similarity index 97% rename from devices/sensor_ver5.py rename to devices/PAA5100JE.py index 893aa61..9ceee97 100644 --- a/devices/sensor_ver5.py +++ b/devices/PAA5100JE.py @@ -262,4 +262,4 @@ def record(self): if __name__ == "__main__": motion_sensor = MotionDetector(2, 17, 9) while True: - motion_sensor.read_sample() \ No newline at end of file + motion_sensor.read_sample() From 4e104e144eedb2310eec1fcb9963d301f81b0e4a Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 12 Aug 2024 13:12:06 +0100 Subject: [PATCH 07/50] Delete PAA5100JE.py --- PAA5100JE.py | 259 --------------------------------------------------- 1 file changed, 259 deletions(-) delete mode 100644 PAA5100JE.py diff --git a/PAA5100JE.py b/PAA5100JE.py deleted file mode 100644 index effae11..0000000 --- a/PAA5100JE.py +++ /dev/null @@ -1,259 +0,0 @@ -import time, math, gc -from array import array -from machine import Pin, SPI -from pyControl.hardware import * -''' -Changes: PAA5100JE does not have SROM ID, need to change way to interrupt queue -''' -def twos_comp(val, bits=16): - """compute the 2's complement of int value val""" - if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255 - val = val - (1 << bits) # compute negative value - return val # return positive value as is - -class PAA5100JE(): - def __init__(self, - SPI_type: str, - reset: str = None, - cs: str = None, - sck: str = None, - mosi: str = None, - miso: str = None): - - # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 9600, 'polarity': 0, 'phase': 1, - 'bits': 8, 'firstbit': machine.SPI.MSB} - if '1' in SPI_type: - self.spi = machine.SPI(1, **SPIparams) - - elif '2' in SPI_type: - self.spi = machine.SPI(2, **SPIparams) - - elif 'soft' in SPI_type.lower(): # Works for newer versions of micropython - self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - miso=machine.Pin(miso, mode=machine.Pin.IN), - **SPIparams - ) - - self.select = Digital_output(pin=cs, inverted=True) - self.reset = Digital_output(pin=reset, inverted=True) - - self.reset.off() - self.select.off() - - time.sleep_ms(50) # Motion delay after reset - - def read_register(self, addrs: int): - """ - addrs < 128 - """ - # ensure MSB=0 - addrs = addrs & 0x7f - addrs = addrs.to_bytes(1, 'little') - self.select.on() - self.spi.write(addrs) - time.sleep_us(2) # tSRAD - data = self.spi.read(1) - time.sleep_us(1) # tSCLK-NCS for read operation is 120ns - self.select.off() - time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - return data - - def write_register(self, addrs: int, data: int): - """ - addrs < 128 - """ - # flip the MSB to 1: - addrs = addrs | 0x80 - addrs = addrs.to_bytes(1, 'little') - data = data.to_bytes(1, 'little') - self.select.on() - self.spi.write(addrs) - self.spi.write(data) - time.sleep_us(20) # tSCLK-NCS for write operation - self.select.off() - time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound - - def send_values(self, values): - self.select.on() # Select the device - for value in values: - self.spi.write(value.to_bytes(2, 'big')) # Sending each value as 2 bytes - self.select.off() # Deselect the device - - def read_pos(self): - # write and read Motion register to lock the content of delta registers - self.write_register(0x02, 0x01) - self.read_register(0x02) - - delta_x_L = self.read_register(0x03) - delta_x_H = self.read_register(0x04) - delta_y_L = self.read_register(0x05) - delta_y_H = self.read_register(0x06) - - delta_x = delta_x_H + delta_x_L - delta_y = delta_y_H + delta_y_L - - delta_x = int.from_bytes(delta_x, 'little') - delta_y = int.from_bytes(delta_y, 'little') - - delta_x = twos_comp(delta_x) - delta_y = twos_comp(delta_y) - - return delta_x, delta_y - - def read_register_buff(self, addrs: bytes, buff: bytes): - """ - addrs < 128 - """ - self.select.on() - self.spi.write(addrs) - utime.sleep_us(100) # tSRAD - self.spi.readinto(buff) - utime.sleep_us(1) # tSCLK-NCS for read operation is 120ns - self.select.off() - utime.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - - def write_register_buff(self, addrs: bytes, data: bytes): - """ - addrs < 128 - """ - # flip the MSB to 1:... - self.select.on() - self.spi.write(addrs) - self.spi.write(data) - utime.sleep_us(20) # tSCLK-NCS for write operation - self.select.off() - utime.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound - - def shut_down(self, deinitSPI=True): - self.select.off() - time.sleep_ms(1) - self.select.on() - self.reset.off() - time.sleep_ms(60) - self.read_motion() - self.select.off() - time.sleep_ms(1) - if deinitSPI: - self.spi.deinit() - -class MotionDetector(Analog_input): - def __init__(self, reset, cs1, cs2, - name='MotDet', threshold=1, calib_coef=1, - sampling_rate=100, event='motion'): - - # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', reset, cs1) - self.motSen_y = PAA5100JE('SPI2', reset, cs2) - - self._threshold = threshold - self.calib_coef = calib_coef - - # Motion sensor variables - self.x_buffer = bytearray(2) - self.y_buffer = bytearray(2) - self.x_buffer_mv = memoryview(self.x_buffer) - self.y_buffer_mv = memoryview(self.y_buffer) - self.delta_x_l = self.x_buffer_mv[0:1] - self.delta_x_h = self.x_buffer_mv[1:] - self.delta_y_l = self.y_buffer_mv[0:1] - self.delta_y_h = self.y_buffer_mv[1:] - - self.delta_x, self.delta_y = 0, 0 # accumulated position - self._delta_x, self._delta_y = 0, 0 # instantaneous position - self.x, self.y = 0, 0 # to be accessed from the task, unit=mm - - # Parent - Analog_input.__init__(self, pin=None, name=name + '-X', sampling_rate=int(sampling_rate), - threshold=threshold, rising_event=event, falling_event=None, - data_type='l') - self.data_chx = self.data_channel - self.data_chy = Data_channel(name + '-Y', sampling_rate, data_type='l') - self.crossing_direction = True # to conform to the Analog_input syntax - self.timestamp = fw.current_time - self.acquiring = False - - gc.collect() - time.sleep_ms(2) - - @property - def threshold(self): - # return the value in cms - return math.sqrt((self._threshold**2)* self.calib_coef / 10) - - def reset_delta(self): - # reset the accumulated position data - self.delta_x, self.delta_y = 0, 0 - - def read_sample(self): - self.motSen_y.write_register_buff(b'\x82', b'\x01') - self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) - self._delta_y = twos_comp(int.from_bytes(self.y_buffer_mv, 'little')) - - self.motSen_x.write_register_buff(b'\x82', b'\x01') - self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) - self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) - self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) - self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) - self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) - self._delta_x = twos_comp(int.from_bytes(self.x_buffer_mv, 'little')) - - self.delta_y += self._delta_y - self.delta_x += self._delta_x - - disp = self.displacement() - - print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") - - def displacement(self): - # Calculate displacement using the change of coordinates with time - disp = math.sqrt(self._delta_x**2 + self._delta_y**2) - return disp - - def tilt_angle(self): - # Calculate tilt angle with respect to positive y axis (in degrees) - if self.delta_y == 0: - return 0 - angle = math.atan2(self.delta_x, self.delta_y) * 180 / math.pi - return angle - - def _timer_ISR(self, t): - # Read a sample to the buffer, update write index. - self.read_sample() - self.data_chx.put(self._delta_x) - self.data_chy.put(self._delta_y) - - if self.delta_x**2 + self.delta_y**2 >= self._threshold: - self.x = self.delta_x - self.y = self.delta_y - self.reset_delta() - self.timestamp = fw.current_time - interrupt_queue.put(self.ID) ### no self.ID (use timer/GPIO interrupt pin) - - def _stop_acquisition(self): - # Stop sampling analog input values. - self.timer.deinit() - self.data_chx.stop() - self.data_chy.stop() - self.motSen_x.shut_down(deinitSPI=False) - self.motSen_y.shut_down() - self.acquiring = False - self.reset_delta() - - def _start_acquisition(self): - # Start sampling analog input values. - self.timer.init(freq=self.data_chx.sampling_rate) - self.timer.callback(self._timer_ISR) - self.acquiring = True - - def record(self): - # Start streaming data to computer. - self.data_chx.record() - self.data_chy.record() - if not self.acquiring: - self._start_acquisition() From 5e7517bdba6e08da8929c7f92d625976cb306c00 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 20 Aug 2024 16:04:30 +0100 Subject: [PATCH 08/50] Update PAA5100JE.py Version updated with no external library required; can put init_registers function to another file to reduce some lines of codes --- devices/PAA5100JE.py | 482 ++++++++++++++++++++++++++++--------------- 1 file changed, 316 insertions(+), 166 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 9ceee97..2599857 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -1,142 +1,295 @@ -import time, math, gc +import time, gc, math from array import array from machine import Pin, SPI from pyControl.hardware import * -''' -Changes: PAA5100JE does not have SROM ID, need to change way to interrupt queue -''' -def twos_comp(val, bits=16): - """compute the 2's complement of int value val""" - if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255 - val = val - (1 << bits) # compute negative value - return val # return positive value as is + +def to_signed_16(value): + """Convert a 16-bit integer to a signed 16-bit integer.""" + if value & 0x8000: # Check if the sign bit is set + value -= 0x10000 # Convert to negative + return value + +WAIT = -1 +# PAA5100 register definitions +REG_ID = 0x00 +REG_DATA_READY = 0x02 +REG_MOTION_BURST = 0x16 +REG_POWER_UP_RESET = 0x3A +REG_ORIENTATION = 0x5B +REG_RESOLUTION = 0x4E + +REG_RAWDATA_GRAB = 0x58 +REG_RAWDATA_GRAB_STATUS = 0x59 class PAA5100JE(): - def __init__(self, - SPI_type: str, - reset: str = None, - cs: str = None, - sck: str = None, - mosi: str = None, - miso: str = None): - + def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=0, mosi=0, miso=0): + # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 9600, 'polarity': 0, 'phase': 1, + SPIparams = {'baudrate': 400000, 'polarity': 0, 'phase': 0, 'bits': 8, 'firstbit': machine.SPI.MSB} - if '1' in SPI_type: - self.spi = machine.SPI(1, **SPIparams) + if '0' in spi_port: + self.spi = SPI(0, **SPIparams) - elif '2' in SPI_type: - self.spi = machine.SPI(2, **SPIparams) + elif '1' in spi_port: + self.spi = SPI(1, **SPIparams) - elif 'soft' in SPI_type.lower(): # Works for newer versions of micropython - self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - miso=machine.Pin(miso, mode=machine.Pin.IN), + elif 'soft' in spi_port.lower(): # Works for newer versions of micropython + self.spi = SoftSPI(sck=Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + mosi=Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + miso=Pin(miso, mode=machine.Pin.IN), **SPIparams ) + + # Handle Chip Select (CS) pin + self._spi_cs_gpio = None + if spi_cs_gpio is not None: + self.select = Digital_output(pin=spi_cs_gpio, inverted=True) + self.reset = Digital_output(pin=reset, inverted=True) + self.select.off() # Deselect the device by setting CS high + self.reset.off() + time.sleep(0.05) + + # Reset the sensor + self._write(REG_POWER_UP_RESET, 0x5A) + time.sleep(0.02) + for offset in range(5): + self._read(REG_DATA_READY + offset) + + self.init_registers() + + # Validate device ID and revision + product_id = self.get_id() + if product_id != 0x49: + raise RuntimeError(f"Invalid Product ID or Revision for PAA5100: 0x{product_id:02x}") + + def get_id(self): + """Get chip ID and revision from PAA5100.""" + return self._read(REG_ID, 1) + + def set_rotation(self, degrees=0): + """Set orientation of PAA5100 in increments of 90 degrees.""" + if degrees == 0: + self.set_orientation(invert_x=True, invert_y=True, swap_xy=True) + elif degrees == 90: + self.set_orientation(invert_x=False, invert_y=True, swap_xy=False) + elif degrees == 180: + self.set_orientation(invert_x=False, invert_y=False, swap_xy=True) + elif degrees == 270: + self.set_orientation(invert_x=True, invert_y=False, swap_xy=False) + else: + raise TypeError("Degrees must be one of 0, 90, 180 or 270") + + def set_orientation(self, invert_x=True, invert_y=True, swap_xy=True): + """Set orientation of PAA5100 manually.""" + value = 0 + if swap_xy: + value |= 0b10000000 + if invert_y: + value |= 0b01000000 + if invert_x: + value |= 0b00100000 + self._write(REG_ORIENTATION, value) + + def get_motion(self): + buf = bytearray(12) + self.read_registers(REG_MOTION_BURST, buf, 12) + dr = buf[0] + x_out = to_signed_16((buf[3] << 8) | buf[2]) + y_out = to_signed_16((buf[5] << 8) | buf[4]) + quality = buf[6] + shutter_upper = buf[10] + if (dr & 0b10000000) and not ((quality < 0x19) and (shutter_upper == 0x1f)): + return x_out, y_out + else: + x_out, y_out = 0, 0 + time.sleep_ms(1) + + return x_out, y_out + + def _write(self, register, value): + if self._spi_cs_gpio: + self.select.on() + self.spi.write(bytearray([register | 0x80, value])) + if self._spi_cs_gpio: + self.select.off() + + def _read(self, register, length=1): + # Create a buffer to send (with one extra byte for the register) + send_buf = bytearray([register]) + bytearray(length) + # Create a result buffer of the same length as the send_buf + result = bytearray(len(send_buf)) - self.select = Digital_output(pin=cs, inverted=True) - self.reset = Digital_output(pin=reset, inverted=True) + if self._spi_cs_gpio: + self.select.on() + self.spi.write_readinto(send_buf, result) + if self._spi_cs_gpio: + self.select.off() - self.reset.off() - self.select.off() + # Return the read result, skipping the first byte (which corresponds to the register) + return result[1:] if length > 1 else result[1] - time.sleep_ms(50) # Motion delay after reset - - def read_register(self, addrs: int): - """ - addrs < 128 - """ - # ensure MSB=0 - addrs = addrs & 0x7f - addrs = addrs.to_bytes(1, 'little') - self.select.on() - self.spi.write(addrs) - time.sleep_us(2) # tSRAD - data = self.spi.read(1) - time.sleep_us(1) # tSCLK-NCS for read operation is 120ns - self.select.off() - time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - return data - - def write_register(self, addrs: int, data: int): - """ - addrs < 128 - """ - # flip the MSB to 1: - addrs = addrs | 0x80 - addrs = addrs.to_bytes(1, 'little') - data = data.to_bytes(1, 'little') + def _bulk_write(self, data): + for x in range(0, len(data), 2): + register, value = data[x : x + 2] + if register == WAIT: + time.sleep(value / 1000) + else: + self._write(register, value) + + def read_registers(self, reg, buf, len): self.select.on() - self.spi.write(addrs) - self.spi.write(data) - time.sleep_us(20) # tSCLK-NCS for write operation - self.select.off() - time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound - - def send_values(self, values): - self.select.on() # Select the device - for value in values: - self.spi.write(value.to_bytes(2, 'big')) # Sending each value as 2 bytes - self.select.off() # Deselect the device - - def read_pos(self): - # write and read Motion register to lock the content of delta registers - self.write_register(0x02, 0x01) - self.read_register(0x02) - - delta_x_L = self.read_register(0x03) - delta_x_H = self.read_register(0x04) - delta_y_L = self.read_register(0x05) - delta_y_H = self.read_register(0x06) - - delta_x = delta_x_H + delta_x_L - delta_y = delta_y_H + delta_y_L - - delta_x = int.from_bytes(delta_x, 'little') - delta_y = int.from_bytes(delta_y, 'little') - - delta_x = twos_comp(delta_x) - delta_y = twos_comp(delta_y) - - return delta_x, delta_y + self.spi.write(bytearray([reg])) + # Read data from the register + buf[:] = self.spi.read(len) + self.select.off() - def read_register_buff(self, addrs: bytes, buff: bytes): - """ - addrs < 128 - """ - self.select.on() - self.spi.write(addrs) - utime.sleep_us(100) # tSRAD - self.spi.readinto(buff) - utime.sleep_us(1) # tSCLK-NCS for read operation is 120ns - self.select.off() - utime.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - - def write_register_buff(self, addrs: bytes, data: bytes): - """ - addrs < 128 - """ - # flip the MSB to 1:... - self.select.on() - self.spi.write(addrs) - self.spi.write(data) - utime.sleep_us(20) # tSCLK-NCS for write operation - self.select.off() - utime.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound - - def shut_down(self, deinitSPI=True): - self.select.off() - time.sleep_ms(1) - self.select.on() - self.reset.off() - time.sleep_ms(60) - self.read_motion() - self.select.off() - time.sleep_ms(1) - if deinitSPI: - self.spi.deinit() + def init_registers(self): + self._bulk_write([ + 0x7F, 0x00, + 0x55, 0x01, + 0x50, 0x07, + + 0x7F, 0x0E, + 0x43, 0x10 + ]) + if self._read(0x67) & 0b10000000: + self._write(0x48, 0x04) + else: + self._write(0x48, 0x02) + self._bulk_write([ + 0x7F, 0x00, + 0x51, 0x7B, + 0x50, 0x00, + 0x55, 0x00, + 0x7F, 0x0E + ]) + if self._read(0x73) == 0x00: + c1 = self._read(0x70) + c2 = self._read(0x71) + if c1 <= 28: + c1 += 14 + if c1 > 28: + c1 += 11 + c1 = max(0, min(0x3F, c1)) + c2 = (c2 * 45) // 100 + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + 0x51, 0x70, + 0x7F, 0x0E + ]) + self._write(0x70, c1) + self._write(0x71, c2) + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + + 0x7F, 0x03, + 0x40, 0x00, + + 0x7F, 0x05, + 0x41, 0xB3, + 0x43, 0xF1, + 0x45, 0x14, + + 0x5F, 0x34, + 0x7B, 0x08, + 0x5E, 0x34, + 0x5B, 0x11, + 0x6D, 0x11, + 0x45, 0x17, + 0x70, 0xE5, + 0x71, 0xE5, + + 0x7F, 0x06, + 0x44, 0x1B, + 0x40, 0xBF, + 0x4E, 0x3F, + + 0x7F, 0x08, + 0x66, 0x44, + 0x65, 0x20, + 0x6A, 0x3A, + 0x61, 0x05, + 0x62, 0x05, + + 0x7F, 0x09, + 0x4F, 0xAF, + 0x5F, 0x40, + 0x48, 0x80, + 0x49, 0x80, + 0x57, 0x77, + 0x60, 0x78, + 0x61, 0x78, + 0x62, 0x08, + 0x63, 0x50, + + 0x7F, 0x0A, + 0x45, 0x60, + + 0x7F, 0x00, + 0x4D, 0x11, + 0x55, 0x80, + 0x74, 0x21, + 0x75, 0x1F, + 0x4A, 0x78, + 0x4B, 0x78, + 0x44, 0x08, + + 0x45, 0x50, + 0x64, 0xFF, + 0x65, 0x1F, + + 0x7F, 0x14, + 0x65, 0x67, + 0x66, 0x08, + 0x63, 0x70, + 0x6F, 0x1C, + + 0x7F, 0x15, + 0x48, 0x48, + + 0x7F, 0x07, + 0x41, 0x0D, + 0x43, 0x14, + 0x4B, 0x0E, + 0x45, 0x0F, + 0x44, 0x42, + 0x4C, 0x80, + + 0x7F, 0x10, + 0x5B, 0x02, + + 0x7F, 0x07, + 0x40, 0x41, + + WAIT, 0x0A, # Wait 10ms + + 0x7F, 0x00, + 0x32, 0x00, + + 0x7F, 0x07, + 0x40, 0x40, + + 0x7F, 0x06, + 0x68, 0xF0, + 0x69, 0x00, + + 0x7F, 0x0D, + 0x48, 0xC0, + 0x6F, 0xD5, + + 0x7F, 0x00, + 0x5B, 0xA0, + 0x4E, 0xA8, + 0x5A, 0x90, + 0x40, 0x80, + 0x73, 0x1F, + + WAIT, 0x0A, # Wait 10ms + + 0x73, 0x00 + ]) class MotionDetector(Analog_input): def __init__(self, reset, cs1, cs2, @@ -144,22 +297,21 @@ def __init__(self, reset, cs1, cs2, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', reset, cs1) - self.motSen_y = PAA5100JE('SPI2', reset, cs2) + ## Change to same SPI, sck, miso, and mosi + self.motSen_x = PAA5100JE('SPI0', reset, cs1, 18, 19, 16) + self.motSen_y = PAA5100JE('SPI1', reset, cs2, 10, 11, 12) self._threshold = threshold self.calib_coef = calib_coef # Motion sensor variables - self.x_buffer = bytearray(2) - self.y_buffer = bytearray(2) + self.x_buffer = array('i', [0, 0]) + self.y_buffer = array('i', [0, 0]) self.x_buffer_mv = memoryview(self.x_buffer) self.y_buffer_mv = memoryview(self.y_buffer) - self.delta_x_l = self.x_buffer_mv[0:1] - self.delta_x_h = self.x_buffer_mv[1:] - self.delta_y_l = self.y_buffer_mv[0:1] - self.delta_y_h = self.y_buffer_mv[1:] + self.prev_x = 0 + self.prev_y = 0 self.delta_x, self.delta_y = 0, 0 # accumulated position self._delta_x, self._delta_y = 0, 0 # instantaneous position self.x, self.y = 0, 0 # to be accessed from the task, unit=mm @@ -179,51 +331,44 @@ def __init__(self, reset, cs1, cs2, @property def threshold(self): - # return the value in cms - return math.sqrt((self._threshold**2)* self.calib_coef / 10) + "return the value in cms" + return math.sqrt(int(self._threshold**2) * self.calib_coef) def reset_delta(self): # reset the accumulated position data self.delta_x, self.delta_y = 0, 0 def read_sample(self): - self.motSen_y.write_register_buff(b'\x82', b'\x01') - self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) - self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) - self._delta_y = twos_comp(int.from_bytes(self.y_buffer_mv, 'little')) - - self.motSen_x.write_register_buff(b'\x82', b'\x01') - self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) - self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) - self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) - self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) - self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) - self._delta_x = twos_comp(int.from_bytes(self.x_buffer_mv, 'little')) - - self.delta_y += self._delta_y - self.delta_x += self._delta_x + # Units are in millimeters + current_motion_x = self.motSen_x.get_motion() + current_motion_y = self.motSen_y.get_motion() - disp = self.displacement() + if current_motion_x is not None and current_motion_y is not None: + self.x_buffer_mv = current_motion_x + self.y_buffer_mv = current_motion_y - print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") + self.delta_x = self.x_buffer_mv[0] + self.delta_y = self.y_buffer_mv[1] + self._delta_x = self.delta_x - self.prev_x + self._delta_y = self.delta_y - self.prev_y + + # Update previous coordinates + self.prev_x = self.delta_x + self.prev_y = self.delta_y + + disp = self.displacement() + + if self.delta_x**2 + self.delta_y**2 >= self._threshold: + print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") + def displacement(self): # Calculate displacement using the change of coordinates with time disp = math.sqrt(self._delta_x**2 + self._delta_y**2) return disp - - def tilt_angle(self): - # Calculate tilt angle with respect to positive y axis (in degrees) - if self.delta_y == 0: - return 0 - angle = math.atan2(self.delta_x, self.delta_y) * 180 / math.pi - return angle def _timer_ISR(self, t): - # Read a sample to the buffer, update write index. + "Read a sample to the buffer, update write index." self.read_sample() self.data_chx.put(self._delta_x) self.data_chy.put(self._delta_y) @@ -233,7 +378,6 @@ def _timer_ISR(self, t): self.y = self.delta_y self.reset_delta() self.timestamp = fw.current_time - interrupt_queue.put(self.ID) ### no self.ID (use timer/GPIO interrupt pin) def _stop_acquisition(self): # Stop sampling analog input values. @@ -252,7 +396,7 @@ def _start_acquisition(self): self.acquiring = True def record(self): - # Start streaming data to computer. + "Start streaming data to computer." self.data_chx.record() self.data_chy.record() if not self.acquiring: @@ -260,6 +404,12 @@ def record(self): # -------------------------------------------------------- if __name__ == "__main__": - motion_sensor = MotionDetector(2, 17, 9) - while True: - motion_sensor.read_sample() + motion_sensor = MotionDetector(2, 17, 13) + try: + while True: + try: + motion_sensor.read_sample() + except RuntimeError: + continue + except KeyboardInterrupt: + pass From e9fead74ba5362ebc856c29afbf4798dcca81fc5 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 20 Aug 2024 16:06:44 +0100 Subject: [PATCH 09/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 1 - 1 file changed, 1 deletion(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 2599857..13f7592 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -41,7 +41,6 @@ def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=0, mosi=0, m ) # Handle Chip Select (CS) pin - self._spi_cs_gpio = None if spi_cs_gpio is not None: self.select = Digital_output(pin=spi_cs_gpio, inverted=True) self.reset = Digital_output(pin=reset, inverted=True) From 257019325db7ec42d7f6ab3d3217574345643eeb Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 20 Aug 2024 16:22:02 +0100 Subject: [PATCH 10/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 149 +------------------------------------------ 1 file changed, 1 insertion(+), 148 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 13f7592..9d569c6 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -2,6 +2,7 @@ from array import array from machine import Pin, SPI from pyControl.hardware import * +from PAA5100JE_firmware import init_registers def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -142,154 +143,6 @@ def read_registers(self, reg, buf, len): buf[:] = self.spi.read(len) self.select.off() - def init_registers(self): - self._bulk_write([ - 0x7F, 0x00, - 0x55, 0x01, - 0x50, 0x07, - - 0x7F, 0x0E, - 0x43, 0x10 - ]) - if self._read(0x67) & 0b10000000: - self._write(0x48, 0x04) - else: - self._write(0x48, 0x02) - self._bulk_write([ - 0x7F, 0x00, - 0x51, 0x7B, - 0x50, 0x00, - 0x55, 0x00, - 0x7F, 0x0E - ]) - if self._read(0x73) == 0x00: - c1 = self._read(0x70) - c2 = self._read(0x71) - if c1 <= 28: - c1 += 14 - if c1 > 28: - c1 += 11 - c1 = max(0, min(0x3F, c1)) - c2 = (c2 * 45) // 100 - self._bulk_write([ - 0x7F, 0x00, - 0x61, 0xAD, - 0x51, 0x70, - 0x7F, 0x0E - ]) - self._write(0x70, c1) - self._write(0x71, c2) - self._bulk_write([ - 0x7F, 0x00, - 0x61, 0xAD, - - 0x7F, 0x03, - 0x40, 0x00, - - 0x7F, 0x05, - 0x41, 0xB3, - 0x43, 0xF1, - 0x45, 0x14, - - 0x5F, 0x34, - 0x7B, 0x08, - 0x5E, 0x34, - 0x5B, 0x11, - 0x6D, 0x11, - 0x45, 0x17, - 0x70, 0xE5, - 0x71, 0xE5, - - 0x7F, 0x06, - 0x44, 0x1B, - 0x40, 0xBF, - 0x4E, 0x3F, - - 0x7F, 0x08, - 0x66, 0x44, - 0x65, 0x20, - 0x6A, 0x3A, - 0x61, 0x05, - 0x62, 0x05, - - 0x7F, 0x09, - 0x4F, 0xAF, - 0x5F, 0x40, - 0x48, 0x80, - 0x49, 0x80, - 0x57, 0x77, - 0x60, 0x78, - 0x61, 0x78, - 0x62, 0x08, - 0x63, 0x50, - - 0x7F, 0x0A, - 0x45, 0x60, - - 0x7F, 0x00, - 0x4D, 0x11, - 0x55, 0x80, - 0x74, 0x21, - 0x75, 0x1F, - 0x4A, 0x78, - 0x4B, 0x78, - 0x44, 0x08, - - 0x45, 0x50, - 0x64, 0xFF, - 0x65, 0x1F, - - 0x7F, 0x14, - 0x65, 0x67, - 0x66, 0x08, - 0x63, 0x70, - 0x6F, 0x1C, - - 0x7F, 0x15, - 0x48, 0x48, - - 0x7F, 0x07, - 0x41, 0x0D, - 0x43, 0x14, - 0x4B, 0x0E, - 0x45, 0x0F, - 0x44, 0x42, - 0x4C, 0x80, - - 0x7F, 0x10, - 0x5B, 0x02, - - 0x7F, 0x07, - 0x40, 0x41, - - WAIT, 0x0A, # Wait 10ms - - 0x7F, 0x00, - 0x32, 0x00, - - 0x7F, 0x07, - 0x40, 0x40, - - 0x7F, 0x06, - 0x68, 0xF0, - 0x69, 0x00, - - 0x7F, 0x0D, - 0x48, 0xC0, - 0x6F, 0xD5, - - 0x7F, 0x00, - 0x5B, 0xA0, - 0x4E, 0xA8, - 0x5A, 0x90, - 0x40, 0x80, - 0x73, 0x1F, - - WAIT, 0x0A, # Wait 10ms - - 0x73, 0x00 - ]) - class MotionDetector(Analog_input): def __init__(self, reset, cs1, cs2, name='MotDet', threshold=1, calib_coef=1, From 36bf966493f8f857144e021ca8fb3aca08d1fb8f Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 20 Aug 2024 16:22:40 +0100 Subject: [PATCH 11/50] Add files via upload --- devices/PAA5100JE_firmware.py | 147 ++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 devices/PAA5100JE_firmware.py diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py new file mode 100644 index 0000000..60d261c --- /dev/null +++ b/devices/PAA5100JE_firmware.py @@ -0,0 +1,147 @@ +def init_registers(self): + self._bulk_write([ + 0x7F, 0x00, + 0x55, 0x01, + 0x50, 0x07, + + 0x7F, 0x0E, + 0x43, 0x10 + ]) + if self._read(0x67) & 0b10000000: + self._write(0x48, 0x04) + else: + self._write(0x48, 0x02) + self._bulk_write([ + 0x7F, 0x00, + 0x51, 0x7B, + 0x50, 0x00, + 0x55, 0x00, + 0x7F, 0x0E + ]) + if self._read(0x73) == 0x00: + c1 = self._read(0x70) + c2 = self._read(0x71) + if c1 <= 28: + c1 += 14 + if c1 > 28: + c1 += 11 + c1 = max(0, min(0x3F, c1)) + c2 = (c2 * 45) // 100 + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + 0x51, 0x70, + 0x7F, 0x0E + ]) + self._write(0x70, c1) + self._write(0x71, c2) + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + + 0x7F, 0x03, + 0x40, 0x00, + + 0x7F, 0x05, + 0x41, 0xB3, + 0x43, 0xF1, + 0x45, 0x14, + + 0x5F, 0x34, + 0x7B, 0x08, + 0x5E, 0x34, + 0x5B, 0x11, + 0x6D, 0x11, + 0x45, 0x17, + 0x70, 0xE5, + 0x71, 0xE5, + + 0x7F, 0x06, + 0x44, 0x1B, + 0x40, 0xBF, + 0x4E, 0x3F, + + 0x7F, 0x08, + 0x66, 0x44, + 0x65, 0x20, + 0x6A, 0x3A, + 0x61, 0x05, + 0x62, 0x05, + + 0x7F, 0x09, + 0x4F, 0xAF, + 0x5F, 0x40, + 0x48, 0x80, + 0x49, 0x80, + 0x57, 0x77, + 0x60, 0x78, + 0x61, 0x78, + 0x62, 0x08, + 0x63, 0x50, + + 0x7F, 0x0A, + 0x45, 0x60, + + 0x7F, 0x00, + 0x4D, 0x11, + 0x55, 0x80, + 0x74, 0x21, + 0x75, 0x1F, + 0x4A, 0x78, + 0x4B, 0x78, + 0x44, 0x08, + + 0x45, 0x50, + 0x64, 0xFF, + 0x65, 0x1F, + + 0x7F, 0x14, + 0x65, 0x67, + 0x66, 0x08, + 0x63, 0x70, + 0x6F, 0x1C, + + 0x7F, 0x15, + 0x48, 0x48, + + 0x7F, 0x07, + 0x41, 0x0D, + 0x43, 0x14, + 0x4B, 0x0E, + 0x45, 0x0F, + 0x44, 0x42, + 0x4C, 0x80, + + 0x7F, 0x10, + 0x5B, 0x02, + + 0x7F, 0x07, + 0x40, 0x41, + + WAIT, 0x0A, # Wait 10ms + + 0x7F, 0x00, + 0x32, 0x00, + + 0x7F, 0x07, + 0x40, 0x40, + + 0x7F, 0x06, + 0x68, 0xF0, + 0x69, 0x00, + + 0x7F, 0x0D, + 0x48, 0xC0, + 0x6F, 0xD5, + + 0x7F, 0x00, + 0x5B, 0xA0, + 0x4E, 0xA8, + 0x5A, 0x90, + 0x40, 0x80, + 0x73, 0x1F, + + WAIT, 0x0A, # Wait 10ms + + 0x73, 0x00 + ]) \ No newline at end of file From 133ed6d54b0960f87d8a00737341b957c33ad955 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 11:25:38 +0100 Subject: [PATCH 12/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 9d569c6..8f262e1 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -23,17 +23,17 @@ def to_signed_16(value): REG_RAWDATA_GRAB_STATUS = 0x59 class PAA5100JE(): - def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=0, mosi=0, miso=0): + def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=None, mosi=None, miso=None): # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 400000, 'polarity': 0, 'phase': 0, 'bits': 8, 'firstbit': machine.SPI.MSB} - if '0' in spi_port: - self.spi = SPI(0, **SPIparams) - - elif '1' in spi_port: + if '1' in spi_port: self.spi = SPI(1, **SPIparams) + elif '2' in spi_port: + self.spi = SPI(2, **SPIparams) + elif 'soft' in spi_port.lower(): # Works for newer versions of micropython self.spi = SoftSPI(sck=Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), mosi=Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), @@ -54,16 +54,17 @@ def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=0, mosi=0, m time.sleep(0.02) for offset in range(5): self._read(REG_DATA_READY + offset) + + # Initiate registers using firmware + init_registers() - self.init_registers() - - # Validate device ID and revision + # Validate device ID product_id = self.get_id() if product_id != 0x49: - raise RuntimeError(f"Invalid Product ID or Revision for PAA5100: 0x{product_id:02x}") + raise RuntimeError(f"Invalid Product ID for PAA5100: 0x{product_id:02x}") def get_id(self): - """Get chip ID and revision from PAA5100.""" + """Get chip ID from PAA5100.""" return self._read(REG_ID, 1) def set_rotation(self, degrees=0): @@ -149,9 +150,8 @@ def __init__(self, reset, cs1, cs2, sampling_rate=100, event='motion'): # Create SPI objects - ## Change to same SPI, sck, miso, and mosi - self.motSen_x = PAA5100JE('SPI0', reset, cs1, 18, 19, 16) - self.motSen_y = PAA5100JE('SPI1', reset, cs2, 10, 11, 12) + self.motSen_x = PAA5100JE('SPI2', reset, cs1) + self.motSen_y = PAA5100JE('SPI2', reset, cs2) self._threshold = threshold self.calib_coef = calib_coef @@ -253,15 +253,3 @@ def record(self): self.data_chy.record() if not self.acquiring: self._start_acquisition() - -# -------------------------------------------------------- -if __name__ == "__main__": - motion_sensor = MotionDetector(2, 17, 13) - try: - while True: - try: - motion_sensor.read_sample() - except RuntimeError: - continue - except KeyboardInterrupt: - pass From 1f4033b1ea43384b0bee9e3e08a0cf9ec0b8b49d Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 15:32:41 +0100 Subject: [PATCH 13/50] Update PAA5100JE.py update 1 --- devices/PAA5100JE.py | 89 +++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 8f262e1..52c700c 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -23,7 +23,14 @@ def to_signed_16(value): REG_RAWDATA_GRAB_STATUS = 0x59 class PAA5100JE(): - def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=None, mosi=None, miso=None): + """Optical tracking sensor.""" + def __init__(self, spi_port: str, + reset: str = None, + spi_cs_gpio: str, + sck: str = None, + mosi: str =None, + miso: str =None): + # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 400000, 'polarity': 0, 'phase': 0, @@ -42,32 +49,22 @@ def __init__(self, spi_port=None, reset=None, spi_cs_gpio=None, sck=None, mosi=N ) # Handle Chip Select (CS) pin - if spi_cs_gpio is not None: - self.select = Digital_output(pin=spi_cs_gpio, inverted=True) - self.reset = Digital_output(pin=reset, inverted=True) - self.select.off() # Deselect the device by setting CS high - self.reset.off() - time.sleep(0.05) + self.select = Digital_output(pin=spi_cs_gpio, inverted=True) + self.reset = Digital_output(pin=reset, inverted=True) + self.select.off() # Deselect the device by setting CS high + self.reset.off() + time.sleep_ms(50) # Reset the sensor self._write(REG_POWER_UP_RESET, 0x5A) - time.sleep(0.02) + time.sleep_ms(20) for offset in range(5): self._read(REG_DATA_READY + offset) # Initiate registers using firmware init_registers() - - # Validate device ID - product_id = self.get_id() - if product_id != 0x49: - raise RuntimeError(f"Invalid Product ID for PAA5100: 0x{product_id:02x}") - - def get_id(self): - """Get chip ID from PAA5100.""" - return self._read(REG_ID, 1) - def set_rotation(self, degrees=0): + def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" if degrees == 0: self.set_orientation(invert_x=True, invert_y=True, swap_xy=True) @@ -80,7 +77,7 @@ def set_rotation(self, degrees=0): else: raise TypeError("Degrees must be one of 0, 90, 180 or 270") - def set_orientation(self, invert_x=True, invert_y=True, swap_xy=True): + def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool =True): """Set orientation of PAA5100 manually.""" value = 0 if swap_xy: @@ -92,6 +89,7 @@ def set_orientation(self, invert_x=True, invert_y=True, swap_xy=True): self._write(REG_ORIENTATION, value) def get_motion(self): + """Retrieve motion from registers""" buf = bytearray(12) self.read_registers(REG_MOTION_BURST, buf, 12) dr = buf[0] @@ -107,37 +105,37 @@ def get_motion(self): return x_out, y_out - def _write(self, register, value): - if self._spi_cs_gpio: - self.select.on() + def _write(self, register: bytes, value: bytes): + """Write into register""" + self.select.on() self.spi.write(bytearray([register | 0x80, value])) - if self._spi_cs_gpio: - self.select.off() + self.select.off() - def _read(self, register, length=1): + def _read(self, register: bytes, length: int =1): + """Read register""" # Create a buffer to send (with one extra byte for the register) send_buf = bytearray([register]) + bytearray(length) # Create a result buffer of the same length as the send_buf result = bytearray(len(send_buf)) - if self._spi_cs_gpio: - self.select.on() + self.select.on() self.spi.write_readinto(send_buf, result) - if self._spi_cs_gpio: - self.select.off() + self.select.off() # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] - def _bulk_write(self, data): + def _bulk_write(self, data: int): + """Write a group of commands into registers""" for x in range(0, len(data), 2): register, value = data[x : x + 2] if register == WAIT: - time.sleep(value / 1000) + time.sleep_ms(value) else: self._write(register, value) - def read_registers(self, reg, buf, len): + def read_registers(self, reg: bytes, buf: bytearray, len: int): + """Read a group of data from the registers""" self.select.on() self.spi.write(bytearray([reg])) # Read data from the register @@ -145,7 +143,11 @@ def read_registers(self, reg, buf, len): self.select.off() class MotionDetector(Analog_input): - def __init__(self, reset, cs1, cs2, + """ + Using the Analog_input code to interface with 2 PAA5100JE sensors + reading `x` (SPI2) and `y` (softSPI) separately. + """ + def __init__(self, reset: str, cs1: str, cs2: str, name='MotDet', threshold=1, calib_coef=1, sampling_rate=100, event='motion'): @@ -183,14 +185,15 @@ def __init__(self, reset, cs1, cs2, @property def threshold(self): - "return the value in cms" + "return the value in mms" return math.sqrt(int(self._threshold**2) * self.calib_coef) def reset_delta(self): - # reset the accumulated position data + "reset the accumulated position data" self.delta_x, self.delta_y = 0, 0 def read_sample(self): + "read motion once" # Units are in millimeters current_motion_x = self.motSen_x.get_motion() current_motion_y = self.motSen_y.get_motion() @@ -208,16 +211,9 @@ def read_sample(self): # Update previous coordinates self.prev_x = self.delta_x self.prev_y = self.delta_y - - disp = self.displacement() - - if self.delta_x**2 + self.delta_y**2 >= self._threshold: - print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f} | Displacement: {disp:>12.5f}") - def displacement(self): - # Calculate displacement using the change of coordinates with time - disp = math.sqrt(self._delta_x**2 + self._delta_y**2) - return disp + if self.delta_x**2 + self.delta_y**2 >= self._threshold: + print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f}") def _timer_ISR(self, t): "Read a sample to the buffer, update write index." @@ -230,9 +226,10 @@ def _timer_ISR(self, t): self.y = self.delta_y self.reset_delta() self.timestamp = fw.current_time + interrupt_queue.put(self.ID) def _stop_acquisition(self): - # Stop sampling analog input values. + "Stop sampling analog input values." self.timer.deinit() self.data_chx.stop() self.data_chy.stop() @@ -242,7 +239,7 @@ def _stop_acquisition(self): self.reset_delta() def _start_acquisition(self): - # Start sampling analog input values. + "Start sampling analog input values" self.timer.init(freq=self.data_chx.sampling_rate) self.timer.callback(self._timer_ISR) self.acquiring = True From dfbdbfe57e951653203b05f7a71f356d21162053 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 15:36:51 +0100 Subject: [PATCH 14/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 290 +++++++++++++++++----------------- 1 file changed, 147 insertions(+), 143 deletions(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 60d261c..7911b8d 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -1,147 +1,151 @@ -def init_registers(self): - self._bulk_write([ - 0x7F, 0x00, - 0x55, 0x01, - 0x50, 0x07, - - 0x7F, 0x0E, - 0x43, 0x10 - ]) - if self._read(0x67) & 0b10000000: - self._write(0x48, 0x04) - else: - self._write(0x48, 0x02) - self._bulk_write([ - 0x7F, 0x00, - 0x51, 0x7B, - 0x50, 0x00, - 0x55, 0x00, - 0x7F, 0x0E - ]) - if self._read(0x73) == 0x00: - c1 = self._read(0x70) - c2 = self._read(0x71) - if c1 <= 28: - c1 += 14 - if c1 > 28: - c1 += 11 - c1 = max(0, min(0x3F, c1)) - c2 = (c2 * 45) // 100 +class PAA5100JE_firmware(): + def __init__(self): + self.name = "firmware" + + def init_registers(self): self._bulk_write([ 0x7F, 0x00, - 0x61, 0xAD, - 0x51, 0x70, + 0x55, 0x01, + 0x50, 0x07, + + 0x7F, 0x0E, + 0x43, 0x10 + ]) + if self._read(0x67) & 0b10000000: + self._write(0x48, 0x04) + else: + self._write(0x48, 0x02) + self._bulk_write([ + 0x7F, 0x00, + 0x51, 0x7B, + 0x50, 0x00, + 0x55, 0x00, 0x7F, 0x0E ]) - self._write(0x70, c1) - self._write(0x71, c2) - self._bulk_write([ - 0x7F, 0x00, - 0x61, 0xAD, - - 0x7F, 0x03, - 0x40, 0x00, - - 0x7F, 0x05, - 0x41, 0xB3, - 0x43, 0xF1, - 0x45, 0x14, - - 0x5F, 0x34, - 0x7B, 0x08, - 0x5E, 0x34, - 0x5B, 0x11, - 0x6D, 0x11, - 0x45, 0x17, - 0x70, 0xE5, - 0x71, 0xE5, - - 0x7F, 0x06, - 0x44, 0x1B, - 0x40, 0xBF, - 0x4E, 0x3F, - - 0x7F, 0x08, - 0x66, 0x44, - 0x65, 0x20, - 0x6A, 0x3A, - 0x61, 0x05, - 0x62, 0x05, - - 0x7F, 0x09, - 0x4F, 0xAF, - 0x5F, 0x40, - 0x48, 0x80, - 0x49, 0x80, - 0x57, 0x77, - 0x60, 0x78, - 0x61, 0x78, - 0x62, 0x08, - 0x63, 0x50, - - 0x7F, 0x0A, - 0x45, 0x60, - - 0x7F, 0x00, - 0x4D, 0x11, - 0x55, 0x80, - 0x74, 0x21, - 0x75, 0x1F, - 0x4A, 0x78, - 0x4B, 0x78, - 0x44, 0x08, - - 0x45, 0x50, - 0x64, 0xFF, - 0x65, 0x1F, - - 0x7F, 0x14, - 0x65, 0x67, - 0x66, 0x08, - 0x63, 0x70, - 0x6F, 0x1C, - - 0x7F, 0x15, - 0x48, 0x48, - - 0x7F, 0x07, - 0x41, 0x0D, - 0x43, 0x14, - 0x4B, 0x0E, - 0x45, 0x0F, - 0x44, 0x42, - 0x4C, 0x80, - - 0x7F, 0x10, - 0x5B, 0x02, - - 0x7F, 0x07, - 0x40, 0x41, - - WAIT, 0x0A, # Wait 10ms - - 0x7F, 0x00, - 0x32, 0x00, - - 0x7F, 0x07, - 0x40, 0x40, - - 0x7F, 0x06, - 0x68, 0xF0, - 0x69, 0x00, - - 0x7F, 0x0D, - 0x48, 0xC0, - 0x6F, 0xD5, - - 0x7F, 0x00, - 0x5B, 0xA0, - 0x4E, 0xA8, - 0x5A, 0x90, - 0x40, 0x80, - 0x73, 0x1F, - - WAIT, 0x0A, # Wait 10ms - - 0x73, 0x00 - ]) \ No newline at end of file + if self._read(0x73) == 0x00: + c1 = self._read(0x70) + c2 = self._read(0x71) + if c1 <= 28: + c1 += 14 + if c1 > 28: + c1 += 11 + c1 = max(0, min(0x3F, c1)) + c2 = (c2 * 45) // 100 + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + 0x51, 0x70, + 0x7F, 0x0E + ]) + self._write(0x70, c1) + self._write(0x71, c2) + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + + 0x7F, 0x03, + 0x40, 0x00, + + 0x7F, 0x05, + 0x41, 0xB3, + 0x43, 0xF1, + 0x45, 0x14, + + 0x5F, 0x34, + 0x7B, 0x08, + 0x5E, 0x34, + 0x5B, 0x11, + 0x6D, 0x11, + 0x45, 0x17, + 0x70, 0xE5, + 0x71, 0xE5, + + 0x7F, 0x06, + 0x44, 0x1B, + 0x40, 0xBF, + 0x4E, 0x3F, + + 0x7F, 0x08, + 0x66, 0x44, + 0x65, 0x20, + 0x6A, 0x3A, + 0x61, 0x05, + 0x62, 0x05, + + 0x7F, 0x09, + 0x4F, 0xAF, + 0x5F, 0x40, + 0x48, 0x80, + 0x49, 0x80, + 0x57, 0x77, + 0x60, 0x78, + 0x61, 0x78, + 0x62, 0x08, + 0x63, 0x50, + + 0x7F, 0x0A, + 0x45, 0x60, + + 0x7F, 0x00, + 0x4D, 0x11, + 0x55, 0x80, + 0x74, 0x21, + 0x75, 0x1F, + 0x4A, 0x78, + 0x4B, 0x78, + 0x44, 0x08, + + 0x45, 0x50, + 0x64, 0xFF, + 0x65, 0x1F, + + 0x7F, 0x14, + 0x65, 0x67, + 0x66, 0x08, + 0x63, 0x70, + 0x6F, 0x1C, + + 0x7F, 0x15, + 0x48, 0x48, + + 0x7F, 0x07, + 0x41, 0x0D, + 0x43, 0x14, + 0x4B, 0x0E, + 0x45, 0x0F, + 0x44, 0x42, + 0x4C, 0x80, + + 0x7F, 0x10, + 0x5B, 0x02, + + 0x7F, 0x07, + 0x40, 0x41, + + WAIT, 0x0A, # Wait 10ms + + 0x7F, 0x00, + 0x32, 0x00, + + 0x7F, 0x07, + 0x40, 0x40, + + 0x7F, 0x06, + 0x68, 0xF0, + 0x69, 0x00, + + 0x7F, 0x0D, + 0x48, 0xC0, + 0x6F, 0xD5, + + 0x7F, 0x00, + 0x5B, 0xA0, + 0x4E, 0xA8, + 0x5A, 0x90, + 0x40, 0x80, + 0x73, 0x1F, + + WAIT, 0x0A, # Wait 10ms + + 0x73, 0x00 + ]) From 060fad2a7fde27cae1f2ead1839b5cc1fe096f6c Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 15:39:26 +0100 Subject: [PATCH 15/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 52c700c..83efa75 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -2,7 +2,7 @@ from array import array from machine import Pin, SPI from pyControl.hardware import * -from PAA5100JE_firmware import init_registers +from PAA5100JE_firmware import * def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -62,7 +62,8 @@ def __init__(self, spi_port: str, self._read(REG_DATA_READY + offset) # Initiate registers using firmware - init_registers() + self.firmware = PAA5100JE_firmware() + self.firmware.init_registers() def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" From 0535b559758ad80dcefa2b3ce58414a587216672 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 15:49:59 +0100 Subject: [PATCH 16/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 7911b8d..81d604e 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -1,7 +1,37 @@ class PAA5100JE_firmware(): def __init__(self): self.name = "firmware" - + + def _read(self, register: bytes, length: int =1): + """Read register""" + # Create a buffer to send (with one extra byte for the register) + send_buf = bytearray([register]) + bytearray(length) + # Create a result buffer of the same length as the send_buf + result = bytearray(len(send_buf)) + + self.select.on() + self.spi.write_readinto(send_buf, result) + self.select.off() + + # Return the read result, skipping the first byte (which corresponds to the register) + return result[1:] if length > 1 else result[1] + + def _bulk_write(self, data: bytearray): + """Write a group of commands into registers""" + for x in range(0, len(data), 2): + register, value = data[x : x + 2] + if register == WAIT: + time.sleep_ms(value) + else: + self._write(register, value) + + def _write(self, register: bytes, value: bytes): + if self._spi_cs_gpio: + self.set_pin(self._spi_cs_gpio, 0) + self.spi.write(bytearray([register | 0x80, value])) + if self._spi_cs_gpio: + self.set_pin(self._spi_cs_gpio, 1) + def init_registers(self): self._bulk_write([ 0x7F, 0x00, From 642a5ec22aa049643b8c79d8c30149c0e353b164 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 16:12:34 +0100 Subject: [PATCH 17/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 83efa75..5c9ae38 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -2,7 +2,7 @@ from array import array from machine import Pin, SPI from pyControl.hardware import * -from PAA5100JE_firmware import * +from PAA5100JE_firmware import PAA5100JE_firmware def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -126,7 +126,7 @@ def _read(self, register: bytes, length: int =1): # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] - def _bulk_write(self, data: int): + def _bulk_write(self, data: bytearray): """Write a group of commands into registers""" for x in range(0, len(data), 2): register, value = data[x : x + 2] @@ -160,13 +160,11 @@ def __init__(self, reset: str, cs1: str, cs2: str, self.calib_coef = calib_coef # Motion sensor variables - self.x_buffer = array('i', [0, 0]) - self.y_buffer = array('i', [0, 0]) + self.x_buffer = bytearray(12) + self.y_buffer = bytearray(12) self.x_buffer_mv = memoryview(self.x_buffer) self.y_buffer_mv = memoryview(self.y_buffer) - self.prev_x = 0 - self.prev_y = 0 self.delta_x, self.delta_y = 0, 0 # accumulated position self._delta_x, self._delta_y = 0, 0 # instantaneous position self.x, self.y = 0, 0 # to be accessed from the task, unit=mm @@ -195,24 +193,16 @@ def reset_delta(self): def read_sample(self): "read motion once" - # Units are in millimeters - current_motion_x = self.motSen_x.get_motion() - current_motion_y = self.motSen_y.get_motion() - - if current_motion_x is not None and current_motion_y is not None: - self.x_buffer_mv = current_motion_x - self.y_buffer_mv = current_motion_y + # All units are in millimeters + self.motSen_x.read_registers(REG_MOTION_BURST, self.x_buffer_mv, 12) + self._delta_x = to_signed_16((self.x_buffer_mv[3] << 8) | self.x_buffer_mv[2]) - self.delta_x = self.x_buffer_mv[0] - self.delta_y = self.y_buffer_mv[1] + self.motSen_y.read_registers(REG_MOTION_BURST, self.y_buffer_mv, 12) + self._delta_y = to_signed_16((self.x_buffer_mv[5] << 8) | self.x_buffer_mv[4]) - self._delta_x = self.delta_x - self.prev_x - self._delta_y = self.delta_y - self.prev_y - - # Update previous coordinates - self.prev_x = self.delta_x - self.prev_y = self.delta_y - + self.delta_y += self._delta_y + self.delta_x += self._delta_x + if self.delta_x**2 + self.delta_y**2 >= self._threshold: print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f}") From ab0f44029ada06cbcbc7f453f39095a7082d3064 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 16:29:57 +0100 Subject: [PATCH 18/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 81d604e..7dbf735 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -20,10 +20,7 @@ def _bulk_write(self, data: bytearray): """Write a group of commands into registers""" for x in range(0, len(data), 2): register, value = data[x : x + 2] - if register == WAIT: - time.sleep_ms(value) - else: - self._write(register, value) + self._write(register, value) def _write(self, register: bytes, value: bytes): if self._spi_cs_gpio: @@ -150,10 +147,11 @@ def init_registers(self): 0x5B, 0x02, 0x7F, 0x07, - 0x40, 0x41, - - WAIT, 0x0A, # Wait 10ms + 0x40, 0x41,]) + + time.sleep_ms(10) + self._bulk_write([ 0x7F, 0x00, 0x32, 0x00, @@ -173,9 +171,7 @@ def init_registers(self): 0x4E, 0xA8, 0x5A, 0x90, 0x40, 0x80, - 0x73, 0x1F, + 0x73, 0x1F,]) - WAIT, 0x0A, # Wait 10ms - - 0x73, 0x00 - ]) + time.sleep_ms(10) + self._bulk_write([0x73, 0x00]) From 4bf06420faebe5197c1d1129a758cc496a2a7439 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 21 Aug 2024 16:31:16 +0100 Subject: [PATCH 19/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 5c9ae38..c67e1f8 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -10,7 +10,6 @@ def to_signed_16(value): value -= 0x10000 # Convert to negative return value -WAIT = -1 # PAA5100 register definitions REG_ID = 0x00 REG_DATA_READY = 0x02 @@ -89,23 +88,6 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool value |= 0b00100000 self._write(REG_ORIENTATION, value) - def get_motion(self): - """Retrieve motion from registers""" - buf = bytearray(12) - self.read_registers(REG_MOTION_BURST, buf, 12) - dr = buf[0] - x_out = to_signed_16((buf[3] << 8) | buf[2]) - y_out = to_signed_16((buf[5] << 8) | buf[4]) - quality = buf[6] - shutter_upper = buf[10] - if (dr & 0b10000000) and not ((quality < 0x19) and (shutter_upper == 0x1f)): - return x_out, y_out - else: - x_out, y_out = 0, 0 - time.sleep_ms(1) - - return x_out, y_out - def _write(self, register: bytes, value: bytes): """Write into register""" self.select.on() From 3ea291e2fcbd4f858e4aeb60815cdf1b46a5a7e3 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Thu, 22 Aug 2024 17:37:44 +0100 Subject: [PATCH 20/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index c67e1f8..6df917a 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -112,10 +112,7 @@ def _bulk_write(self, data: bytearray): """Write a group of commands into registers""" for x in range(0, len(data), 2): register, value = data[x : x + 2] - if register == WAIT: - time.sleep_ms(value) - else: - self._write(register, value) + self._write(register, value) def read_registers(self, reg: bytes, buf: bytearray, len: int): """Read a group of data from the registers""" From 6772da520e484dcf046fe823dc765fd4f5ee028c Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 10:51:51 +0100 Subject: [PATCH 21/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 6df917a..45f5053 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -10,7 +10,7 @@ def to_signed_16(value): value -= 0x10000 # Convert to negative return value -# PAA5100 register definitions +# PAA5100 registers definitions REG_ID = 0x00 REG_DATA_READY = 0x02 REG_MOTION_BURST = 0x16 @@ -109,13 +109,13 @@ def _read(self, register: bytes, length: int =1): return result[1:] if length > 1 else result[1] def _bulk_write(self, data: bytearray): - """Write a group of commands into registers""" + """Write a list of commands into registers""" for x in range(0, len(data), 2): register, value = data[x : x + 2] self._write(register, value) def read_registers(self, reg: bytes, buf: bytearray, len: int): - """Read a group of data from the registers""" + """Read an array of data from the registers""" self.select.on() self.spi.write(bytearray([reg])) # Read data from the register @@ -163,30 +163,33 @@ def __init__(self, reset: str, cs1: str, cs2: str, @property def threshold(self): - "return the value in mms" + """return the value in mms""" return math.sqrt(int(self._threshold**2) * self.calib_coef) def reset_delta(self): - "reset the accumulated position data" + """reset the accumulated position data""" self.delta_x, self.delta_y = 0, 0 def read_sample(self): - "read motion once" + """read motion once""" # All units are in millimeters + # Read motion in x direction self.motSen_x.read_registers(REG_MOTION_BURST, self.x_buffer_mv, 12) self._delta_x = to_signed_16((self.x_buffer_mv[3] << 8) | self.x_buffer_mv[2]) + # Read motion in y direction self.motSen_y.read_registers(REG_MOTION_BURST, self.y_buffer_mv, 12) - self._delta_y = to_signed_16((self.x_buffer_mv[5] << 8) | self.x_buffer_mv[4]) + self._delta_y = to_signed_16((self.y_buffer_mv[5] << 8) | self.y_buffer_mv[4]) + # Record accumulated motion self.delta_y += self._delta_y self.delta_x += self._delta_x if self.delta_x**2 + self.delta_y**2 >= self._threshold: - print(f"x coordinate: {self.delta_x:>10.5f} | y coordinate: {self.delta_y:>10.5f}") + print(f"x coordinate: {self._delta_x:>10.5f} | y coordinate: {self._delta_y:>10.5f}") def _timer_ISR(self, t): - "Read a sample to the buffer, update write index." + """Read a sample to the buffer, update write index.""" self.read_sample() self.data_chx.put(self._delta_x) self.data_chy.put(self._delta_y) @@ -199,7 +202,7 @@ def _timer_ISR(self, t): interrupt_queue.put(self.ID) def _stop_acquisition(self): - "Stop sampling analog input values." + """Stop sampling analog input values.""" self.timer.deinit() self.data_chx.stop() self.data_chy.stop() @@ -209,13 +212,13 @@ def _stop_acquisition(self): self.reset_delta() def _start_acquisition(self): - "Start sampling analog input values" + """Start sampling analog input values""" self.timer.init(freq=self.data_chx.sampling_rate) self.timer.callback(self._timer_ISR) self.acquiring = True def record(self): - "Start streaming data to computer." + """Start streaming data to computer.""" self.data_chx.record() self.data_chy.record() if not self.acquiring: From 4528a9ab00c3b248128ca5f2d85a1b8d8ffedfb1 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 11:02:20 +0100 Subject: [PATCH 22/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 45f5053..de39e7f 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -15,6 +15,7 @@ def to_signed_16(value): REG_DATA_READY = 0x02 REG_MOTION_BURST = 0x16 REG_POWER_UP_RESET = 0x3A +REG_SHUTDOWN = 0x3B REG_ORIENTATION = 0x5B REG_RESOLUTION = 0x4E @@ -121,6 +122,21 @@ def read_registers(self, reg: bytes, buf: bytearray, len: int): # Read data from the register buf[:] = self.spi.read(len) self.select.off() + + def shut_down(self, deinitSPI:bool =True): + """Shutdown the sensor""" + self.select.off() + time.sleep_ms(1) + self.select.on() + time.sleep_ms(1) + self.reset.on() + time.sleep_ms(60) + self.write(REG_SHUTDOWN, 1) + time.sleep_ms(1) + self.select.off() + time.sleep_ms(1) + if deinitSPI: + self.SPI.deinit() class MotionDetector(Analog_input): """ From 9269ba9682b6b732cd91a5c6d032ba73ad4275d2 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 11:29:04 +0100 Subject: [PATCH 23/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index de39e7f..21f7dbe 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -10,23 +10,11 @@ def to_signed_16(value): value -= 0x10000 # Convert to negative return value -# PAA5100 registers definitions -REG_ID = 0x00 -REG_DATA_READY = 0x02 -REG_MOTION_BURST = 0x16 -REG_POWER_UP_RESET = 0x3A -REG_SHUTDOWN = 0x3B -REG_ORIENTATION = 0x5B -REG_RESOLUTION = 0x4E - -REG_RAWDATA_GRAB = 0x58 -REG_RAWDATA_GRAB_STATUS = 0x59 - class PAA5100JE(): """Optical tracking sensor.""" def __init__(self, spi_port: str, + spi_cs_gpio: str, reset: str = None, - spi_cs_gpio: str, sck: str = None, mosi: str =None, miso: str =None): From e7d6eb9c0a45faf20bcd6e4a230c25bd3f7929a1 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 11:29:49 +0100 Subject: [PATCH 24/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 21f7dbe..b8acea7 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -2,7 +2,7 @@ from array import array from machine import Pin, SPI from pyControl.hardware import * -from PAA5100JE_firmware import PAA5100JE_firmware +from PAA5100JE_firmware import * def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -187,10 +187,7 @@ def read_sample(self): # Record accumulated motion self.delta_y += self._delta_y - self.delta_x += self._delta_x - - if self.delta_x**2 + self.delta_y**2 >= self._threshold: - print(f"x coordinate: {self._delta_x:>10.5f} | y coordinate: {self._delta_y:>10.5f}") + self.delta_x += self._delta_x def _timer_ISR(self, t): """Read a sample to the buffer, update write index.""" From c071e8728bd21b636c7e3bd3b29e13791dd09614 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 13:01:31 +0100 Subject: [PATCH 25/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 89 ++++++++++------------------------- 1 file changed, 25 insertions(+), 64 deletions(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 7dbf735..6cc0a22 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -1,72 +1,37 @@ class PAA5100JE_firmware(): def __init__(self): + """ + PAA5100JE firmware includes registers and initiation protocol for + the optical tracking chip + """ self.name = "firmware" - - def _read(self, register: bytes, length: int =1): - """Read register""" - # Create a buffer to send (with one extra byte for the register) - send_buf = bytearray([register]) + bytearray(length) - # Create a result buffer of the same length as the send_buf - result = bytearray(len(send_buf)) - - self.select.on() - self.spi.write_readinto(send_buf, result) - self.select.off() - - # Return the read result, skipping the first byte (which corresponds to the register) - return result[1:] if length > 1 else result[1] - - def _bulk_write(self, data: bytearray): - """Write a group of commands into registers""" - for x in range(0, len(data), 2): - register, value = data[x : x + 2] - self._write(register, value) - - def _write(self, register: bytes, value: bytes): - if self._spi_cs_gpio: - self.set_pin(self._spi_cs_gpio, 0) - self.spi.write(bytearray([register | 0x80, value])) - if self._spi_cs_gpio: - self.set_pin(self._spi_cs_gpio, 1) + # PAA5100 registers definitions + REG_ID = 0x00 + REG_DATA_READY = 0x02 + REG_MOTION_BURST = 0x16 + REG_POWER_UP_RESET = 0x3A + REG_SHUTDOWN = 0x3B + REG_ORIENTATION = 0x5B + REG_RESOLUTION = 0x4E + + REG_RAWDATA_GRAB = 0x58 + REG_RAWDATA_GRAB_STATUS = 0x59 def init_registers(self): - self._bulk_write([ + PROGMEM = [ 0x7F, 0x00, 0x55, 0x01, 0x50, 0x07, 0x7F, 0x0E, - 0x43, 0x10 - ]) - if self._read(0x67) & 0b10000000: - self._write(0x48, 0x04) - else: - self._write(0x48, 0x02) - self._bulk_write([ + 0x43, 0x10, + 0x7F, 0x00, 0x51, 0x7B, 0x50, 0x00, 0x55, 0x00, - 0x7F, 0x0E - ]) - if self._read(0x73) == 0x00: - c1 = self._read(0x70) - c2 = self._read(0x71) - if c1 <= 28: - c1 += 14 - if c1 > 28: - c1 += 11 - c1 = max(0, min(0x3F, c1)) - c2 = (c2 * 45) // 100 - self._bulk_write([ - 0x7F, 0x00, - 0x61, 0xAD, - 0x51, 0x70, - 0x7F, 0x0E - ]) - self._write(0x70, c1) - self._write(0x71, c2) - self._bulk_write([ + 0x7F, 0x0E, + 0x7F, 0x00, 0x61, 0xAD, @@ -147,11 +112,8 @@ def init_registers(self): 0x5B, 0x02, 0x7F, 0x07, - 0x40, 0x41,]) - - time.sleep_ms(10) - - self._bulk_write([ + 0x40, 0x41, + 0x7F, 0x00, 0x32, 0x00, @@ -171,7 +133,6 @@ def init_registers(self): 0x4E, 0xA8, 0x5A, 0x90, 0x40, 0x80, - 0x73, 0x1F,]) - - time.sleep_ms(10) - self._bulk_write([0x73, 0x00]) + 0x73, 0x1F, + + 0x73, 0x00] From c3c1b3c216e16fb96afb7754c221f0f2cb4d8810 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 13:28:46 +0100 Subject: [PATCH 26/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index b8acea7..ae86dd9 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -2,7 +2,7 @@ from array import array from machine import Pin, SPI from pyControl.hardware import * -from PAA5100JE_firmware import * +from device.PAA5100JE_firmware import * def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -44,14 +44,24 @@ def __init__(self, spi_port: str, time.sleep_ms(50) # Reset the sensor - self._write(REG_POWER_UP_RESET, 0x5A) + self.firmware = PAA5100JE_firmware() + self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) time.sleep_ms(20) for offset in range(5): - self._read(REG_DATA_READY + offset) + self._read(self.firmware.REG_DATA_READY + offset) - # Initiate registers using firmware - self.firmware = PAA5100JE_firmware() - self.firmware.init_registers() + # Initiate registers + PROGMEM = self.firmware.init_registers() + self._bulk_write(PROGMEM[0:10]) + if self._read(0x67) & 0b10000000: + self._write(0x48, 0x04) + else: + self._write(0x48, 0x02) + self._bulk_write(PROGMEM[10:154]) + time.sleep_ms(10) + self._bulk_write(PROGMEM[154:186]) + time.sleep_ms(10) + self._bulk_write(PROGMEM[186:]) def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -75,7 +85,7 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool value |= 0b01000000 if invert_x: value |= 0b00100000 - self._write(REG_ORIENTATION, value) + self._write(self.firmware.REG_ORIENTATION, value) def _write(self, register: bytes, value: bytes): """Write into register""" @@ -119,7 +129,7 @@ def shut_down(self, deinitSPI:bool =True): time.sleep_ms(1) self.reset.on() time.sleep_ms(60) - self.write(REG_SHUTDOWN, 1) + self.write(self.firmware.REG_SHUTDOWN, 1) time.sleep_ms(1) self.select.off() time.sleep_ms(1) @@ -136,8 +146,8 @@ def __init__(self, reset: str, cs1: str, cs2: str, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', reset, cs1) - self.motSen_y = PAA5100JE('SPI2', reset, cs2) + self.motSen_x = PAA5100JE('SPI2', cs1, reset) + self.motSen_y = PAA5100JE('SPI2', cs2, reset) self._threshold = threshold self.calib_coef = calib_coef @@ -178,11 +188,11 @@ def read_sample(self): """read motion once""" # All units are in millimeters # Read motion in x direction - self.motSen_x.read_registers(REG_MOTION_BURST, self.x_buffer_mv, 12) + self.motSen_x.read_registers(self.firmware.REG_MOTION_BURST, self.x_buffer_mv, 12) self._delta_x = to_signed_16((self.x_buffer_mv[3] << 8) | self.x_buffer_mv[2]) # Read motion in y direction - self.motSen_y.read_registers(REG_MOTION_BURST, self.y_buffer_mv, 12) + self.motSen_y.read_registers(self.firmware.REG_MOTION_BURST, self.y_buffer_mv, 12) self._delta_y = to_signed_16((self.y_buffer_mv[5] << 8) | self.y_buffer_mv[4]) # Record accumulated motion From 33f0ef80e7ff866fce32fce0e04719fb3c393aa1 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 23 Aug 2024 13:30:50 +0100 Subject: [PATCH 27/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 6cc0a22..1138ebf 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -6,19 +6,19 @@ def __init__(self): """ self.name = "firmware" # PAA5100 registers definitions - REG_ID = 0x00 - REG_DATA_READY = 0x02 - REG_MOTION_BURST = 0x16 - REG_POWER_UP_RESET = 0x3A - REG_SHUTDOWN = 0x3B - REG_ORIENTATION = 0x5B - REG_RESOLUTION = 0x4E + self.REG_ID = 0x00 + self.REG_DATA_READY = 0x02 + self.REG_MOTION_BURST = 0x16 + self.REG_POWER_UP_RESET = 0x3A + self.REG_SHUTDOWN = 0x3B + self.REG_ORIENTATION = 0x5B + self.REG_RESOLUTION = 0x4E + + self.REG_RAWDATA_GRAB = 0x58 + self.REG_RAWDATA_GRAB_STATUS = 0x59 - REG_RAWDATA_GRAB = 0x58 - REG_RAWDATA_GRAB_STATUS = 0x59 - def init_registers(self): - PROGMEM = [ + return [ 0x7F, 0x00, 0x55, 0x01, 0x50, 0x07, From 34d5924370da36abeec5f3947a9b8e828c552e85 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 3 Sep 2024 11:14:18 +0100 Subject: [PATCH 28/50] Update PAA5100JE.py threshold change --- devices/PAA5100JE.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index ae86dd9..99ea9a2 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -149,7 +149,7 @@ def __init__(self, reset: str, cs1: str, cs2: str, self.motSen_x = PAA5100JE('SPI2', cs1, reset) self.motSen_y = PAA5100JE('SPI2', cs2, reset) - self._threshold = threshold + self.threshold = threshold self.calib_coef = calib_coef # Motion sensor variables @@ -177,9 +177,14 @@ def __init__(self, reset: str, cs1: str, cs2: str, @property def threshold(self): - """return the value in mms""" - return math.sqrt(int(self._threshold**2) * self.calib_coef) - + "return the value in mms" + return math.sqrt(self._threshold) + + @threshold.setter + def threshold(self, new_threshold): + self._threshold = int((new_threshold)**2) * self.calib_coef + self.reset_delta() + def reset_delta(self): """reset the accumulated position data""" self.delta_x, self.delta_y = 0, 0 From 7038fed7bec8d7d0341c28c8ebeac123e2c9aec3 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 3 Sep 2024 15:10:12 +0100 Subject: [PATCH 29/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 82 ++++++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 25 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 99ea9a2..73b2ea9 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -1,8 +1,8 @@ import time, gc, math from array import array -from machine import Pin, SPI +import machine from pyControl.hardware import * -from device.PAA5100JE_firmware import * +from devices.PAA5100JE_firmware import * def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -13,8 +13,7 @@ def to_signed_16(value): class PAA5100JE(): """Optical tracking sensor.""" def __init__(self, spi_port: str, - spi_cs_gpio: str, - reset: str = None, + spi_cs_gpio: str, sck: str = None, mosi: str =None, miso: str =None): @@ -24,23 +23,21 @@ def __init__(self, spi_port: str, SPIparams = {'baudrate': 400000, 'polarity': 0, 'phase': 0, 'bits': 8, 'firstbit': machine.SPI.MSB} if '1' in spi_port: - self.spi = SPI(1, **SPIparams) + self.spi = machine.SPI(1, **SPIparams) elif '2' in spi_port: - self.spi = SPI(2, **SPIparams) + self.spi = machine.SPI(2, **SPIparams) elif 'soft' in spi_port.lower(): # Works for newer versions of micropython - self.spi = SoftSPI(sck=Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - mosi=Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - miso=Pin(miso, mode=machine.Pin.IN), + self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + miso=machine.Pin(miso, mode=machine.Pin.IN), **SPIparams ) # Handle Chip Select (CS) pin self.select = Digital_output(pin=spi_cs_gpio, inverted=True) - self.reset = Digital_output(pin=reset, inverted=True) self.select.off() # Deselect the device by setting CS high - self.reset.off() time.sleep_ms(50) # Reset the sensor @@ -57,12 +54,38 @@ def __init__(self, spi_port: str, self._write(0x48, 0x04) else: self._write(0x48, 0x02) - self._bulk_write(PROGMEM[10:154]) + self._bulk_write(PROGMEM[10:20]) + + if self._read(0x73) == 0x00: + c1 = self._read(0x70) + c2 = self._read(0x71) + if c1 <= 28: + c1 += 14 + if c1 > 28: + c1 += 11 + c1 = max(0, min(0x3F, c1)) + c2 = (c2 * 45) // 100 + + self._bulk_write([ + 0x7F, 0x00, + 0x61, 0xAD, + 0x51, 0x70, + 0x7F, 0x0E + ]) + self._write(0x70, c1) + self._write(0x71, c2) + + self._bulk_write(PROGMEM[20:154]) time.sleep_ms(10) self._bulk_write(PROGMEM[154:186]) time.sleep_ms(10) self._bulk_write(PROGMEM[186:]) - + + # Check if registers are initialized + rev_ID = self._read(0x01) + if rev_ID == 0: + print("Registers initialized successfully.") + def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" if degrees == 0: @@ -91,7 +114,9 @@ def _write(self, register: bytes, value: bytes): """Write into register""" self.select.on() self.spi.write(bytearray([register | 0x80, value])) + time.sleep_us(20) self.select.off() + time.sleep_us(100) def _read(self, register: bytes, length: int =1): """Read register""" @@ -102,32 +127,39 @@ def _read(self, register: bytes, length: int =1): self.select.on() self.spi.write_readinto(send_buf, result) + time.sleep_us(1) self.select.off() + time.sleep_us(19) # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] def _bulk_write(self, data: bytearray): """Write a list of commands into registers""" + self.select.on() for x in range(0, len(data), 2): register, value = data[x : x + 2] self._write(register, value) + time.sleep_us(20) + self.select.off() + time.sleep_us(100) - def read_registers(self, reg: bytes, buf: bytearray, len: int): + def read_registers(self, registers: bytes, buf: bytearray, len: int): """Read an array of data from the registers""" self.select.on() - self.spi.write(bytearray([reg])) + self.spi.write(bytearray([registers])) + time.sleep_us(100) # Read data from the register buf[:] = self.spi.read(len) - self.select.off() + time.sleep_us(1) + self.select.off() + time.sleep_us(19) def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" self.select.off() time.sleep_ms(1) self.select.on() - time.sleep_ms(1) - self.reset.on() time.sleep_ms(60) self.write(self.firmware.REG_SHUTDOWN, 1) time.sleep_ms(1) @@ -141,16 +173,16 @@ class MotionDetector(Analog_input): Using the Analog_input code to interface with 2 PAA5100JE sensors reading `x` (SPI2) and `y` (softSPI) separately. """ - def __init__(self, reset: str, cs1: str, cs2: str, - name='MotDet', threshold=1, calib_coef=1, - sampling_rate=100, event='motion'): + def __init__(self, cs1: str, cs2: str, + name='MotSen', event='motion', + calib_coef=1, threshold=1, sampling_rate=100): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', cs1, reset) - self.motSen_y = PAA5100JE('SPI2', cs2, reset) + self.motSen_x = PAA5100JE('SPI2', cs1) + self.motSen_y = PAA5100JE('SPI2', cs2) - self.threshold = threshold self.calib_coef = calib_coef + self.threshold = threshold # Motion sensor variables self.x_buffer = bytearray(12) @@ -198,7 +230,7 @@ def read_sample(self): # Read motion in y direction self.motSen_y.read_registers(self.firmware.REG_MOTION_BURST, self.y_buffer_mv, 12) - self._delta_y = to_signed_16((self.y_buffer_mv[5] << 8) | self.y_buffer_mv[4]) + self._delta_y = to_signed_16((self.y_buffer_mv[3] << 8) | self.y_buffer_mv[2]) # Record accumulated motion self.delta_y += self._delta_y From 0bc69de66d7443c2f8ef7ca0f07d2188ccee186e Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 4 Sep 2024 10:22:55 +0100 Subject: [PATCH 30/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 73b2ea9..c326078 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -167,6 +167,30 @@ def shut_down(self, deinitSPI:bool =True): time.sleep_ms(1) if deinitSPI: self.SPI.deinit() + + def read_register_buff(self, addrs: bytes, buff: bytes): + """ + addrs < 128 + """ + self.select.on() + self.spi.write(addrs) + time.sleep_us(100) # tSRAD + self.spi.readinto(buff) + time.sleep_us(1) # tSCLK-NCS for read operation is 120ns + self.select.off() + time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS + + def write_register_buff(self, addrs: bytes, data: bytes): + """ + addrs < 128 + """ + # flip the MSB to 1:... + self.select.on() + self.spi.write(addrs) + self.spi.write(data) + time.sleep_us(20) # tSCLK-NCS for write operation + self.select.off() + time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound class MotionDetector(Analog_input): """ @@ -190,6 +214,11 @@ def __init__(self, cs1: str, cs2: str, self.x_buffer_mv = memoryview(self.x_buffer) self.y_buffer_mv = memoryview(self.y_buffer) + #self.delta_x_l = self.x_buffer_mv[0:1] + #self.delta_x_h = self.x_buffer_mv[1:] + #self.delta_y_l = self.y_buffer_mv[0:1] + #self.delta_y_h = self.y_buffer_mv[1:] + self.delta_x, self.delta_y = 0, 0 # accumulated position self._delta_x, self._delta_y = 0, 0 # instantaneous position self.x, self.y = 0, 0 # to be accessed from the task, unit=mm @@ -232,6 +261,22 @@ def read_sample(self): self.motSen_y.read_registers(self.firmware.REG_MOTION_BURST, self.y_buffer_mv, 12) self._delta_y = to_signed_16((self.y_buffer_mv[3] << 8) | self.y_buffer_mv[2]) + #self.motSen_y.write_register_buff(b'\x82', b'\x01') + #self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) + #self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) + #self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) + #self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) + #self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) + #self._delta_y = to_signed_16(int.from_bytes(self.y_buffer_mv, 'little')) + + #self.motSen_x.write_register_buff(b'\x82', b'\x01') + #self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) + #self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) + #self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) + #self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) + #self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) + #self._delta_x = to_signed_16(int.from_bytes(self.x_buffer_mv, 'little')) + # Record accumulated motion self.delta_y += self._delta_y self.delta_x += self._delta_x From 6a7b454da650552a23d9c5257f650acdb1b6048c Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 4 Sep 2024 10:31:20 +0100 Subject: [PATCH 31/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index c326078..7a0721c 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -259,9 +259,9 @@ def read_sample(self): # Read motion in y direction self.motSen_y.read_registers(self.firmware.REG_MOTION_BURST, self.y_buffer_mv, 12) - self._delta_y = to_signed_16((self.y_buffer_mv[3] << 8) | self.y_buffer_mv[2]) + self._delta_y = to_signed_16((self.y_buffer_mv[5] << 8) | self.y_buffer_mv[4]) - #self.motSen_y.write_register_buff(b'\x82', b'\x01') + #self.motSen_y.write_register_buff(b'\x82', b'\x01') # not sure what register 0x82 is (from previous sensor) #self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) #self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) #self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) From f6b91085511b537c7303ea400f83e5d8074a8d10 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 4 Sep 2024 11:00:12 +0100 Subject: [PATCH 32/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 59 ++++++-------------------------------------- 1 file changed, 7 insertions(+), 52 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 7a0721c..b0dad5f 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -66,7 +66,7 @@ def __init__(self, spi_port: str, c1 = max(0, min(0x3F, c1)) c2 = (c2 * 45) // 100 - self._bulk_write([ + self._bulk_write([ 0x7F, 0x00, 0x61, 0xAD, 0x51, 0x70, @@ -82,8 +82,8 @@ def __init__(self, spi_port: str, self._bulk_write(PROGMEM[186:]) # Check if registers are initialized - rev_ID = self._read(0x01) - if rev_ID == 0: + prod_ID = self._read(0x00) + if prod_ID == 0x49: print("Registers initialized successfully.") def set_rotation(self, degrees:int =0): @@ -166,40 +166,16 @@ def shut_down(self, deinitSPI:bool =True): self.select.off() time.sleep_ms(1) if deinitSPI: - self.SPI.deinit() - - def read_register_buff(self, addrs: bytes, buff: bytes): - """ - addrs < 128 - """ - self.select.on() - self.spi.write(addrs) - time.sleep_us(100) # tSRAD - self.spi.readinto(buff) - time.sleep_us(1) # tSCLK-NCS for read operation is 120ns - self.select.off() - time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - - def write_register_buff(self, addrs: bytes, data: bytes): - """ - addrs < 128 - """ - # flip the MSB to 1:... - self.select.on() - self.spi.write(addrs) - self.spi.write(data) - time.sleep_us(20) # tSCLK-NCS for write operation - self.select.off() - time.sleep_us(100) # tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + self.spi.deinit() class MotionDetector(Analog_input): """ Using the Analog_input code to interface with 2 PAA5100JE sensors reading `x` (SPI2) and `y` (softSPI) separately. """ - def __init__(self, cs1: str, cs2: str, - name='MotSen', event='motion', - calib_coef=1, threshold=1, sampling_rate=100): + def __init__(self, reset: str, cs1: str, cs2: str, + name='MotSen', threshold=1, calib_coef=1, + sampling_rate=100, event='motion'): # Create SPI objects self.motSen_x = PAA5100JE('SPI2', cs1) @@ -213,11 +189,6 @@ def __init__(self, cs1: str, cs2: str, self.y_buffer = bytearray(12) self.x_buffer_mv = memoryview(self.x_buffer) self.y_buffer_mv = memoryview(self.y_buffer) - - #self.delta_x_l = self.x_buffer_mv[0:1] - #self.delta_x_h = self.x_buffer_mv[1:] - #self.delta_y_l = self.y_buffer_mv[0:1] - #self.delta_y_h = self.y_buffer_mv[1:] self.delta_x, self.delta_y = 0, 0 # accumulated position self._delta_x, self._delta_y = 0, 0 # instantaneous position @@ -260,22 +231,6 @@ def read_sample(self): # Read motion in y direction self.motSen_y.read_registers(self.firmware.REG_MOTION_BURST, self.y_buffer_mv, 12) self._delta_y = to_signed_16((self.y_buffer_mv[5] << 8) | self.y_buffer_mv[4]) - - #self.motSen_y.write_register_buff(b'\x82', b'\x01') # not sure what register 0x82 is (from previous sensor) - #self.motSen_y.read_register_buff(b'\x02', self.delta_y_l) - #self.motSen_y.read_register_buff(b'\x03', self.delta_y_l) - #self.motSen_y.read_register_buff(b'\x04', self.delta_y_l) - #self.motSen_y.read_register_buff(b'\x05', self.delta_y_l) - #self.motSen_y.read_register_buff(b'\x06', self.delta_y_h) - #self._delta_y = to_signed_16(int.from_bytes(self.y_buffer_mv, 'little')) - - #self.motSen_x.write_register_buff(b'\x82', b'\x01') - #self.motSen_x.read_register_buff(b'\x02', self.delta_x_l) - #self.motSen_x.read_register_buff(b'\x03', self.delta_x_l) - #self.motSen_x.read_register_buff(b'\x04', self.delta_x_h) - #self.motSen_x.read_register_buff(b'\x05', self.delta_y_l) - #self.motSen_x.read_register_buff(b'\x06', self.delta_y_l) - #self._delta_x = to_signed_16(int.from_bytes(self.x_buffer_mv, 'little')) # Record accumulated motion self.delta_y += self._delta_y From 4422e19fd2f75d5f55a982bad6f41558729a412a Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 6 Sep 2024 11:03:33 +0100 Subject: [PATCH 33/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index b0dad5f..2549ab9 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -129,7 +129,7 @@ def _read(self, register: bytes, length: int =1): self.spi.write_readinto(send_buf, result) time.sleep_us(1) self.select.off() - time.sleep_us(19) + time.sleep_us(1) # try 19us if not working # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] @@ -153,7 +153,7 @@ def read_registers(self, registers: bytes, buf: bytearray, len: int): buf[:] = self.spi.read(len) time.sleep_us(1) self.select.off() - time.sleep_us(19) + time.sleep_us(1) def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" From ea3f277cfe4aa7b95e868d5dabfe5e4efd0dd95e Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Fri, 6 Sep 2024 11:39:07 +0100 Subject: [PATCH 34/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 2549ab9..2777bb1 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -129,7 +129,7 @@ def _read(self, register: bytes, length: int =1): self.spi.write_readinto(send_buf, result) time.sleep_us(1) self.select.off() - time.sleep_us(1) # try 19us if not working + time.sleep_us(19) # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] @@ -140,20 +140,20 @@ def _bulk_write(self, data: bytearray): for x in range(0, len(data), 2): register, value = data[x : x + 2] self._write(register, value) - time.sleep_us(20) + time.sleep_us(11) self.select.off() - time.sleep_us(100) + time.sleep_us(15) def read_registers(self, registers: bytes, buf: bytearray, len: int): """Read an array of data from the registers""" self.select.on() self.spi.write(bytearray([registers])) - time.sleep_us(100) + time.sleep_us(7) # Read data from the register buf[:] = self.spi.read(len) - time.sleep_us(1) - self.select.off() - time.sleep_us(1) + time.sleep_us(2) + self.select.off() + time.sleep_us(15) def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" From 06ec9cb3a96c2bc41012e48520e8f27585458087 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Sat, 7 Sep 2024 12:27:38 +0100 Subject: [PATCH 35/50] Update PAA5100JE.py 2 bugs found, needed to test --- devices/PAA5100JE.py | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 2777bb1..a402236 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -20,8 +20,10 @@ def __init__(self, spi_port: str, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 400000, 'polarity': 0, 'phase': 0, + SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 0, 'bits': 8, 'firstbit': machine.SPI.MSB} + #BUG: Test for different polarity and phase combinations (because different SPI mode is used) + if '1' in spi_port: self.spi = machine.SPI(1, **SPIparams) @@ -37,8 +39,10 @@ def __init__(self, spi_port: str, # Handle Chip Select (CS) pin self.select = Digital_output(pin=spi_cs_gpio, inverted=True) - self.select.off() # Deselect the device by setting CS high + + self.select.on() time.sleep_ms(50) + self.select.off() # Deselect the device by setting CS high # Reset the sensor self.firmware = PAA5100JE_firmware() @@ -83,8 +87,7 @@ def __init__(self, spi_port: str, # Check if registers are initialized prod_ID = self._read(0x00) - if prod_ID == 0x49: - print("Registers initialized successfully.") + assert prod_ID == 0x49 def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -112,14 +115,24 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, register: bytes, value: bytes): """Write into register""" + #self.select.on() + #self.spi.write(bytearray([register | 0x80, value])) + #time.sleep_us(20) + #self.select.off() + #time.sleep_us(100) + + buf = bytearray(2) + buf[0] = register | 0x80 + buf[1] = value self.select.on() - self.spi.write(bytearray([register | 0x80, value])) + self.spi.write(buf) time.sleep_us(20) self.select.off() time.sleep_us(100) def _read(self, register: bytes, length: int =1): """Read register""" + ''' # Create a buffer to send (with one extra byte for the register) send_buf = bytearray([register]) + bytearray(length) # Create a result buffer of the same length as the send_buf @@ -133,24 +146,27 @@ def _read(self, register: bytes, length: int =1): # Return the read result, skipping the first byte (which corresponds to the register) return result[1:] if length > 1 else result[1] + ''' + data = bytearray(1) # Create a single byte buffer for the result + self.select.on() + spi.write_readinto(bytearray([reg]), data) + self.select.off() + return data[0] # Return the byte read (#Possible BUG: originally was set to first) def _bulk_write(self, data: bytearray): """Write a list of commands into registers""" - self.select.on() for x in range(0, len(data), 2): register, value = data[x : x + 2] self._write(register, value) - time.sleep_us(11) - self.select.off() - time.sleep_us(15) - + def read_registers(self, registers: bytes, buf: bytearray, len: int): """Read an array of data from the registers""" self.select.on() self.spi.write(bytearray([registers])) time.sleep_us(7) # Read data from the register - buf[:] = self.spi.read(len) + #buf[:] = self.spi.read(len) + spi.readinto(buf, len) time.sleep_us(2) self.select.off() time.sleep_us(15) @@ -235,7 +251,9 @@ def read_sample(self): # Record accumulated motion self.delta_y += self._delta_y self.delta_x += self._delta_x - + + time.sleep_ms(1) + def _timer_ISR(self, t): """Read a sample to the buffer, update write index.""" self.read_sample() From fb94109bb81ccc6d5915c86cf2bd15d957edaf6a Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Sat, 7 Sep 2024 12:34:27 +0100 Subject: [PATCH 36/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index 1138ebf..b6587e7 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -7,6 +7,7 @@ def __init__(self): self.name = "firmware" # PAA5100 registers definitions self.REG_ID = 0x00 + self.REVISION = 0x01 self.REG_DATA_READY = 0x02 self.REG_MOTION_BURST = 0x16 self.REG_POWER_UP_RESET = 0x3A From ce0a02b2ba94635e3aa5f0d1a9826bc7cdffdd0e Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Sun, 8 Sep 2024 18:30:33 +0100 Subject: [PATCH 37/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index a402236..e9475eb 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -20,7 +20,7 @@ def __init__(self, spi_port: str, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 0, + SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} #BUG: Test for different polarity and phase combinations (because different SPI mode is used) @@ -151,7 +151,7 @@ def _read(self, register: bytes, length: int =1): self.select.on() spi.write_readinto(bytearray([reg]), data) self.select.off() - return data[0] # Return the byte read (#Possible BUG: originally was set to first) + return data[1] # Return the byte read def _bulk_write(self, data: bytearray): """Write a list of commands into registers""" From d4c591cdaba8222d0507ff163bce32e02ddb6503 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Mon, 9 Sep 2024 17:46:51 +0100 Subject: [PATCH 38/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index e9475eb..eb83f83 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -151,7 +151,7 @@ def _read(self, register: bytes, length: int =1): self.select.on() spi.write_readinto(bytearray([reg]), data) self.select.off() - return data[1] # Return the byte read + return data[0] # Return the byte read def _bulk_write(self, data: bytearray): """Write a list of commands into registers""" From 10a9a74f20986f76ede3ef13425e85df55f57dfe Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 10 Sep 2024 16:34:43 +0100 Subject: [PATCH 39/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 84 +++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index eb83f83..0b1e79c 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -11,7 +11,10 @@ def to_signed_16(value): return value class PAA5100JE(): - """Optical tracking sensor.""" + """ + Optical tracking sensor: + C++ code reference can be found on: https://github.com/pimoroni/pimoroni-pico.git + """ def __init__(self, spi_port: str, spi_cs_gpio: str, sck: str = None, @@ -21,8 +24,7 @@ def __init__(self, spi_port: str, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, - 'bits': 8, 'firstbit': machine.SPI.MSB} - #BUG: Test for different polarity and phase combinations (because different SPI mode is used) + 'bits': 16, 'firstbit': machine.SPI.MSB} if '1' in spi_port: self.spi = machine.SPI(1, **SPIparams) @@ -50,7 +52,12 @@ def __init__(self, spi_port: str, time.sleep_ms(20) for offset in range(5): self._read(self.firmware.REG_DATA_READY + offset) - + + ## Check if registers are initialized + #prod_ID = self._read(0x00) + #assert prod_ID == 0x49, 'bad initialization' + str(prod_ID) + + # check if initialisation protocol is correct # Initiate registers PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) @@ -87,7 +94,7 @@ def __init__(self, spi_port: str, # Check if registers are initialized prod_ID = self._read(0x00) - assert prod_ID == 0x49 + assert prod_ID == 0x49, 'bad initialization' + str(prod_ID) def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -113,60 +120,51 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool value |= 0b00100000 self._write(self.firmware.REG_ORIENTATION, value) - def _write(self, register: bytes, value: bytes): - """Write into register""" - #self.select.on() - #self.spi.write(bytearray([register | 0x80, value])) - #time.sleep_us(20) - #self.select.off() - #time.sleep_us(100) + def _write(self, address: int, value: int): + """Write value into register""" + addrs = address | 0x80 # Flip MSB to 1 + addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte + value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte - buf = bytearray(2) - buf[0] = register | 0x80 - buf[1] = value self.select.on() - self.spi.write(buf) - time.sleep_us(20) + self.spi.write(addrs) # find specific address of the device + self.spi.write(value) # write value into the above address of the device + time.sleep_us(15) self.select.off() - time.sleep_us(100) - - def _read(self, register: bytes, length: int =1): + time.sleep_us(25) + + def _read(self, address: int): """Read register""" - ''' - # Create a buffer to send (with one extra byte for the register) - send_buf = bytearray([register]) + bytearray(length) - # Create a result buffer of the same length as the send_buf - result = bytearray(len(send_buf)) + # Create a buffer to send + addrs = address & 0x7f # Ensure MSB is 0 (check if MSB = 1 for read and MSB = 0 for write) + addrs = addrs.to_bytes(1, 'little') # Convert the integer to a single byte self.select.on() - self.spi.write_readinto(send_buf, result) + self.spi.write(addrs) + time.sleep_us(2) #tSRAD + data = self.spi.read(1) # does this return one byte only? + assert type(data) == int + + val = int.from_bytes(data, 'little') # converts back to integer for comparison + assert type(val) == bytes time.sleep_us(1) self.select.off() time.sleep_us(19) - - # Return the read result, skipping the first byte (which corresponds to the register) - return result[1:] if length > 1 else result[1] - ''' - data = bytearray(1) # Create a single byte buffer for the result - self.select.on() - spi.write_readinto(bytearray([reg]), data) - self.select.off() - return data[0] # Return the byte read + return val - def _bulk_write(self, data: bytearray): + def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): - register, value = data[x : x + 2] - self._write(register, value) + address, value = data[x : x + 2] + self._write(address, value) - def read_registers(self, registers: bytes, buf: bytearray, len: int): + def read_registers(self, registers: int, buf: int, len: int): """Read an array of data from the registers""" self.select.on() - self.spi.write(bytearray([registers])) + self.spi.write(registers) time.sleep_us(7) # Read data from the register - #buf[:] = self.spi.read(len) - spi.readinto(buf, len) + self.spi.readinto(buf, len) time.sleep_us(2) self.select.off() time.sleep_us(15) @@ -184,7 +182,7 @@ def shut_down(self, deinitSPI:bool =True): if deinitSPI: self.spi.deinit() -class MotionDetector(Analog_input): +class MotionDetector2(Analog_input): """ Using the Analog_input code to interface with 2 PAA5100JE sensors reading `x` (SPI2) and `y` (softSPI) separately. From 34a8d436413214eae6389b5d1ff2cef29d4bf5e0 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 11 Sep 2024 16:37:15 +0100 Subject: [PATCH 40/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 0b1e79c..3b65be3 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -24,7 +24,7 @@ def __init__(self, spi_port: str, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, - 'bits': 16, 'firstbit': machine.SPI.MSB} + 'bits': 8, 'firstbit': machine.SPI.MSB} if '1' in spi_port: self.spi = machine.SPI(1, **SPIparams) @@ -124,7 +124,7 @@ def _write(self, address: int, value: int): """Write value into register""" addrs = address | 0x80 # Flip MSB to 1 addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte - value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte + value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte self.select.on() self.spi.write(addrs) # find specific address of the device @@ -141,12 +141,12 @@ def _read(self, address: int): self.select.on() self.spi.write(addrs) - time.sleep_us(2) #tSRAD + time.sleep_us(2) #tSRAD: check if waiting time is long enough data = self.spi.read(1) # does this return one byte only? - assert type(data) == int + assert type(data) == bytes val = int.from_bytes(data, 'little') # converts back to integer for comparison - assert type(val) == bytes + assert type(val) == int time.sleep_us(1) self.select.off() time.sleep_us(19) From 48f40a6cec1e974c65de2ad2c40e8f7bde2a1051 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 11 Sep 2024 21:37:27 +0100 Subject: [PATCH 41/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 3b65be3..52760c5 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -18,10 +18,11 @@ class PAA5100JE(): def __init__(self, spi_port: str, spi_cs_gpio: str, sck: str = None, - mosi: str =None, - miso: str =None): + mosi: str = None, + miso: str = None): # Initialize SPI + ''' # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} @@ -38,7 +39,12 @@ def __init__(self, spi_port: str, miso=machine.Pin(miso, mode=machine.Pin.IN), **SPIparams ) - + ''' + if '2' in spi_port: + self.spi = machine.SPI(1, baudrate=9600, polarity=0, phase=1, bits=8, firstbit=machine.SPI.MSB, + sck=machine.Pin(sck, mode=0, pull=1), mosi=machine.Pin(mosi, mode=0, pull=1), + miso=machine.Pin(miso, mode=1)) # check baudrate and SPI mode + # Handle Chip Select (CS) pin self.select = Digital_output(pin=spi_cs_gpio, inverted=True) @@ -51,7 +57,8 @@ def __init__(self, spi_port: str, self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) time.sleep_ms(20) for offset in range(5): - self._read(self.firmware.REG_DATA_READY + offset) + data = 0 + self.read_registers(self.firmware.REG_DATA_READY + offset, data, 1) ## Check if registers are initialized #prod_ID = self._read(0x00) @@ -122,13 +129,14 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - addrs = address | 0x80 # Flip MSB to 1 - addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte - value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte - self.select.on() - self.spi.write(addrs) # find specific address of the device - self.spi.write(value) # write value into the above address of the device + #addrs = address | 0x80 # Flip MSB to 1 + #addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte + #value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte + self.spi.write(bytearray([address | 0x80, value])) + + #self.spi.write(addrs) # find specific address of the device + #self.spi.write(value) # write value into the above address of the device time.sleep_us(15) self.select.off() time.sleep_us(25) @@ -151,7 +159,20 @@ def _read(self, address: int): self.select.off() time.sleep_us(19) return val - + + def _read(self, register, length=1): + # Create a buffer to send (with one extra byte for the register) + send_buf = bytearray([register]) + bytearray(length) + # Create a result buffer of the same length as the send_buf + result = bytearray(len(send_buf)) + + self.select.on() + self.spi.write_readinto(send_buf, result) + self.select.off() + + # Return the read result, skipping the first byte (which corresponds to the register) + return result[1] + def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): From 514b9c6fbce9f70811823decc6ba2a908af8d3f9 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Wed, 11 Sep 2024 21:58:44 +0100 Subject: [PATCH 42/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 52760c5..b25f1de 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -3,6 +3,12 @@ import machine from pyControl.hardware import * from devices.PAA5100JE_firmware import * +# things to test: +# SPI settings: compare with pi pico (SPI type 1) +# initialize changed to read_registers instead of _read +# _read function added dummy byte +# _read and _write function MSB --> 0 or 1 +# _write function uses bytearray def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -129,12 +135,13 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - self.select.on() #addrs = address | 0x80 # Flip MSB to 1 #addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte #value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte - self.spi.write(bytearray([address | 0x80, value])) + buf = bytearray([address | 0x80, value]) + self.select.on() + self.spi.write(buf) #self.spi.write(addrs) # find specific address of the device #self.spi.write(value) # write value into the above address of the device time.sleep_us(15) @@ -144,22 +151,23 @@ def _write(self, address: int, value: int): def _read(self, address: int): """Read register""" # Create a buffer to send - addrs = address & 0x7f # Ensure MSB is 0 (check if MSB = 1 for read and MSB = 0 for write) + addrs = address & 0x7f # Ensure MSB is 0 (or | 0x80?) addrs = addrs.to_bytes(1, 'little') # Convert the integer to a single byte self.select.on() self.spi.write(addrs) time.sleep_us(2) #tSRAD: check if waiting time is long enough + dummy_byte = 0x00 + self.spi.write(bytearray([dummy_byte])) + data = self.spi.read(1) # does this return one byte only? - assert type(data) == bytes val = int.from_bytes(data, 'little') # converts back to integer for comparison - assert type(val) == int time.sleep_us(1) self.select.off() time.sleep_us(19) return val - + ''' def _read(self, register, length=1): # Create a buffer to send (with one extra byte for the register) send_buf = bytearray([register]) + bytearray(length) @@ -172,7 +180,7 @@ def _read(self, register, length=1): # Return the read result, skipping the first byte (which corresponds to the register) return result[1] - + ''' def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): From 95c017aea589df80df93431fa049e85e2f0e3475 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Thu, 12 Sep 2024 11:16:14 +0100 Subject: [PATCH 43/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index b25f1de..e9da841 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -9,6 +9,7 @@ # _read function added dummy byte # _read and _write function MSB --> 0 or 1 # _write function uses bytearray +# added reset def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -23,6 +24,7 @@ class PAA5100JE(): """ def __init__(self, spi_port: str, spi_cs_gpio: str, + reset: str = None, sck: str = None, mosi: str = None, miso: str = None): @@ -68,7 +70,7 @@ def __init__(self, spi_port: str, ## Check if registers are initialized #prod_ID = self._read(0x00) - #assert prod_ID == 0x49, 'bad initialization' + str(prod_ID) + #assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) # check if initialisation protocol is correct # Initiate registers @@ -141,12 +143,13 @@ def _write(self, address: int, value: int): buf = bytearray([address | 0x80, value]) self.select.on() - self.spi.write(buf) + self.spi.write(buf[0]) + self.spi.write(buf[1]) #self.spi.write(addrs) # find specific address of the device #self.spi.write(value) # write value into the above address of the device - time.sleep_us(15) + time.sleep_us(11) # tSWW self.select.off() - time.sleep_us(25) + time.sleep_us(25) # buffer time def _read(self, address: int): """Read register""" @@ -156,16 +159,16 @@ def _read(self, address: int): self.select.on() self.spi.write(addrs) - time.sleep_us(2) #tSRAD: check if waiting time is long enough + time.sleep_us(8) # tSRAD + tSWR dummy_byte = 0x00 self.spi.write(bytearray([dummy_byte])) - data = self.spi.read(1) # does this return one byte only? + data = self.spi.read(1) val = int.from_bytes(data, 'little') # converts back to integer for comparison - time.sleep_us(1) + time.sleep_us(1) # tSCLK-NCS for read operation is 120ns self.select.off() - time.sleep_us(19) + time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS return val ''' def _read(self, register, length=1): @@ -221,8 +224,8 @@ def __init__(self, reset: str, cs1: str, cs2: str, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', cs1) - self.motSen_y = PAA5100JE('SPI2', cs2) + self.motSen_x = PAA5100JE('SPI2', cs1, reset) + self.motSen_y = PAA5100JE('SPI2', cs2, reset) self.calib_coef = calib_coef self.threshold = threshold From 7f67d77341ed57bf257ba2425343adaeb13bef86 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Thu, 12 Sep 2024 13:18:26 +0100 Subject: [PATCH 44/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 69 ++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index e9da841..6c6cb44 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -3,13 +3,6 @@ import machine from pyControl.hardware import * from devices.PAA5100JE_firmware import * -# things to test: -# SPI settings: compare with pi pico (SPI type 1) -# initialize changed to read_registers instead of _read -# _read function added dummy byte -# _read and _write function MSB --> 0 or 1 -# _write function uses bytearray -# added reset def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -23,14 +16,12 @@ class PAA5100JE(): C++ code reference can be found on: https://github.com/pimoroni/pimoroni-pico.git """ def __init__(self, spi_port: str, - spi_cs_gpio: str, - reset: str = None, - sck: str = None, + cs: str, + miso: str = None, mosi: str = None, - miso: str = None): + sck: str = None): # Initialize SPI - ''' # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} @@ -47,14 +38,9 @@ def __init__(self, spi_port: str, miso=machine.Pin(miso, mode=machine.Pin.IN), **SPIparams ) - ''' - if '2' in spi_port: - self.spi = machine.SPI(1, baudrate=9600, polarity=0, phase=1, bits=8, firstbit=machine.SPI.MSB, - sck=machine.Pin(sck, mode=0, pull=1), mosi=machine.Pin(mosi, mode=0, pull=1), - miso=machine.Pin(miso, mode=1)) # check baudrate and SPI mode # Handle Chip Select (CS) pin - self.select = Digital_output(pin=spi_cs_gpio, inverted=True) + self.select = Digital_output(pin=cs, inverted=True) self.select.on() time.sleep_ms(50) @@ -68,11 +54,10 @@ def __init__(self, spi_port: str, data = 0 self.read_registers(self.firmware.REG_DATA_READY + offset, data, 1) - ## Check if registers are initialized - #prod_ID = self._read(0x00) - #assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) + # Check if registers are initialized + prod_ID = self._read(0x00) + assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) - # check if initialisation protocol is correct # Initiate registers PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) @@ -137,16 +122,13 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - #addrs = address | 0x80 # Flip MSB to 1 - #addrs = address.to_bytes(1, 'little') # Convert the address from integer to a single byte - #value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte - buf = bytearray([address | 0x80, value]) + addrs = address | 0x80 # Flip MSB to 1 + addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte + value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte self.select.on() - self.spi.write(buf[0]) - self.spi.write(buf[1]) - #self.spi.write(addrs) # find specific address of the device - #self.spi.write(value) # write value into the above address of the device + self.spi.write(addrs) # find specific address of the device + self.spi.write(value) # write value into the above address of the device time.sleep_us(11) # tSWW self.select.off() time.sleep_us(25) # buffer time @@ -170,20 +152,7 @@ def _read(self, address: int): self.select.off() time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS return val - ''' - def _read(self, register, length=1): - # Create a buffer to send (with one extra byte for the register) - send_buf = bytearray([register]) + bytearray(length) - # Create a result buffer of the same length as the send_buf - result = bytearray(len(send_buf)) - - self.select.on() - self.spi.write_readinto(send_buf, result) - self.select.off() - - # Return the read result, skipping the first byte (which corresponds to the register) - return result[1] - ''' + def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): @@ -192,11 +161,15 @@ def _bulk_write(self, data: int): def read_registers(self, registers: int, buf: int, len: int): """Read an array of data from the registers""" + addrs = registers & 0x7f # Flip MSB to 1 + addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte + self.select.on() - self.spi.write(registers) + self.spi.write(addrs) time.sleep_us(7) # Read data from the register - self.spi.readinto(buf, len) + #self.spi.readinto(buf, len) + buf[:] = self.spi.read(len) time.sleep_us(2) self.select.off() time.sleep_us(15) @@ -224,8 +197,8 @@ def __init__(self, reset: str, cs1: str, cs2: str, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', cs1, reset) - self.motSen_y = PAA5100JE('SPI2', cs2, reset) + self.motSen_x = PAA5100JE('SPI2', cs1) + self.motSen_y = PAA5100JE('SPI2', cs2) self.calib_coef = calib_coef self.threshold = threshold From e738edd9ab7cfe7efedbaae416e941867dd85717 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Thu, 12 Sep 2024 14:50:18 +0100 Subject: [PATCH 45/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 84 ++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index 6c6cb44..e0a2fbf 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -3,6 +3,10 @@ import machine from pyControl.hardware import * from devices.PAA5100JE_firmware import * +#things to test +# power supply (ensure its the same as the pmw3360dm old sensor) +# unplug and plug all the wires +# digital output in cs pin def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -23,7 +27,7 @@ def __init__(self, spi_port: str, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 1000000, 'polarity': 0, 'phase': 1, + SPIparams = {'baudrate': 1000000, 'polarity': 1, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} if '1' in spi_port: @@ -40,24 +44,47 @@ def __init__(self, spi_port: str, ) # Handle Chip Select (CS) pin - self.select = Digital_output(pin=cs, inverted=True) + #self.select = Digital_output(pin=cs, inverted=True) + self.select = machine.Pin(cs, Pin.OUT) - self.select.on() + self.select.value(1) + time.sleep_ms(50) + self.select.value(0) time.sleep_ms(50) - self.select.off() # Deselect the device by setting CS high + self.select.value(1) # Deselect the device by setting CS high + + # Check if registers are initialized + prod_ID = self._read(0x00) + dIpihc = self._read(0x5F) + assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) + assert dIpihc == 0xB8, 'bad initialization' + # Reset the sensor self.firmware = PAA5100JE_firmware() - self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) - time.sleep_ms(20) - for offset in range(5): - data = 0 - self.read_registers(self.firmware.REG_DATA_READY + offset, data, 1) + #self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) + self._write(0x3A, 0x5A) + time.sleep_ms(50) # wait time increases + for offset in range(5): + self._read(self.firmware.REG_DATA_READY + offset) # can try comment it + time.sleep_ms(1) + + # try reading motion registers once + self._read(0x02) + self._read(0x03) + self._read(0x04) + self._read(0x05) + self._read(0x06) + time.sleep_ms(1) + # Check if registers are initialized prod_ID = self._read(0x00) + dIpihc = self._read(0x5F) + assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) - + assert dIpihc == 0xB8, 'bad initialization' + # Initiate registers PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) @@ -122,13 +149,15 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - addrs = address | 0x80 # Flip MSB to 1 - addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte - value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte + #addrs = address | 0x80 # Flip MSB to 1 + #addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte + #value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte self.select.on() - self.spi.write(addrs) # find specific address of the device - self.spi.write(value) # write value into the above address of the device + self.spi.write(bytearray([address | 0x80, value])) + + #self.spi.write(addrs) # find specific address of the device + #self.spi.write(value) # write value into the above address of the device time.sleep_us(11) # tSWW self.select.off() time.sleep_us(25) # buffer time @@ -142,8 +171,6 @@ def _read(self, address: int): self.select.on() self.spi.write(addrs) time.sleep_us(8) # tSRAD + tSWR - dummy_byte = 0x00 - self.spi.write(bytearray([dummy_byte])) data = self.spi.read(1) @@ -152,17 +179,28 @@ def _read(self, address: int): self.select.off() time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS return val - + ''' + def _read(self, address: int): + result = [] + val = bytearray(2) + cmd = bytearray(2) + cmd[0] = address+1 + cmd[1] = 0 + self.spi.write_readinto(cmd, val) + result.append(val[1]) + return result[0] + ''' def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): address, value = data[x : x + 2] self._write(address, value) - def read_registers(self, registers: int, buf: int, len: int): + def read_registers(self, registers: int, buf: bytearray, len: int): """Read an array of data from the registers""" - addrs = registers & 0x7f # Flip MSB to 1 - addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte + #addrs = registers & 0x7f # Flip MSB to 1 + #addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte + addrs = bytearray(registers) self.select.on() self.spi.write(addrs) @@ -197,8 +235,8 @@ def __init__(self, reset: str, cs1: str, cs2: str, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', cs1) - self.motSen_y = PAA5100JE('SPI2', cs2) + self.motSen_x = PAA5100JE('SPI2', cs1, reset) + self.motSen_y = PAA5100JE('SPI2', cs2, reset) self.calib_coef = calib_coef self.threshold = threshold From 2a0a86ce70484bf497b463627cf6d382b8c6dde1 Mon Sep 17 00:00:00 2001 From: Mostafa Safaie Date: Thu, 12 Sep 2024 18:19:14 +0100 Subject: [PATCH 46/50] Update PAA5100JE.py initialization error 66 tried unplug and plug, maybe something to do with the hardware because the spi seems not to connect --- devices/PAA5100JE.py | 116 +++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 77 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index e0a2fbf..e31b948 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -1,12 +1,8 @@ -import time, gc, math +import time, gc, math, machine from array import array -import machine from pyControl.hardware import * from devices.PAA5100JE_firmware import * -#things to test -# power supply (ensure its the same as the pmw3360dm old sensor) -# unplug and plug all the wires -# digital output in cs pin + def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" @@ -19,71 +15,49 @@ class PAA5100JE(): Optical tracking sensor: C++ code reference can be found on: https://github.com/pimoroni/pimoroni-pico.git """ - def __init__(self, spi_port: str, - cs: str, - miso: str = None, - mosi: str = None, - sck: str = None): + def __init__(self, + SPI_type: str, + CS: str = None, + MI: str = None, + MO: str = None, + SCK: str = None): # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 1000000, 'polarity': 1, 'phase': 1, + SPIparams = {'baudrate': 400000, 'polarity': 1, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} - if '1' in spi_port: + if '1' in SPI_type: self.spi = machine.SPI(1, **SPIparams) - elif '2' in spi_port: + elif '2' in SPI_type: self.spi = machine.SPI(2, **SPIparams) - elif 'soft' in spi_port.lower(): # Works for newer versions of micropython - self.spi = machine.SoftSPI(sck=machine.Pin(sck, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - mosi=machine.Pin(mosi, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), - miso=machine.Pin(miso, mode=machine.Pin.IN), + elif 'soft' in SPI_type.lower(): # Works for newer versions of micropython + self.spi = machine.SoftSPI(sck=machine.Pin(SCK, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + mosi=machine.Pin(MO, mode=machine.Pin.OUT, pull=machine.Pin.PULL_DOWN), + miso=machine.Pin(MI, mode=machine.Pin.IN), **SPIparams ) # Handle Chip Select (CS) pin - #self.select = Digital_output(pin=cs, inverted=True) - self.select = machine.Pin(cs, Pin.OUT) + self.select = Digital_output(pin=CS, inverted=True) - self.select.value(1) - time.sleep_ms(50) - self.select.value(0) + self.select.on() time.sleep_ms(50) - self.select.value(1) # Deselect the device by setting CS high - - # Check if registers are initialized - prod_ID = self._read(0x00) - dIpihc = self._read(0x5F) - - assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) - assert dIpihc == 0xB8, 'bad initialization' + self.select.off() # Deselect the device by setting CS high # Reset the sensor self.firmware = PAA5100JE_firmware() - #self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) - self._write(0x3A, 0x5A) + self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) time.sleep_ms(50) # wait time increases for offset in range(5): - self._read(self.firmware.REG_DATA_READY + offset) # can try comment it - time.sleep_ms(1) - - # try reading motion registers once - self._read(0x02) - self._read(0x03) - self._read(0x04) - self._read(0x05) - self._read(0x06) - time.sleep_ms(1) - + self._read(self.firmware.REG_DATA_READY + offset) + # Check if registers are initialized - prod_ID = self._read(0x00) - dIpihc = self._read(0x5F) - + prod_ID = self._read(self.firmware.REG_ID) assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) - assert dIpihc == 0xB8, 'bad initialization' # Initiate registers PROGMEM = self.firmware.init_registers() @@ -120,8 +94,8 @@ def __init__(self, spi_port: str, self._bulk_write(PROGMEM[186:]) # Check if registers are initialized - prod_ID = self._read(0x00) - assert prod_ID == 0x49, 'bad initialization' + str(prod_ID) + prod_ID = self._read(self.firmware.REG_ID) + assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -149,28 +123,26 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - #addrs = address | 0x80 # Flip MSB to 1 - #addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte - #value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte + address = address | 0x80 # Flip MSB to 1 + address = address.to_bytes(1, 'little') # Convert the address from integer to a single byte + value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte self.select.on() - self.spi.write(bytearray([address | 0x80, value])) - - #self.spi.write(addrs) # find specific address of the device - #self.spi.write(value) # write value into the above address of the device - time.sleep_us(11) # tSWW + self.spi.write(address) # find specific address of the device + self.spi.write(value) # write value into the above address of the device + time.sleep_us(20) # tSWW self.select.off() - time.sleep_us(25) # buffer time - + time.sleep_us(100) # buffer time + def _read(self, address: int): """Read register""" # Create a buffer to send - addrs = address & 0x7f # Ensure MSB is 0 (or | 0x80?) - addrs = addrs.to_bytes(1, 'little') # Convert the integer to a single byte + address = address & 0x7f # Ensure MSB is 0 (or | 0x80?) + address = address.to_bytes(1, 'little') # Convert the integer to a single byte self.select.on() - self.spi.write(addrs) - time.sleep_us(8) # tSRAD + tSWR + self.spi.write(address) + time.sleep_us(120) # tSRAD + tSWR data = self.spi.read(1) @@ -179,17 +151,7 @@ def _read(self, address: int): self.select.off() time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS return val - ''' - def _read(self, address: int): - result = [] - val = bytearray(2) - cmd = bytearray(2) - cmd[0] = address+1 - cmd[1] = 0 - self.spi.write_readinto(cmd, val) - result.append(val[1]) - return result[0] - ''' + def _bulk_write(self, data: int): """Write a list of commands into registers""" for x in range(0, len(data), 2): @@ -235,8 +197,8 @@ def __init__(self, reset: str, cs1: str, cs2: str, sampling_rate=100, event='motion'): # Create SPI objects - self.motSen_x = PAA5100JE('SPI2', cs1, reset) - self.motSen_y = PAA5100JE('SPI2', cs2, reset) + self.motSen_x = PAA5100JE('SPI2', cs1) + self.motSen_y = PAA5100JE('SPI2', cs2) self.calib_coef = calib_coef self.threshold = threshold From ad025a8189ce2baef0e9883766c573c53fad69c9 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Thu, 12 Sep 2024 22:42:34 +0100 Subject: [PATCH 47/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 54 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index e31b948..ec935bb 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -3,7 +3,6 @@ from pyControl.hardware import * from devices.PAA5100JE_firmware import * - def to_signed_16(value): """Convert a 16-bit integer to a signed 16-bit integer.""" if value & 0x8000: # Check if the sign bit is set @@ -14,6 +13,7 @@ class PAA5100JE(): """ Optical tracking sensor: C++ code reference can be found on: https://github.com/pimoroni/pimoroni-pico.git + and on https://github.com/zic-95/PAA5100JE/blob/main/src/PAA5100JE.cpp """ def __init__(self, SPI_type: str, @@ -24,7 +24,7 @@ def __init__(self, # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 400000, 'polarity': 1, 'phase': 1, + SPIparams = {'baudrate': 2000000, 'polarity': 1, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} if '1' in SPI_type: @@ -43,18 +43,23 @@ def __init__(self, # Handle Chip Select (CS) pin self.select = Digital_output(pin=CS, inverted=True) + time.sleep_ms(1) + self.select.off() + time.sleep_ms(1) self.select.on() - time.sleep_ms(50) + time.sleep_ms(1) self.select.off() # Deselect the device by setting CS high - + time.sleep_ms(1) + # Reset the sensor self.firmware = PAA5100JE_firmware() self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) - time.sleep_ms(50) # wait time increases + time.sleep_ms(20) for offset in range(5): self._read(self.firmware.REG_DATA_READY + offset) - + time.sleep_ms(1) + # Check if registers are initialized prod_ID = self._read(self.firmware.REG_ID) assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) @@ -62,15 +67,16 @@ def __init__(self, # Initiate registers PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) - if self._read(0x67) & 0b10000000: + res = self._read(0x67) & 0x80 + if res == 0x80: self._write(0x48, 0x04) else: self._write(0x48, 0x02) self._bulk_write(PROGMEM[10:20]) if self._read(0x73) == 0x00: - c1 = self._read(0x70) - c2 = self._read(0x71) + c1 = int(self._read(0x70)) + c2 = int(self._read(0x71)) if c1 <= 28: c1 += 14 if c1 > 28: @@ -123,34 +129,37 @@ def set_orientation(self, invert_x:bool =True, invert_y:bool =True, swap_xy:bool def _write(self, address: int, value: int): """Write value into register""" - address = address | 0x80 # Flip MSB to 1 + address |= 0x80 # Flip MSB to 1 address = address.to_bytes(1, 'little') # Convert the address from integer to a single byte value = value.to_bytes(1, 'little') # Convert the value from integer to a single byte self.select.on() + time.sleep_us(1) self.spi.write(address) # find specific address of the device self.spi.write(value) # write value into the above address of the device - time.sleep_us(20) # tSWW + time.sleep_us(5) # tSWW self.select.off() - time.sleep_us(100) # buffer time + time.sleep_us(5) # buffer time def _read(self, address: int): """Read register""" # Create a buffer to send - address = address & 0x7f # Ensure MSB is 0 (or | 0x80?) + address &= ~0x80 # Ensure MSB is 0 address = address.to_bytes(1, 'little') # Convert the integer to a single byte + buf = bytearray(len(address)) self.select.on() + time.sleep_us(1) self.spi.write(address) - time.sleep_us(120) # tSRAD + tSWR + time.sleep_us(5) # tSRAD + tSWR - data = self.spi.read(1) + data = self.spi.write_readinto(0x00, buf) # or simply spi.read would be ok - val = int.from_bytes(data, 'little') # converts back to integer for comparison + #val = int.from_bytes(data, 'little') # converts back to integer for comparison time.sleep_us(1) # tSCLK-NCS for read operation is 120ns self.select.off() - time.sleep_us(19) # tSRW/tSRR (=20us) minus tSCLK-NCS - return val + time.sleep_us(5) # tSRW/tSRR (=20us) minus tSCLK-NCS + return data def _bulk_write(self, data: int): """Write a list of commands into registers""" @@ -160,19 +169,20 @@ def _bulk_write(self, data: int): def read_registers(self, registers: int, buf: bytearray, len: int): """Read an array of data from the registers""" - #addrs = registers & 0x7f # Flip MSB to 1 + registers &= ~0x80 # Flip MSB to 1 #addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte addrs = bytearray(registers) self.select.on() + time.sleep_us(1) self.spi.write(addrs) - time.sleep_us(7) + time.sleep_us(5) # Read data from the register #self.spi.readinto(buf, len) buf[:] = self.spi.read(len) - time.sleep_us(2) + time.sleep_us(5) self.select.off() - time.sleep_us(15) + time.sleep_us(50) def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" From 6c46795e4e7792fdfb1ff52255b2b0243de9fb67 Mon Sep 17 00:00:00 2001 From: Mostafa Safaie Date: Fri, 13 Sep 2024 11:54:59 +0100 Subject: [PATCH 48/50] Update PAA5100JE.py --- devices/PAA5100JE.py | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index ec935bb..f4191cf 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -17,14 +17,14 @@ class PAA5100JE(): """ def __init__(self, SPI_type: str, - CS: str = None, + CS: str, MI: str = None, MO: str = None, SCK: str = None): # Initialize SPI # SPI_type = 'SPI1' or 'SPI2' or 'softSPI' - SPIparams = {'baudrate': 2000000, 'polarity': 1, 'phase': 1, + SPIparams = {'baudrate': 1000000, 'polarity': 1, 'phase': 1, 'bits': 8, 'firstbit': machine.SPI.MSB} if '1' in SPI_type: @@ -59,11 +59,7 @@ def __init__(self, for offset in range(5): self._read(self.firmware.REG_DATA_READY + offset) time.sleep_ms(1) - - # Check if registers are initialized - prod_ID = self._read(self.firmware.REG_ID) - assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) - + # Initiate registers PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) @@ -99,9 +95,9 @@ def __init__(self, time.sleep_ms(10) self._bulk_write(PROGMEM[186:]) + time.sleep_ms(10) # Check if registers are initialized - prod_ID = self._read(self.firmware.REG_ID) - assert prod_ID == 0x49, 'bad initialization ' + str(prod_ID) + ID = self._read(0x02) def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -146,20 +142,19 @@ def _read(self, address: int): # Create a buffer to send address &= ~0x80 # Ensure MSB is 0 address = address.to_bytes(1, 'little') # Convert the integer to a single byte - buf = bytearray(len(address)) self.select.on() time.sleep_us(1) self.spi.write(address) time.sleep_us(5) # tSRAD + tSWR - data = self.spi.write_readinto(0x00, buf) # or simply spi.read would be ok + data = self.spi.read(1) - #val = int.from_bytes(data, 'little') # converts back to integer for comparison + val = int.from_bytes(data, 'little') # converts back to integer for comparison time.sleep_us(1) # tSCLK-NCS for read operation is 120ns self.select.off() time.sleep_us(5) # tSRW/tSRR (=20us) minus tSCLK-NCS - return data + return val def _bulk_write(self, data: int): """Write a list of commands into registers""" @@ -167,22 +162,23 @@ def _bulk_write(self, data: int): address, value = data[x : x + 2] self._write(address, value) - def read_registers(self, registers: int, buf: bytearray, len: int): + def read_registers(self, address: int, buf: bytearray, len: int): """Read an array of data from the registers""" - registers &= ~0x80 # Flip MSB to 1 - #addrs = addrs.to_bytes(1, 'little') # Convert the address from integer to a single byte - addrs = bytearray(registers) + address &= ~0x80 # Flip MSB to 1 + address = address.to_bytes(1, 'little') # Convert the address from integer to a single byte self.select.on() time.sleep_us(1) - self.spi.write(addrs) + self.spi.write(address) time.sleep_us(5) # Read data from the register - #self.spi.readinto(buf, len) - buf[:] = self.spi.read(len) + for i in range(12): + buf[i] = self.spi.read(1) time.sleep_us(5) self.select.off() time.sleep_us(50) + assert buf[10] == 0x1F, str(buf[10]) + assert buf[6] < 0x19 def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" @@ -190,7 +186,7 @@ def shut_down(self, deinitSPI:bool =True): time.sleep_ms(1) self.select.on() time.sleep_ms(60) - self.write(self.firmware.REG_SHUTDOWN, 1) + self._write(self.firmware.REG_SHUTDOWN, 0xB6) time.sleep_ms(1) self.select.off() time.sleep_ms(1) @@ -265,8 +261,6 @@ def read_sample(self): self.delta_y += self._delta_y self.delta_x += self._delta_x - time.sleep_ms(1) - def _timer_ISR(self, t): """Read a sample to the buffer, update write index.""" self.read_sample() @@ -278,7 +272,7 @@ def _timer_ISR(self, t): self.y = self.delta_y self.reset_delta() self.timestamp = fw.current_time - interrupt_queue.put(self.ID) + interrupt_queue.put(self.ID) def _stop_acquisition(self): """Stop sampling analog input values.""" From 8ecd594f82b04890602f07d8cef09670d535f9c9 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 17 Sep 2024 14:24:05 +0100 Subject: [PATCH 49/50] Update PAA5100JE.py Final version after testing: both sensors were successfully initialized and were able to read motion, however the read data were not send to the pyboard. A possible cause would be an error during the ISR, because no ID were provided by this sensor to allow it to perform an interrupt. --- devices/PAA5100JE.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/devices/PAA5100JE.py b/devices/PAA5100JE.py index f4191cf..d0baf84 100644 --- a/devices/PAA5100JE.py +++ b/devices/PAA5100JE.py @@ -40,15 +40,15 @@ def __init__(self, **SPIparams ) - # Handle Chip Select (CS) pin + # Define Chip Select (CS) pin (active low) self.select = Digital_output(pin=CS, inverted=True) time.sleep_ms(1) - self.select.off() + self.select.off() # Deselect the device by setting CS high time.sleep_ms(1) - self.select.on() + self.select.on() # Select the device by setting CS low time.sleep_ms(1) - self.select.off() # Deselect the device by setting CS high + self.select.off() time.sleep_ms(1) # Reset the sensor @@ -56,11 +56,12 @@ def __init__(self, self._write(self.firmware.REG_POWER_UP_RESET, 0x5A) time.sleep_ms(20) + # Read motion registers once after reset for offset in range(5): self._read(self.firmware.REG_DATA_READY + offset) time.sleep_ms(1) - # Initiate registers + # Registers initialization protocol PROGMEM = self.firmware.init_registers() self._bulk_write(PROGMEM[0:10]) res = self._read(0x67) & 0x80 @@ -96,8 +97,9 @@ def __init__(self, self._bulk_write(PROGMEM[186:]) time.sleep_ms(10) - # Check if registers are initialized - ID = self._read(0x02) + # Check for successful initialization + prod_ID = self._read(0x00) + assert prod_ID == 0x49 def set_rotation(self, degrees:int =0): """Set orientation of PAA5100 in increments of 90 degrees.""" @@ -150,10 +152,10 @@ def _read(self, address: int): data = self.spi.read(1) - val = int.from_bytes(data, 'little') # converts back to integer for comparison + val = int.from_bytes(data, 'little') # converts received data back to integer for further calculations time.sleep_us(1) # tSCLK-NCS for read operation is 120ns self.select.off() - time.sleep_us(5) # tSRW/tSRR (=20us) minus tSCLK-NCS + time.sleep_us(5) # tSRW/tSRR minus tSCLK-NCS return val def _bulk_write(self, data: int): @@ -163,7 +165,7 @@ def _bulk_write(self, data: int): self._write(address, value) def read_registers(self, address: int, buf: bytearray, len: int): - """Read an array of data from the registers""" + """Read an array of data from the registers, used for reading motion burst""" address &= ~0x80 # Flip MSB to 1 address = address.to_bytes(1, 'little') # Convert the address from integer to a single byte @@ -171,14 +173,14 @@ def read_registers(self, address: int, buf: bytearray, len: int): time.sleep_us(1) self.spi.write(address) time.sleep_us(5) - # Read data from the register + # Read 12 bytes of data from the motion burst register for i in range(12): buf[i] = self.spi.read(1) time.sleep_us(5) self.select.off() time.sleep_us(50) + # Check for data being successfully read into the buffer assert buf[10] == 0x1F, str(buf[10]) - assert buf[6] < 0x19 def shut_down(self, deinitSPI:bool =True): """Shutdown the sensor""" @@ -272,7 +274,7 @@ def _timer_ISR(self, t): self.y = self.delta_y self.reset_delta() self.timestamp = fw.current_time - interrupt_queue.put(self.ID) + interrupt_queue.put(self.ID) # Note: no self.ID for this sensor, cannot interrupt queue so data were not sent def _stop_acquisition(self): """Stop sampling analog input values.""" From 5d645b06df9a70cf6ef8f49501d82e8fd96d0176 Mon Sep 17 00:00:00 2001 From: Leoch24 Date: Tue, 17 Sep 2024 14:27:06 +0100 Subject: [PATCH 50/50] Update PAA5100JE_firmware.py --- devices/PAA5100JE_firmware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/PAA5100JE_firmware.py b/devices/PAA5100JE_firmware.py index b6587e7..468c903 100644 --- a/devices/PAA5100JE_firmware.py +++ b/devices/PAA5100JE_firmware.py @@ -4,7 +4,6 @@ def __init__(self): PAA5100JE firmware includes registers and initiation protocol for the optical tracking chip """ - self.name = "firmware" # PAA5100 registers definitions self.REG_ID = 0x00 self.REVISION = 0x01 @@ -19,6 +18,7 @@ def __init__(self): self.REG_RAWDATA_GRAB_STATUS = 0x59 def init_registers(self): + """Initialization protocol for sensor setup (C++ code reference can be found on: https://github.com/pimoroni/pimoroni-pico.git)""" return [ 0x7F, 0x00, 0x55, 0x01,