From 22786992f720c8b64b7435eda9c10a9664233af1 Mon Sep 17 00:00:00 2001 From: jinggaoyang-68 Date: Mon, 9 Dec 2024 21:04:49 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E7=89=88=E6=9C=AC?= =?UTF-8?q?=EF=BC=8C=E6=8E=A5=E8=BF=91=E6=95=B0=E6=8D=AE=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 41 ++++ cap_read.py | 155 +++++++++++++++ class_ch341.py | 265 +++++++++++++++++++++++++ class_finger.py | 160 +++++++++++++++ class_sensorcmd.py | 255 ++++++++++++++++++++++++ lib/ch341/CH341DLLA64.DLL | Bin 0 -> 68624 bytes lib/ch341/CH341DLLA64.LIB | Bin 0 -> 14986 bytes lib/ch341/ch341_lib.h | 399 ++++++++++++++++++++++++++++++++++++++ lib/ch341/libch347.so | Bin 0 -> 83352 bytes sensorPara.py | 66 +++++++ 10 files changed, 1341 insertions(+) create mode 100644 .gitignore create mode 100644 cap_read.py create mode 100644 class_ch341.py create mode 100644 class_finger.py create mode 100644 class_sensorcmd.py create mode 100644 lib/ch341/CH341DLLA64.DLL create mode 100644 lib/ch341/CH341DLLA64.LIB create mode 100644 lib/ch341/ch341_lib.h create mode 100644 lib/ch341/libch347.so create mode 100644 sensorPara.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a0f77d --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Python 编译产物和临时文件 +*.pyc +__pycache__/ +*.pyo +*.pyd +*.pyz +*.pyw + +# virtualenv / venv / env +env/ +venv/ +.venv/ + +# pipenv +.venv +Pipfile.lock + +# poetry +.venv +poetry.lock + +# PyCharm +.idea/ +*.iml + +# Visual Studio Code +.vscode/ + +# 日志和临时文件 +*.log +logs/ +temp/ +Log/ + +# macOS +.DS_Store + +# Windows +Thumbs.db + +det_images/* \ No newline at end of file diff --git a/cap_read.py b/cap_read.py new file mode 100644 index 0000000..ac23804 --- /dev/null +++ b/cap_read.py @@ -0,0 +1,155 @@ +from enum import * +import threading +import queue # 导入 queue 模块 +from class_ch341 import * +from class_sensorcmd import * +from class_finger import * +from collections import namedtuple +import socket + + +DEF_CDC_SYNC_MS = 1000 #电容同步间隔 +DEF_GET_CAP_MS = (30) #读取电容间隔 +DEF_PRO_CYC = 100 + +# 定义一个全局的队列,用于线程间通信 +capReadQueue = queue.Queue() + +# 341通信 +class EnumCh341ConnectStatus(Enum): + CH341_CONNECT_INIT = 0 + CH341_CONNECT_OPEN = 1 + CH341_CONNECT_SET_SPEED = 2 + CH341_CONNECT_SAMPLE_START = 3 + CH341_CONNECT_CHECK = 4 + CH341_CONNECT_SAMPLE_STOP = 5 + +class ClassCapRead: + def __init__(self): + self.ch341 = ClassCh341() + + # 最大连接5个手指 + self.fingers = list() # 传感器列表 + for i in range(5): + self.fingers.append(ClassFinger(2+i, self.ch341)) + + self.currCh341State = 0 # 当前ch341连接状态 + self.prevCh341State = 0 # 上次ch341连接状态 + + self.ch341CheckTimer = 0 + self.mcuInit = 0 + self.pcaAddr = 0x70 # iic控制芯片地址 + + self.ch341Init = 0 # ch341初始化标志位 + + self.syncTimer = 0 + + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT + + #连接到调试的服务器 + self.vofaClient = socket.socket() + addr = ('127.0.0.1', 1347) + try: + self.vofaClient.connect(addr) + # client.send('hello world\r\n'.encode()) + self.socketConnected = 1 + print('连接服务器成功') + except Exception as e: + self.socketConnected = 0 + print('连接服务器失败') + def __del__(self): + if self.vofaClient: + self.vofaClient.close() + self.ch341.disconnect() + print("ch341释放") + pass + + def set_sensor_enable(self, idx): + _pack = list() + _pack.append(idx) + self.ch341.write(self.pcaAddr, _pack) + + def ch341Connect(self): + if self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_INIT: + if(True == self.ch341.init()): + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_OPEN + elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_OPEN: + if(True == self.ch341.open()): + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SET_SPEED + else: + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT + elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_SET_SPEED: + if(True == self.ch341.set_speed(self.ch341.IIC_SPEED_400)): + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START + else: + print("set speed err") + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START + + elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_START: + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_CHECK + self.timer = threading.Timer(DEF_GET_CAP_MS/1000, self.capRead) + self.timer.start() + elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_CHECK: + self.ch341CheckTimer += DEF_PRO_CYC + if(self.ch341CheckTimer >= 1000): + self.ch341CheckTimer = 0 + if(False == self.ch341.connectCheck()): + print("ch341 拔出") + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_STOP + elif self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_SAMPLE_STOP: + self.syncTimer = 0 + for i in range(0, len(self.fingers)): + self.fingers[i].disconnected() + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT + else: + self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT + + def capRead(self): + capReadTime = time.time() + ms_capReadTime = capReadTime + + connectedSensorChan = 0 + connectedSensorCnt = 0 + for fingerIndex in range(0, len(self.fingers)): + self.set_sensor_enable(1 << (self.fingers[fingerIndex].pcaIdx)) + connectedSensorChan |= 1 << (self.fingers[fingerIndex].pcaIdx) + + if self.fingers[fingerIndex].connect == False: + if True == self.fingers[fingerIndex].checkSensor(): + print(f"sensor[{fingerIndex}] connected") + else: + print(f"addr = {fingerIndex}, connected false") + else: + self.fingers[fingerIndex].capRead() + connectedSensorCnt += 1 + + # 大于1个传感器连接需要设置接近采集序列 + if connectedSensorCnt > 1 and (time.time() - self.syncTimer) > DEF_CDC_SYNC_MS: + self.syncTimer = time.time() + self.set_sensor_enable(connectedSensorChan) + + if self.connectStatus == EnumCh341ConnectStatus.CH341_CONNECT_CHECK: + capReadTime = time.time() + difftime = int(capReadTime*1000-ms_capReadTime*1000) + print(f"diffTime={difftime}") + #定时器在任务完成后重新启动 + if(difftime>DEF_GET_CAP_MS): + timer = threading.Timer(DEF_GET_CAP_MS/1000, self.capRead) + else: + timer = threading.Timer((DEF_GET_CAP_MS-difftime)/1000, self.capRead) + timer.start() + + +def capReadThread(): + # 线程的主体功能 + cap = ClassCapRead() + + while True: + cap.ch341Connect() + time.sleep(DEF_PRO_CYC/1000) + +def main(): + capReadThread() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/class_ch341.py b/class_ch341.py new file mode 100644 index 0000000..a079371 --- /dev/null +++ b/class_ch341.py @@ -0,0 +1,265 @@ +import time +import os +import sys +from ctypes import * +import ctypes +import glob + +# ch341类:iic读写,int引脚读写,速度设置 +class ClassCh341: + # 接口固定宏 + _mCH341A_CMD_I2C_STREAM = 0xAA # I2C接口的命令包,从次字节开始为I2C命令流 + _mCH341A_CMD_I2C_STM_STA = 0x74 # I2C接口的命令流:产生起始位 + _mCH341A_CMD_I2C_STM_STO = 0x75 # I2C接口的命令流:产生停止位 + _mCH341A_CMD_I2C_STM_OUT = 0x80 # I2C接口的命令流:输出数据,位5-位0为长度,后续字节为数据,0长度则只发送一个字节并返回应答 + _mCH341A_CMD_I2C_STM_IN = 0xC0 # I2C接口的命令流:输入数据,位5-位0为长度,0长度则只接收一个字节并发送无应答 + _mCH341A_CMD_I2C_STM_MAX = 63 # I2C接口的命令流单个命令输入输出数据的最大长度 + _mCH341A_CMD_I2C_STM_SET = 0x60 # I2C接口的命令流:设置参数,位2=SPI的I/O数(0=单入单出,1=双入双出),位1位0=I2C速度(00=低速,01=标准,10=快速,11=高速) + _mCH341A_CMD_I2C_STM_US = 0x40 # I2C接口的命令流:以微秒为单位延时,位3-位0为延时值 + _mCH341A_CMD_I2C_STM_MS = 0x50 # I2C接口的命令流:以亳秒为单位延时,位3-位0为延时值 + _mCH341A_CMD_I2C_STM_DLY = 0x0F # I2C接口的命令流单个命令延时的最大值 + _mCH341A_CMD_I2C_STM_END = 0x00 # I2C接口的命令流:命令包提前结束 + + _mStateBitINT = 0x00000400 + + IIC_SPEED_20 = 0 + IIC_SPEED_100 = 1 + IIC_SPEED_400 = 2 + IIC_SPEED_750 = 3 + + def __init__(self): + self.deviceID = ctypes.c_uint32() + pass + + def init(self): + #print('ch341初始化') + + addr = os.path.dirname(sys.argv[0]) + r'/lib/ch341/CH341DLLA64.DLL' + print(addr) + dllExist = os.path.exists(addr) + if not dllExist: + print('未找到库文件') + return False + else: + try: + self.ic = windll.LoadLibrary(addr) # ch341接口 + print("ch341加载成功") + return True + except Exception as e: + print("ch341加载失败") + return False + + def connectCheck(self): + if True == self.ic.CH341GetInput(self.fd, ctypes.byref(self.deviceID)): + return True + else: + return False + + # 判断ch341是否插入 + # return:0未插入,1插入 + def open(self): + if os.name == 'nt': # Windows 环境 + try: + self.fd = self.ic.CH341OpenDevice(0) + if self.fd == -1: + print("CH341 device open failed on Windows.") + return False + else: + print("CH341 device opened successfully on Windows.") + return True + except Exception as e: + print(f"Error occurred while opening CH341 device on Windows: {e}") + return False + + elif os.name == 'posix': # Linux 环境 + try: + devices = glob.glob('/dev/ch34x_pis*') # 动态查找设备 + if devices: + device_path = devices[0].encode() + self.fd = self.ic.CH34xOpenDevice(device_path) + if self.fd == -1: + print("CH341 device open failed on Linux.") + return False + else: + print("CH341 device opened successfully on Linux.") + return True + else: + print("No CH341 device found on Linux.") + return False + except Exception as e: + print(f"Error occurred while opening CH341 device on Linux: {e}") + return False + + else: + print("Unsupported operating system.") + return False + def open1(self): + # 查找匹配的设备路径 + self.fd = self.ic.CH341OpenDevice(0) + if self.fd == -1: + print("ch341 open false") + return False + else: + print("ch341 open true") + return True + # devices = glob.glob('/dev/ch34x_pis*') + # if devices: + # device_path = devices[0].encode() # 选择第一个找到的设备路径 + # self.fd = self.ic.CH34xOpenDevice(device_path)#.encode()"/dev/ch34x_pis1".encode() + # if self.fd == -1: + # print("ch341 open false") + # return False + # else: + # print("ch341 open true") + # return True + # else: + # print("No CH34x device found.") + # return False + + + def disconnect(self): + self.ic.CH341CloseDevice(0) + + # iic写数据 + # addr:iic从机地址 + # data:要写的数据列表 + # return:写入长度,不一定正确 + def write(self, addr, data): + sLen = len(data) + tmpData = [] # 临时列表 + tmpLen = sLen # 发送数据 + + pack = [] # 发送列表 + cnt = 20 # 每包数量 + packNum = sLen // cnt # 拆包数量 + sLen %= cnt # 不足字节数 + + tmpData.extend(data) + + pack.append(self._mCH341A_CMD_I2C_STREAM) + pack.append(self._mCH341A_CMD_I2C_STM_STA) + pack.append(self._mCH341A_CMD_I2C_STM_OUT | 1) + pack.append(addr << 1) + for i in range(0, packNum): + pack.append(self._mCH341A_CMD_I2C_STM_OUT | cnt) + pack.extend(tmpData[0:20]) + del tmpData[0:20] + pack.append(self._mCH341A_CMD_I2C_STM_END) + sendBuf = (c_byte * len(pack))() + for j in range(0, len(pack)): + sendBuf[j] = pack[j] + sendLen = (c_byte * 1)() + sendLen[0] = len(pack) + if not self.ic.CH341WriteData(0, sendBuf, sendLen): + return 0 + if sendLen == 0: + return 0 + pack.clear() + pack.append(self._mCH341A_CMD_I2C_STREAM) + if sLen >= 1: + pack.append(self._mCH341A_CMD_I2C_STM_OUT | sLen) + pack.extend(tmpData[0:sLen]) + pack.append(self._mCH341A_CMD_I2C_STM_STO) + pack.append(self._mCH341A_CMD_I2C_STM_END) + sendBuf = (c_byte * len(pack))() + for j in range(0, len(pack)): + sendBuf[j] = pack[j] + sendLen = (c_byte * 1)() + sendLen[0] = len(pack) + if not self.ic.CH341WriteData(0, sendBuf, sendLen): + return 0 + if sendLen == 0: + return 0 + return tmpLen + + # iic读数据 + # addr:iic从机地址 + # data:读取数据列表。根据列表大小确定读取长度 + # return:读取长度,不一定正确 + def read(self, addr, data): + if id(data) == 0 or len(data) == 0: + return 0 + rLen = len(data) + #print(f"rLen={rLen}") + pack = [] + readBuf = [] + readLen = 0 + + packNum = rLen // 30 + rLen %= 30 + if rLen == 0: + rLen = 30 + packNum -= 1 + #print(f"packNum={packNum}") + pack.append(self._mCH341A_CMD_I2C_STREAM) + pack.append(self._mCH341A_CMD_I2C_STM_STA) + pack.append(self._mCH341A_CMD_I2C_STM_OUT | 1) + pack.append((addr << 1) | 0x01) + pack.append(self._mCH341A_CMD_I2C_STM_MS | 1) + for i in range(0, packNum): + pack.append(self._mCH341A_CMD_I2C_STM_IN | 30) + pack.append(self._mCH341A_CMD_I2C_STM_END) + sendBuf = (c_byte * len(pack))() + for j in range(0, len(pack)): + sendBuf[j] = pack[j] + recLen = (c_byte * 1)() + recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)() + if not self.ic.CH341WriteRead(0, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): + return 0 + if recLen == 0: + return 0 + for j in range(0, recLen[0]): + readBuf.append(recBuf[j]) + readLen += 30 + pack.clear() + pack.append(self._mCH341A_CMD_I2C_STREAM) + if rLen > 1: + pack.append(self._mCH341A_CMD_I2C_STM_IN | (rLen - 1)) + pack.append(self._mCH341A_CMD_I2C_STM_IN | 0) + pack.append(self._mCH341A_CMD_I2C_STM_STO) + pack.append(self._mCH341A_CMD_I2C_STM_END) + sendBuf = (c_byte * len(pack))() + for j in range(0, len(pack)): + sendBuf[j] = pack[j] + recLen = (c_byte * 1)() + recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)() + if not self.ic.CH341WriteRead(0, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): + return 0 + if recLen[0] == 0: + return 0 + for j in range(0, recLen[0]): + readBuf.append(recBuf[j]) + data.clear() + data.extend(readBuf) + readLen = len(pack) + #print(f"readLen={len(readBuf)}") + return readLen + + # 设置int引脚状态 + # lvl:高低电平。1高电平,0低电平 + def set_int(self, lvl): + status = (c_long * 1)() + self.ic.CH341GetInput(0, status) + time.sleep(0.01) + if lvl: + self.ic.CH341SetOutput(0, 0x03, 0xFF00, status[0] | self._mStateBitINT) + else: + self.ic.CH341SetOutput(0, 0x03, 0xFF00, status[0] & (~self._mStateBitINT)) + + # 读取int引脚状态 + # 返回:高低电平 + def get_int(self): + status = (c_long * 1)() + self.ic.CH341GetInput(0, status) + return (status[0] & self._mStateBitINT) >> 10 + + # 设置IIC速度 + # return:0错误,1成功 + def set_speed(self, speed): + if speed != self.IIC_SPEED_20 and speed != self.IIC_SPEED_100 and speed != self.IIC_SPEED_400 and speed != self.IIC_SPEED_750: + return False + if False == self.ic.CH341SetStream(0, speed | 0): + print("speed err") + return False + else: + return True diff --git a/class_finger.py b/class_finger.py new file mode 100644 index 0000000..c1814e2 --- /dev/null +++ b/class_finger.py @@ -0,0 +1,160 @@ + +from class_sensorcmd import * +from sensorPara import * +import struct + + +# 电容数据存储结构 +class capData: + def __init__(self): + self.sensorIndex = 0 # 电容序号,与iic addr相同 + self.channelCapData = list() # 原始通道数值 + self.tf = list() # 切向力数组 + self.tfDir = list() # 切向力方向数组 + self.nf = list() # 法向力数组 + self.sProxCapData = list() # 接近(自电容)数组 + self.mProxCapData = list() # 接近(互电容)数组 + self.cnt = 0 # 计数,测试用 + + def init(self, addr, yddsNum, sProxNum, mProxNum, capChannelNum): + self.sensorIndex = addr # 电容序号,与iic addr相同 + self.channelCapData = list(range(capChannelNum))# 原始通道数值 + self.tf = list(range(yddsNum)) # 切向力数组 + self.tfDir = list(range(yddsNum)) # 切向力方向数组 + self.nf = list(range(yddsNum)) # 法向力数组 + self.sProxCapData = list(range(sProxNum)) # 接近(自电容)数组 + self.mProxCapData = list(range(mProxNum)) # 接近(互电容)数组 + self.cnt = 0 # 计数,测试用 + + def deinit(self): + self.channelCapData = None + self.tf = None + self.tfDir = None + self.nf = None + self.sProxCapData = None + self.mProxCapData = None + +# 传感器类:包换传感器相关参数 +class ClassFinger: + def __init__(self, pca_idx, ch341): + self.snsCmd = ClassSensorCmd(ch341) + self.pcaIdx = pca_idx # iic使能芯片序号,从2开始 + self.readData = capData() + self.disconnected() + + # 检查传感器是否连接,如果读写地址正确则认为连接正常 + def checkSensor(self): + # 广播的方式读取当前传感器地址,并默认将iic地址配置为和pca相同的地址 + addrRead = self.snsCmd.getAddr(0) + if addrRead != self.pcaIdx: + if self.pcaIdx != self.snsCmd.setAddr(addrRead, self.pcaIdx): + print(f"set addr false, setaddr={self.pcaIdx}") + return False + else: + addrRead = self.pcaIdx + print(f"update iic addr, new addr ={addrRead}") + + # 设置发送数据类型为原始值 + if True != self.snsCmd.setSensorSendType(addrRead, 0): + print(f"setSensorSendType err, addr = {addrRead}") + + # 设置电容采集序列,这里按照地址来分配采集时序,只要每个传感器不同即可 + if self.snsCmd.setSensorCapOffset(addrRead, addrRead) != True: + print(f"setSensorCapOffset err, addr = {addrRead}") + + # 实际用户使用中只需要根据使用的传感器来定义参数即可,不需要读取项目号 + projectRead = self.snsCmd.getSensorProjectIdex(self.addr) + print(f"project={projectRead}") + findProjectFlg = False + if projectRead > 0: + for pro in finger_params: + if pro.prg == projectRead: + self.projectPara = pro + print(f"finger connected, project = {self.projectPara.name}") + findProjectFlg = True + + if findProjectFlg == False: + print("not found vailed project, project para use default") + + self.connected(addrRead) + + return True + + # 传感器连接,初始化参数 + def connected(self, addr): + self.addr = addr + self.connect = True + self.connectTimer = time.time() + self.packIdx = 0 + self.data = list() + self.data.extend(range(self.projectPara.pack_len))# + + self.readData.init(addr, self.projectPara.ydds_num, self.projectPara.s_prox_num, self.projectPara.m_prox_num, self.projectPara.sensor_num) + + #print(f"datalen={len(self.data)}") + + # 传感器断开,复位参数 + def disconnected(self): + self.addr = 0xFF # iic地址 + self.connect = False # 连接标志位 + self.packIdx = 0 # 采样序号 + self.connectTimer = 0 # 连接超时计时 + + self.projectPara = finger_params[0] + + self.readData.deinit() + + def capRead(self): + for retry in range(0, 3): + if self.snsCmd.getSensorCapData(self.addr, self.data) == True: + if self.data[5] != self.projectPara.sensor_num: + print(f"cap channel num err, read num = {self.data[5]}, expect num = {self.projectPara.sensor_num}") + + if self.data[4] != self.packIdx: + self.packIdx = self.data[4] + self.connectTimer = time.time() + + # 根据通道值占用字节大小获取通道数据 + if self.projectPara.cap_byte == 4: + for j in range(0, self.projectPara.sensor_num): + self.readData.channelCapData[j] = ((self.data[6 + j * self.projectPara.cap_byte] & 0xFF) + + ((self.data[6 + j * self.projectPara.cap_byte + 1] & 0xFF) << 8) + + ((self.data[6 + j * self.projectPara.cap_byte + 2] & 0xFF) << 16) + + ((self.data[6 + j * 4 + 3] & 0xFF) << 24)) + else: + for j in range(0, self.projectPara.sensor_num): + self.readData.channelCapData[j] = ((self.data[6 + j * self.projectPara.cap_byte] & 0xFF) + + ((self.data[6 + j * self.projectPara.cap_byte + 1] & 0xFF) << 8) + + ((self.data[6 + j * self.projectPara.cap_byte + 2] & 0xFF) << 16)) + + yddsOffset = 6 + self.projectPara.sensor_num*self.projectPara.cap_byte + + if self.projectPara.ydds_type == 2: + struct_size = sizeof(DynamicYddsComTs) + for i in range(self.projectPara.ydds_num): + offset = yddsOffset + i * struct_size + struct_data = self.data[offset : offset + struct_size] + struct_data = [value & 0xFF for value in struct_data] + struct_data = bytes(struct_data) # 转换为 bytes 类型 + instance = DynamicYddsComTs.from_buffer_copy(struct_data) + self.readData.nf[i] = instance.nf + self.readData.tf[i] = instance.tf + self.readData.tfDir[i] = instance.tfDir + self.readData.sProxCapData[i] = instance.prox + elif self.projectPara.ydds_type == 4: + pass + + print(f"capChannelDat={self.readData.channelCapData}") + for i in range(self.projectPara.ydds_num): + print(f"nf[{i}] = {self.readData.nf[i]}") + print(f"tf[{i}] = {self.readData.tf[i]}") + print(f"tfDir[{i}] = {self.readData.tfDir[i]}") + print(f"sProxCapData[{i}] = {self.readData.sProxCapData[i]}") + + break + else: + pass + + # 2S未接收到数据超时 + if (time.time() - self.connectTimer) > 2: + self.disconnected() diff --git a/class_sensorcmd.py b/class_sensorcmd.py new file mode 100644 index 0000000..818fd55 --- /dev/null +++ b/class_sensorcmd.py @@ -0,0 +1,255 @@ +import time +from class_ch341 import * +from enum import * + + +class ClassSensorCmd: + def __init__(self, ch341): + # 命令定义 + self.CMD_GET_CHANNEL_NUM = 0x01 # 读取通道数量 + self.CMD_GET_SENSOR_CAP_DATA = 0x60 # 读取传感器数据 + self.CMD_SET_SENSOR_UART_SEND_TYPE = 0x61 # 设置串口输出模式 + self.CMD_GET_SENSOR_CHANNEL = 0x62 # 读取通道数量 + self.CMD_SET_SENSOR_AUTO_DAC = 0x63 # 自动dac + self.CMD_GET_SENSOR_ERR_CODE = 0x64 # 读取错误码 + self.CMD_SET_SENSOR_CHANNEL_CALIBRATE = 0x65 # 设置通道重量标定 + self.CMD_GET_SENSOR_CHANNEL_CALIBRATE = 0x66 # 读取通道重量标定 + self.CMD_SET_SENSOR_AUTO_THRESHOLD = 0x67 # 设置自动阈值 + self.CMD_GET_SENSOR_THRESHOLD = 0x68 # 读取自动阈值 + self.CMD_SET_SENSOR_THRESHOLD = 0x69 # 设置通道阈值 + self.CMD_SET_SENSOR_TOUCH_THRESHOLD = 0x6A # 设置压力阈值 + self.CMD_SET_SENSOR_CHANNEL_CALIBRATE_QUICK = 0x6B # 最大力快速标定 + self.CMD_GET_SENSOR_TEST_HZ = 0x6C # 读取采样率 + self.CMD_GET_SENSOR_CALI_N_LIST = 0x6D # 读取标定列表 + self.CMD_SET_SENSOR_CHANNEL_GROUP_CALIBRATE = 0x6E # 设置通道组重量标定 + + self.CMD_SET_SENSOR_IIC_ADDR = 0x70 # 设置传感器IIC地址 + self.CMD_GET_SENSOR_IIC_ADDR = 0x71 # 读取传感器IIC地址 + self.CMD_SET_SENSOR_CDC_SYNC = 0x72 # 采集停止,用于同步 + self.CMD_SET_SENSOR_CDC_START_OFFSET = 0x73 # 设置电容开始偏移 + self.CMD_GET_SENSOR_TEMP = 0x74 # 读取温度 + self.CMD_SET_SENSOR_CDC_CFG_SAVE = 0x75 # 保存cdc配置 + self.CMD_SET_SENSOR_FACTOR = 0x76 # 恢复出厂设置 + self.CMD_SET_SENSOR_RESTART = 0x77 # 软件复位 + self.CMD_SET_SENSOR_WEIGHT_CALIBRATE = 0x78 # 设置重量标定参数 + self.CMD_SET_SENSOR_BASE_RESET = 0x79 # 基线复位 + self.CMD_SET_SENSOR_UNIFORMIZATION = 0x7A # 归一化 + self.CMD_SET_SENSOR_CHANNEL_ENABLE = 0x7B # 通道使能 + self.CMD_SET_SENSOR_CHANNEL_BASE_RESET = 0x7C # 单独通道复位 + self.CMD_GET_SENSOR_WEIGHT_CALIBRATE = 0x7D # 读取重量标定参数 + self.CMD_GET_SENSOR_CHANNEL_STATE = 0x7E # 读取电容通道状态 + self.CMD_SET_SENSOR_SEND_TYPE = 0x7F # 数据传输类型 + + self.CMD_GET_VERSION = 0xA0 # 读取软件版本 + self.CMD_SOFT_RESTART = 0xA1 # 软件复位 + self.CMD_GET_TYPE = 0xA2 # 读取设备类型 + self.CMD_SET_TYPE = 0xA3 # 设置设备类型 + self.CMD_SET_INF = 0xA5 # 设置输出接口 + self.CMD_GET_PRG = 0xA6 # 获取项目类型 + + self._ch341 = ch341 + self.sendTimePre = list() + self.sendTimePre.append(time.time()) + self.sendTimePre.append(time.time()) + self.sendTimeNow = list() + self.sendTimeNow.append(time.time()) + self.sendTimeNow.append(time.time()) + self.sendCnt = list() + self.sendCnt.append(0) + self.sendCnt.append(0) + + # 计算校验和 + # pack 数据 + def calcSum(self, pack): + if len(pack) <= 5: + return 0 + _sum = 0 + for i in range(0, len(pack)): + _sum += (pack[i] & 0xFF) + pack.append(_sum & 0xFF) + pack.append(_sum >> 8) + + # 检查校验值 + # pack:要检查的数据 + # return 0:校验值不一致 + # 1:校验值一致 + def checkSum(self, pack): + if len(pack) <= 5: + return False + _sum = 0 + for i in range(0, len(pack) - 2): + _sum += (pack[i] & 0xFF) + chkL = _sum & 0xFF + chkH = (_sum >> 8) & 0xFF + rchkL = pack[len(pack) - 2] & 0xFF + rchkH = pack[len(pack) - 1] & 0xFF + if chkL == rchkL and chkH == rchkH: + return True + else: + return False + + # 设置传感器iic地址 + # addr:当前地址 + # new_addr:系地址 + # return:读取的地址 + def setAddr(self, addr, new_addr): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_SET_SENSOR_IIC_ADDR) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(new_addr) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(11))) + time.sleep(0.01) + self._ch341.read(new_addr, _pack) + checksum = self.checkSum(_pack) + if checksum == True and ((self.CMD_SET_SENSOR_IIC_ADDR|0x80) == c_uint8(_pack[3]).value): + return _pack[7] & 0xFF + return 0 + + def getAddr(self, addr): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_GET_SENSOR_IIC_ADDR) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(11))) + time.sleep(0.01) + self._ch341.read(addr, _pack) + if self.checkSum(_pack) == True: + return _pack[7] & 0xFF + return 0 + + def getSensorVersion(self, addr): + pass + + # 读取电容通道数 + # addr:传感器地址 + def getSensorNum(self, addr): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_GET_CHANNEL_NUM) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(15))) + time.sleep(0.01) + self._ch341.read(addr, _pack) + if self.checkSum(_pack) == True: + return (_pack[5] & 0xFF) + (_pack[6] & 0xFF) * 256 + return 0 + + # 读取项目编号 + # addr:传感器地址 + def getSensorProjectIdex(self, addr): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_GET_PRG) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(11))) + time.sleep(0.01) + self._ch341.read(addr, _pack) + if self.checkSum(_pack) == True: + return (_pack[7] & 0xFF) + (_pack[8] & 0xFF) * 256 + return 0 + + # 设置发送类型 + # addr:传感器地址 + def setSensorSendType(self, addr, sendType): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_SET_SENSOR_SEND_TYPE) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(sendType) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(11))) + time.sleep(0.01) + self._ch341.read(addr, _pack) + if (self.checkSum(_pack) == True) and ((self.CMD_SET_SENSOR_SEND_TYPE|0x80) == c_uint8(_pack[3]).value): + return True + return False + + # 设置采集偏置 + # addr:传感器地址 + def setSensorCapOffset(self, addr, offset): + _pack = list() + _pack.append(0xAA) + _pack.append(0x55) + _pack.append(0x03) + _pack.append(self.CMD_SET_SENSOR_CDC_START_OFFSET) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(0x00) + _pack.append(offset) + _pack.append(0x00) + self.calcSum(_pack) + self._ch341.write(addr, _pack) + _pack.clear() + _pack.extend(list(range(6))) + time.sleep(0.01) + self._ch341.read(addr, _pack) + checksum = self.checkSum(_pack) + if (checksum == True) and ((self.CMD_SET_SENSOR_CDC_START_OFFSET|0x80) == c_uint8(_pack[3]).value): + return True + return False + + # 读取电容数据 + # addr:传感器地址 + def getSensorCapData(self, addr, buf): + tarLen = len(buf) + err = self._ch341.read(addr, buf) + if(err == 0): + print("get data err") + checkSum = self.checkSum(buf) + + if(tarLen != len(buf)): + buf.clear() + buf.extend(range(tarLen)) + # try: + if (len(buf) == tarLen and buf[0] & 0xFF) == 0x55 and (buf[1] & 0xFF) == 0xAA and checkSum == True: + return True + else: + #print(f"time={time.time()},len(buf) = {len(buf)}, tarLen={tarLen}, buf[0]={buf[0]}, buf[1]={buf[1]}, sum={checkSum}") + pass + return False + + + diff --git a/lib/ch341/CH341DLLA64.DLL b/lib/ch341/CH341DLLA64.DLL new file mode 100644 index 0000000000000000000000000000000000000000..12a11a558dba31f3abd86cc136044eecdae911ba GIT binary patch literal 68624 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 literal 0 HcmV?d00001 diff --git a/lib/ch341/CH341DLLA64.LIB b/lib/ch341/CH341DLLA64.LIB new file mode 100644 index 0000000000000000000000000000000000000000..c62ba77f13425dc54f9fdffd80d95b066b467cd8 GIT binary patch literal 14986 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 SaS4CT?Mv{Nmo2pT?DT*8$x9Rf literal 0 HcmV?d00001 diff --git a/lib/ch341/ch341_lib.h b/lib/ch341/ch341_lib.h new file mode 100644 index 0000000..88b0093 --- /dev/null +++ b/lib/ch341/ch341_lib.h @@ -0,0 +1,399 @@ +#ifndef _CH341_LIB_H +#define _CH341_LIB_H + +typedef enum _EEPROM_TYPE { + ID_24C01, + ID_24C02, + ID_24C04, + ID_24C08, + ID_24C16, + ID_24C32, + ID_24C64, + ID_24C128, + ID_24C256, + ID_24C512, + ID_24C1024, + ID_24C2048, + ID_24C4096 +} EEPROM_TYPE; + +typedef enum _CHIP_TYPE { + CHIP_CH341 = 0, + CHIP_CH347T = 1, + CHIP_CH347F = 2, +} CHIP_TYPE; + +typedef enum { + TYPE_TTY = 0, + TYPE_HID, + TYPE_VCP, +} FUNCTYPE; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * CH34XOpenDevice - open device + * @pathname: device path in /dev directory + * + * The function return positive file descriptor if successful, others if fail. + */ +extern int CH34xOpenDevice(const char *pathname); + +/** + * CH34XCloseDevice - close device + * @fd: file descriptor of device + * + * The function return true if successful, false if fail. + */ +extern bool CH34xCloseDevice(int fd); + +/** + * CH34x_GetDriverVersion - get vendor driver version + * @fd: file descriptor of device + * @Drv_Version: pointer to version string + * + * The function return true if successful, false if fail. + */ +extern bool CH34x_GetDriverVersion(int fd, unsigned char *Drv_Version); + +/** + * CH34x_GetChipVersion - get chip version + * @fd: file descriptor of device + * @Version: pointer to version + * + * The function return true if successful, false if fail. + */ +extern bool CH34x_GetChipVersion(int fd, unsigned char *Version); + +/** + * CH34x_GetChipType - get chip type + * @fd: file descriptor of device + * @ChipType: pointer to chip type + * + * The function return true if successful, false if fail. + */ +extern bool CH34x_GetChipType(int fd, CHIP_TYPE *ChipType); + +/** + * CH34X_GetDeviceID - get device vid and pid + * @fd: file descriptor of device + * @id: pointer to store id which contains vid and pid + * + * The function return true if successful, false if fail. + */ +extern bool CH34X_GetDeviceID(int fd, uint32_t *id); + +/** + * CH34xSetParaMode - set chip parrallel work mode + * @fd: file descriptor of device + * @Mode: work mode, 0/1->EPP mode, 2->MEM mode + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSetParaMode(int fd, uint8_t Mode); + +/** + * CH34xInitParallel - initial chip parrallel work mode + * @fd: file descriptor of device + * @Mode: work mode, 0/1->EPP mode, 2->MEM mode + * + * The function return true if successful, false if fail. + */ +extern bool CH34xInitParallel(int fd, uint8_t Mode); + +/** + * CH34xEppRead - read data or addr in parrallel EPP mode + * @fd: file descriptor of device + * @oBuffer: pointer to read buffer + * @ioLength: read length + * @PipeMode: 0->read pipe0 data, 1->read pipe1 addr + * + * The function return read 0 if successful, others if fail. + */ +extern int CH34xEppRead(int fd, uint8_t *oBuffer, uint32_t ioLength, uint8_t PipeMode); + +/** + * CH34xEppWrite - write data or addr in parrallel EPP mode + * @fd: file descriptor of device + * @iBuffer: pointer to write buffer + * @ioLength: write length + * @PipeMode: 0->write pipe0 data, 1->write pipe1 addr + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppWrite(int fd, uint8_t *iBuffer, uint32_t ioLength, uint8_t PipeMode); + +/** + * CH34xEppWriteData - write data in parrallel EPP mode + * @fd: file descriptor of device + * @iBuffer: pointer to write buffer + * @ioLength: write length + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppWriteData(int fd, uint8_t *iBuffer, uint32_t ioLength); + +/** + * CH34xEppReadData - read data in parrallel EPP mode + * @fd: file descriptor of device + * @oBuffer: pointer to read buffer + * @ioLength: read length + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppReadData(int fd, uint8_t *oBuffer, uint32_t ioLength); + +/** + * CH34xEppWriteAddr - write addr in parrallel EPP mode + * @fd: file descriptor of device + * @iBuffer: pointer to write buffer + * @ioLength: write length + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppWriteAddr(int fd, uint8_t *iBuffer, uint32_t ioLength); + +/** + * CH34xEppReadAddr - read addr in parrallel EPP mode + * @fd: file descriptor of device + * @oBuffer: pointer to read buffer + * @ioLength: read length + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppReadAddr(int fd, uint8_t *oBuffer, uint32_t ioLength); + +/** + * CH34xEppSetAddr - set addr in parrallel EPP mode + * @fd: file descriptor of device + * @iAddr: addr data + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xEppSetAddr(int fd, uint32_t iAddr); + +/** + * CH34xSetTimeout - set USB data read and write timeout + * @fd: file descriptor of device + * @iWriteTimeout: data download timeout in milliseconds + * @iReadTimeout: data upload timeout in milliseconds + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSetTimeout(int fd, uint32_t iWriteTimeout, uint32_t iReadTimeout); + +/** + * CH34xInitMEM - initial chip in parrallel MEM mode + * @fd: file descriptor of device + * + * The function return true if successful, false if fail. + */ +extern bool CH34xInitMEM(int fd); + +/** + * CH34xMEMReadData - read data in parrallel MEM mode + * @fd: file descriptor of device + * @oBuffer: pointer to read buffer + * @ioLength: read length + * @PipeMode: 0->read pipe0, 1->read pipe1 + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xMEMReadData(int fd, uint8_t *oBuffer, uint32_t ioLength, uint8_t PipeMode); + +/** + * CH34xMEMWriteData - write data in parrallel MEM mode + * @fd: file descriptor of device + * @iBuffer: pointer to write buffer + * @ioLength: write length + * @PipeMode: 0->write pipe0, 1->write pipe1 + * + * The function return 0 if successful, others if fail. + */ +extern int CH34xMEMWriteData(int fd, uint8_t *iBuffer, uint32_t ioLength, uint32_t PipeMode); + +/** + * CH34xSetStream - configure spi/i2c interface in stream mode + * @fd: file descriptor of device + * @Mode: stream mode + * ->bit0~1: set I2C SCL rate + * --> 00 : low rate 20KHz + * --> 01 : standard rate 100KHz + * --> 10 : fast rate 400KHz + * --> 11 : high rate 750KHz + * ->bit2: set spi mode + * --> 0 : one in one out(D3: clk, D5: out, D7: in) + * --> 1 : two in two out(D3 :clk, D4/D5: out, D6/D7 :in) + * ->bit7: set spi data mode + * --> 0 : low bit first + * --> 1 : high bit first + * other bits must keep 0 + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSetStream(int fd, uint8_t Mode); + +/** + * CH34xSetDelaymS - delay operation + * @fd: file descriptor of device + * @iDelay: delay time in millseconds + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSetDelaymS(int fd, uint32_t iDelay); + +/** + * CH34xReadData - read for spi/i2c operation + * @fd: file descriptor of device + * @oReadBuffer: pointer to read buffer + * @oReadLength: pointer to read length + * + * The function return true if successful, false if fail. + */ +extern bool CH34xReadData(int fd, void *oReadBuffer, uint32_t *oReadLength); + +/** + * CH34xWriteData - write data for spi/i2c operation + * @fd: file descriptor of device + * @iWriteBuffer: pointer to write buffer + * @iWriteLength: pointer to write length + * + * The function return true if successful, false if fail. + */ +extern bool CH34xWriteData(int fd, void *iWriteBuffer, uint32_t *iWriteLength); + +/** + * CH34xWriteRead - write data then read for spi/i2c operation + * @fd: file descriptor of device + * @iWriteLength: write length + * @iWriteBuffer: pointer to write buffer + * @iReadStep: per read length + * @iReadTimes: read times + * @oReadLength: pointer to read length + * @oReadBuffer: pointer to read buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xWriteRead(int fd, uint32_t iWriteLength, void *iWriteBuffer, uint32_t iReadStep, uint32_t iReadTimes, + uint32_t *oReadLength, void *oReadBuffer); + +/** + * CH34xGetInput - get io status of CH341 + * @fd: file descriptor of device + * @iStatus: pointer to io status + * Note: + * Bit7~Bit0<==>D7-D0, Bit8<==>ERR#, Bit9<==>PEMP, Bit10<==>INT# + * Bit11<==>SLCT, Bit13<==>WAIT#, Bit14<==>DATAS#/READ#, Bit15<==>ADDRS#/ADDR/ALE, Bit23<==>SDA + * + * The function return true if successful, false if fail. + */ +extern bool CH34xGetInput(int fd, uint32_t *iStatus); + +/** + * CH34xSetOutput - set direction and output data of CH341 + * @fd: file descriptor of device + * @iEnable: set direction and data enable + * --> Bit16 High : effect on Bit15~8 of iSetDataOut + * --> Bit17 High : effect on Bit15~8 of iSetDirOut + * --> Bit18 High : effect on Bit7~0 of iSetDataOut + * --> Bit19 High : effect on Bit7~0 of iSetDirOut + * --> Bit20 High : effect on Bit23~16 of iSetDataOut + * @iSetDirOut: set io direction + * -- > Bit High : Output + * -- > Bit Low : Input + * @iSetDataOut: set io data + * Output: + * -- > Bit High : High level + * -- > Bit Low : Low level + * Note: + * Bit7~Bit0<==>D7-D0, Bit8<==>ERR#, Bit9<==>PEMP, Bit10<==>INT# + * Bit11<==>SLCT, Bit13<==>WAIT#, Bit14<==>DATAS#/READ#, Bit15<==>ADDRS#/ADDR/ALE + * + * The pins below can only be used in output mode: + * Bit16<==>RESET#, Bit17<==>WRITE#, Bit18<==>SCL, Bit29<==>SDA + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSetOutput(int fd, uint32_t iEnable, uint32_t iSetDirOut, uint32_t iSetDataOut); + +/** + * CH34xSet_D5_D0 - set direction and output data of D5-D0 on CH341 + * @fd: file descriptor of device + * @iSetDirOut: set io direction + * -- > Bit High : Output + * -- > Bit Low : Input + * @iSetDataOut: set io data + * Output: + * -- > Bit High : High level + * -- > Bit Low : Low level + * + * The function return true if successful, false if fail. + */ +extern bool CH34xSet_D5_D0(int fd, uint8_t iSetDirOut, uint8_t iSetDataOut); + +/** + * CH34xStreamI2C - write/read i2c in stream mode + * @fd: file descriptor of device + * @iWriteLength: write length + * @iWriteBuffer: pointer to write buffer + * @iReadLength: read length + * @oReadBuffer: pointer to read buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xStreamI2C(int fd, uint32_t iWriteLength, void *iWriteBuffer, uint32_t iReadLength, void *oReadBuffer); + +/** + * CH34xReadEEPROM - read data from eeprom + * @fd: file descriptor of device + * @iEepromID: eeprom type + * @iAddr: address of eeprom + * @iLength: read length + * @oBuffer: pointer to read buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xReadEEPROM(int fd, EEPROM_TYPE iEepromID, uint32_t iAddr, uint32_t iLength, uint8_t *oBuffer); + +/** + * CH34xWriteEEPROM - write data to eeprom + * @fd: file descriptor of device + * @iEepromID: eeprom type + * @iAddr: address of eeprom + * @iLength: write length + * @iBuffer: pointer to write buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xWriteEEPROM(int fd, EEPROM_TYPE iEepromID, uint32_t iAddr, uint32_t iLength, uint8_t *iBuffer); + +/** + * CH34xStreamSPIx - write/read spi in stream mode + * @fd: file descriptor of device + * @iChipSelect: cs enable + * @iLength: the length of data + * @ioBuffer: one in one out buffer + * @ioBuffer2: two in two out buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xStreamSPIx(int fd, uint32_t iChipSelect, uint32_t iLength, void *ioBuffer, void *ioBuffer2); + +/** + * CH34xStreamSPI4 - write/read spi in 4-line stream mode + * @fd: file descriptor of device + * @iChipSelect: cs enable + * @iLength: the length of data + * @ioBuffer: one in one out buffer + * + * The function return true if successful, false if fail. + */ +extern bool CH34xStreamSPI4(int fd, uint32_t iChipSelect, uint32_t iLength, void *ioBuffer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/ch341/libch347.so b/lib/ch341/libch347.so new file mode 100644 index 0000000000000000000000000000000000000000..a2d78e8e0b59ece1f667a6659144972631988377 GIT binary patch literal 83352 zcmeFa34ByV);4}SYyr`Z2+Al*Tu=cQL=+H^AT%^68i63rsDKb8$`X?fi$gHjX-(U< z6PG9|qv)uEii!)12-rbQBEx7<95b33qvF_YGh@UpDw6McPSw4AyVD(D-kJCReg7W~ zxm9&e)u~gbPF0;+Zrz(*myAwxI1KaG#<(;-Ydg z@yI;7ty5uN@OLKu&cYvmXDi1zN4x#u4#3~}_#247!T2+O7r-+Fe?#$i zA^wKrkG~Q4bK!3c{>I|(V*Fi#zw!9v?=t*dj=zZv;5SJ*Mh@J`_`4E+S7{iXYw$N! zho@_IrgpE>?)7lz;BT%D=fk~G`wO*eIv#k6@V5Ye3-PxYfBfBqzu)3-3I2-l$KO)? z-7LP?qeOdef$NQdjZZf3oA8h2`M){!!={S@AMCro}Kivp>poPWkgkAHMqTd!HOPy<(s1)t$TU`@^H7Pxz(a z>?g~I?3umz`>yVh%SY8u+8eANK5p>8JU8F=N~ag!Z@lK#l5szef3erg6Q^wqR$j2N z>%R9UeK%-zr}{zRpI-XLoB!(iX#F|!XZ(HB&(HPx&C~sVncU?!NB(NUKkKiZdf5Bt zrM>j)2VU~+IBjG7{73$2>>GFC`fJiIe(R-<=RWgfpW1ytk2ti%FM9rOb0)md{mL7sOm2Gn-N(M&knv;QMZGpIy?olG<2GJ? z^^xb#zT=utGC%sb?TJ@KLKkeDvTE5=Pfwb7^+Uh;^!?oQZzs?4Prq{fgX;$uUKkF2 za^v$sC`rPZJGdGqy`dc83G%yZlIo&V2{ z=|g9(4*X{0U$duOx_IX&$IX8Jog*&q_f4s-s#fuD6!Dj%tjXfDT_l5dNWssP4$0#qx{-;$#9!MK z{QQ!Fz5@eNGJZZ!L8n~`dFFzVWOOQ1(D^K5Tu+R<$#CZxymv6!w4b#<7X9sVjI*^q_`X(ve`q?>ba|g1tl%8u z`TK{?xArL&F4lbZ(e?7^TMAyG;ZzUu|Lu7SP8);2r8@p|@2YU2&i5{z?-U)ehe8|A z>iGB6DL@|$@2T~tk2Y3b(D0YwGT(7pZe~5cso`ZBZrVwYfzFY}F-Ajw#dyh4iqKS@ zZ?Wd{SWW+4E$6@At^i*^zWhC_<@^LQ!cVK6u}#yN^r3=lGu@b}^<*Ip9{k=@j?qce zdG8qoxKYEO*Yf#T%SSg$W0;0d*7aq|hy4WQ@MVJj{7&<8;`@rQTep|Bx?J-fP;i}% zk)h>$sTO3t=I2c4+e`#6{@9K6DP+;R@&Huom3b0ed zN9uYx>Ny2C_Auq1s`I_*Ar&^;?{Hne>t-tWIm$7P(D7e*Pyz1J@GTl%`-BESUi|%p zKg#EXK@v0ylw*9Q`8-Suz@$^G>Ad}@iukH7?+Kd!QL7dFA*AQ;AiF|-P~ku5_?PJVJ*Gs#uh#I_b-o|Hso9*TZPxJ3 zx*xt@^ZA3;xAD)Y__u2M59|1O3H^3QJ?{MYxr%r}C*?k@|uwTuuL~M-;q3 z%lULIhYz(Jh9Mq*%eDNEc~gb&RF3ga4PQ~E0H!`%rRjKwjE@Ij+cW z>eTDz&Yw56&@;WjGj*ylb?l_^Q)lKD&c#8Fn#>|nL3-X z^YR2TXHKrB?aI%ekUM>*QNV`~2IPu@JdaLchDJ^IOtH>j051}r zN=eVSVd{+8H%y&1Jx{X%?4-Q8x$}!WiUJuO?;5WH@Wkf`faDnC0qG}tP=>iGMN90c z+&R-1&z-2y7PYz4=ggTu!4JZpDyu3}?s|BUEGXoh5e%6e6P`kWB1QtsP2HMoP&{Kf_28v#2fM9-}lFvZ9Qd5TpD(;~9W9Q9^ zgb$HM^2r1#n?$B-9}DfZo$O- zJj9#)j2yBaU66ZYQSQ7Mx{S;O&=EzR`6HE%2tNQ(#V*Q2 zd8J&87vp2}RAFvn|EaEd(mz;nP>{F+fDqg2#N0W#Gjt&&>FnwAuFJLOC>0pX%O)=5 zEJzzj6drU2#1c%LGkrnsR7k;7Fkdw-RS*GRzA(37vDId5h|yz5Up67PQ0txzVJXl> zMR{{(X5ZjJNzo}zoSiqzGirj71Ul9RWmXb37SRGt9X-9!W1x2`%pEy*<^*(9H+YQv zg1mX2S+Ur;i}E}|VAA{vMf1kaL>FvjY2z2o;<~A0FC9Bc$w7h>7v_0p%$_toAB{!# zWdj9auNnb}a!s16Ygo}jP6DOC4u@9uBetOQ8Md&!UL=03ok=jJ_Y#y6spRNuB}Wm14zLqJ{l`VH4*>&J zFT-vcz3$xk3slD+15brYj+k+SBEYt7qor(h0Ds$y9B%-9b2{{QsPKxx_-S+E^5v>A5qUH8GFYio?t zu&#M|hjX_lM~91!&BKf(&6tWXf98Ig4jNf{UBWQ;%TzSO(C~hGJ;01_hHK7NhA%P) z_1Lf01~+*mw$2708H;W-*x;wx;9(p5S2lQ~4SuW*-eiNDYbHc@@2B{L87_M?W|B-B zT$}!aI^700_g;wTXM>+^5`jC&1~=y*L}%IH*4!U(w+*g1V{Ggv+Ta%}kUXc@;F!W# zziBr3SPNNyx+y*ywg}CK5_;1a~ za97&kdP*(WRW`Vu5(#{b4L;dMXM+vywZTI+_?0$zjSc>58@$#AKgCm*#Nh!3WvkIW~Bf4Su8z?zX|*Hh8uT{u>*7iVbeg*@>QJgZH$>pKXI5 zZ-eLC;C*cHMK<`kHh75*Zmx9@U1o!q+Txen;78fu%Wd$kHuy>#+??YRy~+mfW{bbZ z20z*c-(Z7Jv%y0)xVc6|bd3!@!xq2R247@@*V*8?Hh6;#Zmz)+9k#*Ep@r~98@xLK z`qyNGn`;q*(&G;M7;_Du@N^rzpF~uT3>*9u8@#&>ezFaoX@ehUgP(4LXWHP0yoV%k zNCJl>a7Y4&BydOqha_-F0*54UNCJl>@V_O2Z=5Im}%40Gu&5|Z#XhTAZ_M#5Ve z<`Pn5rGz&!%q5~oxrAS2m`g*E5(z)eFsEygdo)!d%jb zWJq`q!zVCoNcc;JdotYk3(Nm0!NccmBPh_}O!tXMC62l=0Z)2EKBve6Z)CU^!{riwk>OJrE|Kul44=kuzJwoR_;iM+N%%pAdo!FZ;ky~;QcNUE!nZTr zm*IXAzJ=j47|xXNO$?vOaE62n7(R<(L&DcH%%y@z<6fzMhPgx#X^`-*8Rl`2NUek? zFwCX6NJzrt80HdCWQ~L`Vt4?Y0^ zo?oH+xd8Fl{TzOEbHl8&oITYl*wwGkiUpiK4WwVjbiwm(5;DE_A-%hDUmLf-(OnrH z?sinUckJ_Y2f>b-U}q9cMXzMoV9+s&&USl;ef$%AIpn9aJ20#Z;Nf8?aoCMVRl6e* zc3l0svu6nl^3CfYIZ8#pIx-6}n9yImyV|vv_~?*;Yp;8`JJ|J222tESfCt*9`9e4M z(5TKP>I0&3cKf?JqMf|1y$)~jUgy%+%CCMsf#$G8nhz_QgI&9ej!(qh0$eqZi-VaM z)yu;|P{4)kk0>&xwb8+GuSLZV=UU13OtdODF0X3BEb035WU4)t1-{`VZSliKtOXVgs~r*I%eJX zwwTmve^Gd*JJ7ZlM?p7~`kWA2ahdA}cX0d<5cWoOiiU6ZmV0bs_kxOOBWjrBJDcRU*}0{xAV=w8Q0BM4vy~*V zj+3d=@yJDYMlCBhUm*{ma_w1-PgGyKGCd3CQ|4C-?twgj{IdPUo7l)Uv=HhhNZ||V z2DW>Pw;PK)iS|eCLyZMo+vEDiH@}C;y{>IqnX9U#xT@zu-5bqwu5)ehHvF#4RbAz( zeHx)^Z-diYGp)?EqssNJQSGXswGC<3Kp$!Yu1!i6x}YhPhCF=&t{QKXW5G!}JF7Rl zLbA)LHhVPPhodF1dIy-*8~xT+g;LI<()KM-N1b^8Bwvpc6%i9@3yt;rfjK?$&?Dc(3UmZnGz(Mrq96YHJNyXs&rXr*6;8h$M!6B0f(#xHu`u$EX(y+0chQ)HV{=g;m81OpHt z1quNQ@2`SKllK>GynhD1%*r@Rh-6dlwVHNTTP=xp8IUDu1&TAm^FYj2w)tPTtPQF> zM(8yOEgOotvf8yBB|^(9+K$#IEsry6G{Vcr`oA6_s`m& zKYwL$v}|`aYR|uU`^QkNwKaIU{S_ewf}8NX#P3@JCxJ_M(AOw2ePK8OpCqmhe+G^B z`*s23-OQ&o5;(Ec5fDV&VDL(#?k0VI5i(2@7vX*@!UsT=Z057)Lfz6fVm&3`Ba?%D zZ3VU3vGT zgCR;rap({|RDL#^)0C9rI}S^K8oZ%QB|TH&sASGtqEI+3-jogdzb%wjI=;~V#k>_M z-coegT0fw8hHlWF8&v&!Lp>|Q?flh&wW7?yAoa){sE7?++$(5a%W-b3|E&@JCb4Jy z)#05HM1&~=OT}-i{lPNmc+QRW*U{75K^aEefuN17tPjkOvH@xW-p%a83};0saSAd! zYz@iY-P+c~1lB*k;S!jd3FEJ~INWyOf@ogAwW~7R!P{_zw`OIT>stiNT;FZrYqQIW z8|Yx3(SR8*GOG)WZ-f9q5cL}=V)M))%#lx*hHGSj~n+n>Cu zsexu{qk-~lK`mmQS{?opvaI&)L?l82J{GW73A|X1EB!z~L`vUJk3Y^_e?}#MwT?v~ zPj@^DI>0{m92-4FSqI+&!#jf-=glq=rK*$lVCq^L14D{IVc5FkL%F?`2 z*O`j3AwEjbc}qDOXJj7skZg4@UV{I{ItZYWBUPvf#mkPcuy2=t$UUPLGCmH&w5+1~ z-UhdEu|Cz6mHwG)?XPtUgIho+S<_= z+=x!82aejZbgw98%ldcvPHu)+Urk-YvX%vk6~9dQQ>Fd?h<_LO|8J?>qrb2Zq#KZw;Gq8^b+sU0AsN9) zk+d9P)XMy#23a8MraA$1$qT!xWtdN(Go11RY=q0b2*S@CIAo^f+*_(IGl4 zP@$T!FQyx?u&u_epgUAkX?(`)j#N8`Rx+__=LKsc?l{_!(-5@ChglkFOQv2jsW-k` zVILxjX)(O`X5Dnqlsaits27&9uu!dXN6W56VHKm5?iuf*O^>k{)v3|atVW4Oh!S>; zPp+D}PM&Thk4}wcZxKKfPDrgXuPMXqM)v`6*|m~D2@;DdF+nLP@izJG zA6b@+eRkvakFTqCHBsL&FM&Ef3a!ywRi;sLk09fB*h}hlrMkYy##NlLGSfo+P*VKE zDGLtA^K5gQX&hElzxOt@Mc3Dh-d@=3W~CtSzaaP0`UH#aSZu1)1#)%;io%Ga7P}E| z=-dUFUk&-j?SY+<|Tv|CI$JgL$DrRB%Ua04B}N#{+kE3!LzgWHp_WMlMv`+3J7t&KI>*o*TO+FeRyD`! zPg})D#|a?NZ6NZ191@rETbQmnA?)&P=`-n!5_2o+^S{!F@EncfGRHoJ4VDThl`s(z*LZMCgdvOPmQ*k!{^7Ec~05Q=>#osnfyyhl+y3KVOxaT6YCQj88TquUvn z8H@e^qj$BUtD_-_TX6T*nR?=0wwv!R>X~RGme@I;D)VuwnOO2_nJclv+||A&L=bPV zf+zPj(Ekb#^11yVeB*uwG4cVH7}dT-rZ<>Vk{Gkf_Zdl5%fylK)nr91YEGB@p{mk$ zjqq$sg+R~Ah%HmJAVpJxB`YiKMv5w(-xn4?pO^{YiMf?>WQGgVn=n~3dFU^?Av%4S zT7W*|4Hg1U-@Ab547^R4r}MxOhrF&nc(7B`?(jb%Nn4nsG?felpl;0#)snmeqHrca zE{>-kGXeT}){-*;`nlnQnLsi|DQr8V6QB{Yeo{2t&ScR!D&!5f@o&Y1hsm(T*9Lt) z>s@b?zW)J^U`e$&qHW8uMTb(ck`kAO#zPubbHUP3eS*ed4-%cynK;;nr5sXx*t6MadbmLgjGNIDM}lV zZs70|%;o*!RRC|pJ7$o4a-!S|kDJ?r(hlaTX4ODcL?QPEQd+- z7cwKgG$d5AJ$YWsgCw zpF>wRfPv=7hr%p15w$BzAxA||Qn`B@UQ^sfv4KH$v9qiJGO&`+P>axAR;ha->dj`k zrXWz}y0yx68>C3ZG@IGh9aQ29mRlo0fbH*<_kw$0J2ar|N~ZAp4c0h3%%zxtQ3yJOTa8o4IhK?f(f`nc0BUI#uY-*o*FDdw@P^3yJiHhtalsE zJMTn^)My0)SAR!*gN!eFW%<1MI>W#;>k5%?D(&l*;rQ*;{gul;k@(DM5qu&@g`32B&pGMoavV)$D;7z!-~QW;6o~u zGh|DP7N9jm&W}s*m`+fo5=fR4TFugkd@CAYGCfaID78_@XqAE^@**f$Ts3?Tu2zz! z)s{}wH2aywSSILh8~&737^2>+VTIrQ(ohF(DMiRaMeXtf~pd<-Xgu=|7)J(vu+ z{cA(wRBV8g(D7j18$kZBlL;SaV}4L)`F2^pFny2?PWr$H^$LL*Y*r!Q-OQ(Da&Dz+ zU=0Z{>qwt;n2}Y2={9H^$uLcvVJVg)3VVT-*pDUtAemR0s)@k_ND93mpy=fAG@SmC zpc~;u;n4Jt-bzgz4MoizVA(Tw&JKc{Fxu(w`BNhOvM(VOm06ne&JqM*r85}}W#_tz zuf@lsZH`)WT0)Lb_wqoHl5__uqyii}xRXRcumBr*TX{n9ePo7t0)P&Yx>fYS5-dDM zaq11HDL_GcdHW0JDwTXIn1MedItnHKq^7l!rxj&_5mrhPnO+E)Mh{o2)xKORD{=Ye zPj6WYdJx(b(?>US7qZ$5IjW-c71qID1&bgHonT0}1hfhN)-9}gwuUdN%|ywY=4rs&Ti6iM zprUz7BB}*N$}kEhZ6B3bB}kTV1QJWZ_nxd-2*f&E2E@fU{Y>S`C)j&T%~qoxtP z0B26Io`sibKyw3+!6A9rO8OdvkEEEtexX(-GV#8H0}BJLZyi`88|?bFU<(q4kRE-9 zUUzVPkvP|_0|#+qlE0|QkEI0sACE)gdu5&G^*5|YSl~hv!~z!<7%=M8!buozuvXOw zD9gZwEz^sIEmWFxJFi?woGff%U4#+PP3U;gC-`LXi5|6F1rL_1k|VHOl^g**NRH4c zpk$y6B~?z4Ohd^ClS1!%9itD=%s73e;1?@uKyfVz`UT(qlJ|rjtBSbL< ze|nI0^$)ON)m1*mYMi#=q{_c`jW`vn;P`7s`L|x;4=#tl%(sD%+A`nca1+{6m9Gjw zhhEIT?VdK(zHb3A=G3R@RsG9*!M7K%lGootJOc)jG5i_$+R}IaJowV#D_PYAzRvK4 zKG}udCWEj?-iNO%;~lpizV7g4P4dIn1HOj*0{AlF3%zqae5b%y68sH(r^A=|^eFhw zfG>a0VEFpM*Kpl=@C|@3RCOkNgW$`^ISIa@@C{ng1HLTy8ecjVzESW^`|N1=-0+q4 zITF50;OlYe5%6Wh*L3CK@J)hm&5eh_HwC^?73uJCHS~znI>0v#zP+p4!#5MYx()5% zn+@M%Nh+uDlwNQnMb$hf zp{j1VI-_cj3nZ$3xSFDBfV(uLSF)LUX?UEl_^7+HsHuRvYMc>n65FX$tlQp?2MA&SE!{Jt1`+@Ak8q2fF37n!q2zs=}fUd)&@G zp=dW6r#rsHX%2VCtpsh|dOoo2)E4LVu#i?-XSm{tPvXXA~qM0q$o=f=9hCF}-=vm2Zo%|~18@+Hi5{(v0Xp8!j##aL1*_!RGDuca$huJ@r66p?X!B!6?d%da`)(^A$2F zhjV>ansa@~k9XrgLBB@PN%8z5J9-f#UBzf_h>w9CA(KXX72|ISG1`}G7Ac7zs| zY-ZnyJ*bPoUA3%7lBq~fDwSHETplQu1tLE;qZ}y;r<8DTrSrDGBYCxNJE`Iq?QK!H zSEyie2^3}`rP8Ae-s?`xV2}H?eqSJ~xR7vV~5D*($RhFXzj*FXGmgT^4HXSO< zc3{Y2+S;&5gCWPl+DJXhhHuk+S-;fYjA!3kr*BM;(XmAzCQE2acrgNdC? zJxmEMygDIOU@bcmt33ZB{-Zq;nD5}c^$K*|Q7s*+Acs^f)UphlfMj>FjZ&(zzsIW` z?zGU&HJbvaj+SM2HG%81x#LRUEPr+v6S^*|6WT%#jd8Ba>V`M%{HC^-Wp|6>*oRVP z;u>zY<6=Y(Ye6K-9x;K)Yj&KhwImwq*KM%O4}UQ(5aE3Zu12)DBfJg5o~ubZF@dGe zLce>oj?kU~oDp+1;p|}aG7U|ig(0Y(UUX(2Gvkke3*A(aPhllEz~~#2#z~gE=unT`_qJ= zHP7Wi7P&`4k^Hc}1xb!bMM+7Xj-CsX?r>S_G;d8#^TebyRo9(JbJ^}`80h?)S7X~7 z*6B((3C1U<+JHi|GSxP{m}>8BIn_R>c{w8UMjTDKF6QOEIfTx_lwG`tS)*Z9zIF6f z`PS13_{g}wI{fB3@-MM4_m#-zThv2PKr7zFj}YCr35oD#4=I>O!@8;Qo++zK>X13t z=+Ht5fU$G2!7*P4#v%}`An%H<+DgA=y`Fk_w9T_sHn+On{gQN+h#j{|Eyo|6>y25% zvf3=D-#RyqgoJs3;QJK^;;Aq#^5=cvp`n+UIwZVn?`u8PFK-ms3MsMoATkO51s2a zvm%}Af-Ko3LEl=ESHg7%L?2a_r z{;UL}*1WENS*88Gq+kYvC7r1j0l*O%w^J;RO~7aChUgBK(UQ?Bx3Mez>aCla z7s0z~t%$@0PlVW9v2jsH$M10aFQ=K=AfXW%*u4$IJIgjDOHhk zqHv#k-V!d}w?o5Li@M5Ubu&6mfyD%PK!!7h&5uhC=0pu z$0;$j?1~cuAZpbDFO7kkFuz_7v}l+xoumvAehPX2i_4W>St`b?NNB)bLGOrzG+cjl zuETjrxMB+_kY1mRW+ud-2^kjy^jLUIP{C0mJsi-9kKKYEPVB_TKuZyhlCqbX5V-}p zhL00ss8VBuiCZTRRInaxtFR1WCyM=9TtvU_&wx(A1S&)aVhgzp?uUd~e|l_6lon0* zIN7!=UJB{=;wg~-r2Ng{vjvO)gBnmW&S+i6DpL2>BPmhuhmwl4A4*|V8a5}eA!*ex zg7RAD9t(n_M&jpFt-NDoVt~8uf{E>@tbSVejYUVGdk0c&iI}$>nXmcA->Ne38LQio zR|@KBxYuDD3?0IWJCLcK3uwb`xAdFcUctff#OXjg^zY~<@INH)N_V?AStOnVFeBmg zXt(*vA~6@-N5n~zs}8RQ{0k9BCyU5#Jdo9;mr)`XS8t3myA20A;D6yHE)!xB1Ya+j zA7H97_uGuh+@_3Zdq#ACk(_@y*B9*`g(*{@7+Z3UD7ecsx8^{e)^MlKlgK_RMAPqC zE$evcS_|LPGG*cWV+gQOy<+MQu0aUh_-Z&<`sQmVSo+2@N#_kHN+z=o(~DULmV#u| zd}SZHU2}u4nh{_q!U13N-l3klLAOyEF4Sq04$hR7YPY$Se{b<6C9==eM+O9zEL2L>bvcas3~DAHVk9n zZD(1~9?l7;r-;S#(t5(MpdaE5P7yD*NQYw7fk&jn0JT#y9(e%aKn2ULUc12|YPpza z@K(MR%?j!(k^S;&Ruu1(Gp-6*?=z_vhH5eA3ImD7GCV03tZcz2)-64L%*5ro0~i=p z#v83*1M>D4Z^)=cI&Isgq{X{5^!r|%Vx)|RRSZQq5M&&S8LVQdD|(d0c;6}tgBd%7 zmZFIHB+~>d#0dnc0IGhKC7bo6+1FeTYZJ)9lDKFprmF~LK^&ErhdmM^{Z^^Ad?VF1AG3FwuPimw6sQn_x(0h zyb8VwdH)9+>gymEYp8F^5h)fs_OhaPvnZh74ijegC-au^S2V*LYdq$1LAzP1h0IKy&c>=Mek*GAB zKav8JU!0aLr~MzSuJJ2$;|YmTdZxokDc{bP*!_?`ELQmsBC+NfYb8TLF3^&kEwg+T ze-#T)YYv-H)K{Ep1>!)f?)BD1+_rTz`IY?BTZ_jkH6t3jt885XY_Y{`JK)%2zqKA) zak{@MKk8Ad$`8Y67!cJ^Nm<$P%Uww&Y}rsbjfOEK!&z*wF-rwu9Nsrs${Ke& zmC{j*E2t2w9b>6ieiL5Rk$C-OJL`w*#v#ME)#@w$v+(v+e-%cDKYqrWPT9$g+isyv!e{_l)yV^)gzRv`~1OF!4< zm_gz&cl9F&KFlRIMB~OD>`o&SRDcYz)#0{pwzM?Wc+GRZjJnuq-U(L`igf5;jR#9R zFH-{p%ujQZJj}5U_8(Tpl39tiVMrE?2d9WhH_dl4wDX~DNRZ5Tm9JYM$iSIdQwI^SfX+*9B)hc4^ccA%UDKx zj_|D-$BTc@HcngPV2tCFAF}%Uy&QF=M^FZ`)AywXiS>{WtMrh4REZnWi>#T^?Xfsk9~i^>0sM}=O0n$d&9UrY8PMz2T%Dc9bz!94^ zNbffltg0@-5jh1p*%j53bWRwINo5b(gKbuJo0=ldd>~1Kf?CV)1e1?LBPVQRt~Xp( zONxmbc4NAA+YdQK2J5T3@G|m>Ey~W7tHKA@R9jVJ%Ga9Usl~pON`FvlOv3wB;};(^ zYxiK(_^RcWzHyB&UXA}HT>*!>mTElXf!5SGH>L-xsrRvg8@3R7=!A^!jK%pcsPS^J z8b1f68VnKLa^7*>FP4(0oPZvmVgmXwq}OtQ=NOteP``Y?HBigPEcQYt$2a1>!mxi& zSxysfB*e|n_$$IKA(}cZy z+q#RsU}8*3aoE6u0=^Ldi5pv9wq5A|A~?iqT(pOUU2DVMtFU|<)P6U)`Hc;?{w&8j z+xIG_OA`5zNYVu~^-&KixAnH%PTg zv785YOZ#NdjylS*(Sy?_)!{2D&}iarX~&mTs$AH%cQv=cawg}!WjW}zVUFT94K58G znH&teE?I1J+R4C2B?qS&K89SsPVrAzvd%tWWhgrgCt7({3lPi7DWWRDa zxRM=Q?Eib@fY(}}oVCzRw|~4k20eZQ-fAh!d85n~s&ajaH&nR(4W~&tjdUVUscG2o zyV;oQdeT`|g^Se=8R+9w2BMB5Q!eCf*eXEvo zTBhPy2(*7|<(EAjwL6+H^K)RW-qmP3T8Lhb-J5Vw3l3y*hn4)s2P(fj9<$$u4!v;| z$9wzyjmoZXU*(G8l^r}$-bN_yU{_sHr;_53;Q{c=w3ckb40nCm$rY9REa<@=QA=L+ z4bWcP`wF^4Ty@SnF2?lF&96&%I?9)0aD@(vv)ej)m6fOsxbR|Ind=RlhR3T#F1*wv zosma#=bihlL-x^GQ(>UXERjz-SZ8m~)MpUWX;O-%~QtOWSs?e5|kd2ES>ik7JF*w$s0}8>+ z*q}QcGU*DNA8>+1IkkA#Svpb7a~0g_EL9sit~$#aW6Nh;On0#;8XR`YG<`K)xYO@& zu(m6bgIBcN!LFS*7Br}RX8QCI@NZOrSA(7V^|hpuy%#abhCye1?uME z3qWgBzSc?(zT)KyB{XH}u}f zkGhz4HiqtW`u_Is7oSb70e$3N$^os2O{ErsFP^9+C&x;yBBK!{N}(p;+SwPq3+@+c z8iCzQR_UYHm{mG3$VMa)*kGjL>ZV$!8ZKnVW^8M?=c1~~hYQ!6vH3>Zf;q^8f^yy= z8{B+x6DMvmtj-dKtL`kd>T2^LxtYW7dnB=}aNA!puvjH6?XdrxMPavi_QCSo*M=5g zbP(9tC;9HP+kebA30N#T^bKXM=91#(_6yhrB#D**K_ysW`;u5;`!rAHUEVybkF9<= z1fOIB8d&I{Y(Se{`_*YD5U1ZT{fH58Of{?{Vjc`1OyN)0z>zCZa6vMW=Em05vR0vE zwZDV5xunMNRUu*?Up)`;O60DWcFx<5m*tFZ!_lTOV+6T)6DYRwhs6hymOFjG#jQ}- zxD#V~F^)M|r(1P^r(3Z(3XD1ktKL%=KtN8$;yOol>14O{Wy$%VP33pv^mVt26ftBt z?ze%P@8*&_hwfP^qH80~`Fy2}@WWe)=6B=0n{LOah@s4YcFj)Cxex#0Ht=5|a%^=s z0N+b-V2OMq*_B(@$?HZYPErXOQh2CvdGsNZwo>*d2j=nLE>Y}!_^KP@2Jk|*Nj5k=4I(-iVf@>B4;fc@G zL`rYhM2hBPEgR8sDOb@d2+Kkg3eard09-pV&qaB}PWL zEG4`He8sEvYk;6*?sp}I(l+p0TjNZfxWX_25v(2W*!#2pkG^1@rVgs@?+x+a`^;*% zR=p3~5wbk6_hGk|nMwOVZ?FiYUoOW*&lP-A7o7x83YKLY$ji&`gOAu4<}ccXPoHr( z@D}fK6u}Z~PUBnzJ1edRwg-F{4HGt58o;tovv*qt?dRmEBYEv?4%lheuM~QMp`hJq z@Kk5<+jMBmkUADn0XitTJ|EgYO)3}t-J%uKb1M2@M8$2Mc;cJJ!C_-^OdIVUWVwOz z!#Chq^@-iVNZ_pr0ha0Z@--=Z+|-W`xbAkY8^2rTs`8c75kdet-yBP`YoeP-wJy1I`?w833VIrF&--e*Y{GM46*`zZ@|M>XyA}@c$-WwYQDs( zc~r2fc}9LD7TNS7GImI%^tNMijQm$(=}7Ohgy5xjhSMQa9OEDcKP=_LhZE&^Mm9jCc$EU)z5>8KJMb6c z3@Kggj^N4fuf$>~SjlEqwBf-CEK-xPT*%55xPa`8{wfvfy#xdfsv&`2PBg=V?iEk- zbO*dK;`&gu6$v9c{1EO+IEDw`EMQXX>Jb{KAe7S*9+xG&f*i({6dqWHvY{z4O_C^b z+LKEUca@k-;X^KntIO^z-GlaJN^^q{sZfr}PV_*T7DoVE=39>^hJQL@lsbJ2JJ@R` za<331KXRmE6Fle+loO;5E8S8wk41OTl4emSY|i22`iRi-sVz;gAef1Qm4b$H!3I@caXO5QjW~ zi2q2+o8V1n9*t*Ngqn!P~$DqCWYP$w|M-b0nk>o0D?2`XLyCjNYk& zNUPsM%ND34;d=yeX=HWIii_+2kreg=ogtE$^A=aLsa*TXN%!*3(vvYxSiRBFTuwV6 zRV%qExvFtzUUi2%xMHUop{)aYBwk}5C{9l1IU5-qqf)Jk{mc`rAj}@)Gf8L`AIE1h zAVnsZNG42c?_a=SlKy4QZ$UF@|H5Xy11qU9Cgn~Qn?5}*>?*Zr;}6LjOUMPwooc-> z%Tl&5XesNG$Han;aNOC6I7htZ_zalV1+)hb5id;h@KNoxH&PK14`728H3sVa?Y z=Fws*AlS!|Eb$#^w=jKC@_OZ4hcTjNPvF5F$g7Z%H`GDf*+Jn>w?1V>p9PJx`jPuB zI%2S^Q5l^zh^Tk?vcLe&pd$>c8BbWv3@EEvpnLEWMY z17=tWNa%4p@m9z0fU+jnxV%J(cIRQaBx6Yxpq)!~`Hrvy36!pj20n5%D-R3A=m z49}4$Eg3Sf4bQ29{%UlY?|A@Ap5PM}Bqt#J7Iw*I0+=cwrQxyEHvr*_5-h6yW`}0#P(6A+(3lJjZIfG>@A=5^MH= zN5-K$uwl-E25AgeJg{ojwus#hWf8x0C@XB)uy@H17R#kb#V~7Gw7vqxXa&FG!x811 zD(gM15X(A3CBVDgYHsPTdW|v-FZDn(*{3(5c2pjuIn<)L2YE;E z;y#9Hsyt56;7OBDg{Tcm4vXju4uF^#!(R>Cbd!K87}m8t){mVmY!dGA@1siiz%T|J z;4!|R1zQomSPnv4YcI}}F`5JGEuwAyjF@(+iov*y2+ljUV6{gzFbvo%-xk=mT$me# zlAw0k^xU}75no#GY<_9|Z-usgAwQEd=1m`?5c++v_)iYBllMSw13c)dYZy~^5qY5G16Co##(fgCE#9-&a z?{<@G)l%flB3p`_?^H##-ef$ffVHt(lJlu#gT)8&E0Htf__w~yc_6h`wdZm9W_w1T z!Lxsg8{KD=jx_s>E;pim??J6<6>*lb&%w=ANCGXxG8#Ny+~FW6f2l77s1UlV?Fd-2 z&{*zgpuS_h$%R&KkAtfS7CN;KDS1$hRnI|c>^U@9X-n!8ul8Q!GO&z=L&Qa|F&LL6 zz^B6Oml%q@r={d>9`S$p7itGvog>PNugI|3J^Tu+na#whi0Z~7$M%^hadGa+`L=GO zpO_Q%=v}y1$Du31W~lOg0U-N@M!NXH+hlstMKqcQgY0F=mL2qpl)O#towwbJ=HU<7 zzWU?52y!&P`jdtL?Ahi9^t7bURyTPbRWhU&L?dI6-oEm>8?j{SLXX<+fd?IJas*W& zc?xT>l=#HO(nu|ut&OKW^X)vn1mymC!irY%tdB~CIQSH)%iVhMzGgkfL_Pz>)AIfR znE$8C7R6GvrAtpkmsG)|<*+`~;yPMu7y)(yaI`@w*9KSP_yn%3!3Pl;0sAs|CT^p# zNt8UnCvSqIo7l)k%&{7ocQ;?{#8CGzU`bB`CxGid_?5Z-es^>aBHF z`Wz@LUxRK+R^N4;b0M-Zjy*c8a^mxPHEJh|}V0dgHH?o`=H&A_TLMA7c*QS~NK&4GjP6y+}}cuRN#>dauJ&Qg2iG0(`@= z0(K2*PYx}({n%5cBx2W*GnCkWEdwvsnnMWU_HVw5MEq;B#i?+^X<2b(2a#EEm^vzN z(vKX@AQrKpBRZB`_qmbVNR|P=1WZ6&B#Mq&6;*M;t;#217Kydy^x<1E-0C7vI1;XX zxKs>2vRj5}*G!#J#E-ZE3A&earHy1uLyP<9xau^X;O$Fma>!W|OH3{6bXlZr9npkm@ z*%zkla?brxt2D1KV7rKQq6{T=qC>B>+N*lsNbW36cnvCKd-doUB*6J{+iOsjHf&H~ zy~TUuUW4Khe?Pum;$k;2zOF21XPK+EMeA1wmT@g!m>OB-`V42+>l~i;cy?Hz?_PQj z%~V~?^e(c3iGr7G@XnTb9N=XO%~NdnU@s0BS&P2+1Bq2j$25}I6~6{zf4Q2k2-Hta z5-E1R5{H#OgI(?VOwGkLbLS%i0PEN*OlsFiuXkICdwH1-#KE(Y!$194!ApvF;?fWd z6)h>YwNlpd3US`E?wN-hdOZUp&r2(g=it*{toU)9eY#JF;B8);IacoQP8%`?~29*Vvk623}X-YkM;5 z`R!CY#`?h9q!<`?`a%!!(KZ+#For_nRBR9@xCRcd^${ny5>AU|co5enR{1^#1@x!P z3x)g^2X$aVBA`Cj!Lnl+4q>rj=}|T;Jlj$sc$_S03Re55t4NVK#J695fDks#P)8Bg zEt4m2!=pIKQ9Kz--sHwO^sPLO4|73XE~n&3bM7)&cG_Ing_6NeM?8Lz1h(%1B+o}3 z>Ex><5wlUJDxE&OpNay=$53Diz{fL|Fx&K79X!y>ub~JeqnCjz46!ChC_j!O(C%W? zv%0yiR`CNoF!or}JRw=pq{37H3IC|V)C&p!q{37Qp6O)8#)b}t5{C{4q+!|LS+PaB zwOm-^F}SKx{yVC0SY-B!1^8ZOUG3UBdDE)%UFY45y9pq zk>+M!K=u0_e=Vv5Oq}Y-=K|4tI(<>-5EKCsl(tIDDqPJ;V$i)BoHJHSE^!3p7pIJryF6Nvx>~tGqK59<@cL?}dshjei^Z-1tax8_@nD$t6IkBgJ7j z#>sY*l~GG{Ex2q2%?+?t&{Ul`bY(-v;qBc<$dB_>vO8>BE0f+e{QZ?w4K9)LRGGJ@y=Wb+k}{9=7eCc;2pfxs!}ePEXx^Ayt;*6WI@~!g2Z9{ zZN#;zTB-$i73{xazCdlZs2Ca+s$!NdX;CqcDybfg&^DqMRk?O_N4F-O9ytdtoi&JSNhjN1yvbjb_>IHplnr=w2d@oma5^u= zyK%1VxUCu=F_gD*TpL*V2zhI=QK5>h?Yh#ot4iCXDvckMQl`6JlRk_uC>K4ZkBY|| zW8GtX;n;T;ZL8I)sS{C^f{$y9<`7rZ83&<}jN4#Q zTn930a`04G=R9LD#a%n8`r>Ev+}!0{!(o*!T`aMI)F0;3d{ zw>v!DD%RuO%$#A{hrk$)A86u{M-U7I$z@=zJS7kFg|XV+l~`7?@rlXM!r@U|2igiJ zC@YJEFXi|v=)t+&cW}rBK21rW3Nm+2b+d3 zKn;+E;Sgj%gx*(*&ZP*#@fBC>wox(~ZYwI!RimqQna{#%TRPE4&K0( zdZI__X(c>rFFh4a7@7{e&C)`4FDM2F3sL7Sm+hlwt^hey<|eefgQ~qY|LNLmT@{{I zYUfybV=J<(GCZwfm*K`c6c_Jj`zS@{I#_L~glN8+9v!L`9$y1{mXQxCHP*%DU5|1o}xTGd1hE38?LSyl%%giU5G)fXS0AvUm?{WmtF8W@m4 z*Nu~gTnYHNx%qPS4VqUtV*0DY1(zZ!4jy9ejTgC?pn^57nkVPxN<(nq``(Zh2X81L z4m!tT`koA6jBJdy?5B$$I(%Y1c4owOkjVCw&BNJOtDpj61T8=Uu>D7 z%XXJ!?cdzPxy3Tb=f7t4J&e7iq*mV&W%czN%PzW}Rb>CF=6}-a>w=sPS$%x3L5$f$ zR^KXUn>n~0vic5LeQ~qBLslQhZ09Y9tiA>gO6)XSn?w9htknN)t8dAu1k>f9tiIp= z=dC{8v~9PV{ynSjJQS-{tIsnMR^KLAq5r)wg2rI6i}B8n3BbXA#K6ax2iF!kW0P!kzf5Y&@vAJ$a+L2#?Zy{n z{W-h*;~Q}Fp)SEb#Ic383l~Li!2U5l+~RFG!dtTv*W4ml=K5}ffp?FW6*thqLT7_~ zOsc33n*msVRwrW{&F^&%My_$k5Y|(ixPZ~M3(wv-jElSYmb)D|i~xc~zl-1+Ost@W z+d6MurhJ_j{x*7}@Zq%cfh{DkA`E?hKn!uI6*Hq%!%c?t;%e{I-1wt9j(JN*{Bg41 zgH&vf5vq<0R4|qHujQxg{S`a;4DuW!R$h~w)5KeUz_oSW))Pr1yaEQVEdUY|xc;Cw zf%pg+o+^ohpF$!K+XHgp3!4=$fKadwatKg0H6DaiXCH^iiAs5a1u*P4Sr|F(tj;!K zE(x&PTbZWc`ac%cr;k(S^SiP{!t>hwv?ZGAbfZ?Q)+mXKME%@3#3e0-8#Vvbavk6LLhREC0hc1jvlX&^3^{ybmWi~4+;d~5hS^1Va6-mi2vxkhV(o=7A-8Eo z$p0K_3R$b=Bvlorg#T>OIdBPYg)~~%^7Y8|t=EljoG1Jw>kZyF(qBi>5B&~F;E)6k zN#KwK4oTpU1P)2ykOU4%;E)9VmnBe=70O&Wts$d4ySCq&{KoV$cg^XmW{0~kpHeqy z!=k3PrK8?B@#p0m5himU2}Zd$T8?UswSp7hwYpLV%> z!n^%nEZEcO_Ho<#JbnF_$3FP$4=>nw(@*Vh9bI+mle7MD^nH{6a{jsn-yd=3rEj10 z+`NCC@bEQ%zi`XaeU4=#Do=cL`X@);b@`5SUbyk=j{dQ4^?oWZ@~a1~{OjOX7XR4J z=X$f(6S<#vtH^o(ytPH&9UdJ2=QE$3`_JQlfA!ynZ7%+$jd$ehC;wr_XGh&L@x60j zD*WcKz{P*+`^*i|;~x6WM?+ry?av)<8&iGSAFuo3nES8zVBq?NKRADP**j-HKmV(q zk4*h|_-i*ePZ~MKHECjhW75cp;Mo&uw2USUyw{`>+@ z?##@Y(>>EO3-fNu?Gy*jpI(sXSsV{5^vuuCyf)8M*vatBDAcszUOT;LX2En%E~Y~+{;Bd!>G>FCQ4e`HZXLGCo3 za~I^z$j!`#H?J^r-h5ByNcXt|2A)MtN)m5!i+C~W+3qVR8hP{bi#+|5bB=QQ8#88~ zJ7Ce&{JcU^HH=BKk=2aZdHI<$=jRsAJI#}+C6ig0pO;xUXZnI%$^XO?PaHdM!Sp$K zGc!l$6%=|zY-jcA*MCYTA7^o9&tLZRg2LR9b7xM-^<>}RIV*2&es006+!>zCf?Q8g z!90}=@lHH(V)obxxzlH6=FOWie{TMqTu<)ly{4SmYfA4kdd&pQnTq11>G_$3sI=S* zGAHCB+cPiBoKQ6H%t`Q{J$B|CFgMOK{ko}><`#~eJ$>GFxtX)3=glc9P)s8qk>J#^ z^C!-jJ`X$~zoNoxRh`GhRPnAro#zTysiKaXG-AxuShT|2dC4gz#7jlah>th*qRbQ& zTf|F2vCWp|=1lzEkH44kw*c#d+!Vhoq;~LGM}t<}G2+1o_iS@ z&CLz)pWY95kYYiv%Q>J0cNW|%xSQZkgZn+)61b=JZ*E=*HxF(I?h3dKaJ!s~yl@R- zHr!0OOW|h0eG%?7xc`7#0(Z&)q=&l=ZV2wTa2w#Ba30dP#}QVznQ#l>X2Gq5I}L8; z`A83UDBP8B{|Gk(_n3i554Rbv(ZMj@8-(<5XAVYsxJO@r^lY+#v z_ba#!aBsmPvw`D-+g(Tx_ddL5ngw^-WUiH944`!Bc+aL<{D^w@}= z2R9S$m_xF>L9`14+lP!UJDh^bygu5BXr9*IY&|fsbT{;cvq5jXq&4gPCHw$i0 zyvRBY?(kVi54Q{63S9|zAlwk#4`(Ai+$q;1Jzn={i`Vlq;d(tt5BI4eq=)+x+!DB# zEgZZL@SaL=zmdbs&; z8{iuEA^qWy58O<+i{WO$?Q}oV!|k#h>ERyx0Mf%fOY{v1Vmxa0qc^lsvN^{(*(n@D8q4F=ML%RSPne}@E*jS4jO2i z#t|8#kLW(mdDz1A5@YzWL;9S1%1J~s{V4o(KSSL=Oc;Mmi&vS9F9FB-YuvJNM8>UY zBM)z%b{TR|_$2_JaAtEe4zb(tms|K108aw`0z1A)&_aI`;PZe#$Bv&9!`B0TGw>JL z@x?LxUces({#-kLUJTFE|1Sf-z>ZJ5Q6lsJY$))Z&T4MH(2idmOMeycrvrbM9iJD& zF9H61;0M_8^JDlGz>jMMzX|wjfG@D8Pn&D;R}cJ}R`7d)PdmH0xsN@4ZY=*EI0)9c z75q@(k8TBj74Vs@;Fkd32l&hE`KOJu_+J71mB3Gp!~fdCZvuWf@T?0JLB;0Soo`ee+FaTU2*t3 zEc_DSd-QK^elZUJtc70z{ByulkFD~hePH1?0e?Nl#24c5t1Wyz@IM1D2g4O%&2ikpy^O~FQ!PsWy-{#H^j3D|C{7o49#zBu_c$dS(Uj_W*!|)~~ zG3i;nUtCTD5=RpBxKY*^a+h(1NcA{yyNR z*zsu-t@L|=f4dcY57-p#$2B*Pw5MMhqdye*-oOvD;}^y7R{=j7_*D9{1o$Ix7?1aB zS>1{j~2b{x<=iacOh&opE@-g|7#GDe(8Bo@4ayZo>$yzrDbJ z2is(MT>5*g^gUn`&4q2!8*+-JKc%giekkw@Ca3OyuLAxy;AN;W^>4h8m;9FizZm#y z;>v$zto*>Qhpm$t$NzB_|C@-14fI|d|8HCT*8_h&Y@=Rw{Ipp9dx4)1{2$`<_hCza zdqD5^T;1HfG!DPm!Vd*Lduns@>Nxx#E&Nr$*H3G1zBx|6Z?g1z3GhSmnww9JOMjx3 zeg*I&uE)V#JO62ySmoOUd;##O#@Bk_pKJxc7x)i>9|k>$)$gEo%z*mQ1APB5zq$G9 zIR2(s{0#+uz>Uq#3*+>oz|xPafDaTkHy;<5{%9-x65wxL(A<2sy?ocl>SqP;6~I3g zSO1U4>L2(MZfb7+0`(Z<|I_v+|MkFsUV`)}XAJ+i)qm{;eqLE>`=SSI>N4QV?fj($ zEcp)wz6to0<3I36`*D)lo_;~Bd_3NI4)CeQhZVp-2E2UQ#cV%q+F0`21pMu{r^eR< zUl70r=5g(-oz=ef0)JHy_Cp+gu7&S`Il*(kYi@ou4!_RA4+Vbg-ObG}*~^#qoF$*D zfUgJs-Z=d4Ec_DS|4%#D9voM7-j7|zbs!;N9wx>~O&$(q;;pRMk|0ha>#>#`%QBK| zpmBRGX?LZ?`w+V;S)!DI&O|iHz&JQD0o-Jm!ES&i6N2kb9GE1X*rmyocralSaG*>Y zm(rQmnSx^v9=5;l`+n!{y(=v<{i}b}XGZ$Y`F)Rb&UYU7+;i^PEqkzLjrE_m!}i?= zeiPRI@-2C-{ibX62mfBY6U@f!-yPaN0seo3e_M=i3q+m&!0*Dl#KSTBdqexn@lNxD zhZ?WX)`5Q<{AHlE@?CL}6hQ262ma4^_qZxnzsth$aR>NE9;;ScWBlfjzYqKq->O#A zvHrg!^v~1aU-M+O+7hjwEl)N76X3Uk-{|>p4*dP^qQ4yPWZwt>OHum^XG8z41Apar z8|S|r{B`fbzXSXM@Eh6R2mUkf!hah4YoDrCzZYx2gJJuf0RKk^tJSZ@_`5^?Iq+K! zRjc2L`S*#?zsvDXc@*z}xE5b)--Sc9`h#Bu|Bqw(*N6Js!T%uM9hXJ*x2n25|G-}g z{?aIapvK<^{>9*zW8<$7j=!hDAI2U{Pb~k=F#i+ae*y2H*G26|*7xVY-}5f~5{7-Taz(M9 zMXGBq#M|Vj(-1t~!-D#9o-($RC5i-}-#b++|6#q>Ek;w;l)InCVv`Txai zpZbEdu-%iS+ekB{Q>3$`dr1$F9wI$T`XcE`($l1iR$IPm?ZcX8xorNn1#hq}xa{q*J7`q=0hzBnCxMIo z3=Lm`w1hMG6MTyJ&5Anrd$1??b9J~c&&r!ju#@r^Q~m()*NMyT6^fnvXju2S!COQ8 z=YL{?y0?vJlK8RD8GfG;x1IQ(Z8t#ovJt(N_*;57gmWOuPZ59p9}UpG0YvX2e)2iP z^_?D3!1MZTFBl8lCGO0XA`uVs`T0?`|32rRk6z<(wR5HOQ8w!4M+huRD%5zi>jM1<_3 z{F@vvH;{J^@og->?sil zo5W{tH{6fw_uxT~S!PaHruci^@h6OR8zmAA2q@4`$3r`rJ zdn<@eHn9H+y2yOxyr+TuL$v?ZcC&xK5hr`};pH_?IU^{@vm()oYSiBd1r|~_lN;{W`gMAMdHu7;cCUP9^kyn z?Aiw-HePH1HL3&A3gJ&+_@JXVH+`rubZiamxP{^G#6XcH)PK`+5FL#CP3l zg2yTU5b+B(8X)tggeQn!^K(n<-ZY|ziFZ6~xSnYtdW`s=@_Fs&jek|#WTXFoNBLL3 zWvDObV!XgfzwQ5&;eH*ml=wFvG<-LWC4gUw_2Iwxb=qadXrug*FI!sA4iWt<@oztG zxbDd#x}Er|HyA!)#1)7?K>4M_?<8J&*?_MRzn}QRx_RSK;sutU*|>wmKl3v)pl1+> z9wokUpW)umauzK9dE}oA*E11B-yr_{QNz9e-y!~Q9ACZ+x7|Az+hzi5E}JMCP=0q6bjJK~=_Wrhb982x>H&c4^Z@JocPw@HT z=lKN}&OaYs4P5%?BK9Bswg3iK6W_=7`YtnRC;s{08lY!Ih~7y2>~9R$GblvIh;O8S zK4xsUi}+t~ye}g@OMHs{;q>IbL44KsOi<5=5q*mII!+||?FjH^fq$4bqvY3=|NgY0 z`Yj2fXB3Z0TKuAm%s=}%9_dzh4e<)c5v%8#iSI6$zJBY1XczEH=bCqJv{WfqCNr7D zs7s|w>9K6Nk}d_6d@!0T7Sd%G1gT;$mMe}7=YmwFSSkm@lhe+B%AZQKwybTNUnt0A z3)x_}R2rTMaP^@y<1(e;d^$)?=JPX9iKGHZR$`)ZePMNw%Z{L2WB8O`uQTXZ{?qGI zsbtEcUFjU68`71o^i*#lm7aF_zVgV>@JKGL7a&?`AqXb3h05xdAe%x-aNoo33)C#N znkD3iOB?~NdW=ru5=cJ0@S)cRkWX(RQ#4&Bf==A2n2RJ!>Dye*T5+P{Q^kpNf%02= z`)jeFci_gLvj{&Gb8t>_V0~YA(7m}U2%I`nd|WODxcq?I5H9GtW%K&J-cFz!Hg65O zd#KRUH2`R+uhXk;*wo*#epAq&Oya&wFtom7Q#TBXyDe+IRnfb-cSw`dgXzjpHlHp| z8hg4Ig1G82oXe$il&IiFLcX`T)AaRI2sxQ@weE`_@w0q$?4sop&?F@U=mX!vxx zeZ!{Sj?SPt(VSRoruyLuG#u{2SQ^r54$V?>LGMssQ0tMXlJrMwT5Sz2c}KRQD!l_% z9F!|S9R^Xz%ILUZCOA_`TUjZIYbhcrMTe2H!xftHoFcZGd*E^8KDyIo%eYoFGgoZu za7pTonrhK#TROpHx-iP#CWSK3Wkw5?T+9kCEKOunF-R;)djwr;gRZ8!sz_eG&M`-5 z`{N?b=+0ntd}kncUqY*EB1W`6xJ_e_mS5eY>1plU@CdFyWpb0{@z8{{-9!mvDHDTg z4KlbZ3&-O&&!Al9G3#`PjFiE24wr;16Y+jJH$0Ocv@|=5w!RwNfB_r9ttuj^ex^Ib5!|Y;m-%A=K*u>My0;mM)dE#e&0BSe~cfLSLny4DLtmS}KlM*mq-En5xLi3qRv4~zm2ixDR}9Te)J7@V#KveamM<0pbT@SW zfb($w$gR=_qZ74pvU-h{_GWdM4*1?KIs(_nFun8@Q|v3X3TR7cGFoBnsoD*~`KG&o zCzg5+onPJNM}&9_SRT_?akN2?uq$I>X{=?;jP_f(85hpW{z=qY6NneMRm&G`>b~wi z7123TOQ#~*vSx)Hw|*?tX+^q>TG7EF&kPwL^Z3Hg4l0KNSzb}kgcADr#4Iciw^FTU zgIaaOx6H`<9eTFo(O0rDAEWI%lX_Q>r_|JBe~d zv9cvwz|>c(nNGCfyfqy*<8(~~t<$s17=DImjhD366K~v=F3r>mAPn}lun&Y2iOcMg zdyaGK5uH}_IqTPbbDy%wK(go@Y!12$x~x(^*7Z$I#ysgCb?|3pJszCuZ}imY;=;y? z%wRQFSf5MwCi`ov2%A1N4{e>TWeoR;EL=5I?Ae7SQzkoxMbGH;uq>2tb@ldmmA%YR zv8L(D64KO48%ekBc6VgxF;9*2&wFgoyM8H*Wba2rw6f5ErBHb zuE9`zSAYm!k;44_cMP6R^95b58)&$Sv>jAz`~3ZP5uW<*ASkXjd|YS`{^Xkk zukXJ@@bp6NOZf78IZtI9R=#oYY5$#rr|sZNs4u^vxC2R^*Z1F5c*^aOIsWtYm)A|P z-9dx?I}K0$cO6pxEAUtEKY1ONDq_3PLQ|Yd|A+?3D;mm3MD78}@ zJGfElsV}e3)2BU^?Tcvs{yf~%w}0R@jrx2&dT*V+KbQ3MeO{2Xt~zcH^?mzUX++pM zPFtPA3SmXU!-$Kd*Z1d{p32?vXs9#i9s@I~@BBt4>A8!e+O_ugsO&$Ah|k8K-=F!Q zmEZf>%lova;@0_hv&m9OuxxD42^g_Pk&XX@6WHBK5Y74&X=J+{cA7; z^X2#7VYE@d>2j}WbVWSrUPoGTYO2G%{zvL?E0yU8OVV{!RE-_X|CBT?g6z|+#Gfy} rpMS9J7?vH;Fl^y@!A&x`M}p7aL$WvK>+Uh3fAeXR Date: Tue, 10 Dec 2024 12:55:02 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9linux=E5=92=8Cwindow?= =?UTF-8?q?=E4=B8=8B=E7=9A=84ch341=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap_read.py | 16 ++++++---- class_ch341.py | 85 ++++++++++++++++++++++++-------------------------- sensorPara.py | 1 + 3 files changed, 52 insertions(+), 50 deletions(-) diff --git a/cap_read.py b/cap_read.py index ac23804..62462ee 100644 --- a/cap_read.py +++ b/cap_read.py @@ -46,6 +46,16 @@ class ClassCapRead: self.connectStatus = EnumCh341ConnectStatus.CH341_CONNECT_INIT + self.connectDebug() + + def __del__(self): + if self.vofaClient: + self.vofaClient.close() + self.ch341.disconnect() + print("ch341释放") + pass + + def connectDebug(self): #连接到调试的服务器 self.vofaClient = socket.socket() addr = ('127.0.0.1', 1347) @@ -57,12 +67,6 @@ class ClassCapRead: except Exception as e: self.socketConnected = 0 print('连接服务器失败') - def __del__(self): - if self.vofaClient: - self.vofaClient.close() - self.ch341.disconnect() - print("ch341释放") - pass def set_sensor_enable(self, idx): _pack = list() diff --git a/class_ch341.py b/class_ch341.py index a079371..f022bcc 100644 --- a/class_ch341.py +++ b/class_ch341.py @@ -32,28 +32,41 @@ class ClassCh341: pass def init(self): - #print('ch341初始化') - - addr = os.path.dirname(sys.argv[0]) + r'/lib/ch341/CH341DLLA64.DLL' - print(addr) - dllExist = os.path.exists(addr) + if os.name == 'nt': # Windows 环境 + libPath = os.path.dirname(sys.argv[0]) + r'/lib/ch341/CH341DLLA64.DLL' + elif os.name == 'posix': + libPath = './lib/ch341/libch347.so' + dllExist = os.path.exists(libPath) if not dllExist: print('未找到库文件') return False else: try: - self.ic = windll.LoadLibrary(addr) # ch341接口 + if os.name == 'nt': # Windows 环境 + self.ic = windll.LoadLibrary(libPath) # ch341接口 + + self.ch341GetInput = self.ic.CH341GetInput + self.ch341CloseDevice = self.ic.CH341CloseDevice + self.ch341WriteData = self.ic.CH341WriteData + self.ch341WriteRead = self.ic.CH341WriteRead + self.ch341SetOutput = self.ic.CH341SetOutput + self.ch341SetStream = self.ic.CH341SetStream + + elif os.name == 'posix': + self.ic = cdll.LoadLibrary(addr) # ch341接口 + + self.ch341GetInput = self.ic.CH34xGetInput + self.ch341CloseDevice = self.ic.CH34xCloseDevice + self.ch341WriteData = self.ic.CH34xWriteData + self.ch341WriteRead = self.ic.CH34xWriteRead + self.ch341SetOutput = self.ic.CH34xSetOutput + self.ch341SetStream = self.ic.CH34xSetStream + print("ch341加载成功") return True except Exception as e: print("ch341加载失败") return False - - def connectCheck(self): - if True == self.ic.CH341GetInput(self.fd, ctypes.byref(self.deviceID)): - return True - else: - return False # 判断ch341是否插入 # return:0未插入,1插入 @@ -65,6 +78,7 @@ class ClassCh341: print("CH341 device open failed on Windows.") return False else: + self.fd = 0 #todo 改成扫描端口 print("CH341 device opened successfully on Windows.") return True except Exception as e: @@ -93,33 +107,16 @@ class ClassCh341: else: print("Unsupported operating system.") return False - def open1(self): - # 查找匹配的设备路径 - self.fd = self.ic.CH341OpenDevice(0) - if self.fd == -1: - print("ch341 open false") - return False - else: - print("ch341 open true") - return True - # devices = glob.glob('/dev/ch34x_pis*') - # if devices: - # device_path = devices[0].encode() # 选择第一个找到的设备路径 - # self.fd = self.ic.CH34xOpenDevice(device_path)#.encode()"/dev/ch34x_pis1".encode() - # if self.fd == -1: - # print("ch341 open false") - # return False - # else: - # print("ch341 open true") - # return True - # else: - # print("No CH34x device found.") - # return False - - + def disconnect(self): - self.ic.CH341CloseDevice(0) + self.ch341CloseDevice(self.fd) + def connectCheck(self): + if True == self.ch341GetInput(self.fd, ctypes.byref(self.deviceID)): + return True + else: + return False + # iic写数据 # addr:iic从机地址 # data:要写的数据列表 @@ -150,7 +147,7 @@ class ClassCh341: sendBuf[j] = pack[j] sendLen = (c_byte * 1)() sendLen[0] = len(pack) - if not self.ic.CH341WriteData(0, sendBuf, sendLen): + if not self.ch341WriteData(self.fd, sendBuf, sendLen): return 0 if sendLen == 0: return 0 @@ -166,7 +163,7 @@ class ClassCh341: sendBuf[j] = pack[j] sendLen = (c_byte * 1)() sendLen[0] = len(pack) - if not self.ic.CH341WriteData(0, sendBuf, sendLen): + if not self.ch341WriteData(self.fd, sendBuf, sendLen): return 0 if sendLen == 0: return 0 @@ -204,7 +201,7 @@ class ClassCh341: sendBuf[j] = pack[j] recLen = (c_byte * 1)() recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)() - if not self.ic.CH341WriteRead(0, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): + if not self.ch341WriteRead(self.fd, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): return 0 if recLen == 0: return 0 @@ -223,7 +220,7 @@ class ClassCh341: sendBuf[j] = pack[j] recLen = (c_byte * 1)() recBuf = (c_byte * self._mCH341A_CMD_I2C_STM_MAX)() - if not self.ic.CH341WriteRead(0, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): + if not self.ch341WriteRead(self.fd, len(pack), sendBuf, self._mCH341A_CMD_I2C_STM_MAX, 1, recLen, recBuf): return 0 if recLen[0] == 0: return 0 @@ -242,9 +239,9 @@ class ClassCh341: self.ic.CH341GetInput(0, status) time.sleep(0.01) if lvl: - self.ic.CH341SetOutput(0, 0x03, 0xFF00, status[0] | self._mStateBitINT) + self.ch341SetOutput(self.fd, 0x03, 0xFF00, status[0] | self._mStateBitINT) else: - self.ic.CH341SetOutput(0, 0x03, 0xFF00, status[0] & (~self._mStateBitINT)) + self.ch341SetOutput(self.fd, 0x03, 0xFF00, status[0] & (~self._mStateBitINT)) # 读取int引脚状态 # 返回:高低电平 @@ -258,7 +255,7 @@ class ClassCh341: def set_speed(self, speed): if speed != self.IIC_SPEED_20 and speed != self.IIC_SPEED_100 and speed != self.IIC_SPEED_400 and speed != self.IIC_SPEED_750: return False - if False == self.ic.CH341SetStream(0, speed | 0): + if False == self.ch341SetStream(self.fd, speed | 0): print("speed err") return False else: diff --git a/sensorPara.py b/sensorPara.py index 05b0c8f..5b76521 100644 --- a/sensorPara.py +++ b/sensorPara.py @@ -3,6 +3,7 @@ from ctypes import Structure, sizeof, c_float, c_uint32, c_uint16 class DynamicYddsComTs(Structure): + _pack_ = 1 # 按 1 字节对齐 _fields_ = [ ("nf", c_float), ("nfCap", c_uint32), From 2c81b975734f452d9b1dca6cb339c7cb6d96c424 Mon Sep 17 00:00:00 2001 From: jinggaoyang-68 Date: Tue, 10 Dec 2024 14:45:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dlinux=E4=B8=8B=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=A9=B1=E5=8A=A8bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- class_ch341.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/class_ch341.py b/class_ch341.py index f022bcc..ec877a3 100644 --- a/class_ch341.py +++ b/class_ch341.py @@ -53,7 +53,7 @@ class ClassCh341: self.ch341SetStream = self.ic.CH341SetStream elif os.name == 'posix': - self.ic = cdll.LoadLibrary(addr) # ch341接口 + self.ic = cdll.LoadLibrary(libPath) # ch341接口 self.ch341GetInput = self.ic.CH34xGetInput self.ch341CloseDevice = self.ic.CH34xCloseDevice From bcbce1ec86eb74318557dcc9450c048fddbf2585 Mon Sep 17 00:00:00 2001 From: jinggaoyang-68 Date: Tue, 10 Dec 2024 16:18:31 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0vofa=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap_read.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/cap_read.py b/cap_read.py index 62462ee..e39a4ed 100644 --- a/cap_read.py +++ b/cap_read.py @@ -49,8 +49,7 @@ class ClassCapRead: self.connectDebug() def __del__(self): - if self.vofaClient: - self.vofaClient.close() + self.disConnectDebug() self.ch341.disconnect() print("ch341释放") pass @@ -62,12 +61,16 @@ class ClassCapRead: try: self.vofaClient.connect(addr) # client.send('hello world\r\n'.encode()) - self.socketConnected = 1 + self.socketConnected = True print('连接服务器成功') except Exception as e: - self.socketConnected = 0 + self.socketConnected = False print('连接服务器失败') + def disConnectDebug(self): + if self.vofaClient: + self.vofaClient.close() + def set_sensor_enable(self, idx): _pack = list() _pack.append(idx) @@ -126,6 +129,25 @@ class ClassCapRead: else: self.fingers[fingerIndex].capRead() connectedSensorCnt += 1 + + if self.socketConnected == True: + _log1 = "" + # 输出原始通道数值 + for index in range(0, self.fingers[fingerIndex].projectPara.sensor_num): + _log1 += str(self.fingers[fingerIndex].readData.channelCapData[index]) + _log1 += ',' + for index in range(0, self.fingers[fingerIndex].projectPara.ydds_num): + _log1 += str(int(self.fingers[fingerIndex].readData.nf[index]*1000)) + _log1 += ',' + _log1 += str(int(self.fingers[fingerIndex].readData.tf[index]*1000)) + _log1 += ',' + _log1 += str(self.fingers[fingerIndex].readData.tfDir[index]) + _log1 += ',' + _log1 += str(0) + _log1 += '\r\n' + #print(_log1) + if self.socketConnected == 1: + self.vofaClient.send(_log1.encode()) # 大于1个传感器连接需要设置接近采集序列 if connectedSensorCnt > 1 and (time.time() - self.syncTimer) > DEF_CDC_SYNC_MS: From 112c7cdb2d487b1fd93470f1c17b862cc24182e1 Mon Sep 17 00:00:00 2001 From: jinggaoyang-68 Date: Tue, 10 Dec 2024 16:19:49 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0vofa=E6=89=8B=E6=8C=87?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E8=AE=BE=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cap_read.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cap_read.py b/cap_read.py index e39a4ed..62034a7 100644 --- a/cap_read.py +++ b/cap_read.py @@ -131,6 +131,7 @@ class ClassCapRead: connectedSensorCnt += 1 if self.socketConnected == True: + fingerIndex = 0 _log1 = "" # 输出原始通道数值 for index in range(0, self.fingers[fingerIndex].projectPara.sensor_num):