# PIC + USB Communication (how it works)

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 ((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)?

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.

• First of all thanks for reply. I might have expressed myself badly. I agree with you that the PC(HOST) is who "controls" the transfers (sends data and requests for new data). But my device not always will have data to receive or to send. As you said, for data transactions between PIC->PC is easy to work around by adding this new 'if'statement. But what if the transaction is PC->PIC? Whenever PC sends new data or not, PIC will execute HIDRxPacket(). What data will it read to hid_out when PC had not send anything? How can I know if is old or new data? – Felipe_Ribas Feb 11 '14 at 4:51
• As for the code, I'm not entirely sure but I remember to copy this piece of code from Microchip example. I'll double check on that. But could you explain better where is exaclty this race condition that you said? I'm not sure I see that. – Felipe_Ribas Feb 11 '14 at 4:53
• The PC will send a "you may return data when you want to" message, and the device can then stall that request for some time. The PC host program may time out after some amount of time. When it comes to the "read" part, you can set a flag when you call HIDTxReceive() and clear it when you read the buffer, and not read the buffer again unless the flag is set. The race condition is there if the USB receive code is asynchronous; it may then start writing into the buffer (overwriting previous data) before you actually read the data out. Put the Receive call last in the if() to fix. – Jon Watte Feb 12 '14 at 3:03
• Oh I see. It really makes sense. I just put that code into there in a hurry to test but thanks for reminding. – Felipe_Ribas Feb 12 '14 at 4:54