📄 usb_framework.c
字号:
/// \addtogroup module_usb_framework
/// @{
#define USBFRAMEWORK_C
#include "cc2511_usb_library_headers.h"
/** \brief Initializes the USB framework
*
* This function should be called when the microcontroller is ready to accept USB traffic. It enables the
* USB peripheral unit and enables the pull-up resistor on the D+ line. Endpoint status, current
* configuration value, etc. are initialized and evenetually re-initialized in the
* \ref usbfwResetHandler() function.
*/
void usbfwInit(void) {
// Set default values
usbfwData.selfPowered = (usbdpGetConfigurationDesc(1, 0)->bmAttributes & 0x40) ? TRUE : FALSE;
usbfwData.remoteWakeup = FALSE;
// Enable pullup on D+
USB_ENABLE_PULLUP();
// Enable USB
SLEEP |= SLEEP_USB_EN;
} // usbfwInit
/** \brief Handles USB reset signalling
*
* This function should be called, either from the USB interrupt or the main loop, when the \c USBCIF.RST
* flag has been set. Keep in mind that all bits in \c USBCIF register are cleared when the register is
* read. The function puts the device into the default state (not yet addressed), and puts all endpoints
* (except EP0) into the \ref EP_HALT state
*/
void usbfwResetHandler(void) {
// Reset the USB state
usbfwData.usbState = DEV_DEFAULT;
USBADDR = 0;
usbfwData.configurationValue = 0;
// Reset all endpoints
usbfwData.ep0Status = EP_IDLE;
usbfwSetAllEpStatus(EP_HALT);
} // usbfwResetHandler
/** \brief USB Setup Handler
*
* This function should be called either from the USB interrupt or the main loop when the \c USBIIF.EP0IF
* flag has been set. Keep in mind that all bits in \c USBIIF register are cleared when the register is
* read. A detailed description of the framework is found in the \ref section_setup_handler_usage
* section.
*
* \note The USB header data is always little-endian, so if a big-endian compiler is used (such as Keil
* C51), the 16-bit values in the \ref usbSetupHeader must be flipped before they are used.
*/
void usbfwSetupHandler(void) {
static VFPT ProcessFunc = NULL;
BYTE controlReg;
UINT8 bytesNow;
UINT8 oldEndpoint;
// Save the old index setting, then select endpoint 0 and fetch the control register
oldEndpoint = USBFW_GET_SELECTED_ENDPOINT();
USBFW_SELECT_ENDPOINT(0);
controlReg = USBCS0;
// The last transfer was ended prematurely by a new SETUP packet
if (controlReg & USBCS0_SETUP_END) {
USBCS0 = USBCS0_CLR_SETUP_END;
usbfwData.ep0Status = EP_CANCEL;
if (ProcessFunc) ProcessFunc();
usbfwData.ep0Status = EP_IDLE;
}
// A STALL handshake was transmitted to the PC
if (controlReg & USBCS0_SENT_STALL) {
USBCS0 = 0x00;
usbfwData.ep0Status = EP_IDLE;
}
// Receive OUT packets
if (usbfwData.ep0Status == EP_RX) {
// Read FIFO
bytesNow = USBCNT0;
usbfwReadFifo(&USBF0, bytesNow, usbSetupData.pBuffer);
usbSetupData.bytesLeft -= bytesNow;
usbSetupData.pBuffer += bytesNow;
// Arm the endpoint
USBCS0 = usbSetupData.bytesLeft ? USBCS0_CLR_OUTPKT_RDY : (USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END);
// Make a call to the appropriate request handler when done
if (usbSetupData.bytesLeft == 0) {
if (ProcessFunc) ProcessFunc();
usbfwData.ep0Status = EP_IDLE;
}
// Return here since nothing more will happen until the next interrupt
USBFW_SELECT_ENDPOINT(oldEndpoint);
return;
// Let the application handle the reception
} else if (usbfwData.ep0Status == EP_MANUAL_RX) {
ProcessFunc();
}
// Receive SETUP header
if (usbfwData.ep0Status == EP_IDLE) {
if (controlReg & USBCS0_OUTPKT_RDY) {
usbfwReadFifo(&USBF0, 8, (BYTE __data *) &usbSetupHeader);
// Handle control transfers individually
ProcessFunc = NULL;
switch (usbSetupHeader.requestType & (RT_MASK_TYPE | RT_MASK_DIR)) {
// Standard requests with data from the host (OUT)
case RT_STD_OUT:
switch (usbSetupHeader.request) {
case SET_ADDRESS: usbsrSetAddress(); break;
case SET_FEATURE: usbsrSetFeature(); break;
case CLEAR_FEATURE: usbsrClearFeature(); break;
case SET_CONFIGURATION: usbsrSetConfiguration(); break;
case SET_INTERFACE: usbsrSetInterface(); break;
case SET_DESCRIPTOR: usbsrHookSetDescriptor(); break;
default: usbfwData.ep0Status = EP_STALL; break;
}
break;
// Standard requests with data to the host (IN)
case RT_STD_IN:
switch (usbSetupHeader.request) {
case GET_STATUS: usbsrGetStatus(); break;
case GET_DESCRIPTOR: usbsrGetDescriptor(); break;
case GET_CONFIGURATION: usbsrGetConfiguration(); break;
case GET_INTERFACE: usbsrGetInterface(); break;
case SYNCH_FRAME: usbsrHookSynchFrame(); break;
default: usbfwData.ep0Status = EP_STALL; break;
}
break;
// Vendor requests
case RT_VEND_OUT:
ProcessFunc = usbvrHookProcessOut; usbvrHookProcessOut();
break;
case RT_VEND_IN:
ProcessFunc = usbvrHookProcessIn; usbvrHookProcessIn();
break;
// Class requests
case RT_CLASS_OUT:
ProcessFunc = usbcrHookProcessOut; usbcrHookProcessOut();
break;
case RT_CLASS_IN:
ProcessFunc = usbcrHookProcessIn; usbcrHookProcessIn();
break;
// Unrecognized request: Stall the endpoint
default:
usbfwData.ep0Status = EP_STALL;
break;
}
// Arm/stall the endpoint
USBCS0 = (usbfwData.ep0Status == EP_STALL) ? (USBCS0_CLR_OUTPKT_RDY | USBCS0_SEND_STALL) : USBCS0_CLR_OUTPKT_RDY;
}
}
// Transmit IN packets
if (usbfwData.ep0Status == EP_TX) {
controlReg = USBCS0_INPKT_RDY;
// The last frame should contain 0 to (EP0_PACKET_SIZE - 1) bytes
if (usbSetupData.bytesLeft < EP0_PACKET_SIZE) {
bytesNow = usbSetupData.bytesLeft;
controlReg |= USBCS0_DATA_END;
// All other packets should have the maximum length
} else {
bytesNow = EP0_PACKET_SIZE;
}
// Load the FIFO and move the pointer
usbfwWriteFifo(&USBF0, bytesNow, usbSetupData.pBuffer);
usbSetupData.pBuffer += bytesNow;
usbSetupData.bytesLeft -= bytesNow;
// Arm the FIFO (even for a zero-length packet)
USBCS0 = controlReg;
// Make a call to the appropriate request handler when done
if (bytesNow < EP0_PACKET_SIZE) {
if (ProcessFunc) ProcessFunc();
usbfwData.ep0Status = EP_IDLE;
}
// Let the application handle the transmission
} else if (usbfwData.ep0Status == EP_MANUAL_TX) {
ProcessFunc();
}
// Restore the old index setting
USBFW_SELECT_ENDPOINT(oldEndpoint);
} // usbfwSetupHandler
/** \brief Changes the state of endpoint 1-5 IN/OUT
*
* This is an internal function used by the framework.
*
* \param[in] status
* The new status for each endpoint
*/
void usbfwSetAllEpStatus(EP_STATUS status) {
UINT8 n;
for (n = 0; n < sizeof(usbfwData.pEpInStatus); n++) usbfwData.pEpInStatus[n] = status;
for (n = 0; n < sizeof(usbfwData.pEpOutStatus); n++) usbfwData.pEpOutStatus[n] = status;
} // usbfwSetAllEpStatus
/** \brief Reads from the selected OUT endpoint FIFO, without using DMA
*
* The FIFO must be re-armed after reading it empty (using the \ref USBFW_ARM_OUT_ENDPOINT() macro). This
* is not necessary when flushing the FIFO.
*
* \param[in] *pFifo
* Pointer to the FIFO (\c &USBFx)
* \param[in] count
* The number of bytes to read
* \param[in] *pData
* A pointer to the storage location for the read data (in any memory space)
*/
void usbfwReadFifo(BYTE volatile __xdata *pFifo, UINT8 count, void __generic *pData) {
BYTE __generic *pTemp = pData;
if (count) {
do {
*(pTemp++) = *pFifo;
} while (--count);
}
} // usbfwReadFifo
/** \brief Writes to the selected IN endpoint FIFO, without using DMA
*
* Note that the FIFO must be armed in order to be transmitted (using the \ref USBFW_ARM_IN_ENDPOINT()
* macro).
*
* \param[in] *pFifo
* Pointer to the FIFO (\c &USBFx)
* \param[in] count
* The number of bytes to write
* \param[in] *pData
* A pointer to the data to be written (from any memory space)
*/
void usbfwWriteFifo(BYTE volatile __xdata *pFifo, UINT8 count, void __generic *pData) {
BYTE __generic *pTemp = pData;
if (count) {
do {
*pFifo = *(pTemp++);
} while (--count);
}
} // usbfwWriteFifo
/// @}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -