diff --git a/README.md b/README.md index 56733f8..8c334b3 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,22 @@ [![sites](docs/CH55.png)](http://www.wch.cn/products/category/5.html) [![sites](docs/CH552.png)](http://www.wch.cn/products/CH552.html) +* 芯片封装:TSSOP20、MSOP10、QFN16、SOP16 + +[![sites](docs/sop.png)](http://www.wch.cn/products/CH554.html) +[![sites](docs/package.png)](http://www.wch.cn/products/CH554.html) + ### [资源收录](https://github.com/sochub/CH552) * [参考文档](docs/) * [参考资源](src/) +* [相关工具](tools/) + +#### [外部资源](https://github.com/sochub) + +* [OS-Q H05](https://github.com/OS-Q/H02) +* [编译器SDCC](https://github.com/sochub/sdcc) + ### [选型建议](https://github.com/sochub) diff --git a/docs/package.png b/docs/package.png new file mode 100644 index 0000000..639c429 Binary files /dev/null and b/docs/package.png differ diff --git a/docs/sop.png b/docs/sop.png new file mode 100644 index 0000000..3af598a Binary files /dev/null and b/docs/sop.png differ diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..13b7cc2 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,53 @@ +ch55x-tool +------------ + +An open sourced python command line flash tool for flashing WinChipHead CH55x series 8051 USB micro controllers, including CH551, CH552, CH553, CH554, and CH559 with bootloader version 2.30 or 2.31. + +Usage +------------ +* __-f \__ Erase the whole chip, and flash the bin file to the CH55x. +* __-r__ Issue reset and run after the flashing. +* __-d__ Debug mode, if enabled, will print out all USB communications. + +Tool Setup +------------ +* Linux Distros + > Most Linux distros come with libusb, so you only need to install the pyusb packge. +```bash +python3 -mpip install pyusb +``` + +* Mac OS + > For Mac OS, you need to install both libusb and pyusb. + +```bash +# If you dont have brew installed. +# /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +brew install libusb +python3 -mpip install pyusb +``` + +* As for Windows, oh no... :( + > 1. First, you need to download the [Zadig](https://zadig.akeo.ie/) for replacing the CH375 driver from WCH to libusb. + > 2. Click the Options->List all devices, to show all devices + > 3. Find the device marked with __USB Module__, which presented driver is __CH375_balabala__ + > 4. Replace the driver with libusb-win32. + > 5. Install the pyusb package. + ``` + python -mpip install pyusb #Since for windows, they dont use python3, but you have to make sure you have the pythono3 in the PATH + ``` + > 6. If you want to use the WCH Toolchain, open the device manager, find the device marked with __libusb-win32 deives__, right clicked on it, and Uninstall the driver and delete the driver. You can also check the FAQ of Zadig [HERE](https://github.com/pbatard/libwdi/wiki/Zadig). + + FAQ + --------- + * Why I got a __Error: No backend available__ ? + > On windows, this means you dont a valid libusb device, see the guide above. For other system, you might dont have the libusb installed, follow the guide above. + + * Why it said __NO_DEV_FOUND__ ? + > Pyusb unable to fine the device with given PID&VID. Maybe you dont power on your device, or it is not in DFU mode. + + * I got a __USB_ERROR_CANNOT_DETACH_KERNEL_DRIVER__, __USB_ERROR_CANNOT_SET_CONFIG__, or __USB_ERROR_CANNOT_CLAIM_INTERFACE__ error. + > I never met with those problems on a working CH552. Checking the power, the previliage, and praying may help. + + * What if it return __Bootloader version not supported__? + > The program dont support BTVER lower than 2.30(welcome PR, but since they are too old, I dont have plan to support them). Or maybe they have a newer verison, for this situlation, it is welcome for you to open an issue. diff --git a/tools/ch55xtool.py b/tools/ch55xtool.py new file mode 100644 index 0000000..94f98bb --- /dev/null +++ b/tools/ch55xtool.py @@ -0,0 +1,338 @@ +import sys, os +import time +import array +import math +import argparse + +import usb.core +import usb.util + +#======= Some C-like static constants ======= +DFU_ID_VENDOR = 0x4348 +DFU_ID_PRODUCT = 0x55e0 + +EP_OUT_ADDR = 0x02 +EP_IN_ADDR = 0x82 + +USB_MAX_TIMEOUT = 2000 + +DETECT_CHIP_CMD_V2 = [0xa1, 0x12, 0x00, 0x52, 0x11, 0x4d, 0x43, 0x55, 0x20, 0x49, 0x53, + 0x50, 0x20, 0x26, 0x20, 0x57, 0x43, 0x48, 0x2e, 0x43, 0x4e] +END_FLASH_CMD_V2 = [0xa2, 0x01, 0x00, 0x00] +RESET_RUN_CMD_V2 = [0xa2, 0x01, 0x00, 0x01] +SEND_KEY_CMD_V20 = [0xa3, 0x30, 0x00] +SEND_KEY_CMD_V23 = [0xa3, 0x38, 0x00] + [0x00] * (0x38) +ERASE_CHIP_CMD_V2 = [0xa4, 0x01, 0x00, 0x08] +WRITE_CMD_V2 = [0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] +VERIFY_CMD_V2 = [0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] +READ_CFG_CMD_V2 = [0xa7, 0x02, 0x00, 0x1f, 0x00] + +CH55X_IC_REF = {} +CH55X_IC_REF[0x51] = {'device_name': 'CH551', 'device_flash_size': 10240, 'device_dataflash_size': 128, 'chip_id': 0x51} +CH55X_IC_REF[0x52] = {'device_name': 'CH552', 'device_flash_size': 16384, 'device_dataflash_size': 128, 'chip_id': 0x52} +CH55X_IC_REF[0x53] = {'device_name': 'CH553', 'device_flash_size': 10240, 'device_dataflash_size': 128, 'chip_id': 0x53} +CH55X_IC_REF[0x54] = {'device_name': 'CH554', 'device_flash_size': 14336, 'device_dataflash_size': 128, 'chip_id': 0x54} +CH55X_IC_REF[0x59] = {'device_name': 'CH559', 'device_flash_size': 61440, 'device_dataflash_size': 128, 'chip_id': 0x59} +#============================================= + +def __get_dfu_device(idVendor=DFU_ID_VENDOR, idProduct=DFU_ID_PRODUCT): + dev = usb.core.find(idVendor=idVendor, idProduct=idProduct) + if dev is None: + return (None, 'NO_DEV_FOUND') + try: + if dev.is_kernel_driver_active(0): + try: + dev.detach_kernel_driver(0) + except usb.core.USBError: + return (None, 'USB_ERROR_CANNOT_DETACH_KERNEL_DRIVER') + except: + pass #Windows dont need detach + + try: + dev.set_configuration() + except usb.core.USBError: + return (None, 'USB_ERROR_CANNOT_SET_CONFIG') + + try: + usb.util.claim_interface(dev, 0) + except usb.core.USBError: + return (None, 'USB_ERROR_CANNOT_CLAIM_INTERFACE') + + return (dev, '') + +def __detect_ch55x_v2(dev): + dev.write(EP_OUT_ADDR, DETECT_CHIP_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + try: + return CH55X_IC_REF[ret[4]] + except KeyError: + return None + +def __read_cfg_ch55x_v2(dev): + dev.write(EP_OUT_ADDR, READ_CFG_CMD_V2) + ret = dev.read(EP_IN_ADDR, 30, USB_MAX_TIMEOUT) + + ver_str = 'V%d.%d%d' % (ret[19], ret[20], ret[21]) + chk_sum = (ret[22] + ret[23] + ret[24] + ret[25]) % 256 + + return (ver_str, chk_sum) + +def __write_key_ch55x_v20(dev, chk_sum): + SEND_KEY_CMD_V20 = SEND_KEY_CMD_V20 + [chk_sum] * 0x30 + + dev.write(EP_OUT_ADDR, SEND_KEY_CMD_V20) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + if ret[3] == 0: + return True + else: + return None + +def __write_key_ch55x_v23(dev): + dev.write(EP_OUT_ADDR, SEND_KEY_CMD_V23) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + if ret[3] == 0: + return True + else: + return None + +def __erase_chip_ch55x_v2(dev): + dev.write(EP_OUT_ADDR, ERASE_CHIP_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + if ret[3] == 0: + return True + else: + return None + +def __write_flash_ch55x_v20(dev, chk_sum, chip_id, payload): + payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number + file_length = len(payload) + + for index in range(file_length): + if index % 8 == 7: + payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256 + + left_len = file_length + curr_addr = 0 + + while curr_addr < file_length: + + if left_len >= 56: + pkt_length = 56 + else: + pkt_length = left_len + + __WRITE_CMD_V2 = WRITE_CMD_V2 + __WRITE_CMD_V2[1] = pkt_length + 5 + __WRITE_CMD_V2[3] = curr_addr % 256 + __WRITE_CMD_V2[4] = (curr_addr >> 8) % 256 + __WRITE_CMD_V2[7] = left_len % 256 + __WRITE_CMD_V2 = __WRITE_CMD_V2 + payload[curr_addr:curr_addr+pkt_length] + + dev.write(EP_OUT_ADDR, __WRITE_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + + curr_addr = curr_addr + pkt_length + left_len = left_len - pkt_length + + if ret[4] != 0x00: + return None + + return file_length + +def __write_flash_ch55x_v23(dev, chk_sum, chip_id, payload): + payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number + file_length = len(payload) + + for index in range(file_length): + if index % 8 == 7: + payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256 + else: + payload[index] = (payload[index] ^ chk_sum) % 256 + + left_len = file_length + curr_addr = 0 + + while curr_addr < file_length: + + if left_len >= 56: + pkt_length = 56 + else: + pkt_length = left_len + + __WRITE_CMD_V2 = WRITE_CMD_V2 + __WRITE_CMD_V2[1] = pkt_length + 5 + __WRITE_CMD_V2[3] = curr_addr % 256 + __WRITE_CMD_V2[4] = (curr_addr >> 8) % 256 + __WRITE_CMD_V2[7] = left_len % 256 + __WRITE_CMD_V2 = __WRITE_CMD_V2 + payload[curr_addr:curr_addr+pkt_length] + + dev.write(EP_OUT_ADDR, __WRITE_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + + curr_addr = curr_addr + pkt_length + left_len = left_len - pkt_length + + if ret[4] != 0x00: + return None + + return file_length + +def __verify_flash_ch55x_v20(dev, chk_sum, chip_id, payload): + payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number + file_length = len(payload) + + for index in range(file_length): + if index % 8 == 7: + payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256 + + left_len = file_length + curr_addr = 0 + + while curr_addr < file_length: + + if left_len >= 56: + pkt_length = 56 + else: + pkt_length = left_len + + __VERIFY_CMD_V2 = VERIFY_CMD_V2 + __VERIFY_CMD_V2[1] = pkt_length + 5 + __VERIFY_CMD_V2[3] = curr_addr % 256 + __VERIFY_CMD_V2[4] = (curr_addr >> 8) % 256 + __VERIFY_CMD_V2[7] = left_len % 256 + __VERIFY_CMD_V2 = __VERIFY_CMD_V2 + payload[curr_addr:curr_addr+pkt_length] + + dev.write(EP_OUT_ADDR, __VERIFY_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + + curr_addr = curr_addr + pkt_length + left_len = left_len - pkt_length + + if ret[4] != 0x00: + return None + + return file_length + +def __verify_flash_ch55x_v23(dev, chk_sum, chip_id, payload): + payload = payload + [0] * ((math.ceil(len(payload) / 56) * 56) - len(payload)) # Payload needs to be padded, 56 is a good number + file_length = len(payload) + + for index in range(file_length): + if index % 8 == 7: + payload[index] = (payload[index] ^ ((chk_sum + chip_id) % 256)) % 256 + else: + payload[index] = (payload[index] ^ chk_sum) % 256 + + left_len = file_length + curr_addr = 0 + + while curr_addr < file_length: + + if left_len >= 56: + pkt_length = 56 + else: + pkt_length = left_len + + __VERIFY_CMD_V2 = VERIFY_CMD_V2 + __VERIFY_CMD_V2[1] = pkt_length + 5 + __VERIFY_CMD_V2[3] = curr_addr % 256 + __VERIFY_CMD_V2[4] = (curr_addr >> 8) % 256 + __VERIFY_CMD_V2[7] = left_len % 256 + __VERIFY_CMD_V2 = __VERIFY_CMD_V2 + payload[curr_addr:curr_addr+pkt_length] + + dev.write(EP_OUT_ADDR, __VERIFY_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + + curr_addr = curr_addr + pkt_length + left_len = left_len - pkt_length + + if ret[4] != 0x00: + return None + + return file_length + +def __end_flash_ch55x_v2(dev): + dev.write(EP_OUT_ADDR, END_FLASH_CMD_V2) + ret = dev.read(EP_IN_ADDR, 6, USB_MAX_TIMEOUT) + if ret[4] != 0x00: + return None + else: + return True + +def __restart_run_ch55x_v2(dev): + dev.write(EP_OUT_ADDR, RESET_RUN_CMD_V2) + +def __main(): + parser = argparse.ArgumentParser(description="USBISP Tool For WinChipHead CH55x.") + parser.add_argument('-f', '--file', type=str, default='', help="The target file to be flashed.") + parser.add_argument('-r', '--reset_after_flash', action='store_true', default=False, help="Reset after finsh flash.") + args = parser.parse_args() + + ret = __get_dfu_device() + if ret[0] is None: + sys.exit(ret[1]) + dev = ret[0] + + ret = __detect_ch55x_v2(dev) + if ret is None: + print('Unable to detect CH55x.') + print('Found %s.' % ret['device_name']) + chip_id = ret['chip_id'] + + ret = __read_cfg_ch55x_v2(dev) + chk_sum = ret[1] + + print('BTVER: %s.' % ret[0]) + + if args.file != '': + payload = list(open(args.file, 'rb').read()) + if ret[0] == 'V2.30': + ret = __write_key_ch55x_v20(dev, chk_sum) + if ret is None: + sys.exit('Failed to write key to CH55x.') + + ret = __erase_chip_ch55x_v2(dev) + if ret is None: + sys.exit('Failed to erase CH55x.') + + ret = __write_flash_ch55x_v20(dev, chk_sum, chip_id, payload) + if ret is None: + sys.exit('Failed to flash firmware of CH55x.') + + ret = __verify_flash_ch55x_v20(dev, chk_sum, chip_id, payload) + if ret is None: + sys.exit('Failed to verify firmware of CH55x.') + else: + if ret[0] == 'V2.31': + ret = __write_key_ch55x_v23(dev) + if ret is None: + sys.exit('Failed to write key to CH55x.') + + ret = __erase_chip_ch55x_v2(dev) + if ret is None: + sys.exit('Failed to erase CH55x.') + + ret = __write_flash_ch55x_v23(dev, chk_sum, chip_id, payload) + if ret is None: + sys.exit('Failed to flash firmware of CH55x.') + + ret = __verify_flash_ch55x_v23(dev, chk_sum, chip_id, payload) + if ret is None: + sys.exit('Failed to verify firmware of CH55x.') + else: + sys.exit('Bootloader version not supported.') + + ret = __end_flash_ch55x_v2(dev) + if ret is None: + sys.exit('Failed to end flash process.') + + print('Flash done.') + + if args.reset_after_flash: + __restart_run_ch55x_v2(dev) + print('Restart and run.') + + + +if __name__ == '__main__': + __main()