Futaba MDM166A – C++Builder/Delphi example
This page tries to give some clues on programming the Futaba MDM166A USB display using Delphi/C++Builder under Windows. First of all we need an additional component created by the unforgotten Robert Marquardt. The component does most of all work, as it provides an easy to use interface to the complex WinAP-interfaces to enumerate, accessing and working with USB devices. Mike Lischke provides access to the latest revision of the “HID Controller” (TJvHidDeviceController) on his website. There is no need to install it as component – it can be instanciated dynamically prior use. On all following steps, I assume to work on an instance of the TJvHidDeviceController class.
First of all we need to open (check-out) the USB device to get exclusive access and handles. These handles and further informations are held by the TJvHidDevice class. To check-out a device we need a variable of this type which will be used when calling the CheckOutXXX() methods. As we know the Vendor ID (VID) and Product ID (PID), we can use the CheckOutByID() method.
var
lDevice: TJvHidDevice;
begin
// check out device
if JvHidDeviceController1.CheckOutByID(lDevice, $19c2, $6a11) then
begin
// open device
if lDevice.OpenFile then
begin
// read on...
lDevice.CloseFile;
end;
JvHidDeviceController1.CheckIn(lDevice);
end;
end;
Now it is the time to open the inofficial protocol documentation written by me (can be found here) and start to send commands to the display. The display requires data packets in a predefined size. This size can be retrieved using the property Caps.OutputReportByteLength of the local lDevice instance (returned on check-out). On the Futaba MDM166A display the returned value should be 65 bytes. To be prepared for the future, use a dynamic array sized by the property.
var
lBuffer: array of byte;
begin
// check-out & open device
// size the buffer & initialize with zeros
SetLength(lBuffer, lDevice.Caps.OutputReportByteLength);
FillChar(lBuffer[0], lDevice.Caps.OutputReportByteLength, 0);
// so, read on ...
end;
The first byte of the buffer contains the ReportID used to transmit the data to the device. This ReportID need to be zero for the Futaba MDM166A display. All other bytes (64 bytes) are raw data and needs to be filled according to the protocol documentation. The first byte of the raw data represents the amount of data following.
As an example we lit the mute symbol on the display. So the command data consists of the prefix (0x1b), the command id (0×30 – SetSymbol), the symbol id (0×05 – mute) and the symbol state (0×01 – enable). The command data overall contains 4 bytes, so the complete buffer including the ReportID should look like this:
0x00, 0x04, 0x1b, 0x30, 0x05, 0x01
ReportID, DataSize, CmdPrefix, CmdSetSymbol, SymbolID, SymbolState
To send the data to the display use the WriteFile() method of the lDevice instance. Check the result and the amount of bytes written. Now, all codes combined:
var
lDevice: TJvHidDevice;
lBuffer: array of byte;
lWritten: LongWord;
begin
// check out device
if JvHidDeviceController1.CheckOutByID(lDevice, $19c2, $6a11) then
begin
// open device
if lDevice.OpenFile then
begin
// size the buffer & initialize with zeros
SetLength(lBuffer, lDevice.Caps.OutputReportByteLength);
FillChar(lBuffer[0], lDevice.Caps.OutputReportByteLength, 0);
// build data
lBuffer[0] := $00; // ReportID
lBuffer[1] := $04; // data size
lBuffer[2] := $1b; // command prefix (ESCape)
lBuffer[3] := $30; // command (SetSymbol)
lBuffer[4] := $05; // SymbolID (mute)
lBuffer[5] := $01; // SymbolState (on)
// send to device
lDevice.WriteFile(lBuffer[0], lDevice.Caps.OutputReportByteLength, lWritten);
// close USB device
lDevice.CloseFile;
end;
// check in device
JvHidDeviceController1.CheckIn(lDevice);
end;
end;
Hallo robomrburns,
kleiner Tipp als Erstes: mit Deiner alten e-mail Adresse warst Du nun schon von mir bestätigt worden. Wenn Du bei dieser bleibst, dann erscheinen Deine Kommentare auch direkt und ich muss sie nicht einzeln freischalten.
Und zu Deiner Frage: Grundsätzlich solltest Du Dir mal das Ereignis OnDeviceDatan anschauen und mit einem Handler belegen. Darin solltest Du dann bei einem Aufruf die Zeichen entsprechend einlesen (werden per Pointer Größe der Daten beim Aufruf schon mit übergeben). Falls das Gerät unterschiedliche Daten sendet, dann müsstest Du noch zusätzlich auf die ReportID achten.
Viele Grüße,
Muetze1
Hallo Muetze1,
ich bin schon echt weit gekommen! Mein Display hat zusätzlich 3 Knöpfe. Weißt du, wie man sie abfragen kann? Ich habe die RAW data mal mit einem usbSniffer abgefangen.
da kommt dann sowas wie 00 11 39 00 00 00 00 genaues poste ich wenn ich zu Hause bin
kann man, wenn man deine procedure umschreibt von writefile zu readfile ein Ergebnis bekommen? Oder gibt es andere Möglichkeiten sowas zu erkennen? Danke im Vorraus.
Hallo robomrburns,
sende einfach bestimmte Datenmuster an das Display. Im Normalfall entspricht ein gesetztes Bit einem leuchtenden Pixel. Von daher sende bekannte Bitmasken und schau welche Pixel leuchten. Davon kannst Du Dir dann die Datenorganisation des Displays ermitteln.
Zu der Bitverarbeitung schau einfach mal auf die Websites Deines Vertrauens oder auch z.B. auf Wikipedia: Bitmaske.
Viele Grüße,
Muetze1
Also… Ich habe es geschafft! Das Display ist aus einem Zotac Nitro Overclocking Controller.
Alle Befehle aus deiner Dokumentation klappen, bis auf die Uhren, vermutlich hat das VFD einfach keinen Uhrenchip.. Ist ja auch nicht so wichtig! Also mit dem Command $30 habe ich es geschafft alle Elemente, wie das ZOTAC oder die Balken rechts anzusteuern.
Jetzt scheitere ich an dem Display unten. Ich kann mit dem Command $70 pixel an und ausschalten. Nur habe ich das System dahinter noch nicht. Es wird aber anscheinend genauso sein wie beim mdm166 nur halt kleiner. Vielleicht kannst du mir ja weiterhelfen.
P.S. Dein Beispielcode klappt nun und ist super! Danke.!
Hallo robomrburns,
das Display Futaba 198aa ist mir unbekannt und folgt auch nicht den mir bisherigen bekannten Namen der Firma Futaba. Auch bezweifle ich, dass der Befehlssatz auch nur annähernd gleich ist und das die Anleitung überhaupt etwas bringt. Ich habe schon ein paar Displays per USB angesprochen, aber bisher war keines wirklich ähnlich in der Verarbeitung der Daten und Protokoll.
Nun zu Deinem Delphi Problem: die Variable JvHidDeviceController1 ist vom Typ TJvHidDeviceController und sollte eigentlich dem Standardnamen entsprechen, den die Komponente bekommt, wenn sie frisch auf die Form gezogen wird. Da Du dies gemacht hast, sollte Deine Komponente genauso heißen. Alternativ kannst du die Instanz auch jederzeit selbst per Hand erzeugen und am Ende freigeben, wie ich es in der Anleitung schon erwähnt habe.
Viele Grüße,
Muetze1
Hi,
Ich habe ein Futaba 198aa Display und experementiere ein wenig herum. Mein Problem ist nur, ich bekomme es nichtmal eingeschaltet. Es geht nur an, wenn das mitgelieferte Programm an ist. Wie genau sieht dein Programm aus? welche klassen brauche ich usw.?
Mein Problem ist :
[Error] Unit2.pas(15): Undeclared identifier: ‘JvHidDeviceController1′
wie soll ich JvHidDevicecontroller1 deklarieren? Ich habe die komponente auf die form gezogen
Hallo Datenschredder,
oh, da habe ich dann deine Anfrage etwas missverstanden. Auch hätte ich dir in Sachen C# keine Hilfestellungen geben können. Die Theorie lautet aber sprachenunabhängig:
1. Entweder auf einen Canvas/Bild mit dem gewünschten Font die Ausgabe machen und dann über die Pixeldaten des Bildes die Raw-Bytes für das Display erzeugen, oder aber….
2. … mit einem Font in lesbarem Format die Raw-Bytes anhand des Zeichensatzes direkt zusammen bauen.
Du hast ja nun eine gute Lösung für den zweiten Ansatz gefunden und dank deiner Rückmeldung steht diese nun hier auch allen anderen Interessierten zur Verfügung.
Vielen Dank dafür,
Muetze1
Hallo Mütze,
Danke für deine Antwort. Deine sehr hilfreiche Doku hatte ich mir schon durchgelesen und wusste nur noch nicht, wie ich auf einfachem Wege einen Zeichensatz erstellen bzw ein Zeichen dann in die entsprechenden Bytes konvertieren kann.
Gestern habe ich den LED-Font-Creator entdeckt:
http://www.codeproject.com/KB/miscctrl/LedDisplay.aspx
Er erzeugt für einen Zeichensatz ein XML-File, in dem die Bitzustände als ulong-Daten abgelegt sind. Das erfolgt zwar in Leserichtung (links nach rechts, oben nach unten) und nicht spaltenweise aber das entsprechend umzuwandeln ist
ja nicht so schwer. Schön ist, dass bereits ein Zeichensatz als Beispiel beiliegt
Grüße
Datenschredder
Hallo Datenschredder,
das Display selbst nimmt nur Grafikdaten entgegen. Dabei legt man am besten vorher mit dem Befehl “Set RAM Address” (siehe meine Dokumentation, Seite 7) fest, an welcher Stelle man das Display beschreiben möchte. Danach können mit dem Befehl “Set Pixel Data” die Grafikdaten an das Display gesendet werden. Diese sind, wie in der Dokumentation beschrieben, spaltenweise aufgebaut. Dabei besteht eine Spalte aus 16 Pixel und wird mit 2 Bytes Pixel Daten wiedergegeben (aus diesem Grund die Empfehlung mit geraden Adressen bei SetPixelData zu arbeiten, also immer eine Spalte zu adressieren). Wenn also ein Wort aus 2 Bytes besteht, dann wäre das Bit 0 von dem Wort das oberste Pixel der Spalte und das Bit 15 das unterste Pixel der Spalte. Somit würde ein SetPixelData Aufruf mit einem Datenwort von 0×5555 jeden zweiten Pixel der aktuellen Spalte einschalten, alle andere aus.
Somit hat man eine rein monochrome Zeichenfläche auf dem VFD in einer Auflösung von 96 Spalten mit je 16 Pixeln zur Verfügung. Ein Font zur Darstellung von Text muss somit von dem Programm selbst gerendert werden. Das Display hat einen Font eingebaut für die Uhrzeit und Statustexte, doch es gibt keinen Befehl diesen zu nutzen. Ich habe alle von mir dokumentierten Kommandos durch reines try-&-error ermittelt und dabei auch immer versucht eine solche Text-Rendering Funktion zu finden – leider ohne Erfolg.
Mit freundlichen Grüßen,
Muetze1
Hallo Mütze,
ich habe das Futabe MDM166A und experimentiere mehr spaßeshalber mit diesem Display (Visual Studio / C#)
Ich kann das VFD hinsichtlich der fixen Symbole ansprechen. Jedoch habe ich noch keine Idee, wie man Zeichen ausgibt, sprich: einen Zeichensatz anlegt und diesen in die entsprechenden Rawbytes übersetzt.
Könntest du mir bitte etwas “aus dem Nähkästchen” erzählen?
Danke für deine Hilfe.
Gruß
Datenschredder