• AVR Freaks

Helpful ReplyUSB Bulk transfer on PIC18F4550

Page: < 12 Showing page 2 of 2
Author
ee_martin
Starting Member
  • Total Posts : 56
  • Reward points : 0
  • Joined: 2007/10/18 13:20:44
  • Location: 0
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/14 10:29:30 (permalink)
0
Xiaofan,
 
I've got the isochronous transfer going, sort of.  But it isn't right.  The timing is *very* strange.
 
I note that in the code you sent me you have "Sleep(300);" instructions.  When I have these present my code works.  When I *don't* have these present, my code fails.
 
I have now five "context" variables, and five of everything else too, and I run the whole set once.  I therefore have a block of extremely repetitive code as follows:
 

 void *context0 = NULL;
 void *context1 = NULL;
 void *context2 = NULL;
 void *context3 = NULL;
 void *context4 = NULL;
 ret0 = usb_isochronous_setup_async(dev, &context0, EPOUT, ISO_OUT_PACKET_SIZE);
 ret1 = usb_isochronous_setup_async(dev, &context1, EPOUT, ISO_OUT_PACKET_SIZE);
 ret2 = usb_isochronous_setup_async(dev, &context2, EPOUT, ISO_OUT_PACKET_SIZE);
 ret3 = usb_isochronous_setup_async(dev, &context3, EPOUT, ISO_OUT_PACKET_SIZE);
 ret4 = usb_isochronous_setup_async(dev, &context4, EPOUT, ISO_OUT_PACKET_SIZE);
 printf("ret0 from usb_isochronous_setup_async in write routine is %d\n\n", ret0);
 printf("START-ISO-WRITE-TEST\n\n");
 for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
  buf0[i] = ((i * 3) + 1) % 256;
 }
 ret0 = usb_submit_async(context0, (char*)buf0, ISO_OUT_PACKET_SIZE);
 if(ret0 < 0) printf("usb_submit 1_async in write routine failed with error %d\n", ret0);
 else printf("usb_submit_async 1 in write routine succeeded with return value %d\n\n", ret0);
 data_cnt0 = usb_reap_async(context0, 5000);
 if(data_cnt0 < 0) printf("usb_reap_async 1 failed with error %d\n\n", data_cnt0);
 else printf("usb_reap_async 1 succeeded with return value %d\n\n", data_cnt0);
// **********
 for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
  buf1[i] = ((i * 4) + 2) % 256;
 }
// Sleep(300);  // (1) Units are milli-seconds (300 works, 200 does not)
 ret1 = usb_submit_async(context1, (char*)buf1, ISO_OUT_PACKET_SIZE);
 if(ret1 < 0) printf("usb_submit_async 2 in write routine failed with error %d\n", ret1);
 else printf("usb_submit_async 2 in write routine succeeded with return value %d\n\n", ret1);
 data_cnt1 = usb_reap_async(context1, 5000);
 if(data_cnt1 < 0) printf("usb_reap_async 2 failed with error %d\n\n", data_cnt1);
 else printf("usb_reap_async 2 succeeded with return value %d\n\n", data_cnt1);
// **********
 for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
  buf2[i] = ((i * 5) + 3) % 256;
 }
// Sleep(300); // (2)
 ret2 = usb_submit_async(context2, (char*)buf2, ISO_OUT_PACKET_SIZE);
 if(ret2 < 0) printf("usb_submit_async 3 in write routine failed with error %d\n", ret2);
 else printf("usb_submit_async 3 in write routine succeeded with return value %d\n\n", ret2);
 data_cnt2 = usb_reap_async(context2, 5000);
 if(data_cnt2 < 0) printf("usb_reap_async 3 failed with error %d\n\n", data_cnt2);
 else printf("usb_reap_async 3 succeeded with return value %d\n\n", data_cnt2);
// **********
 for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
  buf3[i] = ((i * 6) + 4) % 256;
 }
// Sleep(300); // (3)
 ret3 = usb_submit_async(context3, (char*)buf3, ISO_OUT_PACKET_SIZE);
 if(ret3 < 0) printf("usb_submit_async 4 in write routine failed with error %d\n", ret3);
 else printf("usb_submit_async 4 in write routine succeeded with return value %d\n\n", ret3);
 data_cnt3 = usb_reap_async(context3, 5000);
 if(data_cnt3 < 0) printf("usb_reap_async 4 failed with error %d\n\n", data_cnt3);
 else printf("usb_reap_async 4 succeeded with return value %d\n\n", data_cnt3);
// **********
 for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
  buf4[i] = ((i * 7) + 5) % 256;
 }
// Sleep(300); // (4)
 ret4 = usb_submit_async(context4, (char*)buf4, ISO_OUT_PACKET_SIZE);
 if(ret4 < 0) printf("usb_submit_async 5 in write routine failed with error %d\n", ret4);
 else printf("usb_submit_async 5 in write routine succeeded with return value %d\n\n", ret4);
 data_cnt4 = usb_reap_async(context4, 5000);
 if(data_cnt4 < 0) printf("usb_reap_async 5 failed with error %d\n\n", data_cnt4);
 else printf("usb_reap_async 5 succeeded with return value %d\n\n", data_cnt4);
}

Note the "Sleep(300)" instructions (all commented out above, but all required if the system is to work).  If I reduce the 300 (which seems to represent 300ms) it always fails.
 
If the "Sleep(300)" instruction is *not* present, the next write operation fails.  If two in sequence are not present the write after the first missing one fails, but the one after the second (next) missing one succeeds.
 
To me, all this lot indicates that my PIC is not fast enough to process the data.  But I don't even move the data out of its buffer, all I do is toggle a few bits and then set UOWN again.  I can't tell you exactly how long the stuff I do takes, but I am pretty sure it's significantly less than, say, 20us.  Since Isochronous only transmits one packet per 1ms frame, I would have thought I had *plenty* time available to get things ready for the next frame.  (My write [OUT] packet size is 256.)
 
Also, a 300ms delay is a *long* delay.  200ms won't do, and 200ms is still a very long delay.  What is happening that needs between 200ms and 300ms to sort itself out?
 
When you ran your code successfully, did the 300ms delays have to be included?  I see the same Sleep instructions are also in Vlocki's code.  Did he need them to be present before the system worked?  It makes the whole isochronous transfer scheme a bit pointless if we have to wait 300ms between packets!
 
Any thoughts on this would be much appreciated!
 
Martin
 
#21
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/14 17:28:19 (permalink)
0
I do not think my code has the sleep() thingy, I am not sure about vloki's code. You might want to use debugview or some USB sniffer to see what is wrong.
http://article.gmane.org/gmane.comp.lib.libusb.devel.windows/1439



#include <usb.h>

#define VERSION "0.1.0"
#define VENDOR_ID 0x04D8
#define PRODUCT_ID 0x0080
#define INTERFACE 0
#define BUFFER_SIZE 65

usb_dev_handle *find_pickit2_isoc();

usb_dev_handle* setup_libusb_access() {
usb_dev_handle *pickit2_isoc;

usb_set_debug(255);
usb_init();
usb_find_busses();
usb_find_devices();

if(!(pickit2_isoc = find_pickit2_isoc())) {
printf("Couldn't find the mouse, Exiting\n");
return NULL;
}

if (usb_set_configuration(pickit2_isoc, 1) < 0) {
printf("Could not set configuration 1 : %s\n");
return NULL;
}

if (usb_claim_interface(pickit2_isoc, INTERFACE) < 0) {
printf("Could not claim interface: %s\n");
return NULL;
}

return pickit2_isoc;
}

usb_dev_handle *find_pickit2_isoc()
{
struct usb_bus *bus;
struct usb_device *dev;

for (bus = usb_busses; bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor == VENDOR_ID &&
dev->descriptor.idProduct == PRODUCT_ID ) {
usb_dev_handle *handle;
printf("pickit2_isoc with Vendor Id: %x and Product Id: %x
found.\n", VENDOR_ID, PRODUCT_ID);
if (!(handle = usb_open(dev))) {
printf("Could not open USB device\n");
return NULL;
}

return handle;
}

}
}

return NULL;
}

void test_isochronous_async(usb_dev_handle *dev)
{
unsigned char buf0[640];
unsigned char buf1[640];
unsigned char buf2[640];

int i;

/* use three contexts for this example (more can be used) */
void *context0 = NULL;
void *context1 = NULL;
void *context2 = NULL;

/* write transfer (stream) */
/*
usb_isochronous_setup_async(dev, &context0, 0x01);
usb_isochronous_setup_async(dev, &context1, 0x01);
usb_isochronous_setup_async(dev, &context2, 0x01);

usb_submit_async(context0, buf0, sizeof(buf0));
usb_submit_async(context1, buf1, sizeof(buf1));
usb_submit_async(context2, buf2, sizeof(buf2));

for(i = 0; i < 10; i++)
{
usb_reap_async(context0, 5000);
usb_submit_async(context0, buf0, sizeof(buf0));

usb_reap_async(context1, 5000);
usb_submit_async(context1, buf1, sizeof(buf1));

usb_reap_async(context2, 5000);
usb_submit_async(context2, buf2, sizeof(buf2));
}

usb_reap_async(context0, 5000);
usb_reap_async(context1, 5000);
usb_reap_async(context2, 5000);

usb_free_async(&context0);
usb_free_async(&context1);
usb_free_async(&context2);

*/

/* read transfer (stream) */
usb_isochronous_setup_async(dev, &context0, 0x81,64);
usb_isochronous_setup_async(dev, &context1, 0x81,64);
usb_isochronous_setup_async(dev, &context2, 0x81,64);

usb_submit_async(context0, buf0, sizeof(buf0));
usb_submit_async(context1, buf1, sizeof(buf1));
usb_submit_async(context2, buf2, sizeof(buf2));

for(i = 0; i < 10; i++)
{
usb_reap_async(context0, 5000);
usb_submit_async(context0, buf0, sizeof(buf0));

usb_reap_async(context1, 5000);
usb_submit_async(context1, buf1, sizeof(buf1));

usb_reap_async(context2, 5000);
usb_submit_async(context2, buf2, sizeof(buf2));
}

for(i = 0; i < 640; i++) {
printf(" %02x, %02x, %02x ", buf0[i],buf1[i],buf2[i]);
// printf(" %02x, %02x, %02x ", buf0[i]);
}

usb_reap_async(context0, 5000);
usb_reap_async(context1, 5000);
usb_reap_async(context2, 5000);
usb_free_async(&context0);
usb_free_async(&context1);
usb_free_async(&context2);

/* release interface */
usb_set_altinterface(dev, 0);
usb_release_interface(dev, 0);
}

int main(void)
{
usb_dev_handle *pickit2_isoc;
if ((pickit2_isoc = setup_libusb_access()) == NULL) {
exit(-1);
}
test_isochronous_async(pickit2_isoc);
usb_close(pickit2_isoc);

return 0;
}



  USB_Links and libusb
#22
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/14 17:31:20 (permalink)
0
Three steps to capture the libusb-win32 debug message.
1) add usb_set_debug(255) to your code after the call of usb_init()
2) download DebugView from microsoft.com
http://www.microsoft.com/technet/sysinternals/utilities/debugview.mspx
3) run DebugView to capture the driver's an the DLL's debug messages

Example output:
http://forum.microchip.com/tm.aspx?m=270049&mpage=2

But as I told you before, libusb-win32's asynchronous API is basically undocumented. So you will face quite some difficulties. There are alternative driver like USBSAMP and EZUSB/CYUSB.

  USB_Links and libusb
#23
ee_martin
Starting Member
  • Total Posts : 56
  • Reward points : 0
  • Joined: 2007/10/18 13:20:44
  • Location: 0
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/19 08:06:21 (permalink)
0
I think I've cracked this USB-on-a-PIC business, at least so far as I can.  I have a PIC18F4550 and I have running on it, simultaneously, Control, Interrupt, Bulk and Isochronous transfers.  The only catch is that the Isochronous and Control buffers are on top of one another, so I can only use one or other of these transfer types at a time.  The Control and Bulk transfers are quite separate.
 
I have been focused on meeting the needs of a particular application, which is simply to be able to do two things:
 
1/ Transfer the contents of a 512kByte SRAM that is connected to the
   PIC to and from the PC as fast as reasonably possible.  The SRAM-PIC
   connection is a bottleneck, so it still takes 1 second, or so, but
   that's fine for my application.
 
2/ Get 128 bytes from the PIC to the PC, and 256 bytes from the PC to the
   PIC, repeatedly and regularly, as fast as reasonably possible,
   preferably all within a single full-speed USB frame.  This is to be
   done in the face of severe bandwidth competition from one or two
   (low-resolution) cameras on the same USB bus.
 
The first of these was a simple Bulk transfer.
 
I have tried everything for the second.
 
Control transfers are 64-bytes per (1ms) frame, so useless (though needed for setup purposes).
 
Bulk transfers work well, but have the lowest priority so don't get through at all when the cameras are working.
Isochronous was a real pain to set up, and very difficult to use. More about that later.
 
I think I have settled on interrupt transfers with a minimum latency.  I can get one interrupt transfer *per endpoint*, per frame, but the maximum packet size is only 64-bytes.  However, I can have many endpoints and get the data through that way.  Also interrupt transfers get a high priority.  I therefore use EP3-6_OUT to transfer 256 bytes
from the PC to the PIC, and EP3-4_IN to transfer 128 bytes from the PIC to the PC.
 
Buffers are set out as follows:

EP0_OUT_START  0x440 ; up to 0x45F
EP0_IN_START  0x460 ; up to 0x47F
EP2_IN_A_START  0x480 ; up to 0x4FF
EP1_OUT_START  0x500 ; up to 0x54F
EP1_IN_START  0x540 ; up to 0x57F
EP2_IN_B_START  0x580 ; up to 0x5FF
EP2_OUT_A_START  0x600 ; up to 0x6FF
EP2_OUT_B_START  0x700 ; up to 0x7FF
EP3_IN_A_START  0x480 ; up to 0x4BF
EP4_IN_A_START  0x4C0 ; up to 0x4FF
EP3_IN_B_START  0x580 ; up to 0x5BF
EP4_IN_B_START  0x5C0 ; up to 0x5FF
EP3_OUT_A_START  0x600 ; up to 0x63F
EP4_OUT_A_START  0x640 ; up to 0x67F
EP5_OUT_A_START  0x680 ; up to 0x6BF
EP6_OUT_A_START  0x6C0 ; up to 0x6FF
EP3_OUT_B_START  0x700 ; up to 0x73F
EP4_OUT_B_START  0x740 ; up to 0x77F
EP5_OUT_B_START  0x780 ; up to 0x7BF
EP6_OUT_B_START  0x7C0 ; up to 0x7FF

These endpoint buffers are laid out slightly oddly in the list above, but they are done that way so I can swap between the "A" and "B" sets (home made ping-pong buffers) by only changing one bit of the Bank Select Register
 
The buffer description tables (for EP0-7) are held in 0x400 - 0x43F.
 
The Control transfers are 32 bytes (some folk say they should be 8-bytes) and are quite routine, using EP0_IN and EP0_OUT.  They follow Brad Minch's scheme fairly closely.
 
Bulk transfers use 64-byte packets and are also fairly routine.  I send a Control message to the PIC to initialise the EP1 endpoints (makes BD1OAL = low EP1_OUT_START, BD1OAH = high EP1_OUT_START, BD1IAL = low EP1_IN_START, BD1IAH = high EP1_IN_START, BD1OBC = BD1IBC = 0x40, and UEP1 = 0x1E).  It then makes sure the first set of IN data is in the EP1_IN buffer, and initialises the DATA0/1 flag.  It then sets UOWN = 1 in both the EP1_IN and EP1_OUT status registers.
 
I use the libusb-win32 libraries (version 0.1.12.2) on the PC.  These work well for Control, Bulk and Interrupt transfers, but they are a bit dodgy for Isochronous transfers.
 
I use usb_bulk_(read/write) to move a packet of data (to/from) external SRAM.
 
After a packet of Bulk data is read (IN transfer) the hardware raises the TRNIF flag.  When I see that I move the next block of data from the external SRAM into the EP1_IN buffer, knock down the TRNIFflag, toggle the DATA0/1 flag and set the UOWN flag.
 
After a packet of Bulk data is written (OUT transfer) the hardware raises the TRNIF flag.  When I see that I move the next block of data from the EP1_IN buffer into the external SRAM, knock down the TRNIF flag, toggle the DATA0/1 flag and set the UOWN flag.
 
I believe that requirement (2), above, is best met using Interrupt transfers.  Interrupt transfers get priority over Bulk transfers (which are at the bottom of the priority heap) but I can only get one 64-byte packet through per endpoint per (full speed, 1ms) USB frame.  I need a 256-byte OUT transfer and a 128-byte IN transfer.  I therefore use an OUT transfer to each of EP3_OUT, EP4_OUT, EP5_OUT and EP6_OUT, and use IN transfers from EP3_IN and EP4_IN.
 
The problem is that the large blocks of data are being moved in 64-byte packets.  Therefore the PIC could see a mix of old and new data.  To deal with this I use something like a ping-pong scheme.  I have an "A" set of buffers and a "B" set of buffers.  I send the four OUT packets to EP3_OUT_A, EP4_OUT_A, EP5_OUT_A and EP6_OUT_A, and once they have all arrived, and the 256-byte overall OUT "A" buffer is full, I toggle the flag so that the USB points at the "B" buffer, and I allow the PIC to use the "A" buffer.
 
I keep the working copy of the data in the "A" and "B" buffers, so I don't have to transfer it elsewhere.  This helps keep the speed up.  The USB gets the "A" buffer while the PIC works on the "B" buffer, or vice versa.
 
Similarly for the IN buffer, I have the PC read the EP3_IN_A and EP4_IN_A buffers and, once they have *both* been read, I toggle the flag so the USB points at the other buffer set ("B").  I also have to copy the new set ("B", which the USB is pointing at now) to the old set ("A"), so we don't lose data.  The PIC now writes to the "old" set ("A"). 
 
The roles of the "A" and "B" buffers are toggled when I have dealt with a complete set of data.
 
As the data I was working with for requirement (2) had to be sent/received regularly with a high priority, I considered using Isochronous transfers.  Packets can be up to 1023-bytes long and Isochronous gets top priority (but no error correction) so it looks like just the thing.
 
I used the same buffers as for the Control transfers, above, so I did have a kind of home-made ping-pong.  (The PIC18F4550 is able to deal with these internally, but there is some doubt as to whether they work, and also I
would have to make all the endpoints ping-pong, whereas I want most "normal".)
 
The PIC end of the link was not very difficult.  The endpoint descriptors had to be correct, and the usual sort of initialisation had to be done.  The DATA0/1 flag is not used and no ACK is sent after a data transfer, but the idea is the same.
 
The PC-end is a pain.  There is almost no documentation about the Isochronous routines in the libusb-win32 package, and they have some "quirks" I don't understand.  In fact, I have to say I understand little of it.
 
My code is as follows:

#include <usb.h>
#include <stdio.h>
#define VERSION "0.1.0"
#define VENDOR_ID 0x04D8
#define PRODUCT_ID 0xE05B
#define INTERFACE 0
#define EPOUT 0x02
#define EPIN 0x82
#define ISO_IN_PACKET_SIZE 0x80
#define ISO_OUT_PACKET_SIZE 0x100
#define SET_BLOCK_NUMBER 5
#define START_EP2 7
////////////////////////////////////////////////////////////////////////////////
usb_dev_handle *find_picdem_isoc()
{
    struct usb_bus *bus;
  struct usb_device *dev;
  for (bus = usb_busses; bus; bus = bus->next) {
      for (dev = bus->devices; dev; dev = dev->next) {
         if (dev->descriptor.idVendor == VENDOR_ID &&
          dev->descriptor.idProduct == PRODUCT_ID ) {
        usb_dev_handle *handle;
          printf("Device with Vendor Id: %x and Product Id: %x found\n\n", VENDOR_ID, PRODUCT_ID);
        if (!(handle = usb_open(dev))) {
          printf("Could not open USB device\n");
          return NULL;
        }
        return handle;
      }
    }
  }
  return NULL;
}
////////////////////////////////////////////////////////////////////////////////
usb_dev_handle* setup_libusb_access() {
    usb_dev_handle *picdem_isoc;
  usb_set_debug(255);
    usb_init();
/* Just like the name implies, usb_init sets up some internal structures.
   usb_init must be called before any other libusb functions.*/
    usb_find_busses();
/* usb_find_busses will find all of the busses on the system.
   Returns the number of changes since previous call to this function
   (total of new busses and busses removed).*/
  usb_find_devices();
/* usb_find_devices will find all of the devices on each bus.
   This should be called after usb_find_busses.
   Returns the number of changes since the previous call to this function
   (total of new device and devices removed).*/

    if(!(picdem_isoc = find_picdem_isoc())) {
    printf("Couldn't find the device, Exiting\n");
    return NULL;
  }
  if (usb_set_configuration(picdem_isoc, 1) < 0) {
    printf("Could not set configuration 1\n");
    return NULL;
  }
  if (usb_claim_interface(picdem_isoc, INTERFACE) < 0) {
    printf("Could not claim interface\n");
    return NULL;
  }
  return picdem_isoc;
}

////////////////////////////////////////////////////////////////////////////////
void read_isochronous_async(usb_dev_handle *dev)
{
  unsigned char buf0[640];
  int data_cnt, ret, j;
  void *context0 = NULL;
  ret = usb_isochronous_setup_async(dev, &context0, EPIN, ISO_IN_PACKET_SIZE);
  printf("ret from usb_isochronous_setup_async in read routine is %d\n\n", ret);
  printf("START-ISO-READ-TEST\n\n");
  usb_submit_async(context0, (char*)buf0, ISO_IN_PACKET_SIZE);
  data_cnt = usb_reap_async(context0, 5000);
  if(data_cnt < 0) printf("ISO-READ-TEST-OUT failed with error %d\n", data_cnt);
  else printf("ISO-READ-TEST-OUT succeeded with return value %d\n", data_cnt);
  for(j = 0; j < ISO_IN_PACKET_SIZE; j++) {
    if(j % 256 == 0)printf("\n");
    if(j % 16 == 0)printf("\n0x%02X    ", (j/16));
    printf("%02X  ", buf0[j]);
  }
  printf("\n");
  data_cnt = usb_reap_async(context0, 500);
  if(data_cnt < 0) printf("ISO-READ-TEST-OUT failed with error %d\n", data_cnt);
  else printf("ISO-READ-TEST-OUT succeeded with return value %d\n", data_cnt);
  for(j = 0; j < ISO_IN_PACKET_SIZE; j++) {
    if(j % 256 == 0)printf("\n");
    if(j % 16 == 0)printf("\n0x%02X    ", (j/16));
    printf("%02X  ", buf0[j]);
  }
  usb_free_async(&context0);
}
////////////////////////////////////////////////////////////////////////////////
 void write_isochronous_async(usb_dev_handle *dev)
 {
  unsigned char buf0[ISO_OUT_PACKET_SIZE], buf1[ISO_OUT_PACKET_SIZE], buf2[ISO_OUT_PACKET_SIZE];
  int data_cnt0, data_cnt1, data_cnt2, ret0, ret1, ret2, i, j;
  void *context0 = NULL;
  void *context1 = NULL;
  void *context2 = NULL;
  ret0 = usb_isochronous_setup_async(dev, &context0, EPOUT, ISO_OUT_PACKET_SIZE);
  ret1 = usb_isochronous_setup_async(dev, &context1, EPOUT, ISO_OUT_PACKET_SIZE);
  ret2 = usb_isochronous_setup_async(dev, &context2, EPOUT, ISO_OUT_PACKET_SIZE);
  printf("ret0 from usb_isochronous_setup_async in write routine is %d\n\n", ret0);
  printf("START-ISO-WRITE-TEST\n\n");
  ret0 = usb_submit_async(context0, (char*)buf0, ISO_OUT_PACKET_SIZE);
  if(ret0 < 0) printf("usb_submit 1_async in write routine failed with error %d\n", ret0);
  else printf("usb_submit_async 1 in write routine succeeded with return value %d\n", ret0);
  ret1 = usb_submit_async(context1, (char*)buf1, ISO_OUT_PACKET_SIZE);
  if(ret1 < 0) printf("usb_submit_async 2 in write routine failed with error %d\n", ret1);
  else printf("usb_submit_async 2 in write routine succeeded with return value %d\n", ret1);
  ret2 = usb_submit_async(context2, (char*)buf2, ISO_OUT_PACKET_SIZE);
  if(ret2 < 0) printf("usb_submit_async 3 in write routine failed with error %d\n", ret2);
  else printf("usb_submit_async 3 in write routine succeeded with return value %d\n", ret2);
// **********
  for(j = 0; j < 10; j++) {
    for(i = 0; i < ISO_OUT_PACKET_SIZE; i++) {
      buf0[i] = ((i * (j+3)) + (j+1)) % 256;
      buf1[i] = ((i * (j+4)) + (j+2)) % 256;
      buf2[i] = ((i * (j+5)) + (j+3)) % 256;
    }
    data_cnt0 = usb_reap_async(context0, 500);
    if(data_cnt0 < 0) printf("usb_reap_async 0 in loop %d failed with error %d\n", j, data_cnt0);
    else printf("usb_reap_async 0 in loop %d succeeded with return value %d\n", j, data_cnt0);
    ret0 = usb_submit_async(context0, (char*)buf0, ISO_OUT_PACKET_SIZE);
    if(ret0 < 0) printf("usb_submit_async 0 in write routine loop %d failed with error %d\n", j, ret0);
    else printf("usb_submit_async 0 in write routine loop %d succeeded with return value %d\n", j, ret0);
// **********
    data_cnt1 = usb_reap_async(context1, 500);
    if(data_cnt1 < 0) printf("usb_reap_async 1 in loop %d failed with error %d\n", j, data_cnt1);
    else printf("usb_reap_async 1 in loop %d succeeded with return value %d\n", j, data_cnt1);
    ret1 = usb_submit_async(context1, (char*)buf1, ISO_OUT_PACKET_SIZE);
    if(ret1 < 0) printf("usb_submit_async 1 in write routine loop %d failed with error %d\n", j, ret1);
    else printf("usb_submit_async 1 in write routine loop %d succeeded with return value %d\n", j, ret1);
// **********
    data_cnt2 = usb_reap_async(context2, 500);
    if(data_cnt2 < 0) printf("usb_reap_async 2 in loop %d failed with error %d\n", j, data_cnt2);
    else printf("usb_reap_async 2 in loop %d succeeded with return value %d\n", j, data_cnt2);
    ret2 = usb_submit_async(context2, (char*)buf2, ISO_OUT_PACKET_SIZE);
    if(ret2 < 0) printf("usb_submit_async 2 in write routine loop %d failed with error %d\n", j, ret2);
    else printf("usb_submit_async 2 in write routine loop %d succeeded with return value %d\n", j, ret2);
// **********
  }
  data_cnt0 = usb_reap_async(context0, 500);
  if(data_cnt0 < 0) printf("usb_reap_async 0 in last loop failed with error %d\n", data_cnt0);
  else printf("usb_reap_async 0 in last loop succeeded with return value %d\n", data_cnt0);
  data_cnt1 = usb_reap_async(context1, 500);
  if(data_cnt1 < 0) printf("usb_reap_async 1 in last loop failed with error %d\n", data_cnt1);
  else printf("usb_reap_async 1 in last loop succeeded with return value %d\n", data_cnt1);
  data_cnt2 = usb_reap_async(context2, 500);
  if(data_cnt2 < 0) printf("usb_reap_async 2 in last loop failed with error %d\n", data_cnt2);
  else printf("usb_reap_async 2 in last loop succeeded with return value %d\n", data_cnt2);
  return;
}

////////////////////////////////////////////////////////////////////////////////
int main( int argc, char **argv)
{
  unsigned char tmp1[32];
  int ret;
  usb_dev_handle *picdem_isoc;

  if ((picdem_isoc = setup_libusb_access()) == NULL) {
    printf("setup_lib_access failed\n");
    exit(-1);
  }
  ret = usb_control_msg(picdem_isoc, 0x40, START_EP2, 0, 0, tmp1, 1, 100);
  if (ret < 0) {
    printf("Unable to send vendor request to start EP2, ret = %d...\n", ret);
  }
  write_isochronous_async(picdem_isoc);
  read_isochronous_async(picdem_isoc);
// ### release interface
  usb_set_altinterface(picdem_isoc, 0);
  usb_release_interface(picdem_isoc, 0);
  usb_close(picdem_isoc);
//  while(1);
  return 0;
}
////////////////////////////////////////////////////////////////////////////////

A lot of the above is telling me about errors, so it can be severely trimmed.  Isochronous doesn't tend to notice errors anyway - the lack of an ACK packet means that it can't really tell if things have gone bad.  It's actually quite hard to get an error, even if the code, or the PIC firmware, is complete nonsense.
 
First we set up the device. which is done by setup_libusb_access.
This, in turn calls a series of routines in sequence:
usb_init - initialises the whole thing
usb_find_busses - finds the USB busses on the system
usb_find_devices - finds all the devices on each bus
 
Now we should have a picture of all USB busses and devices.
We keep going with the next sequence:
find_picdem_isoc - returns a structure that holds information about   the device.  It checks all devices on all busses, in turn, till it finds one that matches the PRODUCT_ID and VENDOR_ID we want.
usb_set_configuration(picdem_isoc, 1) - sets configuration 1
usb_claim_interface(picdem_isoc, INTERFACE) - "claims" interface zero (INTERFACE = 0)
 
When setup_libusb_access returns successfully it returns the device "handle" which will be used to reference the device thereafter.
 
Now we have one of my own schemes.  I use the following command:
  usb_control_msg(picdem_isoc, 0x40, START_EP2, 0, 0, tmp1, 1, 100);
This sends a vendor-class request to my PIC with bRequest = START_EP2, which happens to be integer 7.  This is recognised by the PIC which sets up EndPoints EP2_IN and EP2_OUT (including setting UOWN).  I don't want
these on all the time as they can be a pest when I am working with EP0 (Control) and EP1 (Bulk) only.  This command is also used to reset the ping-pong-like buffering.
 
I want ping-pong-like buffering on EP2 ONLY.  The official ping-pong on the PIC18F4550 may not work, according to what I have read, and I don't want ping-pong on EP0 and EP1 anyway.  Therefore I do the job myself, even though that is fractionally slower.
 
I do not transfer data out of the EP2 buffers, but instead I use it in place. I have a flag that indicates which of the "A" or "B" buffers is "connected" to the USB (one flag for IN, and one for OUT), and I have the main PIC software read or write individual bytes of data from or to the other buffer.
 
I can now call my test routine "write_isochronous_async".
This routine first sets up an *empty* structure context0.
It then calls
  usb_isochronous_setup_async(dev, &context0, EPIN, ISO_IN_PACKET_SIZE);
and that fills the structure suitably with the information required by the next routine.  (EPIN = 0x82, ISO_IN_PACKET_SIZE = 128). Note that this is the only place where we see a difference between a read and a write operation.
 
We then call
  usb_submit_async(context0, (char*)buf0, ISO_IN_PACKET_SIZE);
followed by a call to
  usb_reap_async(context0, 500);

Note that these come in pairs; first usb_submit_async followed by usb_reap_async.
 
Many code examples on the web show multiple (three?) "context" variables being set up, with everything from, and including, the call to usb_isochronous_setup_async being done several times.  This seems to be to ensure that the PC queues sufficient requests that we can be sure that there is always a request available to run every frame.  After all, the PC may have other things to do. If this is done data will be returned into several buffers which will have to be pieced together.
 
You will observe the loops in the write_isochronous_async code.  The j = 0 -> 9 loop is just so lots of stuff is written, there is no magic about it.
 
Apart from that you see the following pseudo-code:

usb_submit_async(0)
usb_submit_async(1)
usb_submit_async(2)
for(j = 0; j < 10; j++) {
  usb_reap_async(0)
  usb_submit_async(0)
  usb_reap_async(1)
  usb_submit_async(1)
  usb_reap_async(2)
  usb_submit_async(2)
}
usb_reap_async(0)
usb_reap_async(1)
usb_reap_async(2)

Now it is absolutely obvious that this could be drawn together into one big loop.  Don't!!!  If you do you will find that 300ms delays (Sleep (300);) have to be inserted between each operation.  I have *no* idea why this might be - I can't think of anything that needs this delay.  Less than 300ms won't do.  I don't think my PIC is the problem as it works fine with the format above, but needs the 300ms delay if I use the "obvious" loop.  Anyway, 300ms is *ages* on a PIC!
 
Using the format above does seem to work, and it is the format advocated by Xiaofan.  "Vlocki" seems to be having trouble with the 300ms delays too, at least at one time.
 
To add to the fun, I then discovered that while the above worked fine on my home PC, it didn't work so well on my office PC, which is a older machine (single core), but runs identical software.  On my office machine packets are dropped.
 
Anyway, once I had the Isochronous working to the above extent I started doing some more thinking.  OK, maybe I should have done more of that first.  I am dropping (let's say) OUT requests into the PC queue, but I don't know how many are in the queue at any one time.  The whole aim is to get one IN and one OUT request into the queue every ms, but how do I know I am doing that?  Without ACKs coming back, I am sending data out into a black hole.
 
In my example, I am sending a finite number of requests, so it's easy, but if the data is flowing over a long period I could grossly over-fill the queue, or I could let it run out, and I have no idea how to avoid that.  (If anyone has read this far and knows the answer I would be more than interested to hear it.)
 
About this time my "customer", who I had explained this lot to before, suddenly came to realise that I meant it, and baulked at having to provide data at 1ms intervals.  This was a bit annoying, as I had spent some time on the Isochronous transfers, but also a bit of a relief, as I wasn't at all sure about the libusb-win32 package for Isochronous transfers.  (Using a PC was a requirement - I know about libusb 1.0 for Linux.)
 
Hence the move to Interrupt transfers, which seem to be doing the job fine at a slightly reduced repetition rate.
 
I hope this story is of some use to somebody, and that it helps you avoid some of the bigger pitfalls.  If there are any errors in what I have written, I apologise in advance.  This is just my "best effort", and I would like to think that it will improve over time.
 
Thanks to all those who helped me when I was completely at sea, particularly Xiaofan!
 
Martin
 
#24
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/19 08:28:13 (permalink)
0
Wow, this is a long answer. Thanks a lot for reporting back. I think you know more about libusb-win32 isochronous transfer now than I.

If you use interrupt transfer, then you should consider libusb 1.0, now it works under Windows as well with WinUSB backend and Windows Native HID backend. Unfortunately both backends will not support isochronous transfer. libusb-1.0 is already working under Linux and Mac OS X and they support isochronous transfer. The next backend for libusb 1.0 might be
libusb-win32 device driver (libusb0.sys) but I am not so sure if it will be done any time soon.

http://www.libusb.org/
http://www.libusb.org/wiki/windows_backend
post edited by xiaofan - 2010/02/19 08:29:36

  USB_Links and libusb
#25
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/22 02:11:48 (permalink)
0
Just want to post some more examples by Stephan Meyer, the author of libusb-win32.


On Fri, Jun 16, 2006 at 3:46 PM, Stephan Meyer  wrote:
> Libusb works with threads (test program attached).
>
> Stephan
>


#include <stdio.h>
#include <windows.h>

#include "usb.h"

static int read_thread_running = 1;
static int write_thread_running = 1;

#define NUM_LOOPS 1000

static DWORD WINAPI thread_proc_read(void *param);
static DWORD WINAPI thread_proc_write(void *param);
static struct usb_device *find_dev(void);

static DWORD WINAPI thread_proc_read(void *param)
{
char tmp[1024];
int i;
usb_dev_handle *hdev = (usb_dev_handle *)param;

printf("thread_proc_read: starts\n");

for(i = 0; i < NUM_LOOPS; i++)
{
printf("thread_proc_read: reading\n");
if(usb_bulk_read(hdev, 0x82, tmp, sizeof(tmp), 1000) != sizeof(tmp))
{
printf("bulk_read failed\n");
}
}

printf("thread_proc_read: ends\n");
read_thread_running = 0;
return 0;
}
static DWORD WINAPI thread_proc_write(void *param)
{
char tmp[1024];
int i;
usb_dev_handle *hdev = (usb_dev_handle *)param;

printf("thread_proc_write: starts\n");

for(i = 0; i < NUM_LOOPS; i++)
{
printf("thread_proc_write: writing\n");
if(usb_bulk_write(hdev, 0x02, tmp, sizeof(tmp), 1000) != sizeof(tmp))
{
printf("bulk_write failed\n");
}
}

printf("thread_proc_write: ends\n");

write_thread_running = 0;
return 0;
}

static struct usb_device *find_dev(void)
{
const int vid = 0x1234, pid = 0x5678;
struct usb_bus *bus;
struct usb_device *dev;

for(bus = usb_get_busses(); bus; bus = bus->next) {
for(dev = bus->devices; dev; dev = dev->next) {
if(dev->descriptor.idVendor == vid
&& dev->descriptor.idProduct == pid)
return dev;
}
}
return NULL;
}

int main(void)
{
HANDLE read_thread = NULL;
HANDLE write_thread = NULL;
usb_dev_handle *hdev = NULL;
struct usb_device *dev;

usb_init();
usb_set_debug(255);
usb_find_busses();
usb_find_devices();

if(!(dev = find_dev()))
{
printf("device not found\n");
return 1;
}

if(!(hdev = usb_open(dev)))
{
printf("usb_open failed\n");
return 1;
}

usb_set_configuration(hdev, 1);
usb_claim_interface(hdev, 0);
usb_set_altinterface(hdev, 2);

read_thread = CreateThread(NULL, 0, thread_proc_read, hdev, 0, NULL);
write_thread = CreateThread(NULL, 0, thread_proc_write, hdev, 0, NULL);

while(read_thread_running && write_thread_running)
Sleep(10);

CloseHandle(read_thread);
CloseHandle(write_thread);

usb_release_interface(hdev, 0);
usb_close(hdev);

return 0;
}

#26
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/22 02:13:52 (permalink)
0
One more from Stephan Meyer's past posts.

http://osdir.com/ml/lib.libusb.devel.windows/2006-06/msg00007.html

On Wed, Jun 14, 2006 at 1:18 AM, Stephan Meyer wrote:
>
> Libusb's kernel driver doesn't have any built-in limitations because
> it doesn't internally queue any data. It just allocates an URB for each
> userspace request and passes that URB down the stack.
>
> But it should be obvious that the host controller driver has to queue
> these request and that the size of this queue is limited since kernel
> memory is a limited resource.
>
> I wrote a small test program (see attachment) to see how many requests
> Windows' USB stack can handle at once. And the limit on WinXP-SP2
> (with 1GB of RAM) seems to be exactly 2500 URBs regardless of the URB's
> data size.
>
> The maximum amount of data I was able to request at once with the test
> program was 2500 URB each with 64kB of data. That's more than 150MB!
>
> This limit may depend on the host controller driver and the Windows version.
>
>
> Stephan
>

On Thu, Jun 15, 2006 at 4:20 PM, Stephan Meyer wrote:
>
> Libusb's kernel driver limits the maximum data size to 64k for
> each URB although Windows' host controller drivers support
> larger sizes.
>
> See: http://support.microsoft.com/default.aspx?scid=kb;%5BLN%5D;832430
>

Stephan's code:

#include <stdio.h>
#include "main.h"


#define NUM_PACKETS 1024
#define PACKET_SIZE (16 * 1024)

int main(int argc, char **argv)
{
usb_dev_handle *dev;
int i;
char tmp[PACKET_SIZE];
void *packets[NUM_PACKETS];

usb_init();
usb_set_debug(4);
usb_find_busses();
usb_find_devices();

dev = usb_open_test_device(0);
usb_set_configuration(dev, 1);
usb_claim_interface(dev, 0);
usb_set_altinterface(dev, 2);

for(i = 0; i < NUM_PACKETS; i++)
{
if(usb_bulk_setup_async(dev, &packets[i], 0x82) < 0)
{
printf("allocating packet %d failed\n", i);
return 0;
}
}

for(i = 0; i < NUM_PACKETS; i++)
{
if(usb_submit_async(packets[i], tmp, sizeof(tmp)) < 0)
{
printf("submitting packet %d failed\n", i);
return 0;
}
}

for(i = 0; i < NUM_PACKETS; i++)
{
if(usb_reap_async(packets[i], 5000) != sizeof(tmp))
{
printf("reaping packet %d failed\n", i);
return 0;
}
}

for(i = 0; i < NUM_PACKETS; i++)
{
usb_free_async(&packets[i]);
}

usb_release_interface(dev, 0);

usb_close(dev);
return 0;
}


post edited by xiaofan - 2010/02/22 02:20:18
#27
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/22 02:16:51 (permalink)
0

On Fri, Sep 29, 2006 at 2:55 AM, Stephan Meyer wrote:
>
> I tried the same on my system (WinXP, 2GHz CPU) and my test program
> (code attached) consumes less than 5% of CPU resources.
>
> But if I enable debugging by calling usb_set_debug(4) and if I connect
> a tool such as DebugView to capture the debug messages then CPU
> load goes up to 80%.
>
> Try to disable debug messages by calling usb_set_debug(0) after usb_init().
>
> My test program also calls usb_find_devices() from a seperate thread and
> it doesn't crash (as you mentioned in your other mail). If I unplug my device
> then the application just terminates.
> The current DLL isn't fully thread safe but refreshing the device list from
> a second thread shouldn't crash the DLL.
>
> Which version of the DLL are you using? Have you tried one of the latest
> development versions?
>
> Stephan


#include <windows.h>

#include "usb.h"


static DWORD WINAPI thread_proc(void *param);
static struct usb_device *find_dev(void);

static DWORD WINAPI thread_proc(void *param)
{
struct usb_device * dev;

while(1) {
dev = find_dev();
Sleep(100);
}
return 0;
}

static struct usb_device *find_dev(void)
{
usb_find_devices();

const int vid = 0x1234, pid = 0x5678;
struct usb_bus *bus;
struct usb_device *dev;

for(bus = usb_get_busses(); bus; bus = bus->next) {
for(dev = bus->devices; dev; dev = dev->next) {
if(dev->descriptor.idVendor == vid
&& dev->descriptor.idProduct == pid)
return dev;
}
}
return NULL;
}

#define BUFSIZE 64

int main(void)
{
HANDLE thread = NULL;
usb_dev_handle *hdev = NULL;
struct usb_device *dev;
char tmp[BUFSIZE];

usb_init();
usb_set_debug(0);
usb_find_busses();
dev = find_dev();

if(!dev)
return 1;
if(!(hdev = usb_open(dev)))
return 1;

usb_set_configuration(hdev, 1);
usb_claim_interface(hdev, 0);
usb_set_altinterface(hdev, 0);

thread = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL);

while(1) {
if(usb_bulk_read(hdev, 0x82, tmp, sizeof(tmp), 1000) < 0)
break;
}

CloseHandle(thread);

usb_release_interface(hdev, 0);
usb_close(hdev);

return 0;
}


#28
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/22 03:01:46 (permalink)
0
There are past report about the potential problems with isochronous transfer with libusb-win32.

http://old.nabble.com/Isochronous-Issues-to12295243.html


I am receiving Isochronous packets from a streaming audio device and I am seeing it take 200 to 700 milliseconds after all of the packets have been received before the completion routine in the driver is called.  However, the packets themselves have nice 999.999 uS spacing.  I have control and interrupt endpoints open as well.
Rob Krakora


  USB_Links and libusb
#29
xiaofan
Super Member
  • Total Posts : 6247
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline
RE: USB Bulk transfer on PIC18F4550 2010/02/22 06:14:36 (permalink)
0
As for the data-toggle (as well as ZLP zero length package), this may be an interesting read.

http://old.nabble.com/usb_bulk_read%28%29-bug--td16260450.html


The whole data toggle thing is not handled by libusb's kernel driver but by Windows' low level host controller drivers.

  USB_Links and libusb
#30
alikazemi8
New Member
  • Total Posts : 2
  • Reward points : 0
  • Joined: 2016/07/22 15:02:23
  • Location: 0
  • Status: offline
Re: USB Bulk transfer on PIC18F4550 2016/07/31 15:42:43 (permalink)
0
hello
Can you introduce a film that done it? Thanks
#31
Page: < 12 Showing page 2 of 2
Jump to:
© 2019 APG vNext Commercial Version 4.5