Most of the Arduino Zero/MKR-based oscilloscopes are either:
- using the computer's sound card
- using the USB port but are limited by the Bulk USB transfer protocol, ie. 64bytes per ms following a 27 byte per ms transfer request (USB 2.0)
The Arduino Zero/MKR features a 350kSps 12-bit ADC. Which are awesome specs thinking about it. However, unleashing this capability is another story. The SAMD21G18 hosted in the Arduino Zero/MKR provides a USB2.0 full speed.
Consequently, accessing the 350kSps ADC, requires the oscilloscope solution to capture these samples, transfer them over the USB2.0 line and project them on the screen at a 60Hz rate, that is within a 16ms time frame. Moreover, to allow accurate timing of the samples, the ADC readings need to be accompagnied by a time measurement.
This project goes on the quest to crack this challenge!
The below image lays out the main features of the application:
60hz or 16ms per frame vs ADC: 350kSps
=> 5600 samples per 16ms,
1 sample = overhead bytes (integrity at least) + 12/16/8 bit sample + 12/16/8 bit timestamp = 3 to 6 bytes
=> 5600 x 3 to 6 bytes per 16ms = 16 800 to 33 600 bytes per 16ms
USB2.0 Full Speed delivers:
- in USB Bulk transfer: 64bytes per ms ~ 1024bytes per 16ms --> To little!!
- in USB Isochronous transfer: 1023bytes per ms ~ 16 368bytes per 16ms --> OK
The above (wrongly) assumes USB transfer can occur in parallel with the ADC capturing.
=> implement multi-threading accross the line were possible, because it will be necessary to get as close as possible to our goal.
openGL requires:
- transformation of ADC readings and timestamp data into {-1;1} data. We want to do this on the CPU (judged fastest) in another thread.
- building of screen. Done on GPU by OpenGL
The above example (assuming readings are accurate), show:
We are sampling at 270kSps (when we are sampling, remember USB transfer on Arduino board will take processor time as well).
The time to collect and send the 1023byte data accross the USB line takes around 6,7ms: 1023 bytes data here equals: 1023 x 8 / (12 + 12 + 7) = 264 samples repeated 4 times to attain 1024 samples (theoretically it would only take 4 ms).
This data is transformed, build and drawn in.15ms+.35ms+1.6ms ~2.1ms.
The total frame rate openGL is measuring 9.2ms (letting the program run as fast as possible). Hence, we could try to increase the sample reading to 2048, without sacrficing too much screen refresh rate.
As we wrote above, we need to implement multiple threads to allow parallel processing as much as possible.
The application features 3 threads:
1) C# graphical user interface
(split from other threads to have the relatively slow UI interfere at least as possible with the main task, ie. capture USB and project on screen)
2) C++ DLL OpenGL. A native C DLL to wrap the OpenGL window. The benefit is easy portability of the code towards other platform / UI. OpenGL is open source and one of the fastest out there.
3) C++ DLL USB. A native C DLL to wrap the winUSB driver. Reason for the choice was that the winUSB.sys USB driver is on every Windows computer and it supports isochronous data transfer. Isochronous data transfer is used for streaming data. It leaves behind data integrity checks for speed. In this way you can tranfer 1023bytes per ms.
By implementing a muli-threading scheme, the screen can build while the other thread is capturing the new data from the USB line, while the user is hovering its mouse over the user interface.
Installing the application:The application is not that user friendly to be used:
1) The Arduino board (or any other ADC / USB capturing device) needs to have Isochronous USB datatransfer implemented. The ArduinoCore does not support this by default. In the sketch provided in this project, I provide the Arduino code to extend the ArduinoCore with an Isochronous USB interface (without losing the possibility to still use the Serial.print capability). The extension can be used in the regular Arduino IDE (so no need to change the ArduinoCore itself).
2) The WinUSB.inf driver file needs to be installed by installing it running Windows in test mode. This because of a lack of driver certificate.
3) Currently I have left the YetAnotherUSB Oscilloscope software uncompiled on the Github. This to invite users to go into the code and adjust, compile and build on their own.
Future improvements:
The YetAnotherUSB Oscilloscope software is developed on VS 2019 with latest.NET framework and tested on 64-bit. In other words, the application is relatively untested and underdeveloped to run on multiple platforms (32 bit or linux).
Also the multi-threading has not been made super robust. (Possible infinite loops are possible in extreme cases, eg. unplugging the USB whilest reading, etc.)
Some features are still missing, ie. FFT analysis, triggers and memorizing capability.
I hope this can be inspiring for you and might you can use / borrow the code for your own (further) development.
Keep making!
Comments