I found the example code of silicon labs's MouseExample is not explicit: the input signal is from analog to digital converter (ADC) pin: it blurs the key-point for it is an USB example; and the mouse is without scrolling function: the mouse is pretty incomplete.
I simply the example code to make it as an uart-input mouse, it will be helpful for the USB beginners.
零. Create a Keil-C project for MouseExample.
It is very intuitive, copy the code from the Silicon Labs folder, C8051F3xx\USB_HID\Firmware\MouseExample, and create a Keil-C project as your chip then add the sources under the project. Beware, the F3xx_USB0_Mouse.c depends on what chip you adopt. For me, I use C8051F340, it is F340_USB0_Mouse.c:
And the flasher and debugger should be In Circuit Emulator(ICE):
Press rebuild then load, this firmware would be recognized as USB mouse by the computer. You will find the mouse cursor moves by this device as the ADC triggers positions changes in function Adc_ConvComplete_ISR of file F3X0_USB0_Mouse.c, about line 147.
一. Modify the code as standard mouse:
In the file F3x0_USB0_Mouse.c, line 82, the array size of IN_PACKET needs to be 5:
unsigned char IN_PACKET[5]; //-----------------------------------------------------------------------------
in the same file, comment out the ADC function which would be called in function System_Init, about line 197:
void System_Init (void) { PCA0MD &= ~0x40; // Disable Watchdog timer Sysclk_Init (); // initialize system clock Port_Init (); // configure cross bar #if(0) Timer_Init (); // configure timer ADC0_Init (); #endif }
And Add the uart functions in the bottom:
#include <stdlib.h> #define BAUDRATE 9600 // Baud rate of UART in bps void (*g_uart_received_callback_function_ptr)(unsigned char received_datum) = NULL; void UART0_Init ( void (*uart_received_callback_function_ptr)(unsigned char received_datum) ) { XBR0 = 0x01; // Enable UART on P0.4(TX) and P0.5(RX) XBR1 = 0x40; // Enable crossbar and weak pull-ups OSCICN |= 0x03; // Configure internal oscillator for // its maximum frequency RSTSRC = 0x04; // Enable missing clock detector SCON0 = 0x10; // SCON0: 8-bit variable bit rate // level of STOP bit is ignored // RX enabled // ninth bits are zeros // clear RI0 and TI0 bits if (SYSCLK/BAUDRATE/2/256 < 1) { TH1 = -(SYSCLK/BAUDRATE/2); CKCON &= ~0x0B; // T1M = 1; SCA1:0 = xx CKCON |= 0x08; } else if (SYSCLK/BAUDRATE/2/256 < 4) { TH1 = -(SYSCLK/BAUDRATE/2/4); CKCON &= ~0x0B; // T1M = 0; SCA1:0 = 01 CKCON |= 0x01; } else if (SYSCLK/BAUDRATE/2/256 < 12) { TH1 = -(SYSCLK/BAUDRATE/2/12); CKCON &= ~0x0B; // T1M = 0; SCA1:0 = 00 } else { TH1 = -(SYSCLK/BAUDRATE/2/48); CKCON &= ~0x0B; // T1M = 0; SCA1:0 = 10 CKCON |= 0x02; } TL1 = TH1; // init Timer1 TMOD &= ~0xf0; // TMOD: timer 1 in 8-bit autoreload TMOD |= 0x20; TR1 = 1; // START Timer1 IP |= 0x10; // Make UART high priority ES0 = 1; // Enable UART0 interrupts g_uart_received_callback_function_ptr = uart_received_callback_function_ptr; }/*UART0_Init*/ //----------------------------------------------------------------------------- // Interrupt Service Routines //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // UART0_Interrupt //----------------------------------------------------------------------------- // // This routine is invoked whenever a character is entered or displayed on the // Hyperterminal. // //----------------------------------------------------------------------------- void UART0_Interrupt (void) interrupt 4 { if (RI0 == 1) { unsigned char c; RI0 = 0; c = SBUF0; if(NULL != g_uart_received_callback_function_ptr) g_uart_received_callback_function_ptr(c); }/*RI0 == 1*/ }/*UART0_Interrupt*/ char putchar (char c) { SBUF0 = c; while(0 == TI0); TI0 = 0; return c; }/*putchar*/
And add the declaration and macro-constant at F3x0_USB0_Mouse.h :
void UART0_Init ( void (*uart_received_callback_function_ptr)(unsigned char received_datum) ); #define MOUSE_IN_REPORT_SIZE (5)
For the file F3xx_USB0_ReportHandler.c, about line 125, to remove line about fetching the data as mouse input from ADC in function IN_Report, it be :
void IN_Report(void){ IN_BUFFER.Ptr = IN_PACKET; IN_BUFFER.Length = MOUSE_IN_REPORT_SIZE; }
F3xx_USB0_ReportHandler.h should be added the macro-constant as mouse report id:
#define MOUSE_REPORT_ID (0x02)
The file F3xx_USB0_Main.c, it needs to be replaced as :
#include "c8051f3xx.h" #include "F3xx_USB0_InterruptServiceRoutine.h" #include "F3xx_USB0_Mouse.h" #include "F3xx_USB0_ReportHandler.h" #include <stdio.h> #include <string.h> #define MAX_UART_RX_DATA_BUFFER_SIZE (24) unsigned char xdata uart_rx_data_buffer[MAX_UART_RX_DATA_BUFFER_SIZE];// = {0}; unsigned char uart_rx_data_size = 0; void ReportParser(void) { if(uart_rx_data_size >= 2) { if( 0x0d == uart_rx_data_buffer[uart_rx_data_size - 2] && 0x0a == uart_rx_data_buffer[uart_rx_data_size - 1] ) { if(uart_rx_data_size >= MOUSE_IN_REPORT_SIZE + 2) { memcpy(&IN_PACKET[0], &uart_rx_data_buffer[0], uart_rx_data_size - 2); }/*if */ memset(&uart_rx_data_buffer[0], 0, MAX_UART_RX_DATA_BUFFER_SIZE); uart_rx_data_size = 0; }/*if end of line */ }/*if \r\n exist*/ if(MAX_UART_RX_DATA_BUFFER_SIZE == uart_rx_data_size) { memset(&uart_rx_data_buffer[0], 0, MAX_UART_RX_DATA_BUFFER_SIZE); uart_rx_data_size = 0; }/*if MAX_DATA_BUFFER_SIZE == g_received_data_size */ }/*ReportParser*/ void UARTReceived(unsigned char c) { uart_rx_data_buffer[uart_rx_data_size] = c; uart_rx_data_size++; ReportParser(); }/*UARTReceived*/ //----------------------------------------------------------------------------- // Main Routine //----------------------------------------------------------------------------- void main(void) { System_Init (); UART0_Init(UARTReceived); USB0_Init (); EA = 1; while (1) { if(MOUSE_REPORT_ID == IN_PACKET[0]) { SendPacket (0); #if(0) { int i; for(i = 0; i< 5; i++) printf("0x%02x ", (unsigned int)IN_PACKET[i]); printf("\r\n"); } #endif IN_PACKET[0] = 0x00; } } }
For file F3xx_USB0_Descriptor.c, it is requsite to include F3xx_USB0_ReportHandler.h ; and the structure device_descriptor, configuration_descriptor, interface_descriptor, class_descriptor, endpoint_descriptor and report_descriptor would be rewritten and rectified (from line 39 to 155):
#include "F3xx_USB0_Descriptor.h" #include "F3xx_USB0_ReportHandler.h" #define SWAP_UINT16(X) \ ((((unsigned short)(X)) >> 8) | (((unsigned short)(X)) << 8)) #define LITTLE_ENDIAN(VALUE) (SWAP_UINT16(VALUE)) //----------------------------------------------------------------------------- // Descriptor Declarations //----------------------------------------------------------------------------- #define VENDOR_ID (0x10C4) #define PRODUCT_ID (0x81B9) #define FIRMWARE_REVISION_NUMER (0x0000) const device_descriptor code DEVICEDESC = { 18, // bLength 0x01, // bDescriptorType 0x1001, // bcdUSB 0x00, // bDeviceClass 0x00, // bDeviceSubClass 0x00, // bDeviceProtocol EP0_PACKET_SIZE, // bMaxPacketSize0 LITTLE_ENDIAN(VENDOR_ID), // idVendor LITTLE_ENDIAN(PRODUCT_ID), // idProduct LITTLE_ENDIAN(FIRMWARE_REVISION_NUMER), // bcdDevice 0x01, // iManufacturer 0x02, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigurations }; //end of DEVICEDESC // 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 0x2200, // Totallength (= 9+9+9+7) 0x01, // NumInterfaces 0x01, // bConfigurationValue 0x00, // iConfiguration 0x80, // bmAttributes 0x20 // MaxPower (in 2mA units) }, { // 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 0x81, // bEndpointAddress 0x03, // bmAttributes LITTLE_ENDIAN(EP1_PACKET_SIZE), // MaxPacketSize (LITTLE ENDIAN) 10 // bInterval } }; const mouse_report_descriptor code MOUSEREPORTDESC = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x02, // USAGE (Mouse) 0xa1, 0x01, // COLLECTION (Application) 0x85, MOUSE_REPORT_ID, //Report ID (2) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x01, // REPORT_SIZE (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x03, // REPORT_SIZE (3) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x09, 0x38, // USAGE (Wheel) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x03, // REPORT_COUNT (3) 0x81, 0x06, // INPUT (Data,Var,Rel) 0xc0, // END_COLLECTION 0xc0 // END_COLLECTION }; #define STR0LEN 4 code unsigned char String0Desc [STR0LEN] = { STR0LEN, 0x03, 0x09, 0x04
From line 143, F3xx_USB0_Descriptor.h would be :
// it returns the Configuration descriptor, all Interface descriptors, // all Endpoint descriptors, and the HID descriptor for each interface." typedef code struct { configuration_descriptor this_configuration_descriptor; interface_descriptor mouse_interface_descriptor; class_descriptor mouse_descriptor; endpoint_descriptor mouse_endpoint_in_descriptor; }this_configuration_descriptor_all; #define MOUSE_REPORT_DESCRIPTOR_SIZE (54) typedef code unsigned char mouse_report_descriptor[MOUSE_REPORT_DESCRIPTOR_SIZE]; //-----------------------------
F3xx_USB0_Standard_Requests.c needs to be simplified:
About line 50 to 54:
// Additional declarations for HID: extern const this_configuration_descriptor_all code CONFIGDESC; extern mouse_report_descriptor code MOUSEREPORTDESC; extern setup_buffer SETUP;
About line 73, att a macro-function:
// This minimizes the impact on the existing source code. #define ConfigDesc (CONFIGDESC.this_configuration_descriptor) //----------------------------------------------------------------------------- // Get_Status
Remove the useless switch cases : DSC_INTERFACE, DSC_ENDPOINT and DSC_HID, modify the variable in case DSC_HID_REPORT, about line 377 to 411:
#if(0) case DSC_INTERFACE: DATAPTR = (unsigned char*) &InterfaceDesc; DATASIZE = InterfaceDesc.bLength; break; case DSC_ENDPOINT: // This example splits endpoint 1 into an // IN endpoint and an OUT endpoint // In the ...Descriptor.c and ...Descriptor.h files, // OUT endpoint 1 is referred to as Endpoint 2. if ( (SETUP.wValue.c[LSB] == IN_EP1) ) { DATAPTR = (unsigned char*) &Endpoint1Desc; DATASIZE = Endpoint1Desc.bLength; } else if ( (SETUP.wValue.c[LSB] == OUT_EP1) ) { DATAPTR = (unsigned char*) &Endpoint2Desc; DATASIZE = Endpoint2Desc.bLength; } else { Force_Stall(); } break; case DSC_HID: // HID Specific (HID class descriptor) DATAPTR = (unsigned char*)&HidDesc; DATASIZE = HidDesc.bLength; break; #endif case DSC_HID_REPORT: // HID Specific (HID report descriptor) DATAPTR = (unsigned char*)&MOUSEREPORTDESC; DATASIZE = sizeof(MOUSEREPORTDESC); break;
Now, this mouse device works: you could try to use uart to input 02 00 a0 40 00 0d 0a (in hex), and your cursor would move to left-down.
About the input byte array :
Byte 0, device id: it should be 2, to indicate it is mouse
Byte 1, buttons: bit 0 represents left button, bit 1 represents right button, and bit 2 represents middle button. 1 means pressed, 0 means released.
Byte 2, horizontal move: from -127 (to left) to 127( to right).
Byte 3, vertical moving: from -127(to up) to 127(to bottom).
Byte 4 scrolling : from -127(scrolling down) to 127(scrolling up).
For byte zeroth, you may feel this column is redundant : this device has been specified it is mouse, why this column is necessary ? It is because I will enable another endpoint as keyboard in my next post. Therefore, I reserve the zeroth byte as device id first.
You could download this full code with Keil-C project in here.
網誌管理員已經移除這則留言。
回覆刪除