├── README.md └── quick.py /README.md: -------------------------------------------------------------------------------- 1 | pyusb wrappers for using a ch341 in i2c mode. 2 | 3 | Somewhat based on git@github.com:commandtab/ch341eeprom.git but more openended, 4 | and not feeling the need to do everything in C. 5 | 6 | usb2i2c: https://code.google.com/p/usb2i2c/ seems to be identical protocols, 7 | possibly earlier versions of the same chips. (And there's code available! 8 | whee!) 9 | 10 | Interesting ideas on directly getting/setting the status of the pins via 11 | "CH341GetInput"/"CH341SetOutput" see if that method is in the usb2i2c code? 12 | http://common-codebase.googlecode.com/svn-history/r70/trunk/others/python_test/i2c/CH341.py 13 | 14 | More links here: 15 | https://hackingbtbusinesshub.wordpress.com/category/ch341/ 16 | 17 | Lots more datasheets and links down a bit on this thread: 18 | http://www.kenotrontv.ru/forum/topic/199-usb-%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0%D1%82%D0%BE%D1%80-%D0%B4%D0%BB%D1%8F-flash-%D0%B8-eeprom-usb-bus-convert-chip-ch341/ 19 | -------------------------------------------------------------------------------- /quick.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Karl Palsson, October 2014 3 | # Considered to be released under your choice of MIT/Apache2/BSD 2 clause 4 | # 5 | # Provides generic hooks for reading/writing i2c via a CH341 in i2c/mem/epp 6 | # mode. Has only been _currently_ tested with a CH341A device. 7 | import logging 8 | 9 | logging.basicConfig(level=logging.DEBUG) 10 | log = logging.getLogger("py2c-ch34x") 11 | 12 | import struct 13 | import usb.core 14 | import usb.util 15 | 16 | # These definitions are from ch341eeprom, and from ch34x vendor serial uart driver 17 | # They're all just magic numbers, so talk to your lawyers about who owns what 18 | # spellings of the names. (No debate that work was done by people to recover these 19 | # And that was not all me) 20 | # Oh, and it's all the same as https://code.google.com/p/usb2i2c 21 | # the definitions are the same! 22 | magic_read_setup = "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x06\x04\x00\x00" \ 23 | "\x00\x00\x00\x00\x40\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \ 24 | "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \ 25 | "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \ 26 | "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \ 27 | "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \ 28 | "\xaa\xdf\xc0\x75\x00" 29 | 30 | class CtrlCommands(): 31 | """ 32 | This is just usb standard stuff... 33 | """ 34 | WRITE_TYPE = 0x40 35 | READ_TYPE = 0xc0 36 | 37 | class VendorCommands(): 38 | READ_REG = 0x95 39 | WRITE_REG = 0x9a 40 | SERIAL = 0xa1 41 | PRINT = 0xa3 42 | MODEM = 0xa4 43 | MEMW = 0xa6 # aka mCH341_PARA_CMD_W0 44 | MEMR = 0xac # aka mCH341_PARA_CMD_R0 45 | SPI = 0xa8 46 | SIO = 0xa9 47 | I2C = 0xaa 48 | UIO = 0xab 49 | I2C_STATUS = 0x52 50 | I2C_COMMAND = 0x53 51 | VERSION = 0x5f # at least in serial mode? 52 | 53 | class I2CCommands(): 54 | # These are all from ch341dll.h, mostly untested 55 | """ 56 | After STA, you can insert MS|millis, and US|usecs to insert a delay 57 | (you can insert multiple) 58 | MS|0 = 250ms wait, 59 | US|0 = ~260usecs? 60 | US|10 is ~10usecs, 61 | be careful, US|20 = MS|4! US|40 = ? (switched back to 20khz mode) 62 | """ 63 | STA = 0x74 64 | STO = 0x75 65 | OUT = 0x80 66 | IN = 0xc0 67 | MAX = 32 # min (0x3f, 32) ?! (wrong place for this) 68 | SET = 0x60 # bit 7 apparently SPI bit order, bit 2 spi single vs spi double 69 | US = 0x40 # vendor code uses a few of these in 20khz mode? 70 | MS = 0x50 71 | DLY = 0x0f 72 | END = 0x00 # Finish commands with this. is this really necessary? 73 | 74 | class PinState(): 75 | """ 76 | This is kinda gross, should be a more pythonic way of doing this? 77 | I've verified this works on a few pins, not sure about all of them, d7..d0 work. 78 | 79 | """ 80 | ERR = 0x100 # read-write 81 | PEMP = 0x200 # read-write 82 | INT = 0x400 # read-write 83 | SLCT = 0x800 # read-write 84 | WAIT = 0x2000 # read-write 85 | DATAS = 0x4000 # "write readable only" ?! 86 | ADDRS = 0x8000 # "write readable only" ?! 87 | RESET = 0x10000 # "just write" 88 | WRITE = 0x20000 # "just write" 89 | SCL = 0x400000 # read-only 90 | SDA = 0x800000 # read-only 91 | DXX = 0xff000000 92 | def __init__(self, bits): 93 | if (type(bits) != type(int)): 94 | # assume it's the raw field from reading i2c status 95 | out = struct.unpack_from(">IH", bytearray(bits)) 96 | bits = out[0] 97 | # TODO - no clue what the last word is for. 98 | s = [] 99 | if bits & self.ERR: s += ["ERR"] 100 | if bits & self.PEMP: s += ["PEMP"] 101 | if bits & self.INT: s += ["INT"] 102 | if bits & self.SLCT: s += ["SLCT"] 103 | if bits & self.WAIT: s += ["WAIT"] 104 | if bits & self.DATAS: s += ["DATAS"] 105 | if bits & self.ADDRS: s += ["ADDRS"] 106 | if bits & self.RESET: s += ["RESET"] 107 | if bits & self.WRITE: s += ["WRITE"] 108 | if bits & self.SCL: s += ["SCL"] 109 | if bits & self.SDA: s += ["SDA"] 110 | if bits & self.DXX: 111 | datax = (bits & self.DXX) >> 24 112 | for i in range(8): 113 | if (1<> 8, dev.bcdDevice & 0xff) 137 | # These devices only have one that I know of... 138 | assert(dev.bNumConfigurations == 1) 139 | dev.set_configuration() 140 | self.dev = dev 141 | # i2c vs epp vs mem mode? or is this fixed? 142 | log.debug("Device protocol? %d", dev.bDeviceProtocol) 143 | 144 | #ch34x_vendor_read( VENDOR_VERSION, 0x0000, 0x0000, serial, buf, 0x02 ); 145 | #static int ch34x_vendor_read( __u8 request,__u16 value, __u16 index, 146 | # struct usb_serial *serial, unsigned char *buf, __u16 len ) 147 | #retval = usb_control_msg( serial->dev, usb_rcvctrlpipe(serial->dev, 0), 148 | # request, VENDOR_READ_TYPE, value, index, buf, len, 1000 ); 149 | vv = self.vendor_read(VendorCommands.VERSION, 0, 0, 2) 150 | log.info("vendor version = %d.%d (%x.%x)", vv[0], vv[1], vv[0], vv[1]) 151 | iss = self.vendor_read(VendorCommands.I2C_STATUS, 0, 0, 8) 152 | log.debug("i2c status = %s, pins = %s", iss, PinState(iss)) 153 | 154 | def set_speed(self, speed=100): 155 | """ 156 | Set the i2c speed desired 157 | :param speed: in khz, will round down to 20, 100, 400, 750 158 | :return: na 159 | 20 and 100 work well, 400 is not entirely square, but I don't think it's meant to be 160 | 750 is closer to 1000 for bytes, but slower around acks and each byte start. 161 | All seem to work well. 162 | """ 163 | sbit = 1 164 | if speed < 100: 165 | sbit = 0 166 | elif speed < 400: 167 | sbit = 1 168 | elif speed < 750: 169 | sbit = 2 170 | else: 171 | sbit = 3 172 | 173 | # TODO ^ how does linux handle this sort of stuff normally? 174 | # logging when it doesn't match? 175 | cmd = [VendorCommands.I2C, I2CCommands.SET | sbit, I2CCommands.END] 176 | count = self.dev.write(self.EP_OUT, cmd) 177 | assert count == len(cmd), "Failed to write cmd to usb" 178 | 179 | def i2c_start(self): 180 | """ 181 | Just a start bit... 182 | :return: 183 | """ 184 | cmd = [VendorCommands.I2C, I2CCommands.STA, I2CCommands.END] 185 | log.debug("writing: %s", [hex(cc) for cc in cmd]) 186 | cnt = self.dev.write(self.EP_OUT, cmd) 187 | assert(cnt == len(cmd)) 188 | 189 | def i2c_stop(self): 190 | # This doesn't seem to be very reliable :( 191 | cmd = [VendorCommands.I2C, I2CCommands.STO, I2CCommands.END] 192 | log.debug("writing: %s", [hex(cc) for cc in cmd]) 193 | cnt = self.dev.write(self.EP_OUT, cmd) 194 | assert(cnt == len(cmd)) 195 | 196 | def i2c_detect(self, addr): 197 | """ 198 | Use the single byte write style to get an ack bit from writing to an address with no commands. 199 | :param addr: 200 | :return: true if the address was acked. 201 | """ 202 | cmd = [VendorCommands.I2C, 203 | I2CCommands.STA, I2CCommands.OUT, addr, I2CCommands.STO, I2CCommands.END] 204 | log.debug("writing: %s", [hex(cc) for cc in cmd]) 205 | cnt = self.dev.write(self.EP_OUT, cmd) 206 | assert(cnt == len(cmd)) 207 | rval = self.dev.read(self.EP_IN, I2CCommands.MAX) 208 | assert(len(rval) == 1) 209 | return not (rval[0] & 0x80) 210 | 211 | def i2c_write_byte_check(self, bb): 212 | """ 213 | write a byte and return the ack bit 214 | :param bb: byte to write 215 | :return: true for ack, false for nak 216 | """ 217 | cmd = [VendorCommands.I2C, I2CCommands.OUT, bb, I2CCommands.END] 218 | log.debug("writing: %s", [hex(cc) for cc in cmd]) 219 | cnt = self.dev.write(self.EP_OUT, cmd) 220 | assert(cnt == len(cmd)) 221 | rval = self.dev.read(self.EP_IN, I2CCommands.MAX) 222 | assert(len(rval) == 1) 223 | return not (rval[0] & 0x80) 224 | log.debug("read in %s", rval) 225 | 226 | def i2c_read_block(self, length): 227 | """ 228 | Requests a read of up to 32 bytes 229 | :return: array of data 230 | """ 231 | # not sure why/if this needs a -1 like I seemed to elsewhere 232 | #cmd = [VendorCommands.I2C, I2CCommands.IN | length, I2CCommands.END] 233 | cmd = [VendorCommands.I2C, I2CCommands.IN, I2CCommands.END] 234 | cnt = self.dev.write(self.EP_OUT, cmd) 235 | assert(cnt == len(cmd)) 236 | rval = self.dev.read(self.EP_IN, I2CCommands.MAX) 237 | print(len(rval), length) 238 | log.debug("read in %s", rval) 239 | return rval 240 | 241 | def eeprom_read(self, address, start, count): 242 | """ 243 | Issue an i2c read (single byte addressing, 24cxx styleee) 244 | :param address: the i2c address of the eeprom 245 | :param start: the starting address to read from 246 | :param count: how many bytes to read 247 | :return: 248 | """ 249 | # HACK ALERT - still decodign the i2c read/write stuff 250 | if start > 0x7ff: 251 | raise ValueError("Can't handle > 16k devices yet") 252 | len_write = 2 # for 24c01/24c02, address is one byte 253 | # for up to 16k, this fits in 254 | eep_cmd = [address | (start >> 7) & 0x0e, (start & 0xff)] 255 | cmd = [VendorCommands.I2C, 256 | I2CCommands.STA, 257 | # waits can be inserted here 258 | I2CCommands.OUT | len(eep_cmd), 259 | ] + eep_cmd 260 | cmd += [I2CCommands.STA, 261 | # Waits can be inserted here too 262 | I2CCommands.OUT | 1, address | 1] # Write address 263 | if (count <= 32): 264 | if count > 1: # ie, only include the IN|len if you have something to | in. 265 | cmd += [I2CCommands.IN | count - 1] 266 | cmd += [I2CCommands.IN, I2CCommands.STO, I2CCommands.END] 267 | iss = self.vendor_read(VendorCommands.I2C_STATUS, 0, 0, 8) 268 | log.debug("i2c status = %s, pins = %s", iss, PinState(iss)) 269 | log.debug("writing: %s", [hex(cc) for cc in cmd]) 270 | cnt = self.dev.write(self.EP_OUT, cmd) 271 | assert(cnt == len(cmd)) 272 | iss = self.vendor_read(VendorCommands.I2C_STATUS, 0, 0, 8) 273 | log.debug("i2c status = %s, pins = %s", iss, PinState(iss)) 274 | q = self.dev.read(self.EP_IN, count) 275 | return q 276 | else: 277 | # need a 32 byte pad block and memset(buf, 0x55, sizeof(buf)); 278 | #[aa][e0][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 279 | #[00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 280 | # for every chunk. 281 | cmd += [I2CCommands.IN | 32] # 0xe0 282 | cmd += [0 for x in range(32 - len(cmd))] # pad to start of next 32? 283 | 284 | cmd += [VendorCommands.I2C] 285 | if count - 1 - 32 > 0: 286 | cmd += [I2CCommands.IN | (count - 1 - 32)] 287 | cmd += [I2CCommands.IN, I2CCommands.STO, I2CCommands.END] 288 | 289 | cnt = self.dev.write(self.EP_OUT, cmd) 290 | assert(cnt == len(cmd)) 291 | read_count = 0 292 | rval = [] 293 | # reads need to be in chunks of up to 32. 294 | while read_count < count: 295 | q = self.dev.read(self.EP_IN, count - read_count) 296 | read_count += len(q) 297 | rval += q 298 | 299 | return rval 300 | 301 | 302 | # them 64 0x40 (same as me) 303 | #0000 aa 74 82 a0 00 74 81 a1 e0 00 12 00 02 00 00 00 304 | #0010 01 00 00 00 24 e1 12 00 34 00 30 00 00 00 00 00 305 | #0020 aa df c0 75 00 306 | #them 65 0x41 307 | #0000 aa 74 82 a0 00 74 81 a1 e0 00 12 00 02 00 00 00 308 | #0010 01 00 00 00 24 e1 12 00 2e e1 12 00 02 00 00 00 309 | #0020 aa e0 00 00 e8 ee 97 7c 00 00 00 00 17 a8 42 73 310 | #0030 1e 00 00 00 e8 ee 97 7c cc 3e c8 00 00 00 00 00 311 | #0040 aa c0 75 00 312 | # my 65 313 | #0000 aa 74 82 a0 00 74 81 a1 e0 00 00 00 00 00 00 00 314 | #0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 315 | #0020 aa e0 c0 75 00 316 | 317 | #them 66 318 | #0000 aa 74 82 a0 00 74 81 a1 e0 00 12 00 02 00 00 00 319 | #0010 01 00 00 00 24 e1 12 00 2e e1 12 00 02 00 00 00 320 | #0020 aa e0 00 00 e8 ee 97 7c 00 00 00 00 17 a8 42 73 321 | #0030 1e 00 00 00 e8 ee 97 7c cc 3e c8 00 00 00 00 00 322 | #0040 aa c1 c0 75 00 323 | 324 | #them 0x80 (setup, then aa e0 at 32bytes pads, til the regular trailer 325 | #0000 aa 74 82 a0 00 74 81 a1 e0 00 12 00 02 00 00 00 326 | #0010 01 00 00 00 24 e1 12 00 2e e1 12 00 02 00 00 00 327 | #0020 aa e0 00 00 e8 ee 97 7c 00 00 00 00 17 a8 42 73 328 | #0030 1e 00 00 00 e8 ee 97 7c 35 04 91 7c 00 00 00 00 329 | #0040 aa e0 00 00 3c 00 08 02 35 04 91 7c 3e 04 91 7c 330 | #0050 d0 e3 12 00 24 00 02 00 a0 e1 12 00 02 00 00 00 331 | #0060 aa df c0 75 00 332 | #usbi2c read 0x80 333 | #writing 101 bytes, read_step = 32, read times = 4 334 | #[aa][74][82][a0][00][74][81][a1][e0][00][00][00][00][00][00][00] 335 | #[00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 336 | #[aa][e0][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 337 | #[00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 338 | #[aa][e0][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 339 | #[00][00][00][00][00][00][00][00][00][00][00][00][00][00][00][00] 340 | #[aa][df][c0][75][00] 341 | 342 | def test_manual(q): 343 | q.i2c_start() 344 | print(q.i2c_write_byte_check(0xa0)) 345 | print(q.i2c_write_byte_check(0x00)) 346 | q.i2c_start() 347 | print(q.i2c_write_byte_check(0xa1)) 348 | data = q.i2c_read_block(6) 349 | print([hex(z) for z in data]) 350 | q.i2c_stop() 351 | 352 | def scan(q): 353 | results = [] 354 | for i in range(250): 355 | r = q.i2c_detect(i) 356 | print("address: %d (%#x) is: %s" % (i, i, r)) 357 | if r: results += [i] 358 | print("Responses from i2c devices at: ", results, [hex(a) for a in results]) 359 | 360 | 361 | if __name__ == "__main__": 362 | q = CH341() 363 | q.set_speed(400) 364 | #x = q.eeprom_read(0xa0, 0, 65) 365 | #print([hex(z) for z in x]) 366 | #log.info("received: %d bytes: %s", len(x), x) 367 | #test_manual(q) 368 | scan(q) 369 | --------------------------------------------------------------------------------