There is 8 USB endpoints in Silicon Labs's C8051F3X0, but there is few post or discussion about how to enable multi-pair endpoints.this post want to fill the gap, to demonstrate how to enable 2 pair as keyboard and mouse respectively.
The endpoints are set as pairs, one in is corresponds to one out. You could not configure an out endpoint as an in endpoint. An (logical) USB device may have only in endpoint but without out point (for example, mouse), vice versa (for example, soundcard).
Note: data in or out is based on HOST's view, so in means data from device to host, out is conversely.
In the Silicon Labs's example code, ExampleMouse enables 1(control) + 1(data in) as ADC mouse; The other two example, BlinkyExample and HIDtoUARTExample have showed how to send/receive data to/from host, but they do not manifest how to use 2 pairs of endpoint. But multi-pairs endpoints are very necessary : for example, it is a way to implement keyboard-mouse combo; for mouse over 3 buttons, the manufacturer could add a set endpoint pair( as customed HID) in the mouses for use to configurate the definition of buttons. Or one endpoint pair is as camera, another is as audio..etc
Below woks is based on my previous post, which has implement an uart-input mouse. In here I continue it to implement an uart-input keyboard-mouse combo.
F3xx_USB0_Descriptor.c needs to add a structure, keyboard_report_descriptor, and complete configuration_descriptor (including configuration_descriptor, interface_descriptor and endpoint_descriptor) should be modified as the keyboard_report_descriptor as been added, from( line 79 to line 126):
// From "USB Device Class Definition for Human Interface Devices (HID)".
// Section 7.1:
// "When a Get_Descriptor(Configuration) request is issued,
// it returns the Configuration descriptor, all Interface descriptors,
// all Endpoint descriptors, and the HID descriptor for each interface."
const this_configuration_descriptor_all code CONFIGDESC =
{
{ // configuration_descriptor hid_configuration_descriptor
0x09, // Length
0x02, // Type
0x4200, // Totallength = 9 + (9+9+7+7) + (9+9+7)
0x02, // NumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0x80, // bmAttributes
0x20 // MaxPower (in 2mA units)
},
{ // interface_descriptor hid_interface_descriptor
0x09, // bLength
0x04, // bDescriptorType
KEYBOARD_REPORT_ID, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x03, // bInterfaceClass (3 = HID)
0x01, // bInterfaceSubClass
0x01, // bInterfaceProcotol //keyboard
0x00 // iInterface
},
{ // class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x0101, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
LITTLE_ENDIAN(sizeof(keyboard_report_descriptor)) // wItemLength (tot. len. of report descriptor)
},
// IN endpoint (mandatory for HID)
{ // endpoint_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x81, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP1_IN_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
},
// OUT endpoint (optional for HID)
{ // endpoint_descriptor hid_endpoint_out_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x01, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP1_OUT_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
},
{ // interface_descriptor hid_interface_descriptor
0x09, // bLength
0x04, // bDescriptorType
MOUSE_REPORT_ID, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x03, // bInterfaceClass (3 = HID)
0x01, // bInterfaceSubClass
0x02, // bInterfaceProcotol
0x00 // iInterface
},
{ // class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x0101, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
LITTLE_ENDIAN(sizeof(mouse_report_descriptor)) // wItemLength (tot. len. of report
// descriptor)
},
// IN endpoint (mandatory for HID)
{ // endpoint_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x82, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP2_OUT_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
}
};
const keyboard_report_descriptor code KEYBOARDREPORTDESC =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, KEYBOARD_REPORT_ID, //Report ID
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};
const mouse_report_descriptor code MOUSEREPORTDESC =
That code means I adopt the first endpoint set as keyboard, the second(only in) as mouse.
The declaration of in F3xx_USB0_Descriptor.h followed the change :
// all Endpoint descriptors, and the HID descriptor for each interface."
typedef code struct {
configuration_descriptor this_configuration_descriptor;
interface_descriptor keyboard_interface_descriptor;
class_descriptor keyboard_descriptor;
endpoint_descriptor keyboard_endpoint_in_descriptor;
endpoint_descriptor keyboard_endpoint_out_descriptor;
interface_descriptor mouse_interface_descriptor;
class_descriptor mouse_descriptor;
endpoint_descriptor mouse_endpoint_in_descriptor;
}this_configuration_descriptor_all;
#define KEYBOARD_REPORT_DESCRIPTOR_SIZE (67)
typedef code unsigned char keyboard_report_descriptor[KEYBOARD_REPORT_DESCRIPTOR_SIZE];
#define MOUSE_REPORT_DESCRIPTOR_SIZE (54)
typedef code unsigned char mouse_report_descriptor[MOUSE_REPORT_DESCRIPTOR_SIZE];
//-----------------------------
// SETUP Packet Type Definition
//-----------------------------
In file F3xx_USB0_Standard_Requests.c, add keyboard_report_descriptor declaration:
// Additional declarations for HID:
extern const this_configuration_descriptor_all code CONFIGDESC;
extern keyboard_report_descriptor code KEYBOARDREPORTDESC;
extern mouse_report_descriptor code MOUSEREPORTDESC;
case OUT_ENDPOINT in function Get_Status should be generic:
case OUT_ENDPOINT: // See if recipient was an endpoint
if ((USB0_STATE != DEV_CONFIGURED) ||
SETUP.wIndex.c[MSB]) // Make sure device is configured
// and index msb = 0x00
{ // otherwise return stall to host
Force_Stall();
}
else
{
if (SETUP.wIndex.c[LSB] == IN_EP1
|| SETUP.wIndex.c[LSB] == IN_EP2
|| SETUP.wIndex.c[LSB] == IN_EP3)
{
unsigned char endpoint_status_index;
endpoint_status_index = TO_IN_EP_STATUS(SETUP.wIndex.c[LSB]);
if (EP_STATUS[endpoint_status_index] == EP_HALT)
{ // If endpoint is halted,
// return 0x01,0x00
DATAPTR = (unsigned char*)&ONES_PACKET;
DATASIZE = 2;
}
else
{
// Otherwise return 0x00,0x00 to indicate endpoint active
DATAPTR = (unsigned char*)&ZERO_PACKET;
DATASIZE = 2;
}
}
else
{
Force_Stall (); // Send stall if unexpected data
// encountered
}
}
break;
The code makes function Clear_Feature and Set_Feature to support multi-endpoint:
//-----------------------------------------------------------------------------
// Clear_Feature
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Clear_Feature () // This routine can clear Halt Endpoint
{ // features on endpoint 1
// Send procedural stall if device isn't configured
if ( (USB0_STATE != DEV_CONFIGURED) ||
// Or request is made to host(remote wakeup not supported)
(SETUP.bmRequestType == IN_DEVICE) ||
// Or request is made to interface
(SETUP.bmRequestType == IN_INTERFACE) ||
// Or msbs of value or index set to non-zero value
SETUP.wValue.c[MSB] || SETUP.wIndex.c[MSB] ||
// Or data length set to non-zero.
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB])
{
Force_Stall ();
}
else
{
// Verify that packet was directed at an endpoint
if ( (SETUP.bmRequestType == IN_ENDPOINT)&&
// The feature selected was HALT_ENDPOINT
(SETUP.wValue.c[LSB] == ENDPOINT_HALT) &&
// And that the request was directed at EP 1 in
((SETUP.wIndex.c[LSB] == IN_EP1) || (SETUP.wIndex.c[LSB] == IN_EP2)
|| (SETUP.wIndex.c[LSB] == IN_EP3 ) ))
{
unsigned char endpoint_status_index;
endpoint_status_index = TO_IN_EP_STATUS(SETUP.wIndex.c[LSB]);
POLL_WRITE_BYTE (INDEX, endpoint_status_index);
POLL_WRITE_BYTE (EINCSR1, rbInCLRDT);
EP_STATUS[endpoint_status_index] = EP_IDLE; // Set endpoint 1 status back to idle
}
else
{
Force_Stall (); // Send procedural stall
}
}/*if*/
POLL_WRITE_BYTE (INDEX, 0); // Reset Index to 0
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Set Serviced Out packet ready and
// data end to indicate transaction
// is over
}
}
//-----------------------------------------------------------------------------
// Set_Feature
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Set_Feature (void) // This routine will set the EP Halt
{ // feature for endpoint 1
// Make sure device is configured, SETUP data
if ((USB0_STATE != DEV_CONFIGURED) ||
// is all valid and that request is directed at an endpoint
(SETUP.bmRequestType == IN_DEVICE) ||
(SETUP.bmRequestType == IN_INTERFACE) ||
SETUP.wValue.c[MSB] || SETUP.wIndex.c[MSB] ||
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB])
{
Force_Stall (); // Otherwise send stall to host
}
else
{
// Make sure endpoint exists and that halt
if ( (SETUP.bmRequestType == IN_ENDPOINT)&&
(SETUP.wValue.c[LSB] == ENDPOINT_HALT) )
{
unsigned char endpoint_status_index;
endpoint_status_index = 0xFF;
// endpoint feature is selected
if ((IN_EP1 == SETUP.wIndex.c[LSB]) || (OUT_EP1 == SETUP.wIndex.c[LSB]) )
{
endpoint_status_index = 1;
}
else if (IN_EP2 == SETUP.wIndex.c[LSB])
{
endpoint_status_index = 2;
}
else if ((IN_EP3 == SETUP.wIndex.c[LSB]) || (OUT_EP3 == SETUP.wIndex.c[LSB]) )
{
endpoint_status_index = 3;
}/*endpoint*/
if(0xFF != endpoint_status_index)
{
POLL_WRITE_BYTE (INDEX, endpoint_status_index);// Set feature endpoint 1 halt
POLL_WRITE_BYTE (EINCSR1, rbInSDSTL);
EP_STATUS[endpoint_status_index] = EP_HALT;
}/*if !0xFF*/
}
else
{
Force_Stall (); // Send procedural stall
}
}
POLL_WRITE_BYTE (INDEX, 0);
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Indicate SETUP packet has been
// serviced
}
}
case DSC_HID_REPORT in function Get_Descriptor would set keyboard or mouse report_descriptor as returning data, it depends on the host's request:
case DSC_HID_REPORT: // HID Specific (HID report descriptor)
switch(SETUP.wIndex.c[LSB]) //Endpoint index, as bInterfaceNumber in interface_descriptor
{
case KEYBOARD_REPORT_ID:
DATAPTR = (unsigned char*)&KEYBOARDREPORTDESC;
DATASIZE = sizeof(KEYBOARDREPORTDESC);
break;
case MOUSE_REPORT_ID:
DATAPTR = (unsigned char*)&MOUSEREPORTDESC;
DATASIZE = sizeof(MOUSEREPORTDESC);
break;
default:
break;
}/*if SETUP.wValue.c[LSB]*/
break;
Function Set_Configuration needs to enable all endpoints:
//-----------------------------------------------------------------------------
// Set_Configuration
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Set_Configuration (void) // This routine allows host to change
{ // current device configuration value
// Device must be addressed before configured
if ((USB0_STATE == DEV_DEFAULT) ||
// and request recipient must be the device
(SETUP.bmRequestType != IN_DEVICE) ||
// the index and length words must be zero
SETUP.wIndex.c[MSB] || SETUP.wIndex.c[LSB]||
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB] ||
SETUP.wValue.c[MSB] || (SETUP.wValue.c[LSB] > 1))
// This software only supports config = 0,1
{
Force_Stall (); // Send stall if SETUP data is invalid
}
else
{
if (SETUP.wValue.c[LSB] > 0) // Any positive configuration request
{ // results in configuration being set
// to 1
USB0_STATE = DEV_CONFIGURED;
EP_STATUS[1] = EP_STATUS[2] = EP_STATUS[3] = EP_IDLE; // Set endpoint status to idle (enabled)
POLL_WRITE_BYTE (INDEX, 1); // Change index to endpoint 1
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
POLL_WRITE_BYTE (INDEX, 2); // Change index to endpoint 2
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
POLL_WRITE_BYTE (INDEX, 3); // Change index to endpoint 3
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
// Set DIRSEL to indicate endpoint 1 is IN/OUT
Handle_In1();
Handle_In2();
//Handle_In3();
}
else
{
USB0_STATE = DEV_ADDRESS; // Unconfigures device by setting state
EP_STATUS[1] = EP_HALT; // to address, and changing endpoint
// 1 and 2
EP_STATUS[2] = EP_HALT;
EP_STATUS[3] = EP_HALT;
}
}
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Indicate SETUP packet has been
// serviced
}
}
F340_USB0_Mouse.c, the IN_PACKET size should be changed, as the buffer size is not small, I specify it is xdata type (xdata means it is in eXternal data memory, does not be stored in strunction memory. You could refer to here):
unsigned char xdata IN_PACKET[REPORT_IN_BUFFER_SIZE];
The F340_USB0_Mouse.h should be added macro-constant, and add xdata in front IN_PACKET:
extern unsigned char MOUSE_BUTTON;
unsigned char xdata IN_PACKET[];
#define X_Axis 0
#define Y_Axis 1
void System_Init(void);
void USB0_Init(void);
void UART0_Init (
void (*uart_received_callback_function_ptr)(unsigned char received_datum)
);
char putchar (char c);
#define REPORT_IN_BUFFER_SIZE (32)
#define REPORT_OUT_BUFFER_SIZE (8)
#define KEYBOARD_IN_REPORT_SIZE (9)
#define KEYBOARD_OUT_REPORT_SIZE (2)
#define MOUSE_IN_REPORT_SIZE (5)
#endif /* _MOUSE_H_ */
File F3xx_USB0_InterruptServiceRoutine.c : the interrupt handling needs to be generic :
line 59: EP_STATUS supports all endpoints (but I use 3 pairs only), and add some declaration:
unsigned char EP_STATUS[4] = {EP_IDLE, EP_HALT, EP_HALT, EP_HALT};
// Holds the status for each endpoint
//-----------------------------------------------------------------------------
// Local Function Definitions
//-----------------------------------------------------------------------------
void Usb_Resume (void); // resumes USB operation
void Usb_Reset (void); // Called after USB bus reset
void Handle_Control (void); // Handle SETUP packet on EP 0
void Handle_In1 (void); // Handle in packet on EP 1
void Handle_Out1 (void); // Handle out packet on EP 1
//void Handle_In2 (void); // Handle in packet on EP 2
void Handle_Out2 (void); // Handle out packet on EP 2
void Handle_In3 (void); // Handle in packet on EP 3
void Handle_Out3 (void); // Handle out packet on EP 3
Function for USB interrupt : add the handling functions to deal with endpoint 2 and 3 in/out :
//-----------------------------------------------------------------------------
// Usb_ISR
//-----------------------------------------------------------------------------
//
// Called after any USB type interrupt, this handler determines which type
// of interrupt occurred, and calls the specific routine to handle it.
//
//-----------------------------------------------------------------------------
void Usb_ISR (void) interrupt 8 // Top-level USB ISR
{
unsigned char bCommon, bIn, bOut;
POLL_READ_BYTE (CMINT, bCommon); // Read all interrupt registers
POLL_READ_BYTE (IN1INT, bIn); // this read also clears the register
POLL_READ_BYTE (OUT1INT, bOut);
{
if (bCommon & rbRSUINT) // Handle Resume interrupt
{
Usb_Resume ();
}
if (bCommon & rbRSTINT) // Handle Reset interrupt
{
Usb_Reset ();
}
if (bIn & rbEP0) // Handle SETUP packet received
{ // or packet transmitted if Endpoint 0
Handle_Control (); // is in transmit mode
}
if (bIn & rbIN1) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In1 ();
}
if (bOut & rbOUT1) // Handle Out packet received, take
{ // data off endpoint 2 fifo
Handle_Out1 ();
}
if (bIn & rbIN1) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In1 ();
}
if (bOut & rbOUT2) // Handle Out packet received, take
{ // data off endpoint 2 fifo
//Handle_Out2 ();
}
if (bIn & rbIN2) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In2 ();
}
if (bOut & rbOUT3) // Handle Out packet received, take
{ // data off endpoint 2 fifo
Handle_Out3 ();
}
if (bIn & rbIN3) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In3 ();
}
if (bCommon & rbSUSINT) // Handle Suspend interrupt
{
Usb_Suspend ();
}
}
}
In here, the endpoint 2nd (mouse) does not receive data from the host, thus the handling is blank. And handle3 in and out are prepared for future usage.
Function Usb_Reset, add code to handle EP_STATUS[3]:
void Usb_Reset (void)
{
USB0_STATE = DEV_DEFAULT; // Set device state to default
POLL_WRITE_BYTE (POWER, 0x01); // Clear usb inhibit bit to enable USB
// suspend detection
EP_STATUS[0] = EP_IDLE; // Set default Endpoint Status
EP_STATUS[1] = EP_HALT;
EP_STATUS[2] = EP_HALT;
EP_STATUS[3] = EP_HALT;
}
About line 405, the function Handle_In1 and Handle_Out1 have been expanded to handle multi-endpoints:
void Handle_In1 (void){EP_STATUS[1] = EP_IDLE;}
void Handle_In2(void){ EP_STATUS[2] = EP_IDLE;}
void Handle_In3(void){ EP_STATUS[3] = EP_IDLE;}
void Handle_Out_Endpoint_Number(unsigned char endpoint_number)
{
unsigned char Count = 0;
unsigned char ControlReg;
POLL_WRITE_BYTE (INDEX, endpoint_number); // Set index to endpoint X registers
POLL_READ_BYTE (EOUTCSR1, ControlReg);
if (EP_STATUS[endpoint_number] == EP_HALT) // If endpoint is halted, send a stall
{
POLL_WRITE_BYTE (EOUTCSR1, rbOutSDSTL);
}
else // Otherwise read received packet
// from host
{
if (ControlReg & rbOutSTSTL) // Clear sent stall bit if last
// packet was a stall
{
POLL_WRITE_BYTE (EOUTCSR1, rbOutCLRDT);
}
Setup_OUT_BUFFER(); // configure buffer to save
// received data
Fifo_Read(FIFO_EP0 + endpoint_number, OUT_BUFFER.Length, OUT_BUFFER.Ptr);
// process data according to received Report ID.
// In systems with Report Descriptors that do not define report IDs,
// the host will still format OUT packets with a prefix byte
// of '0x00'.
ReportHandler_OUT(endpoint_number);
POLL_WRITE_BYTE (EOUTCSR1, 0); // Clear Out Packet ready bit
}
}
//-----------------------------------------------------------------------------
// Handle_Out1
//-----------------------------------------------------------------------------
// Take the received packet from the host off the fifo and put it into
// the Out_Packet array.
//
//-----------------------------------------------------------------------------
void Handle_Out1()
{
Handle_Out_Endpoint_Number(EP1_OUT_ID);
}
//void Handle_Out2(){}
void Handle_Out3()
{
Handle_Out_Endpoint_Number(EP3_OUT_ID);
}/*Handle_Out3*/
In the buttom, the function SendPacket need to be changed to deal with different endpoint :
void SendPacket (unsigned char ReportID)
{
bit EAState;
unsigned char ControlReg;
if(ReportID <= 0 || ReportID > 3)
return;
EAState = EA;
EA = 0;
POLL_WRITE_BYTE (INDEX, ReportID); // Set index to endpoint 1 registers
// Read contol register for EP 1
POLL_READ_BYTE (EINCSR1, ControlReg);
if (EP_STATUS[ReportID] == EP_HALT) // If endpoint is currently halted,
// send a stall
{
POLL_WRITE_BYTE (EINCSR1, rbInSDSTL);
}
else if(EP_STATUS[ReportID] == EP_IDLE)
{
// the state will be updated inside the ISR handler
EP_STATUS[ReportID] = EP_TX;
if (ControlReg & rbInSTSTL) // Clear sent stall if last
// packet returned a stall
{
POLL_WRITE_BYTE (EINCSR1, rbInCLRDT);
}
if (ControlReg & rbInUNDRUN) // Clear underrun bit if it was set
{
POLL_WRITE_BYTE (EINCSR1, 0x00);
}
ReportHandler_IN_Foreground(ReportID);
// Put new data on Fifo
//Fifo_Write_Foreground (FIFO_EP1, IN_BUFFER.Length,
// (unsigned char *)IN_BUFFER.Ptr);
Fifo_Write_Foreground (FIFO_EP0 + ReportID, IN_BUFFER.Length,
(unsigned char *)IN_BUFFER.Ptr);
POLL_WRITE_BYTE (EINCSR1, rbInINPRDY);
// Set In Packet ready bit,
} // indicating fresh data on FIFO 1
EA = EAState;
}
To the corresponding header, F3xx_USB0_InterruptServiceRoutine.h, add the following line for the macro-constants definition and the function declaration:
#define EP1_OUT_PACKET_SIZE 0x000A// Can range 0 - 1024 depending on data
// and transfer type
#define EP1_IN_PACKET_SIZE (0x000A)
#define EP2_OUT_PACKET_SIZE (0x000A)
void Handle_In2(void);
void Handle_In3(void);
#define IN_EP3 0x83
#define OUT_EP3 0x03
Add function callbacks in File F3xx_USB0_ReportHandler.c to deal with the correspond endpoint in and out, from line 51 to function Setup_OUT_BUFFER:
// ****************************************************************************
// Add custom Report Handler Prototypes Here
// ****************************************************************************
void EP1_IN_Report(void);
void EP1_OUT_Report(void);
void EP2_IN_Report(void);
// ----------------------------------------------------------------------------
// Local Definitions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Set Definitions to sizes corresponding to the number of reports
// ****************************************************************************
#define IN_VECTORTABLESize 2
#define OUT_VECTORTABLESize 1
// ----------------------------------------------------------------------------
// Global Constant Declaration
// ----------------------------------------------------------------------------
// ****************************************************************************
// Link all Report Handler functions to corresponding Report IDs
// ****************************************************************************
const VectorTableEntry code IN_VECTORTABLE[IN_VECTORTABLESize] =
{
// FORMAT: Report ID, Report Handler
{EP1_IN_ID, EP1_IN_Report},
{EP2_IN_ID, EP2_IN_Report}
};
// ****************************************************************************
// Link all Report Handler functions to corresponding Report IDs
// ****************************************************************************
const VectorTableEntry code OUT_VECTORTABLE[OUT_VECTORTABLESize] =
{
// FORMAT: Report ID, Report Handler
{EP1_OUT_ID, EP1_OUT_Report}
};
// ----------------------------------------------------------------------------
// Global Variable Declaration
// ----------------------------------------------------------------------------
BufferStructure IN_BUFFER, OUT_BUFFER;
// ----------------------------------------------------------------------------
// Local Functions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Add all Report Handler routines here
// ****************************************************************************
// ****************************************************************************
// For Input Reports:
// Point IN_BUFFER.Ptr to the buffer containing the report
// Set IN_BUFFER.Length to the number of bytes that will be
// transmitted.
//
// REMINDER:
// Systems using more than one report must define Report IDs
// for each report. These Report IDs must be included as the first
// byte of an IN report.
// Systems with Report IDs should set the FIRST byte of their buffer
// to the value for the Report ID
// AND
// must transmit Report Size + 1 to include the full report PLUS
// the Report ID.
//
// ****************************************************************************
void EP1_IN_Report(void)
{
// point IN_BUFFER pointer to data packet and set
// IN_BUFFER length to transmit correct report size
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Length = KEYBOARD_IN_REPORT_SIZE;
}/*IntoHostValidReport*/
void EP2_IN_Report(void)
{
// point IN_BUFFER pointer to data packet and set
// IN_BUFFER length to transmit correct report size
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Length = MOUSE_IN_REPORT_SIZE;
}/*IntoHostValidReport*/
// ****************************************************************************
// For Output Reports:
// Data contained in the buffer OUT_BUFFER.Ptr will not be
// preserved after the Report Handler exits.
// Any data that needs to be preserved should be copyed from
// the OUT_BUFFER.Ptr and into other user-defined memory.
//
// ****************************************************************************
void EP1_OUT_Report(void)
{
putchar(OUT_BUFFER.Ptr[0]);
putchar(OUT_BUFFER.Ptr[1]);
putchar(0x0d);
putchar(0x0a);
}
unsigned char xdata OUT_PACKET[REPORT_OUT_BUFFER_SIZE];
// ----------------------------------------------------------------------------
// Global Functions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Configure Setup_OUT_BUFFER
//
// Reminder:
// This function should set OUT_BUFFER.Ptr so that it
// points to an array in data space big enough to store
// any output report.
// It should also set OUT_BUFFER.Length to the size of
// this buffer.
//
// ****************************************************************************
void Setup_OUT_BUFFER(void)
{
OUT_BUFFER.Length = sizeof(OUT_PACKET);
OUT_BUFFER.Ptr = &OUT_PACKET[0];
}
File F3xx_USB0_ReportHandler.h should be add the lines :
#define EP1_IN_ID (0x01)
#define EP1_OUT_ID (0x01)
#define EP2_IN_ID (0x02)
#define EP3_IN_ID (0x03)
#define EP3_OUT_ID (0x03)
#define KEYBOARD_REPORT_ID (EP1_IN_ID)
#define MOUSE_REPORT_ID (EP2_IN_ID)
To specify endpoint code and device id.
F3xx_USB0_Main.c's change is pretty simply, the main function becomes :
void main(void)
{
System_Init ();
UART0_Init(UARTReceived);
USB0_Init ();
EA = 1;
while (1)
{
if(KEYBOARD_REPORT_ID == IN_PACKET[0]
|| MOUSE_REPORT_ID == IN_PACKET[0])
{
SendPacket (IN_PACKET[0]);
#if(0)
{
int i;
for(i = 0; i< 9; i++)
printf("0x%02x ", (unsigned int)IN_PACKET[i]);
printf("\r\n");
}
#endif
IN_PACKET[0] = 0x00;
}
}
}
To support KEYBOARD_REPORT_ID.
build and flash the code to the C8051 development board, and connect the board to your computer via USB. Once it is inserting into the computer, the device manager in Windows would discover there are new inserting of a keyboard and a mouse .
Before insert :
Inserting:
And after the inserting, the uart data would display the host request (of led states):
The zeroth byte specify device id, 0x01 represents it is for keyboad; the 1st byte represents keyboard LED states (which are synchronized by the host), one bit for one led: there are number lock, caps lock and screen lock, respectively.
The full code I have modified you could download in here.
There is 8 USB endpoints in Silicon Labs's C8051F3X0, but there is few post or discussion about how to enable multi-pair endpoints.this post want to fill the gap, to demonstrate how to enable 2 pair as keyboard and mouse respectively.
The endpoints are set as pairs, one in is corresponds to one out. You could not configure an out endpoint as an in endpoint. An (logical) USB device may have only in endpoint but without out point (for example, mouse), vice versa (for example, soundcard).
Note: data in or out is based on HOST's view, so in means data from device to host, out is conversely.
In the Silicon Labs's example code, ExampleMouse enables 1(control) + 1(data in) as ADC mouse; The other two example, BlinkyExample and HIDtoUARTExample have showed how to send/receive data to/from host, but they do not manifest how to use 2 pairs of endpoint. But multi-pairs endpoints are very necessary : for example, it is a way to implement keyboard-mouse combo; for mouse over 3 buttons, the manufacturer could add a set endpoint pair( as customed HID) in the mouses for use to configurate the definition of buttons. Or one endpoint pair is as camera, another is as audio..etc
Below woks is based on my previous post, which has implement an uart-input mouse. In here I continue it to implement an uart-input keyboard-mouse combo.
F3xx_USB0_Descriptor.c needs to add a structure, keyboard_report_descriptor, and complete configuration_descriptor (including configuration_descriptor, interface_descriptor and endpoint_descriptor) should be modified as the keyboard_report_descriptor as been added, from( line 79 to line 126):
// From "USB Device Class Definition for Human Interface Devices (HID)".
// Section 7.1:
// "When a Get_Descriptor(Configuration) request is issued,
// it returns the Configuration descriptor, all Interface descriptors,
// all Endpoint descriptors, and the HID descriptor for each interface."
const this_configuration_descriptor_all code CONFIGDESC =
{
{ // configuration_descriptor hid_configuration_descriptor
0x09, // Length
0x02, // Type
0x4200, // Totallength = 9 + (9+9+7+7) + (9+9+7)
0x02, // NumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0x80, // bmAttributes
0x20 // MaxPower (in 2mA units)
},
{ // interface_descriptor hid_interface_descriptor
0x09, // bLength
0x04, // bDescriptorType
KEYBOARD_REPORT_ID, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x03, // bInterfaceClass (3 = HID)
0x01, // bInterfaceSubClass
0x01, // bInterfaceProcotol //keyboard
0x00 // iInterface
},
{ // class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x0101, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
LITTLE_ENDIAN(sizeof(keyboard_report_descriptor)) // wItemLength (tot. len. of report descriptor)
},
// IN endpoint (mandatory for HID)
{ // endpoint_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x81, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP1_IN_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
},
// OUT endpoint (optional for HID)
{ // endpoint_descriptor hid_endpoint_out_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x01, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP1_OUT_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
},
{ // interface_descriptor hid_interface_descriptor
0x09, // bLength
0x04, // bDescriptorType
MOUSE_REPORT_ID, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x03, // bInterfaceClass (3 = HID)
0x01, // bInterfaceSubClass
0x02, // bInterfaceProcotol
0x00 // iInterface
},
{ // class_descriptor hid_descriptor
0x09, // bLength
0x21, // bDescriptorType
0x0101, // bcdHID
0x00, // bCountryCode
0x01, // bNumDescriptors
0x22, // bDescriptorType
LITTLE_ENDIAN(sizeof(mouse_report_descriptor)) // wItemLength (tot. len. of report
// descriptor)
},
// IN endpoint (mandatory for HID)
{ // endpoint_descriptor hid_endpoint_in_descriptor
0x07, // bLength
0x05, // bDescriptorType
0x82, // bEndpointAddress
0x03, // bmAttributes
LITTLE_ENDIAN(EP2_OUT_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN)
10 // bInterval
}
};
const keyboard_report_descriptor code KEYBOARDREPORTDESC =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, KEYBOARD_REPORT_ID, //Report ID
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x08, // REPORT_COUNT (8)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0xFF, // LOGICAL_MAXIMUM (255)
0x05, 0x07, // USAGE_PAGE (Keyboard/Keypad)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x05, // REPORT_COUNT (5)
0x75, 0x01, // REPORT_SIZE (1)
0x05, 0x08, // USAGE_PAGE (LEDs)
0x19, 0x01, // USAGE_MINIMUM (Num Lock)
0x29, 0x05, // USAGE_MAXIMUM (Kana)
0x91, 0x02, // OUTPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x03, // REPORT_SIZE (3)
0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
0xc0 // END_COLLECTION
};
const mouse_report_descriptor code MOUSEREPORTDESC =
That code means I adopt the first endpoint set as keyboard, the second(only in) as mouse.
The declaration of in F3xx_USB0_Descriptor.h followed the change :
// all Endpoint descriptors, and the HID descriptor for each interface."
typedef code struct {
configuration_descriptor this_configuration_descriptor;
interface_descriptor keyboard_interface_descriptor;
class_descriptor keyboard_descriptor;
endpoint_descriptor keyboard_endpoint_in_descriptor;
endpoint_descriptor keyboard_endpoint_out_descriptor;
interface_descriptor mouse_interface_descriptor;
class_descriptor mouse_descriptor;
endpoint_descriptor mouse_endpoint_in_descriptor;
}this_configuration_descriptor_all;
#define KEYBOARD_REPORT_DESCRIPTOR_SIZE (67)
typedef code unsigned char keyboard_report_descriptor[KEYBOARD_REPORT_DESCRIPTOR_SIZE];
#define MOUSE_REPORT_DESCRIPTOR_SIZE (54)
typedef code unsigned char mouse_report_descriptor[MOUSE_REPORT_DESCRIPTOR_SIZE];
//-----------------------------
// SETUP Packet Type Definition
//-----------------------------
In file F3xx_USB0_Standard_Requests.c, add keyboard_report_descriptor declaration:
// Additional declarations for HID:
extern const this_configuration_descriptor_all code CONFIGDESC;
extern keyboard_report_descriptor code KEYBOARDREPORTDESC;
extern mouse_report_descriptor code MOUSEREPORTDESC;
case OUT_ENDPOINT in function Get_Status should be generic:
case OUT_ENDPOINT: // See if recipient was an endpoint
if ((USB0_STATE != DEV_CONFIGURED) ||
SETUP.wIndex.c[MSB]) // Make sure device is configured
// and index msb = 0x00
{ // otherwise return stall to host
Force_Stall();
}
else
{
if (SETUP.wIndex.c[LSB] == IN_EP1
|| SETUP.wIndex.c[LSB] == IN_EP2
|| SETUP.wIndex.c[LSB] == IN_EP3)
{
unsigned char endpoint_status_index;
endpoint_status_index = TO_IN_EP_STATUS(SETUP.wIndex.c[LSB]);
if (EP_STATUS[endpoint_status_index] == EP_HALT)
{ // If endpoint is halted,
// return 0x01,0x00
DATAPTR = (unsigned char*)&ONES_PACKET;
DATASIZE = 2;
}
else
{
// Otherwise return 0x00,0x00 to indicate endpoint active
DATAPTR = (unsigned char*)&ZERO_PACKET;
DATASIZE = 2;
}
}
else
{
Force_Stall (); // Send stall if unexpected data
// encountered
}
}
break;
The code makes function Clear_Feature and Set_Feature to support multi-endpoint:
//-----------------------------------------------------------------------------
// Clear_Feature
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Clear_Feature () // This routine can clear Halt Endpoint
{ // features on endpoint 1
// Send procedural stall if device isn't configured
if ( (USB0_STATE != DEV_CONFIGURED) ||
// Or request is made to host(remote wakeup not supported)
(SETUP.bmRequestType == IN_DEVICE) ||
// Or request is made to interface
(SETUP.bmRequestType == IN_INTERFACE) ||
// Or msbs of value or index set to non-zero value
SETUP.wValue.c[MSB] || SETUP.wIndex.c[MSB] ||
// Or data length set to non-zero.
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB])
{
Force_Stall ();
}
else
{
// Verify that packet was directed at an endpoint
if ( (SETUP.bmRequestType == IN_ENDPOINT)&&
// The feature selected was HALT_ENDPOINT
(SETUP.wValue.c[LSB] == ENDPOINT_HALT) &&
// And that the request was directed at EP 1 in
((SETUP.wIndex.c[LSB] == IN_EP1) || (SETUP.wIndex.c[LSB] == IN_EP2)
|| (SETUP.wIndex.c[LSB] == IN_EP3 ) ))
{
unsigned char endpoint_status_index;
endpoint_status_index = TO_IN_EP_STATUS(SETUP.wIndex.c[LSB]);
POLL_WRITE_BYTE (INDEX, endpoint_status_index);
POLL_WRITE_BYTE (EINCSR1, rbInCLRDT);
EP_STATUS[endpoint_status_index] = EP_IDLE; // Set endpoint 1 status back to idle
}
else
{
Force_Stall (); // Send procedural stall
}
}/*if*/
POLL_WRITE_BYTE (INDEX, 0); // Reset Index to 0
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Set Serviced Out packet ready and
// data end to indicate transaction
// is over
}
}
//-----------------------------------------------------------------------------
// Set_Feature
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Set_Feature (void) // This routine will set the EP Halt
{ // feature for endpoint 1
// Make sure device is configured, SETUP data
if ((USB0_STATE != DEV_CONFIGURED) ||
// is all valid and that request is directed at an endpoint
(SETUP.bmRequestType == IN_DEVICE) ||
(SETUP.bmRequestType == IN_INTERFACE) ||
SETUP.wValue.c[MSB] || SETUP.wIndex.c[MSB] ||
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB])
{
Force_Stall (); // Otherwise send stall to host
}
else
{
// Make sure endpoint exists and that halt
if ( (SETUP.bmRequestType == IN_ENDPOINT)&&
(SETUP.wValue.c[LSB] == ENDPOINT_HALT) )
{
unsigned char endpoint_status_index;
endpoint_status_index = 0xFF;
// endpoint feature is selected
if ((IN_EP1 == SETUP.wIndex.c[LSB]) || (OUT_EP1 == SETUP.wIndex.c[LSB]) )
{
endpoint_status_index = 1;
}
else if (IN_EP2 == SETUP.wIndex.c[LSB])
{
endpoint_status_index = 2;
}
else if ((IN_EP3 == SETUP.wIndex.c[LSB]) || (OUT_EP3 == SETUP.wIndex.c[LSB]) )
{
endpoint_status_index = 3;
}/*endpoint*/
if(0xFF != endpoint_status_index)
{
POLL_WRITE_BYTE (INDEX, endpoint_status_index);// Set feature endpoint 1 halt
POLL_WRITE_BYTE (EINCSR1, rbInSDSTL);
EP_STATUS[endpoint_status_index] = EP_HALT;
}/*if !0xFF*/
}
else
{
Force_Stall (); // Send procedural stall
}
}
POLL_WRITE_BYTE (INDEX, 0);
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Indicate SETUP packet has been
// serviced
}
}
case DSC_HID_REPORT in function Get_Descriptor would set keyboard or mouse report_descriptor as returning data, it depends on the host's request:
case DSC_HID_REPORT: // HID Specific (HID report descriptor)
switch(SETUP.wIndex.c[LSB]) //Endpoint index, as bInterfaceNumber in interface_descriptor
{
case KEYBOARD_REPORT_ID:
DATAPTR = (unsigned char*)&KEYBOARDREPORTDESC;
DATASIZE = sizeof(KEYBOARDREPORTDESC);
break;
case MOUSE_REPORT_ID:
DATAPTR = (unsigned char*)&MOUSEREPORTDESC;
DATASIZE = sizeof(MOUSEREPORTDESC);
break;
default:
break;
}/*if SETUP.wValue.c[LSB]*/
break;
Function Set_Configuration needs to enable all endpoints:
//-----------------------------------------------------------------------------
// Set_Configuration
//-----------------------------------------------------------------------------
//
// Return Value - None
// Parameters - None
//
// Standard request that should not change in custom HID designs.
//
//-----------------------------------------------------------------------------
void Set_Configuration (void) // This routine allows host to change
{ // current device configuration value
// Device must be addressed before configured
if ((USB0_STATE == DEV_DEFAULT) ||
// and request recipient must be the device
(SETUP.bmRequestType != IN_DEVICE) ||
// the index and length words must be zero
SETUP.wIndex.c[MSB] || SETUP.wIndex.c[LSB]||
SETUP.wLength.c[MSB] || SETUP.wLength.c[LSB] ||
SETUP.wValue.c[MSB] || (SETUP.wValue.c[LSB] > 1))
// This software only supports config = 0,1
{
Force_Stall (); // Send stall if SETUP data is invalid
}
else
{
if (SETUP.wValue.c[LSB] > 0) // Any positive configuration request
{ // results in configuration being set
// to 1
USB0_STATE = DEV_CONFIGURED;
EP_STATUS[1] = EP_STATUS[2] = EP_STATUS[3] = EP_IDLE; // Set endpoint status to idle (enabled)
POLL_WRITE_BYTE (INDEX, 1); // Change index to endpoint 1
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
POLL_WRITE_BYTE (INDEX, 2); // Change index to endpoint 2
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
POLL_WRITE_BYTE (INDEX, 3); // Change index to endpoint 3
POLL_WRITE_BYTE (EINCSR2, rbInSPLIT);
POLL_WRITE_BYTE (INDEX, 0); // Set index back to endpoint 0
// Set DIRSEL to indicate endpoint 1 is IN/OUT
Handle_In1();
Handle_In2();
//Handle_In3();
}
else
{
USB0_STATE = DEV_ADDRESS; // Unconfigures device by setting state
EP_STATUS[1] = EP_HALT; // to address, and changing endpoint
// 1 and 2
EP_STATUS[2] = EP_HALT;
EP_STATUS[3] = EP_HALT;
}
}
if (EP_STATUS[0] != EP_STALL)
{
POLL_WRITE_BYTE (E0CSR, (rbSOPRDY | rbDATAEND));
// Indicate SETUP packet has been
// serviced
}
}
F340_USB0_Mouse.c, the IN_PACKET size should be changed, as the buffer size is not small, I specify it is xdata type (xdata means it is in eXternal data memory, does not be stored in strunction memory. You could refer to here):
unsigned char xdata IN_PACKET[REPORT_IN_BUFFER_SIZE];
The F340_USB0_Mouse.h should be added macro-constant, and add xdata in front IN_PACKET:
extern unsigned char MOUSE_BUTTON;
unsigned char xdata IN_PACKET[];
#define X_Axis 0
#define Y_Axis 1
void System_Init(void);
void USB0_Init(void);
void UART0_Init (
void (*uart_received_callback_function_ptr)(unsigned char received_datum)
);
char putchar (char c);
#define REPORT_IN_BUFFER_SIZE (32)
#define REPORT_OUT_BUFFER_SIZE (8)
#define KEYBOARD_IN_REPORT_SIZE (9)
#define KEYBOARD_OUT_REPORT_SIZE (2)
#define MOUSE_IN_REPORT_SIZE (5)
#endif /* _MOUSE_H_ */
File F3xx_USB0_InterruptServiceRoutine.c : the interrupt handling needs to be generic :
line 59: EP_STATUS supports all endpoints (but I use 3 pairs only), and add some declaration:
unsigned char EP_STATUS[4] = {EP_IDLE, EP_HALT, EP_HALT, EP_HALT};
// Holds the status for each endpoint
//-----------------------------------------------------------------------------
// Local Function Definitions
//-----------------------------------------------------------------------------
void Usb_Resume (void); // resumes USB operation
void Usb_Reset (void); // Called after USB bus reset
void Handle_Control (void); // Handle SETUP packet on EP 0
void Handle_In1 (void); // Handle in packet on EP 1
void Handle_Out1 (void); // Handle out packet on EP 1
//void Handle_In2 (void); // Handle in packet on EP 2
void Handle_Out2 (void); // Handle out packet on EP 2
void Handle_In3 (void); // Handle in packet on EP 3
void Handle_Out3 (void); // Handle out packet on EP 3
Function for USB interrupt : add the handling functions to deal with endpoint 2 and 3 in/out :
//-----------------------------------------------------------------------------
// Usb_ISR
//-----------------------------------------------------------------------------
//
// Called after any USB type interrupt, this handler determines which type
// of interrupt occurred, and calls the specific routine to handle it.
//
//-----------------------------------------------------------------------------
void Usb_ISR (void) interrupt 8 // Top-level USB ISR
{
unsigned char bCommon, bIn, bOut;
POLL_READ_BYTE (CMINT, bCommon); // Read all interrupt registers
POLL_READ_BYTE (IN1INT, bIn); // this read also clears the register
POLL_READ_BYTE (OUT1INT, bOut);
{
if (bCommon & rbRSUINT) // Handle Resume interrupt
{
Usb_Resume ();
}
if (bCommon & rbRSTINT) // Handle Reset interrupt
{
Usb_Reset ();
}
if (bIn & rbEP0) // Handle SETUP packet received
{ // or packet transmitted if Endpoint 0
Handle_Control (); // is in transmit mode
}
if (bIn & rbIN1) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In1 ();
}
if (bOut & rbOUT1) // Handle Out packet received, take
{ // data off endpoint 2 fifo
Handle_Out1 ();
}
if (bIn & rbIN1) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In1 ();
}
if (bOut & rbOUT2) // Handle Out packet received, take
{ // data off endpoint 2 fifo
//Handle_Out2 ();
}
if (bIn & rbIN2) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In2 ();
}
if (bOut & rbOUT3) // Handle Out packet received, take
{ // data off endpoint 2 fifo
Handle_Out3 ();
}
if (bIn & rbIN3) // Handle In Packet sent, put new data
{ // on endpoint 1 fifo
Handle_In3 ();
}
if (bCommon & rbSUSINT) // Handle Suspend interrupt
{
Usb_Suspend ();
}
}
}
In here, the endpoint 2nd (mouse) does not receive data from the host, thus the handling is blank. And handle3 in and out are prepared for future usage.
Function Usb_Reset, add code to handle EP_STATUS[3]:
void Usb_Reset (void)
{
USB0_STATE = DEV_DEFAULT; // Set device state to default
POLL_WRITE_BYTE (POWER, 0x01); // Clear usb inhibit bit to enable USB
// suspend detection
EP_STATUS[0] = EP_IDLE; // Set default Endpoint Status
EP_STATUS[1] = EP_HALT;
EP_STATUS[2] = EP_HALT;
EP_STATUS[3] = EP_HALT;
}
About line 405, the function Handle_In1 and Handle_Out1 have been expanded to handle multi-endpoints:
void Handle_In1 (void){EP_STATUS[1] = EP_IDLE;}
void Handle_In2(void){ EP_STATUS[2] = EP_IDLE;}
void Handle_In3(void){ EP_STATUS[3] = EP_IDLE;}
void Handle_Out_Endpoint_Number(unsigned char endpoint_number)
{
unsigned char Count = 0;
unsigned char ControlReg;
POLL_WRITE_BYTE (INDEX, endpoint_number); // Set index to endpoint X registers
POLL_READ_BYTE (EOUTCSR1, ControlReg);
if (EP_STATUS[endpoint_number] == EP_HALT) // If endpoint is halted, send a stall
{
POLL_WRITE_BYTE (EOUTCSR1, rbOutSDSTL);
}
else // Otherwise read received packet
// from host
{
if (ControlReg & rbOutSTSTL) // Clear sent stall bit if last
// packet was a stall
{
POLL_WRITE_BYTE (EOUTCSR1, rbOutCLRDT);
}
Setup_OUT_BUFFER(); // configure buffer to save
// received data
Fifo_Read(FIFO_EP0 + endpoint_number, OUT_BUFFER.Length, OUT_BUFFER.Ptr);
// process data according to received Report ID.
// In systems with Report Descriptors that do not define report IDs,
// the host will still format OUT packets with a prefix byte
// of '0x00'.
ReportHandler_OUT(endpoint_number);
POLL_WRITE_BYTE (EOUTCSR1, 0); // Clear Out Packet ready bit
}
}
//-----------------------------------------------------------------------------
// Handle_Out1
//-----------------------------------------------------------------------------
// Take the received packet from the host off the fifo and put it into
// the Out_Packet array.
//
//-----------------------------------------------------------------------------
void Handle_Out1()
{
Handle_Out_Endpoint_Number(EP1_OUT_ID);
}
//void Handle_Out2(){}
void Handle_Out3()
{
Handle_Out_Endpoint_Number(EP3_OUT_ID);
}/*Handle_Out3*/
In the buttom, the function SendPacket need to be changed to deal with different endpoint :
void SendPacket (unsigned char ReportID)
{
bit EAState;
unsigned char ControlReg;
if(ReportID <= 0 || ReportID > 3)
return;
EAState = EA;
EA = 0;
POLL_WRITE_BYTE (INDEX, ReportID); // Set index to endpoint 1 registers
// Read contol register for EP 1
POLL_READ_BYTE (EINCSR1, ControlReg);
if (EP_STATUS[ReportID] == EP_HALT) // If endpoint is currently halted,
// send a stall
{
POLL_WRITE_BYTE (EINCSR1, rbInSDSTL);
}
else if(EP_STATUS[ReportID] == EP_IDLE)
{
// the state will be updated inside the ISR handler
EP_STATUS[ReportID] = EP_TX;
if (ControlReg & rbInSTSTL) // Clear sent stall if last
// packet returned a stall
{
POLL_WRITE_BYTE (EINCSR1, rbInCLRDT);
}
if (ControlReg & rbInUNDRUN) // Clear underrun bit if it was set
{
POLL_WRITE_BYTE (EINCSR1, 0x00);
}
ReportHandler_IN_Foreground(ReportID);
// Put new data on Fifo
//Fifo_Write_Foreground (FIFO_EP1, IN_BUFFER.Length,
// (unsigned char *)IN_BUFFER.Ptr);
Fifo_Write_Foreground (FIFO_EP0 + ReportID, IN_BUFFER.Length,
(unsigned char *)IN_BUFFER.Ptr);
POLL_WRITE_BYTE (EINCSR1, rbInINPRDY);
// Set In Packet ready bit,
} // indicating fresh data on FIFO 1
EA = EAState;
}
To the corresponding header, F3xx_USB0_InterruptServiceRoutine.h, add the following line for the macro-constants definition and the function declaration:
#define EP1_OUT_PACKET_SIZE 0x000A// Can range 0 - 1024 depending on data
// and transfer type
#define EP1_IN_PACKET_SIZE (0x000A)
#define EP2_OUT_PACKET_SIZE (0x000A)
void Handle_In2(void);
void Handle_In3(void);
#define IN_EP3 0x83
#define OUT_EP3 0x03
Add function callbacks in File F3xx_USB0_ReportHandler.c to deal with the correspond endpoint in and out, from line 51 to function Setup_OUT_BUFFER:
// ****************************************************************************
// Add custom Report Handler Prototypes Here
// ****************************************************************************
void EP1_IN_Report(void);
void EP1_OUT_Report(void);
void EP2_IN_Report(void);
// ----------------------------------------------------------------------------
// Local Definitions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Set Definitions to sizes corresponding to the number of reports
// ****************************************************************************
#define IN_VECTORTABLESize 2
#define OUT_VECTORTABLESize 1
// ----------------------------------------------------------------------------
// Global Constant Declaration
// ----------------------------------------------------------------------------
// ****************************************************************************
// Link all Report Handler functions to corresponding Report IDs
// ****************************************************************************
const VectorTableEntry code IN_VECTORTABLE[IN_VECTORTABLESize] =
{
// FORMAT: Report ID, Report Handler
{EP1_IN_ID, EP1_IN_Report},
{EP2_IN_ID, EP2_IN_Report}
};
// ****************************************************************************
// Link all Report Handler functions to corresponding Report IDs
// ****************************************************************************
const VectorTableEntry code OUT_VECTORTABLE[OUT_VECTORTABLESize] =
{
// FORMAT: Report ID, Report Handler
{EP1_OUT_ID, EP1_OUT_Report}
};
// ----------------------------------------------------------------------------
// Global Variable Declaration
// ----------------------------------------------------------------------------
BufferStructure IN_BUFFER, OUT_BUFFER;
// ----------------------------------------------------------------------------
// Local Functions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Add all Report Handler routines here
// ****************************************************************************
// ****************************************************************************
// For Input Reports:
// Point IN_BUFFER.Ptr to the buffer containing the report
// Set IN_BUFFER.Length to the number of bytes that will be
// transmitted.
//
// REMINDER:
// Systems using more than one report must define Report IDs
// for each report. These Report IDs must be included as the first
// byte of an IN report.
// Systems with Report IDs should set the FIRST byte of their buffer
// to the value for the Report ID
// AND
// must transmit Report Size + 1 to include the full report PLUS
// the Report ID.
//
// ****************************************************************************
void EP1_IN_Report(void)
{
// point IN_BUFFER pointer to data packet and set
// IN_BUFFER length to transmit correct report size
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Length = KEYBOARD_IN_REPORT_SIZE;
}/*IntoHostValidReport*/
void EP2_IN_Report(void)
{
// point IN_BUFFER pointer to data packet and set
// IN_BUFFER length to transmit correct report size
IN_BUFFER.Ptr = IN_PACKET;
IN_BUFFER.Length = MOUSE_IN_REPORT_SIZE;
}/*IntoHostValidReport*/
// ****************************************************************************
// For Output Reports:
// Data contained in the buffer OUT_BUFFER.Ptr will not be
// preserved after the Report Handler exits.
// Any data that needs to be preserved should be copyed from
// the OUT_BUFFER.Ptr and into other user-defined memory.
//
// ****************************************************************************
void EP1_OUT_Report(void)
{
putchar(OUT_BUFFER.Ptr[0]);
putchar(OUT_BUFFER.Ptr[1]);
putchar(0x0d);
putchar(0x0a);
}
unsigned char xdata OUT_PACKET[REPORT_OUT_BUFFER_SIZE];
// ----------------------------------------------------------------------------
// Global Functions
// ----------------------------------------------------------------------------
// ****************************************************************************
// Configure Setup_OUT_BUFFER
//
// Reminder:
// This function should set OUT_BUFFER.Ptr so that it
// points to an array in data space big enough to store
// any output report.
// It should also set OUT_BUFFER.Length to the size of
// this buffer.
//
// ****************************************************************************
void Setup_OUT_BUFFER(void)
{
OUT_BUFFER.Length = sizeof(OUT_PACKET);
OUT_BUFFER.Ptr = &OUT_PACKET[0];
}
File F3xx_USB0_ReportHandler.h should be add the lines :
#define EP1_IN_ID (0x01)
#define EP1_OUT_ID (0x01)
#define EP2_IN_ID (0x02)
#define EP3_IN_ID (0x03)
#define EP3_OUT_ID (0x03)
#define KEYBOARD_REPORT_ID (EP1_IN_ID)
#define MOUSE_REPORT_ID (EP2_IN_ID)
To specify endpoint code and device id.
F3xx_USB0_Main.c's change is pretty simply, the main function becomes :
void main(void)
{
System_Init ();
UART0_Init(UARTReceived);
USB0_Init ();
EA = 1;
while (1)
{
if(KEYBOARD_REPORT_ID == IN_PACKET[0]
|| MOUSE_REPORT_ID == IN_PACKET[0])
{
SendPacket (IN_PACKET[0]);
#if(0)
{
int i;
for(i = 0; i< 9; i++)
printf("0x%02x ", (unsigned int)IN_PACKET[i]);
printf("\r\n");
}
#endif
IN_PACKET[0] = 0x00;
}
}
}
To support KEYBOARD_REPORT_ID.
build and flash the code to the C8051 development board, and connect the board to your computer via USB. Once it is inserting into the computer, the device manager in Windows would discover there are new inserting of a keyboard and a mouse .
Before insert :
Inserting:
And after the inserting, the uart data would display the host request (of led states):
The zeroth byte specify device id, 0x01 represents it is for keyboad; the 1st byte represents keyboard LED states (which are synchronized by the host), one bit for one led: there are number lock, caps lock and screen lock, respectively.
The full code I have modified you could download in here.