rpcope1 / Hantek6022API

Hantek 6022BE Python API for Windows and Linux.
GNU General Public License v2.0
126 stars 41 forks source link

USB reverse engineering #2

Closed jhoenicke closed 9 years ago

jhoenicke commented 9 years ago

Hi, I have looked a bit into the protocol and I thought I share what I found out.

Control message with bRequest == 0xa0 is used to upload firmware. The wValue is the start address, the next package contains the data. I'm currently trying to look into the firmware (8051 code) to see if there is something interesting. Also, see http://www.cypress.com/?docID=48811

Control message 0xa2 is calibration data, 0xe0/0xe1 is mV/Div for channel0/1, 0xe2 is sample rate and 0xe3 triggers the oscilloscope.

rpcope1 commented 9 years ago

Very cool, you're already several steps ahead of me. :)

I'll see if I can't reproduce all of these locally, and I'm going to create a new markdown doc in the repo to note all of these. Thanks so much for sharing!

rpcope1 commented 9 years ago

So Linux support is there now, pretty much, if that's your cup of tea (it is for me). I'll try and write up all of what I found today relatively soon on the (relatively straightforward) protocol.

As far as modifying the actual firmware that gets flashed to the scope, I think there might be some potential there, as Hantek sells some identical looking scopes that are supposedly "faster", and this might mean a relatively basic unlock kind of like a lot of the Rigol scopes. This also might make it relatively straightforward to add an external trigger (need to reopen the case).

dc-to-daylight commented 9 years ago

Wow what fantastic news! Thank you to all of you for your great work with this device - without it, it would be next to useless.

rpcope1 commented 9 years ago

It's still not great, but I am beginning to think more and more it's better to look at it as a simple USB ADC than an oscilloscope lol.

On Mon, Apr 20, 2015 at 1:46 AM, curtis-b notifications@github.com wrote:

Wow what fantastic news! Thank you to all of you for your great work with this device - without it, it would be next to useless.

— Reply to this email directly or view it on GitHub https://github.com/rpcope1/Hantek6022API/issues/2#issuecomment-94383177.

jhoenicke commented 9 years ago

Wow, great work.

It's fantastic, now I can get up to 7MSamples in a row and it also works with 4, 8, and 16 MS/s (my next SPA uses a device that doesn't filter out high frequencies). I guess there is a bulk transfer limit that prevents me from going to 8 MSamples (16 MByte) and beyond.

Of course, it's also very convenient that it now works under Linux.

rpcope1 commented 9 years ago

So, interesting update: check out example/example_linux_perf_test.py. I am able to squeeze ~18 MB/s per channel on the highest sampling rate, and it's important to note we're most likely at the point where the Python interpreter itself is becoming a bottleneck.

I am going to Cythonize the code later, and consider writing a pure C binding on libusb instead of wrapping the Python libusb package, which might get you to approximately the 24 MS/s performance this thing may be capable of. I think this, with a clever threaded implementation would allow you to get software triggering up to about 10-15 MHz, and more or less make the device do what it was supposed to do. :)

jhoenicke commented 9 years ago

I just tried getting 48 MHz samples. In that case the 8MB blocks are not continuous, so the USB bulk transfer doesn't produce continuous samples.

But USB 2.0 is specified as 480 Mbps. Thats bits, not bytes. At 16 bit for two channels the limit would be 30 MSamples per second. I get around 20 MSamples. There is probably some protocol overhead (start/stop bits, frames?)

Maybe one can tell the device to send only one channel via USB. I'm still looking into the firmware.

jhoenicke commented 9 years ago

BTW, my analysis of the firmware makes progress.

The control messages supported by the firmware are 0xa2, 0xb2, 0xe0-0xe3 and 0xe7 (0xa0 is handled by the chip outside of the firmware). I still have to understand what 0xb2 and 0xe7 are good for.

The voltage selectors (0xe0 and 0xe1) only support the values 1, 2, 5,10. Every other value is a no-op. The effect of these is that three bits in PORT C are set, which probably drive the adc. One of these three bits is always cleared for all four possible values.

The sampling rates (0xe2) are 48,30,24,16,8,4,2,1,50,20,10. All but the last three give the frequency in MHz, the last three in multiples of 10kHz. I'm still trying to understand the code; it reprograms the waveform of the ez-usb chip.

The message (0xe3) seems to clear the fifo. It is not necessary to sent before every bulk transfer; you can do several bulk transfers in a row. I'm wondering if one can do a continuous transfer using the asynchronous api for bulk transfers.

The message (0xb2) replies with a single bit (0 or 1). It seems to be related to high speed usb mode.

jhoenicke commented 9 years ago

I have now managed to get a clean dump of 30 MHz with 1 channel (16MSamples). This requires a small firmware patch (and after the patch it only supports the first channel). 48 MHz with 1 channel is too fast for USB bulk transfer. I have yet to write some firmware code to switch to one channel via an usb control packet (it's always channel 1 for limitation of the hardware).

It's possible to support more intermediate sampling rates like 15 MHz, 6MHz, 2 MHz, basically every divisor of 30 and 48 is possible. One could even do something like 40 MHz, but then the samples would not be evenly spaced.

There seems to be a bug in the firmware, for the 100/200/500 kHz code, that makes the sampling interval off by two cycles (1/24 µs). I'm wondering if someone can confirm this, or if the tolerance of the 48 MHz oscillator is too big.

The 24 MHz mode is broken. I can try to fix this, but it will be too fast for 2 channel input and for 1 channel input the 30 MHz mode is superior.

rpcope1 commented 9 years ago

Hmm, very cool.

I can confirm anything you find, if you tell me the steps you took to get there. I did notice that stitching together samples for the sub-1 MHz sampling speeds seemed to be missing things (i.e. the sample square wave wasn't "square" everywhere). I am still trying to get MCU 8051 IDE working here so I can disassemble the code firmware, and try and work my own patches maybe with sdcc. What tool chain are you using to interact with the device? As far as the bad tolerance of the 48 MHz oscillator, it appears the FX2LP is actually attached to a 24 MHz oscillator. I will see if I can't look at the xtal output on another scope later and see if it's particularly bad or anything.

I agree with you, the 30 MHz mode is probably the best we're going to do and gets some real device functionality out of it. Do you think a 15 MHz mode for dual channels can be likewise done without skipping data? If so, and we're getting all of the data pumped to the host, I can certainly write a good trigger implementation on the host that gives you some actual triggering functionality. Really just having a couple of modes where all of the data can be transferred continuously, and then handling the nicer stuff (like triggering and reducing) on the software side would be an enormous step forward. But at any rate, excellent work! When I get home today, I'm to go try and write more of this up and put it into the repo, and the weirdness with the slower sampling modes.

On the hardware side, I don't know if you've noticed, but the scope may be slightly noisy sometimes (10-15 +/- mV pp noise). I had managed to quiet it down originally by adding a couple of film caps across C107 and C8 (probably about 0.1 uF) and putting a couple of 100 uF electrolytic caps across C12 and C15. I also wrapped the big DC/DC converter in foil and grounded it (this is easily the noisiest component in this little scope, and is somewhat in appropriate for what they're using it for here...). This mods seemed to get me into the +/- 3 mV noise range. I am going to investigate the noise in the device further, and look at other mods that might clean it up, and can report back if you're interested. Also, very interesting is that J3 looks like you can solder another BNC connector in, and with an SMD resistor and cap to form the pull-up circuit, add a real external trigger to the hardware. It appears J3 runs through a resistor to one of the ports on the FX2LP chip, and may be possible (pending further investigation) to use as a real trigger.

Again, thanks for all the work you've done. :)

On Wed, Apr 22, 2015 at 8:40 AM, Jochen Hoenicke notifications@github.com wrote:

I have now managed to get a clean dump of 30 MHz with 1 channel (16MSamples). This requires a small firmware patch (and after the patch it only supports the first channel). 48 MHz with 1 channel is too fast for USB bulk transfer. I have yet to write some firmware code to switch to one channel via an usb control packet (it's always channel 1 for limitation of the hardware).

It's possible to support more intermediate sampling rates like 15 MHz, 6MHz, 2 MHz, basically every divisor of 30 and 48 is possible. One could even do something like 40 MHz, but then the samples would not be evenly spaced.

There seems to be a bug in the firmware, for the 100/200/500 kHz code, that makes the sampling interval off by two cycles (1/24 µs). I'm wondering if someone can confirm this, or if the tolerance of the 48 MHz oscillator is too big.

The 24 MHz mode is broken. I can try to fix this, but it will be too fast for 2 channel input and for 1 channel input the 30 MHz mode is superior.

— Reply to this email directly or view it on GitHub https://github.com/rpcope1/Hantek6022API/issues/2#issuecomment-95213246.

jhoenicke commented 9 years ago

The firmware bug for 100kHz - 500kHz is more a theoretical issue. The number in the firmware is off by one, which should result in a 0.4 % (for 100kHz) - 2 % (for 500 kHz) error in the sampling rate. I'm not sure if this error is even measurable. I just uploaded a fix for it to my branch (untested).

I was running 16 MHz with two channels just fine, although I used only one of them. I can read up to 16 MSamples per block, i.e. 8 MSamples per channel.

Regarding continuous gap-free measurements, one thing we could try is using asynchronous usb calls. Don't send e3 packets in between, as these clear the FIFO. Instead just send one bulk read after the next. With the asynchronous api one can probably always have two bulk transfers in the queue, so that when the first finishes, the next can immediately start. The timing is very important because the device has only a 2kB fifo (well the real limit is 4kB but that would require some changes to the firmware and the driver to switch to EP2 instead of EP6). So the two bulk reads must be not more than 60 µs apart. I have never tried asynchronous accesses with python, though.

Usually, for timing sensitive data, one should use isochronous transfer instead of bulk transfer, but that has a limit of 24 MB/s, which is worse than what we can already achieve. Also it would again require changes to the firmware.

jhoenicke commented 9 years ago

I have a new branch async in my repository: jhoenicke/Hantek6022API@df668ef2f49506145c433b1d9e7d96e3241e5d3c This can do continuous transfers at 16 MB/s. For higher rates there can be a small gap (about a ms) between blocks.

rpcope1 commented 9 years ago

Interesting. I merged your master in; I have some ideas about the async secret sauce on the host side that I'm currently investigating as well as your async branch.

jhoenicke commented 9 years ago

If you want reliable continuous sampling, I think getting the isochronous mode would be best. With 24 MB/s it should support sampling 1 channel at 24 MHz (this mode needs to be fixed though) or 2 channels at 12 MHz (a new mode). I can look into this, but it is probably not that trivial.

BTW, I also know where the 1kHz test signal is produced and how to turn the LED red, green or off. Does it make sense to change that behaviour??

rpcope1 commented 9 years ago

I had looked at isochronous, I think it would be interesting to test it and see what is possible, if time/resources permit, but it's not probably something we have to have; I've been reading up, and it looks like bulk data transfer is also "good enough" for many projects utilizing a chip like this (like all the RTL-SDR stuff) that probably have similar timing requirements. From my preliminary search, it looks like isochronous mode is not well supported/understood on the FX2LP chips (I don't know that for a fact though), and most people seem to avoid it. Still, I think it looks some what promising (and you'd figure this was what isochronous was designed to do, anyways), so I'll take a look into as well when I get the change, and we might consider it moving forward. Let me take some time to port the async changes, and we'll see how close we can get the device to pumping out all of the data continuously. You might just have to put a warning sticker on everything that the scope should be on a USB bus all by itself, so that nothing is competing with the bulk transfers.

Being able to turn the red/green LEDs on and off would also be neat, might make multi-device enumeration much easier; knowing a little more about how the reference signal is generated might be neat, but modifying it's behavior is probably not super critical.

Again, awesome work dude, I just want to reiterate, thanks so much for your help and support here!

On Thu, Apr 23, 2015 at 5:12 AM, Jochen Hoenicke notifications@github.com wrote:

If you want reliable continuous sampling, I think getting the isochronous mode would be best. With 24 MB/s it should support sampling 1 channel at 24 MHz (this mode needs to be fixed though) or 2 channels at 12 MHz (a new mode). I can look into this, but it is probably not that trivial.

BTW, I also know where the 1kHz test signal is produced and how to turn the LED red, green or off. Does it make sense to change that behaviour??

— Reply to this email directly or view it on GitHub https://github.com/rpcope1/Hantek6022API/issues/2#issuecomment-95542998.

rpcope1 commented 9 years ago

Just FYI, I took what you were thinking about the async and ran with it on my async branch. You'll have to pull and setup the python-libusb1 package from my github repo for it, as there is a bug in the released one that has broken the async stuff.

Preliminary, we're managing to hold on to 95+% of our packets at 16 MSa/s, and they all appear to be continuous, so there is a reasonable opportunity to have non-crap software triggering as reasonably high speeds.

I also finally moved the source into hex files; and attempted to disassemble the hex. Apparently no 8051 assembler agrees on syntax; are you using Keil or SDCC or something else for assembling the code? I'm still trying to get a grip on what the firmware is doing.

jhoenicke commented 9 years ago

I'm a bit reluctant to check in my disassembled and partly commented firmware. I can send it to you if you can give me an email address. I plan to make at least a list of the functions and their parameters.

So far I used the compiler and disassembler built into mcu8051ide. The compiler when run on the small .asm file I checked in, will create the five additional lines in the ihex file, which I just added manually.

rpcope1 commented 9 years ago

Understood, that's an excellent point. Probably only good to ship the repo with the ihex files, no need to provide the source; I was only curious as to what you were using to assemble/reassemble the firmware.

vpelletier commented 9 years ago

As far as reassembling, fx2lib + sdcc worked fine for the FX2-using device I worked on (an USB protocol analyser). It provides definitions for all registers, helper macros and boilerplate code.

rpcope1 commented 9 years ago

Oh cool, thanks dude! fx2lib looks like a real winner here (I don't feel so bad ordering a couple extra fx2lp dev boards now :+1: ).

baruch commented 9 years ago

@jhoenicke , the three bits set for the voltage selection drive the analog passthrough chip and essentially select for the resistor value that feeds into the gain opamp. The inputs for that chip are 3 bits which selects one of 8 possible paths, only three (or four, don't exactly remember now) of those paths are in fact usable so the third top level bit isn't used and they could just not bother controlling it every time.

jhoenicke commented 9 years ago

I think we have the things discussed in this issue covered by the current driver.