[UPDATED] Using AVRUSB with Delphi TJvHidDevice
Posted: Tue Feb 03, 2009 4:51 pm
[UPDATE]
I now found a better solution using the way shown by example HID_DATA from VUSB. It also works well with TJvHidDevice but one has to use the getFeature/setFeature methods for data transfer instead of reports. This doesnt trigger the events of the component so you have to poll it with a timer. In my current development (HTPC) this works absolutely fine.
[/UPDATE]
Getting the ingenious USBDRV up and running on the ATMEGA was a snap. Since I am bound to Delphi 7 on the host side I had quite a few frustrating nights trying to get things up and running with LibUSB. It crashed often when disconnecting the device and so on. Then I discovered TJvHidDevice and tried this one since it does not need any additional drivers on the PC. It connected to my device but It took me some time to figure out how to communicate,. Maybe it saves time and frustration for someone out there so heres my solution:
This device descriptor made it possible to communicate in 8 byte packtets which seems to be the upper limit for low speed devices.
On the AVR I Implemented only these functions
Transferring data to the host is done this way
This works well and even triggers the onData Event of the delphi component! Here are the important parts of the delphi side:
I now found a better solution using the way shown by example HID_DATA from VUSB. It also works well with TJvHidDevice but one has to use the getFeature/setFeature methods for data transfer instead of reports. This doesnt trigger the events of the component so you have to poll it with a timer. In my current development (HTPC) this works absolutely fine.
[/UPDATE]
Getting the ingenious USBDRV up and running on the ATMEGA was a snap. Since I am bound to Delphi 7 on the host side I had quite a few frustrating nights trying to get things up and running with LibUSB. It crashed often when disconnecting the device and so on. Then I discovered TJvHidDevice and tried this one since it does not need any additional drivers on the PC. It connected to my device but It took me some time to figure out how to communicate,. Maybe it saves time and frustration for someone out there so heres my solution:
This device descriptor made it possible to communicate in 8 byte packtets which seems to be the upper limit for low speed devices.
Code: Select all
#define USB_BUFFER_SIZE 8
PROGMEM char usbHidReportDescriptor[29] = {
0x06, 0xA0, 0xFF, // USAGE_PAGE (Vendor Defined page 0xA1)
0x09, 0x01, // USAGE (Vendor Usage 0x01)
0xA1, 0x01, // COLLECTION (Application)
// Global items
0x15, 0x00, // LOGICAL_MINIMUM(0)
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255)
0x75, 0x08, // REPORT_SIZE (8 Bits)
0x95, USB_BUFFER_SIZE, // REPORT_COUNT (8 8 Bits, 8 Bytes)
// INPUT
0x09, 0x03, // USAGE (Vendor Usage 0x03)
0x81, 0x02, // INPUT (Data,Variable,Abs)
// OUTPUT
0x09, 0x04, // USAGE (Vendor Usage 0x04)
0x91, 0x02, // OUTPUT(Data, Variable,Abs
// Feature
0x09, 0x05, // USAGE (Vendor Usage 0x05)
0xB1, 0x02, // Feature (Data, Variable,Abs)
0xC0 // END_COLLECTION
};
On the AVR I Implemented only these functions
Code: Select all
static uchar rest, curpos, currep;
usbMsgLen_t usbFunctionSetup(uchar data[8]) {
usbRequest_t *rq = (void *)data;
// HID-Class Request
if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
if(rq->bRequest == USBRQ_HID_SET_REPORT) {
// Receiving more thana single byte needs usbFunctionWrite()
curpos = 0;
rest = rq->wLength.word;
// Limit size
if(rest > sizeof(usbrec)) rest = sizeof(usbrec);
// flag usage of usbFunctionWrite()
return USB_NO_MSG;
}
}
return 0;
}
uchar usbFunctionWrite(uchar *data, uchar len)
{
uchar i;
if(len > rest) len = rest;
rest -= len;
for(i = 0; i < len; i++)
usbrec[curpos++] = data[i];
if (rest == 0) {
// Process results
// Write the buffer to my lcd display
write(0,0,0,usbrec);
return 1;
}
else
return 0;
}
Transferring data to the host is done this way
Code: Select all
usbSetInterrupt(usbcmd, sizeof(usbcmd)); // usbcmd is char[8]
This works well and even triggers the onData Event of the delphi component! Here are the important parts of the delphi side:
Code: Select all
...
private
{ Private-Deklarationen }
dev: TJvHidDevice;
...
const
USB_BUFFER_SIZE = 8;
...
type
TLCD = packed record
ID: byte;
text: array[0..USB_BUFFER_SIZE-1] of char;
end;
...
procedure TForm1.log(s: string);
begin
ListBox1.ItemIndex := ListBox1.Items.Add(s);
end;
...
procedure TForm1.HIDCtlDeviceChange(Sender: TObject);
var
x: integer;
s: string;
begin
with hidCtl do begin
// check if owned device was plugged out
if Assigned(Dev) and not Dev.IsPluggedIn then begin
// give back device
s := dev.ProductName;
CheckIn(Dev);
log(s + ' disconnected');
dev := nil;
end;
// no device connected
if not Assigned(Dev) then
// take over device
if CheckOutByID(Dev, $16C0, $05DF) then begin
log(dev.ProductName + ' connected');
end;
end;
end;
...
procedure TForm1.Button1Click(Sender: TObject);
var
rep:TLCD;
begin
if assigned(dev) then begin
rep.id:=0;
strpcopy(rep.text,'Hallo');
dev.setOutputReport(rep,sizeOf(rep));
end;
end;
...
procedure TForm1.HIDCtlDeviceData(HidDev: TJvHidDevice; ReportID: Byte;
const Data: Pointer; Size: Word);
var
buffer: array[0..100] of char;
I: integer;
s:string;
begin
Move(Data^, buffer[0], Size);
s:='';
for i:=0 to size-1 do s:=s+', '+intToStr(ord(buffer[i]));
log(s);
end;