2018年1月15日 星期一

C8051 USB Device : Modify the MouseExample Code as UART-Input Mouse.



     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

1 則留言: