commit 54f4eabff9744276be2d438c46d4f705012b76c8 Author: kicer Date: Thu Jun 3 01:03:42 2021 +0800 v0.1.2, init diff --git a/README.md b/README.md new file mode 100644 index 0000000..03b5f67 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# HC32FLASH +HC32xx flash downloader with USB-TTL adapter. + + +### Usage +``` +usage: hc32flash.py [-h] [-l] [-d device] [-p port] [-b baudrate] [-u] [-L] + [-R] [-w ] [-r ] [-v ] + +HC32xx Flash Downloader. + +optional arguments: + -h, --help show this help message and exit + -l, --list List support device + -d device Device name, default HC32F003 + -p port Serial port, default /dev/ttyUSB0 + -b baudrate Serial baudrate + -u, --unlock Unlock. Erase device when locked + -L, --lock Lock. SWD port disabled + -R, --reboot Reboot device + -w Write data from file to device + -r Read data from device to file + -v Verify chksum data in device against file +``` + + +### Tested Device +[ ] HC32D391 +[ ] HC32F4A0 +[ ] HC32F146x8/HC32M140x8 +[ ] HC32F146xA/HC32M140xA +[ ] HC32F120 +[ ] HC32F460xExx +[ ] HC32L13xx8/HC32F030x8 +[ ] HC32L15xx8 +[ ] HC32L15xxA +[*] HC32L110x4xx/HC32F003x4xx +[*] HC32L110x6xx/HC32F005x6xx +[ ] HC32M120 +[ ] HC32x19xxCxx +[ ] HC32x07xxAxx/HC32x17xxAxx + + + +### Hack Tools +* HDSC ISP V2.07, hdsc.exe +* ILSpy +* Logic + + +### TODO +* Some device need Crystal Freq setting to load ramcode +* Auto Number function +* More device test diff --git a/docs/HC32F003系列的FLASH串行编程.pdf b/docs/HC32F003系列的FLASH串行编程.pdf new file mode 100644 index 0000000..a6b5932 Binary files /dev/null and b/docs/HC32F003系列的FLASH串行编程.pdf differ diff --git a/docs/desktop.ini b/docs/desktop.ini new file mode 100644 index 0000000..0446675 --- /dev/null +++ b/docs/desktop.ini @@ -0,0 +1,2 @@ +[LocalizedFileNames] +FLASHб.pdf=@FLASHб.pdf,0 diff --git a/docs/pkg1.logicdata b/docs/pkg1.logicdata new file mode 100644 index 0000000..2dceba7 Binary files /dev/null and b/docs/pkg1.logicdata differ diff --git a/docs/pkg2.logicdata b/docs/pkg2.logicdata new file mode 100644 index 0000000..eabcf8e Binary files /dev/null and b/docs/pkg2.logicdata differ diff --git a/dump.md b/dump.md new file mode 100644 index 0000000..5fee19a --- /dev/null +++ b/dump.md @@ -0,0 +1,126 @@ +### bootloader启动 +1. 复位RESET引脚约18ms +2. pc循环发送0x18 0xFF共10组 +3. mcu应答0x11共9组 +4. 检查mcu状态 +> 01 FC 0B 00 00 02 00 00 00 0A +< 01 02 EE FF ED (加密) +< 01 02 FF FF FE (未加密) +5. 加密则执行解密动作(擦除FLASH上内容) +> B5 34 84 52 BF +< 01 +6. 准备下载ramcode +> 00 00 00 00 20 A4 07 00 00 CB +< 01 +7. 开始下载ramcode +> B8 0A ... 9F +< 01 +8. 启动ramcode +> C0 00 00 00 00 00 00 00 00 C0 +< happybaby 00 00 + + +### ramcode启动 +启动后mcu端发送字符串 *happybaby* +> 68 61 70 70 79 62 61 62 79 00 00 + +0. 修改通信用波特率 +- 修改成9600bps (00 00 80 25) +> 49 01 00 00 00 00 04 00 80 25 00 00 F3 +- 修改成115200bps (00 01 C2 00) +> 49 01 00 00 00 00 04 00 00 C2 01 00 11 +- 应答 +< 49 00 00 00 00 00 00 00 49 + +1. 请求0x20字节数据 (指定波特率) +> 49 05 00 00 00 00 20 00 6E +< 49 00 00 00 00 00 20 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF EF 38 + +2. 校验错误 +> 49 05 00 00 00 00 20 00 5E +< 49 01 00 00 00 00 00 00 4A + +3. 请求0x10字节数据 +> 49 05 00 00 00 00 10 00 5E +< 49 00 00 00 00 00 10 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FE 48 + +4. 请求0x0200字节数据 +> 49 05 00 00 00 00 00 02 50 +> 49 05 00 02 00 00 00 02 52 +> ... +> 49 05 00 3C 00 00 00 02 8C +> 49 05 00 3E 00 00 00 02 8E + +5. 空白检测 +> 49 07 00 00 00 00 04 00 00 40 00 00 94 +< 49 00 00 00 00 00 01 00 01 4B +> 49 07 00 00 00 00 04 00 00 02 00 00 56 +< 49 00 0F 00 00 00 01 00 00 59 + +6. 整片擦除 +> 49 02 00 00 00 00 00 00 4B + +7. 页擦除(1sector=512Bytes) +> 49 03 00 00 00 00 00 00 4C +< 49 00 00 00 00 00 00 00 49 +> 49 03 00 02 00 00 00 00 4E +< 49 00 00 02 00 00 00 00 4B +> ... +> 49 03 00 3E 00 00 00 00 8A +> 49 00 00 3E 00 00 00 00 87 + +8. 编程 +> 49 04 00 00 00 00 20 00 CHK +< 49 00 00 00 00 00 00 00 49 + +9. 校验,所有数据的累加和取最后两个字节 +> 49 06 00 00 00 00 04 00 20 00 00 00 73 +< 49 00 00 00 00 00 02 00 CF 1F 39 +> 49 06 00 00 00 00 04 00 00 40 00 00 93 +< 49 00 00 00 00 00 02 00 00 C0 0B + + +#### 读取FLASH数据 +> 49 05 A0 A1 A2 A3 L0 L1 CHK +< 49 00 A0 A1 A2 A3 L0 L1 CHK +* 49 05: 协议头 +* A3..0: 起始地址 +* L1..0: 字节长度 +* data: FLASH数据 + +#### 按页擦除FLASH数据 +> 49 03 A0 A1 A2 A3 00 00 CHK +< 49 00 A0 A1 A2 A3 00 00 CHK +* 49 03: 协议头 +* A3..0: 起始地址 + +#### 整片擦除FLASH数据,耗时约40ms +> 49 02 00 00 00 00 00 00 4B +< 49 00 00 00 00 00 00 00 49 +* 49 02: 协议头 + + +#### 空白检测 +> 49 07 00 00 00 00 04 00 A0 A1 A2 A3 CHK +< 49 C1 C0 00 00 00 01 00 00 CHK +* 49 07: 协议头 +* C1..0: 检测值 +* A3..0: 字节长度 + +#### 校验 +> 49 06 00 00 00 00 04 00 L0 L1 L2 L3 CHK +< 49 00 00 00 00 00 02 00 S1 S0 CHK +* 49 06: 协议头 +* S1..0: 校验和 +* L3..0: 字节长度 + +#### 编程 +> 49 04 A0 A1 A2 A3 L0 L1 CHK +< 49 00 A0 A1 A2 A3 00 00 CHK +* 49 04: 协议头 +* A3..0: 起始地址 +* L1..0: 字节长度,最大支持0x40字节 + +#### 加密 +> 49 09 00 00 00 00 00 00 52 +< 49 00 00 00 00 00 00 00 49 diff --git a/hc32flash.py b/hc32flash.py new file mode 100644 index 0000000..57cafa8 --- /dev/null +++ b/hc32flash.py @@ -0,0 +1,472 @@ +import os, sys, time, struct +import serial +import argparse + +version = "0.1.2" + +HDSC = { + 'HC32D391': { + 'MCUName': "HC32D391", + 'FrequecyList': ["1000000", "500000", "256000", "128000", "115200"], + 'StartAddress': "00000000", + 'PageSize': "8192", + 'PageCount': "64", + 'FlashSize': "512K", + 'BootloaderBaudrate': 115200, + 'RamCodeBinFile': "m_flash.hc010", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA13) <---> Serial.RXD\nMCU.RXD(PA14) <---> Serial.TXD\nMCU.MODE <---> MCU.GND\n", + }, + 'HC32F4A0': { + 'MCUName': "HC32F4A0", + 'FrequecyList': ["1000000", "500000", "256000", "128000", "115200"], + 'StartAddress': "00000000", + 'PageSize': "8192", + 'PageCount': "256", + 'FlashSize': "2M", + 'BootloaderBaudrate': 115200, + 'RamCodeBinFile': "m_flash.hc020", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA13,PB10) <---> Serial.RXD\nMCU.RXD(PA14,PB11) <---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32F146x8/HC32M140x8': { + 'MCUName': "HC32F146x8/HC32M140x8", + 'FrequecyList': ["Internal CR", "4MHz", "6MHz", "8MHz", "10MHz", "12MHz", "16MHz", "18MHz", "20MHz", "24MHz", "32MHz"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "128", + 'FlashSize': "64K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc001", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P11) <---> Serial.RXD\nMCU.RXD(P12) <---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32F146xA/HC32M140xA': { + 'MCUName': "HC32F146xA/HC32M140xA", + 'FrequecyList': ["Internal CR", "4MHz", "6MHz", "8MHz", "10MHz", "12MHz", "16MHz", "18MHz", "20MHz", "24MHz", "32MHz"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "256", + 'FlashSize': "128K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc001", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P11) <---> Serial.RXD\nMCU.RXD(P12) <---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32F120': { + 'MCUName': "HC32F120", + 'FrequecyList': ["1000000"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "64", + 'FlashSize': "32K", + 'BootloaderBaudrate': 1000000, + 'RamCodeBinFile': "m_flash.hc012", + 'WritePacketSize': 512, + 'IspConnection': "请确认目标芯片与转接板的连接:\n半双工:VCC,GND,TOOL0,NRST\n全双工:VCC,GND,TXD,RXD,TOOL0,NRST\n", + }, + 'HC32F460xExx': { + 'MCUName': "HC32F460xExx", + 'FrequecyList': ["1000000", "500000", "256000", "128000", "115200"], + 'StartAddress': "00000000", + 'PageSize': "8192", + 'PageCount': "64", + 'FlashSize': "512K", + 'BootloaderBaudrate': 115200, + 'RamCodeBinFile': "m_flash.hc010", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA13) <---> Serial.RXD\nMCU.RXD(PA14) <---> Serial.TXD\nMCU.MODE <---> MCU.GND\n", + }, + 'HC32L13xx8/HC32F030x8': { + 'MCUName': "HC32L13xx8/HC32F030x8", + 'FrequecyList': ["1000000", "256000", "128000", "115200", "76800", "38400", "19200", "9600"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "128", + 'FlashSize': "64K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc006", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA09/PA14)<---> Serial.RXD\nMCU.RXD(PA10/PA13)<---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32L15xx8': { + 'MCUName': "HC32L15xx8", + 'FrequecyList': ["Internal CR", "4MHz", "6MHz", "8MHz", "10MHz", "12MHz", "16MHz", "18MHz", "20MHz", "24MHz", "32MHz"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "128", + 'FlashSize': "64K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc001", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P12) <---> Serial.RXD\nMCU.RXD(P11) <---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32L15xxA': { + 'MCUName': "HC32L15xxA", + 'FrequecyList': ["Internal CR", "4MHz", "6MHz", "8MHz", "10MHz", "12MHz", "16MHz", "18MHz", "20MHz", "24MHz", "32MHz"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "256", + 'FlashSize': "128K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc001", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P12) <---> Serial.RXD\nMCU.RXD(P11) <---> Serial.TXD\nMCU.MODE <---> MCU.VCC\n", + }, + 'HC32L110x4xx/HC32F003x4xx': { + 'MCUName': "HC32L110x4xx/HC32F003x4xx", + 'FrequecyList': ["691200", "230400", "115200", "38400", "19200", "9600"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "32", + 'FlashSize': "16K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc005", + 'WritePacketSize': 64, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P31/P35) <---> Serial.RXD\nMCU.RXD(P27/P36) <---> Serial.TXD\nMCU.RESET <---> Serial.RTS/DTR\n", + }, + 'HC32L110x6xx/HC32F005x6xx': { + 'MCUName': "HC32L110x6xx/HC32F005x6xx", + 'FrequecyList': ["691200", "460800", "230400", "115200", "38400", "19200", "9600"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "64", + 'FlashSize': "32K", + 'BootloaderBaudrate': 9600, + 'RamCodeBinFile': "m_flash.hc005", + 'WritePacketSize': 64, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(P31/P35) <---> Serial.RXD\nMCU.RXD(P27/P36) <---> Serial.TXD\nMCU.RESET <---> Serial.RTS/DTR\n", + }, + 'HC32M120': { + 'MCUName': "HC32M120", + 'FrequecyList': ["1000000"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "32", + 'FlashSize': "16K", + 'BootloaderBaudrate': 1000000, + 'RamCodeBinFile': "m_flash.hc013", + 'WritePacketSize': 512, + 'IspConnection': "请确认目标芯片与转接板的连接:\n半双工:VCC,GND,TOOL0,NRST\n全双工:VCC,GND,TXD,RXD,TOOL0,NRST\n", + }, + 'HC32x19xxCxx': { + 'MCUName': "HC32x19xxCxx", + 'FrequecyList': ["1000000", "256000", "128000", "115200", "76800", "38400", "19200"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "512", + 'FlashSize': "256K", + 'BootloaderBaudrate': 115200, + 'RamCodeBinFile': "m_flash.hc015", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA14) <---> Serial.RXD\nMCU.RXD(PA13) <---> Serial.TXD\nMCU.BOOT0 <---> MCU.VCC\n", + }, + 'HC32x07xxAxx/HC32x17xxAxx': { + 'MCUName': "HC32x07xxAxx/HC32x17xxAxx", + 'FrequecyList': ["1000000", "256000", "128000", "115200", "76800", "38400", "19200"], + 'StartAddress': "00000000", + 'PageSize': "512", + 'PageCount': "256", + 'FlashSize': "128K", + 'BootloaderBaudrate': 115200, + 'RamCodeBinFile': "m_flash.hc008", + 'WritePacketSize': 512, + 'IspConnection': "MCU.VCC <---> Serial.VCC\nMCU.GND <---> Serial.GND\nMCU.TXD(PA14) <---> Serial.RXD\nMCU.RXD(PA13) <---> Serial.TXD\nMCU.BOOT0 <---> MCU.VCC\n", + }, +} + + +class TransportError(Exception): + """Custom exception to represent errors with a transport + """ + def __init__(self, message): + self.message = message + + def __str__(self): + return self.message + + +class SerialTransport(): + def __init__(self, port, baud): + self.port = port + self.baud = baud + self.serial = None + + try: + self.serial = serial.Serial(port, baud) + except serial.SerialException as e: + raise TransportError(str(e)) from None + + self.serial.timeout = 1 + #self.serial.write_timeout = 0 + + def init_baud(self, baud): + self.serial.baudrate = baud + + def write(self, data): + if self.serial.inWaiting() > 0: + self.serial.flushInput() + self.serial.write(data) + + def read(self, length): + return self.serial.read(length) + + def close(self): + self.serial.flush() + self.serial.close() + + def goto_bootloader(self): + self.serial.rts = True + time.sleep(0.1) + self.serial.write_timeout = 0 + self.write(b'\x18\xFF'*10) + self.serial.rts = False + ack = self.read(10) + self.serial.write_timeout = None + return ack[-6:] == b'\x11'*6 + + def check_lock(self): + self.write(b'\x01\xFC\x0B\x00\x00\x02\x00\x00\x00\x0A') + ack = self.read(5) + if len(ack)==5 and ack[:2]==b'\x01\x02': + return ack[2:4] != b'\xFF\xFF' + return None + + def unlock(self): + self.write(b'\xB5\x34\x84\x52\xBF') + return self.read(1) == b'\x01' + + def load_ramcode(self, _f): + with open(_f, "rb") as fr: + dat = fr.read() + fr.close() + size = len(dat) + addr = 0x20000000 + pkg = struct.pack('= 0: + args.dev,_ = dev,dev + break + if not _: + sys.stdout.write("Invalid Device name '%s'.\n\nList of support device:\n" % args.dev) + args.list = True + + if args.list: + for dev in HDSC.keys(): + sys.stdout.write("%-28s %-8s %s\n" % (dev, HDSC[dev]['FlashSize'], HDSC[dev]['BootloaderBaudrate'])) + sys.exit(0) + + # mcu info + hc32xx = HDSC[args.dev] + args.baud = args.baud or hc32xx['BootloaderBaudrate'] + sys.stdout.write('Device: %s\n' % args.dev) + sys.stdout.write('Boot Baud: %s\n' % args.baud) + sys.stdout.write('Page Size: %s\n' % hc32xx['PageSize']) + sys.stdout.write('Page Count: %s\n' % hc32xx['PageCount']) + sys.stdout.write('Flash Size: %s\n' % hc32xx['FlashSize']) + #sys.stdout.write('RameCode: %s\n' % hc32xx['RamCodeBinFile']) + sys.stdout.write('\n%s\n' % hc32xx['IspConnection']) + # global vars + transport = SerialTransport(args.port, hc32xx['BootloaderBaudrate']) + base_dir = os.path.dirname(os.path.realpath(__file__)) + + # stage 1. goto bootload + sys.stdout.write("Stage 1. Goto bootloader: ") + sys.stdout.flush() + if transport.goto_bootloader(): + sys.stdout.write("succ\n") + else: + sys.stdout.write("error\n") + sys.exit(1) + + # state 2. Check device + sys.stdout.write("Stage 2. Check device: ") + if transport.check_lock(): + if args.unlock and transport.unlock(): + sys.stdout.write("unlock\n") + else: + sys.stdout.write("%s\n" % args.unlock and "locked" or "unlock failed") + sys.exit(1) + else: + sys.stdout.write("pass\n") + + # stage 3. load ramcode + sys.stdout.write("Stage 3. Load ramcode: ") + sys.stdout.flush() + _f = os.path.join(base_dir, 'hdsc', 'HDSC.'+hc32xx['RamCodeBinFile']) + if transport.load_ramcode(_f): + sys.stdout.write("%s\n" % hc32xx['RamCodeBinFile']) + else: + sys.stdout.write("error\n") + sys.exit(1) + + # stage 4. run ramcode + sys.stdout.write("Stage 4. Run ramcode: %s\n" % + transport.run_ramcode()) + + # stage 5. set baud + sys.stdout.write("Stage 5. Set baud: ") + if transport.set_baud(args.baud): + sys.stdout.write("%s\n\n" % args.baud) + transport.init_baud(args.baud) + else: + sys.stdout.write("error\n") + sys.exit(1) + + # write, with erase + if args.wfile: + with open(args.wfile, "rb") as fs: + sys.stdout.write("[ WRITE] ") + if not transport.flash_erase(): + sys.stderr.write("flash erase error") + sys.exit(1) + psize = int(hc32xx['WritePacketSize']) + addr0 = int(hc32xx['StartAddress'], 16) + addr = addr0 + while True: + _last = False + dat = fs.read(psize) + if len(dat) == 0: + fs.close() + break + elif len(dat) < psize: + dat = dat + b'\xFF'*(psize-len(dat)) + _last = True + if not transport.flash_write(addr, dat): + sys.stderr.write("flash write error: 0x%08X\n" % addr) + sys.exit(1) + else: + sys.stdout.write("."); sys.stdout.flush() + addr += psize + if _last: + sys.stdout.write(" ok\n") + fs.close() + if not args.vfile: + args.vfile = args.wfile + break + + # read to file + if args.rfile: + with open(args.rfile, "wb") as fs: + sys.stdout.write("[ READ ] ") + psize = int(hc32xx['PageSize']) + pcnt = int(hc32xx['PageCount']) + addr0 = int(hc32xx['StartAddress'], 16) + addr = addr0 + for _ in range(pcnt): + dat = transport.flash_read(addr, psize) + if not dat: + sys.stderr.write("flash read error: 0x%08X\n" % addr) + sys.exit(1) + else: + fs.write(dat) + sys.stdout.write("."); sys.stdout.flush() + addr += psize + sys.stdout.write(" ok\n") + fs.close() + + # verify chksum + if args.vfile: + with open(args.vfile, "rb") as fs: + sys.stdout.write("[VERIFY] ") + dat = fs.read() + fs.close() + ack = transport.flash_verify(len(dat)) + chk0,chk1 = sum(dat)&0xFFFF,None + if ack: + chk1 = struct.unpack('