PIC + USB Communication (how it works)

picusb device

I've been reading a lot about USB communication for a while. I've already made two projects with PIC18F4550 using USB and it all works perfectly. But I still have one unanswered question in my mind. How does the USB stack know if there is new data or not?

I mean, I'm using Microchip's USB HID examples to guide me. So my chip is working in polling mode. My device will receive data constantly and transmit it to the computer through USB. But the computer will also send back some data. The thing is that the computer output data will only happens sometimes. Here's part of the code:

void QueryPCData(void)
{    
if (!HIDRxHandleBusy(lastReceived))
{
    lastReceived = HIDRxPacket(HID_EP,(BYTE*)&hid_out, HID_OUT_LEN);
    if ((hid_out[0]==0xEF)&&(hid_out[1]==0xEF)&&(hid_out[2]==0xEF))
        LED_ON;
    else
        LED_OFF;
}

//If the last transmision is complete
if(!HIDTxHandleBusy(lastTransmission))
{
    lastTransmission = HIDTxPacket(HID_EP, (BYTE*)&hid_in, HID_IN_LEN);
}

return;
}

Looking at the code it seems that every loop it tries to update all data (TX and RX). But what if I did not send any data from the computer? My hid_out array will keep last value until it actually receives new data?

If I want to send a single command through USB then I should send it one time and then send blank spaces or something to prevent repeating the last command (sending it twice)?

Best Answer

The USB spec has the PC (host) tell the device when to send a packet, and when to receive a packet. The USB device cannot "send" data without the PC asking it for it. Thus, just checking "is last transmission done" is not sufficient; you also have to check "has PC sent a request?"

That being said, it's likely the PIC USB support library abstracts that part away. Maybe HIDTxHandleBusy does that for you?

To update the code in question to only send data when you have data available, you could simply change the if() statement that calls HIDTxHandleBusy() to also check whether you have any data you want to send, and only send data if that is the case:

if(!HIDTxHandleBusy(lastTransmission) && IHaveSomeDataToSend)

Or maybe the send/receive functions block until the PC sends the appropriate command on the bus -- you'll have to check the documentation for the chip and the support library to get the real answer for that.

Doing a very quick Google, it seems that the support library takes ownership of your buffer when transmitting, and you cannot overwrite the buffer until the HandleBusy call returns done.

Btw: The code above is actually kind-of gross, because it first checks whether a transmission was complete (for the Rx case) and then starts a new transmission (receipt) and THEN it starts reading bytes out of the buffer (at the point when the USB hardware already has re-taken ownership of the buffer.) This seems like it's a race condition, given that HIDRxBuffer() takes ownership of the buffer.