It works as single master on the I2C bus, that's OK for over 99% of the use cases. Slave mode is not supported and it's save to assume that a multi-master setup isn't supported as well, so if you need these functions you'll have to find another solution.
The USB to SPI/I2C/IIC/UART/TTL/ISP Serial Adapter Module |
On the top there are a 12 MHz quartz, a CH314A converter chip, two LEDs, four jumpers a a lot of connectors, and on the bottom are a few resistors and capacitors and a AMS1117-3.3 voltage regulator chip to supply 3.3 volts.
Who needs more documentation? |
The jumpers are more or less self-explaining. It's important to set the signal voltage level select jumpers as a pair, not individually. The external supply voltage jumper sets the voltage supplied at the VCC pin to either to 5V fix or to the voltage that is selected by the signal voltage level select jumpes, i.e. 3.3V or 5V depending on the jumper positions.
Windows driver with 64bit installer:
ch341a.zip (Source: http://www.abacom-online.de/div/ch341a.zip)
Folder: .\Driver\CH341_EPP_MEM_I2C_IO\
The 64 bit installer should show:
WCH.CN
|__ USB-EPP/I2C... CH341A
|__ 06/05/2009, 2.2.2009.06
Windows 32 bit installer:
CH341SER.ZIP (Source: http://wch.cn/downfile/5) with 32bit and 64bit installer
Download this file only if you need a 32 bit installer, but do not install the included driver. Use the driver supplied with the 64bit installer linked above.
API documentation: CH341DS2.PDF (Source: http://wch.cn/downfile/24)
A mirror for these files: https://drive.google.com/drive/folders/0B1xSSg6lZlzaR1BlSVNKLUhwRDQ?resourcekey=0-D_lXKzjEfqGHlsmPNmZ5aw&usp=sharing
After installing the driver and plugging in the device configured as IIC/SPI and you'll see it in the device manager:
A screenshot of the Device Manager with the device. |
- CH341OpenDevice - initialize the device
- CH341SetStream - set the I2C speed
- CH341StreamI2C - read and/or write data
- CH341CloseDevice - close the device
//************************ //CH341OpenDevice // /* Call the function CH341OpenDevice once before calling other functions. */ HANDLE WINAPI CH341OpenDevice( // open CH341 device, return a handle ULONG iIndex // id of the USB device ); /* If the returned number is smaller then zero the device couldn't be opened. */ //************************ //CH341CloseDevice // /* Call the function CH341CloseDevice to close the device. */ VOID CH341CloseDevice( // close the CH341 device ULONG iIndex // id of the USB device ); //************************ //CH341SetStream // /* Call the function CH341SetStream once to set the data rate. */ BOOL WINAPI CH341SetStream( // configure the serial flow mode ULONG iIndex, // id of the USB device ULONG iMode // specify the mode, see below ); /* // Bit 1,0: I2C interface speed / SCL frequency // 00 = low / 20KHz // 01 = standard / 100KHz (default) // 10 = fast / 400KHz // 11 = High Speed / 750KHz // Bit 2: SPI's I / O Number / IO pin // 0 = single-input single-output (D3 clock / D5 out / D7 into) (default) // 1 = double into a double (D3 clock / D5 out D4 a / D7 D6 enter into) // Bit 7: SPI byte bit order // 0 = LSB first // 1 = MSB first // Other Reserved, must be 0 // To sum this up: Set iMode to 0, 1, 2 or 3 for 20, 100, 400 or 750 kHz. */ //************************ //CH341ReadI2C // /* Call this function to write and read one byte from a slave device on
the I2C bus. */ BOOL WINAPI CH341ReadI2C( // read data ULONG iIndex, // id of the USB device UCHAR iDevice, // I2C device address (7 bits) UCHAR iAddr, // I2C device register (writes one byte) PUCHAR oByte // I2C data (reads one byte) ); //************************ //CH341WriteI2C // /* Call this function to write two bytes of data on the I2C bus. */ BOOL WINAPI CH341WriteI2C( // write data ULONG iIndex, // id of the USB device UCHAR iDevice, // I2C device address (7 bits) UCHAR iAddr, // I2C device register (writes first byte) UCHAR iByte // I2C data (writes second byte) ); //************************ //CH341StreamI2C // /* Call this function to read, write, or write and read a specified amount of bytes on the I2C bus. Read length or write length can be set to zero BOOL WINAPI CH341StreamI2C( // read/write data ULONG iIndex, // id of the USB device ULONG iWriteLength, // length of the data (bytes to write) PVOID iWriteBuffer, // pointer to the data ULONG iReadLength, // length of the data (bytes to read) PVOID oReadBuffer // pointer to the data ); /* This function provides all functionality of CH341ReadI2C and CH341WriteI2C plus much more flexibility. It's not that elegant because you have to prepare the iWriteBuffer with the device address that is shifted by one bit to the left by yourself (the least significant bit will be controlled by the chip for read (1) and write (0) access automatically). */
Putting this all together in an undocumented test script for AutoIT:
#include <WinAPISys.au3> ;device ID $id = 0 ;open the dll $dll = DllOpen("C:\Windows\System32\CH341DLL.DLL") If $dll <> -1 Then ConsoleWrite ("DllOpen: OK" & @CRLF) Else ConsoleWrite ("DllOpen: Error, could not open the dll." & @CRLF) Beep(1000, 5000) Exit EndIf ;open the device $aResult = DllCall($dll, "BOOL", "CH341OpenDevice", "ULONG", $id) If $aResult[0] <> -1 Then ConsoleWrite ("CH341OpenDevice: OK" & @CRLF) Else ConsoleWrite ("CH341OpenDevice: Error: Could not open device " & @CRLF) Beep(1000, 5000) Exit EndIf ;set the I2C speed $iMode = 0 $aResult = DllCall($dll, "BOOL", "CH341SetStream", "ULONG", $id, "ULONG", $iMode) If $aResult[0] Then ConsoleWrite("CH341SetStream: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $iMode=" & $iMode & @CRLF) Else ConsoleWrite("CH341SetStream: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $iMode=" & $iMode & @CRLF) Beep(1000, 5000) Exit EndIf ;Read and Write data ;to be used with CH341ReadI2C and CH341WriteI2C: $i2c_addr = 85 ;b: 0101'0101 (I2C: 1010'101*, *=R/W bit) ;to be used with CH341ReadI2C and CH341WriteI2C: $i2c_B1 = 0 ;b: 0000'0000 ;to be used with CH341ReadI2C: $sBYTE_r4 = DllStructCreate ("BYTE[4]" ) $pBYTE_r4 = DllStructGetPtr($sBYTE_r4) ;to be used with CH341WriteI2C: $i2c_B2 = 231 ;b: 1110'0111 ;to be used with CH341StreamI2C $rlen = 0 $sBYTE_r = DllStructCreate ("BYTE[8]" ) $pBYTE_r = DllStructGetPtr($sBYTE_r) $wlen = 3 $sBYTE_w = DllStructCreate ("BYTE;BYTE;BYTE;BYTE;BYTE;BYTE;BYTE;BYTE" ) DllStructSetData ( $sBYTE_w, 1, 170 ) ;b: 1010'1010 (I2C: 1010'101*, *=R/W bit) - I2C slave address DllStructSetData ( $sBYTE_w, 2, 0 ) ;b: 0000'0000 DllStructSetData ( $sBYTE_w, 3, 231 ) ;b: 1110'0111 DllStructSetData ( $sBYTE_w, 4, 0 ) DllStructSetData ( $sBYTE_w, 5, 0 ) DllStructSetData ( $sBYTE_w, 6, 0 ) DllStructSetData ( $sBYTE_w, 7, 0 ) DllStructSetData ( $sBYTE_w, 8, 0 ) $pBYTE_w = DllStructGetPtr($sBYTE_w) MsgBox(0, "PAUSED", "Click OK to continue with the tests") ConsoleWrite("WRITE TEST: START" & @CRLF) ;CH341WriteI2C $aResult = DllCall($dll, "BOOL", "CH341WriteI2C", "BYTE", $id, "BYTE", $i2c_addr, "BYTE", $i2c_B1, "BYTE", $i2c_B2) If $aResult[0] Then ConsoleWrite("CH341WriteI2C: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $i2c_addr=" & $i2c_addr & ", $i2c_B1=" & $i2c_B1 & ", $i2c_B2=" & $i2c_B2 & @CRLF) Else ConsoleWrite("CH341WriteI2C: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $i2c_addr=" & $i2c_addr & ", $i2c_B1=" & $i2c_B1 & ", =$i2c_B2" & $i2c_B2 & @CRLF) Beep(1000, 5000) Exit EndIf MsgBox(0, "PAUSED", "Click OK to continue with the tests") ;CH341StreamI2C writing $rlen = 0 $wlen = 3 $aResult = DllCall($dll, "BOOL", "CH341StreamI2C", "BYTE", $id, "BYTE", $wlen, "ptr", $pBYTE_w, "BYTE", $rlen, "ptr", $pBYTE_r) If $aResult[0] Then ConsoleWrite("CH341StreamI2C: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Else ConsoleWrite("CH341StreamI2C: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Beep(1000, 5000) Exit EndIf MsgBox(0, "PAUSED", "Click OK to continue with the tests") ConsoleWrite("WRITE TEST COMLETE" & @CRLF) ConsoleWrite("READ TEST: START" & @CRLF) ;CH341ReadI2C $aResult = DllCall($dll, "BOOL", "CH341ReadI2C", "BYTE", $id, "BYTE", $i2c_addr, "BYTE", $i2c_B1, "PTR", $pBYTE_r4) If $aResult[0] Then ConsoleWrite("CH341ReadI2C: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $i2c_addr=" & $i2c_addr & _ ", $i2c_B1=" & $i2c_B1 & _ ", $sBYTE_r4=" & DllStructGetData($sBYTE_r4, 1) & @CRLF) Else ConsoleWrite("CH341ReadI2C: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $i2c_addr=" & $i2c_addr & _ ", $i2c_B1=" & $i2c_B1 & _ ", $sBYTE_r4=" & DllStructGetData($sBYTE_r4, 1) & @CRLF) Beep(1000, 5000) Exit EndIf MsgBox(0, "PAUSED", "Click OK to continue with the tests") ;CH341StreamI2C reading similar to CH341ReadI2C $rlen = 1 $wlen = 2 ;I2C slave address and one byte $aResult = DllCall($dll, "BOOL", "CH341StreamI2C", "BYTE", $id, "BYTE", $wlen, "ptr", $pBYTE_w, "BYTE", $rlen, "ptr", $pBYTE_r) If $aResult[0] Then ConsoleWrite("CH341StreamI2C: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Else ConsoleWrite("CH341StreamI2C: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Beep(1000, 5000) Exit EndIf MsgBox(0, "PAUSED", "Click OK to continue with the tests") ;CH341StreamI2C reading $rlen = 8 $wlen = 1 $aResult = DllCall($dll, "BOOL", "CH341StreamI2C", "BYTE", $id, "BYTE", $wlen, "ptr", $pBYTE_w, "BYTE", $rlen, "ptr", $pBYTE_r) If $aResult[0] Then ConsoleWrite("CH341StreamI2C: OK" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Else ConsoleWrite("CH341StreamI2C: Error" & @CRLF) ConsoleWrite(" Param: $id="& $id & ", $wlen=" & $wlen & ", $rlen=" & $rlen & _ ", $sBYTE_w1,2,3,4=" & DllStructGetData($sBYTE_w, 1) & _ "," & DllStructGetData($sBYTE_w, 2) & _ "," & DllStructGetData($sBYTE_w, 3) & _ "," & DllStructGetData($sBYTE_w, 4) & _ ", $sBYTE_r=" & DllStructGetData($sBYTE_r, 1) & @CRLF) Beep(1000, 5000) Exit EndIf MsgBox(0, "PAUSED", "Click OK to finish") ConsoleWrite("READ TEST COMLETE" & @CRLF)
The console output should like this if everything works fine (no I2c devices connected):
DllOpen: OK
CH341OpenDevice: OK
CH341SetStream: OK
Param: $id=0, $iMode=0
WRITE TEST: START
CH341WriteI2C: OK
Param: $id=0, $i2c_addr=85, $i2c_B1=0, $i2c_B2=231
CH341StreamI2C: OK
Param: $id=0, $wlen=3, $rlen=0, $sBYTE_w1,2,3,4=170,0,231,0, $sBYTE_r=0x0000000000000000
WRITE TEST COMLETE
READ TEST: START
CH341ReadI2C: OK
Param: $id=0, $i2c_addr=85, $i2c_B1=0, $sBYTE_r4=0xFF000000
CH341StreamI2C: OK
Param: $id=0, $wlen=2, $rlen=1, $sBYTE_w1,2,3,4=170,0,231,0, $sBYTE_r=0xFF00000000000000
CH341StreamI2C: OK
Param: $id=0, $wlen=1, $rlen=8, $sBYTE_w1,2,3,4=170,0,231,0, $sBYTE_r=0xFFFFFFFFFFFFFFFF
READ TEST COMLETE
And here are the pictures showing what's happening on the wire:
First write test (CH341WriteI2C) |
Second write test (CH341StreamI2C) |
First read test (CH341ReadI2C) |
Second read test (CH341StreamI2C) |
Third read test (CH341StreamI2C) |
Analog Ch State Scale Position Coupling BW Limit Invert
CH1 On 2.00V/ -4.00V DC Off Off
CH2 On 2.00V/ 0.00uV DC Off Off
Analog Ch Impedance Probe
CH1 1M Ohm 1X
CH2 1M Ohm 1X
Time Time Ref Main Scale Delay
Main Center 200.0us/ 840.0000us
Trigger Source Slope Mode Coupling Level Holdoff
Edge CH1 Falling Normal DC 1.68V 500ns
Acquisition Sampling Memory Depth Sample Rate
Normal Realtime Normal 1.000MSa
Hi Jenny,
ReplyDeleteI want congratulate and thanks you so much for gently share your searchings/discoverings/work about the CH341/CH341A chip and the ALL IN 1 Multifunction USB to SPI/I2C/IIC/UART/TTL board. Like you I thought this board would be a simple and good solution to connect I2C devices to PC and suffered the same problem of lack of documentation and the available is only in Chinese and the diver available in the manufacturers’ page to be only for 32bits. The device only worked properly in my 64bits version after install the driver from the link of your blog. It be wonderful if you also can make an example in C.
What this tools can flash ic eeprom in module battery laptop?? Thank be for
ReplyDeletethank you for the detailed and translated information.
ReplyDeleteI got CH341 from aliexpress. And I could use it with 1 byte register address.
do you know if 2 bytes register address is possible?
I didn't carefully read your post.
DeleteCH341StreamI2C can do that with left shiefted device address at write butter no '0'
thank you!
Thanks a lot Very Very Helful, i have learnt Auto V3 many years ago so its very easy for me..
ReplyDeleteNew Link: https://drive.google.com/drive/folders/0B1xSSg6lZlzaR1BlSVNKLUhwRDQ?resourcekey=0-D_lXKzjEfqGHlsmPNmZ5aw&usp=sharing
ReplyDelete