Anschlüsse laut Datenblatt
P0 1 • 18 P1 XTALIN 2 17 P2 n.c. 3 16 P3 XTALOUT 4 15 RxD Data+ (?) 5 14 TxD Uss (0 V) 6 13 USB D+ Upp (0 V) 7 12 USB D– Ureg 8 11 Ucc (5 V) Data– (?) 9 10 n.c.
Dieser 18-polige SMD-Schaltkreis hat folgende Eigenschaften:
Eine Nachbildung ließe sich mit PIC16F1454 realisieren. Die Vorteile gegenüber CDC (Communication Device Class) wäre ein stiller Windows-Installationsprozess ohne INF-Datei und das Funktionieren unter Windows 98/Me. (CDC wird nicht von Win98/Me unterstützt.)
Dieser Schaltkreis hat einen Silizium-Fehler! Erst nachdem er ein USB-Reset-Signal abbekommt, funktioniert dieser. Windows gibt beim Enumerieren (Anstecken) immer ein USB-Reset-Signal (Single-Ended-Zero für 50 ms) aus, Linux nicht (enumeriert dadurch schneller). Dadurch scheint dieser Chip unter Linux nicht zu funktionieren. Erst nachdem man diesen in Suspend schickt, klappt es. (Das Aufwecken erfolgt mit einem USB-Reset-Signal.)
Dies hat Ralf Burger dankenswerterweise herausgefunden und dokumentiert.
USB-Deskriptoren
Device Bus Speed: Low neuerdings Full Device Descriptor: bcdUSB: 0x0100 bDeviceClass: 0x00 bDeviceSubClass: 0x00 bDeviceProtocol: 0x00 bMaxPacketSize0: 0x08 (8) idVendor: 0x04FA (Dallas Semiconductor) neuerdings 0x1A86 (QinHeng Electronics) idProduct: 0x2490 (DS1490F - iButton) neuerdings 0xE008 bcdDevice: 0x0000 neuerdings 0x1100 iManufacturer: 0x01 "Hoitek Semiconductor" neuerdings "WCH.CN \1" iProduct: 0x02 "USB to Serial" iSerialNumber: 0x00 bNumConfigurations: 0x01 Configuration Descriptor: wTotalLength: 0x0029 bNumInterfaces: 0x01 bConfigurationValue: 0x01 iConfiguration: 0x04 "Sample HID" bmAttributes: 0x80 (Bus Powered ) MaxPower: 0x32 (100 mA) Interface Descriptor: bInterfaceNumber: 0x00 bAlternateSetting: 0x00 bNumEndpoints: 0x02 bInterfaceClass: 0x03 (HID) bInterfaceSubClass: 0x00 bInterfaceProtocol: 0x00 iInterface: 0x00 HID Descriptor: bcdHID: 0x0100 bCountryCode: 0x00 bNumDescriptors: 0x01 bDescriptorType: 0x22 (Report) wDescriptorLength: 0x0025 (37 Bytes) Endpoint Descriptor: bEndpointAddress: 0x81 (EP1IN) Transfer Type: Interrupt wMaxPacketSize: 0x0008 (8 Bytes) bInterval: 0x0A (10 ms) Endpoint Descriptor: bEndpointAddress: 0x02 (EP2OUT) Transfer Type: Interrupt wMaxPacketSize: 0x0008 (8 Bytes) bInterval: 0x0A (10 ms)
Auf der Suche nach einer Möglichkeit, diesen Konverter mittels Win32-API selbst anzusprechen, musste so viel wie möglich in Erfahrung gebracht werden.
Die zur Verfügung stehende Software für das Multimeter genügt nicht den Anforderungen. Insbesondere ist es nicht möglich, aus mehreren solchen USB-Seriell-Konvertern auszuwählen.
Megabyte-schwere quietschbunte chinesische Zappel-Software voller Übersetzungsfehler ist ohnehin nicht mein Fall, und ohne Quelltext wird man immer auf dem Trockenen sitzen. Ganz zu schweigen von Linux- und MacOS-Anwendung …
Mittels „USB View“ (usbview.exe) wurden die nebenstehenden Deskriptoren
ausgelesen.
Die Vendor-ID 0x04FA
scheint von Dallas Semiconducter
überlassen worden zu sein;
entweder ist Hoitek eine Tochterfirma, ein Joint Venture, oder aber die
(2500 US$ teure) Vendor-ID wurde einfach gemopst.
{0x06,0xA0,0xFF, //G Usage Page: 65440 (Unknown)
0x09,0x01, //L Usage: 1 (Unknown)
0xA1,0x02, //M Collection
0x09,0x01, //L Usage: 1 (Unknown)
0x15,0x00, //G Logical Minimum: 0
0x26,0xFF,0x00, //G Logical Maximum: 255
0x75,0x08, //G Report Size: 8 bits
0x95,0x08, //G Report Count: 8 elements
0x81,0x02, //M Input: 2 (Data,Variable,Absolute, …)
0x09,0x02, //L Usage: 2 (Unknown)
0x75,0x08, //G Report Size: 8 bits … superfluous here!
0x95,0x08, //G Report Count: 8 elements … superfluous here!
0x91,0x02, //M Output: 2 (Data,Variable,Absolute, …)
0x09,0x03, //L Usage: 3 (Unknown)
0x75,0x08, //G Report Size: 8 bits … superfluous here!
0x95,0x05, //G Report Count: 5 elements
0xB1,0x02, //M Feature: 2 (Data,Variable,Absolute, …)
0xC0}; //M End Collection
Die Windows-API-Funktion HidD_GetCaps()
beförderte folgende Zahlen zu Tage:
Usage | 1 | ||||||||||||||||||||||||
UsagePage | 0xFFA0 | ||||||||||||||||||||||||
InputReportByteLength | 9
OutputReportByteLength | 9
| FeatureReportByteLength | 6
| NumberLinkCollectionNodes | 1
| NumberInputButtonCaps | 0
| NumberInputValueCaps | 1
| NumberInputDataIndices | 1
| NumberOutputButtonCaps | 0
| NumberOutputValueCaps | 1
| NumberOutputDataIndices | 1
| NumberFeatureButtonCaps | 0
| NumberFeatureValueCaps | 1
| NumberFeatureDataIndices | 1
| |
Feature-Report (6 Byte)
#pragma pack(1) typedef struct { char ReportID; //nicht benutzt: 0 long BaudRate; //little endian char unknown; //ist gleich 3 * }HE2325U_Feature_t; #pragma pack* für 8 Datenbits, ohne Parität, 1 Stoppbit
Durch Ausspähen des Aufrufs von HidD_SetFeature()
(mit dem Debugger SoftICE) aus der vorhandenen chinesischen Software heraus
wurden die Daten des Feature-Reports erraten.
Die Software schickt fälschlicherweise 10 statt 6 Bytes; der Überhang (nur Nullen) wird vom HID-Treiber klaglos ignoriert.
Das Geheimnis des unbekannten Bytes kann erst gelüftet werden, wenn irgendeine Software ein anderes Byte als 3 schickt und (an der alternativen echten seriellen Schnittstelle) andere Parameter einstellt. Vermutung: Datenwortlänge, 0 = 5 bit, 1 = 6 bit, 2 = 7 bit, 3 = 8 bit.
Wegen der unklaren Gestaltung der Paritätsfehlermeldung ist eine Festlegung auf „keine Parität“ wahrscheinlich. Diese könnte sich gut und gerne in höherwertigen Bits versteckt halten.
Input-Report (9 Byte)
typedef struct { BYTE ReportID; //nicht benutzt: 0 BYTE DataLen:3;//Bit 2:0, s.u. BYTE Data[7]; //0..7 Datenbytes }HE2325U_Input_t;
- Bit 7:4 repräsentieren wahrscheinlich
das parallele 4-bit-Port.
Lieferten stets 1.- Bit 3 war stets 0. Fehler-Bit?
Mit der chinesischen Software ist es mir trotz SoftICE nicht gelungen, den Input-Report abzufangen!!
Dieser läuft normalerweise über die schnöde Win32-Funktion
ReadFile()
.
Daher blieb mir nichts anderes übrig, als ein Testprogramm zu schreiben
und durch „scharfes Hingucken“ die Bedeutung der Bytes zu erschließen.
Die Richtungsumschaltung des 4-Bit-Parallelports erfolgt vermutlich entweder mit dem Feature-Report, oder — was wahrscheinlicher ist — überhaupt nicht: Offene Kollektor-Ausgänge mit eingebautem (oder gar externem) Pull-Up.
Die Länge des Input- und Output-Reports mit 8 Byte führt zu guter USB-Bandbreitenausnutzung. Wäre er länger, müssten stets weitere USB-Interrupt-Transfers angestoßen werden, auch wenn weniger Daten vorliegen. (HID überträgt immer ganze Reports.) Eine feingliedrige, standardkonforme Beschreibung der Reports (obgleich mit „ausgedachten“ Usages) würde einiges Rätselraten ersparen.
he2325u.dll
zum ZugriffFunktionen
Vorläufige Implementierung!
void HeEnum(BYTE info[256])
HANDLE HeOpen(int n=0, int baud=0, DWORD FlagsAndAttributes=0)
int HeRead(HANDLE, BYTE *buf, int len,
UINT IntervalTimeOut=100, UINT TotalTimeOut=2000, OVERLAPPED*o=0)BOOL HeWrite(HANDLE, const BYTE *data, int len, OVERLAPPED*o=0)
BOOL HeReadReport(HANDLE, HEINPUTREPORT*, OVERLAPPED*o=0)
BOOL HeWriteReport(HANDLE, const HEOUTPUTREPORT*, OVERLAPPED*o=0)
void ComEnum(BYTE info[256])
Letzte Änderung 02/11: Parameter für asynchronen Zugriff o.
Mit diesem Wissen gestaltet sich der Zugriff auf solche HID-basierten USB-Seriell-Umsetzer recht einfach.
Im Beispiel wird dem Problem etwas Aufmerksamkeit geschenkt,
dem Benutzer unterscheidbare und an USB-Buchsen gebundene
„Nummern“ anzubieten.
Einfach „erster gefundener Adapter“, „zweiter gefundener Adapter“ usw.
ist auf Dauer konfus, weil man nie weiß, welcher gerade wo ist.
Der angezeigte Name ist dann beispielsweise "USB1", "USB2" usw.
(nicht diese Monster aus
SetupDiGetDeviceInterfaceDetail()
!)
Die Zuordnung wird in der Registrierung gespeichert und von der DLL
automatisch verwaltet.
(Dummerweise hakelt die derzeitige Implementierung unter Vistas UAC.)
Die 4-Bit-Parallelport-Bits werden vorläufig ignoriert.
Einige neuere Geräte, namentlich UT109, sind mit dem USB-Seriell-Wandler CP210x von Silicon Labs ausgestattet. Allerdings wird da standardmäßig ein Treiber installiert, der kein COM-Port abbildet.Funktionen
void HeEnum(BYTE info[256])
void FillCombo(HWND hComboBox, UINT flags)
void DrawCombo(HWND hComboBox, DRAWITEMSTRUCT*dis)
typedef struct _HE{ // „Basisklasse“
HANDLE hCom;
DCB dcb;
COMMTIMEOUTS to;
OVERLAPPED o;
HANDLE hCancel;
// weitere Daten je nach tatsächlicher Schnittstelle
}*HHE;HHE HeOpen(int n=0)
KonstruktorBOOL HeClose(HHE)
DestruktorBOOL HeSetConfig(HHE)
int HeRead(HHE, BYTE *buf, int len)
int HeWrite(HHE, const BYTE *data, int len)
int HeFlush(HHE, UINT action)
Ähnliche Situationen gibt es auch mit FT232, FT245 usw., aber da ist mir bisher kein derartiges Gerät über den Weg gelaufen.
Die künftige DLL fasst alle derartigen Schnittstellen zusammen und vereinfacht auch den Zugriff auf die konventionelle serielle Schnittstelle in einem Worker-Thread.
Die Funktionen mit HHE sollten im Worker-Thread laufen,
die anderen im GUI-Thread.
Das ist aber kein Muss; nur hat man dann das Problem,
dass die synchronen Funktionen HeRead, HeWrite und HeFlush blockieren können.
Der Parameter hCancel
ermöglicht das Beenden der Blockade
im Worker-Thread vom GUI-Thread aus.
Die übliche Anwendung ist, dass im GUI-Thread die Schnittstelle gewechselt wird.
Dann muss im Worker-Thread der Schnittstellen-Zugriff abgebrochen werden,
dann die Schnittstelle geschlossen und eine andere geöffnet werden.
Der GUI-Thread ruft dazu SetEvent(hCancel)
auf,
und wartet auf die Reaktion des Worker-Threads mit irgend einem anderen Handle
per WaitForSingleObject()
.
Recht häufig wird man den Worker-Thread einfach beenden,
dann kann der GUI-Thread einfach auf das Thread-Ende warten:
WaitForSingleObject(hThread)
.
Tut man das ohne hCancel
, muss man mit kurzen TimeOuts arbeiten
(das erzeugt unnötige Systemlast und überflüssigen Output im Port Monitor),
oder aber man hat es mit klassischen Bugs zu tun
(Software braucht „ewig“ aufs Beenden oder Rekonfigurieren).
Intern arbeiten die asynchronen Varianten von ReadFile()
usw.,
d.h. das Datei-Handle hCom
ist mit FILE_FLAG_OVERLAPPED
geöffnet.
hCancel
ist vom Typ Manual Reset.
Die Strukturen DCB dcb
und COMMTIMEOUTS to
werden beim Öffnen
mit den vorgefundenen Vorgabe-Werten gefüllt.
Anwenderseitige Änderungen müssen mit HeSetConfig()
zur DLL
gemeldet werden, aber je nach tatsächlicher Schnittstelle können die Daten
auch direkt genutzt werden.
Das heißt, die Strukturelemente sollten zwischendurch nicht geändert werden.
Der neueste Schrei seit 2021 ist die experimentelle Implementierung von
WebHID
in Chrome-basierte Desktop-Browser.
Damit lassen sich diese Multimeter direkt im Browser auslesen.
TODO