├── .gitattributes ├── .gitignore ├── ExpressPCB ├── SchComponents_Custom │ └── 93LC86.s ├── ch341-spi-93lc86.png └── ch341-spi-93lc86.sch ├── LICENSE ├── PulseView ├── ewds-cmd.png ├── ewen-cmd.png ├── read-cmd-overview.png └── write-cmd.png ├── README.md ├── VS2010_sol └── ch341_spi_93lc86 │ ├── ch341_spi_93lc86.sln │ └── ch341_spi_93lc86 │ ├── ReadMe.txt │ ├── ch341_spi_93lc86.cpp │ ├── ch341_spi_93lc86.vcxproj │ ├── ch341_spi_93lc86.vcxproj.filters │ ├── hpch_93c.cpp │ ├── hpch_93c.h │ ├── stdafx.cpp │ ├── stdafx.h │ └── targetver.h └── images └── ch341-spi-5v.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # ExpressPCB library 2 | *.s binary 3 | # ExpressPCB schematics 4 | *.sch binary 5 | 6 | # should be default 7 | *.png binary 8 | *.jpg binary 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore vim files 2 | *.swp 3 | *~ 4 | # Ignore PhotoScape backup directory 5 | Originals/ 6 | # Ignore these VS2010 files 7 | *.suo 8 | *.sdf 9 | *.user 10 | Debug/ 11 | Release/ 12 | ipch/ 13 | *.ipch 14 | *.opensdf 15 | 16 | -------------------------------------------------------------------------------- /ExpressPCB/SchComponents_Custom/93LC86.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/ExpressPCB/SchComponents_Custom/93LC86.s -------------------------------------------------------------------------------- /ExpressPCB/ch341-spi-93lc86.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/ExpressPCB/ch341-spi-93lc86.png -------------------------------------------------------------------------------- /ExpressPCB/ch341-spi-93lc86.sch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/ExpressPCB/ch341-spi-93lc86.sch -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Henryk Paluch, et al. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /PulseView/ewds-cmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/PulseView/ewds-cmd.png -------------------------------------------------------------------------------- /PulseView/ewen-cmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/PulseView/ewen-cmd.png -------------------------------------------------------------------------------- /PulseView/read-cmd-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/PulseView/read-cmd-overview.png -------------------------------------------------------------------------------- /PulseView/write-cmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/PulseView/write-cmd.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPI example for Microwire 93LC86 EEPROM with CH341 USB adapter 2 | 3 | Here is project of accessing [93LC86] EEPROM 4 | from [CH341A USB to UART/IIC/SPI/TTL/ISP adapter EPP/MEM Parallel converter] 5 | using SPI mode. 6 | 7 | NOTE: The [93LC86] is actually `Microwire` device which is 8 | similar to SPI but definitely not same. 9 | There is nice [Sigrok Microwire decoder] page and it also includes 10 | link to [Microwire Specs] 11 | 12 | Here are key differences of `Microwire` [93LC86] from SPI: 13 | * `CS` pin is active on HIGH (typical SPI has `/SS` - slave select 14 | active in Low) 15 | * after each command the `CS` pin must be deactivated and activated 16 | again - otherwise following commands will be ignored 17 | * after any programming command the `DO` (or `MISO`) pin 18 | is `READY/BUSY` pin which can be polled (even without clock). 19 | However `CS` may not be deactivated before this pin comes 20 | to `READY` state (otherwise this pin function is lost). 21 | 22 | 23 | > WARNING! 24 | > 25 | > This project will overwrite data on connected [93LC86] EEPROM! 26 | > 27 | > Current status: 28 | > 1. Basic READ and WRITE to [93LC86] EEPROM functions implemented 29 | > The test program now does following: 30 | > 1. It writes and reads-back test value to/from EEPROM at address 1024 (0x400) 31 | > 1. It dumps contents of whole EEPROM to console 32 | > 33 | > WARNING! 34 | > Once my VirtualBox VM with XP SP3 (where I run my programs) 35 | > suddenly rebooted. But it (fortunately) can't be reproduced again... 36 | 37 | Circuit schematic is below: 38 | 39 | ![Schematic of SPI w 93LC86](https://github.com/hpaluch/ch341-spi-93lc86/blob/master/ExpressPCB/ch341-spi-93lc86.png?raw=true) 40 | 41 | 42 | # Requirements 43 | 44 | Hardware: 45 | * you 46 | need [CH341A USB to UART/IIC/SPI/TTL/ISP adapter EPP/MEM Parallel converter] 47 | I got my from Amazon.de as [DollaTek CH341A USB zu UART/IIC/SPI/TTL/ISP Adapter EPP/MEM Parallelwandler]. If you never used this module 48 | please see my article [Getting started with LC CH341A USB conversion module] 49 | * EEPROM [93LC86], 100nF ceramic capacitor. 50 | 51 | Software: 52 | 53 | * Windows OS - I tested this project on Windows XP SP3 guest in VirtualBox 54 | * Visual Studio 2010 (This it the last version supported on XP SP3) 55 | 56 | 57 | # Setup 58 | 59 | The CH341A adapter must be setup following way: 60 | * jumper set to `I2C/SPI` mode 61 | * voltage set to 5V TTL logic (to ensure highest possible 62 | speed and response of [93LC86] 63 | * please see picture below for correct configuration: 64 | 65 | ![USB CH341A adapter configuration](https://github.com/hpaluch/ch341-spi-93lc86//blob/master/images/ch341-spi-5v.jpg?raw=true) 66 | 67 | 68 | Software setup: 69 | * Download and install [CH341PAR.ZIP] - USB driver for CH341 chip 70 | in Parallel mode (EPP, MEM). This driver is valid 71 | also for **I2C mode and SPI mode** (yes - even when it is marked _parallel_). 72 | * install VisualSutdio 2010 73 | 74 | Create environment variable `CH341_SDK` that should point to extracted 75 | `CH341PAR.ZIP` header and library files. For example 76 | if you have extracted file: 77 | 78 | ``` 79 | C:\CH341_DRIVER\LIB\C\CH341DLL.H 80 | ``` 81 | Then your `CH341_SDK` should be set to `C:\CH341_DRIVER\LIB\C`. 82 | 83 | Open and rebuild solution `VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86.sln` 84 | in VisualStudio 2010. There should be no errors. 85 | 86 | Connect your `CH341A USB module` to target circuit. Following pins are used: 87 | 88 | |PIN Name|Direction|Description| 89 | |--------|---------|-----------| 90 | |GND|N/A|Common ground| 91 | |VCC|N/A|5V supply| 92 | |MISO|Input|master in slave out - SPI| 93 | |MOSI|Output|master out slave in - SPI| 94 | |SCK|Output|master clock - SPI| 95 | |CS0|Output|Chip select 0, active in high (93LC86 is Microwire but generally NOT SPI compatible)| 96 | 97 | ---- 98 | 99 | NOTE: Direction is from `CH341A USB Module` "view". 100 | 101 | Connect your `CH341 USB module` to your PC. There should 102 | be permanently lighting red LED on USB module. 103 | 104 | 105 | # Bitstream mode 106 | 107 | The `ch341dll.h` API offers two interfaces for SPI: 108 | 109 | * byte oriented `CH341SetStream()` + `CH341StreamSPI4()` called 110 | for each byte. This is convenient API, but 111 | may not be flexible enough in complex scenarios (like this EEPROM) 112 | * bit-stream oriented `CH341Set_D5_D0()` + `CH341BitStreamSPI()` called 113 | for each bit-set. Each byte represents (roughly) set of bits 114 | `D7` to `D0` that have following meaning: 115 | 116 | |Bit|Direction|Description| 117 | |---|---------|-----------| 118 | |D7|In|MISO - master in slave out data| 119 | |D6|In|SPI 5-wire pin?| 120 | |D5|Out|MOSI - master out slave in data| 121 | |D4|Out|SPI 5-wire pin?| 122 | |D3|Out|Clock (automatic cycle on each bit-stream byte transfer)| 123 | |D2|Out|CS2| 124 | |D1|Out|CS1| 125 | |D0|Out|CS0| 126 | 127 | WARNING: I'm unable to find reliable documentation on SPI 5-wire 128 | standard (common is SPI 4-wire). 129 | 130 | WARNING: I did not verify above table (yet). 131 | 132 | 133 | # Output 134 | 135 | When you run compiled executable you should see messages like: 136 | ``` 137 | CH341 SPI shift register example 138 | CH341 version: 33 139 | Opening device# 0 140 | DEBUG: WRITE succeed after 1 ms wait 141 | DEBUG: WRITE succeed after 1 ms wait 142 | Reading 2048 bytes from 93LC86... 143 | Done. Data dump follows: 144 | Dump of buffer at 0x0012F724, bytes 2048 145 | 146 | 0x0000 90 30 10 b5 02 90 00 00 ff 00 00 01 04 0f 17 61 .0.............a 147 | 0x0010 00 00 00 40 00 00 00 00 48 01 48 01 00 00 00 00 ...@....H.H..... 148 | ... 149 | # here is test byte 0xab... 150 | 0x0400 ab ca ca ca ca ca ca ca ca ca ca ca ca ca ca ca ................ 151 | ``` 152 | 153 | NOTE: you can see lots of 0xca bytes in my EEPROM because 154 | I already tried pattern write (WRAL) command on 155 | my [STM32 Nucleo board] 156 | 157 | 158 | Please note that Reading whole EEPROM takes around 10 seconds on my 159 | VirtualBox VM 160 | 161 | 162 | ## Logic Analyzer output 163 | 164 | NOTES on USB: According to my current knowledge the USB PC<->CH341A 165 | adapter communication is done using packets with up to 32 instructions. 166 | The packets are send/received each 1ms or so (this is how such 167 | USB device works). Therefore there can be (and are) inherent delays. 168 | 169 | Fortunately it is not problem, because there are no (low range) 170 | constraints on SPI master clock timings. 171 | 172 | 173 | I finally found root cause why the PulseView's 93Cxx decoder did 174 | not work. It was because [93LC86] is `Microwire` not `SPI` device 175 | as described on [Sigrok Microwire decoder] page. 176 | 177 | So now it works perfectly as can be seen here: 178 | 179 | ![PulseView 93LC86 READ overview](https://github.com/hpaluch/ch341-spi-93lc86//blob/master/PulseView/read-cmd-overview.png?raw=true) 180 | 181 | Thanks to [PulseView Triggers] it is incredibly easy to capture 182 | start of communications (I just used _rising edge_ trigger on channel 183 | `CH1` (shown as `D0` in PulseView) and connected to `CS0` signal. 184 | 185 | NOTE: You need to run our exe program for the first time to ensure 186 | that `CS0` ends low before next run. And then starting trigger capture 187 | in PulseView 188 | 189 | Here are screenshots from whole `WRITE` cycle: 190 | 1. `EWEN` (Write Enable) 191 | 1. `WRITE` (Write one byte) 192 | 1. `EWDS` (Write disable) 193 | 194 | 195 | So `EWEN` (Write Enable): 196 | 197 | ![PulseView 93LC86 EWEN](https://github.com/hpaluch/ch341-spi-93lc86//blob/master/PulseView/ewen-cmd.png?raw=true) 198 | 199 | Followed by `WRITE` (Write one byte of data): 200 | 201 | ![PulseView 93LC86 WRITE](https://github.com/hpaluch/ch341-spi-93lc86//blob/master/PulseView/write-cmd.png?raw=true) 202 | 203 | > NOTE: The whole WRITE can't fit on screen because there is then large delay before 204 | > remainder of data (cause by limited packet size on USB, followed by 205 | > USB polling period - minimum 1ms for Full speed (does it applies for CH341A?). 206 | 207 | 208 | And finally `EWDS` (Write Disable): 209 | 210 | ![PulseView 93LC86 EWDS](https://github.com/hpaluch/ch341-spi-93lc86//blob/master/PulseView/ewds-cmd.png?raw=true) 211 | 212 | # Known Bugs and Limitations 213 | 214 | * only 8-bit data organization (`ORG` pin Low) supported 215 | * the `HpCh_93c_Read()` function does not utilize sequential `READ` 216 | (reading more than 1-byte in single command) - it would be faster. 217 | * the `BOOL HpCh_93c_SendCommand()` function has hard limit of 218 | maximum total 32 transferred bits in whole command. 219 | * function `void HpCh_DumpBuf(BYTE *buf, int n)` works correctly 220 | for `n` dividable by `16` (or constant `VALUES_PER_LINE`) only. 221 | 222 | 223 | [PulseView Triggers]: https://sigrok.org/doc/pulseview/0.4.1/manual.html#_triggers 224 | [Microwire Specs]: http://www.ti.com/lit/an/snoa743/snoa743.pdf 225 | [Sigrok Microwire decoder]: https://sigrok.org/blog/new-protocol-decoder-microwire 226 | [STM32 Nucleo board]: https://github.com/hpaluch-pil/nucleo-93cxx 227 | [93LC86]: http://ww1.microchip.com/downloads/en/DeviceDoc/21131F.pdf 228 | [CH341PAR.ZIP]: http://www.wch.cn/downloads/file/7.html 229 | [Getting started with LC CH341A USB conversion module]: https://github.com/hpaluch/hpaluch.github.io/wiki/Getting-started-with-LC-CH341A-USB-conversion-module 230 | [CH341A USB to UART/IIC/SPI/TTL/ISP adapter EPP/MEM Parallel converter]:http://www.chinalctech.com/index.php?_m=mod_product&_a=view&p_id=1220 231 | [DollaTek CH341A USB zu UART/IIC/SPI/TTL/ISP Adapter EPP/MEM Parallelwandler]:https://www.amazon.de/gp/product/B07DJZDRKG/ 232 | 233 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ch341_spi_93lc86", "ch341_spi_93lc86\ch341_spi_93lc86.vcxproj", "{B4D0B292-FEFD-426D-9352-B9E06F4172F1}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {B4D0B292-FEFD-426D-9352-B9E06F4172F1}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {B4D0B292-FEFD-426D-9352-B9E06F4172F1}.Debug|Win32.Build.0 = Debug|Win32 14 | {B4D0B292-FEFD-426D-9352-B9E06F4172F1}.Release|Win32.ActiveCfg = Release|Win32 15 | {B4D0B292-FEFD-426D-9352-B9E06F4172F1}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | CONSOLE APPLICATION : ch341_spi_93lc86 Project Overview 3 | ======================================================================== 4 | 5 | AppWizard has created this ch341_spi_93lc86 application for you. 6 | 7 | This file contains a summary of what you will find in each of the files that 8 | make up your ch341_spi_93lc86 application. 9 | 10 | 11 | ch341_spi_93lc86.vcxproj 12 | This is the main project file for VC++ projects generated using an Application Wizard. 13 | It contains information about the version of Visual C++ that generated the file, and 14 | information about the platforms, configurations, and project features selected with the 15 | Application Wizard. 16 | 17 | ch341_spi_93lc86.vcxproj.filters 18 | This is the filters file for VC++ projects generated using an Application Wizard. 19 | It contains information about the association between the files in your project 20 | and the filters. This association is used in the IDE to show grouping of files with 21 | similar extensions under a specific node (for e.g. ".cpp" files are associated with the 22 | "Source Files" filter). 23 | 24 | ch341_spi_93lc86.cpp 25 | This is the main application source file. 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | Other standard files: 29 | 30 | StdAfx.h, StdAfx.cpp 31 | These files are used to build a precompiled header (PCH) file 32 | named ch341_spi_93lc86.pch and a precompiled types file named StdAfx.obj. 33 | 34 | ///////////////////////////////////////////////////////////////////////////// 35 | Other notes: 36 | 37 | AppWizard uses "TODO:" comments to indicate parts of the source code you 38 | should add to or customize. 39 | 40 | ///////////////////////////////////////////////////////////////////////////// 41 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/ch341_spi_93lc86.cpp: -------------------------------------------------------------------------------- 1 | // ch341_spi_93lc86.cpp : Defines the entry point for the console application. 2 | // 3 | #include "stdafx.h" 4 | #include "hpch_93c.h" 5 | 6 | // BUG: does not support n != VALUES_PER_LINE - TODO.... 7 | void HpCh_DumpBuf(BYTE *buf, int n){ 8 | const int VALUES_PER_LINE = 16; 9 | 10 | int i=0; 11 | printf("Dump of buffer at 0x%p, bytes %u\n",buf,n); 12 | for(i=0;i= VALUES_PER_LINE ){ 16 | int j=0; 17 | // XXX: puts(3) always append \n 18 | putc(' ',stdout); 19 | for(j=0;j=32 && b<127){ 22 | putc(b,stdout); 23 | } else { 24 | putc('.',stdout); 25 | } 26 | } 27 | } 28 | printf("\n0x%04x",i); 29 | } 30 | printf(" %02x",buf[i]); 31 | } 32 | printf("\n"); 33 | } 34 | 35 | BOOL MyWriteAndReadBack(ULONG iDevIndex,DWORD addr, DWORD data){ 36 | DWORD outData = 0; 37 | 38 | // test write 39 | if (!HpCh_93c_Write(iDevIndex,addr,data)){ 40 | return FALSE; 41 | } 42 | 43 | // read back value 44 | if (!HpCh_93c_Read(iDevIndex,addr,&outData)){ 45 | return FALSE; 46 | } 47 | if (outData != data){ 48 | fprintf(stderr,"Written data mismatch at addr 0x%x: 0x%x <> 0x%x\n", addr, data,outData); 49 | return FALSE; 50 | } 51 | return TRUE; 52 | } 53 | 54 | int _tmain(int argc, _TCHAR* argv[]) 55 | { 56 | const DWORD MY_TEST_ADDR = 1024; 57 | const DWORD MY_TEST_DATA = 0x34; 58 | const DWORD MY_TEST_DATA2 = 0xAB; 59 | 60 | DWORD outData = 0; 61 | int addr = 0; 62 | BYTE dataBuf[ADDR_MASK+1]; 63 | int ret = EXIT_SUCCESS; 64 | HANDLE h341 = NULL; 65 | ULONG iDevIndex = 0; // first device 66 | 67 | printf("CH341 SPI shift register example\r\n"); 68 | 69 | printf("CH341 version: %lu\r\n", CH341GetVersion( )); 70 | 71 | printf("Opening device# %lu\r\n", iDevIndex); 72 | h341 = CH341OpenDevice( iDevIndex); 73 | if (h341 == NULL){ 74 | printf("OpenDevice(iDevIndex=%lu) failed\r\n",iDevIndex); 75 | ret = EXIT_FAILURE; 76 | goto exit0; 77 | } 78 | 79 | // set CS0, CLK and MOSI as output 80 | // all pins active on high 81 | if (!CH341Set_D5_D0(iDevIndex,HPCH_MASK_CS0|HPCH_MASK_CLK|HPCH_MASK_MOSI,0 )){ 82 | printf("CH341_D5_D0 failed\r\n"); 83 | ret = EXIT_FAILURE; 84 | goto exit1; 85 | } 86 | 87 | // write and read back sample data at sample address 88 | if (!MyWriteAndReadBack(iDevIndex,MY_TEST_ADDR,MY_TEST_DATA)){ 89 | ret = EXIT_FAILURE; 90 | goto exit1; 91 | } 92 | // write and read back another value - to verify that data were really WRITTEN 93 | if (!MyWriteAndReadBack(iDevIndex,MY_TEST_ADDR,MY_TEST_DATA2)){ 94 | ret = EXIT_FAILURE; 95 | goto exit1; 96 | } 97 | 98 | 99 | // the while is here only to have repeated output to Logic Analyzer 100 | while(1){ 101 | memset(dataBuf,0,sizeof(dataBuf)); 102 | printf("Reading %u bytes from 93LC86...\n",sizeof(dataBuf)); 103 | for(addr=0; addr<=ADDR_MASK;addr++){ 104 | DWORD b = 0; 105 | if (!HpCh_93c_Read(iDevIndex,addr,&b)){ 106 | ret = EXIT_FAILURE; 107 | goto exit1; 108 | } 109 | // currently we expect 8-bit data (organization of 93LC86) 110 | if (b & ~0xff){ 111 | fprintf(stderr,"Got too large value %u, maximum byte %u expected\n",b,255); 112 | ret = EXIT_FAILURE; 113 | goto exit1; 114 | } 115 | dataBuf[addr] = (BYTE)b; 116 | } 117 | printf("Done. Data dump follows:\n"); 118 | HpCh_DumpBuf(dataBuf,sizeof(dataBuf)); 119 | Sleep(5); 120 | break; // comment it out to have stream of data - for Logic Analyzer 121 | } 122 | 123 | exit1: 124 | CH341CloseDevice(iDevIndex); 125 | exit0: 126 | return ret; 127 | 128 | } 129 | 130 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/ch341_spi_93lc86.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {B4D0B292-FEFD-426D-9352-B9E06F4172F1} 15 | Win32Proj 16 | ch341_spi_93lc86 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | 24 | 25 | Application 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | $(CH341_SDK);$(IncludePath) 43 | $(CH341_SDK);$(LibraryPath) 44 | 45 | 46 | false 47 | $(CH341_SDK);$(IncludePath) 48 | $(CH341_SDK);$(LibraryPath) 49 | 50 | 51 | 52 | 53 | 54 | Level3 55 | Disabled 56 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 57 | 58 | 59 | Console 60 | true 61 | ch341dll.lib;%(AdditionalDependencies) 62 | 63 | 64 | 65 | 66 | Level3 67 | 68 | 69 | MaxSpeed 70 | true 71 | true 72 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 73 | 74 | 75 | Console 76 | true 77 | true 78 | true 79 | ch341dll.lib;%(AdditionalDependencies) 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/ch341_spi_93lc86.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/hpch_93c.cpp: -------------------------------------------------------------------------------- 1 | // hpch_93.cpp - Henryk Paluch's routines for 93LC86 EEPROM connected using CH341A adapter 2 | #include"stdafx.h" 3 | #include"hpch_93c.h" 4 | 5 | 6 | // from: https://stackoverflow.com/a/41862592 7 | /* Windows sleep in 100ns units */ 8 | BOOLEAN nanosleep(LONGLONG ns){ 9 | /* Declarations */ 10 | HANDLE timer; /* Timer handle */ 11 | LARGE_INTEGER li; /* Time defintion */ 12 | /* Create timer */ 13 | if(!(timer = CreateWaitableTimer(NULL, TRUE, NULL))) 14 | return FALSE; 15 | /* Set timer properties */ 16 | li.QuadPart = -ns; 17 | if(!SetWaitableTimer(timer, &li, 0, NULL, NULL, FALSE)){ 18 | CloseHandle(timer); 19 | return FALSE; 20 | } 21 | /* Start & wait for timer */ 22 | WaitForSingleObject(timer, INFINITE); 23 | /* Clean resources */ 24 | CloseHandle(timer); 25 | /* Slept without problems */ 26 | return TRUE; 27 | } 28 | // 29 | // Send 93LC86 command and returns response 30 | // DWORD iIndex - CH341A device SN, or 0 for 1st device 31 | // DWORD inData - input data bits 32 | // DWORD nBits - number of bits to send in inData 33 | // DWORD csBits - number of CS off bits to send (can be 0 to leave CS ON) 34 | // DWORD outData - data bits returned from 93LC86 - only nBits inserted... 35 | static BOOL HpCh_93c_SendCommand(ULONG iIndex, DWORD inData,DWORD nBits, DWORD csBits,DWORD *outData){ 36 | BOOL ret = FALSE; 37 | int i=0; 38 | static BYTE ioBuf[HPCH_93C_MAX_BITS]; 39 | 40 | if (outData == NULL ){ 41 | fprintf(stderr,"Parameter outData may not be NULL\n"); 42 | return FALSE; 43 | } 44 | 45 | if (nBits + csBits > HPCH_93C_MAX_BITS){ 46 | fprintf(stderr,"nBits %u + csBits %u = %u exceeds limit %u\n",nBits,csBits,nBits+csBits, HPCH_93C_MAX_BITS); 47 | return FALSE; 48 | } 49 | // ensure consistent data in buffer 50 | memset(ioBuf,0,sizeof(ioBuf)); 51 | 52 | for(i=0;i<(int)nBits;i++){ 53 | BYTE b = 0; 54 | BOOL isBitSet = ( inData & (1 << (nBits-i-1))) ? TRUE : FALSE; 55 | 56 | b |= HPCH_MASK_CS0; // activate CS0 57 | if (isBitSet){ 58 | b |= HPCH_MASK_MOSI; // MOSI data output if bit is set 59 | } 60 | ioBuf[i] = b; 61 | } 62 | 63 | #if 0 64 | // these data are alrady off 65 | for(i=0;i 0x6 94 | cmd = 0x6 << ( ADDR_BITS + DATA_BITS ); 95 | if ( addr & ~ ADDR_MASK ){ 96 | fprintf(stderr,"addr 0x%x is too large. Maximum allowed is: 0x%x\n",addr, ADDR_MASK); 97 | return FALSE; 98 | } 99 | cmd |= (addr << DATA_BITS); 100 | // typically 1 CS OFF bit is enough 101 | if (!HpCh_93c_SendCommand(iIndex, cmd,(ADDR_BITS+DATA_BITS+PREFIX_BITS), 1, outData)){ 102 | return FALSE; 103 | } 104 | // verify that A0 MISO is 0 105 | if ( *outData & ( 1 << DATA_BITS) ){ 106 | fprintf(stderr,"MISO response on A0 address bit is not 0\n"); 107 | return FALSE; 108 | } 109 | *outData &= DATA_MASK; // cut off all non-data bits from input 110 | 111 | return TRUE; 112 | } 113 | 114 | BOOL HpCh_93c_Ewen(ULONG iIndex){ 115 | BOOL ret = FALSE; 116 | DWORD outData = 0; 117 | // 1|00|11 where last 2-bit are taken from ADDRESS 118 | // = 0x13 119 | DWORD cmd = 0x13 << ( ADDR_BITS - 2); 120 | // NOTE: outData is ignored - there is no meaningful feedback from 93LC86 on this command 121 | ret = HpCh_93c_SendCommand(iIndex, cmd,(ADDR_BITS+PREFIX_BITS), 1, &outData); 122 | if (!ret){ 123 | fprintf(stderr,"%s failed\n",__FUNCTION__); 124 | } 125 | return ret; 126 | } 127 | 128 | BOOL HpCh_93c_Ewds(ULONG iIndex){ 129 | BOOL ret = FALSE; 130 | DWORD outData = 0; 131 | // 1|00|00 where last 2-bit are taken from ADDRESS 132 | // = 0x10 133 | DWORD cmd = 0x10 << ( ADDR_BITS - 2); 134 | // NOTE: outData is ignored - there is no meaningful feedback from 93LC86 on this command 135 | ret = HpCh_93c_SendCommand(iIndex, cmd,(ADDR_BITS+PREFIX_BITS), 1, &outData); 136 | if (!ret){ 137 | fprintf(stderr,"%s failed\n",__FUNCTION__); 138 | } 139 | return ret; 140 | } 141 | 142 | BOOL HpCh_93c_Write(ULONG iIndex,DWORD addr,DWORD inData){ 143 | BOOL ret = FALSE; 144 | DWORD outData = 0; 145 | DWORD cmd = 0; 146 | int i = 0; 147 | 148 | if ( addr & ~ ADDR_MASK ){ 149 | fprintf(stderr,"addr 0x%x is too large. Maximum allowed is: 0x%x\n",addr, ADDR_MASK); 150 | goto exit0; 151 | } 152 | 153 | if ( inData & ~ DATA_MASK ){ 154 | fprintf(stderr,"inData value 0x%x is too large. Maximum allowed is: 0x%x\n",inData, DATA_MASK); 155 | goto exit0; 156 | } 157 | 158 | // enable write to EEPROM 159 | if (!HpCh_93c_Ewen(iIndex)){ 160 | goto exit0; 161 | } 162 | 163 | // WRITE command is 1|01| => 0x5 164 | cmd = 0x5 << ( ADDR_BITS + DATA_BITS ); 165 | cmd |= (addr << DATA_BITS); 166 | cmd |= inData; 167 | 168 | // NOTE: csBits must be 0 to keep MISO READ/BUSY status function!!! 169 | if (!HpCh_93c_SendCommand(iIndex, cmd,(ADDR_BITS+DATA_BITS+PREFIX_BITS), 0, &outData)){ 170 | goto exit2; // try to disable writes first... 171 | } 172 | 173 | // now we need to poll for results 174 | for(int i=0;i< HPCH_93C_WRITE_CMD_TIMEOUT_MS;i++){ 175 | cmd = 0; 176 | outData = 0; 177 | nanosleep( 1 * 1000 * 1000 ); // 1ms 178 | if (!HpCh_93c_SendCommand(iIndex, cmd, 1, 0, &outData)){ 179 | goto exit2; // try to disable writes first... 180 | } 181 | if (outData &1){ 182 | printf("DEBUG: WRITE succeed after %u ms wait\n",i+1); 183 | ret = TRUE; 184 | break; 185 | } 186 | } 187 | if (!ret){ 188 | fprintf(stderr,"ERROR: Timeout %u ms after WRITE\n",i); 189 | } 190 | exit2: 191 | // always lower CS after polling... 192 | cmd = 0; outData = 0; 193 | HpCh_93c_SendCommand(iIndex, cmd, 1, 1, &outData); 194 | 195 | // try to disable write on any circumstances 196 | //exit1: // unused label 197 | if (!HpCh_93c_Ewds(iIndex)){ 198 | ret = FALSE; 199 | } 200 | exit0: 201 | if (!ret){ 202 | fprintf(stderr,"%s failed\n",__FUNCTION__); 203 | } 204 | return ret; 205 | } 206 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/hpch_93c.h: -------------------------------------------------------------------------------- 1 | #ifndef HPCH_93C_H 2 | #define HPCH_93C_H 3 | // hpch_93c.h - Henryk Paluch's routines for 93LC86 EEPROM connected using CH341A adapter 4 | 5 | // bit numbers for D7-D0 in bit-stream modes 6 | #define HPCH_BIT_CS0 0 7 | #define HPCH_BIT_CS1 1 8 | #define HPCH_BIT_CS2 2 9 | #define HPCH_BIT_CLK 3 10 | #define HPCH_BIT_MOSI 5 11 | #define HPCH_BIT_MISO 7 12 | 13 | #define HPCH_MASK_CS0 (1 << HPCH_BIT_CS0) 14 | #define HPCH_MASK_CLK (1 << HPCH_BIT_CLK) 15 | #define HPCH_MASK_MOSI (1 << HPCH_BIT_MOSI) 16 | #define HPCH_MASK_MISO (1 << HPCH_BIT_MISO) 17 | 18 | 19 | #define PREFIX_BITS 3 20 | #define ADDR_BITS 11 21 | #define ADDR_MASK 0x7ff 22 | #define DATA_BITS 8 23 | #define DATA_MASK 0xff 24 | 25 | // WRITE command time is 5ms in data sheet - using 10ms 26 | #define HPCH_93C_WRITE_CMD_TIMEOUT_MS 10 27 | 28 | // max total bits in bit-stream = bits in DWORD (this program limitation) 29 | #define HPCH_93C_MAX_BITS 32 30 | 31 | // READ command - Read one byte from 93LC86 from address addr to *outData 32 | // ULONG iIndex - CH341A adapter SN or 0 33 | // DWORD addr - EEPROM address to read data from 34 | // DWORD *outData - pointer where to write data from EEPROM (currently only 8-bit = 1 byte) 35 | BOOL HpCh_93c_Read(ULONG iIndex,DWORD addr,DWORD *outData); 36 | 37 | // EWEN command - enable write operations on 93LC86 EEPROM 38 | // ULONG iIndex - CH341A adapter SN or 0 39 | BOOL HpCh_93c_Ewen(ULONG iIndex); 40 | 41 | // EWDS command - disable write operations on 93LC86 EEPROM 42 | BOOL HpCh_93c_Ewds(ULONG iIndex); 43 | 44 | // WRITE command - writes inData to 93LC86 EEPROM 45 | // ULONG iIndex - CH341A adapter SN or 0 46 | // DWORD addr - address to write data to 47 | // DWORD inData - data to write at specified address (currently only 1 byte used) 48 | BOOL HpCh_93c_Write(ULONG iIndex,DWORD addr,DWORD inData); 49 | 50 | 51 | #endif -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/stdafx.cpp: -------------------------------------------------------------------------------- 1 | // stdafx.cpp : source file that includes just the standard includes 2 | // ch341_spi_93lc86.pch will be the pre-compiled header 3 | // stdafx.obj will contain the pre-compiled type information 4 | 5 | #include "stdafx.h" 6 | 7 | // TODO: reference any additional headers you need in STDAFX.H 8 | // and not in this file 9 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "ch341dll.h" 18 | 19 | -------------------------------------------------------------------------------- /VS2010_sol/ch341_spi_93lc86/ch341_spi_93lc86/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /images/ch341-spi-5v.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hpaluch/ch341-spi-93lc86/a42b65eb683927d6513f4ab08ec68bb070e114fa/images/ch341-spi-5v.jpg --------------------------------------------------------------------------------