Open Source Visual C++ Class for USB Generic HID Communication
Introduction
If you've tried to build your own PIC USB devices (perhaps following my earlier tutorial Building a PIC18F USB device) you will have noticed that the Windows host code (based on the Microchip USB stack examples) is far from clear on how to communicate with the device. To make matters easier for people who are not familiar with the intricacy of Windows programming and USB I've created a generic Visual C++ Class which handles all of the device communication issues including the plug and play aspects of device attachment and detachment detection.
The generic class is suitable for all types of Generic HID USB devices (including devices using the generic HID protocol based on common microcontrollers supporting USB communication other than the PIC18F).
In this article I will cover how to use this class, how the class functions and also give some practical examples of how it can be used to make your own Windows software.
Please note: This article and associated software is no longer maintained. It is superseded by my C# library which you can read about by following this link: Open Source Framework for USB Generic HID devices based on the PIC18F and Windows
Public Methods
The public methods of the class are those which are exposed to the programmer using the class. You can utilise the class using these methods without needing to dig into the code and understand how it works. The following sections detail the public methods, what they do and how to invoke them
usbHidCommunication(System::Void) - Constructor
The constructor method should be called by adding a definition of the class into a 'private:' section of the Form's .h file, it should look something like this:
// Initialise the USB HID Communication object private: usbHidCommunicationNameSpace::usbHidCommunication a_usbHidCommunication;
This action makes the class ready to use (it instantiates an object from the class).
System::Void requestDeviceNotificationsToForm(System::IntPtr handleOfWindow)
This method requests that device notifications are sent to the main form which allows the class to receive device attachment and detachment messages from the Windows operating system. You have to pass the 'handle' of the main form to the method so it knows where to direct the notifications. To do this you simply place the following code in to your form's constructor:
// //TODO: Add the constructor code here // // Request that this form receives device notifications a_usbHidCommunication.requestDeviceNotificationsToForm(this->Handle);
System::Void handleDeviceChangeMessages(Message% m, System::Int16 vid, System::Int16 pid)
This method is responsible for handling device change notification messages coming from windows (which tell the class when USB devices attach and detach from the host). To use this you have to include the following code in the main form's class:
// This call back function receives the Windows messages coming into the form. protected: virtual void WndProc(Message% m) override { a_usbHidCommunication.handleDeviceChangeMessages(m, VID, PID); Form::WndProc( m ); } // END WndProc method
You have to pass this method the VID and PID of your device which can be done either directly (by simply specifying the numbers such as 0x1234, 0x4321) or (as in the example above) from two
#define
commands at the top of your form's .h file.System::Boolean isDeviceAttached(System::Void)
This method returns 'true' if the device is currently attached and 'false' if it isn't and can be used to see if the USB device is available for communication.
System::Boolean isDeviceBroken(System::Void)
This method returns 'true' if a device is attached but, for some reason, is considered 'broken' (i.e. communication problems, etc.). Note that if this flag is 'true' the isDeviceAttached method will return 'false'. This allows you to add in some extra warning information if anything unexpected happens to the device.
System::Void findDevice(System::Int16 usbVid, System::Int16 usbPid)
This method attempts to find the USB device with the VID and PID supplied. If successful isDeviceAttached will return 'true' when queried. You should perform an initial search for the device when your main form is created, if the device is attached later the device notification will cause it to call this method automatically. You should include a call to this method in the main form's constructor similar to the following code:
public: Form1(void) { InitializeComponent(); // //TODO: Add the constructor code here // // Request that this form receives device notifications a_usbHidCommunication.requestDeviceNotificationsToForm(this->Handle); // Perform an initial search for the desired USB device a_usbHidCommunication.findDevice(VID, PID); // VID, PID }
System::Boolean sendUsbCommandWriteOnly(System::Int16 usbCommandId)
This method sends a command (and possibly some data) to the USB device, but does not expect the USB device to send anything back in response. To use this you simply set the usbCommandId to the value you wish to send and call the method. The commandId is in fact the 1st byte of the 64-byte packet that generic HID uses to communicate. The other 63 bytes are available for data and can be set by using the writeToTheOutputBuffer() method detailed below.
Here is an example of this method in action:
// Send the toggle LED command if (a_usbHidCommunication.sendUsbCommandWriteOnly(0x80) != true) this->lastErrorMessageLabel->Text = L"Read/Write command failed"; else this->lastErrorMessageLabel->Text = L"Read/Write command succeeded";
This method returns 'true' if the write was successful and 'false' if it fails.
System::Boolean sendUsbCommandWriteRead(System::Int16 usbCommandId)
This method is similar to the sendUsbCommandWriteOnly() method, however it expects the USB device to reply to the command. The response from the USB device is accessed using the readFromTheInputBuffer() command detailed below.
Here is the method in action:
// Check the status of the switch a_usbHidCommunication.sendUsbCommandWriteRead(0x81); if (a_usbHidCommunication.readFromTheInputBuffer(2) == 0) this->switchStatusLabel->Text = L"Switch ON"; else this->switchStatusLabel->Text = L"Switch OFF";
System::Boolean writeToTheOutputBuffer(System::Int16 byteNumber, System::Byte value)
This method allows you to write bytes to the output buffer to be sent when the next send command method is called. You simply supply the byte number (2-64) of the byte you want to set (bytes 0 and 1 are reserved for communication) and the value of the byte. The method returns 'true' if successful and 'false' if unsuccessful.
System::Byte readFromTheInputBuffer(System::Int16 byteNumber)
This method allows you to read bytes from the input buffer (data which is returned after a sendUsbCommandWriteRead() method is called). Simply specify the byte number you are interested in (2-64) and the method returns the value.
System::Void detachUsbDevice(System::Void)
This method removes the USB device from your code and cleans up any file handles, threads, etc. leaving the class in the state when it was constructed. This is useful if you wish to detach the device for any reason. Note: Since this class is in 'user-space' this doesn't detach the device from the host, only from your application.
Required include files
In order to use this class you have to include the four required libraries in your stdafx.h file. It should look similar to the following:
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently #pragma once // TODO: reference additional headers your program requires here #include <Windows.h> #include <setupapi.h> #include <Dbt.h>
Status and Limitations
This class is relatively new and untested. If you use it and find anything that is incorrect or could be done better please let me know, I'd love to hear from you. The main limitation of this class is that it can only handle one device at a time, i.e. if you have 2 or more devices with the same VID and PID it will simply pick the first one it finds. I hope to remove this limitation in the near future.
Further information about the class
If you would like to know more about how this class works please go ahead and look directly in the source code. I spent a lot of time making the code as clear as possible as well as including lots of commentary about how it works and what's going on.
Example code
Below you will find an example windows application with the class code included. This code is designed to work with my simple PIC18F generic HID device which you can find on this page.
It is designed to use the same PIC firmware as the original project. Obviously this is a very simple device, however the class should allow you to build far more complex devices without having to worry about the low-level details of USB HID programming on Windows.
This code will compile in Visual C++ 2008 for both (the free) Visual Studio 2008 express and the professional version. To compile load the project file and select compile from within Visual Studio.
To use the class in your own code simply make your own project and copy over the usbHidCommunication.h file into your project directory.
Files for download
This software is distributed under the GPL Licence:
2010-03-31 - Project updated, removed some minor bugs: See the revision notes in the source code for details.
Please let me know if you find any errors in the class or if you have any suggestions for improvement. There is a topic for this article over in the forum.
Hiç yorum yok:
Yorum Gönder