Lufa GenericHID

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi! I develop a HID device identical func. at the V-USB and Lufa. The library, basis on the http://www.codeproject.com/KB/cs... and added functions for working with Feature Reports. so, the device will be analogous to Lib-usb, from the program on C# you can write \ read registers, eeprom, memory IC. It is also accept and process interrupts by AVR in program C#.
so here. in v-usb I have implemented this functionality. Code Examples:

// Report Descriptor
PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = 
{    
	0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x08,                    //   REPORT_COUNT (128)
    //+16
	
	0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
	//+5
	
	//0x85, 0x77,                    // Report ID
	0x09, 0x02,                    // USAGE 
	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x07,                    //   REPORT_COUNT                                                       
    0x81, 0x02,                    // Input (Data,Var,Abs)
	//+13
	
	//0x85, 0x78,                    // Report ID
	0x09, 0x03,                    // USAGE 
	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x08,                    //   REPORT_COUNT
	0x91, 0x02,                    // Output (Data.Var,Abs)
	//+13
		
	
    0xc0                           // END_COLLECTION
	//+1
};

Then processing Reports, first Output\Feature :

uchar   usbFunctionWrite(uchar *data, uchar len)
{
 
 if(ReportID == 0x00)
   {
	LastRequest = data[0];
    AddrRequest[0] = data[1];
	
	// Если пришел запрос с множественным чтением регистров, то копируем все адреса
	if(LastRequest == 0x18) 
	{
	 AddrRequest[1] = data[2];
	 AddrRequest[2] = data[3];
	 AddrRequest[3] = data[4];	 
	}
	
	// Формируем адрес EEPROM при соответствующих запросах
	if(LastRequest == 0x11 || LastRequest == 0x13 || LastRequest == 0x21 || LastRequest == 0x23)
	{
     AddrRequestEEPROM = data[1]<<8 | data[2];
	}
	
	switch(LastRequest)
          { 
	       case 0x20:                                      /* 0x20 -> запись байта в регистр по адресу */
		             _SFR_IO8(AddrRequest[0]) = data[2];
		             break;
					 
	       case 0x21:                                      /* 0x21 -> запись байта в eeprom по адресу */
                     eeprom_write_byte(AddrRequestEEPROM, data[3]);
		             break;
					 
	       case 0x22:                                      /* 0x22 -> запись кучи байт в память мк по адресу :) */
                      
		             break;		

	       case 0x23:                                      /* 0x23 -> запись кучи байт в eeprom мк по адресу */
                      
		             break;

	       case 0x28:                                      /* 0x28 -> запись сразу нескольких байт в несколько регистров */
                     // первые 3 байта
					 _SFR_IO8(AddrRequest[0]) = data[2];
					 
					 // 5 байт
					 if(data[3]>0x00) // защита от неинициализированного значения
					 {
					  _SFR_IO8(data[3]) = data[4];
					 }
					 // 7 байт
					 if(data[5]>0x00)
					 {
					  _SFR_IO8(data[5]) = data[6];
					 }
					 // можно расширять далее при необходимости (^__^)
		             break;					 
					 
           default:					 
					 break;
	      }
   }	
	
	if(bytesRemaining == 0)
        return 1;               /* end of transfer */
    if(len > bytesRemaining)
        len = bytesRemaining;
    //eeprom_write_block(data, (uchar *)0 + currentAddress, len);
    currentAddress += len;
    bytesRemaining -= len;
    return bytesRemaining == 0; /* return 1 if this was the last chunk */
}

// and then Inputs :

uchar   usbFunctionRead(uchar *data, uchar len)
{
 //SendString("Read:",5);
 //SendMassive(data);
	
 if(len > bytesRemaining) len = bytesRemaining;
        


 switch(LastRequest)
       {
	    case 0x10:                                      /* 0x10 -> чтение байта из регистра по адресу */
	              data[0] = AddrRequest[0];
	              data[1] = _SFR_IO8(AddrRequest[0]);
	              break;
	
	    case 0x11:                                      /* 0x11 -> чтение байта из eeprom по адресу */
	              data[0] = AddrRequest[0];
	              data[1] = eeprom_read_byte(AddrRequestEEPROM);
	              break;
				  
	    case 0x12:                                      /* 0x12 -> чтение блока байтиков из регистр по адресу (^.^) */

	              break;

	    case 0x13:                                      /* 0x13 -> чтение блока байтов из eeprom по адресу */
                  //eeprom_read_block(data, (uchar *)0 + currentAddress, len);
	              break;

	    case 0x18:                                      /* 0x18 -> чтение сразу нескольких байт из разных регистров по адресу */
                  // первые 2 байта
				  data[0] = AddrRequest[0];
	              data[1] = _SFR_IO8(AddrRequest[0]);
					 
			      // 4 байт
				  if(AddrRequest[1]>0x00) // защита от неинициализированного значения
				   {
					  data[2] = AddrRequest[1];
					  data[3] = _SFR_IO8(AddrRequest[1]);
				   }

				  // 6 байт
				  if(AddrRequest[2]>0x00) // защита от неинициализированного значения
				   {
					  data[4] = AddrRequest[2];
					  data[5] = _SFR_IO8(AddrRequest[2]);
				   }
				   
				  // 8 байт
				  if(AddrRequest[3]>0x00) // защита от неинициализированного значения
				   {
					  data[6] = AddrRequest[3];
					  data[7] = _SFR_IO8(AddrRequest[3]);
				   }
				   
				  // можно расширять далее при необходимости (^__^)
	              break;				  
	    default:
	              //for(i=0;i<8;i++) data[i] = 0xaf;
	              break;
	   }
	
	
	//SendString("Read2:",6);
	//SendMassive(data);
	
	currentAddress += len;
    bytesRemaining -= len;
    return len;
}

// In program on Host it's look like this:

// Writing value :

public bool Poke(byte addr, byte data)
        {
            bool success = false;
            byte[] Report = new byte[specified_device.OutputReportLength];

            // Report ID
            Report[0] = 0x00;

            // Команда для записи в регистр
            Report[1] = 0x20;

            // Адрес регистра
            Report[2] = addr;

            // Данные для записи
            Report[3] = data;

            try
            {
                SpecifiedDevice.SendData(Report);
                success = true;
            }
            catch (Exception ex)
            {
                ErrorMask = (UInt16)(ErrorMask | 0x08);  
                MessageBox.Show(ex.ToString());
            }

            // Полноценно проверить отсюда правильность записи не представляеться возможным
            // Можно разве только вызвать после записи  Peek() и сравнить результат, но это занимает время. 
            return success;
        }

// and Reading :

 public Int16 Peek(byte addr)
        {
            Int16 data = -1;
            byte[] Report = new byte[specified_device.FeatureReportLength];

            // Report ID
            Report[0] = 0x00;

            // Команда для чтения из регистра
            Report[1] = 0x10;

            // Адрес регистра
            Report[2] = addr;

            try
            {
                byte[] dataF = new byte[specified_device.FeatureReportLength];
                dataF = specified_device.SendFeature(Report);
                if (dataF[1] == Report[2]) data = dataF[2];
            }
            catch (Exception ex)
            {
                ErrorMask = (UInt16)(ErrorMask | 0x10); 
                MessageBox.Show(ex.ToString());
            }

            return data;
        }

and now i start work with lufa and I do not understand where to start ....

All reports pass void HID_Task(void), yes ?
and in this func. we look, what type report are come and what need to do then ...
i add after

// Read Generic Report Data 
Endpoint_Read_Stream_LE(&GenericData, sizeof(GenericData), NULL);


LastRequest = GenericData[0];
AddrRequest[0] = GenericData[1];

and

void ProcessGenericHIDReport(uint8_t* DataArray)
{

switch(LastRequest)
      { 
	case 0x20:                                      /* 0x20 -> запись байта в регистр по адресу */
		             _SFR_IO8(AddrRequest[0]) = DataArray[2];
		             break;
					 
	       case 0x21:                                      /* 0x21 -> запись байта в eeprom по адресу */
                     eeprom_write_byte(AddrRequestEEPROM, DataArray[3]);
		             break;
					 
		   default:					 
					 break;
	      }
}

it's work. i can write value in registers! but read :

void CreateGenericHIDReport(uint8_t* DataArray)
{
    
	
	switch(LastRequest)
       {
	    case 0x10:                                      /* 0x10 -> чтение байта из регистра по адресу */
	              DataArray[0] = AddrRequest[0];
	              DataArray[1] = _SFR_IO8(AddrRequest[0]);
	              break;
	
	    case 0x11:                                      /* 0x11 -> чтение байта из eeprom по адресу */
	              DataArray[0] = AddrRequest[0];
	              DataArray[1] = eeprom_read_byte(AddrRequestEEPROM);
	              break;			  
	    default:
	              break;
	   }
}

don't work..... i'm don't why, pls help!

p\s sorry for my english.. :)
and, if i done, it will be open source, article on microsin.ru (not adv!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i use LowLevel demo.
Report after Feature request don't come.. where is my mistake ?

and.. How in Lufa generate Input Report (like func. usbSetInterrupt(buf,7); in V-Usb, where buf - it's array of report and 7 - length of array.. ) ?

sorry for nub.questions and bad english.

oh, and i use Report Descriptor like that one, what written in 1st post (full copy from v-usb).
And code from v-usb - not done, it's just first alfa :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd strongly encourage you to try the "ClassDriver" version of the demo, located in Demos/Device/ClassDriver/GenericHID. This gives a much more high level approach to the HID class, and makes the places you need to edit to get your required functionality much clearer.

This is the upcoming releases' new GenericHID ClassDriver demo, which controls some LEDs:

https://github.com/abcminiuser/l...

Which should be fairly easy to follow.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm...
bool CALLBACK_HID_Device_CreateHIDReport
will be call after Feature Report ?
and after this CALLBACK array Data send to PC ? just i don't see any use next this array..

And how to generate Input Report without before Output\Feature income Report to device, like usbSetInterrupt(buf,7); in V-Usb ?
(in future maybe added interrupts and need generate reports about it.. )

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry, arg void* ReportData - at first ignored this, pointer here - it's like ref argument in C#, yes ?
and Thanks to You for Example with LEDs, i think i'm understand how it works... ;) tonight I'll try to realize the idea )

Last Edited: Wed. Sep 21, 2011 - 12:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

and have + one question:
every 1 - 1,5sec leds on bord are blinking, most time they in state 1-0-1-0 and every 1-1,5sec they going to 1-0-0-1
Leds set like this:

			#define LEDS_LED1        (1 << 0)
			#define LEDS_LED2        (1 << 1)
			#define LEDS_LED3        (1 << 2)
			#define LEDS_LED4        (1 << 3)

is this normal ? and what this means ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Hmm...
bool CALLBACK_HID_Device_CreateHIDReport
will be call after Feature Report ?
and after this CALLBACK array Data send to PC ? just i don't see any use next this array..

There are two HID driver callback functions: CALLBACK_HID_Device_ProcessHIDReport() and CALLBACK_HID_Device_CreateHIDReport(). The former is where you can process reports (either OUT or FEATURE) sent from the host, while the latter is where you can generate reports (either IN or FEATURE) to be sent back to the host.

The various parameters are documented inside the demo above the function definition, but essentially you get a pointer to the start of an array, where you can store or process a HID report. The array is allocated by LUFA, but will be the size indicated in the .PrevReportINBufferSize element of the HID class driver instance you created. The type of the report to be created or processed is passed in as the ReportType parameter -- you don't get a choice what type the host is requesting, so you need to generate or process the report appropriately, or ignore the request if needed.

The two callbacks will be called automatically by the HID class driver, either in response to GET REPORT/SET REPORT requests sent via the host to the control endpoint, or when the HID driver is ready to create a report on the IN endpoint. Unlike V-USB, which relies on you calling a function to queue a report, LUFA instead just calls back into your application when the host wants more data and allows you to either generate a report, or ignore the request.

Quote:

sorry, arg void* ReportData - at first ignored this, pointer here - it's like ref argument in C#, yes ?

It's a pointer to the report array, made by the LUFA HID driver. You need to either read from it or write to it, depending on which HID callback is being run. Pointers and references are two different things (and C doesn't have the latter at all) however for your purposes they can be treated as roughly equivalent.

Quote:

Leds set like this:
Code:
#define LEDS_LED1 (1 << 0)
#define LEDS_LED2 (1 << 1)
#define LEDS_LED3 (1 << 2)
#define LEDS_LED4 (1 << 3)

is this normal ? and what this means ?

Those defines are used to make a mask, which is then passed to the LUFA LED driver functions (such as "LEDs_SetAllLEDs(LEDS_LED1 | LEDS_LED2);". Don't look at the implementation of the driver, just treat it as a black box as it differs for each board - and some cases that implementation is hideous as Cliff helpfully pointed out a little while back.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much for the detailed description!
I will deal further ;))
And about the LEDs, I mean,LED1 and LED3 glow most of the time, and every 1,5 sec Led's state change to LED1 and LED4 (for short time). This is normal OR it's point to a problem in my code?

And I ask because it seems to me, that it's in these short periods to send Out\Fea.Report - then of course the device does not process it, what program on the host reports to me.

Thanks to you I have learned to read \ write to the registers and EEPROM at90usb162 (by the way, is on the 16MHz Crystal), now I think more how to write the blocks in the EEPROM, not by one byte:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i found my mistake :

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
 
 uint8_t* Data = (uint8_t*)ReportData;
 char i = 0;
	
 switch(LastRequest)
       {
	    // чтение байта из регистра по адресу
		case 0x10:                                      
	              Data[0] = AddrRequest[0];
	              Data[1] = _SFR_IO8(AddrRequest[0]);
	              break;
	
	    // чтение байта из eeprom по адресу
		case 0x11:                                      
				  Data[0] = AddrRequest[0];
	              Data[1] = eeprom_read_byte(AddrRequestEEPROM);
	              break;
				  
	    default:
	              break;
	   }	

// When i comment this - write work perfectly,but input reports don't work...
	//*ReportSize = GENERIC_REPORT_SIZE;
 	
 return false;
}

my descriptor is :

const USB_Descriptor_HIDReport_Datatype_t PROGMEM GenericReport[] =
{
	0x06, 0x00, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x08,                    //   REPORT_COUNT (128)
    //+16
	
	0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
	//+5
	
	//0x85, 0x77,                    // Report ID
	0x09, 0x02,                    // USAGE 
	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x07,                    //   REPORT_COUNT                                                       
    0x81, 0x02,                    // Input (Data,Var,Abs)
	//+13
	
	//0x85, 0x78,                    // Report ID
	0x09, 0x03,                    // USAGE 
	0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x08,                    //   REPORT_COUNT
	0x91, 0x02,                    // Output (Data.Var,Abs)
	//+13
		
	
    0xc0                           // END_COLLECTION	
};

and now i understand how it work ) i'm like nub,know X)

it work all, when i write this:
*ReportSize = 0x07; // Input Report Length = 0x95, 0x07, REPORT_COUNT
and if Report ID = 0x00, ReportSize = 0x07
and Led's don't change state after this fix code.
it's Right way?