463 lines
16 KiB
Python
463 lines
16 KiB
Python
from functools import lru_cache
|
|
from random import randint
|
|
from time import sleep
|
|
from typing import List, Tuple, Optional, Union, Iterable
|
|
|
|
from biopro.impl.selector import Selector
|
|
from biopro.util.console import pc, GREEN, RED
|
|
from .gpio import P3Pin, OutputPin, InputPin
|
|
from .interface_spi import HardwareImplSpiInterface, LowLevelHardwareInterface
|
|
from datetime import datetime
|
|
|
|
MSM_REG_WRITE = 0x01
|
|
MEM_INS_WRITE = 0x02
|
|
MEM_INS_READ = 0x03
|
|
MEM_REG_READ = 0x05
|
|
DEFAULT_REGISTER_VALUE = 0b0100_0011 # 67
|
|
MEM_SIZE = 0x1000
|
|
|
|
_RUNTIME_COMPILE = False
|
|
|
|
_SLEEP_TIME_ = 0.001
|
|
|
|
|
|
@lru_cache(maxsize=4)
|
|
def zero_buffer(size: int) -> List[int]:
|
|
return [0] * size
|
|
|
|
class MultiExtMemSpiInterface(LowLevelHardwareInterface):
|
|
MEM_INS_MARKED = [MEM_INS_WRITE, 0, 2, 1, 1]
|
|
MEM_INS_RESET = [MEM_INS_WRITE, 0, 2, 1, 1, 0, 0xFF]
|
|
|
|
__slots__ = ('_selector', '_wait_for_first_data', '_spi', '_tx_buffer', '_tx_buffer_header', '_tx_buffer_data',
|
|
'pin_busy', 'pin_request', 'pin_reset', 'pin_sel',
|
|
'_pin_sel_val',
|
|
'_read_green_times','_read_red_times',
|
|
'_elite_data_len', '_mem_header_len', '_mem_tailer_len', '_single_data_len',
|
|
'_head_wrong_cnt')
|
|
|
|
def __init__(self,
|
|
select: Selector,
|
|
device: Tuple[int, int] = None):
|
|
|
|
self._spi = HardwareImplSpiInterface(device,
|
|
spi_speed=12_000_000 # XXX temp parameter
|
|
)
|
|
|
|
self._elite_data_len = 120
|
|
self._mem_header_len = 3
|
|
self._mem_tailer_len = 8
|
|
self._single_data_len = self._elite_data_len + self._mem_header_len + self._mem_tailer_len
|
|
|
|
# buffer
|
|
self._tx_buffer = [0] * 512
|
|
self._tx_buffer_header = [0] * 11
|
|
self._tx_buffer_data = [0] * (self._single_data_len * 10 + 3)
|
|
|
|
# memory control pin
|
|
self.pin_busy = OutputPin.get_used(P3Pin.MEM_BZY, True)
|
|
self.pin_request = OutputPin.get_used(P3Pin.MEM_REQ, True)
|
|
self.pin_reset = OutputPin.get_used(P3Pin.MEM_RST, True)
|
|
self.pin_sel: Optional[InputPin] = InputPin.get_used(P3Pin.MEM_SEL)
|
|
|
|
self._read_green_times = 0
|
|
self._read_red_times = 0
|
|
|
|
self._head_wrong_cnt = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
|
|
self._selector = select
|
|
|
|
self._pin_sel_val = [False for _ in range(Selector.SIZE)]
|
|
self._wait_for_first_data = [True for _ in range(Selector.SIZE)]
|
|
|
|
@property
|
|
def select(self) -> int:
|
|
return self._selector.channel
|
|
|
|
@select.setter
|
|
def select(self, value: int):
|
|
self._selector.select(value)
|
|
|
|
def changed(self, flip=False) -> bool:
|
|
channel = self._selector.channel
|
|
old = self._pin_sel_val[channel]
|
|
value = bool(self.pin_sel)
|
|
|
|
if flip:
|
|
self._pin_sel_val[channel] = value
|
|
|
|
return value != old
|
|
|
|
def reset(self):
|
|
self._spi.reset()
|
|
|
|
def close(self):
|
|
self._selector.close()
|
|
self._spi.close()
|
|
|
|
def flush(self):
|
|
self.pin_reset.output(False)
|
|
self.pin_reset.output(True)
|
|
|
|
def send_byte(self, data: bytes):
|
|
raise RuntimeError()
|
|
|
|
def recv_byte(self, size: int) -> Optional[bytes]:
|
|
raise RuntimeError()
|
|
|
|
# def flush_all(self):
|
|
# for _ in self._selector.foreach():
|
|
# self.pin_reset.output(False)
|
|
# self.pin_reset.output(True)
|
|
|
|
def foreach(self) -> Iterable[int]:
|
|
for channel in self._selector.foreach():
|
|
yield channel
|
|
|
|
def set_wait_flag(self, spi_idx:int = None, value:bool = None):
|
|
self._wait_for_first_data[spi_idx] = value
|
|
|
|
def get_wait_flag(self, spi_idx:int = None) -> Optional[bool]:
|
|
if spi_idx < Selector.SIZE:
|
|
return self._wait_for_first_data[spi_idx]
|
|
else:
|
|
return None
|
|
|
|
def request_data(self):
|
|
self.pin_request.output(False)
|
|
sleep(0.001)
|
|
|
|
# count = 0
|
|
# for i in range(300):
|
|
# count = count + 1
|
|
|
|
self.pin_request.output(True)
|
|
|
|
# sleep(0.001) -> 1.2ms
|
|
# no sleep -> 5us ~ 12us (central can't receive)
|
|
|
|
# for i in range
|
|
# (1000) -> 500us ~ 1ms
|
|
# (500) -> 200us ~ 500us
|
|
# (300) -> 120us ~ 270us (seldom)
|
|
# (200) -> 70us ~ 160us (skip req about every 64 time) x
|
|
# (100) -> 40us ~ 100us (skip req sometimes) x
|
|
|
|
def compare_green_data(self, data_first: Union[bytes, List[int]], data_second: Union[bytes, List[int]], data_third: Union[bytes, List[int]], length: int):
|
|
data = []
|
|
data.append(255)
|
|
data.append(255)
|
|
data.append(255)
|
|
|
|
for i in range(3, length):
|
|
if (data_first[i] == data_second[i] or data_first[i] == data_third[i]):
|
|
data.append(data_first[i])
|
|
elif(data_second[i] == data_third[i]):
|
|
data.append(data_second[i])
|
|
else:
|
|
data.append(data_third[i]) # use last data
|
|
self._read_green_times = self._read_green_times + 1
|
|
print("read green data times", self._read_green_times)
|
|
return data
|
|
|
|
def compare_red_data(self, data_first: Union[bytes, List[int]], data_second: Union[bytes, List[int]], data_third: Union[bytes, List[int]], length: int):
|
|
data = []
|
|
data.append(255)
|
|
data.append(255)
|
|
data.append(255)
|
|
|
|
for i in range(3, length):
|
|
if (data_first[i] == data_second[i] or data_first[i] == data_third[i]):
|
|
data.append(data_first[i])
|
|
elif(data_second[i] == data_third[i]):
|
|
data.append(data_second[i])
|
|
else:
|
|
data.append(data_third[i]) # use last data
|
|
self._read_red_times = self._read_red_times + 1
|
|
print("read red data times", self._read_red_times)
|
|
return data
|
|
|
|
def recv_memory(self, device: int) -> Optional[bytes]:
|
|
self.pin_busy.output(False)
|
|
|
|
rx = []
|
|
|
|
try:
|
|
address = 0
|
|
|
|
# first read to get data length
|
|
tx_h = self._tx_buffer_header
|
|
|
|
tx_h[0] = MEM_INS_READ
|
|
tx_h[1] = 0
|
|
tx_h[2] = 0
|
|
|
|
|
|
# -------------------------------------read one times-------------------------------------------
|
|
data = []
|
|
flag_print = False
|
|
|
|
data = self._spi.send_byte(tx_h)
|
|
|
|
if (data[0] != 255 or data[1] != 255 or data[2] != 255):
|
|
self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
if (self._head_wrong_cnt[device] < 6):
|
|
print('data_first[0:3] != [255, 255, 255]', device)
|
|
print(list(data[0:7]))
|
|
|
|
data[0:3] = [255, 255, 255]
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
|
|
# -------------------------------------read two times-------------------------------------------
|
|
# data = []
|
|
# data_first = []
|
|
# data_second = []
|
|
# flag_print = False
|
|
|
|
# data_first = self._spi.send_byte(tx_h)
|
|
# data_second = self._spi.send_byte(tx_h)
|
|
|
|
# if (data_first[0] != 255 or data_first[1] != 255 or data_first[2] != 255):
|
|
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
# if (self._head_wrong_cnt[device] < 10):
|
|
# print('data_first[0:3] != [255, 255, 255]', device)
|
|
# print(list(data_first[0:7]))
|
|
|
|
# if (data_second[0] != 255 or data_second[1] != 255 or data_second[2] != 255):
|
|
# self._head_wrong_cnt[device] = self._head_wrong_cnt[device] + 1
|
|
# if (self._head_wrong_cnt[device] < 10):
|
|
# print('data_second[0:3] != [255, 255, 255]', device)
|
|
# print(list(data_second[0:7]))
|
|
|
|
# if (data_first[3:] == data_second[3:]):
|
|
# data = data_first
|
|
# data[0:3] = [255, 255, 255]
|
|
|
|
# else:
|
|
# data_third = self._spi.send_byte(tx_h)
|
|
# print("read data_third", data_third)
|
|
# data = self.compare_green_data(data_first, data_second, data_third, len(tx_h))
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
# debug use
|
|
# green_data = []
|
|
# green_data = data
|
|
# print("_[Debug] @ spi green data = ", green_data)
|
|
|
|
length = (data[3] << 8) | data[4]
|
|
header = data[7]
|
|
elite_data_len = data[9]
|
|
|
|
if length <= 4:
|
|
# print("length <= 4")
|
|
# print("green data print:", data, device, datetime.now())
|
|
return None
|
|
|
|
if data[5] != 0 or data[6] != 0:
|
|
# give a default length if the data header did not update
|
|
length = 7000
|
|
print("green data: is not [0,0], = ", data[5:7])
|
|
print("green data print:", data, device, datetime.now())
|
|
return None
|
|
|
|
if (length >= 7000):
|
|
flag_print = True
|
|
print("green data: big length:", length)
|
|
|
|
if (header != 255):
|
|
flag_print = True
|
|
print("green data: header is not 255: ", header)
|
|
|
|
if (elite_data_len != self._elite_data_len):
|
|
flag_print = True
|
|
print("green data: length is not", self._elite_data_len, ": ", elite_data_len)
|
|
|
|
if (flag_print):
|
|
print("green data print:", data, device, datetime.now())
|
|
|
|
# neulive
|
|
# address += len(data) - 11
|
|
# tx_d = self._tx_buffer_data
|
|
# red_length = len(tx_d)
|
|
|
|
# elite read all data of ram
|
|
# address = 4
|
|
# red_length = length - 1
|
|
# tx_d = [0] * red_length
|
|
|
|
address = 4
|
|
red_length = int(length / 2)
|
|
tx_d = [0] * red_length
|
|
|
|
# elite read len(_tx_buffer_data) byte
|
|
# address = 4
|
|
# tx_d = self._tx_buffer_data
|
|
|
|
while True:
|
|
tx_d[0] = MEM_INS_READ
|
|
tx_d[1] = ((address >> 8) & 0xFF)
|
|
tx_d[2] = (address & 0xFF)
|
|
# -------------------------------------read one times-------------------------------------------
|
|
data = []
|
|
data = self._spi.send_byte(tx_d)
|
|
data[0:3] = [255, 255, 255]
|
|
# ----------------------------------------------------------------------------------------------
|
|
|
|
# -------------------------------------read two times-------------------------------------------
|
|
# data = []
|
|
# data_first = []
|
|
# data_second = []
|
|
|
|
# data_first = self._spi.send_byte(tx_d)
|
|
# data_second = self._spi.send_byte(tx_d)
|
|
|
|
# if (data_first[3:] == data_second[3:]):
|
|
# data = data_first
|
|
# data[0:3] = [255, 255, 255]
|
|
# else:
|
|
# data_third = self._spi.send_byte(tx_d)
|
|
# print("read red data_third", data_third)
|
|
# data = self.compare_red_data(data_first, data_second, data_third, len(tx_d))
|
|
# ----------------------------------------------------------------------------------------------
|
|
if length + 3 <= address + len(data):
|
|
rx.extend(data[3:3 + length - address])
|
|
del data
|
|
|
|
break
|
|
|
|
else:
|
|
rx.extend(data[3:])
|
|
address += len(data) - 3
|
|
del data
|
|
|
|
# print("_[Debug] @ spi recv data rx = ")
|
|
# for i in range(0, len(rx), self._single_data_len):
|
|
# print(rx[i:i+self._single_data_len])
|
|
# print()
|
|
|
|
# /*
|
|
# * red data formate:
|
|
# * ramHdr, ramHdr, ramHdr, (3B)
|
|
# * 255, #, data_length, (3B)
|
|
# * data, (20B)
|
|
# * red_wrong, red_retry_cnt, green_wrong, green_retry_cnt, (4B)
|
|
# * 255, #, data_length, (3B)
|
|
# * */
|
|
|
|
# mark read
|
|
self._spi.send_byte(self.MEM_INS_MARKED)
|
|
|
|
except BaseException as e:
|
|
print(e)
|
|
|
|
finally:
|
|
# print("\n")
|
|
self.pin_busy.output(True)
|
|
|
|
return bytes(rx)
|
|
|
|
def read_register(self) -> int:
|
|
return self._spi.send_byte([MEM_REG_READ, 0])[1]
|
|
|
|
def write_register(self, register: int):
|
|
self._spi.send_byte([MSM_REG_WRITE, register])
|
|
|
|
def read_memory(self, address: int, length: int) -> List[int]:
|
|
tx = [MEM_INS_READ, ((address >> 8) & 0xFF), (address & 0xFF)]
|
|
tx.extend(zero_buffer(length))
|
|
|
|
return self._spi.send_byte(tx)[3:]
|
|
|
|
def write_memory(self, address: int, data: Union[bytes, List[int]]):
|
|
tx = [MEM_INS_WRITE, ((address >> 8) & 0xFF), (address & 0xFF)]
|
|
|
|
if isinstance(data, bytes):
|
|
tx.extend(iter(data))
|
|
else:
|
|
tx.extend(data)
|
|
|
|
self._spi.send_byte(tx)
|
|
|
|
class ExtMemManager:
|
|
def __init__(self, ext_mem: MultiExtMemSpiInterface):
|
|
self._mem_sel = InputPin.get_used(P3Pin.MEM_SEL, pull_up_down=True)
|
|
self._mem_req = OutputPin.get_used(P3Pin.MEM_REQ, initial=True)
|
|
|
|
self._ext_mem = ext_mem
|
|
|
|
print('ExtMemManager.....')
|
|
print()
|
|
|
|
def mem_request(self):
|
|
self._mem_req.pulse()
|
|
sleep(_SLEEP_TIME_)
|
|
|
|
def get_ext_mem_register(self) -> List[Tuple[Optional[int], Optional[int]]]:
|
|
print('get_ext_mem_register......')
|
|
print()
|
|
ret = [(None, None) for _ in range(Selector.SIZE)]
|
|
|
|
for channel in self._ext_mem.foreach():
|
|
r = [None, None]
|
|
|
|
self.mem_request()
|
|
sleep(0.01)
|
|
m1 = int(self._mem_sel)
|
|
# print("m1 = ", m1)
|
|
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
|
|
r[m1] = self._ext_mem.read_register()
|
|
|
|
self.mem_request()
|
|
sleep(0.01)
|
|
m2 = int(self._mem_sel)
|
|
# print("m2 = ", m2)
|
|
self._ext_mem.write_register(DEFAULT_REGISTER_VALUE)
|
|
r[m2] = self._ext_mem.read_register()
|
|
|
|
ret[channel] = tuple(r)
|
|
|
|
return ret
|
|
|
|
@staticmethod
|
|
def is_no_device(result: Tuple[Optional[int], Optional[int]]) -> bool:
|
|
r1, r2 = result
|
|
return (r1 is None or r2 is None) and (r1 is None or r1 == 0) and (r2 is None or r2 == 0)
|
|
|
|
@staticmethod
|
|
def is_memory_test_fail(result: Tuple[Optional[int], Optional[int]]) -> int:
|
|
r1, r2 = result
|
|
|
|
if r1 is None or r2 is None:
|
|
return 1
|
|
|
|
if r1 is not None and r1 > 0 and r1 != DEFAULT_REGISTER_VALUE:
|
|
return 2
|
|
|
|
if r2 is not None and r2 > 0 and r2 != DEFAULT_REGISTER_VALUE:
|
|
return 3
|
|
|
|
return 0
|
|
|
|
def get_available_channel(self, result: List[Tuple[Optional[int], Optional[int]]] = None) -> List[int]:
|
|
print('get_available_channel......')
|
|
print()
|
|
if result is None:
|
|
result = self.get_ext_mem_register()
|
|
|
|
|
|
ret = []
|
|
|
|
for channel, result in enumerate(result):
|
|
if self.is_no_device(result):
|
|
continue
|
|
|
|
if self.is_memory_test_fail(result) != 0:
|
|
continue
|
|
|
|
ret.append(channel)
|
|
|
|
return ret
|
|
|