SLAU533D September 2013 – April 2017
In USB, keyboards are implemented as Human Interface Device (HID) interfaces. Within the USB descriptors reported to the host during enumeration, the application declares itself to contain a keyboard HID interface. It then sends specially formatted HID reports to the host, to tell it about key presses. While no key presses occur, no reports are sent.
Although the word "send" is an easy way to describe it, it is not quite correct. In USB, everything is initiated by the host. What actually happens with HID interfaces is that the USB device prepares a report and makes it available to the host. Then, on a regular interval, the host polls the device to see if it has any reports ready. In the Descriptor Tool, this particular interface was set to have the fastest possible polling interval: 1 ms.
After receiving a report indicating a keystroke, the host assumes the key is held down until a report is later sent to indicate its release. Because of this, the demo application quickly follows every key-press report with a key-release report.
So when a LaunchPad development kit pushbutton press occurs, it sets a flag and wakes main(), had it been sleeping in LPM0. Execution eventually checks the flags associated with the buttons. If the button had been pressed, it calls prepSendingStr() to fetch the target string from the file associated with that button.
// Handle a press of button 1, if it happened
if (bButton1Pressed && !charLeftToSend)
{
prepSendingStr("0:Button1.txt");
bButton1Pressed = FALSE;
}
It uses high-level FatFs calls to do this – to mount the volume, open it, read it, and close it. When the string has been obtained, main() assigns the length of that string to charLeftToSend. While this variable is non-zero, it means there are still characters left to transmit to the host.
Later in main(), code evaluates charLeftToSend, and also checks whether a USB report is still waiting to be fetched from the host. If characters still need to be sent, and if the USB HID interface is available, the report is prepared, and USBHID_sendReport() is called to "send" it. A flag bKeyDirIsDown is used to alternate between down-presses and up-presses, to ensure every down-press is followed by an up-press.
if (bUsbSendComplete && charLeftToSend)
{
if(bKeyDirIsDown) // Will this be a down-press?
{
KB_addKeypressToReport(btnStr[btnStrLen-charLeftToSend]);
bKeyDirIsDown = FALSE;
}
else // Or will it be an up-press?
{
KB_addKeyReleaseToReport(btnStr[btnStrLen-charLeftToSend]);
bKeyDirIsDown = TRUE;
charLeftToSend--;
}
bUsbSendComplete = FALSE;
USBHID_sendReport(KB_getReportPtr(), HID0_INTFNUM);
}
USBHID_sendReport() copies the report to the USB endpoint buffer, making it available to the host. HID0_INTFNUM is a value that references this particular HID interface; if additional HID interfaces had been created within this device, this parameter is how code could access them separately. The Descriptor Tool defines an INTFNUM constant for every interface it creates, stored in descriptors.h.
When the host gets around to fetching the report, a USBHID_handleSendCompleted() event is generated.
BYTE USBHID_handleSendCompleted (BYTE intfNum)
{
bUsbSendComplete = TRUE;
return (TRUE); // Returning TRUE wakes the main loop, if it had been
} // sleeping.
This is one of the USB event handlers in usbEventHandling.c. These handlers are defined by the API, and the developer can insert code that should execute when those events occur. In this application, the bUsbSendComplete flag is set to TRUE in the handler, and the handler returns TRUE, which wakes main() if it had been sleeping at the LPM0 entry. This allows main() to send the next character, if one is waiting to be sent.