diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4d90124
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+**/__pycache__/
+FlashGBX/config/*bak
\ No newline at end of file
diff --git a/FlashGBX/FlashGBX_CLI.py b/FlashGBX/FlashGBX_CLI.py
index 3c0c4b9..bfb87d2 100644
--- a/FlashGBX/FlashGBX_CLI.py
+++ b/FlashGBX/FlashGBX_CLI.py
@@ -16,8 +16,8 @@
from .PocketCamera import PocketCamera
from .Util import APPNAME, ANSI
from . import Util
-from . import hw_GBxCartRW, hw_GBFlash, hw_JoeyJr
-hw_devices = [hw_GBxCartRW, hw_GBFlash, hw_JoeyJr]
+from . import hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr
+hw_devices = [hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr]
class FlashGBX_CLI():
ARGS = {}
diff --git a/FlashGBX/FlashGBX_GUI.py b/FlashGBX/FlashGBX_GUI.py
index 003953e..ce51fb6 100644
--- a/FlashGBX/FlashGBX_GUI.py
+++ b/FlashGBX/FlashGBX_GUI.py
@@ -12,8 +12,8 @@
from .UserInputDialog import UserInputDialog
from .Util import APPNAME, VERSION, VERSION_PEP440
from . import Util
-from . import hw_GBxCartRW, hw_GBFlash, hw_JoeyJr
-hw_devices = [hw_GBxCartRW, hw_GBFlash, hw_JoeyJr]
+from . import hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr
+hw_devices = [hw_Bacon, hw_GBxCartRW, hw_GBFlash, hw_JoeyJr]
class FlashGBX_GUI(QtWidgets.QWidget):
CONN = None
diff --git a/FlashGBX/LK_Device.py b/FlashGBX/LK_Device.py
index 5473304..9ca9947 100644
--- a/FlashGBX/LK_Device.py
+++ b/FlashGBX/LK_Device.py
@@ -626,7 +626,7 @@ def _cart_write_flash(self, commands, flashcart=False):
buffer.extend(struct.pack("B", 1 if flashcart else 0))
buffer.extend(struct.pack("B", num))
for i in range(0, num):
- dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d})".format(commands[i][0], commands[i][1], i+1, num))
+ dprint("Writing to cartridge: 0x{:X} = 0x{:X} ({:d} of {:d}) flashcart={:s}".format(commands[i][0], commands[i][1], i+1, num, str(flashcart)))
if self.MODE == "AGB" and flashcart:
buffer.extend(struct.pack(">I", commands[i][0] >> 1))
else:
@@ -807,8 +807,14 @@ def SetVarState(self, var_state):
self._write(self.DEVICE_CMD["SET_VAR_STATE"])
time.sleep(0.2)
self.DEVICE.write(var_state)
-
+
def GetMode(self):
+ mode = self._getMode()
+ if mode is None:
+ return self._getMode()
+ return mode
+
+ def _getMode(self):
if time.time() < self.LAST_CHECK_ACTIVE + 1: return self.MODE
if self.CheckActive() is False: return None
if self.MODE is None: return None
@@ -919,6 +925,7 @@ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
print("{:s}Error: No mode was set.{:s}".format(ANSI.RED, ANSI.RESET))
return False
+ dprint("Reading ROM header")
header = self.ReadROM(0, 0x180)
if ".dev" in Util.VERSION_PEP440 or Util.DEBUG:
@@ -1036,7 +1043,7 @@ def ReadInfo(self, setPinsAsInputs=False, checkRtc=True):
data["rtc_string"] = "Not available"
if checkRtc and data["logo_correct"] is True and header[0xC5] == 0 and header[0xC7] == 0 and header[0xC9] == 0:
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
- if self.FW["fw_ver"] >= 12:
+ if self.FW["fw_ver"] >= 12 and self.DEVICE_NAME != "Bacon": # Bacon has a different RTC implementation
self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"])
temp = self._read(8)
data["has_rtc"] = _agb_gpio.HasRTC(temp) is True
@@ -1424,6 +1431,8 @@ def ReadFlashSaveID(self):
return (agb_flash_chip, agb_flash_chip_name)
def ReadROM(self, address, length, skip_init=False, max_length=64):
+ if self.DEVICE_NAME == "Bacon":
+ max_length = length
num = math.ceil(length / max_length)
dprint("Reading 0x{:X} bytes from cartridge ROM at 0x{:X} in {:d} iteration(s)".format(length, address, num))
if length > max_length: length = max_length
@@ -1491,6 +1500,8 @@ def ReadROM_GBAMP(self, address, length, max_length=64):
return self.ReadROM(address=addr, length=length, max_length=max_length)
def ReadRAM(self, address, length, command=None, max_length=64):
+ if self.DEVICE_NAME == "Bacon":
+ max_length = length
num = math.ceil(length / max_length)
dprint("Reading 0x{:X} bytes from cartridge RAM in {:d} iteration(s)".format(length, num))
if length > max_length: length = max_length
@@ -1577,6 +1588,8 @@ def ReadRAM_TAMA5(self):
def WriteRAM(self, address, buffer, command=None, max_length=256):
length = len(buffer)
+ if self.DEVICE_NAME == "Bacon":
+ max_length = length
num = math.ceil(length / max_length)
dprint("Writing 0x{:X} bytes to cartridge RAM in {:d} iteration(s)".format(length, num))
if length > max_length: length = max_length
@@ -1702,8 +1715,10 @@ def WriteRAM_TAMA5(self, buffer):
def WriteROM(self, address, buffer, flash_buffer_size=False, skip_init=False, rumble_stop=False, max_length=MAX_BUFFER_WRITE):
length = len(buffer)
+ if self.DEVICE_NAME == "Bacon":
+ max_length = length
num = math.ceil(length / max_length)
- dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s)".format(length, num))
+ dprint("Writing 0x{:X} bytes to Flash ROM in {:d} iteration(s) flash_buffer_size=0x{:X} skip_init={:s}".format(length, num, flash_buffer_size, str(skip_init)))
if length == 0:
dprint("Length is zero?")
return False
@@ -3401,7 +3416,7 @@ def _BackupRestoreRAM(self, args):
elif self.MODE == "AGB":
_agb_gpio = AGB_GPIO(args={"rtc":True}, cart_write_fncptr=self._cart_write, cart_read_fncptr=self._cart_read, cart_powercycle_fncptr=self.CartPowerCycleOrAskReconnect, clk_toggle_fncptr=self._clk_toggle)
rtc_buffer = None
- if self.FW["fw_ver"] >= 12:
+ if self.FW["fw_ver"] >= 12 and self.DEVICE_NAME != "Bacon": # Bacon has a different RTC implementation
self._write(self.DEVICE_CMD["AGB_READ_GPIO_RTC"])
rtc_buffer = self._read(8)
if len(rtc_buffer) == 8 and _agb_gpio.HasRTC(rtc_buffer) is True:
@@ -4287,6 +4302,8 @@ def _FlashROM(self, args):
sector_size = se_ret
dprint("Next sector size: 0x{:X}".format(sector_size))
skip_init = False
+ if "Bacon" in self.DEVICE_NAME:
+ self.DEVICE.cache_rom(pos, [0xFF]*buffer_len)
# ↑↑↑ Sector erase
if se_ret is not False:
@@ -4646,7 +4663,12 @@ def TransferData(self, args, signal):
if args['mode'] == 1: ret = self._BackupROM(args)
elif args['mode'] == 2: ret = self._BackupRestoreRAM(args)
elif args['mode'] == 3: ret = self._BackupRestoreRAM(args)
- elif args['mode'] == 4: ret = self._FlashROM(args)
+ elif args['mode'] == 4:
+ if "Bacon" in self.DEVICE_NAME:
+ self.DEVICE.cache_rom_reset()
+ ret = self._FlashROM(args)
+ if "Bacon" in self.DEVICE_NAME:
+ self.DEVICE.cache_rom_reset()
elif args['mode'] == 5: ret = self._DetectCartridge(args)
elif args['mode'] == 0xFF: self.Debug()
if self.FW is None: return False
diff --git a/FlashGBX/bacon/.gitignore b/FlashGBX/bacon/.gitignore
new file mode 100644
index 0000000..c8fdf1b
--- /dev/null
+++ b/FlashGBX/bacon/.gitignore
@@ -0,0 +1,2 @@
+**/__pycache__/
+gba*.bin
\ No newline at end of file
diff --git a/FlashGBX/bacon/__init__.py b/FlashGBX/bacon/__init__.py
new file mode 100644
index 0000000..202c16d
--- /dev/null
+++ b/FlashGBX/bacon/__init__.py
@@ -0,0 +1,55 @@
+"""
+CH347 Module
+------------
+
+The `ch347` module provides functions for interacting with the CH347 USB-UART/SPI/I2C/JTAG bridge.
+
+Initialization:
+----------------
+
+- Use `ch347.init()` to initialize the CH347 device.
+
+Configuration:
+--------------
+
+- Use `ch347.set_spi_config(clock: int, mode: int)` to configure the SPI parameters.
+
+SPI Communication:
+------------------
+
+- Use `ch347.spi_write(device_index: int, chip_select: int, write_data: bytes, write_step: int)`
+ to write data to the SPI bus.
+
+- Use `ch347.spi_read(device_index: int, chip_select: int, write_data: bytes, read_length: int)`
+ to read data from the SPI bus.
+
+Closing:
+--------
+
+- Use `ch347.close()` to close the CH347 device.
+
+Usage Example:
+--------------
+
+import ch347
+
+# Initialize the CH347 device
+ch347.init()
+
+# Configure the SPI parameters
+ch347.set_spi_config(clock=1000000, mode=0)
+
+# Write data to the SPI bus
+ch347.spi_write(device_index=0, chip_select=1, write_data=b'\x01\x02\x03', write_step=3)
+
+# Read data from the SPI bus
+data = ch347.spi_read(device_index=0, chip_select=1, write_data=b'\x00', read_length=3)
+
+# Close the CH347 device
+ch347.close()
+"""
+
+from .ch347 import *
+from .bacon import *
+from .command import *
+from .serial import *
\ No newline at end of file
diff --git a/FlashGBX/bacon/bacon.py b/FlashGBX/bacon/bacon.py
new file mode 100644
index 0000000..d87e68e
--- /dev/null
+++ b/FlashGBX/bacon/bacon.py
@@ -0,0 +1,452 @@
+# -*- coding: utf-8 -*-
+# bacon
+# Author: ChisBread (github.com/ChisBread)
+
+import platform
+import random
+import time
+import os
+
+from .command import *
+
+
+ROM_MAX_SIZE = 0x2000000 # 32MB
+RAM_MAX_SIZE = 0x20000 # 128KB (with bank)
+
+class BaconWritePipeline:
+ def __init__(self, dev, flush_func):
+ self.cmds = ""
+ self.dev = dev
+ self.flush_func = flush_func
+ self.MAX_LEN = 0x1000
+
+ def WillFlush(self, cmd: str):
+ if (len(self.cmds) + 1 + len(cmd))//8 >= self.MAX_LEN:
+ return True
+ return False
+
+ def Write(self, cmd: str):
+ if (len(self.cmds) + 1 + len(cmd))//8 >= self.MAX_LEN:
+ self.flush_func(command2bytes(self.cmds))
+ self.cmds = ""
+ if self.cmds:
+ self.cmds += "0"
+ self.cmds += cmd
+ return self
+
+ def IsEmpty(self):
+ return len(self.cmds) == 0
+
+ def Flush(self):
+ if self.cmds:
+ self.flush_func(command2bytes(self.cmds))
+ self.cmds = ""
+ return self
+
+
+
+
+class BaconDevice:
+ GPIO_REG_DAT = 0xC4 # Data low 4bit
+ GPIO_REG_CNT = 0xC6 # IO Select 1:Write to GPIO Device 0:Read from GPIO Device
+ GPIO_REG_RE = 0xC8 # Read Enable Flag Register 1:Enable 0:Disable
+
+ def __init__(self, hid_device=None, ch347_device=None, gpio_device=None):
+ self.hid_device = hid_device
+ self.ch347_device = ch347_device
+ self.gpio_device = gpio_device
+ self.power = 0
+ self.MAX_LEN = 0x1000
+ # if all device is None, find a device
+ ## check if windows
+ if self.ch347_device is None and self.hid_device is None and self.gpio_device is None:
+ if platform.system() == "Windows":
+ if self.ch347_device is None:
+ #WCHAPI
+ from .ch347 import SPIConfig, CH347
+ ch347 = CH347(device_index=0, dll_path=os.path.join(os.path.dirname(__file__), "lib/CH347DLLA64.DLL"))
+ ch347.close_device()
+ ch347.open_device()
+ spi_config = SPIConfig(
+ Mode=3,
+ Clock=0,
+ ByteOrder=1,
+ SpiWriteReadInterval=0,
+ SpiOutDefaultData=0xFF,
+ ChipSelect=0x80,
+ CS1Polarity=0,
+ CS2Polarity=0,
+ IsAutoDeative=1,
+ ActiveDelay=0,
+ DelayDeactive=0,
+ )
+ if ch347.spi_init(spi_config): # True if successful, False otherwise.
+ self.ch347_device = ch347
+ # cannot find wch device, try hid device
+ if self.ch347_device is None:
+ try:
+ #HIDAPI
+ from ch347api import SPIDevice, SPIClockFreq
+ self.hid_device = SPIDevice(clock_freq_level=SPIClockFreq.f_60M, is_16bits=False, mode=3, is_MSB=True)
+ except Exception as e:
+ print(e)
+ elif platform.system() == "Linux" and os.path.exists("/dev/spidev3.0"):
+ try:
+ import spidev
+ spi = spidev.SpiDev()
+ spi.open(3, 0) # bus 3, device 0
+ spi.max_speed_hz = 60000000
+ spi.mode = 0b11
+ spi.bits_per_word = 8
+ spi.lsbfirst = False
+ spi.cshigh = False
+ self.gpio_device = spi
+ except Exception as e:
+ print(e)
+
+
+ if self.ch347_device is None and self.hid_device is None and self.gpio_device is None:
+ raise ValueError("No device found")
+ self.pipeline = BaconWritePipeline(self, self.WriteRead)
+ self.pipeline.MAX_LEN = self.MAX_LEN
+
+######## Low Level API ########
+ def Close(self):
+ if self.gpio_device is not None:
+ self.gpio_device.close()
+ if self.ch347_device is not None:
+ self.ch347_device.close_device()
+ if self.hid_device is not None:
+ self.hid_device.dev.close()
+ self.ch347_device = None
+ self.hid_device = None
+ self.gpio_device = None
+
+ def Write(self, data: bytes) -> bool:
+ if len(data) > self.MAX_LEN:
+ raise ValueError("Data length must be less than 0x%04X" % self.MAX_LEN)
+ if self.gpio_device is not None:
+ self.gpio_device.xfer(data)
+ return True
+ if self.ch347_device is not None:
+ return self.ch347_device.spi_write(0x80, data)
+ if self.hid_device is not None:
+ return self.hid_device.write_CS1(data) > 0
+ raise ValueError("No device found")
+
+ def WriteRead(self, data: bytes) -> bytes:
+ if len(data) > self.MAX_LEN:
+ raise ValueError("Data length must be less than 0x%04X" % self.MAX_LEN)
+ if self.gpio_device is not None:
+ return bytes(self.gpio_device.xfer(data))
+ if self.ch347_device is not None:
+ return bytes(self.ch347_device.spi_write_read(0x80, data))
+ if self.hid_device is not None:
+ return bytes(self.hid_device.writeRead_CS1(data))
+ raise ValueError("No device found")
+
+ def PiplelineFlush(self):
+ return self.pipeline.Flush()
+
+ def ResetAndDelayNS(self, ns: int, v16bit=b"\x00\x00", v8bit=b"\x00", phi=False, req=False, wr=True, rd=True, cs1=True, cs2=True) -> BaconWritePipeline:
+ # 1clk=16.67ns
+ # 1reset=35clk
+ for i in range(ns//int(16.6*35)+1):
+ if self.pipeline.Write(make_cart_30bit_write_command(
+ phi=phi, req=req, wr=wr, rd=rd, cs1=cs1, cs2=cs2,
+ v16bit=v16bit, v8bit=v8bit, postfunc=echo_all
+ )).IsEmpty():
+ return self.pipeline
+ return self.pipeline
+
+ def PowerControl(self, v3_3v: bool, v5v: bool) -> BaconWritePipeline:
+ if v3_3v and v5v:
+ raise ValueError("v3_3v and v5v can't be both enabled")
+ if v3_3v:
+ self.power = 3
+ elif v5v:
+ self.power = 5
+ else:
+ self.power = 0
+ # return self.WriteRead(make_power_control_command(not v3_3v, v5v))
+ return self.pipeline.Write(make_power_control_command(not v3_3v, v5v, postfunc=echo_all))
+
+######## High Level API ########
+
+ def AGBReadROM(self, addr: int, size: int, reset=True, callback=None) -> bytes:
+ if size % 2 != 0:
+ raise ValueError("Size must be a multiple of 2")
+ if addr % 2 != 0:
+ raise ValueError("Address must be a multiple of 2")
+ if addr + size > ROM_MAX_SIZE:
+ #raise ValueError("Address + Size must be less than 0x2000000 address:0x%08X size:0x%08X" % (addr, size))
+ pass
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ # prepare chip
+ ## to halfword
+ addr = addr // 2
+ size = size // 2
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]), postfunc=echo_all
+ ))
+ self.pipeline.Write(make_gba_rom_cs_write(cs=False, postfunc=echo_all)).Flush()
+ lowaddr = addr & 0xFFFF
+ highaddr = (addr >> 16) & 0xFF
+ # if lowaddr+1 == 0x10000, highaddr+1, and reset lowaddr
+ # prepare WriteRead stream
+ readbytes = []
+ cycle_times = 0
+ MAX_TIMES = self.MAX_LEN//(len(make_rom_read_cycle_command(times=1))+1)
+ cnt = 0
+ for i in range(size):
+ cycle_times += 1
+ lowaddr += 1
+ cnt += 2
+ if callback is not None and cnt != size*2:
+ callback(cnt, readbytes)
+ if lowaddr == 0x10000:
+ if cycle_times > 0:
+ ret = self.WriteRead(make_rom_read_cycle_command(times=cycle_times))
+ exteds = extract_read_cycle_data(ret, times=cycle_times)
+ for exted in exteds:
+ readbytes.append(exted >> 8)
+ readbytes.append(exted & 0xFF)
+ cycle_times = 0
+ highaddr += 1
+ lowaddr = 0
+ if highaddr <= 0xFF:
+ # cs1 re-falling?
+ self.WriteRead(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=False, cs2=True,
+ v16bit=b"\x00\x00", v8bit=bytes([highaddr])
+ ))
+ if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0:
+ ret = self.WriteRead(make_rom_read_cycle_command(times=cycle_times))
+ exteds = extract_read_cycle_data(ret, times=cycle_times)
+ for exted in exteds:
+ readbytes.append(exted >> 8)
+ readbytes.append(exted & 0xFF)
+ cycle_times = 0
+ if callback is not None:
+ callback(cnt, readbytes)
+ # reset chip
+ if reset:
+ self.WriteRead(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00"
+ ))
+ return bytes(readbytes)
+
+ def ResetChip(self) -> BaconWritePipeline:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ return self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all
+ ))
+
+ def AGBWriteROMSequential(self, addr, data: bytes, reset=True, callback=None) -> BaconWritePipeline:
+ if addr % 2 != 0:
+ raise ValueError("Address must be a multiple of 2")
+ if len(data) % 2 != 0:
+ raise ValueError("Data length must be a multiple of 2")
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ addr = addr // 2
+ # prepare chip
+ self.pipeline.Write(
+ make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]),
+ postfunc=echo_all
+ )
+ )
+ self.pipeline.Write(make_gba_rom_cs_write(cs=False, postfunc=echo_all))
+ lowaddr = addr & 0xFFFF
+ highaddr = (addr >> 16) & 0xFF
+ cnt = 0
+ for i in range(0, len(data), 2):
+ self.pipeline.Write(make_rom_write_cycle_command_sequential(datalist=[int.from_bytes(data[i:i+2], byteorder='little')], postfunc=echo_all))
+ lowaddr += 1
+ cnt += 2
+ if callback is not None and cnt != len(data):
+ callback(cnt, None)
+ if lowaddr == 0x10000:
+ highaddr += 1
+ lowaddr = 0
+ if highaddr <= 0xFF:
+ # cs1 re-falling?
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=False, cs2=True,
+ v16bit=b"\x00\x00", v8bit=bytes([highaddr]), postfunc=echo_all
+ ))
+ if callback is not None:
+ callback(cnt, None)
+ # reset chip.
+ if reset:
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all
+ ))
+ return self.pipeline
+
+ def AGBWriteROMWithAddress(self, commands: list, callback=None) -> BaconWritePipeline:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ cnt = 0
+ # addr这里使用绝对地址,需要//2
+ # 如果commands中的data为bytes,需要转换为int(小端)
+ for i in range(len(commands)):
+ if type(commands[i][1]) == bytes:
+ commands[i] = (commands[i][0]//2, int.from_bytes(commands[i][1], byteorder='little'))
+ else:
+ commands[i] = (commands[i][0]//2, commands[i][1])
+ for i in range(len(commands)):
+ self.pipeline.Write(make_rom_write_cycle_command_with_addr(addrdatalist=[commands[i]], postfunc=echo_all))
+ cnt += 1
+ if callback is not None and cnt != len(commands):
+ callback(cnt, None)
+ if callback is not None:
+ callback(cnt, None)
+ # reset chip
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all
+ ))
+ return self.pipeline
+
+ def AGBReadRAM(self, addr: int, size: int, bankswitch=None, callback=None) -> bytes:
+ if addr + size > RAM_MAX_SIZE:
+ raise ValueError("Address + Size must be less than 0x20000")
+ if addr + size > RAM_MAX_SIZE/2 and bankswitch is None:
+ raise ValueError("Address + Size must be less than 0x10000 or bankswitch must be provided address:0x%08X size:0x%08X" % (addr, size))
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ # prepare chip
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=False,
+ cs1=True, cs2=False,
+ v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=b"\x00", postfunc=echo_all
+ )).Flush()
+ readbytes = []
+ cycle_times = 0
+ MAX_TIMES = self.MAX_LEN//(len(make_ram_read_cycle_command(addr=0, times=1))+1)
+ cnt = 0
+ start_addr = addr
+ for i in range(size):
+ cycle_times += 1
+ cnt += 1
+ if callback is not None and cnt != size:
+ callback(cnt, readbytes)
+ if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0:
+ ret = self.WriteRead(make_ram_read_cycle_command(addr=start_addr, times=cycle_times))
+ exteds = extract_ram_read_cycle_data(ret, times=cycle_times)
+ for exted in exteds:
+ readbytes.append(exted)
+ start_addr += cycle_times
+ cycle_times = 0
+ if callback is not None:
+ callback(cnt, readbytes)
+ # reset chip
+ self.WriteRead(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00"
+ ))
+ return bytes(readbytes)
+
+ def AGBWriteRAM(self, addr: int, data: bytes, bankswitch=None, callback=None) -> bool:
+ size = len(data)
+ if addr + size > RAM_MAX_SIZE:
+ raise ValueError("Address + Size must be less than 0x20000")
+ if addr + size > RAM_MAX_SIZE/2 and bankswitch is None:
+ raise ValueError("Address + Size must be less than 0x10000 or bankswitch must be provided address:0x%08X size:0x%08X" % (addr, size))
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ readbytes = []
+ cycle_times = 0
+ MAX_TIMES = self.MAX_LEN//(len(make_ram_write_cycle_command(addr=0, data=b"\00"))+1)
+ cnt = 0
+ start_addr = addr
+ for i in range(size):
+ cycle_times += 1
+ cnt += 1
+ if callback is not None and cnt != size:
+ callback(cnt, readbytes)
+ if cycle_times == MAX_TIMES or i == size - 1 and cycle_times > 0:
+ ret = self.WriteRead(make_ram_write_cycle_command(addr=start_addr, data=data[start_addr-addr:start_addr-addr+cycle_times]))
+ readbytes = readbytes + ([0]*cycle_times)
+ start_addr += cycle_times
+ cycle_times = 0
+ if callback is not None:
+ callback(cnt, readbytes)
+ # reset chip
+ self.WriteRead(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00"
+ ))
+ return bytes(readbytes)
+
+ def AGBWriteRAMWithAddress(self, commands: list, reset=True, callback=None) -> BaconWritePipeline:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ cnt = 0
+ for i in range(len(commands)):
+ self.pipeline.Write(make_ram_write_cycle_with_addr(addrdatalist=[commands[i]], postfunc=echo_all))
+ cnt += 1
+ if callback is not None and cnt != len(commands):
+ callback(cnt, None)
+ if callback is not None:
+ callback(cnt, None)
+ # reset chip
+ if reset:
+ self.pipeline.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00", postfunc=echo_all
+ ))
+ return self.pipeline
+
+ def AGBCustomWriteCommands(self, commands: list, callback=None) -> bool:
+ pass
+
+ def AGBGPIOEnable(self) -> BaconWritePipeline:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ return self.AGBWriteROMSequential(self.GPIO_REG_RE, b"\x01")
+
+ def AGBGPIOSetDirection(self, direction: int) -> BaconWritePipeline:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ return self.AGBWriteROMSequential(self.GPIO_REG_CNT, bytes([direction]))
+
+ def AGBGPIORead(self) -> int:
+ if self.power != 3:
+ raise ValueError("Power must be 3.3v")
+ return self.AGBReadROM(self.GPIO_REG_DAT, 1)[0]
+
+ def DMGReadROM(self, addr: int, size: int, reset=True, callback=None) -> bytes:
+ pass
\ No newline at end of file
diff --git a/FlashGBX/bacon/ch347.py b/FlashGBX/bacon/ch347.py
new file mode 100644
index 0000000..6b9fbc5
--- /dev/null
+++ b/FlashGBX/bacon/ch347.py
@@ -0,0 +1,665 @@
+import ctypes
+from typing import List
+
+
+class DeviceInfo(ctypes.Structure):
+ MAX_PATH = 260
+ _fields_ = [
+ ("DeviceIndex", ctypes.c_ubyte), # 当前打开序号
+ ("DevicePath", ctypes.c_char * MAX_PATH), # 设备路径
+ (
+ "UsbClass",
+ ctypes.c_ubyte,
+ ), # USB设备类别: 0=CH341 Vendor; 1=CH347 Vendor; 2=HID
+ ("FuncType", ctypes.c_ubyte), # 设备功能类型: 0=UART1; 1=SPI+I2C; 2=JTAG+I2C
+ ("DeviceID", ctypes.c_char * 64), # USB设备ID: USB\VID_xxxx&PID_xxxx
+ (
+ "ChipMode",
+ ctypes.c_ubyte,
+ ), # 芯片模式: 0=Mode0(UART*2); 1=Mode1(Uart1+SPI+I2C); 2=Mode2(HID Uart1+SPI+I2C); 3=Mode3(Uart1+Jtag+I2C)
+ ("DevHandle", ctypes.c_void_p), # 设备句柄
+ ("BulkOutEndpMaxSize", ctypes.c_ushort), # 上传端点大小
+ ("BulkInEndpMaxSize", ctypes.c_ushort), # 下传端点大小
+ ("UsbSpeedType", ctypes.c_ubyte), # USB速度类型: 0=FS; 1=HS; 2=SS
+ ("CH347IfNum", ctypes.c_ubyte), # USB接口号
+ ("DataUpEndp", ctypes.c_ubyte), # 端点地址
+ ("DataDnEndp", ctypes.c_ubyte), # 端点地址
+ ("ProductString", ctypes.c_char * 64), # USB产品字符串
+ ("ManufacturerString", ctypes.c_char * 64), # USB厂商字符串
+ ("WriteTimeout", ctypes.c_ulong), # USB写超时
+ ("ReadTimeout", ctypes.c_ulong), # USB读超时
+ ("FuncDescStr", ctypes.c_char * 64), # 接口功能描述符
+ ("FirmwareVer", ctypes.c_ubyte), # 固件版本
+ ]
+
+
+class SPIConfig(ctypes.Structure):
+ _fields_ = [
+ ("Mode", ctypes.c_ubyte), # 0-3: SPI Mode0/1/2/3
+ (
+ "Clock",
+ ctypes.c_ubyte,
+ ), # 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz
+ ("ByteOrder", ctypes.c_ubyte), # 0=LSB first(LSB), 1=MSB first(MSB)
+ (
+ "SPIWriteReadInterval",
+ ctypes.c_ushort,
+ ), # Regular interval for SPI read/write commands, in microseconds
+ (
+ "SPIOutDefaultData",
+ ctypes.c_ubyte,
+ ), # Default output data when reading from SPI
+ (
+ "ChipSelect",
+ ctypes.c_ulong,
+ ), # Chip select control. Bit 7 as 0 ignores chip select control,
+ # Bit 7 as 1 makes the parameters valid:
+ # Bit 1 and Bit 0 as 00/01 selects CS1/CS2 pin as the active low chip select.
+ (
+ "CS1Polarity",
+ ctypes.c_ubyte,
+ ), # Bit 0: CS1 polarity control, 0: active low, 1: active high
+ (
+ "CS2Polarity",
+ ctypes.c_ubyte,
+ ), # Bit 0: CS2 polarity control, 0: active low, 1: active high
+ (
+ "IsAutoDeativeCS",
+ ctypes.c_ushort,
+ ), # Automatically de-assert chip select after the operation is completed
+ (
+ "ActiveDelay",
+ ctypes.c_ushort,
+ ), # Delay time for executing read/write operations after chip select is set, in microseconds
+ (
+ "DelayDeactive",
+ ctypes.c_ulong,
+ ), # Delay time for executing read/write operations after chip select is de-asserted, in microseconds
+ ]
+
+
+class CH347:
+
+ # MAX devices number
+ MAX_DEVICE_NUMBER = 8
+
+ # Define the callback function type
+ NOTIFY_ROUTINE = ctypes.CFUNCTYPE(None, ctypes.c_ulong)
+
+ INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
+
+ def __init__(self, device_index=0, dll_path="ch347/lib/CH347DLLA64.DLL"):
+ self.ch347dll = ctypes.WinDLL(dll_path)
+ self.device_index = device_index
+
+ # 创建回调函数对象并绑定到实例属性
+ self.callback_func = self.NOTIFY_ROUTINE(self.event_callback)
+
+ # Set the function argument types and return type for CH347OpenDevice
+ self.ch347dll.CH347OpenDevice.argtypes = [ctypes.c_ulong]
+ self.ch347dll.CH347OpenDevice.restype = ctypes.c_void_p
+
+ # Set the function argument types and return type for CH347CloseDevice
+ self.ch347dll.CH347CloseDevice.argtypes = [ctypes.c_ulong]
+ self.ch347dll.CH347CloseDevice.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347GetDeviceInfor
+ self.ch347dll.CH347GetDeviceInfor.argtypes = [
+ ctypes.c_ulong,
+ ctypes.POINTER(DeviceInfo),
+ ]
+ self.ch347dll.CH347GetDeviceInfor.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347GetVersion
+ self.ch347dll.CH347GetVersion.argtypes = [
+ ctypes.c_ulong,
+ ctypes.POINTER(ctypes.c_ubyte),
+ ctypes.POINTER(ctypes.c_ubyte),
+ ctypes.POINTER(ctypes.c_ubyte),
+ ctypes.POINTER(ctypes.c_ubyte),
+ ]
+ self.ch347dll.CH347GetVersion.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SetDeviceNotify
+ self.ch347dll.CH347SetDeviceNotify.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_char_p,
+ ctypes.CFUNCTYPE(None, ctypes.c_ulong),
+ ]
+ self.ch347dll.CH347SetDeviceNotify.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347ReadData
+ self.ch347dll.CH347ReadData.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_ulong),
+ ]
+ self.ch347dll.CH347ReadData.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347WriteData
+ self.ch347dll.CH347WriteData.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ctypes.POINTER(ctypes.c_ulong),
+ ]
+ self.ch347dll.CH347WriteData.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SetTimeout
+ self.ch347dll.CH347SetTimeout.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ]
+ self.ch347dll.CH347SetTimeout.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_Init
+ self.ch347dll.CH347SPI_Init.argtypes = [
+ ctypes.c_ulong,
+ ctypes.POINTER(SPIConfig),
+ ]
+ self.ch347dll.CH347SPI_Init.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_GetCfg
+ self.ch347dll.CH347SPI_GetCfg.argtypes = [
+ ctypes.c_ulong,
+ ctypes.POINTER(SPIConfig),
+ ]
+ self.ch347dll.CH347SPI_GetCfg.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_ChangeCS
+ self.ch347dll.CH347SPI_ChangeCS.argtypes = [ctypes.c_ulong, ctypes.c_ubyte]
+ self.ch347dll.CH347SPI_ChangeCS.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_SetChipSelect
+ self.ch347dll.CH347SPI_SetChipSelect.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ushort,
+ ctypes.c_ushort,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ]
+ self.ch347dll.CH347SPI_SetChipSelect.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_Write
+ self.ch347dll.CH347SPI_Write.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ]
+ self.ch347dll.CH347SPI_Write.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_Read
+ self.ch347dll.CH347SPI_Read.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.POINTER(ctypes.c_ulong),
+ ctypes.c_void_p,
+ ]
+ self.ch347dll.CH347SPI_Read.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347SPI_WriteRead
+ self.ch347dll.CH347SPI_WriteRead.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ]
+ self.ch347dll.CH347SPI_WriteRead.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347StreamSPI4
+ self.ch347dll.CH347StreamSPI4.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ]
+ self.ch347dll.CH347StreamSPI4.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347I2C_Set
+ self.ch347dll.CH347I2C_Set.argtypes = [ctypes.c_ulong, ctypes.c_ulong]
+ self.ch347dll.CH347I2C_Set.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347I2C_SetDelaymS
+ self.ch347dll.CH347I2C_SetDelaymS.argtypes = [ctypes.c_ulong, ctypes.c_ulong]
+ self.ch347dll.CH347I2C_SetDelaymS.restype = ctypes.c_bool
+
+ # Set the function argument types and return type for CH347StreamI2C
+ self.ch347dll.CH347StreamI2C.argtypes = [
+ ctypes.c_ulong,
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ctypes.c_ulong,
+ ctypes.c_void_p,
+ ]
+ self.ch347dll.CH347StreamI2C.restype = ctypes.c_bool
+
+ def list_devices(self):
+ # List all devices
+ num_devices = 0
+ dev_info = DeviceInfo()
+ for i in range(self.MAX_DEVICE_NUMBER):
+ if self.ch347dll.CH347OpenDevice(i) == self.INVALID_HANDLE_VALUE:
+ break
+ num_devices += 1
+ if self.ch347dll.CH347GetDeviceInfor(i, ctypes.byref(dev_info)):
+ for field_name, _ in dev_info._fields_:
+ value = getattr(dev_info, field_name)
+ print(f"{field_name}: {value}")
+ print("-" * 40)
+ self.ch347dll.CH347CloseDevice(i)
+ print(f"Number of devices: {num_devices}")
+ return num_devices
+
+ @staticmethod
+ def event_callback(self, event_status):
+ # Callback function implementation
+ print("Callback event status:", event_status)
+ if event_status == 0:
+ # Device unplug event
+ print("Device unplugged")
+ elif event_status == 3:
+ # Device insertion event
+ print("Device inserted")
+
+ def open_device(self):
+ """
+ Open USB device.
+
+ Returns:
+ int: Handle to the opened device if successful, None otherwise.
+ """
+ handle = self.ch347dll.CH347OpenDevice(self.device_index)
+ if handle != self.INVALID_HANDLE_VALUE:
+ return handle
+ else:
+ return None
+
+ def close_device(self):
+ """
+ Close USB device.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347CloseDevice(self.device_index)
+ return result
+
+ def get_device_info(self):
+ """
+ Get device information.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ DeviceInfo: Device information.
+ """
+ dev_info = DeviceInfo()
+ result = self.ch347dll.CH347GetDeviceInfor(
+ self.device_index, ctypes.byref(dev_info)
+ )
+ if result:
+ return dev_info
+ else:
+ return None
+
+ def get_version(self):
+ """
+ Obtain driver version, library version, device version, and chip type.
+
+ This method retrieves various versions related to the CH347 device and returns them as a tuple.
+
+ Returns:
+ tuple or None: A tuple containing the following information if successful:
+ - driver_ver (int): The driver version.
+ - dll_ver (int): The library version.
+ - device_ver (int): The device version.
+ - chip_type (int): The chip type.
+ Returns None if the retrieval fails.
+ """
+ # Create variables to store the version information
+ driver_ver = ctypes.c_ubyte()
+ dll_ver = ctypes.c_ubyte()
+ device_ver = ctypes.c_ubyte()
+ chip_type = ctypes.c_ubyte()
+
+ # Call the CH347GetVersion function
+ result = self.ch347dll.CH347GetVersion(
+ self.device_index,
+ ctypes.byref(driver_ver),
+ ctypes.byref(dll_ver),
+ ctypes.byref(device_ver),
+ ctypes.byref(chip_type),
+ )
+ if result:
+ return driver_ver.value, dll_ver.value, device_ver.value, chip_type.value
+ else:
+ return None
+
+ def set_device_notify(self, device_id, notify_routine=event_callback):
+ """
+ Configure device event notifier.
+
+ Args:
+ device_id (str): Optional parameter specifying the ID of the monitored device.
+ notify_routine (callable): Callback function to handle device events.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ callback = self.NOTIFY_ROUTINE(notify_routine)
+ result = self.ch347dll.CH347SetDeviceNotify(
+ self.device_index, device_id, callback
+ )
+ return result
+
+ def read_data(self, buffer, length):
+ """
+ Read USB data block.
+
+ Args:
+ buffer (ctypes.c_void_p): Pointer to a buffer to store the read data.
+ length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Contains the length to be read as input and the actual read length after return.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347ReadData(self.device_index, buffer, length)
+ return result
+
+ def write_data(self, buffer, length):
+ """
+ Write USB data block.
+
+ Args:
+ buffer (ctypes.c_void_p): Pointer to a buffer containing the data to be written.
+ length (ctypes.POINTER(ctypes.c_ulong)): Pointer to the length unit. Input length is the intended length, and the return length is the actual length.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347WriteData(self.device_index, buffer, length)
+ return result
+
+ def set_timeout(self, write_timeout, read_timeout):
+ """
+ Set the timeout of USB data read and write.
+
+ Args:
+ write_timeout (int): Timeout for USB to write data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default).
+ read_timeout (int): Timeout for USB to read data blocks, in milliseconds. Use 0xFFFFFFFF to specify no timeout (default).
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347SetTimeout(
+ self.device_index, write_timeout, read_timeout
+ )
+ return result
+
+ def spi_init(self, spi_config: SPIConfig) -> bool:
+ """
+ Initialize the SPI Controller.
+
+ Args:
+ spi_config (SPIConfig): The configuration for the SPI controller.
+
+ Returns:
+ bool: True if initialization is successful, False otherwise.
+ """
+ result = self.ch347dll.CH347SPI_Init(
+ self.device_index, ctypes.byref(spi_config)
+ )
+ return result
+
+ def spi_get_config(self):
+ """
+ Get SPI controller configuration information.
+
+ Returns:
+ tuple: A tuple containing a boolean value indicating if the operation was successful
+ and the SPI configuration structure.
+
+ The first element (bool) represents whether the operation was successful or not.
+ - True: The operation was successful.
+ - False: The operation failed.
+
+ The second element (SPIConfig): An instance of the SPIConfig class, representing the
+ SPI configuration structure. If the operation was successful, this object will contain
+ the configuration information retrieved from the SPI controller. Otherwise, it will be
+ an empty object with default values.
+ """
+ spi_config = SPIConfig()
+ result = self.ch347dll.CH347SPI_GetCfg(
+ self.device_index, ctypes.byref(spi_config)
+ )
+ if result:
+ return spi_config
+ else:
+ return None
+
+ def spi_change_cs(self, status):
+ """
+ Change the chip selection status.
+
+ Args:
+ status (int): Chip selection status. 0 = Cancel the piece to choose, 1 = Set piece selected.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347SPI_ChangeCS(self.device_index, status)
+ return result
+
+ def spi_set_chip_select(
+ self,
+ enable_select,
+ chip_select,
+ is_auto_deactive_cs,
+ active_delay,
+ delay_deactive,
+ ):
+ """
+ Set SPI chip selection.
+
+ Args:
+ enable_select (int): Enable selection status. The lower octet is CS1 and the higher octet is CS2.
+ A byte value of 1 sets CS, 0 ignores this CS setting.
+ chip_select (int): Chip selection status. The lower octet is CS1 and the higher octet is CS2.
+ A byte value of 1 sets CS, 0 ignores this CS setting.
+ is_auto_deactive_cs (int): Auto deactivation status. The lower 16 bits are CS1 and the higher 16 bits are CS2.
+ Whether to undo slice selection automatically after the operation is complete.
+ active_delay (int): Latency of read/write operations after chip selection, in microseconds.
+ The lower 16 bits are CS1 and the higher 16 bits are CS2.
+ delay_deactive (int): Delay time for read and write operations after slice selection, in microseconds.
+ The lower 16 bits are CS1 and the higher 16 bits are CS2.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347SPI_SetChipSelect(
+ self.device_index,
+ enable_select,
+ chip_select,
+ is_auto_deactive_cs,
+ active_delay,
+ delay_deactive,
+ )
+ return result
+
+ def spi_write(
+ self, chip_select: int, write_data: List[int], write_step: int = 512
+ ) -> bool:
+ result = self.spi_write(chip_select, bytes(write_data), write_step)
+ def spi_write(
+ self, chip_select: int, write_data: bytes, write_step: int = 512
+ ) -> bool:
+ """
+ SPI write data.
+
+ Args:
+ chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored.
+ When bit 7 is 1, chip selection operation is performed.
+ write_data (List[int]): List of integers to write.
+ write_step (int, optional): The length of a single block to be read. Default is 512.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ write_length = len(write_data)
+ write_buffer = ctypes.create_string_buffer(write_data)
+ result = self.ch347dll.CH347SPI_Write(
+ self.device_index, chip_select, write_length, write_step, write_buffer
+ )
+ return result
+
+ def spi_read(
+ self, chip_select: int, write_data: List[int], read_length: int
+ ) -> List[int]:
+ """
+ SPI read data.
+
+ Args:
+ chip_select (int): Chip selection control. When bit 7 is 0, chip selection control is ignored.
+ When bit 7 is 1, chip selection operation is performed.
+ write_data (List[int]): List of integers to write.
+ read_length (int): Number of bytes to read.
+
+ Returns:
+ List[int]: Data read in from the SPI stream if successful, None otherwise.
+ """
+ write_length = len(write_data)
+
+ # Create ctypes buffer for write data
+ write_buffer = ctypes.create_string_buffer(bytes(write_data))
+
+ # Create ctypes buffer for read data
+ read_buffer = ctypes.create_string_buffer(read_length)
+
+ # Create combined buffer for read and write data
+ combined_buffer = ctypes.create_string_buffer(
+ write_buffer.raw[:write_length] + read_buffer.raw
+ )
+
+ result = self.ch347dll.CH347SPI_Read(
+ self.device_index,
+ chip_select,
+ write_length,
+ ctypes.byref(ctypes.c_ulong(read_length)),
+ combined_buffer,
+ )
+
+ if result:
+ # Extract the read data from the combined buffer
+ read_data = list(combined_buffer[:read_length])
+ return read_data
+ else:
+ return None
+
+ def spi_write_read(self, chip_select: int, write_data: bytes) -> bytes:
+ io_buffer = ctypes.create_string_buffer(write_data)
+ result = self.ch347dll.CH347SPI_WriteRead(
+ self.device_index, chip_select, len(write_data), io_buffer
+ )
+ # transform the ctypes buffer to bytes
+ io_data = io_buffer.raw[: len(write_data)]
+ return io_data
+
+ def _spi_write_read(self, chip_select: int, length: int, io_buffer: ctypes.c_void_p) -> bool:
+ """
+ Handle SPI data stream 4-wire interface.
+
+ Args:
+ chip_select (int): Selection control. If the film selection control bit 7 is 0, ignore the film selection control.
+ If bit 7 is 1, perform the film selection.
+ length (int): Number of bytes of data to be transferred.
+ io_buffer (ctypes.c_void_p): Points to a buffer that places the data to be written out from DOUT.
+ Returns the data read in from DIN.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347SPI_WriteRead(
+ self.device_index, chip_select, length, io_buffer
+ )
+ return result
+
+ def stream_spi4(self, chip_select, length, io_buffer):
+ """
+ Handle SPI data stream 4-wire interface.
+
+ Args:
+ chip_select (int): Film selection control. If bit 7 is 0, slice selection control is ignored.
+ If bit 7 is 1, the parameter is valid: Bit 1 bit 0 is 00/01/10.
+ Select D0/D1/D2 pins as low-level active chip options, respectively.
+ length (int): Number of bytes of data to be transferred.
+ io_buffer (ctypes.c_void_p): Points to a buffer that places data to be written out from DOUT.
+ Returns data to be read in from DIN.
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347StreamSPI4(
+ self.device_index, chip_select, length, io_buffer
+ )
+ return result
+
+ def i2c_set(self, interface_speed):
+ """
+ Set the serial port flow mode.
+
+ Args:
+ interface_speed (int): I2C interface speed / SCL frequency. Bit 1-bit 0:
+ 0 = low speed / 20KHz
+ 1 = standard / 100KHz (default)
+ 2 = fast / 400KHz
+ 3 = high speed / 750KHz
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347I2C_Set(self.device_index, interface_speed)
+ return result
+
+ def i2c_set_delay_ms(self, delay_ms):
+ """
+ Set the hardware asynchronous delay to a specified number of milliseconds before the next stream operation.
+
+ Args:
+ delay_ms (int): Delay duration in milliseconds (ms).
+
+ Returns:
+ bool: True if successful, False otherwise.
+ """
+ result = self.ch347dll.CH347I2C_SetDelaymS(self.device_index, delay_ms)
+ return result
+
+ def stream_i2c(self, write_data, read_length):
+ """
+ Process I2C data stream.
+
+ Args:
+ write_data (bytes): Data to write. The first byte is usually the I2C device address and read/write direction bit.
+ read_length (int): Number of bytes of data to read.
+
+ Returns:
+ bytes: Data read from the I2C stream.
+ """
+ write_length = len(write_data)
+
+ # Convert write_data to ctypes buffer
+ write_buffer = ctypes.create_string_buffer(bytes(write_data))
+
+ # Create ctypes buffer for read data
+ read_buffer = ctypes.create_string_buffer(read_length)
+
+ result = self.ch347dll.CH347StreamI2C(
+ self.device_index, write_length, write_buffer, read_length, read_buffer
+ )
+
+ if result:
+ return read_buffer[:read_length]
+ else:
+ return None
diff --git a/FlashGBX/bacon/command.py b/FlashGBX/bacon/command.py
new file mode 100644
index 0000000..16ddbdd
--- /dev/null
+++ b/FlashGBX/bacon/command.py
@@ -0,0 +1,292 @@
+# -*- coding: utf-8 -*-
+# bacon
+# Author: ChisBread (github.com/ChisBread)
+
+# 命令结束后需要跟一个0bit
+
+# -- | Command Value (5 bits) | Command Name | Description | Input Bits | Input Description | Output Bits | Output Description |
+# -- |------------------------|--------------|-------------|------------|-------------------|-------------|--------------------|
+# -- | 00001 | CART_30BIT_WRITE | 30位数据写入 | 30 | 30位数据 | - | 无返回 |
+# -- | 00010 | CART_30BIT_READ | 30位数据读取 | - | 无输入 | 30 | 30位数据 |
+# -- | 00011 | MOD_WRITE | 控制电源 | 2 | 10: 都不启用
00: 启用3.3v(GBA)
11: 启用5v(GB)
01: 无效 | - | 无返回 |
+# -- | 00100 | MOD_READ | 读取电源状态 | - | 无输入 | 2 | 0: 都不启用
01: 启用3.3v(GBA)
10: 启用5v(GB)
11: 无效 |
+# -- | 00101 | GBA_WR/RD_WRITE | GBA 2bit寄存器操作,每次上升沿会使锁存的16bit地址自增1 | 2 | 0: 无效
01: 启用WR
10: 启用RD
11: 默认 | - | 无返回 |
+# -- | 00110 | GBA_WR/RD_READ | 读取GBA 2bit寄存器状态 | - | 无输入 | 2 | 0: 无效
01: 启用WR
10: 启用RD
11: 默认 |
+# -- | 00111 | GBA_ROM_EN_WRITE | GBA ROM使能 | 1 | 使能 | - | 无返回 |
+# -- | 01000 | GBA_ROM_ADDR_READ | 读取GBA R高8位地址 | - | 无输入 | 8 | 8位数据 |
+# -- | 01001 | GBA_ROM_DATA_WRITE | GBA ROM写16位数据 | 16 | 16位数据 | - | 无返回 |
+# -- | 01010 | GBA_ROM_DATA_READ | 读取GBA ROM数据 | - | 无输入 | 16 | 16位数据 |
+# -- | 01011 | GBA_ROM_DATA_READ_FLIP | 读取GBA ROM数据 | - | 无输入 | 16 | 16位数据 |
+# -- | 01100 | GBA_ROM_DATA_WRITE_FLIP | GBA ROM写16位数据 | 16 | 16位数据 | - | 无返回 |
+# -- | 10000 - 11111 | RESERVED | 预留命令 | - | - | - | - |
+import traceback
+from bitarray import bitarray
+
+def command2bytes(command, endclk=True) -> bytes:
+ if isinstance(command, str):
+ command = bitarray(command)
+ if endclk and len(command) % 8 == 0:
+ # end clk cycle
+ command += "0"
+ if len(command) % 8:
+ command += bitarray("0" * (8 - len(command) % 8))
+ return command.tobytes()
+
+def echo_all(data):
+ return data
+
+def bytes2command(data: bytes):
+ ret = bitarray()
+ ret.frombytes(data)
+ return ret
+
+def make_power_control_command(
+ # 3.3v低电平有效, 5v高电平有效
+ not_v3_3v: bool = True, v5v: bool = False, postfunc=command2bytes) -> bytes:
+ # 不能两个都有效
+ if not not_v3_3v and v5v:
+ raise ValueError("v3_3v and v5v can't be both enabled")
+ command = "00011"
+ command += "1" if v5v else "0"
+ command += "1" if not_v3_3v else "0"
+ return postfunc(command)
+
+def make_power_read_command(postfunc=command2bytes) -> bytes:
+ command = "00100"
+ return postfunc(command)
+
+def make_cart_30bit_write_command(
+ phi: bool = False, req: bool = False,
+ # 低电平有效
+ wr: bool = True, rd: bool = True,
+ cs1: bool = True, cs2: bool = True,
+ # 小端模式
+ v16bit: bytes = b"\00\00", v8bit: bytes = b"\00", postfunc=command2bytes) -> bytes:
+ command = "00001"
+ command += "1" if req else "0"
+ command += "1" if cs2 else "0"
+ if len(v16bit) != 2:
+ raise ValueError("v16bit must be 2 bytes")
+ if len(v8bit) != 1:
+ raise ValueError("v8bit must be 1 byte")
+ command += bin(v8bit[0])[2:].rjust(8, "0")
+ command += bin(v16bit[1])[2:].rjust(8, "0")
+ command += bin(v16bit[0])[2:].rjust(8, "0")
+ command += "1" if cs1 else "0"
+ command += "1" if rd else "0"
+ command += "1" if wr else "0"
+ command += "1" if phi else "0"
+ return postfunc(command)
+
+def make_cart_30bit_read_command(postfunc=command2bytes) -> bytes:
+ command = "00010"
+ command += "0" * 30
+ return postfunc(command)
+
+def make_gba_wr_rd_write_command(
+ wr: bool = False, rd: bool = False, postfunc=command2bytes) -> bytes:
+ command = "00101"
+ command += "1" if wr else "0"
+ command += "1" if rd else "0"
+ return postfunc(command)
+
+def extract_cart_30bit_read_data(data: bytes) -> dict:
+ if len(data) != 5:
+ raise ValueError("data must be 5 bytes, but got %d" % len(data))
+ ret = {}
+ # 输出和输出是反的,且有6bit无效数据,所以
+ # 000000 00 | 10 010101 | 011010101001010101100010
+ ret["phi"] = bool(data[0] >> 1 & 1)
+ ret["wr"] = bool(data[0] & 1)
+ ret["rd"] = bool(data[1] >> 7)
+ ret["cs1"] = bool(data[1] >> 6 & 1)
+ # 16bit数据, 其中6bit在data[1]的低位, 2bit在data[2]的高位
+ v16bitB0 = ((data[1] & 0b00111111) << 2) | (data[2] >> 6)
+ # 16bit数据, 其中6bit在data[2]的低位, 2bit在data[3]的高位
+ v16bitB1 = ((data[2] & 0b00111111) << 2) | (data[3] >> 6)
+ # 8bit数据, 其中6bit在data[3]的低位, 2bit在data[4]的高位
+ v8bit = ((data[3] & 0b00111111) << 2) | (data[4] >> 6)
+ # 3byte都需要按位翻转
+ # v16bitB0 = reverse_bits(v16bitB0)
+ # v16bitB1 = reverse_bits(v16bitB1)
+ v8bit = reverse_bits(v8bit)
+ # ret["v16bit"] = (v16bitB0 << 8) | v16bitB1
+ ret["v16bit"] = reverse_bits_16bit((v16bitB1 << 8) | v16bitB0)
+ ret["v8bit"] = v8bit
+ ret["cs2"] = bool(data[4] >> 5 & 1)
+ ret["req"] = bool(data[4] >> 4 & 1)
+ return ret
+
+
+def make_v16bit_data_write_command(data: int, flip=False, postfunc=command2bytes) -> bytes:
+ if data > 0xFFFF:
+ raise ValueError("data must be less than 0xFFFF")
+ command = ("01100" if flip else "01001") + bin(data)[2:].rjust(16, "0")
+ return postfunc(command)
+
+def make_gba_rom_data_write_command(data: int, flip=False, postfunc=command2bytes) -> bytes:
+ return make_v16bit_data_write_command(data, flip, postfunc)
+
+__readcyclecmd_30bit = "0".join([
+ make_gba_wr_rd_write_command(wr=True, rd=False, postfunc=echo_all),
+ make_cart_30bit_read_command(postfunc=echo_all),
+ make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all)])
+def make_rom_read_cycle_command_30bit(times=1, postfunc=command2bytes) -> bytes:
+ # 1. pull down RD
+ # 2. read data
+ # 3. pull up RD
+ return postfunc("0".join([__readcyclecmd_30bit for i in range(times)]))
+
+def extract_read_cycle_data_30bit(data: bytes, times=1):
+ ret = []
+ if len(data)*8 < (len(__readcyclecmd_30bit)+1) * times:
+ raise ValueError("data must be %d bytes, but got %d" % ((len(__readcyclecmd_30bit)+1) * times, len(data)*8))
+ bytesstr = bytes2command(data)
+ # 每隔len(__readcyclecmd_30bit)+1bit取一次数据
+ for i in range(0, len(bytesstr), len(__readcyclecmd_30bit) + 1):
+ one = command2bytes( bytesstr[i + 8: i + len(__readcyclecmd_30bit) + 1], endclk=False)
+ ret.append(extract_cart_30bit_read_data(one[:len(one)-1]))
+ if len(ret) >= times:
+ break
+ return ret
+
+def make_gba_rom_data_read_command(flip=False, postfunc=command2bytes) -> bytes:
+ command = "0101" + ("1" if flip else "0") + "0" * 16
+ return postfunc(command)
+
+def extract_gba_rom_read_data(data: bytes) -> int:
+ if len(data) < 3:
+ raise ValueError("data must be 3 bytes, but got %d" % len(data))
+ # 6bit无效数据
+ #ret = (reverse_bits(data[0] << 6 | data[1] >> 2) << 8) | reverse_bits(data[1] << 6 | data[2] >> 2)
+ ret = reverse_bits_16bit(((data[1] << 6 | data[2] >> 2) << 8) | (data[0] << 6 | data[1] >> 2))
+ return ret
+
+__readcyclecmd = "0".join([
+ make_gba_wr_rd_write_command(wr=True, rd=False, postfunc=echo_all),
+ make_gba_rom_data_read_command(flip=True, postfunc=echo_all),
+ #make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all),
+ ])
+def make_rom_read_cycle_command(times=1, postfunc=command2bytes) -> bytes:
+ # 1. pull down RD
+ # 2. read data
+ # 3. pull up RD
+ return postfunc("0".join([__readcyclecmd for i in range(times)]))
+
+def extract_read_cycle_data(data: bytes, times=1):
+ ret = []
+ if len(data)*8 < (len(__readcyclecmd)+1) * times:
+ raise ValueError("data must be %d bits, but got %d" % ((len(__readcyclecmd)+1) * times, len(data)*8))
+ bytesstr = bytes2command(data)
+ # 每隔len(__readcyclecmd)+1bit取一次数据
+ for i in range(0, len(bytesstr), len(__readcyclecmd) + 1):
+ one = command2bytes(bytesstr[i + 8: i + len(__readcyclecmd) + 1], endclk=False)
+ ret.append(extract_gba_rom_read_data(one[:len(one)]))
+ if len(ret) >= times:
+ break
+ return ret
+
+def make_rom_write_cycle_command_with_addr(addrdatalist: list, flip=True, postfunc=command2bytes) -> bytes:
+ readram = "0".join(["0".join([
+ # 1. write addr, reset cs1 and wr
+ make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([(addr >> 16) & 0xFF]), postfunc=echo_all),
+ # 2. pull down cs1
+ make_gba_rom_cs_write(cs=False, postfunc=echo_all),
+ # 3. write data
+ make_gba_rom_data_write_command(data, flip=flip, postfunc=echo_all),
+ # 4. pull down wr
+ # make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all),
+ ]) + ("0"+make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all) if not flip else "") for addr, data in addrdatalist])
+ return postfunc(readram)
+
+def make_rom_write_cycle_command_sequential(datalist: list, flip=True, postfunc=command2bytes) -> bytes:
+ readram = "0".join(["0".join([
+ # 1. reset wr
+ make_gba_wr_rd_write_command(wr=True, rd=True, postfunc=echo_all),
+ # 2. write data
+ make_gba_rom_data_write_command(data, flip=flip, postfunc=echo_all),
+ # 3. pull down wr
+ # make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all),
+ ]) + ("0"+make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all) if not flip else "") for data in datalist])
+ return postfunc(readram)
+
+def make_gba_rom_cs_write(cs: bool = True, postfunc=command2bytes) -> bytes:
+ command = "00111"
+ command += "1" if cs else "0"
+ return postfunc(command)
+
+def make_gba_rom_addr_read_command(postfunc=command2bytes) -> bytes:
+ command = "01000" + "0" * 8
+ return postfunc(command)
+
+def extract_gba_rom_addr_read_data(data: bytes) -> int:
+ if len(data) != 2:
+ raise ValueError("data must be 2 byte, but got %d" % len(data))
+ return reverse_bits((data[0] << 6 ) | (data[1] >> 2))
+
+def make_ram_write_cycle_with_addr(addrdatalist: list, postfunc=command2bytes) -> bytes:
+ writeram = "0".join(["0".join(
+ [make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=False,
+ v16bit=bytes([addr & 0xFF, (addr >> 8) & 0xFF]), v8bit=bytes([data]), postfunc=echo_all),
+ make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all)])
+ for addr, data in addrdatalist])
+ return postfunc(writeram)
+
+def make_ram_write_cycle_command(addr, data, postfunc=command2bytes) -> bytes:
+ cmd = "0".join(["0".join(
+ [make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=False,
+ v16bit=bytes([(addr+i) & 0xFF, ((addr+i) >> 8) & 0xFF]), v8bit=bytes([data[i]]), postfunc=echo_all),
+ make_gba_wr_rd_write_command(wr=False, rd=True, postfunc=echo_all)])
+ for i in range(len(data))])
+ return postfunc(cmd)
+
+def make_ram_read_cycle_command(addr=0, times=1, postfunc=command2bytes) -> bytes:
+ cmd = "0".join(["0".join([
+ make_v16bit_data_write_command(addr+i, postfunc=echo_all),
+ make_gba_rom_addr_read_command(postfunc=echo_all)
+ ]) for i in range(times)])
+ return postfunc(cmd)
+
+__len_of_v16bit_write = len(make_v16bit_data_write_command(data=0, postfunc=echo_all))
+__len_of_v8bit_write = len(make_gba_rom_addr_read_command(postfunc=echo_all))
+def extract_ram_read_cycle_data(data: bytes, times=1):
+ command = bytes2command(data)
+ ret = []
+ for i in range(0, len(command), __len_of_v16bit_write + __len_of_v8bit_write + 2):
+ one = command[i + __len_of_v16bit_write + 1: i + __len_of_v16bit_write + 1 + __len_of_v8bit_write + 1]
+ ret.append(extract_gba_rom_addr_read_data(command2bytes(one, endclk=False)))
+ if len(ret) >= times:
+ break
+ return ret
+
+def _reverse_bits(byte):
+ byte = ((byte & 0xF0) >> 4) | ((byte & 0x0F) << 4)
+ byte = ((byte & 0xCC) >> 2) | ((byte & 0x33) << 2)
+ byte = ((byte & 0xAA) >> 1) | ((byte & 0x55) << 1)
+ return byte
+
+lookup_rev = [_reverse_bits(i) for i in range(256)]
+
+def reverse_bits(byte):
+ return lookup_rev[byte & 0xFF]
+
+def _reverse_bits_16bit(data):
+ return (reverse_bits(data & 0xFF) << 8) | reverse_bits(data >> 8)
+
+lookup_rev_16bit = [_reverse_bits_16bit(i) for i in range(0x10000)]
+
+def reverse_bits_16bit(data):
+ return lookup_rev_16bit[data & 0xFFFF]
+
+
+
\ No newline at end of file
diff --git a/FlashGBX/bacon/include/CH347DLL.H b/FlashGBX/bacon/include/CH347DLL.H
new file mode 100644
index 0000000..692eb2b
--- /dev/null
+++ b/FlashGBX/bacon/include/CH347DLL.H
@@ -0,0 +1,613 @@
+/*****************************************************************************
+** Copyright (C) WCH 2001-2023 **
+** Web: http://wch.cn **
+******************************************************************************/
+// USB߽ӿоƬCH341/7Ӧòӿڿ,CH347480MbpsUSBչUART/SPI/I2C/JTAG/SWD
+// CH347-DLL V1.2
+// л: Windows 98/ME, Windows 2000/XP, WIN7/8/10/11,and later.
+// support USB chip: CH341, CH341A,CH347
+// USB => Parallel, I2C, SPI, JTAG, SWD, UART ...
+//Notes:
+//Copyright (C) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
+
+
+//
+#ifndef _CH347_DLL_H
+#define _CH347_DLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN64
+#define mOFFSET( s, m ) ( (ULONG_PTR) & ( ( ( s * ) 0 ) -> m ) ) // ȡṹԱƫƵַĺ
+#else
+#define mOFFSET( s, m ) ( (ULONG) & ( ( ( s * ) 0 ) -> m ) ) // ȡṹԱƫƵַĺ
+#endif
+
+#ifndef max
+#define max( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) // ϴֵ
+#endif
+
+#ifndef min
+#define min( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) // Сֵ
+#endif
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool // ɾTAGڴ
+#endif
+
+#ifndef NTSTATUS
+typedef LONG NTSTATUS; // ״̬
+#endif
+
+//CH31DLLCH341WDM
+#ifndef _CH341_DLL_H
+typedef struct _USB_SETUP_PKT { // USBƴĽεṹ
+ UCHAR mUspReqType; // 00H
+ UCHAR mUspRequest; // 01H
+ union {
+ struct {
+ UCHAR mUspValueLow; // 02H ֵֽ
+ UCHAR mUspValueHigh; // 03H ֵֽ
+ };
+ USHORT mUspValue; // 02H-03H ֵ
+ };
+ union {
+ struct {
+ UCHAR mUspIndexLow; // 04H ֽ
+ UCHAR mUspIndexHigh; // 05H ֽ
+ };
+ USHORT mUspIndex; // 04H-05H
+ };
+ USHORT mLength; // 06H-07H ݽεݳ
+} mUSB_SETUP_PKT, *mPUSB_SETUP_PKT;
+
+
+typedef struct _WIN32_COMMAND { // WIN32ӿڽṹ
+ union {
+ ULONG mFunction; // ʱָܴ߹ܵ
+ NTSTATUS mStatus; // ʱز״̬
+ };
+ ULONG mLength; // ȡ,غݵij
+ union {
+ mUSB_SETUP_PKT mSetupPkt; // USBƴĽε
+ UCHAR mBuffer[ 512]; // ݻ,Ϊ0255B
+ };
+} mWIN32_COMMAND, *mPWIN32_COMMAND;
+// WIN32Ӧòӿ
+#define IOCTL_CH341_COMMAND ( FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED ) // רýӿ
+
+#define mWIN32_COMMAND_HEAD mOFFSET( mWIN32_COMMAND, mBuffer ) // WIN32ӿڵͷ
+
+#define mCH341_MAX_NUMBER 32 // ͬʱӵCH341/7豸
+
+#define mMAX_BUFFER_LENGTH 0x1000 // ݻ4096
+
+#define mMAX_COMMAND_LENGTH ( mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH ) // ݳȼṹͷij
+
+#define mDEFAULT_BUFFER_LEN 0x0400 // ݻĬϳ1024
+
+#define mDEFAULT_COMMAND_LEN ( mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN ) // Ĭݳȼṹͷij
+
+// CH341˵ַ
+#define mCH347_ENDP_DATA_UP 0x86 // CH347ݿϴ˵ĵַ
+#define mCH347_ENDP_DATA_DOWN 0x06 // CH347ݿ´˵ĵַ
+
+// 豸ӿṩĹܵ
+#define mPipeDeviceCtrl 0x00000004 // CH347ۺϿƹܵ
+#define mPipeDataUp 0x00000006 // CH347ݿϴܵ
+#define mPipeDataDown 0x00000007 // CH347ݿ´ܵ
+
+// ӦòӿڵĹܴ
+#define mFuncNoOperation 0x00000000 //
+#define mFuncGetVersion 0x00000001 // ȡ汾
+#define mFuncGetConfig 0x00000002 // ȡUSB豸
+#define mFuncSetTimeout 0x00000009 // USBͨѶʱ
+#define mFuncSetExclusive 0x0000000b // öռʹ
+#define mFuncResetDevice 0x0000000c // λUSB豸
+#define mFuncResetPipe 0x0000000d // λUSBܵ
+#define mFuncAbortPipe 0x0000000e // ȡUSBܵ
+#define mFuncBufferMode 0x00000020 // 趨ϴģʽѯеݳ
+#define mFuncBufferModeDn 0x00000021 // 趨´ģʽѯеݳ
+#define mFuncGetVersionEx 0x00000022 // ȡ汾żоƬͺ
+// USB豸
+#define mUSB_CLR_FEATURE 0x01
+#define mUSB_SET_FEATURE 0x03
+#define mUSB_GET_STATUS 0x00
+#define mUSB_SET_ADDRESS 0x05
+#define mUSB_GET_DESCR 0x06
+#define mUSB_SET_DESCR 0x07
+#define mUSB_GET_CONFIG 0x08
+#define mUSB_SET_CONFIG 0x09
+#define mUSB_GET_INTERF 0x0a
+#define mUSB_SET_INTERF 0x0b
+#define mUSB_SYNC_FRAME 0x0c
+
+// CH341ƴijר
+#define mCH341_VENDOR_READ 0xC0 // ͨƴʵֵCH341רö
+#define mCH341_VENDOR_WRITE 0x40 // ͨƴʵֵCH341רд
+
+#define mCH341A_CMD_I2C_STREAM 0xAA // I2Cӿڵ,ӴֽڿʼΪI2C
+#define mCH341A_CMD_UIO_STREAM 0xAB // UIOӿڵ,ӴֽڿʼΪ
+#define mCH341A_CMD_PIO_STREAM 0xAE // PIOӿڵ,ӴֽڿʼΪ
+// CH341Aƴijר
+#define mCH341A_BUF_CLEAR 0xB2 // δɵ
+#define mCH341A_I2C_CMD_X 0x54 // I2Cӿڵ,ִ
+#define mCH341A_DELAY_MS 0x5E // Ϊλʱָʱ
+#define mCH341A_GET_VER 0x5F // ȡоƬ汾
+
+#define mCH341A_CMD_I2C_STM_STA 0x74 // I2Cӿڵ:ʼλ
+#define mCH341A_CMD_I2C_STM_STO 0x75 // I2Cӿڵ:ֹͣλ
+#define mCH341A_CMD_I2C_STM_OUT 0x80 // I2Cӿڵ:,λ5-λ0Ϊ,ֽΪ,0ֻһֽڲӦ
+#define mCH341A_CMD_I2C_STM_IN 0xC0 // I2Cӿڵ:,λ5-λ0Ϊ,0ֻһֽڲӦ
+#define mCH341A_CMD_I2C_STM_MAX ( min( 0x3F, mCH341_PACKET_LENGTH ) ) // I2Cӿڵݵ
+#define mCH341A_CMD_I2C_STM_SET 0x60 // I2Cӿڵ:ò,λ2=SPII/O(0=뵥,1=˫˫),λ1λ0=I2Cٶ(00=,01=,10=,11=)
+#define mCH341A_CMD_I2C_STM_US 0x40 // I2Cӿڵ:Ϊλʱ,λ3-λ0Ϊʱֵ
+#define mCH341A_CMD_I2C_STM_MS 0x50 // I2Cӿڵ:Ϊλʱ,λ3-λ0Ϊʱֵ
+#define mCH341A_CMD_I2C_STM_DLY 0x0F // I2Cӿڵʱֵ
+#define mCH341A_CMD_I2C_STM_END 0x00 // I2Cӿڵ:ǰ
+
+#define mCH341A_CMD_UIO_STM_IN 0x00 // UIOӿڵ:D7-D0
+#define mCH341A_CMD_UIO_STM_DIR 0x40 // UIOӿڵ:趨I/OD5-D0,λ5-λ0Ϊ
+#define mCH341A_CMD_UIO_STM_OUT 0x80 // UIOӿڵ:D5-D0,λ5-λ0Ϊ
+#define mCH341A_CMD_UIO_STM_US 0xC0 // UIOӿڵ:Ϊλʱ,λ5-λ0Ϊʱֵ
+#define mCH341A_CMD_UIO_STM_END 0x20 // UIOӿڵ:ǰ
+
+#define MAX_DEVICE_PATH_SIZE 128 // 豸Ƶַ
+#define MAX_DEVICE_ID_SIZE 64 // 豸IDַ
+#endif _CH341_DLL_H
+
+//ӿ
+#define CH347_USB_VENDOR 0
+#define CH347_USB_HID 2
+#define CH347_USB_VCP 3
+
+//CH347_USB_VENDOR֧CH341/7
+#define CHIP_TYPE_CH341 0
+#define CHIP_TYPE_CH347 1
+#define CHIP_TYPE_CH347F 2
+#define CHIP_TYPE_CH347T CHIP_TYPE_CH347
+
+//оƬܽӿ
+#define CH347_FUNC_UART 0
+#define CH347_FUNC_SPI_IIC 1
+#define CH347_FUNC_JTAG_IIC 2
+#define CH347_FUNC_JTAG_IIC_SPI 3 //CH347F
+
+#define DEFAULT_READ_TIMEOUT 500 //Ĭ϶ʱ
+#define DEFAULT_WRITE_TIMEOUT 500 //Ĭдʱ
+
+#define mCH347_PACKET_LENGTH 512 // CH347ֵ֧ݰij
+
+#pragma pack(1)
+
+//SPI
+typedef struct _SPI_CONFIG{
+ UCHAR iMode; // 0-3:SPI Mode0/1/2/3
+ UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz7=468.75KHz
+ UCHAR iByteOrder; // 0=λǰ(LSB), 1=λǰ(MSB)
+ USHORT iSpiWriteReadInterval; // SPIӿڳȡдλΪuS
+ UCHAR iSpiOutDefaultData; // SPIʱĬ
+ ULONG iChipSelect; // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ч: λ1λ0Ϊ00/01ֱѡCS1/CS2Ϊ͵ƽЧƬѡ
+ UCHAR CS1Polarity; // λ0ƬѡCS1Կƣ0͵ƽЧ1ߵƽЧ
+ UCHAR CS2Polarity; // λ0ƬѡCS2Կƣ0͵ƽЧ1ߵƽЧ
+ USHORT iIsAutoDeativeCS; // ɺǷԶƬѡ
+ USHORT iActiveDelay; // Ƭѡִждʱʱ,λus
+ ULONG iDelayDeactive; // Ƭѡִждʱʱ,λus
+}mSpiCfgS,*mPSpiCfgS;
+
+//豸Ϣ
+typedef struct _DEV_INFOR{
+ UCHAR iIndex; // ǰ
+ UCHAR DevicePath[MAX_PATH]; // 豸,CreateFile
+ UCHAR UsbClass; // 0:CH347_USB_CH341, 2:CH347_USB_HID,3:CH347_USB_VCP
+ UCHAR FuncType; // 0:CH347_FUNC_UART,1:CH347_FUNC_SPI_I2C,2:CH347_FUNC_JTAG_I2C
+ CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx
+ UCHAR ChipMode; // оƬģʽ,0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+IIC) 4:CH347F(Uart*2+Jtag/SPI/IIC)
+ HANDLE DevHandle; // 豸
+ USHORT BulkOutEndpMaxSize; // ϴ˵С
+ USHORT BulkInEndpMaxSize; // ´˵С
+ UCHAR UsbSpeedType; // USBٶͣ0:FS,1:HS,2:SS
+ UCHAR CH347IfNum; // USBӿں: CH347T: IF0:UART; IF1:SPI/IIC/JTAG/GPIO
+ // CH347F: IF0:UART0; IF1:UART1; IF 2:SPI/IIC/JTAG/GPIO
+ UCHAR DataUpEndp; // ϴ˵ַ
+ UCHAR DataDnEndp; // ´˵ַ
+ CHAR ProductString[64]; // USBƷַ
+ CHAR ManufacturerString[64]; // USBַ
+ ULONG WriteTimeout; // USBдʱ
+ ULONG ReadTimeout; // USBʱ
+ CHAR FuncDescStr[64]; // ӿڹ
+ UCHAR FirewareVer; // ̼汾,ʮֵ
+}mDeviceInforS,*mPDeviceInforS;
+
+#pragma pack()
+
+// CH347ģʽú,֧CH347ģʽµĴرաUSBUSBдHID
+//USB豸
+HANDLE WINAPI CH347OpenDevice(ULONG DevI);
+
+//رUSB豸
+BOOL WINAPI CH347CloseDevice(ULONG iIndex);
+
+//ȡ豸Ϣ
+BOOL WINAPI CH347GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation);
+
+// ȡCH347оƬ:CHIP_TYPE_CH347T/CHIP_TYPE_CH347F
+UCHAR WINAPI CH347GetChipType(ULONG iIndex ); // ָ豸
+
+// ȡ汾汾豸汾оƬ(CH341(FS)/CH347HS)
+BOOL WINAPI CH347GetVersion(ULONG iIndex,
+ PUCHAR iDriverVer,
+ PUCHAR iDLLVer,
+ PUCHAR ibcdDevice,
+ PUCHAR iChipType); //CHIP_TYPE_CH341/7
+
+typedef VOID ( CALLBACK * mPCH347_NOTIFY_ROUTINE ) ( // 豸֪ͨ¼ص
+ ULONG iEventStatus ); // 豸¼͵ǰ״̬(ж): 0=豸γ¼, 3=豸¼
+
+#define CH347_DEVICE_ARRIVAL 3 // 豸¼,Ѿ
+#define CH347_DEVICE_REMOVE_PEND 1 // 豸Ҫγ
+#define CH347_DEVICE_REMOVE 0 // 豸γ¼,Ѿγ
+
+// 趨豸¼֪ͨ
+BOOL WINAPI CH347SetDeviceNotify(ULONG iIndex, // ָ豸,0Ӧһ豸
+ PCHAR iDeviceID, // ѡ,ַָ,ָص豸ID,ַ\0ֹ
+ mPCH347_NOTIFY_ROUTINE iNotifyRoutine ); // ָ豸¼ص,ΪNULLȡ¼֪ͨ,ڼ¼ʱøó
+
+// ȡUSBݿ
+BOOL WINAPI CH347ReadData( ULONG iIndex, // ָ豸
+ PVOID oBuffer, // ָһ㹻Ļ,ڱȡ
+ PULONG ioLength ); // ָȵԪ,ʱΪȡij,غΪʵʶȡij
+
+// дȡUSBݿ
+BOOL WINAPI CH347WriteData(ULONG iIndex, // ָ豸
+ PVOID iBuffer, // ָһ,д
+ PULONG ioLength ); // ָȵԪ,ʱΪдij,غΪʵдij
+
+// USBݶдijʱ
+BOOL WINAPI CH347SetTimeout(ULONG iIndex, // ָ豸
+ ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+ ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+
+/***************SPI********************/
+// SPIʼ
+BOOL WINAPI CH347SPI_Init(ULONG iIndex,mSpiCfgS *SpiCfg);
+
+//CH347FSPIʱƵʡiSpiSpeedHz= 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz 7=468.75KHz
+BOOL WINAPI CH347SPI_SetFrequency(ULONG iIndex, ULONG iSpiSpeedHz);
+
+//CH347FSPI֧λ.
+BOOL WINAPI CH347SPI_SetDataBits(ULONG iIndex,
+ UCHAR iDataBits); //iDataBits= 0:8bit, 1:16bit
+
+//ȡSPIϢ
+BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex,mSpiCfgS *SpiCfg);
+
+//Ƭѡ״̬,ʹǰȵCH347SPI_InitCS
+BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // ָ豸
+ UCHAR iStatus); // 0=Ƭѡ,1=Ƭѡ
+
+//SPIƬѡ
+BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // ָ豸
+ USHORT iEnableSelect, // ͰλΪCS1߰λΪCS2; ֵֽΪ1=CS,Ϊ0=ԴCS
+ USHORT iChipSelect, // ͰλΪCS1߰λΪCS2;Ƭѡ,0=Ƭѡ,1=Ƭѡ
+ ULONG iIsAutoDeativeCS, // 16λΪCS116λΪCS2;ɺǷԶƬѡ
+ ULONG iActiveDelay, // 16λΪCS116λΪCS2;Ƭѡִждʱʱ,λus
+ ULONG iDelayDeactive); // 16λΪCS116λΪCS2;Ƭѡִждʱʱ,λus
+
+//SPI4д
+BOOL WINAPI CH347SPI_Write(ULONG iIndex, // ָ豸
+ ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ
+ ULONG iLength, // ֽ
+ ULONG iWriteStep, // ȡĵij
+ PVOID ioBuffer); // ָһ,MOSIд
+
+//SPI4.дݣЧʽCH347SPI_WriteReadߺܶ
+BOOL WINAPI CH347SPI_Read(ULONG iIndex, // ָ豸
+ ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ
+ ULONG oLength, // ֽ
+ PULONG iLength, // ֽ
+ PVOID ioBuffer); // ָһ,DOUTд,غǴDIN
+
+// SPI,4߽ӿ
+BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // ָ豸
+ ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ƭѡ
+ ULONG iLength, // ֽ
+ PVOID ioBuffer ); // ָһ,DOUTд,غǴDIN
+
+// SPI,4߽ӿ
+BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // ָ豸
+ ULONG iChipSelect, // Ƭѡ, λ7Ϊ0Ƭѡ, λ7Ϊ1Ч
+ ULONG iLength, // ֽ
+ PVOID ioBuffer ); // ָһ,DOUTд,غǴDIN
+
+/***************JTAG********************/
+//JTAGӿڳʼģʽٶ
+BOOL WINAPI CH347Jtag_INIT(ULONG iIndex,
+ UCHAR iClockRate); //ͨٶȣЧֵΪ0-5ֵԽͨٶԽ
+
+//ȡJtagٶ
+BOOL WINAPI CH347Jtag_GetCfg(ULONG iIndex, // ָ豸
+ UCHAR *ClockRate); //ͨٶȣЧֵΪ0-5ֵԽͨٶԽ
+
+
+//ıTMSֵ״̬л
+BOOL WINAPI CH347Jtag_TmsChange(ULONG iIndex, // 豸
+ PUCHAR tmsValue, // лTMSλֵ,ֽΪλ
+ ULONG Step, // tmsValueڴ洢TMSЧλ
+ ULONG Skip); // Чʼλ
+
+//shift-dr/ir״̬ждִExit DR/IR
+//״̬:Shift-DR/IR.RW.->Exit DR/IR
+BOOL WINAPI CH347Jtag_IoScan(ULONG iIndex,
+ PUCHAR DataBits, //Ҫдλ
+ ULONG DataBitsNb, //Ҫݵλ
+ BOOL IsRead); //ǷҪȡ
+
+//shift-dr/ir״̬жд,ִɺ,һExit DR/IR;,ͣShift-DR/IR
+//״̬:Shift-DR/IR.RW..->[Exit DR/IR]
+BOOL WINAPI CH347Jtag_IoScanT(ULONG iIndex, // 豸
+ PUCHAR DataBits, // Ҫдλ
+ ULONG DataBitsNb, // Ҫݵλ
+ BOOL IsRead, // ǷҪȡ
+ BOOL IsLastPkt); // ǷΪһ
+
+//CH347 λTap״̬.TCKTMSΪ߽ɽ״̬ΪTest-Logic Reset״̬
+ULONG WINAPI CH347Jtag_Reset(ULONG iIndex);
+
+//CH347 TRSTӲλ
+BOOL WINAPI CH347Jtag_ResetTrst(ULONG iIndex, BOOL TRSTLevel);
+
+//λʽJTAG IR/DRݶд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹCH347Jtag_WriteRead_Fast
+//4096ֽΪλд
+//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead(ULONG iIndex, // ָ豸
+ BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд
+ ULONG iWriteBitLength, // д,дij
+ PVOID iWriteBitBuffer, // ָһ,д
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡij
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//JTAG IR/DRд,ڶֽдJTAG̼زӲ4KдȲ4096ֽڡСе
+//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead_Fast(ULONG iIndex, // ָ豸
+ BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд
+ ULONG iWriteBitLength, // д,дij
+ PVOID iWriteBitBuffer, // ָһ,д
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡij
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//λʽJTAG IR/DRݶд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹCH347Jtag_WriteRead_Fast
+//4096ֽΪλд
+//״̬:Run-Test-> Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteReadEx(ULONG iIndex, // ָ豸
+ BOOL IsInDrOrIr, // =TRUE: SHIFT-DR/IR״̬ݽ ==FALSE: Run-Test->Shift-IR/DR.ݽ.->Exit IR/DR -> Run-Test
+ BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд
+ ULONG iWriteBitLength, // д,дij
+ PVOID iWriteBitBuffer, // ָһ,д
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡij
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//JTAG IR/DRд,ڶֽдJTAG̼زӲ4KдȲ4096ֽڡСе
+//״̬:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead_FastEx(ULONG iIndex, // ָ豸
+ BOOL IsInDrOrIr, // =TRUE: SHIFT-DR/IR״̬ݽ ==FALSE: Run-Test->Shift-IR/DR.ݽ.->Exit IR/DR -> Run-Test
+ BOOL IsDR, // =TRUE: DRݶд,=FALSE:IRݶд
+ ULONG iWriteBitLength, // д,дij
+ PVOID iWriteBitBuffer, // ָһ,д
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡij
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//лJTAG״̬
+BOOL WINAPI CH347Jtag_SwitchTapState(UCHAR TapState);
+
+//JTAG DRд,ֽΪλ,ڶֽдJTAG̼ز
+//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteWriteDR(ULONG iIndex, // ָ豸
+ ULONG iWriteLength, // д,дֽڳ
+ PVOID iWriteBuffer); // ָһ,д
+
+//JTAG DR,ֽΪλ,ֽ
+//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteReadDR(ULONG iIndex, // ָ豸
+ PULONG oReadLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID oReadBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//JTAG IRд,ֽΪλ,ֽд
+//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteWriteIR(ULONG iIndex, // ָ豸
+ ULONG iWriteLength, // д,дֽڳ
+ PVOID iWriteBuffer); // ָһ,д
+
+//JTAG IR,ֽΪλ,ֽд
+//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteReadIR(ULONG iIndex, // ָ豸
+ PULONG oReadLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID oReadBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//λʽJTAG DRд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹUSB20Jtag_ByeWriteDR
+//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_BitWriteDR(ULONG iIndex, // ָ豸
+ ULONG iWriteBitLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID iWriteBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//λʽJTAG IRд.ݵĶдָ״̬лȿഫ䡣ݴ䣬ʹUSB20Jtag_ByteWriteIR
+//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_BitWriteIR(ULONG iIndex, // ָ豸
+ ULONG iWriteBitLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID iWriteBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//λʽJTAG IRݶ.ݵĶдָ״̬лȡݴ䣬ʹUSB20Jtag_ByteReadIR
+//״̬:Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_BitReadIR(ULONG iIndex, // ָ豸
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+//λʽJTAG DRݶ.ݵĶдݴ䣬ʹUSB20Jtag_ByteReadDR
+//״̬:Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_BitReadDR(ULONG iIndex, // ָ豸
+ PULONG oReadBitLength, // ָȵԪ,غΪʵʶȡֽڳ
+ PVOID oReadBitBuffer ); // ָһ㹻Ļ,ڱȡ
+
+/***************GPIO********************/
+//ȡCH347GPIOŵƽֵ
+BOOL WINAPI CH347GPIO_Get(ULONG iIndex, // ָ豸
+ UCHAR *iDir, // ŷ:GPIO0-7Ӧλ0-7.0룻1
+ UCHAR *iData); // GPIO0ƽ:GPIO0-7Ӧλ0-7,0͵ƽ1ߵƽ)
+
+//CH347GPIOŵƽֵ
+BOOL WINAPI CH347GPIO_Set(ULONG iIndex, // ָ豸
+ UCHAR iEnable, // Ч־:Ӧλ0-7,ӦGPIO0-7.
+ UCHAR iSetDirOut, // I/O,ijλ0ӦΪ,ijλ1ӦΪ.GPIO0-7Ӧλ0-7.
+ UCHAR iSetDataOut); // ,I/OΪ,ôijλ0ʱӦ͵ƽ,ijλ1ʱӦߵƽ
+
+
+typedef VOID ( CALLBACK * mPCH347_INT_ROUTINE ) ( // жϷ
+ PUCHAR iStatus ); // ж״̬,ολ˵
+// 8ֽGPIO0-7״̬.ÿֽλ:
+// λ7ǰGPIO00룻1
+// λ6ǰGPIO0ƽ0͵ƽ1ߵƽ
+// λ5ǰGPIO0ǷΪжϣ0ѯģʽ1жģʽ
+// λ4-3GPIO0жģʽ00½ش01ش;10˫ش11: ;
+// λ2-0
+
+// 趨GPIOжϷ
+BOOL WINAPI CH347SetIntRoutine(ULONG iIndex, // ָ豸
+ UCHAR Int0PinN, // ж0 GPIOź,7:ôжԴ; Ϊ0-7Ӧgpio0-7
+ UCHAR Int0TripMode, // ж0: 00:½ش; 01:ش; 02:˫ش; 03:;
+ UCHAR Int1PinN, // ж1 GPIOź,7ôжԴ,Ϊ0-7Ӧgpio0-7
+ UCHAR Int1TripMode, // ж1: 00:½ش; 01:ش; 02:˫ش; 03:;
+ mPCH347_INT_ROUTINE iIntRoutine );// ָжϷ,ΪNULLȡжϷ,жʱøó
+
+// ȡж
+BOOL WINAPI CH347ReadInter(ULONG iIndex, // ָ豸
+ PUCHAR iStatus ); // ָ8ֽڵԪ,ֱΪGPIO0-7״̬,ÿֽλ˵οжϷiStatusλ˵
+
+// жݶ
+BOOL WINAPI CH347AbortInter(ULONG iIndex ); // ָ豸
+
+//IAP̼ģʽ
+BOOL WINAPI CH347StartIapFwUpate(ULONG iIndex,
+ ULONG FwSize); // ̼
+
+/**************HID/VCP**********************/
+//
+HANDLE WINAPI CH347Uart_Open(ULONG iIndex);
+
+//رմ
+BOOL WINAPI CH347Uart_Close(ULONG iIndex);
+
+BOOL WINAPI CH347Uart_SetDeviceNotify( // 趨豸¼֪ͨ
+ ULONG iIndex, // ָ豸,0Ӧһ豸
+ PCHAR iDeviceID, // ѡ,ַָ,ָص豸ID,ַ\0ֹ
+ mPCH347_NOTIFY_ROUTINE iNotifyRoutine ); // ָ豸¼ص,ΪNULLȡ¼֪ͨ,ڼ¼ʱøó
+
+//ȡUARTӲ
+BOOL WINAPI CH347Uart_GetCfg(ULONG iIndex, // ָ豸
+ PULONG BaudRate, //
+ PUCHAR ByteSize, // λ(5,6,7,8,16)
+ PUCHAR Parity, // Уλ(0None; 1Odd; 2Even; 3Mark; 4Space)
+ PUCHAR StopBits, // ֹͣλ(01ֹͣλ; 11.5ֹͣλ; 22ֹͣλ)
+ PUCHAR ByteTimeout); // ֽڳʱ
+
+//UART
+BOOL WINAPI CH347Uart_Init(ULONG iIndex, // ָ豸
+ DWORD BaudRate, //
+ UCHAR ByteSize, // λ(5,6,7,8,16)
+ UCHAR Parity, // Уλ(0None; 1Odd; 2Even; 3Mark; 4Space)
+ UCHAR StopBits, // ֹͣλ(01ֹͣλ; 11.5ֹͣλ; 22ֹͣλ)
+ UCHAR ByteTimeout);// ֽڳʱʱ,λ100uS
+
+// USBݶдijʱ
+BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // ָ豸
+ ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+ ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+
+// ȡݿ
+BOOL WINAPI CH347Uart_Read(ULONG iIndex, // ָ豸
+ PVOID oBuffer, // ָһ㹻Ļ,ڱȡ
+ PULONG ioLength );// ָȵԪ,ʱΪȡij,غΪʵʶȡij
+// дݿ
+BOOL WINAPI CH347Uart_Write(ULONG iIndex, // ָ豸
+ PVOID iBuffer, // ָһ,д
+ PULONG ioLength );// ָȵԪ,ʱΪдij,غΪʵдij
+
+//ѯжֽδȡ
+BOOL WINAPI CH347Uart_QueryBufUpload(ULONG iIndex, // ָ豸
+ LONGLONG *RemainBytes); // δȡֽ
+
+//ȡ豸Ϣ
+BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation);
+
+// USBݶдijʱ
+BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // ָ豸
+ ULONG iWriteTimeout, // ָUSBдݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+ ULONG iReadTimeout ); // ָUSBȡݿijʱʱ,ԺmSΪλ,0xFFFFFFFFָʱ(Ĭֵ)
+
+/********IIC***********/
+// I2C
+BOOL WINAPI CH347I2C_Set(ULONG iIndex, // ָ豸
+ ULONG iMode ); // ָģʽ,
+// λ1-λ0: I2Cӿٶ/SCLƵ, 00=/20KHz,01=/100KHz(Ĭֵ),10=/400KHz,11=/750KHz
+// ,Ϊ0
+
+//Set I2C Clock Stretch
+BOOL WINAPI CH347I2C_SetStretch(ULONG iIndex, // ָ豸
+ BOOL iEnable); // I2C Clock Stretch enable, 1:enable,0:disable
+
+// Ӳ첽ʱ,úܿ췵,һ֮ǰʱָ
+BOOL WINAPI CH347I2C_SetDelaymS(ULONG iIndex, // ָ豸
+ ULONG iDelay ) ; // ָʱĺ
+
+ // I2C,2߽ӿ,ʱΪSCL,ΪSDA
+BOOL WINAPI CH347StreamI2C( ULONG iIndex, // ָ豸
+ ULONG iWriteLength, // дֽ
+ PVOID iWriteBuffer, // ָһ,д,ֽͨI2C豸ַдλ
+ ULONG iReadLength, // ȡֽ
+ PVOID oReadBuffer ); // ָһ,غǶ
+
+BOOL WINAPI CH347StreamI2C_RetACK( // I2C,2߽ӿ,ʱΪSCL,ΪSDA(˫I/O),ٶԼ56Kֽ,˻ȡACK
+ ULONG iIndex, // ָCH341豸
+ ULONG iWriteLength, // дֽ
+ PVOID iWriteBuffer, // ָһ,д,ֽͨI2C豸ַдλ
+ ULONG iReadLength, // ȡֽ
+ PVOID oReadBuffer, // ָһ,غǶ
+ PULONG rAckCount); // ָдصACKֵ
+
+#ifndef _CH341_DLL_H
+typedef enum _EEPROM_TYPE {// EEPROMͺ
+ 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;
+#endif
+
+// EEPROMжȡݿ,ٶԼ56Kֽ
+BOOL WINAPI CH347ReadEEPROM(ULONG iIndex, // ָ豸
+ EEPROM_TYPE iEepromID, // ָEEPROMͺ
+ ULONG iAddr, // ָݵԪĵַ
+ ULONG iLength, // ȡֽ
+ PUCHAR oBuffer ); // ָһ,غǶ
+// EEPROMдݿ
+BOOL WINAPI CH347WriteEEPROM(ULONG iIndex, // ָ豸
+ EEPROM_TYPE iEepromID, // ָEEPROMͺ
+ ULONG iAddr, // ָݵԪĵַ
+ ULONG iLength, // дֽ
+ PUCHAR iBuffer ); // ָһ,д
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CH347_DLL_H
diff --git a/FlashGBX/bacon/include/CH347DLL_EN.H b/FlashGBX/bacon/include/CH347DLL_EN.H
new file mode 100644
index 0000000..a41c333
--- /dev/null
+++ b/FlashGBX/bacon/include/CH347DLL_EN.H
@@ -0,0 +1,610 @@
+/*****************************************************************************
+** Copyright (C) WCH 2001-2023 **
+** Web: http://wch.cn **
+*****************************************************************************/
+
+//USB bus interface chip CH341/7 parallel port application layer interface library, CH347 based on 480mbps high-speed USB bus, extends UART/SPI/I2C/JTAG
+//CH347-DLL V1.2
+//Environment: Windows 98/ME, Windows 2000/XP, WIN7/8/10/11,and later.
+//support USB chip: CH341, CH341A,CH347
+//USB => Parallel, I2C, SPI, JTAG, SWD, UART ...
+//Notes:
+//Copyright (c) 2023 Nanjing Qinheng Microelectronics Co., Ltd.
+
+
+#ifndef _CH347_DLL_H
+#define _CH347_DLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN64
+#define mOFFSET(s, m) ((ULONG_PTR) & (((s *)0)->m)) // Define macro, get relative offset address of structure member
+#else
+#define mOFFSET(s, m) ((ULONG) & (((s *)0)->m)) // Define macro, get relative offset address of structure member
+#endif
+
+#ifndef max
+#define max(a, b) (((a) > (b)) ? (a) : (b)) // Determine maximum value
+#endif
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b)) // Determine minimum value
+#endif
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool // Delete memory allocation with TAG
+#endif
+
+#ifndef NTSTATUS
+typedef LONG NTSTATUS; // Return status
+#endif
+
+// Sharing CH341WDM driver with CH341DLL
+#ifndef _CH341_DLL_H
+typedef struct _USB_SETUP_PKT { // USB control transfer structure
+ UCHAR mUspReqType; // 00H request type
+ UCHAR mUspRequest; // 01H request code
+ union {
+ struct {
+ UCHAR mUspValueLow; // 02H Value parameter low byte
+ UCHAR mUspValueHigh; // 03H Value parameter high byte
+ };
+ USHORT mUspValue; // 02H-03H value parameters
+ };
+ union {
+ struct {
+ UCHAR mUspIndexLow; // 04H index parameter low byte
+ UCHAR mUspIndexHigh; // 05H index parameter high byte
+ };
+ USHORT mUspIndex; // 04H-05H index parameter
+ };
+ USHORT mLength; // 06H-07H data length
+} mUSB_SETUP_PKT, *mPUSB_SETUP_PKT;
+
+typedef struct _WIN32_COMMAND { // Define WIN32 command interface structure
+ union {
+ ULONG mFunction; // Specify function code or pipe number when input
+ NTSTATUS mStatus; // Return operation status when output
+ };
+ ULONG mLength; // Access length, return the length of subsequent data
+ union {
+ mUSB_SETUP_PKT mSetupPkt; // Data request in the setup phase of USB control transfer
+ UCHAR mBuffer[512]; // Data buffer, the length is 0 to 255B
+ };
+} mWIN32_COMMAND, *mPWIN32_COMMAND;
+// WIN32 application layer interface command
+#define IOCTL_CH341_COMMAND (FILE_DEVICE_UNKNOWN << 16 | FILE_ANY_ACCESS << 14 | 0x0f34 << 2 | METHOD_BUFFERED) // Private interface
+
+#define mWIN32_COMMAND_HEAD mOFFSET(mWIN32_COMMAND, mBuffer) // Header length of WIN32 command interface
+
+#define mCH341_MAX_NUMBER 32 // Maximum number of CH341/7 connected at the same time
+
+#define mMAX_BUFFER_LENGTH 0x1000 // Maximum length of the data buffer is 4096
+
+#define mMAX_COMMAND_LENGTH (mWIN32_COMMAND_HEAD + mMAX_BUFFER_LENGTH) // Maximum data length plus command structure header length
+
+#define mDEFAULT_BUFFER_LEN 0x0400 // Data buffer default length is 1024
+
+#define mDEFAULT_COMMAND_LEN (mWIN32_COMMAND_HEAD + mDEFAULT_BUFFER_LEN) // default data length plus command structure header length
+
+// CH341 endpoint address
+#define mCH347_ENDP_DATA_UP 0x86 // Data upload endpoint of CH347
+#define mCH347_ENDP_DATA_DOWN 0x06 // Data download endpoint of CH347
+
+// Pipeline operation command provided by equipment layer interface
+#define mPipeDeviceCtrl 0x00000004 // CH347 integrated control pipeline
+#define mPipeDataUp 0x00000006 // CH347 data block upload pipeline
+#define mPipeDataDown 0x00000007 // CH347 data block download pipeline
+
+// Function code of application layer interface
+#define mFuncNoOperation 0x00000000 // No operation
+#define mFuncGetVersion 0x00000001 // Get driver version number
+#define mFuncGetConfig 0x00000002 // Get USB device configuration descriptor
+#define mFuncSetTimeout 0x00000009 // Set USB communication timeout
+#define mFuncSetExclusive 0x0000000b // Set exclusive use
+#define mFuncResetDevice 0x0000000c // Reset USB device
+#define mFuncResetPipe 0x0000000d // Reset USB pipe
+#define mFuncAbortPipe 0x0000000e // Cancel data request of USB pipe
+#define mFuncBufferMode 0x00000020 // Set buffer upload mode and query data length in the buffer
+#define mFuncBufferModeDn 0x00000021 // Set buffer download mode and query data length in the buffer
+#define mFuncGetVersionEx 0x00000022 // Get driver version number and chip model
+// USB device standard request code
+#define mUSB_CLR_FEATURE 0x01
+#define mUSB_SET_FEATURE 0x03
+#define mUSB_GET_STATUS 0x00
+#define mUSB_SET_ADDRESS 0x05
+#define mUSB_GET_DESCR 0x06
+#define mUSB_SET_DESCR 0x07
+#define mUSB_GET_CONFIG 0x08
+#define mUSB_SET_CONFIG 0x09
+#define mUSB_GET_INTERF 0x0a
+#define mUSB_SET_INTERF 0x0b
+#define mUSB_SYNC_FRAME 0x0c
+
+// Vendor specific request type of CH341 control transfer
+#define mCH341_VENDOR_READ 0xC0 // CH341 vendor-specific read operation via control transfer
+#define mCH341_VENDOR_WRITE 0x40 // CH341 vendor-specific write operation via control transfer
+
+#define mCH341A_CMD_I2C_STREAM 0xAA // Command package of I2C, starting with the second byte is I2C command stream
+#define mCH341A_CMD_UIO_STREAM 0xAB // Command package of UIO, starting with the second byte is command stream
+#define mCH341A_CMD_PIO_STREAM 0xAE // Command package of PIO, starting with the second byte is data stream
+// Vendor specific request code of CH341A control transfer
+#define mCH341A_BUF_CLEAR 0xB2 // Clear incomplete data
+#define mCH341A_I2C_CMD_X 0x54 // Issue command of I2C interface and executes it immediately
+#define mCH341A_DELAY_MS 0x5E // Delay specified time, in millisecond
+#define mCH341A_GET_VER 0x5F // Get chip version
+
+#define mCH341A_CMD_I2C_STM_STA 0x74 // I2C interface command stream: generate start bit
+#define mCH341A_CMD_I2C_STM_STO 0x75 // I2C interface command stream: generate stop bit
+#define mCH341A_CMD_I2C_STM_OUT 0x80 // I2C interface command stream: output data, bit5 - bit0 is length, subsequent bytes are data, length 0 only sends one byte and returns an response
+#define mCH341A_CMD_I2C_STM_IN 0xC0 // I2C interface command stream: input data, bit 5-bit 0 is length, length 0 only receives one byte and sends no response
+#define mCH341A_CMD_I2C_STM_MAX (min(0x3F, mCH341_PACKET_LENGTH)) // Maximum length of a single command input/output data of I2C interface command stream
+#define mCH341A_CMD_I2C_STM_SET 0x60 // I2C interface command stream: set parameters, bit2=SPI I/O number (0= single input single output, 1= double input double output), bit1 bit0=I2C speed (00= low speed, 01= standard, 10= fast, 11= high speed)
+#define mCH341A_CMD_I2C_STM_US 0x40 // I2C interface command stream: delay in microseconds, bit3 - bit0 is delay value
+#define mCH341A_CMD_I2C_STM_MS 0x50 // I2C interface command stream: delay in milliseconds, bit3 - bit0 is delay value
+#define mCH341A_CMD_I2C_STM_DLY 0x0F // Maximum value of a single command delay of I2C interface command stream
+#define mCH341A_CMD_I2C_STM_END 0x00 // I2C interface command stream: command package ends in advance
+
+#define mCH341A_CMD_UIO_STM_IN 0x00 // UIO interface command stream: input data D7-D0
+#define mCH341A_CMD_UIO_STM_DIR 0x40 // UIO interface command stream: set I/O direction D5-D0, bit5 - bit0 is direction data
+#define mCH341A_CMD_UIO_STM_OUT 0x80 // UIO interface command stream: output data D5-D0, bit5 - bit0 is data
+#define mCH341A_CMD_UIO_STM_US 0xC0 // UIO interface command stream: delay in microseconds, bit5 - bit0 is delay value
+#define mCH341A_CMD_UIO_STM_END 0x20 // UIO interface command stream: command package ends in advance
+
+#define MAX_DEVICE_PATH_SIZE 128 // Maximum number of characters for device name
+#define MAX_DEVICE_ID_SIZE 64 // Maximum number of characters for device ID
+#endif _CH341_DLL_H
+
+// Driver interface
+#define CH347_USB_VENDOR 0
+#define CH347_USB_HID 2
+#define CH347_USB_VCP 3
+
+// CH347_USB_VENDOR support CH341/7
+#define CHIP_TYPE_CH341 0
+#define CHIP_TYPE_CH347 1
+#define CHIP_TYPE_CH347F 2
+#define CHIP_TYPE_CH347T CHIP_TYPE_CH347
+
+// Chip function interface type
+#define CH347_FUNC_UART 0
+#define CH347_FUNC_SPI_IIC 1
+#define CH347_FUNC_JTAG_IIC 2
+#define CH347_FUNC_JTAG_IIC_SPI 3 // CH347F
+
+#define DEFAULT_READ_TIMEOUT 500 // Default read timeout milliseconds
+#define DEFAULT_WRITE_TIMEOUT 500 // Default write timeout milliseconds
+
+#define mCH347_PACKET_LENGTH 512 // Length of data packet supported by CH347
+#pragma pack(1)
+// SPI controller configuration
+typedef struct _SPI_CONFIG {
+ UCHAR iMode; // 0-3:SPI Mode0/1/2/3
+ UCHAR iClock; // 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz, 7=468.75KHz
+ UCHAR iByteOrder; // 0=LSB, 1=MSB
+ USHORT iSpiWriteReadInterval; // SPI regular read/write data command, in uS
+ UCHAR iSpiOutDefaultData; // Default output data when SPI reads data
+ ULONG iChipSelect; // Chip select control, Bit7=0: ignore chip select control; bit7=1: parameter is valid, bit1/bit0 are 00/01 to select the CS1/CS2 pins as low-level active chip select, respectively.
+ UCHAR CS1Polarity; // Bit0: CS1 polarity control: 0: active low; 1: active high;
+ UCHAR CS2Polarity; // Bit0: CS2 polarity control: 0: active low; 1: active high;
+ USHORT iIsAutoDeativeCS; // Auto undo chip selection or not after operation is completed
+ USHORT iActiveDelay; // Setting delay time for performing read/write operations after chip selection, in uS
+ ULONG iDelayDeactive; // Delay time for read/write operations after de-selecting chip selection, in uS
+} mSpiCfgS, *mPSpiCfgS;
+
+// Device information
+typedef struct _DEV_INFOR {
+ UCHAR iIndex; // Currently open index
+ UCHAR DevicePath[MAX_PATH]; // Device link name, used for CreateFile
+ UCHAR UsbClass; // Driver Type 0:CH347_USB_CH341, 2:CH347_USB_HID, 3:CH347_USB_VCP
+ UCHAR FuncType; // Functional Type 0:CH347_FUNC_UART, 1:CH347_FUNC_SPI_I2C, 2:CH347_FUNC_JTAG_I2C
+ CHAR DeviceID[64]; // USB\VID_xxxx&PID_xxxx
+ UCHAR ChipMode; // Chip Mode, 0:Mode0(UART0/1); 1:Mode1(Uart1+SPI+I2C); 2:Mode2(HID Uart1+SPI+I2C) 3:Mode3(Uart1+Jtag+I2C) 4:CH347F(Uart*2+Jtag/SPI/I2C)
+ HANDLE DevHandle; // Device handle
+ USHORT BulkOutEndpMaxSize; // Upload endpoint size
+ USHORT BulkInEndpMaxSize; // Downstream endpoint size
+ UCHAR UsbSpeedType; // USB speed, 0:FS,1:HS,2:SS
+ UCHAR CH347IfNum; // USB interface number: CH347T: IF0:UART; IF1:SPI/IIC/JTAG/GPIO
+ // CH347F: IF0:UART0; IF1:UART1; IF2:SPI/IIC/JTAG/GPIO
+ UCHAR DataUpEndp; // Bulk upload endpoint address
+ UCHAR DataDnEndp; // Bulk download endpoint address
+ CHAR ProductString[64]; // USB product string
+ CHAR ManufacturerString[64]; // USB manufacturer string
+ ULONG WriteTimeout; // USB write timeout
+ ULONG ReadTimeout; // USB read timeout
+ CHAR FuncDescStr[64]; // Interface function descriptor
+ UCHAR FirewareVer; // Firmware version, hex value
+
+} mDeviceInforS, *mPDeviceInforS;
+#pragma pack()
+
+// CH347 each mode public function, support CH347 all modes of open, close, USB read, USB write, including HID
+// Open USB device
+HANDLE WINAPI CH347OpenDevice(ULONG DevI);
+
+// Close USB device
+BOOL WINAPI CH347CloseDevice(ULONG iIndex);
+
+// Get device information
+BOOL WINAPI CH347GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation);
+
+// Get CH347 chip type:CHIP_TYPE_CH347T/CHIP_TYPE_CH347F
+UCHAR WINAPI CH347GetChipType(ULONG iIndex); // Specify device serial number
+
+// Obtain driver version, library version, device version and chip type(CH341(FS)/CH347HS)
+BOOL WINAPI CH347GetVersion(ULONG iIndex,
+ PUCHAR iDriverVer,
+ PUCHAR iDLLVer,
+ PUCHAR ibcdDevice,
+ PUCHAR iChipType); // CHIP_TYPE_CH341/7
+
+typedef VOID(CALLBACK *mPCH347_NOTIFY_ROUTINE)( // Device plug/unplug notification event callback routine
+ ULONG iEventStatus); // Device plug/unplug event and current status (define below): 0=Device unplug event, 3=Device insertion event
+
+#define CH347_DEVICE_ARRIVAL 3 // Device insertion event, has been inserted
+#define CH347_DEVICE_REMOVE_PEND 1 // Device wil be unplugged
+#define CH347_DEVICE_REMOVE 0 // Device unplug event, has been pulled out
+
+// Set the device event notification routine
+BOOL WINAPI CH347SetDeviceNotify(ULONG iIndex, // Specify device serial number, 0 corresponds to the first device
+ PCHAR iDeviceID, // Optional parameter, points to a string, specifying the monitored device ID, ending with \0.
+ mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies the port device event callback program. If it is NULL, tehn cancel the event notification, otherwise call the routine when an event is detected.
+
+// Read USB data block
+BOOL WINAPI CH347ReadData(ULONG iIndex, // Specify device serial number
+ PVOID oBuffer, // Points to a buffer large enough to save the read data
+ PULONG ioLength); // Points to length unit; When input, it is the length to be read, and after return, it is the actual length to be read.
+
+// Write USB data block
+BOOL WINAPI CH347WriteData(ULONG iIndex, // Specify device serial number
+ PVOID iBuffer, // Points to a buffer large enough to save the written data
+ PULONG ioLength); // Points to length unit; When input, it is the length to be written, and after return, it is the actual length to be written.
+
+// Set the timeout of USB data read/write
+BOOL WINAPI CH347SetTimeout(ULONG iIndex, // Specify device serial number
+ ULONG iWriteTimeout, // Specifies the timeout for USB to write data blocks, in mS, 0xFFFFFFFF specifies no timeout (default)
+ ULONG iReadTimeout); // Specifies the timeout for USB to read data blocks, in mS, 0xFFFFFFFF specifies no timeout (default)
+
+/***************SPI********************/
+// SPI controller initialization
+BOOL WINAPI CH347SPI_Init(ULONG iIndex, mSpiCfgS *SpiCfg);
+
+//CH347F set SPI clock frequency. iSpiSpeedHz= 0=60MHz, 1=30MHz, 2=15MHz, 3=7.5MHz, 4=3.75MHz, 5=1.875MHz, 6=937.5KHz$17 7=468.75KHz
+BOOL WINAPI CH347SPI_SetFrequency(ULONG iIndex, ULONG iSpiSpeedHz);
+
+// CH347F set the SPI databit
+BOOL WINAPI CH347SPI_SetDataBits(ULONG iIndex,
+ UCHAR iDataBits); // iDataBits= 0:8bit, 1:16bit
+
+// Get SPI controller configuration information
+BOOL WINAPI CH347SPI_GetCfg(ULONG iIndex, mSpiCfgS *SpiCfg);
+
+// Sets the chip select state, call CH347SPI_Init to set up the CS before use
+BOOL WINAPI CH347SPI_ChangeCS(ULONG iIndex, // Specify device serial number
+ UCHAR iStatus); // 0= Undo chip select, 1=Set chip select
+
+// Set SPI chip select
+BOOL WINAPI CH347SPI_SetChipSelect(ULONG iIndex, // Specify device serial number
+ USHORT iEnableSelect, // Lower 8-bit is CS1, higher 8-bit is CS2; A byte value of 1= sets CS, 0= ignores this CS setting
+ USHORT iChipSelect, // Lower 8-bit is CS1, higher 8-bit is CS2; A byte value of 1= sets CS, 0= ignores this CS setting
+ ULONG iIsAutoDeativeCS, // Lower 16-bit is CS1, higher 16-bit is CS2; auto undo chip selection or not after operation is completed
+ ULONG iActiveDelay, // Lower 16-bit is CS1, higher 16-bit is CS2; set the delay time of performing read/write operations after chip selection, in us
+ ULONG iDelayDeactive); // Lower 116-bit is CS1, higher 16-bit is CS2; do not set the delay time of performing read/write operations after chip selection, in us
+
+// SPI4 write data
+BOOL WINAPI CH347SPI_Write(ULONG iIndex, // Specify device serial number
+ ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select
+ ULONG iLength, // Number of data bytes ready to be transferred
+ ULONG iWriteStep, // Length of a single block to be read
+ PVOID ioBuffer); // Point to a buffer, place the data to be written out from MOSI
+
+// SPI4 read data, no need to write data first, much more efficient than CH347SPI_WriteRead
+BOOL WINAPI CH347SPI_Read(ULONG iIndex, // Specify device serial number
+ ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select
+ ULONG oLength, // Number of data bytes ready to be transferred
+ PULONG iLength, // Number of data bytes of data to be read
+ PVOID ioBuffer); // Points to a buffer, place the data to be written out from DOUT, returned data is read from DIN
+
+// Handle SPI data stream, 4-wire interface
+BOOL WINAPI CH347SPI_WriteRead(ULONG iIndex, // Specify device serial number
+ ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: perform chip select
+ ULONG iLength, // Number of data bytes ready to be transferred
+ PVOID ioBuffer); // Points to a buffer that place the data to be written out from DOUT, returned data is read from DIN
+
+// Handle SPI data stream, 4-wire interface
+BOOL WINAPI CH347StreamSPI4(ULONG iIndex, // Specify device serial number
+ ULONG iChipSelect, // Chip select control, bit7=0: ignore chip select control, bit7=1: parameter is valid
+ ULONG iLength, // Number of data bytes ready to be transferred
+ PVOID ioBuffer); // Points to a buffer, places data to be written out from DOUT, returned data is read from DIN
+
+/***************JTAG********************/
+// JTAG interface initialization, set mode and speed
+BOOL WINAPI CH347Jtag_INIT(ULONG iIndex,
+ UCHAR iClockRate); // Communication speed; valid values are 0-5, larger values indicating faster speeds
+
+// Gets Jtag speed configuration
+BOOL WINAPI CH347Jtag_GetCfg(ULONG iIndex, // Specify device serial number
+ UCHAR *ClockRate); // Communication speed; valid values are 0-5, larger values indicating faster speeds
+
+// Changing the TMS value for state switching
+BOOL WINAPI CH347Jtag_TmsChange(ULONG iIndex, // Specify device serial number
+ PUCHAR tmsValue, // TMS bit value for switching, in bytes
+ ULONG Step, // Number of valid TMS bits stored in tmsValue
+ ULONG Skip); // Valid start bit
+
+// Read/write data in the shift-dr/ir state, switch to Exit DR/IR state after execution.
+// State machine: Shift-DR/ ir.rw.->Exit DR/IR
+BOOL WINAPI CH347Jtag_IoScan(ULONG iIndex,
+ PUCHAR DataBits, // Data bits to be transmitted
+ ULONG DataBitsNb, // Number of bits of data to be transmitted
+ BOOL IsRead); // Read data or not
+
+// Switch to shift-dr/ir state for read/write, after execution is complete, If it is the last packet, then shift to Exit DR/IR; if not, then in Shift-DR/IR
+// State machine :Shift-DR/IR.RW.. ->[Exit DR/IR]
+BOOL WINAPI CH347Jtag_IoScanT(ULONG iIndex, // Specify the device number
+ PUCHAR DataBits, // Data bits to be transmitted
+ ULONG DataBitsNb, // Number of bits of data to be transmitted
+ BOOL IsRead, // Read data or not
+ BOOL IsLastPkt); // Is it the last packet or not
+
+// CH347 reset Tap state function. six or more consecutive TCKs & TMS are high��will set the state machine to Test-Logic Reset
+ULONG WINAPI CH347Jtag_Reset(ULONG iIndex);
+
+// CH347 operates TRST to complete a hardware reset
+BOOL WINAPI CH347Jtag_ResetTrst(ULONG iIndex, BOOL TRSTLevel);
+
+// Bit band mode JTAG IR/DR data read/write, suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_WriteRead_Fast
+// Command packages are read/write in batches of 4096 bytes
+// State machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead(ULONG iIndex, // Specify device serial number
+ BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write
+ ULONG iWriteBitLength, // Write length, the length to be written
+ PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out
+ PULONG oReadBitLength, // Points to the length unit, returned length is the actual length read
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+// JTAG IR/DR data batch read/write, for multi-byte sequential read/write. Such as JTAG download firmware. Because the hardware has a 4K buffer, if you write first and read later, the length will not exceed 4096 bytes. The buffer size can be adjusted by yourself
+// State machine: Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead_Fast(ULONG iIndex, // Specify device serial number
+ BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write
+ ULONG iWriteBitLength, // Write length, length to be written
+ PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out
+ PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read.
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+// Bitband mode JTAG IR/DR data read/write, suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_WriteRead_Fast
+// Command packages are read/write in batches of 4096 bytes
+// State machine:Run-Test-> Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteReadEx(ULONG iIndex, // Specify device serial number
+ BOOL IsInDrOrIr, // =TRUE: in SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(perform data interaction).->Exit IR/DR -> Run-Test
+ BOOL IsDR, // =TRUE: DR data read/write, =FALSE:IR data read/write
+ ULONG iWriteBitLength, // Write length, The length to be written
+ PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out
+ PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+// JTAG IR/DR data batch read/write, for multi-byte sequential read/write. Such as JTAG download firmware. Because the hardware has a 4K buffer, if you write first and read later, the length will not exceed 4096 bytes. The buffer size can be adjusted by yourself
+// State machine:Run-Test->Shift-IR/DR..->Exit IR/DR -> Run-Test
+BOOL WINAPI CH347Jtag_WriteRead_FastEx(ULONG iIndex, // Specify the device number
+ BOOL IsInDrOrIr, // =TRUE: In SHIFT-DR read/write ==FALSE: Run-Test->Shift-IR/DR.(perform data interaction).->Exit IR/DR -> Run-Test
+ BOOL IsDR, // =TRUE: DR read/write, =FALSE:IR data read/write
+ ULONG iWriteBitLength, // Write length. The length to be written
+ PVOID iWriteBitBuffer, // Points to a buffer to place data ready to be written out
+ PULONG oReadBitLength, // Point to the length unit, returned length is the actual length read
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+// Switch the JTAG state machine
+BOOL WINAPI CH347Jtag_SwitchTapState(UCHAR TapState);
+
+// JTAG DR Write, in bytes, for multi-byte sequential read/write. such as JTAG firmware download operations.
+// State machine: Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteWriteDR(ULONG iIndex, // Specify the device serial number
+ ULONG iWriteLength, // Write length, length of bytes to be written
+ PVOID iWriteBuffer); // Points to a buffer, place the data ready to be written out
+
+// JTAG DR read, in bytes, for multi-byte sequential read.
+// State machine: Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteReadDR(ULONG iIndex, // Specify the device serial number
+ PULONG oReadLength, // Points to the length unit, returned length is the actual length read
+ PVOID oReadBuffer); // Points to a buffer large enough to place the read data
+
+// JTAG IR write, in bytes, for multi-byte sequential write.
+// State machine: Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteWriteIR(ULONG iIndex, // Specify device serial number
+ ULONG iWriteLength, // Write length, the length of bytes to be written
+ PVOID iWriteBuffer); // Points to a buffer, place the data ready to be written out
+
+// JTAG IR read, in bytes, for multi-byte sequential read/write.
+// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_ByteReadIR(ULONG iIndex, // Specify device serial number
+ PULONG oReadLength, // Points to the length unit, returned data is the length of actual bytes read.
+ PVOID oReadBuffer); // Points to a buffer large enough to place the read data
+
+// Bitband mode JTAG DR data write. suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_ByteWriteDR
+// The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_BitWriteDR(ULONG iIndex, // Specify device serial number
+ ULONG iWriteBitLength, // Points to the length unit, returned data is the length of actual bytes read.
+ PVOID iWriteBitBuffer); // Points to a buffer large enough to place the read data
+
+// Bit band mode JTAG IR data write. Suitable for read/write small amounts of data. Such as command operation, state machine switching and other control transfer. For batch data transmission, recommended CH347Jtag_ByteWriteIR
+// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_BitWriteIR(ULONG iIndex, // Specify device serial number
+ ULONG iWriteBitLength, // Points to the length unit, returned data is the length of actual bytes read.
+ PVOID iWriteBitBuffer); // Points to a buffer large enough to place the read data
+
+// Bit band mode JTAG IR data read. Suitable for reading and writing small amounts of data. Such as command operation, state machine switching, etc. For batch data transfer, CH347Jtag_ByteReadIR is recommended.
+// The state machine: Run-Test->Shift-IR..->Exit IR -> Run-Test
+BOOL WINAPI CH347Jtag_BitReadIR(ULONG iIndex, // Specify device serial number
+ PULONG oReadBitLength, // Points to the length unit, returned data is the length of actual bytes read.
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+// Bit band mode JTAG DR data read. Suitable for reading and writing small amounts of data. For batch and high-speed data transfer, CH347Jtag_ByteReadDR is recommended
+// The state machine: Run-Test->Shift-DR..->Exit DR -> Run-Test
+BOOL WINAPI CH347Jtag_BitReadDR(ULONG iIndex, // Specify device serial number
+ PULONG oReadBitLength, // Points to the length unit, returned data is the length of actual bytes read.
+ PVOID oReadBitBuffer); // Points to a buffer large enough to place the read data
+
+/***************GPIO********************/
+// Get the GPIO direction and pin level of CH347
+BOOL WINAPI CH347GPIO_Get(ULONG iIndex,
+ UCHAR *iDir, // Pin direction: GPIO0-7 corresponding bit0-7, 0: input; 1: output
+ UCHAR *iData); // GPIO0 level: GPIO0-7 corresponding bit0-7, 0: low level; 1: high level
+
+// Set the GPIO direction and pin level of CH347
+BOOL WINAPI CH347GPIO_Set(ULONG iIndex, // Specify device serial number
+ UCHAR iEnable, // Data valid flag: corresponding bit0-7, correspond to GPIO0-7.
+ UCHAR iSetDirOut, // Sets I/O direction, A bit is 0 means corresponding pin is input, a bit is 1 means corresponding pin is output. GPIO0-7 corresponds to bit0-7.
+ UCHAR iSetDataOut); // Output data. If I/O direction is output, then a pin outputs low level when a bit cleared to 0, a pin outputs high level when a bit set to 1
+
+typedef VOID(CALLBACK *mPCH347_INT_ROUTINE)( // Interrupt service routine
+ PUCHAR iStatus); // For interrupt status data, refer to the bit description below
+// 8 byte GPIO0-7 pin status, the bits per byte are defined as follows.
+// Bit7:Current GPIO0 direction, 0:Input; 1:Output
+// Bit6:Current GPIO0 level, 0:Low level;1:High level
+// Bit5:Whether the current GPIO0 is set to interrupt; 0:Query mode; 1:Interrupt mode
+// Bit4-3:Set the GPIO0 interrupt mode, 00:Falling edge trigger; 01:Rising edge trigger; 10:Double edge trigger;11: reserved;
+// Bit2-0:reserved;
+
+// Set GPIO interrupt service routine
+BOOL WINAPI CH347SetIntRoutine(ULONG iIndex, // Specify the device serial number
+ UCHAR Int0PinN, // INT0 GPIO pin number greater than 7: disable this interrupt source; 0-7 corresponds to GPIO0-7
+ UCHAR Int0TripMode, // INT0 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve;
+ UCHAR Int1PinN, // INT1 GPIO pin number. If it is greater than 7, disable this interrupt source; 0-7 corresponds to GPIO0-7
+ UCHAR Int1TripMode, // INT1 type: 00:Falling edge trigger; 01:Rising edge trigger; 02:Double edge trigger; 03:reserve;
+ mPCH347_INT_ROUTINE iIntRoutine); // Specify the interrupt service routine, if it is NULL, the interrupt service is canceled, otherwise, the program is called when interrupted.
+
+// Read interrupt data
+BOOL WINAPI CH347ReadInter(ULONG iIndex, // Specify the device serial number
+ PUCHAR iStatus); // Point to the 8-byte unit, used to save read GPIO pin status data, refer to the following bit description
+
+// Abandon interrupt data read operation
+BOOL WINAPI CH347AbortInter(ULONG iIndex); // Specify the device serial number
+
+// Enter IAP firmware upgrade mode
+BOOL WINAPI CH347StartIapFwUpate(ULONG iIndex,
+ ULONG FwSize); // Firmware length
+
+/**************HID/VCP Serial Port*********************/
+// Open serial port
+HANDLE WINAPI CH347Uart_Open(ULONG iIndex);
+
+// Close serial port
+BOOL WINAPI CH347Uart_Close(ULONG iIndex);
+
+// Set device event notification program
+BOOL WINAPI CH347Uart_SetDeviceNotify(
+ ULONG iIndex, // Specify the device serial number, 0 corresponds to the first device
+ PCHAR iDeviceID, // Optional parameter, points to a string, specify the monitored device ID, end with \0
+ mPCH347_NOTIFY_ROUTINE iNotifyRoutine); // Specifies device event callback program; if it is NULL then cancel event notification, otherwise the program is called when an event is detected.
+
+// Obtain UART hardware configuration
+BOOL WINAPI CH347Uart_GetCfg(ULONG iIndex, // Specify the device serial number
+ PULONG BaudRate, // Baud rate
+ PUCHAR ByteSize, // Data bits (5,6,7,8,16)
+ PUCHAR Parity, // Parity bits(0:None; 1:Odd; 2:Even; 3:Mark; 4:Space)
+ PUCHAR StopBits, // Stop bits (0: 1 stop bits; 1: 1.5 stop bit; 2: 2 stop bit);
+ PUCHAR ByteTimeout); // Byte timeout
+
+// Set UART configuration
+BOOL WINAPI CH347Uart_Init(ULONG iIndex, // Specify the device serial number
+ DWORD BaudRate, // Baud rate
+ UCHAR ByteSize, // Data bits (5,6,7,8,16)
+ UCHAR Parity, // Parity bits (0:None; 1:Odd; 2:Even; 3:Mark; 4:Space)
+ UCHAR StopBits, // Stop bits (0: 1 Stop bit; 1: 1.5 stop bit; 2: 2 stop bit);
+ UCHAR ByteTimeout); // Byte timeout, in unit of 100uS
+
+// Set USB data read/write timeout
+BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // Specify the device serial number
+ ULONG iWriteTimeout, // Specify the timeout for USB to write data blocks, in mS,0xFFFFFFFF specifies no timeout (default)
+ ULONG iReadTimeout); // Specify the timeout for USB to read data blocks, in mS,0xFFFFFFFF specifies no timeout (default)
+
+// Read data block
+BOOL WINAPI CH347Uart_Read(ULONG iIndex, // Specify the device serial number
+ PVOID oBuffer, // Points to a buffer large enough to place the read data
+ PULONG ioLength); // Refers to the length unit, the input is the length to be read, the return is the actual length to be read
+
+// Write data blocks
+BOOL WINAPI CH347Uart_Write(ULONG iIndex, // Specify the device serial number
+ PVOID iBuffer, // Points to a buffer to place data ready to be written out
+ PULONG ioLength); // Point to the length unit, the input is the intended length, the return is the actual length
+
+// Query how many bytes are unfetched in read buffer
+BOOL WINAPI CH347Uart_QueryBufUpload(ULONG iIndex, // Specify the device serial number
+ LONGLONG *RemainBytes); // Read buffer unfetched bytes
+
+// Obtain device information
+BOOL WINAPI CH347Uart_GetDeviceInfor(ULONG iIndex, mDeviceInforS *DevInformation);
+
+// Set USB data read/write timeout
+BOOL WINAPI CH347Uart_SetTimeout(ULONG iIndex, // Specify device serial number
+ ULONG iWriteTimeout, // Specify timeout for USB to write data block, in mS, 0xFFFFFFFF specifies no timeout (default)
+ ULONG iReadTimeout); // Specify timeout for USB to read data block, in mS, 0xFFFFFFFF specifies no timeout (default)
+/********I2C***********/
+// I2C settings
+BOOL WINAPI CH347I2C_Set(ULONG iIndex, // Specify device serial number
+ ULONG iMode); // Specify mode, see next line
+// bit1 - bit 0: I2C interface speed/SCL frequency, 00=low speed/20KHz, 01=standard/100KHz(default), 10= fast/400KHz, 11= high speed/750KHz
+// Others are reserved, must be 0
+
+// Set I2C clock stretch
+BOOL WINAPI CH347I2C_SetStretch(ULONG iIndex, // Specify device serial number
+ BOOL iEnable); // I2C Clock Stretch enable, 1:enable,0:disable
+
+// Set hardware asynchronous delay, returns shortly after being called, and then delays for specified milliseconds before the next stream operation.
+BOOL WINAPI CH347I2C_SetDelaymS(ULONG iIndex, // Specify device serial number
+ ULONG iDelay); // Specify the delay in milliseconds
+
+// Process I2C data stream, 2-wire interface, clock wire is SCL pin, data wire is SDA pin
+BOOL WINAPI CH347StreamI2C(ULONG iIndex, // Specify device serial number
+ ULONG iWriteLength, // Number of data bytes to be written
+ PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, first byte is usually the I2C device address and read/write direction bit
+ ULONG iReadLength, // Number of bytes of data to be read
+ PVOID oReadBuffer); // Points to a buffer, returned data is the read-in data
+
+// Process I2C data stream, 2-wire interface, clock wire is SCL pin, data wire is SDA pin (standard bidirectional I/O), speed about 56K bytes, and return the number of ACKs obtained by the host side
+BOOL WINAPI CH347StreamI2C_RetACK(ULONG iIndex, // Specify device serial number
+ ULONG iWriteLength, // Number of data bytes to be written
+ PVOID iWriteBuffer, // Points to a buffer to place data ready to be written out, first byte is usually the I2C device address and read/write direction bit
+ ULONG iReadLength, // Number of bytes of data to be read
+ PVOID oReadBuffer, // Points to a buffer, returned data is the read-in data
+ PULONG rAckCount); // Points to the ACK value returned by read/write
+
+#ifndef _CH341_DLL_H
+typedef enum _EEPROM_TYPE { // 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;
+#endif
+
+// Read data blocks from EEPROM, speed about 56KB
+BOOL WINAPI CH347ReadEEPROM(ULONG iIndex, // Specify device serial number
+ EEPROM_TYPE iEepromID, // Specify EEPROM model
+ ULONG iAddr, // Specify data unit address
+ ULONG iLength, // Number of data bytes to be read
+ PUCHAR oBuffer); // Points to a buffer, returned data is the read-in data
+
+// Write data block to the EEPROM
+BOOL WINAPI CH347WriteEEPROM(ULONG iIndex, // Specify device serial number
+ EEPROM_TYPE iEepromID, // Specify EEPROM model
+ ULONG iAddr, // Specify data unit address
+ ULONG iLength, // Number of data bytes to be written out
+ PUCHAR iBuffer); // Points to a buffer to place data ready to be written out
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _CH347_DLL_H
diff --git a/FlashGBX/bacon/lib/CH347DLL.DLL b/FlashGBX/bacon/lib/CH347DLL.DLL
new file mode 100644
index 0000000..7cd5b1e
Binary files /dev/null and b/FlashGBX/bacon/lib/CH347DLL.DLL differ
diff --git a/FlashGBX/bacon/lib/CH347DLLA64.DLL b/FlashGBX/bacon/lib/CH347DLLA64.DLL
new file mode 100644
index 0000000..3ba5295
Binary files /dev/null and b/FlashGBX/bacon/lib/CH347DLLA64.DLL differ
diff --git a/FlashGBX/bacon/serial.py b/FlashGBX/bacon/serial.py
new file mode 100644
index 0000000..9b7b893
--- /dev/null
+++ b/FlashGBX/bacon/serial.py
@@ -0,0 +1,438 @@
+# -*- coding: utf-8 -*-
+# bacon
+# Author: ChisBread (github.com/ChisBread)
+import zlib
+import time
+from .bacon import BaconDevice
+DEBUG = False
+DEVICE_CMD = {}
+CMD_TO_NAME = {}
+DEVICE_VAR = {}
+VARKEY_TO_NAME = {}
+
+FLASH_TYPES = {
+ "AMD": 0x01,
+ "INTEL": 0x02,
+ "SHARP": 0x02,
+ "OTHERS":0x00,
+}
+
+FLASH_MODS = {
+ "FLASH_METHOD_AGB_FLASH2ADVANCE": 0x05,
+ "FLASH_METHOD_DMG_MMSA": 0x03,
+ "FLASH_METHOD_DMG_DATEL_ORBITV2": 0x09,
+ "FLASH_METHOD_DMG_E201264": 0x0A,
+ "FLASH_METHOD_AGB_GBAMP": 0x0B,
+ "FLASH_METHOD_DMG_BUNG_16M": 0x0C,
+ "FLASH_METHOD_BUFFERED": 0x02,
+ "FLASH_METHOD_PAGED": 0x08,
+ "FLASH_METHOD_UNBUFFERED": 0x01,
+}
+
+def SetDebug(debug: bool):
+ global DEBUG
+ DEBUG = debug
+
+def SetDeviceCMD(device_cmd: dict, device_var: dict):
+ DEVICE_CMD = device_cmd
+ for key in device_cmd.keys():
+ CMD_TO_NAME[device_cmd[key]] = key
+ for key in device_var.keys():
+ VARKEY_TO_NAME[tuple(device_var[key])] = key
+
+def ParseCommand(cmd: bytes):
+ if len(cmd) == 0:
+ return None
+ return CMD_TO_NAME.get(cmd[0], "UNKNOWN")
+
+def dprint(*args, **kwargs):
+ if DEBUG:
+ print(*args, **kwargs)
+
+# | 地址范围 | 功能 | 大小 | 说明 |
+# |------------|-----------|---------|------|
+# | 08000000-09FFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态0) |
+# | 0A000000-0BFFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态1) |
+# | 0C000000-0DFFFFFF | ROM/FlashROM | 32MB | ROM/FlashROM的地址空间(等待状态2) |
+# | 0E000000-0E00FFFF | SRAM | 64KB | SRAM的地址空间(8位总线) |
+
+def MappingAddressToReal(addr):
+ if addr >= 0x08000000 and addr <= 0x09FFFFFF:
+ return addr - 0x08000000
+ if addr >= 0x0A000000 and addr <= 0x0BFFFFFF:
+ return addr - 0x0A000000
+ if addr >= 0x0C000000 and addr <= 0x0DFFFFFF:
+ return addr - 0x0C000000
+ if addr >= 0x0E000000 and addr <= 0x0E00FFFF:
+ return addr - 0x0E000000
+ return addr
+
+# 模拟串口设备,兼容GBX协议
+class BaconFakeSerialDevice:
+ def __init__(self):
+ self.timeout = 1
+
+ self.in_buff = b"" * 0x4000
+ self.in_buff_size = 0x4000
+ self.out_buff = b"\x00" * 0x4000
+ self.out_buff_offset = 0
+ self.out_buff_size = 0x4000
+
+ self.in_waiting = 0
+
+ self.bacon_dev = BaconDevice()
+ self.is_open = True
+
+ self.FLASH_CMD_TYPE = 0x00
+ self.FLASH_CMD_MOD = 0x00
+ self.FLASH_CMD_WE = 0x00
+ self.FLASH_CUSTOM_CMDS = [0x00]*6
+
+ self.BAK_FLASH_TYPE = 0x00
+
+ self.FW_VARS = {}
+ self.MODE = "AGB" # or DMG
+ self.POWER = 0
+ self.AGB_SRAM_WRITING = False
+ self.AGB_BAK_FLASH_WRITING = False
+ self.CALC_CRC32_WAITING = False
+ self.FLASH_PROGRAMMING = False
+ self.SET_FLASH_CMD_WAITING = 0
+
+ # ROM cache 32MB
+ self.ROM_CACHE = [0x00]*0x2000000
+ self.ROM_CACHED = [False]*0x2000000
+
+ def cache_rom(self, addr, data):
+ self.ROM_CACHE[addr:addr+len(data)] = data
+ self.ROM_CACHED[addr:addr+len(data)] = [True]*len(data)
+ def cache_rom_reset(self, addr=0, size=0x2000000):
+ self.ROM_CACHED[addr:addr+size] = [False]*size
+ def read_rom(self, addr, size):
+ if self.ROM_CACHED[addr:addr+size].count(False) > 0:
+ dprint("[BaconFakeSerialDevice] ReadROM:0x%08X Size:%s" % (addr, size))
+ self.cache_rom(addr, self.bacon_dev.AGBReadROM(addr, size))
+ else:
+ dprint("[BaconFakeSerialDevice] ReadROMCached:0x%08X Size:%s" % (addr, size))
+ return bytes(self.ROM_CACHE[addr:addr+size])
+
+ def isOpen(self):
+ return self.bacon_dev is not None
+
+ def close(self):
+ if self.bacon_dev is not None:
+ self.bacon_dev.Close()
+ self.bacon_dev = None
+ self.reset_input_buffer()
+ self.reset_output_buffer()
+ self.is_open = False
+
+ def open(self):
+ self.bacon_dev = BaconDevice()
+ self.is_open = True
+
+ def reset_input_buffer(self):
+ self.in_buff = b""
+ self.in_waiting = 0
+
+ def reset_output_buffer(self):
+ self.out_buff_offset = 0
+
+ def push_to_input_buffer(self, data):
+ dprint("[BaconFakeSerialDevice] PushData:%s" % data.hex())
+ self.in_buff = self.in_buff + data
+ self.in_waiting = len(data)
+
+ def _push_ack(self):
+ self.push_to_input_buffer(b"\x01")
+
+ def write(self, data):
+ self._cmd_parse(data)
+ return len(data)
+
+ def _make_flash_cmds(self, addr, buffer_size):
+ flash_prepare = []
+ flash_commit = []
+ for j in range(6):
+ tcmd = (self.FLASH_CUSTOM_CMDS[j][0]<<1, self.FLASH_CUSTOM_CMDS[j][1])
+ flash_prepare.append(tcmd)
+
+ if flash_prepare[-1] == (0x00, 0x00): # write buffer size?
+ flash_prepare[-1] = (addr, buffer_size//2-1)
+ for k in range(j+1, 6):
+ # commit
+ if not (self.FLASH_CUSTOM_CMDS[k] == (0x00, 0x00)):
+ tcmd = (self.FLASH_CUSTOM_CMDS[k][0]<<1, self.FLASH_CUSTOM_CMDS[k][1])
+ flash_commit.append(tcmd)
+ if flash_commit[-1][0] == 0x00:
+ flash_commit[-1] = (addr, flash_commit[-1][1])
+ break
+ elif flash_prepare[-1][0] == 0x00:
+ flash_prepare[-1] = (addr, flash_prepare[-1][1])
+ return flash_prepare, flash_commit
+
+
+ def _cmd_parse(self, cmd):
+ if self.AGB_SRAM_WRITING:
+ dprint("[BaconFakeSerialDevice] AGB_CART_WRITE_SRAM:0x%08X Value:%s" % (self.FW_VARS["ADDRESS"], cmd.hex()))
+ addr = MappingAddressToReal(self.FW_VARS["ADDRESS"])
+ self.bacon_dev.AGBWriteRAM(addr, cmd)
+ self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]
+ self.AGB_SRAM_WRITING = False
+ self._push_ack()
+ return
+ if self.AGB_BAK_FLASH_WRITING:
+ dprint("[BaconFakeSerialDevice] AGB_CART_WRITE_FLASH_DATA:0x%08X" % (self.FW_VARS["ADDRESS"], ))
+ addr = MappingAddressToReal(self.FW_VARS["ADDRESS"])
+ # prepare unlock cmds and write
+ # TODO: other flash type
+ flash_cmds = [
+ (0x5555, 0xAA),
+ (0x2AAA, 0x55),
+ (0x5555, 0xA0),
+ ]
+ flash_exit = (0, 0xF0)
+ if self.BAK_FLASH_TYPE != FLASH_TYPES["AMD"]:
+ raise Exception("Unsupported Backup Flash Type")
+ for i, data in enumerate(cmd):
+ self.bacon_dev.AGBWriteRAMWithAddress(commands = flash_cmds+[(addr+i, data), flash_exit], reset=False).Flush()
+ self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]
+ self.AGB_BAK_FLASH_WRITING = False
+ self._push_ack()
+ return
+ if self.CALC_CRC32_WAITING:
+ chunk_size = int.from_bytes(cmd, byteorder='big')
+ addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1)
+ dprint("[BaconFakeSerialDevice] CALC_CRC32:0x%08X Size:0x%08X" % (self.FW_VARS["ADDRESS"], chunk_size))
+ ret = self.read_rom(addr, chunk_size)
+ crc32 = zlib.crc32(ret)
+ # push crc32 4byte big-endian
+ self.push_to_input_buffer(crc32.to_bytes(4, byteorder='big'))
+ self.CALC_CRC32_WAITING = False
+ return
+ if self.FLASH_PROGRAMMING:
+ #TODO: More AGB Flash Type, And DMG Flash
+ raw_addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1)
+ addr = raw_addr
+ size = self.FW_VARS["TRANSFER_SIZE"]
+ buffer_size = self.FW_VARS["BUFFER_SIZE"]
+ dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING:0x%08X ValueSize:%s TransferSize:%s BufferSize:%s" % (self.FW_VARS["ADDRESS"], len(cmd), size, buffer_size))
+ if self.FLASH_CMD_MOD == FLASH_MODS["FLASH_METHOD_BUFFERED"] and buffer_size > 0:
+ # per buffer Seq
+ start_time = time.time()
+ self.cache_rom_reset(raw_addr, size)
+ for i in range(0, size, buffer_size):
+ # make flash cmds
+ flash_prepare, flash_commit = self._make_flash_cmds(addr, buffer_size)
+ #dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Prepare:%s Commit:%s" % ([(hex(i[0]), hex(i[1])) for i in flash_prepare], [(hex(i[0]), hex(i[1])) for i in flash_commit]))
+ self.bacon_dev.AGBWriteROMWithAddress(commands=flash_prepare)
+ self.bacon_dev.AGBWriteROMSequential(addr=addr, data=cmd[i:i+buffer_size], reset=False)
+ self.bacon_dev.AGBWriteROMWithAddress(commands=flash_commit).Flush()
+ # S29GL256:
+ # 2-byte[33] 125 750 µs
+ # 32-byte[33] 160 750
+ # 64-byte[33] 175 750
+ # 128-byte[33] 198 750
+ # 256-byte[33] 239 750
+ # 512-byte 340 750
+ # Don't need verify if time is enough
+ addr += buffer_size
+ # retry
+ ret = self.read_rom(raw_addr, size)
+ dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING PerBufferTime:%s(us)" % ((time.time()-start_time)/(size//buffer_size)*1000000))
+ addr = raw_addr
+ for i in range(0, size, buffer_size):
+ j = 1
+ while ret[i:i+buffer_size] != cmd[i:i+buffer_size]:
+ if j > 10:
+ dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Retry Failed!")
+ break
+ flash_prepare, flash_commit = self._make_flash_cmds(addr, buffer_size)
+ self.bacon_dev.AGBWriteROMWithAddress(commands=flash_prepare)
+ self.bacon_dev.AGBWriteROMSequential(addr=addr, data=cmd[i:i+buffer_size], reset=False)
+ self.bacon_dev.AGBWriteROMWithAddress(commands=flash_commit).Flush()
+ # 等待Nms
+ time.sleep(0.001*j)
+ dprint("[BaconFakeSerialDevice] FLASH_PROGRAMMING Retry:%s" % j)
+ ret = ret[:i] + self.read_rom(addr, buffer_size) + ret[i+buffer_size:]
+ j += 1
+ addr += buffer_size
+
+ else:
+ #TODO write with cmd
+ pass
+ self._push_ack()
+ self.FLASH_PROGRAMMING = False
+ self.FW_VARS["ADDRESS"] += size // 2
+ return
+ if self.SET_FLASH_CMD_WAITING > 0:
+ if self.SET_FLASH_CMD_WAITING == 9:
+ self.FLASH_CMD_TYPE = int.from_bytes(cmd, byteorder='big')
+ elif self.SET_FLASH_CMD_WAITING == 8:
+ self.FLASH_CMD_MOD = int.from_bytes(cmd, byteorder='big')
+ elif self.SET_FLASH_CMD_WAITING == 7:
+ self.FLASH_CMD_WE = int.from_bytes(cmd, byteorder='big')
+ elif self.SET_FLASH_CMD_WAITING <= 6:
+ # 0~3: addr
+ # 4~5: cmd
+ self.FLASH_CUSTOM_CMDS[6-self.SET_FLASH_CMD_WAITING] = (int.from_bytes(cmd[:4], byteorder='big'), int.from_bytes(cmd[4:], byteorder='big'))
+ dprint("[BaconFakeSerialDevice] SET_FLASH_CMD: Type:%s Mod:%s WE:%s Cmds:%s" % (self.FLASH_CMD_TYPE, self.FLASH_CMD_MOD, self.FLASH_CMD_WE, self.FLASH_CUSTOM_CMDS))
+ self.SET_FLASH_CMD_WAITING = self.SET_FLASH_CMD_WAITING - 1
+ if self.SET_FLASH_CMD_WAITING == 0:
+ self._push_ack()
+ return
+
+ cmdname = ParseCommand(cmd)
+
+ if cmdname == 'SET_VARIABLE':
+ # 0: cmd
+ # 1: size
+ # 2-5: key
+ # 6-: value
+ size = int(cmd[1])*8
+ key = int.from_bytes(cmd[2:6], byteorder='big')
+ value = int.from_bytes(cmd[6:], byteorder='big')
+ # save
+ self.FW_VARS[VARKEY_TO_NAME.get((size, key), "UNKNOWN")] = value
+ self._push_ack()
+ dprint("[BaconFakeSerialDevice] SetVariable:", VARKEY_TO_NAME.get((size, key), "UNKNOWN"), value)
+ elif cmdname == "GET_VARIABLE":
+ # 0: cmd
+ # 1: size
+ # 2-5: key
+ size = int(cmd[1])
+ key = int.from_bytes(cmd[2:6], byteorder='big')
+ # get
+ value = self.FW_VARS.get(VARKEY_TO_NAME.get((size, key), "UNKNOWN"), 0)
+ self.push_to_input_buffer(bytes([(value >> (i*8)) & 0xFF for i in range(4)]))
+ elif cmdname == "SET_MODE_AGB":
+ self.MODE = "AGB"
+ self._push_ack()
+ elif cmdname == "SET_MODE_DMG":
+ self.MODE = "DMG"
+ self._push_ack()
+ elif cmdname == "SET_VOLTAGE_3_3V":
+ self.POWER = 3
+ self._push_ack()
+ elif cmdname == "SET_VOLTAGE_5V":
+ self.POWER = 5
+ self._push_ack()
+ elif cmdname == "CART_PWR_ON":
+ if self.POWER == 3:
+ self.bacon_dev.PowerControl(v3_3v=True, v5v=False).Flush()
+ elif self.POWER == 5:
+ self.bacon_dev.PowerControl(v3_3v=False, v5v=True).Flush()
+ self._push_ack()
+ elif cmdname == "CART_PWR_OFF":
+ self.bacon_dev.PowerControl(v3_3v=False, v5v=False).Flush()
+ self._push_ack()
+ elif cmdname == "QUERY_CART_PWR":
+ self.push_to_input_buffer(bytes([self.bacon_dev.power]))
+ dprint("[BaconFakeSerialDevice] QueryCartPower:", self.bacon_dev.power)
+ elif cmdname == "AGB_CART_READ_SRAM":
+ addr = MappingAddressToReal(self.FW_VARS["ADDRESS"])
+ dprint("[BaconFakeSerialDevice] AGB_CART_READ_SRAM:0x%08X(0x%08X) Size:%d" % (self.FW_VARS["ADDRESS"], addr, self.FW_VARS["TRANSFER_SIZE"]))
+ ret = self.bacon_dev.AGBReadRAM(addr, self.FW_VARS["TRANSFER_SIZE"])
+ self.push_to_input_buffer(ret)
+ self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]
+ elif cmdname == "AGB_CART_WRITE_SRAM":
+ self.AGB_SRAM_WRITING = True
+ elif cmdname == "AGB_CART_WRITE_FLASH_DATA":
+ self.AGB_BAK_FLASH_WRITING = True
+ self.BAK_FLASH_TYPE = int(cmd[1])
+ elif cmdname == "CALC_CRC32": # 读取一段数据,计算CRC32
+ # 0: cmd
+ # 1~4: chunk_size
+ self.CALC_CRC32_WAITING = True
+ elif cmdname == "AGB_CART_READ":
+ addr = MappingAddressToReal(self.FW_VARS["ADDRESS"]<<1)
+ dprint("[BaconFakeSerialDevice] AGB_CART_READ:0x%08X(0x%08X) Size:%d" % (self.FW_VARS["ADDRESS"], addr, self.FW_VARS["TRANSFER_SIZE"]))
+ ret = self.bacon_dev.AGBReadROM(addr, self.FW_VARS["TRANSFER_SIZE"], reset=False)
+ if ret is not False:
+ self.push_to_input_buffer(ret)
+ if self.MODE == "AGB":
+ self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]//2
+ else:
+ self.FW_VARS["ADDRESS"] += self.FW_VARS["TRANSFER_SIZE"]
+ elif cmdname == "AGB_FLASH_WRITE_SHORT":
+ # 0: cmd
+ # 1~4: addr
+ # 5~6: short
+ dprint("[BaconFakeSerialDevice] AGB_FLASH_WRITE_SHORT:0x%08X Value:%s" % (int.from_bytes(cmd[1:5], byteorder='big'), hex(int.from_bytes(cmd[5:7], byteorder='big'))))
+ addr = int.from_bytes(cmd[1:5], byteorder='big')
+ addr = MappingAddressToReal(addr<<1)
+ self.bacon_dev.AGBWriteROMWithAddress(commands=[(addr, int.from_bytes(cmd[5:7], byteorder='big'))]).Flush()
+ self._push_ack()
+ elif cmdname == "CART_WRITE_FLASH_CMD":
+ # 0: cmd
+ # 1: flashcart
+ # 2: num
+ # 6byte * num: cmds(4byte addr, 2byte cmd)
+ flashcart = int(cmd[1])
+ num = int(cmd[2])
+ cmds = []
+ for i in range(num):
+ addr = int.from_bytes(cmd[3+i*6:7+i*6], byteorder='big')
+ if self.MODE == "AGB" and flashcart:
+ addr = MappingAddressToReal(addr<<1)
+ data = int.from_bytes(cmd[7+i*6:9+i*6], byteorder='big')
+ dprint("[BaconFakeSerialDevice] CART_WRITE_FLASH_CMD:0x%08X Value:%s" % (addr, hex(data)))
+ cmds.append((addr, data))
+ if self.MODE == "AGB" and flashcart:
+ self.bacon_dev.AGBWriteROMWithAddress(commands=cmds).Flush()
+ else:
+ self.bacon_dev.AGBWriteRAMWithAddress(commands=cmds).Flush()
+ self._push_ack()
+ elif cmdname == "FLASH_PROGRAM":
+ self.FLASH_PROGRAMMING = True
+ # self._push_ack() 0x03?
+ elif cmdname == "SET_FLASH_CMD":
+ self.SET_FLASH_CMD_WAITING = 9
+ elif cmdname == "AGB_CART_WRITE":
+ # 0: cmd
+ # 1~4: addr
+ # 5~6: short
+ addr = int.from_bytes(cmd[1:5], byteorder='big')
+ addr = MappingAddressToReal(addr<<1)
+ dprint("[BaconFakeSerialDevice] AGB_CART_WRITE:0x%08X Value:%s" % (addr, hex(int.from_bytes(cmd[5:7], byteorder='big'))))
+ self.bacon_dev.AGBWriteROMWithAddress(commands=[(addr, int.from_bytes(cmd[5:7], byteorder='big'))]).Flush()
+ self._push_ack()
+ elif cmdname == "AGB_READ_GPIO_RTC":
+ dprint("[BaconFakeSerialDevice] !!!! AGB_READ_GPIO_RTC is not implemented !!!!")
+ self.push_to_input_buffer(b"\x00"*8)
+ elif cmdname == "ENABLE_PULLUPS":
+ # TODO
+ dprint("[BaconFakeSerialDevice] !!!! DISABLE_PULLUPS is not implemented !!!!")
+ self._push_ack()
+ elif cmdname == "DISABLE_PULLUPS":
+ # TODO
+ dprint("[BaconFakeSerialDevice] !!!! DISABLE_PULLUPS is not implemented !!!!")
+ self._push_ack()
+ elif cmdname == "AGB_BOOTUP_SEQUENCE":
+ # TODO
+ dprint("[BaconFakeSerialDevice] !!!! AGB_BOOTUP_SEQUENCE is not implemented !!!!")
+ self._push_ack()
+ ###### DMG CMDS ######
+ elif cmdname == "DMG_MBC_RESET":
+ self._push_ack()
+ elif cmd[0] == 0:
+ self._push_ack()
+ else:
+ dprint("[BaconFakeSerialDevice] UnsupportedCommand:%s Value:%s" % (cmdname, cmd.hex()))
+
+
+ def read(self, size: int):
+ dprint("[BaconFakeSerialDevice] ReadSize:%s, Left:%s" % (size, self.in_waiting))
+ if size == 0:
+ return b""
+ if size > self.in_waiting:
+ size = self.in_waiting
+ data = self.in_buff[:size]
+ self.in_buff = self.in_buff[size:]
+ self.in_waiting = len(self.in_buff)
+ dprint("[BaconFakeSerialDevice] ReadData:%s size:%s" % ([ hex(i) for i in data[:64] ], size))
+ return data
+
+ def flush(self):
+ # TODO: Parse the command
+ self.reset_output_buffer()
diff --git a/FlashGBX/bacon/test.py b/FlashGBX/bacon/test.py
new file mode 100644
index 0000000..519919c
--- /dev/null
+++ b/FlashGBX/bacon/test.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+# bacon
+# Author: ChisBread (github.com/ChisBread)
+
+import time
+import os
+import sys
+# append path
+from .bacon import BaconDevice
+from .command import *
+
+bacon_dev = BaconDevice()
+
+def open_current_dif(filename, mode):
+ return open(os.path.join(os.path.dirname(__file__), filename), mode)
+
+def bacon_write_test():
+ # 11: 5v
+ # 00: 3.3v
+ print("[SPI] write power control command")
+ powercmd = make_power_control_command(not_v3_3v=False, v5v=False)
+ print("[SPI] write bytes %s" % bytes2command(powercmd))
+ ret = bacon_dev.Write(powercmd)
+ #True if successful, False otherwise.
+ print("[SPI] write result %s" % ret)
+ print("[SPI] write cart 30bit write command")
+ pintestcmd = make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=False, rd=True,
+ cs1=False, cs2=True,
+ v16bit=b"\xFF\xFF", v8bit=b"\xFF"
+ )
+ print("[SPI] write bytes %s" % bytes2command(pintestcmd))
+ bacon_dev.Write(pintestcmd)
+ print("[SPI] write test done")
+
+def bacon_read_test_30bit():
+ power_read_cmd = make_power_read_command()
+ cart_read_cmd = make_cart_30bit_read_command()
+ # 测试3.3v
+ print("[SPI] write power control command")
+ bacon_dev.Write(make_power_control_command(not_v3_3v=False, v5v=False))
+ ret = bacon_dev.WriteRead(power_read_cmd)
+ print("[SPI] read bytes %s, expected %s" % (bytes2command(ret)[::-1], "00"))
+ ###### 测试读取卡带数据 ######
+ bacon_dev.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00"
+ ))
+ ## CS1上升沿,写入低位地址0x0000
+ bacon_dev.Write(make_gba_rom_cs_write(cs=False))
+ ret = bacon_dev.WriteRead(cart_read_cmd)
+ print(extract_cart_30bit_read_data(ret))
+ readbytes = []
+ avg_start = time.time()
+ start = time.time()
+ # 每0x10000,需要对v8bit+1
+ # 所以0x10000需要能被BULK_SIZE整除
+ high_addr = 0x00
+ BULK_SIZE = 512
+ TOTAL_SIZE = 0x800000 # 16MB
+ if TOTAL_SIZE % BULK_SIZE != 0:
+ raise ValueError("TOTAL_SIZE must be a multiple of BULK_SIZE")
+ if 0x10000 % BULK_SIZE != 0:
+ raise ValueError("BULK_SIZE must be a factor of 0x10000")
+ readcyclecmd = make_rom_read_cycle_command(times=BULK_SIZE)
+ for i in range(TOTAL_SIZE//BULK_SIZE):
+ ret = bacon_dev.WriteRead(readcyclecmd)
+ exteds = extract_read_cycle_data(ret, times=BULK_SIZE)
+ for idx, exted in enumerate(exteds):
+ num = i * BULK_SIZE + idx
+ readbytes.append(exted["v16bit"] >> 8)
+ readbytes.append(exted["v16bit"] & 0xFF)
+ if num < 2:
+ print(f"header bytes: {hex(readbytes[-2])[2:].rjust(2, '0')}, {hex(readbytes[-1])[2:].rjust(2, '0')}")
+ if num % 0x10000 == 0x10000 - 1:
+ high_addr += 1
+ bacon_dev.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=False, cs2=True,
+ v16bit=b"\x00\x00", v8bit=bytes([high_addr])
+ ))
+ if num % 0x1000 == 0x1000 - 1:
+ # print(f"[SPI] high address: 0x{hex(high_addr)[2:].rjust(2, '0')}")
+ # print(f"[SPI] read 0x2000 bytes in {time.time() - start}s speed(KB/s): {0x2000 / (time.time() - start) / 1024}")
+ print(f"\rAddress:0x{num+1:06X} Speed(KB/s):{(0x2000 / (time.time() - start) / 1024):.2f}", end="")
+ start = time.time()
+ print(f"\n[SPI] read {hex(TOTAL_SIZE*2)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_SIZE*2 / (time.time() - avg_start) / 1024}")
+ ch347.spi_change_cs(0x00)
+ with open_current_dif("gba_rom.bin", "wb") as f:
+ f.write(bytes(readbytes))
+
+def bacon_read_test_16bit():
+ power_read_cmd = make_power_read_command()
+ cart_read_cmd = make_cart_30bit_read_command()
+ # 测试3.3v
+ print("[SPI] write power control command")
+ bacon_dev.Write(make_power_control_command(not_v3_3v=False, v5v=False))
+ ret = bacon_dev.WriteRead(power_read_cmd)
+ print("[SPI] read bytes %s, expected %s" % (bytes2command(ret)[::-1], "00"))
+ ###### 测试读取卡带数据 ######
+ bacon_dev.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=True, cs2=True,
+ v16bit=b"\x00\x00", v8bit=b"\x00"
+ ))
+ ## CS1上升沿,写入低位地址0x0000
+ bacon_dev.Write(make_gba_rom_cs_write(cs=False))
+ ret = bacon_dev.WriteRead(cart_read_cmd)
+ print(extract_cart_30bit_read_data(ret))
+ readbytes = []
+ avg_start = time.time()
+ start = time.time()
+ # 每0x10000,需要对v8bit+1
+ # 所以0x10000需要能被BULK_SIZE整除
+ high_addr = 0x00
+ MAX_BULK_SIZE = 0x1000//(len(make_rom_read_cycle_command(times=1))) # buffer不能超过0x1000,但尽量大一点
+ bulk_list = [MAX_BULK_SIZE]*(0x10000//MAX_BULK_SIZE) + [0x10000%MAX_BULK_SIZE]
+ # print(f"[SPI] bulk list: {bulk_list}")
+ TOTAL_SIZE = 0x800000 # 16MB
+ for i in range(TOTAL_SIZE//0x10000):
+ # readcyclecmd = make_rom_read_cycle_command(times=MAX_BULK_SIZE)
+ # ret = bacon_dev.WriteRead(readcyclecmd)
+ # exteds = extract_read_cycle_data(ret, times=dynamic_bulk_size)
+ for j in range(len(bulk_list)):
+ readcyclecmd = make_rom_read_cycle_command(times=bulk_list[j])
+ ret = bacon_dev.WriteRead(readcyclecmd)
+ exteds = extract_read_cycle_data(ret, times=bulk_list[j])
+ for idx, exted in enumerate(exteds):
+ readbytes.append(exted >> 8)
+ readbytes.append(exted & 0xFF)
+ if num < 2:
+ print(f"header bytes: {hex(readbytes[-2])[2:].rjust(2, '0')}, {hex(readbytes[-1])[2:].rjust(2, '0')}")
+ if num % 0x10000 == 0x10000 - 1:
+ high_addr += 1
+ bacon_dev.Write(make_cart_30bit_write_command(
+ phi=False, req=False,
+ wr=True, rd=True,
+ cs1=False, cs2=True,
+ v16bit=b"\x00\x00", v8bit=bytes([high_addr])
+ ))
+ if num % 0x1000 == 0x1000 - 1:
+ # print(f"[SPI] high address: 0x{hex(high_addr)[2:].rjust(2, '0')}")
+ # print(f"[SPI] read 0x2000 bytes in {time.time() - start}s speed(KB/s): {0x2000 / (time.time() - start) / 1024}")
+ print(f"\rAddress:0x{num+1:06X} Speed(KB/s):{(0x2000 / (time.time() - start) / 1024):.2f}", end="")
+ start = time.time()
+ num += 1
+ print(f"\n[SPI] read {hex(TOTAL_SIZE*2)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_SIZE*2 / (time.time() - avg_start) / 1024}")
+ with open_current_dif("gba_rom.bin", "wb") as f:
+ f.write(bytes(readbytes))
+
+def bacon_test_api():
+ bacon_dev.PowerControl(v3_3v=True, v5v=False).Flush()
+ bacon_dev.AGBWriteRAM(addr=0, data=open_current_dif("gba_bak_ram.bin", "rb").read())
+ TOTAL_BYTE_SIZE = 0x800000*2 # 16MB
+ start = time.time()
+ avg_start = time.time()
+ first = True
+ last_addr = 0
+ def callback(addr, data):
+ nonlocal first, start, avg_start, last_addr
+ if addr == 0 or (addr % 0x1000 != 0 and addr != TOTAL_BYTE_SIZE):
+ return
+ if first:
+ print(f"header bytes: {hex(data[0])[2:].rjust(2, '0')}, {hex(data[1])[2:].rjust(2, '0')}")
+ first = False
+ last_addr = 0
+ if addr == TOTAL_BYTE_SIZE:
+ print(f"\n[SPI] read {hex(TOTAL_BYTE_SIZE)} bytes in {time.time() - avg_start}s speed(KB/s): {TOTAL_BYTE_SIZE / (time.time() - avg_start) / 1024}")
+ else:
+ delta = addr - last_addr
+ print(f"\rAddress:0x{addr:06X} Speed(KB/s):{(delta / (time.time() - start) / 1024):.2f}", end="")
+ start = time.time()
+ last_addr = addr
+ ## 读取卡带存档
+ TOTAL_BYTE_SIZE = 0x8000 # 32KB
+ start = time.time()
+ avg_start = time.time()
+ first = True
+ ram = bacon_dev.AGBReadRAM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback)
+ with open_current_dif("gba_ram.bin", "wb") as f:
+ f.write(ram)
+ ## 写入卡带存档
+ new_ram = bytes([0xFF]*TOTAL_BYTE_SIZE)
+ start = time.time()
+ avg_start = time.time()
+ first = True
+ ret = bacon_dev.AGBWriteRAM(addr=0, data=new_ram, callback=callback)
+ TOTAL_BYTE_SIZE = 0x8000 # 32KB
+ start = time.time()
+ avg_start = time.time()
+ first = True
+ new_ram_ret = bacon_dev.AGBReadRAM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback)
+ #### 判断是否写入成功
+ if new_ram_ret == new_ram and new_ram_ret != ram:
+ print("[SPI] write and read ram success")
+ else:
+ print("[SPI] write and read ram failed head 10 bytes: %s, %s" % (new_ram[:10], new_ram_ret[:10]))
+ ## 恢复卡带存档
+ bacon_dev.AGBWriteRAM(addr=0, data=ram)
+ ## 读取卡带ROM
+ TOTAL_BYTE_SIZE = 0x800000*2 # 16MB
+ start = time.time()
+ avg_start = time.time()
+ first = True
+ rom = bacon_dev.AGBReadROM(addr=0, size=TOTAL_BYTE_SIZE, callback=callback)
+ with open_current_dif("gba_rom.bin", "wb") as f:
+ f.write(rom)
+if __name__ == "__main__":
+ # print(ch347.get_version())
+ # bacon_read_test_30bit()
+ # bacon_read_test_16bit()
+ bacon_test_api()
+ bacon_dev.Close()
\ No newline at end of file
diff --git a/FlashGBX/hw_Bacon.py b/FlashGBX/hw_Bacon.py
new file mode 100644
index 0000000..ba4d85d
--- /dev/null
+++ b/FlashGBX/hw_Bacon.py
@@ -0,0 +1,166 @@
+# -*- coding: utf-8 -*-
+# FlashGBX
+# Author: Lesserkuma (github.com/lesserkuma)
+
+# pylint: disable=wildcard-import, unused-wildcard-import
+from .LK_Device import *
+
+from .bacon import BaconFakeSerialDevice, SetDebug, SetDeviceCMD
+
+class GbxDevice(LK_Device):
+ DEVICE_NAME = "Bacon"
+ DEVICE_MIN_FW = 1
+ DEVICE_MAX_FW = 12
+ DEVICE_LATEST_FW_TS = { 5:1730731680, 10:1730731680, 11:1730731680, 12:1730731680, 13:1730731680 }
+ PCB_VERSIONS = { 5:'', 12:'v1.2', 13:'v1.3' }
+
+ def __init__(self):
+ SetDebug(Util.DEBUG)
+ SetDeviceCMD(LK_Device.DEVICE_CMD, LK_Device.DEVICE_VAR)
+ pass
+
+ def Initialize(self, flashcarts, port=None, max_baud=2000000):
+ conn_msg = []
+ try:
+ self.DEVICE = BaconFakeSerialDevice()
+ self.LoadFirmwareVersion()
+ except Exception as e:
+ dprint("Failed to initialize BaconFakeSerialDevice:", e)
+ return False
+ dprint(f"Found a {self.DEVICE_NAME}")
+ dprint("Firmware information:", "?")
+
+ self.MAX_BUFFER_READ = 0x1000
+ self.MAX_BUFFER_WRITE = 0x800
+
+ self.PORT = "Dummy"
+ self.DEVICE.timeout = 1
+
+ conn_msg.append([0, "Welcome to use " + self.DEVICE_NAME + "."])
+
+ # Load Flash Cartridge Handlers
+ self.UpdateFlashCarts(flashcarts)
+ return conn_msg
+
+ def LoadFirmwareVersion(self):
+ dprint("Querying firmware version")
+ try:
+ self.DEVICE.timeout = 0.075
+ self.DEVICE.reset_input_buffer()
+ self.DEVICE.reset_output_buffer()
+ self.DEVICE.timeout = 1
+ self.FW = {}
+ self.FW["fw_ts"] = 1730731680
+ self.FW["cfw_id"] = "L"
+ self.FW["fw_ver"] = 13
+ self.FW["fw_dt"] = datetime.datetime.fromtimestamp(self.FW["fw_ts"]).astimezone().replace(microsecond=0).isoformat()
+ self.FW["ofw_ver"] = None
+ self.FW["pcb_ver"] = 13
+ self.FW["pcb_name"] = "Bacon"
+ # Cartridge Power Control support
+ self.FW["cart_power_ctrl"] = True
+ # Reset to bootloader support
+ self.FW["bootloader_reset"] = False #True if temp & 1 == 1 else False
+ self.FW["unregistered"] = False #True if temp >> 7 == 1 else False
+ return True
+ except Exception as e:
+ traceback.print_exc()
+ dprint("Disconnecting due to an error", e, sep="\n")
+ try:
+ if self.DEVICE.isOpen():
+ self.DEVICE.reset_input_buffer()
+ self.DEVICE.reset_output_buffer()
+ self.DEVICE.close()
+ self.DEVICE = None
+ except:
+ pass
+ return False
+
+ def ChangeBaudRate(self, _):
+ dprint("Baudrate change is not supported.")
+
+ def CheckActive(self):
+ if time.time() < self.LAST_CHECK_ACTIVE + 1: return True
+ dprint("Checking if device is active")
+ if self.DEVICE is None:
+ dprint("Device is None")
+ return False
+ if self.LoadFirmwareVersion():
+ dprint("Device is active")
+ self.LAST_CHECK_ACTIVE = time.time()
+ return True
+ return False
+
+ def GetFirmwareVersion(self, more=False):
+ s = "{:s}{:d}".format(self.FW["cfw_id"], self.FW["fw_ver"])
+ if self.FW["pcb_name"] == None:
+ s += " "
+ if more:
+ s += " ({:s})".format(self.FW["fw_dt"])
+ return s
+
+ def GetFullNameExtended(self, more=False):
+ if more:
+ return "{:s} – Firmware {:s} ({:s}) on {:s}".format(self.GetFullName(), self.GetFirmwareVersion(), self.FW["fw_dt"], self.GetPort())
+ else:
+ return "{:s} – Firmware {:s} ({:s})".format(self.GetFullName(), self.GetFirmwareVersion(), self.GetPort())
+
+ def CanSetVoltageManually(self):
+ return False
+
+ def CanSetVoltageAutomatically(self):
+ return True
+
+ def CanPowerCycleCart(self):
+ return self.FW["cart_power_ctrl"]
+
+ def GetSupprtedModes(self):
+ return ["DMG", "AGB"]
+
+ def IsSupported3dMemory(self):
+ return True
+
+ def IsClkConnected(self):
+ return True
+
+ def SupportsFirmwareUpdates(self):
+ return True
+
+ def FirmwareUpdateAvailable(self):
+ return False
+
+ def GetFirmwareUpdaterClass(self):
+ return None
+
+ def ResetLEDs(self):
+ pass
+
+ def SupportsBootloaderReset(self):
+ return self.FW["bootloader_reset"]
+
+ def BootloaderReset(self):
+ return False
+
+ def SupportsAudioAsWe(self):
+ return not (self.FW["pcb_ver"] < 13 and self.CanPowerCycleCart())
+
+ def GetMode(self):
+ return super().GetMode()
+
+ def SetAutoPowerOff(self, value):
+ value &= 0xFFFFFFFF
+ #if value == 0 or value > 5000: value = 1500
+ return super().SetAutoPowerOff(value)
+
+ def GetFullName(self):
+ if self.FW["pcb_ver"] < 13 and self.CanPowerCycleCart():
+ s = "{:s} {:s} + PLUGIN 01".format(self.GetName(), self.GetPCBVersion())
+ else:
+ s = "{:s} {:s}".format(self.GetName(), self.GetPCBVersion())
+ if self.IsUnregistered():
+ s += " (unregistered)"
+ return s
+
+ def GetRegisterInformation(self):
+ text = "hahaha, no"
+ return text
diff --git a/setup.py b/setup.py
index 87c3d5b..de279c4 100644
--- a/setup.py
+++ b/setup.py
@@ -13,7 +13,7 @@
description="Reads and writes Game Boy and Game Boy Advance cartridge data",
url="https://github.com/lesserkuma/FlashGBX",
packages=setuptools.find_packages(),
- install_requires=['pyserial>=3.5', 'Pillow', 'setuptools', 'requests', 'python-dateutil'],
+ install_requires=['pyserial>=3.5', 'Pillow', 'setuptools', 'requests', 'python-dateutil', 'bitarray', 'ch347api'],
extras_require={
"qt5":["PySide2>=5.14"],
"qt6":["PySide6"]