USBのHIDクラスデバイス開発 1. デバイスプログラミング編

## 動機

自作キーボードを作っているが、キー入力だけでは物足りなくなってきた。

[Stream Deck](https://www.elgato.com/us/en/p/stream-deck-mk2-black)や、[Orbital2](https://brainmagicproduct.com)のように、windows側のアプリケーションでデバイスの操作を受取り、より柔軟なマクロを実行できる左手デバイスを開発したい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/c41dd6b1-ec36-465b-f88c-f5d7e539d3d8.png)
Stream Deck

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/4784ef7c-51d8-e552-3c57-055722c9fdf1.png)
Stream Deckのマクロ設定画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/8c806e43-8a0d-2205-d43d-731df469f9ea.png)
Orbital2

その第一歩として、WindowsとUSBデバイス間で8bitのデータをリアルタイムに送受信することを目指す。USBシリアル通信は余りかっこよくないので使用しない。

## 使用するデバイス

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/bfbf8cc1-e943-9bfe-94b7-2d0360112960.png)

- デバイス: [Pro Micro Type-C版](https://shop.yushakobo.jp/products/3905?_pos=2&_sid=01b99a7ff&_ss=r)
- マイコン: [Atmega 32U4](https://www.microchip.com/en-us/product/atmega32u4)

自作キーボードによく使用されているPro Microを使用することにした。Atmega 32U4はUSB専用のハードウェア回路が入っている。また、[qmk firmware](https://github.com/qmk/qmk_firmware)を用いてNative USBデバイスとして認識されるキーボードを作成し動作した実績があるからである。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/14e8829b-875d-e476-d376-26ad1d153a83.png)

## USBについての調査

[USB2.0の仕様](https://www.usb.org/document-library/usb-20-specification)

- USBの仕様はすべて公開されており、USB2.0の仕様は上記のページで閲覧が可能
- USB2.0までに、通信速度が3種類ある
    - ロースピード（1.5 Mbps）
    - フルスピード（12 Mbps）
    - ハイスピード（480 Mbps）
- HIDデバイスは、通信頻度が低く、1度の通信のデータ量も小さいため、ロースピードでさえ十分な速度である

## UBSデバイスクラスについて

- USBは、実際のパケットによる通信プロトコルの上位にUSBクラスというものがあり、それぞれのクラスに固有のプロトコルが定義されているらしい
- キーボードや、マウスは HID (Human Interface Device)のクラスが使用されている。そのクラスではHostからのポーリングを用いた、(ほぼ)リアルタイムな割り込みをHostに通知できるようだ
- また、HIDクラスは、キーボードやマウス以外にも独自のデータ構造を定義して、通知できる事がわかった
- 今回はこれを使用する

## HIDクラスについて

[HIDクラスのスペック](https://www.usb.org/document-library/device-class-definition-hid-111)

- HIDは、Report Descriptorを使用して、デバイスの性質や、通信に使用するデータ構造を定義する
- そして、Reportを使用して、データを送受信する

## 開発環境・ライブラリ

Atmega 32U4でUSBを使用するには、いくつかの方法があるようだ

https://qiita.com/Shigosen/items/2a9bd388b2381a595c18

1. USBドライバハードウェアを、レジスタを直接書き換えることで使用する
2. [Lightweight USB Framework for AVRs](https://www.fourwalledcubicle.com/LUFA.php)ライブラリを使用して、レジスタ操作を抽象化する
3. [V-USB](https://www.obdev.at/products/vusb/index.html)を使用して、ハードウェアを使用しないで、ソフトウェアでエミュレートしてUSB通信を行う

1は、USBのイベント処理をすべて自分で書くのは難しいし、時間がかかる
3は、せっかく付属しているUSBハードウェアを使用しないのはもったいない

よって、今回は2のLUFAを使用することにした

今回は、以下の開発環境を使用する

- [Microchip Studio](https://www.microchip.com/en-us/tools-resources/develop/microchip-studio)
    - Atmel Studioがいつの間にかMicrochip Studioに変わっていた
- [Lightweight USB Framework for AVRs](https://www.fourwalledcubicle.com/LUFA.php)

## 実装するデバイスの仕様

- 今回は8bitのデータを、送受信できるデバイスを作成する
- ボタンが押されたらデバイスからホストに0x01を送信する
- ホストからデバイスに0x00を受信したらLEDを消灯、0x01を受信したらLEDを点灯する

## ハードウェア

以下のようにUSBステータス用のLEDと、Host(PC)からコントロールするLED、ホスト(PC)に信号を送るトリガーとなるタクトスイッチを配置する

![HID_test_ブレッドボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/e14d9e00-ba00-ffd6-87d9-259acfd6da39.png)

## プロジェクトのセットアップ

https://qiita.com/Shigosen/items/f263bb122f943d05c20b

こちらの記事を参考に、プロジェクトの雛形を作成する
一から自分で定義するのは、細かい設定やLUFAライブラリのプロジェクトへの追加が面倒なのでおすすめしない

1. Generic HID Device Demo (Classic Driver APIs) - AVR8 Architectureを選ぶ
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/c6d51d1a-58cb-a35e-3b90-6a6fc8113cfe.png)
2. Licenseに同意する
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/f27e6a45-8470-6a75-e650-fa0543bd80db.png)
3. プロジェクトのプロパティを開き
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/2aec57cb-c399-64cb-75fe-088ed2f03a06.png)
4. デバイスを32U4に変更
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/334fc341-985e-3a63-cfe1-ace35e9c7f40.png)
5. ASF WizardでBoardをUserに設定しApply
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/b96029e2-3c6d-2278-b030-83a82689a4b8.png)

## 組み込みソフトウェアコード

### ボードのユーティリティコード

まずは、今回使うボードのためのLED、ボタンの設定を書く。今回は、既存のボードのコードを軽く書き換えた。

Pro Microの回路図と、ブレッドボードの配線を見比べながら、使用するポートを書き換える。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/39382c7d-de68-6448-8bd6-b8f18c6bf882.png)

```C:src/Bard/Bard.h
#ifndef __BOARD_MICRO_H__
#define __BOARD_MICRO_H__

	/* Includes: */
		#include "../LUFA/LUFA/Common/Common.h"
		#include "./LEDs.h"

	/* Enable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			extern "C" {
		#endif

	/* Public Interface - May be used in end-application: */
		/* Macros: */
			/** Indicates the board has hardware LEDs mounted. */
			#define BOARD_HAS_LEDS

	/* Disable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			}
		#endif

#endif
```

```C:src/Board/LEDs.h
#ifndef __LEDS_MICRO_H__
#define __LEDS_MICRO_H__

	/* Includes: */
		#include "../LUFA/LUFA/Common/Common.h"

	/* Enable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			extern "C" {
		#endif

	/* Private Interface - For use in library only: */
	#if !defined(__DOXYGEN__)
		/* Macros: */
			#define LEDS_PORTB_LEDS       (0)
			#define LEDS_PORTD_LEDS       (LEDS_LED1 | LEDS_LED2 | LEDS_LED3 | LEDS_LED5)
			#define LEDS_PORTC_LEDS       (LEDS_LED4)
	#endif

	/* Public Interface - May be used in end-application: */
		/* Macros: */
			/** LED mask for the first LED on the board. */
            // PD1
			#define LEDS_LED1        (1 << 1)

			/** LED mask for the second LED on the board. */
            // PD0
			#define LEDS_LED2        (1 << 0)

			/** LED mask for the third LED on the board. */
            // PD4
			#define LEDS_LED3        (1 << 4)
			
			/** LED mask for the third LED on the board. */
            // PC6
			#define LEDS_LED4        (1 << 6)
			
			/** LED mask for the third LED on the board. */
            // PD7
			#define LEDS_LED5        (1 << 7)

			/** LED mask for all the LEDs on the board. */
			#define LEDS_ALL_LEDS    (LEDS_LED1 | LEDS_LED2 | LEDS_LED3 | LEDS_LED4 | LEDS_LED5)

			/** LED mask for none of the board LEDs. */
			#define LEDS_NO_LEDS     0

		/* Inline Functions: */
		#if !defined(__DOXYGEN__)
			static inline void LEDs_Init(void)
			{
				DDRB  |=  LEDS_PORTB_LEDS;
				PORTB &= ~LEDS_PORTB_LEDS;
				DDRD  |=  LEDS_PORTD_LEDS;
				PORTD &= ~LEDS_PORTD_LEDS;
				DDRC  |=  LEDS_PORTC_LEDS;
				PORTC &= ~LEDS_PORTC_LEDS;
			}

			static inline void LEDs_Disable(void)
			{
				DDRB  &= ~LEDS_PORTB_LEDS;
				PORTB &= ~LEDS_PORTB_LEDS;
				DDRD  &= ~LEDS_PORTD_LEDS;
				PORTD &= ~LEDS_PORTD_LEDS;
				DDRC  &= ~LEDS_PORTC_LEDS;
				PORTC &= ~LEDS_PORTC_LEDS;
			}

			static inline void LEDs_TurnOnLEDs(const uint8_t LEDMask)
			{
				PORTB |=  (LEDMask & LEDS_PORTB_LEDS);
				PORTD |=  (LEDMask & LEDS_PORTD_LEDS);
				PORTC |=  (LEDMask & LEDS_PORTC_LEDS);
			}

			static inline void LEDs_TurnOffLEDs(const uint8_t LEDMask)
			{
				PORTB &= ~(LEDMask & LEDS_PORTB_LEDS);
				PORTD &= ~(LEDMask & LEDS_PORTD_LEDS);
				PORTC &= ~(LEDMask & LEDS_PORTC_LEDS);
			}

			static inline void LEDs_SetAllLEDs(const uint8_t LEDMask)
			{
				PORTB = ((PORTB & ~LEDS_PORTB_LEDS) | (LEDMask & LEDS_PORTB_LEDS));
				PORTD = ((PORTD & ~LEDS_PORTD_LEDS) | (LEDMask & LEDS_PORTD_LEDS));
				PORTC = ((PORTC & ~LEDS_PORTC_LEDS) | (LEDMask & LEDS_PORTC_LEDS));
			}

			static inline void LEDs_ChangeLEDs(const uint8_t LEDMask,
			                                   const uint8_t ActiveMask)
			{
				PORTB = ((PORTB & ~(LEDMask & LEDS_PORTB_LEDS)) | (ActiveMask & LEDS_PORTB_LEDS));
				PORTD = ((PORTD & ~(LEDMask & LEDS_PORTD_LEDS)) | (ActiveMask & LEDS_PORTD_LEDS));
				PORTC = ((PORTC & ~(LEDMask & LEDS_PORTC_LEDS)) | (ActiveMask & LEDS_PORTC_LEDS));
			}

			static inline void LEDs_ToggleLEDs(const uint8_t LEDMask)
			{
				PORTB ^= (LEDMask & LEDS_PORTB_LEDS);
				PORTD ^= (LEDMask & LEDS_PORTD_LEDS);
				PORTC ^= (LEDMask & LEDS_PORTC_LEDS);
			}

			static inline uint8_t LEDs_GetLEDs(void) ATTR_WARN_UNUSED_RESULT;
			static inline uint8_t LEDs_GetLEDs(void)
			{
				return ((PORTB & LEDS_PORTB_LEDS) | (PORTD & LEDS_PORTD_LEDS) | (PORTC & LEDS_PORTC_LEDS));
			}
		#endif

	/* Disable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			}
		#endif

#endif
```

```C:src/Board/Buttons.h
#ifndef __BUTTONS_MICROPENDOUS_H__
#define __BUTTONS_MICROPENDOUS_H__

	/* Includes: */
		#include "../LUFA/LUFA/Common/Common.h"

	/* Enable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			extern "C" {
		#endif

	/* Private Interface - For use in library only: */
		#if !defined(__DOXYGEN__)
            // PB4
			#define _BOARD_BUTTON1_MASK             (1 << 4)
			#define _BOARD_BUTTON_PORTLETTER        B

			#define _BOARD_BUTTON_PORT                  CONCAT_EXPANDED(PORT, _BOARD_BUTTON_PORTLETTER)
			#define _BOARD_BUTTON_PIN                   CONCAT_EXPANDED(PIN,  _BOARD_BUTTON_PORTLETTER)
			#define _BOARD_BUTTON_DDR                   CONCAT_EXPANDED(DDR,  _BOARD_BUTTON_PORTLETTER)
		#endif

	/* Public Interface - May be used in end-application: */
		/* Macros: */
			/** Button mask for the first button on the board. */
			#define BUTTONS_BUTTON1     _BOARD_BUTTON1_MASK

		/* Inline Functions: */
		#if !defined(__DOXYGEN__)
			static inline void Buttons_Init(void)
			{
				_BOARD_BUTTON_DDR  &= ~BUTTONS_BUTTON1;
				_BOARD_BUTTON_PORT |=  BUTTONS_BUTTON1;
			}

			static inline void Buttons_Disable(void)
			{
				_BOARD_BUTTON_DDR  &= ~BUTTONS_BUTTON1;
				_BOARD_BUTTON_PORT &= ~BUTTONS_BUTTON1;
			}

			static inline uint8_t Buttons_GetStatus(void) ATTR_WARN_UNUSED_RESULT;
			static inline uint8_t Buttons_GetStatus(void)
			{
				return ((_BOARD_BUTTON_PIN & BUTTONS_BUTTON1) ^ BUTTONS_BUTTON1);
			}
		#endif

	/* Disable C linkage for C++ Compilers: */
		#if defined(__cplusplus)
			}
		#endif

#endif
```

### 続いてUSBの処理を少し改変する

主にCALLBACK_HID_Device_CreateHIDReportとCALLBACK_HID_Device_ProcessHIDReportを修正した

```c:GenericHDI.h
#ifndef _GENERICHID_H_
#define _GENERICHID_H_

	/* Includes: */
		#include <avr/io.h>
		#include <avr/wdt.h>
		#include <avr/power.h>
		#include <avr/interrupt.h>
		#include <string.h>

		#include "Descriptors.h"
		#include "Config/AppConfig.h"

		#include <LUFA/Drivers/Board/LEDs.h>
		#include <LUFA/Drivers/Board/BUTTONs.h>
		#include <LUFA/Drivers/USB/USB.h>
		#include <LUFA/Platform/Platform.h>

	/* Macros: */
		/** LED mask for the library LED driver, to indicate that the USB interface is not ready. */
		#define LEDMASK_USB_NOTREADY      LEDS_LED1

		/** LED mask for the library LED driver, to indicate that the USB interface is enumerating. */
		#define LEDMASK_USB_ENUMERATING  (LEDS_LED2 | LEDS_LED3)

		/** LED mask for the library LED driver, to indicate that the USB interface is ready. */
		#define LEDMASK_USB_READY        (LEDS_LED2 | LEDS_LED4)

		/** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */
		#define LEDMASK_USB_ERROR        (LEDS_LED1 | LEDS_LED3)

	/* Function Prototypes: */
		void SetupHardware(void);

		void EVENT_USB_Device_Connect(void);
		void EVENT_USB_Device_Disconnect(void);
		void EVENT_USB_Device_ConfigurationChanged(void);
		void EVENT_USB_Device_ControlRequest(void);
		void EVENT_USB_Device_StartOfFrame(void);

		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);
		void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
		                                          const uint8_t ReportID,
		                                          const uint8_t ReportType,
		                                          const void* ReportData,
		                                          const uint16_t ReportSize);

#endif
```

```C:GenericHDI.c
#include "GenericHID.h"

/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevHIDReportBuffer[GENERIC_REPORT_SIZE];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t Generic_HID_Interface =
	{
		.Config =
			{
				.InterfaceNumber              = INTERFACE_ID_GenericHID,
				.ReportINEndpoint             =
					{
						.Address              = GENERIC_IN_EPADDR,
						.Size                 = GENERIC_EPSIZE,
						.Banks                = 1,
					},
				.PrevReportINBuffer           = PrevHIDReportBuffer,
				.PrevReportINBufferSize       = sizeof(PrevHIDReportBuffer),
			},
	};


/** Main program entry point. This routine contains the overall program flow, including initial
 *  setup of all components and the main program loop.
 */
int main(void)
{
	SetupHardware();

	LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
	GlobalInterruptEnable();

	for (;;)
	{
		HID_Device_USBTask(&Generic_HID_Interface);
		USB_USBTask();
	}
}

/** Configures the board hardware and chip peripherals for the demo's functionality. */
void SetupHardware(void)
{
#if (ARCH == ARCH_AVR8)
	/* Disable watchdog if enabled by bootloader/fuses */
	MCUSR &= ~(1 << WDRF);
	wdt_disable();

	/* Disable clock division */
	clock_prescale_set(clock_div_1);
#elif (ARCH == ARCH_XMEGA)
	/* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */
	XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
	XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);

	/* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */
	XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
	XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);

	PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
#endif

	/* Hardware Initialization */
	LEDs_Init();
	Buttons_Init();
	USB_Init();
}

/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
	LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING);
}

/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
	LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);
}

/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
	bool ConfigSuccess = true;

	ConfigSuccess &= HID_Device_ConfigureEndpoints(&Generic_HID_Interface);

	USB_Device_EnableSOFEvents();

	LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR);
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
	HID_Device_ProcessControlRequest(&Generic_HID_Interface);
}

/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
	HID_Device_MillisecondElapsed(&Generic_HID_Interface);
}

/** HID class driver callback function for the creation of HID reports to the host.
 *
 *  \param[in]     HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in,out] ReportID    Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
 *  \param[in]     ReportType  Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
 *  \param[out]    ReportData  Pointer to a buffer where the created report should be stored
 *  \param[out]    ReportSize  Number of bytes written in the report (or zero if no report is to be sent)
 *
 *  \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
 */
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;

	Data[0] = !!Buttons_GetStatus();  // 0 or 1にするために、二重否定

	*ReportSize = GENERIC_REPORT_SIZE;  // SIZEは常に1byte
	return false;
}

/** HID class driver callback function for the processing of HID reports from the host.
 *
 *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in] ReportID    Report ID of the received report from the host
 *  \param[in] ReportType  The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
 *  \param[in] ReportData  Pointer to a buffer where the received report has been stored
 *  \param[in] ReportSize  Size in bytes of the received HID report
 */
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                          const uint8_t ReportID,
                                          const uint8_t ReportType,
                                          const void* ReportData,
                                          const uint16_t ReportSize)
{
	uint8_t* Data       = (uint8_t*)ReportData;

	if (Data[0]) {  // 送られてきたデータが0ならばLEDを消す、それ以外ならLEDを点ける
	  LEDs_TurnOnLEDs(LEDS_LED5);
	} else {
	  LEDs_TurnOffLEDs(LEDS_LED5);
	}
}


```

### 続いてDescriptorの定義を編集する

Vender ID、Device IDを主に編集した

```c:Descriptors.h
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_

	/* Includes: */
		#include <avr/pgmspace.h>

		#include <LUFA/Drivers/USB/USB.h>

		#include "Config/AppConfig.h"

	/* Type Defines: */
		/** Type define for the device configuration descriptor structure. This must be defined in the
		 *  application code, as the configuration descriptor contains several sub-descriptors which
		 *  vary between devices, and which describe the device's usage to the host.
		 */
		typedef struct
		{
			USB_Descriptor_Configuration_Header_t Config;

			// Generic HID Interface
			USB_Descriptor_Interface_t            HID_Interface;
			USB_HID_Descriptor_HID_t              HID_GenericHID;
			USB_Descriptor_Endpoint_t             HID_ReportINEndpoint;
		} USB_Descriptor_Configuration_t;

		/** Enum for the device interface descriptor IDs within the device. Each interface descriptor
		 *  should have a unique ID index associated with it, which can be used to refer to the
		 *  interface from other descriptors.
		 */
		enum InterfaceDescriptors_t
		{
			INTERFACE_ID_GenericHID = 0, /**< GenericHID interface descriptor ID */
		};

		/** Enum for the device string descriptor IDs within the device. Each string descriptor should
		 *  have a unique ID index associated with it, which can be used to refer to the string from
		 *  other descriptors.
		 */
		enum StringDescriptors_t
		{
			STRING_ID_Language     = 0, /**< Supported Languages string descriptor ID (must be zero) */
			STRING_ID_Manufacturer = 1, /**< Manufacturer string ID */
			STRING_ID_Product      = 2, /**< Product string ID */
		};

	/* Macros: */
		/** Endpoint address of the Generic HID reporting IN endpoint. */
		#define GENERIC_IN_EPADDR         (ENDPOINT_DIR_IN | 1)

		/** Size in bytes of the Generic HID reporting endpoint. */
		#define GENERIC_EPSIZE            8

	/* Function Prototypes: */
		uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
		                                    const uint16_t wIndex,
		                                    const void** const DescriptorAddress)
		                                    ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);

#endif
```

```c:Descriptors.c
#include "Descriptors.h"

/** HID class report descriptor. This is a special descriptor constructed with values from the
 *  USBIF HID class specification to describe the reports and capabilities of the HID device. This
 *  descriptor is parsed by the host and its contents used to determine what data (and in what encoding)
 *  the device will send, and what it may be sent back from the host. Refer to the HID specification for
 *  more details on HID report descriptors.
 */
const USB_Descriptor_HIDReport_Datatype_t PROGMEM GenericReport[] =
{
	/* Use the HID class driver's standard Vendor HID report.
	 *  Vendor Usage Page: 0
	 *  Vendor Collection Usage: 1
	 *  Vendor Report IN Usage: 2
	 *  Vendor Report OUT Usage: 3
	 *  Vendor Report Size: GENERIC_REPORT_SIZE
	 */
	HID_DESCRIPTOR_VENDOR(0x00, 0x01, 0x02, 0x03, GENERIC_REPORT_SIZE)
};

/** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall
 *  device characteristics, including the supported USB version, control endpoint size and the
 *  number of device configurations. The descriptor is read out by the USB host when the enumeration
 *  process begins.
 */
const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
{
	.Header                 = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},

	.USBSpecification       = VERSION_BCD(1,1,0),
	.Class                  = USB_CSCP_NoDeviceClass,
	.SubClass               = USB_CSCP_NoDeviceSubclass,
	.Protocol               = USB_CSCP_NoDeviceProtocol,

	.Endpoint0Size          = FIXED_CONTROL_ENDPOINT_SIZE,

	.VendorID               = 0xF055,
	.ProductID              = 0x1234,
	.ReleaseNumber          = VERSION_BCD(0,0,1),

	.ManufacturerStrIndex   = STRING_ID_Manufacturer,
	.ProductStrIndex        = STRING_ID_Product,
	.SerialNumStrIndex      = NO_DESCRIPTOR,

	.NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
};

/** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage
 *  of the device in one of its supported configurations, including information about any device interfaces
 *  and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting
 *  a configuration so that the host may correctly communicate with the USB device.
 */
const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor =
{
	.Config =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration},

			.TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t),
			.TotalInterfaces        = 1,

			.ConfigurationNumber    = 1,
			.ConfigurationStrIndex  = NO_DESCRIPTOR,

			.ConfigAttributes       = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_SELFPOWERED),

			.MaxPowerConsumption    = USB_CONFIG_POWER_MA(100)
		},

	.HID_Interface =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},

			.InterfaceNumber        = INTERFACE_ID_GenericHID,
			.AlternateSetting       = 0x00,

			.TotalEndpoints         = 1,

			.Class                  = HID_CSCP_HIDClass,
			.SubClass               = HID_CSCP_NonBootSubclass,
			.Protocol               = HID_CSCP_NonBootProtocol,

			.InterfaceStrIndex      = NO_DESCRIPTOR
		},

	.HID_GenericHID =
		{
			.Header                 = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID},

			.HIDSpec                = VERSION_BCD(1,1,1),
			.CountryCode            = 0x00,
			.TotalReportDescriptors = 1,
			.HIDReportType          = HID_DTYPE_Report,
			.HIDReportLength        = sizeof(GenericReport)
		},

	.HID_ReportINEndpoint =
		{
			.Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},

			.EndpointAddress        = GENERIC_IN_EPADDR,
			.Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
			.EndpointSize           = GENERIC_EPSIZE,
			.PollingIntervalMS      = 0x05
		},
};

/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests
 *  the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate
 *  via the language ID table available at USB.org what languages the device supports for its string descriptors.
 */
const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG);

/** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable
 *  form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"Dean Camera");

/** Product descriptor string. This is a Unicode string containing the product's details in human readable form,
 *  and is read out upon request by the host when the appropriate string ID is requested, listed in the Device
 *  Descriptor.
 */
const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"LUFA Generic HID Demo");

/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
 *  documentation) by the application code so that the address and size of a requested descriptor can be given
 *  to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
 *  is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the
 *  USB host.
 */
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
                                    const uint16_t wIndex,
                                    const void** const DescriptorAddress)
{
	const uint8_t  DescriptorType   = (wValue >> 8);
	const uint8_t  DescriptorNumber = (wValue & 0xFF);

	const void* Address = NULL;
	uint16_t    Size    = NO_DESCRIPTOR;

	switch (DescriptorType)
	{
		case DTYPE_Device:
			Address = &DeviceDescriptor;
			Size    = sizeof(USB_Descriptor_Device_t);
			break;
		case DTYPE_Configuration:
			Address = &ConfigurationDescriptor;
			Size    = sizeof(USB_Descriptor_Configuration_t);
			break;
		case DTYPE_String:
			switch (DescriptorNumber)
			{
				case STRING_ID_Language:
					Address = &LanguageString;
					Size    = pgm_read_byte(&LanguageString.Header.Size);
					break;
				case STRING_ID_Manufacturer:
					Address = &ManufacturerString;
					Size    = pgm_read_byte(&ManufacturerString.Header.Size);
					break;
				case STRING_ID_Product:
					Address = &ProductString;
					Size    = pgm_read_byte(&ProductString.Header.Size);
					break;
			}

			break;
		case HID_DTYPE_HID:
			Address = &ConfigurationDescriptor.HID_GenericHID;
			Size    = sizeof(USB_HID_Descriptor_HID_t);
			break;
		case HID_DTYPE_Report:
			Address = &GenericReport;
			Size    = sizeof(GenericReport);
			break;
	}

	*DescriptorAddress = Address;
	return Size;
}
```

### 少し解説

以下の部分が、HIDデバイスのデスクリプタであり、デバイスが扱うHID Reportの意味や構造を定義している部分。

```c
const USB_Descriptor_HIDReport_Datatype_t PROGMEM GenericReport[] =
{
	/* Use the HID class driver's standard Vendor HID report.
	 *  Vendor Usage Page: 0
	 *  Vendor Collection Usage: 1
	 *  Vendor Report IN Usage: 2
	 *  Vendor Report OUT Usage: 3
	 *  Vendor Report Size: GENERIC_REPORT_SIZE
	 */
	HID_DESCRIPTOR_VENDOR(0x00, 0x01, 0x02, 0x03, GENERIC_REPORT_SIZE)
};
```

HID_DESCRIPTOR_VENDORのマクロは以下のように定義されている

```c
#define HID_DESCRIPTOR_VENDOR(VendorPageNum, CollectionUsage, DataINUsage, DataOUTUsage, NumBytes) \
			HID_RI_USAGE_PAGE(16, (0xFF00 | VendorPageNum)), \
			HID_RI_USAGE(8, CollectionUsage),           \
			HID_RI_COLLECTION(8, 0x01),                 \
				HID_RI_USAGE(8, DataINUsage),           \
				HID_RI_LOGICAL_MINIMUM(8, 0x00),        \
				HID_RI_LOGICAL_MAXIMUM(8, 0xFF),        \
				HID_RI_REPORT_SIZE(8, 0x08),            \
				HID_RI_REPORT_COUNT(8, NumBytes),       \
				HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), \
				HID_RI_USAGE(8, DataOUTUsage),          \
				HID_RI_LOGICAL_MINIMUM(8, 0x00),        \
				HID_RI_LOGICAL_MAXIMUM(8, 0xFF),        \
				HID_RI_REPORT_SIZE(8, 0x08),            \
				HID_RI_REPORT_COUNT(8, NumBytes),       \
				HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), \
			HID_RI_END_COLLECTION(0)
```

- HID_RI_USAGE_PAGE(16, (0xFF00 | VendorPageNum)): Usage PageがVendorPageNum。ベンダー定義の場合、0xFFxxの形であれば何でもいい
- HID_RI_USAGE(8, CollectionUsage): UsageがCollectionUsage。ベンダー定義の場合、なんでもいい
- HID_RI_COLLECTION(8, 0x01): いくつかのプロパティをまとめるためのもの。まとめる基準を示すいくつかのプロパティがある。0x01はアプリケーションによる区分を意味する
- HID_RI_USAGE(8, DataINUsage): UsageがDataINUsage。ベンダー定義の場合、なんでもいい
- HID_RI_LOGICAL_MINIMUM(8, 0x00): 値の下限
- HID_RI_LOGICAL_MAXIMUM(8, 0xFF): 値の上限
- HID_RI_REPORT_SIZE(8, 0x08): 8bitのデータが
- HID_RI_REPORT_COUNT(8, NumBytes): num bytes個分
- HID_RI_INPUT(8, ): 入力に使用する構造であることを表す
- HID_IOF_DATA: HID_IOF_DATAは、書込み可能であることを意味する。書き込み不能はHID_IOF_CONSTANT
- HID_IOF_VARIABLE: HID_IOF_VARIABLEは、単一のデータであることを表す。反対はHID_IOF_ARRAYで、配列を意味する
- HID_IOF_ABSOLUTE: HID_IOF_ABSOLUTEは、絶対値であることを表す。反対は、HID_IOF_RELATIVEで、前回との差分であることを表す
- ...
- HID_RI_END_COLLECTION(0): コレクションを閉じる

```
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x09, 0x01,       // Usage (Collection Usage 0x01)
0xA1, 0x01,       // Collection (Application)
  0x09, 0x02,     //   Usage (Data IN Usage 0x02)
  0x15, 0x00,     //   Logical Minimum (0)
  0x25, 0xFF,     //   Logical Maximum (255)
  0x75, 0x08,     //   Report Size (8)
  0x95, 0x01,     //   Report Count (1)
  0x81, 0x06,     //   Input (Data, Variable, Absolute)
  0x09, 0x03,     //   Usage (Data OUT Usage 0x03)
  0x15, 0x00,     //   Logical Minimum (0)
  0x25, 0xFF,     //   Logical Maximum (255)
  0x75, 0x08,     //   Report Size (8)
  0x95, 0x01,     //   Report Count (1)
  0x91, 0x0E,     //   Output (Data, Variable, Absolute, Non-Volatile)
0xC0              // End Collection
```

多分こんな感じに展開される。

詳しくはHIDのSpecを読もう。

### 最後に、1 byteのみを送受信するようにした

```C:src/config/AppConfig.h
#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_

	#define GENERIC_REPORT_SIZE       1

#endif
```

## ビルド

F7でビルドできる
以下のように、0 failedであればビルドは成功しているだろう。

```
Build succeeded.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
```

## 書き込み

書き込みは、DFUモードを用いて行うことにした。遊舎工房のPro Microは[resetを素早く2回行うとDFUモードに入る仕様](https://shop.yushakobo.jp/products/3905#:~:text=%E2%80%BB2023/11/10%E8%B2%A9%E5%A3%B2%E5%88%86%E3%81%8B%E3%82%89%E4%BB%95%E6%A7%98%E3%81%8C%E5%A4%89%E6%9B%B4%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82%E3%83%95%E3%82%A1%E3%83%BC%E3%83%A0%E3%82%A6%E3%82%A7%E3%82%A2%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF%E6%99%82%E3%81%AF%E3%83%AA%E3%82%BB%E3%83%83%E3%83%88%E3%82%B9%E3%82%A4%E3%83%83%E3%83%81%E3%82%922%E5%9B%9E%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E3%81%97%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82)のようだ。素早く、2回RSTと隣のGNDを接触させるとCOMポートデバイスとして認識される。10秒ほどするとDFUモードを抜けてしまうので、素早く書き込みを行わなければならない。

AVRにDFUモードで書き込むには、arduinoなどでお馴染みのavrdudeを使用する。

[scoop](https://scoop.sh)を使うなり、[インストーラを使う](https://ja.osdn.net/projects/avrdude/)なりしてavrdudeをインストールする

scoopを使う場合は以下でインストールできる

```powershell
scoop install avrdude
```

Microchip Studioでは、外部の書き込みコマンドを登録することが出来る

https://www.jh4vaj.com/archives/12626#atmel-studio%E3%81%B8%E3%81%AEavrdude%E3%81%AE%E7%B5%84%E8%BE%BC%E3%81%BF

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/b19ac620-9642-6e7c-211e-69548f86bae1.png)

```
Title: AVRDUDE # 自分がわかれば何でもいい
Command: C:\Users\{UserName}\scoop\shims\avrdude.exe
Arguments: -p atmega32u4 -c avr109 -U flash:w:"$(TargetDir)$(TargetName).hex":i -P COM9 # 自分のCOMポートの値に設定する
Initial directory: C:\Users\{UserName}\scoop\shims\ # なんでもいい
```

以下の値を設定する。インストーラでインストールした場合は、適宜コマンドのパスを書き換える。(パスが通っていれば `where.exe avrdude` などで探せるだろう。

COMポートの番号は、デバイスマネージャで確認すると良いだろう。

準備ができたら、RSTを2度GNDと接触させて、登録したAVRDUDEを実行する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/bcdd3881-86a9-20bb-26fc-a5dec18f531f.png)

Erase,Write,Verifyが行われ以下のように出たら書き込みは成功だ。

```
avrdude: AVR device initialized and ready to accept instructions
avrdude: device signature = 0x1e9587 (probably m32u4)
avrdude: Note: flash memory has been specified, an erase cycle will be performed.
         To disable this feature, specify the -D option.
avrdude: erasing chip

avrdude: processing -U flash:w:C:\Users\{UserName}\Documents\Atmel Studio\7.0\MyFirstHIDDevice\MyFirstHIDDevice\Debug\MyFirstHIDDevice.hex:i
avrdude: reading input file C:\Users\{UserName}\Documents\Atmel Studio\7.0\MyFirstHIDDevice\MyFirstHIDDevice\Debug\MyFirstHIDDevice.hex for flash
         with 4898 bytes in 1 section within [0, 0x1321]
         using 39 pages and 94 pad bytes
avrdude: writing 4898 bytes flash ...
Writing | ################################################## | 100% 0.36s
avrdude: 4898 bytes of flash written
avrdude: verifying flash memory against C:\Users\{UserName}\Documents\Atmel Studio\7.0\MyFirstHIDDevice\MyFirstHIDDevice\Debug\MyFirstHIDDevice.hex
Reading | ################################################## | 100% 0.03s
avrdude: 4898 bytes of flash verified

avrdude done.  Thank you.
```

## テスト、デバッグ

### デバイスの認識の確認

[USB Device Tree Viewer](https://www.uwe-sieber.de/usbtreeview_e.html)を使って、接続されているUSBの一覧を取得し、作成したデバイスを探す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/cc446769-0baf-a45d-bdc5-69b4ff152821.png)


VID, PIDが一致するデバイスを見つけることができました

### Host側受信テスト

WiresharkとUSBPcapをインストールします。

つながっているUSBツリーに該当するPcapのチャンネルを気合で探します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/8495d6ac-06f1-5815-51b7-7036aa05991f.png)

ボタンを押すと1を、放すと0を返すデバイスが発見できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/d56f70f6-6f11-727a-2943-dbbdd69d7fec.png)

### Host側送信テスト

[libusb-win32](https://sourceforge.net/projects/libusb/)をインストール
[Zadig](https://zadig.akeo.ie)を使用して、ドライバを入れ替える

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/1d600b4a-623d-6790-79bf-3461240da950.png)

適当にスクリプトを書き、USBを介してLチカ出来ることを確かめます。

```python:send_test.py
import usb.core
import usb.util
import usb.backend.libusb1
import time

def list_usb_devices():
    backend = usb.backend.libusb1.get_backend()
    devices = usb.core.find(find_all=True, backend=backend)

    if devices is None:
        print("USBデバイスが見つかりません")
        return

    for device in devices:
        print(f"デバイス: {device}")
        print(f"  ベンダーID: 0x{device.idVendor:04x}")
        print(f"  プロダクトID: 0x{device.idProduct:04x}")

        try:
            manufacturer = usb.util.get_string(device, device.iManufacturer)
            print(f"  メーカー: {manufacturer}")
        except usb.core.USBError:
            print("  メーカー: 取得できません")

        try:
            product = usb.util.get_string(device, device.iProduct)
            print(f"  プロダクト: {product}")
        except usb.core.USBError:
            print("  プロダクト: 取得できません")

        try:
            serial_number = usb.util.get_string(device, device.iSerialNumber)
            print(f"  シリアルナンバー: {serial_number}")
        except usb.core.USBError:
            print("  シリアルナンバー: 取得できません")

        print("")

def send_control_data(device, data):
    bmRequestType = 0x21  # ホストからデバイスへのクラスリクエスト
    bRequest = 0x09       # HID Set_Report リクエスト
    wValue = 0x0200       # Report Type and Report ID (0x02 for output report, 0x00 for Report ID)
    wIndex = 0            # インターフェース番号
    device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, bytearray(data))
    print(f"送信: {data}")

def main():
    backend = usb.backend.libusb1.get_backend()

    VID = 0xF055  # デバイスのベンダーID
    PID = 0x1234  # デバイスのプロダクトID

    dev = usb.core.find(idVendor=VID, idProduct=PID, backend=backend)

    if dev is None:
        raise ValueError("デバイスが見つかりません")

    dev.set_configuration()

    try:
        while True:
            send_control_data(dev, [1])
            time.sleep(1)

            send_control_data(dev, [0])
            time.sleep(1)

    except KeyboardInterrupt:
        print("終了")
        usb.util.dispose_resources(dev)

if __name__ == "__main__":
    list_usb_devices()
    main()
```

![737650613.047268.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74646/b255b4a5-cef5-e7aa-d951-5b2f58bb5a13.gif)

## Vender IDの話

https://qiita.com/gpsnmeajp/items/8eb8ecf0541032f6de0e

USBのVender IDの取得は、かなり大変らしい。今回は、個人的なテストのため、0xF055を使用させていただきました。

## 次回予告

Windowsのドライバを書いて、アプリケーションでInterruptを受け取れるようにします。