banner
yono

yono

哈喽~欢迎光临
follow
github

USBのTMC列挙——計測器業界が避けられない一塊

基本紹介#

標準機器設備インターフェースは、上位機と機器間の通信に使用されるインターフェースであり、オシロスコープ、電源、信号発生器などがあります。一般的な標準機器インターフェースと対応する通信プロトコルは以下の通りです。

通信インターフェース通信プロトコル
LANVXI-11
USBUSB-TMC
GPIBIEEE488

LAN の通信プロトコルは TCP/IP のTCP Client/ServerRAW Socketと本質的に違いはなく、そのうちの一つをサポートしていれば正常に認識されます。

GPIB の通信プロトコルは比較的複雑で、物理層は並列ポート伝送であり、専用の送受信チップを使用して通信する必要があります。専用送受信器の制御方式は SRAM に似ており、特に難しい問題はありません。

USB の通信方式は一度確認する必要があります。

当社はテスト機器および計測器の会社であり、この分野での技術的な蓄積がまったくないため、USB-TMC の列挙について詳しく見てみましょう。

全体の考え方#

まず非常に重要なのは、標準文書を参照することです。以下の 2 つの自分で翻訳したバイリンガル対照版があり、両面表示で照らし合わせて確認する必要があります。

USBTMC_1_00_EnCh.pdf | yono のファイル
USBTMC_usb488_subclass_1_00_EnCh.pdf | yono のファイル

次に非常に重要でないのは、競合他社の正式な機器を使ってデータパケットをキャプチャして、どのような状況かを確認することです。

ps:実際には、私は 2 つ目の文書を一目も見ておらず、完全にデータをキャプチャし、最初の文書と照らし合わせてドライバ開発を完了しました。

注目すべきは、USB-TMC クラスの列挙は実際には USB488 という 1 つのサブクラスしかなく、まだ多くの予約データ位置と通信プロセスがあるため、TMC という列挙はまだ何年も使用できると考えています。

列挙プロセス#

ご存知の通り、ドライバは USB デバイスの列挙段階で記述子を問い合わせる必要があります。この記述子の作成が TMC デバイスとして列挙されるための鍵です。

列挙プロセスデータパケット

パケットをキャプチャした結果、サンプルのデバイス記述子が出てきました。これと全く同じであれば問題ありません。

UCHAR device_framework_full_speed_demo[] = {
    /* Device descriptor デバイス記述子 */
    0x12, 		// len(static)
    0x01, 		// device(static)
    0x00, 0x02,	// USB2.0(static)
    0x00,		// DeviceClass(static)
    0x00,		// DeviceSubClass(static)
    0x00,		// bDeviceProtocol(static)
    0x40,		// bMaxPacketSize0
    0x69, 0x0A,	// idVendor (VID): 0x0A69
    0x8A, 0x08,	// idProduct (PID): 0x088A
    0x00, 0x01,	// bcdDevice: Device release number 0x0100
    0x01,		// iManufacturer メーカー文字列記述子のインデックス(static)
    0x02,		// iProduct 製品文字列記述子のインデックス(static)
    0x03,		// iSerialNumber シリアル番号文字列記述子のインデックス(static)
    0x01,		// bNumConfigurations(static)

    /* Device qualifier descriptor デバイス限定子記述子 */
    0x0a,		// len(static)
    0x06,		// bDescriptorType: Device Qualifier Descriptor(static)
    0x00, 0x02,	// USB2.0(static)
    0x00,		// DeviceClass(static)
    0x00,		// DeviceSubClass(static)
    0x00,		// bDeviceProtocol(static)
    0x40,		// bMaxPacketSize0
    0x01,		// bNumConfigurations(static)
    0x00,		// reserve(static)

    /* Configuration descriptor 構成記述子 */
    0x09, 		// len(static)
    0x02, 		// bDescriptorType: Configuration Descriptor(static)
    0x20, 0x00,	// wTotalLength: Total length of data for this configuration (32 bytes)
    0x01, 		// bNumInterfaces(static)
    0x01, 		// bConfigurationValue(static)
    0x00, 		// iConfiguration: Index of configuration string descriptor
    0x80, 		// bmAttributes: Self-powered 自供電
    0x64, 		// bMaxPower: 200 mA (0x64 * 2 mA)

    /* Interface descriptor インターフェース記述子 */
    0x09, 		// len(static)
    0x04, 		// bDescriptorType: Interface Descriptor(static)
    0x00, 		// bInterfaceNumber: Interface 0
    0x00, 		// bAlternateSetting
    0x02, 		// bNumEndpoints: 2 endpoints(static)
    0xFE, 		// bInterfaceClass: TMC(static)
    0x03, 		// bInterfaceSubClass: USB488(static)
    0x01, 		// bInterfaceProtocol(static)
    0x00, 		// iInterface: Index of interface string descriptor(static)

    /* Endpoint descriptor エンドポイント記述子(Bulk Out) */
    0x07, 		// len(static)
    0x05,		// bDescriptorType: Endpoint Descriptor(static)
    0x81,		// bEndpointAddress: 端点アドレス(IN方向、端点番号1)
    0x02,		// bmAttributes: 伝送タイプはバルク(Bulk)
    0x40, 0x00,	// wMaxPacketSize: 最大パケットサイズ64バイト
    0x00,		// bInterval: クエリ間隔(バルク伝送時は0)

    /* Endpoint descriptor エンドポイント記述子(Bulk In) */
    0x07,		// len(static)
    0x05,		// bDescriptorType: Endpoint Descriptor(static)
    0x01,		// bEndpointAddress: 端点アドレス(OUT方向、端点番号1)
    0x02,		// bmAttributes: 伝送タイプはバルク(Bulk)
    0x40, 0x00,	// wMaxPacketSize: 最大パケットサイズ64バイト
    0x00,		// bInterval: クエリ間隔(バルク伝送時は0)
};

最も重要なのは、Interface descriptor部分のbInterfaceClassbInterfaceSubClassの 2 つの部分が、私たちの列挙タイプを直接決定することです。もちろん、記述子全体の他の部分で(static)とマークされている部分は、すべてサンプルと全く同じでなければならず、標準の規定です。一部は TMC 標準、一部は USB 標準であり、(static)とマークされている部分はすべて同じであれば問題ありません。

特に、USB-TMC のプロトコルには高速と全速の区別がなく、どの USB ラインで動作していても、デバイス限定子が必要です。これは他の現代的な USB プロトコルスタックとは異なる点です。

次に特別なのは、USB-TMC デバイスはコンビネーション列挙を使用できず、NI-VISA による USB-TMC の認識とリソース呼び出しを破壊します。例えば、TMC + シリアルデバイスとしてコンビネーション列挙を試みたところ、デバイスマネージャーには表示されました(列挙成功)が、VISA 呼び出し時にはシリアルポートのみが呼び出され、TMC デバイスは呼び出せませんでした。

機器に指示を送信するプロセス#

通信中、以前の記述子にある Bulk Out エンドポイントを介して機器にメッセージを送信します。したがって、デバイスはこの Bulk Out エンドポイントを繰り返し開いて、あらゆる可能性のあるメッセージを受信する必要があります。デバイスが Bulk Out エンドポイントを開いていない場合、大量の NAK パケットが生成され、デバイスにエンドポイントを再起動するためのセットアップ指令を送信するトリガーが発生します。

例えば、クラシックなメッセージは以下の通りです。

0x01, 0x02, 0xFD, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A, 0x00, 0x00

このメッセージは、USBTMC_1_00 標準文書の記述に基づいて解析でき、以下のいくつかのセグメントに分かれます。

メッセージセグメント意味
0x01このメッセージが OUT リクエストであることを示し、機器がこのメッセージの内容を解析してアクションを実行する必要があることを示します。
0x02, 0xFDメッセージタグおよびその反転、各メッセージで異なるタグです。
0x00固定値 0。
0x06, 0x00, 0x00, 0x00内容の長さは 6 であり、この長さはリトルエンディアン 32 ビットで伝送されます。
0x01, 0x00, 0x00, 0x00最初のバイトの最初のビットのみが意味を持ち、1 は内容の文がすでに送信されたことを示し、0 は後続の内容メッセージがあることを示します。
0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A6 バイトの正式な内容で、*IDN?\r であり、\r はキャリッジリターンのエスケープ文字です。
0x00, 0x00メッセージ全体の長さが 4 バイトに揃うように 0 で埋められています。

簡単に言えば、Bulk Out エンドポイントで受信したメッセージデータの最初のバイトを解析し、0x01 であれば後続の解析を行い、正式な SCPI 指令である "*IDN?" を抽出して処理します。

機器からの応答を受信するプロセス#

通信中、機器は USB を介して上位機に自由に応答を送信することはできません。これはシリアル通信とは異なります。上位機が応答データを受信することを期待している場合、Bulk Out エンドポイントにメッセージを送信し、機器が応答を送信できることを示します。クラシックな応答開始メッセージの例は以下の通りです。

0x02, 0x03, 0xFC, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

このメッセージは、USBTMC_1_00 標準文書の記述に基づいて解析でき、以下のいくつかのセグメントに分かれます。

メッセージセグメント意味
0x02このメッセージが IN リクエストであり、機器が任意の応答を送信する必要があることを示します。
0x03, 0xFCメッセージタグおよびその反転、このタグは各メッセージで異なります。
0x00固定値 0。
0x00, 0x04, 0x00, 0x00上位機が 0x0400 の 1024 バイトの受信バッファを開通したことを示します。この値は完全に無視しても構いません。上位機のリソースは非常に豊富であり、通信に失敗した場合は自分で大きくすれば良いのです。
0x00D1 ビット、すなわち & 0x2 のビットが意味を持ち、1 であれば次のバイトが上位機がサポートし、機器が必ずサポートしなければならない終了符号であることを示し、0 であれば無視しても構いません。他のビットはすべて予約です。
0x00前のバイトの D1 ビットが 1 であれば、このバイトはサポートされる終了符号を示します。例えば \r です。
0x00, 0x00埋められた予約文字で、実際の役割はメッセージ全体の長さが 4 バイトに揃うように 0 で埋められています。

簡単に言えば、Bulk Out エンドポイントで受信したメッセージデータの最初のバイトを解析し、0x02 であればデバイスの送信チャネルを開きます。例えば、上位機が以前に "*IDN?" という正式な SCPI 指令を送信した場合、デバイスはこの時点で生成された応答を送信する必要があります。

指定された終了符号機能は当然サポートされず、後で必須のセットアップパケットの紹介で、上位機に私たちがこの指定をサポートしていないことを知らせる方法を紹介します。

クラシックな応答伝送メッセージは以下の通りです。

0x02, 0x03, 0xFC, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A, 0x00, 0x00,

メッセージセグメント意味
0x02このメッセージが IN リクエストの応答であることを示します。
0x03, 0xFCメッセージタグおよびその反転、このタグはリクエストメッセージをコピーする必要があります。
0x00固定値 0。
0x06, 0x00, 0x00, 0x00応答の有効データは合計 6 バイトであり、この長さはリトルエンディアン 32 ビットで伝送されます。
0x01, 0x00, 0x00, 0x00最初のバイトの最初のビットのみが意味を持ち、1 は内容の文がすでに送信されたことを示し、0 は後続の内容メッセージがあることを示します。
0x2A, 0x49, 0x44, 0x4E, 0x3F, 0x0A*IDN?\r、これはループバックテストを行っており、私の機器は最後に受信した指令を応答します。
0x00, 0x00メッセージ全体の長さが 4 バイトに揃うように 0 で埋められています。

優れた 4 バイト整列方法#

ビット演算を使用して、4 バイト整列を非常に便利かつ効率的に行うことができます。

uint8_t *data;  				// 元のメッセージバッファ
uint32_t len =  OriginalLen;	// 元の長さ

for(uint32_t i = len; i < ((len + 0x3) & ~0x3U); i++)
{
    data[i] = 0x00;
}

_write(data, ((len + 0x3) & ~0x3U));

setup パケットの処理#

0x80,0x06 で始まるこれらの標準 setup パケットには、以下のカテゴリがあります。申し訳ありませんが、私も完全に usbx プロトコルスタックに依存して自動処理しています。

#define UX_GET_STATUS                                                   0u
#define UX_CLEAR_FEATURE                                                1u
#define UX_SET_FEATURE                                                  3u 
#define UX_SET_ADDRESS                                                  5u
#define UX_GET_DESCRIPTOR                                               6u
#define UX_SET_DESCRIPTOR                                               7u
#define UX_GET_CONFIGURATION                                            8u
#define UX_SET_CONFIGURATION                                            9u
#define UX_GET_INTERFACE                                                10u
#define UX_SET_INTERFACE                                                11u
#define UX_SYNCH_FRAME                                                  12u

そして、0xA1 または 0xA2 で始まる COMMAND_REQUEST パケットには、以下の内容があります。これは私たちが処理しなければならないものです。

0xA1,0x07 で始まる場合、デバイスの特性を問い合わせており、この時のサンプル応答パケットは以下の通りです。内容が多いため、文書を参照してください。おそらくこの応答をそのまま使用するのが良いでしょう。この時、上位機に対して、私たちが終了符号を設定できないことを伝えます。

0x01, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

0xA2,0x01 で始まる場合、bulk-out 伝送をクリアすることを示しており、何も気にせず、成功を応答すれば良いです。応答パケットは以下の通りです。

0x01,tag (セットアップパケットの 0 から数えて 2 番目のバイトをコピー)

0xA2,0x02 で始まる場合、以前にクリアした bulk-out 伝送のリクエストの状態を問い合わせており、何も気にせず、成功を応答すれば良いです。応答パケットは以下の通りです。

0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00

他の COMMAND_REQUEST パケットについては、あまり使用されないため、気にしなくても良いです。

[!NOTE]

まず、あなたは usbx の実践に少し慣れておくべきです。少なくとも既存の例の一つ、例えば CDC_ACM を使用したことがあるべきです。そうでなければ、以下の内容は全く意味がありません。以下は usbx という USB プロトコルスタックフレームワークの下で、TMC 列挙をどのように追加するかの例です。

usbx における列挙記述子の実践#

_ux_device_stack_initialize関数に注目すると、以下のような部分があり、これはInterface descriptorおよびConfiguration descriptor部分が記述子全体の中でどの位置にあるかに基づいて登録されることを示しています。Configuration descriptor部分の前のすべては == デバイス記述子 == として扱われ、Interface descriptor部分以降は == エンドポイント記述子 == として扱われます。

            switch(descriptor_type)
            {

            case UX_INTERFACE_DESCRIPTOR_ITEM:
				......
                break;

            case UX_CONFIGURATION_DESCRIPTOR_ITEM:
				......
                break;

            default:
                break;
            }

したがって、元のux_device_descriptors.cファイルのUSBD_Device_Framework_Builder関数の内容を少し修正する必要があります。元の関数には以下のような内容があるはずです。ここでは、高速 USB の場合のみデバイス限定子を追加する必要がありますが、私たちの USB-TMC は通常全速 USB で動作しており、USB-TMC のプロトコルには高速と全速の区別がないため、ここでこの if 文を削除する必要があります。高速でも全速でも、このデバイス限定子を記述子に組み込む必要があります。

    /* Check if USBx is in high speed mode to add qualifier descriptor */
    if(Speed == USBD_HIGH_SPEED)
    {
        pDevQualDesc                     = (USBD_DevQualiDescTypedef *)(pDevFrameWorkDesc + pdev->CurrDevDescSz);
        pDevQualDesc->bLength            = (uint8_t)sizeof(USBD_DevQualiDescTypedef);
        pDevQualDesc->bDescriptorType    = UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM;
        pDevQualDesc->bcdDevice          = 0x0200;
        pDevQualDesc->Class              = 0x00;
        pDevQualDesc->SubClass           = 0x00;
        pDevQualDesc->Protocol           = 0x00;
        pDevQualDesc->bMaxPacketSize     = 0x40;
        pDevQualDesc->bNumConfigurations = 0x01;
        pDevQualDesc->bReserved          = 0x00;
        pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DevQualiDescTypedef);
    }

他の部分は大きな調整は必要なく、サンプル記述子に従ってパラメータを一段ずつ修正すれば良いです。元のファイルに基づいて修正された ux_device_descriptors.c および ux_device_descriptors.h ファイルは以下の通りです。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_device_descriptors.c
  * @author  MCD Application Team
  * @brief   USBX Device descriptor header file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020-2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "ux_device_descriptors.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
USBD_DevClassHandleTypeDef USBD_Device_FS, USBD_Device_HS;

uint8_t                    UserClassInstance[USBD_MAX_CLASS_INTERFACES] = {
    CLASS_TYPE_TMC,
};

uint8_t UserTMCInterface[] = {
    INTERFACE_TMC_USB488,
};

/* The generic device descriptor buffer that will be filled by builder
   Size of the buffer is the maximum possible device FS descriptor size. */
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN static uint8_t DevFrameWorkDesc_FS[USBD_FRAMEWORK_MAX_DESC_SZ] __ALIGN_END = {0};

/* The generic device descriptor buffer that will be filled by builder
   Size of the buffer is the maximum possible device HS descriptor size. */
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN static uint8_t DevFrameWorkDesc_HS[USBD_FRAMEWORK_MAX_DESC_SZ] __ALIGN_END = {0};

static uint8_t              *pDevFrameWorkDesc_FS                                        = DevFrameWorkDesc_FS;

static uint8_t              *pDevFrameWorkDesc_HS                                        = DevFrameWorkDesc_HS;
/* USER CODE BEGIN PV0 */

/* USER CODE END PV0 */

/* String Device Framework :
 Byte 0 and 1 : Word containing the language ID : 0x0904 for US
 Byte 2       : Byte containing the index of the descriptor
 Byte 3       : Byte containing the length of the descriptor string
*/
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN UCHAR USBD_string_framework[USBD_STRING_FRAMEWORK_MAX_LENGTH] __ALIGN_END = {0};

/* Multiple languages are supported on the device, to add
   a language besides English, the Unicode language code must
   be appended to the language_id_framework array and the length
   adjusted accordingly. */

#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN UCHAR USBD_language_id_framework[LANGUAGE_ID_MAX_LENGTH] __ALIGN_END = {0};

/* USER CODE BEGIN PV1 */

/* USER CODE END PV1 */

/* Private function prototypes -----------------------------------------------*/
static void     USBD_Desc_GetString(uint8_t *desc, uint8_t *Buffer, uint16_t *len);
static uint8_t  USBD_Desc_GetLen(uint8_t *buf);

static uint8_t *USBD_Device_Framework_Builder(USBD_DevClassHandleTypeDef *pdev, uint8_t *pDevFrameWorkDesc, uint8_t *UserClassInstance, uint8_t Speed);

static uint8_t  USBD_FrameWork_AddToConfDesc(USBD_DevClassHandleTypeDef *pdev, uint8_t Speed, uint8_t *pCmpstConfDesc);

static uint8_t  USBD_FrameWork_AddClass(USBD_DevClassHandleTypeDef *pdev, USBD_CompositeClassTypeDef class, uint8_t cfgidx, uint8_t Speed, uint8_t *pCmpstConfDesc);

static uint8_t  USBD_FrameWork_FindFreeIFNbr(USBD_DevClassHandleTypeDef *pdev);

static void     USBD_FrameWork_AddConfDesc(uint32_t Conf, uint32_t *pSze);

static void     USBD_FrameWork_AssignEp(USBD_DevClassHandleTypeDef *pdev, uint8_t Add, uint8_t Type, uint32_t Sze);

static void     USBD_FrameWork_TMC_Desc(USBD_DevClassHandleTypeDef *pdev, uint32_t pConf, uint32_t *Sze);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  USBD_Get_Device_Framework_Speed
  *         Return the device speed descriptor
  * @param  Speed : HIGH or FULL SPEED flag
  * @param  length : length of HIGH or FULL SPEED array
  * @retval Pointer to descriptor buffer
  */
uint8_t *USBD_Get_Device_Framework_Speed(uint8_t Speed, ULONG *Length)
{
    uint8_t *pFrameWork = NULL;
    /* USER CODE BEGIN Device_Framework0 */

    /* USER CODE END Device_Framework0 */

    if(USBD_FULL_SPEED == Speed)
    {
        USBD_Device_Framework_Builder(&USBD_Device_FS, pDevFrameWorkDesc_FS, UserClassInstance, Speed);

        /* Get the length of USBD_device_framework_full_speed */
        *Length    = (ULONG)(USBD_Device_FS.CurrDevDescSz + USBD_Device_FS.CurrConfDescSz);

        pFrameWork = pDevFrameWorkDesc_FS;
    }
    else
    {
        USBD_Device_Framework_Builder(&USBD_Device_HS, pDevFrameWorkDesc_HS, UserClassInstance, Speed);

        /* Get the length of USBD_device_framework_high_speed */
        *Length    = (ULONG)(USBD_Device_HS.CurrDevDescSz + USBD_Device_HS.CurrConfDescSz);

        pFrameWork = pDevFrameWorkDesc_HS;
    }
    /* USER CODE BEGIN Device_Framework1 */

    /* USER CODE END Device_Framework1 */
    return pFrameWork;
}

/**
  * @brief  USBD_Get_String_Framework
  *         Return the language_id_framework
  * @param  Length : Length of String_Framework
  * @retval Pointer to language_id_framework buffer
  */
uint8_t *USBD_Get_String_Framework(ULONG *Length)
{
    uint16_t len   = 0U;
    uint8_t  count = 0U;

    /* USER CODE BEGIN String_Framework0 */

    /* USER CODE END String_Framework0 */

    /* Set the Manufacturer language Id and index in USBD_string_framework */
    USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
    USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
    USBD_string_framework[count++] = USBD_IDX_MFC_STR;

    /* Set the Manufacturer string in string_framework */
    USBD_Desc_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_string_framework + count, &len);

    /* Set the Product language Id and index in USBD_string_framework */
    count += len + 1;
    USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
    USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
    USBD_string_framework[count++] = USBD_IDX_PRODUCT_STR;

    /* Set the Product string in USBD_string_framework */
    USBD_Desc_GetString((uint8_t *)USBD_PRODUCT_STRING, USBD_string_framework + count, &len);

    /* Set Serial language Id and index in string_framework */
    count += len + 1;
    USBD_string_framework[count++] = USBD_LANGID_STRING & 0xFF;
    USBD_string_framework[count++] = USBD_LANGID_STRING >> 8;
    USBD_string_framework[count++] = USBD_IDX_SERIAL_STR;

    /* Set the Serial number in USBD_string_framework */
    USBD_Desc_GetString((uint8_t *)USBD_SERIAL_NUMBER, USBD_string_framework + count, &len);

    /* USER CODE BEGIN String_Framework1 */

    /* USER CODE END String_Framework1 */

    /* Get the length of USBD_string_framework */
    *Length = strlen((const char *)USBD_string_framework);

    return USBD_string_framework;
}

/**
  * @brief  USBD_Get_Language_Id_Framework
  *         Return the language_id_framework
  * @param  Length : Length of Language_Id_Framework
  * @retval Pointer to language_id_framework buffer
  */
uint8_t *USBD_Get_Language_Id_Framework(ULONG *Length)
{
    uint8_t count = 0U;

    /* Set the language Id in USBD_language_id_framework */
    USBD_language_id_framework[count++] = USBD_LANGID_STRING & 0xFF;
    USBD_language_id_framework[count++] = USBD_LANGID_STRING >> 8;

    /* Get the length of USBD_language_id_framework */
    *Length = strlen((const char *)USBD_language_id_framework);

    return USBD_language_id_framework;
}

/**
  * @brief  USBD_Get_Interface_Number
  *         Return interface number
  * @param  class_type : Device class type
  * @param  interface_type : Device interface type
  * @retval interface number
  */
uint16_t USBD_Get_Interface_Number(uint8_t class_type, uint8_t interface_type)
{
    uint8_t itf_num = 0U;
    uint8_t idx     = 0U;

    /* USER CODE BEGIN USBD_Get_Interface_Number0 */

    /* USER CODE END USBD_Get_Interface_Number0 */

    for(idx = 0; idx < USBD_MAX_SUPPORTED_CLASS; idx++)
    {
        if((USBD_Device_FS.tclasslist[idx].ClassType == class_type) && (USBD_Device_FS.tclasslist[idx].InterfaceType == interface_type))
        {
            itf_num = USBD_Device_FS.tclasslist[idx].Ifs[0];
        }
    }

    /* USER CODE BEGIN USBD_Get_Interface_Number1 */

    /* USER CODE END USBD_Get_Interface_Number1 */

    return itf_num;
}

/**
  * @brief  USBD_Get_Configuration_Number
  *         Return configuration number
  * @param  class_type : Device class type
  * @param  interface_type : Device interface type
  * @retval configuration number
  */
uint16_t USBD_Get_Configuration_Number(uint8_t class_type, uint8_t interface_type)
{
    uint8_t cfg_num = 1U;

    /* USER CODE BEGIN USBD_Get_CONFIGURATION_Number0 */

    /* USER CODE END USBD_Get_CONFIGURATION_Number0 */

    /* USER CODE BEGIN USBD_Get_CONFIGURATION_Number1 */

    /* USER CODE END USBD_Get_CONFIGURATION_Number1 */

    return cfg_num;
}

/**
  * @brief  USBD_Desc_GetString
  *         Convert ASCII string into Unicode one
  * @param  desc : descriptor buffer
  * @param  Unicode : Formatted string buffer (Unicode)
  * @param  len : descriptor length
  * @retval None
  */
static void USBD_Desc_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len)
{
    uint8_t  idx = 0U;
    uint8_t *pdesc;

    if(desc == NULL)
    {
        return;
    }

    pdesc          = desc;
    *len           = (uint16_t)USBD_Desc_GetLen(pdesc);

    unicode[idx++] = *(uint8_t *)len;

    while(*pdesc != (uint8_t)'\0')
    {
        unicode[idx++] = *pdesc;
        pdesc++;
    }
}

/**
  * @brief  USBD_Desc_GetLen
  *         return the string length
  * @param  buf : pointer to the ASCII string buffer
  * @retval string length
  */
static uint8_t USBD_Desc_GetLen(uint8_t *buf)
{
    uint8_t  len   = 0U;
    uint8_t *pbuff = buf;

    while(*pbuff != (uint8_t)'\0')
    {
        len++;
        pbuff++;
    }

    return len;
}

/**
  * @brief  USBD_Device_Framework_Builder
  *         Device Framework builder
  * @param  pdev: device instance
  * @param  pDevFrameWorkDesc: Pointer to the device framework descriptor
  * @param  UserClassInstance: type of the class to be added
  * @param  Speed: Speed parameter HS or FS
  * @retval status
  */
static uint8_t *USBD_Device_Framework_Builder(USBD_DevClassHandleTypeDef *pdev, uint8_t *pDevFrameWorkDesc, uint8_t *UserClassInstance, uint8_t Speed)
{
    static USBD_DeviceDescTypedef   *pDevDesc;
    static USBD_DevQualiDescTypedef *pDevQualDesc;
    uint8_t                          Idx_Instance = 0U;

    /* Set Dev and conf descriptors size to 0 */
    pdev->CurrConfDescSz = 0U;
    pdev->CurrDevDescSz  = 0U;

    /* Set the pointer to the device descriptor area*/
    pDevDesc = (USBD_DeviceDescTypedef *)pDevFrameWorkDesc;

    /* Start building the generic device descriptor common part */
    pDevDesc->bLength            = (uint8_t)sizeof(USBD_DeviceDescTypedef);
    pDevDesc->bDescriptorType    = UX_DEVICE_DESCRIPTOR_ITEM;
    pDevDesc->bcdUSB             = USB_BCDUSB;
    pDevDesc->bDeviceClass       = 0x00;
    pDevDesc->bDeviceSubClass    = 0x00;
    pDevDesc->bDeviceProtocol    = 0x00;
    pDevDesc->bMaxPacketSize     = USBD_MAX_EP0_SIZE;
    pDevDesc->idVendor           = USBD_VID;
    pDevDesc->idProduct          = USBD_PID;
    pDevDesc->bcdDevice          = 0x0100;
    pDevDesc->iManufacturer      = USBD_IDX_MFC_STR;
    pDevDesc->iProduct           = USBD_IDX_PRODUCT_STR;
    pDevDesc->iSerialNumber      = USBD_IDX_SERIAL_STR;
    pDevDesc->bNumConfigurations = USBD_MAX_NUM_CONFIGURATION;
    pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DeviceDescTypedef);

    /* Check if USBx is in high speed mode to add qualifier descriptor */
    pDevQualDesc                     = (USBD_DevQualiDescTypedef *)(pDevFrameWorkDesc + pdev->CurrDevDescSz);
    pDevQualDesc->bLength            = (uint8_t)sizeof(USBD_DevQualiDescTypedef);
    pDevQualDesc->bDescriptorType    = UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM;
    pDevQualDesc->bcdDevice          = 0x0200;
    pDevQualDesc->Class              = 0x00;
    pDevQualDesc->SubClass           = 0x00;
    pDevQualDesc->Protocol           = 0x00;
    pDevQualDesc->bMaxPacketSize     = 0x40;
    pDevQualDesc->bNumConfigurations = 0x01;
    pDevQualDesc->bReserved          = 0x00;
    pdev->CurrDevDescSz += (uint32_t)sizeof(USBD_DevQualiDescTypedef);

    /* Build the device framework */
    while(Idx_Instance < USBD_MAX_SUPPORTED_CLASS)
    {
        if((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->NumClasses < USBD_MAX_SUPPORTED_CLASS) && (UserClassInstance[Idx_Instance] != CLASS_TYPE_NONE))
        {
            /* Call the composite class builder */
            (void)USBD_FrameWork_AddClass(pdev, (USBD_CompositeClassTypeDef)UserClassInstance[Idx_Instance], 0, Speed, (pDevFrameWorkDesc + pdev->CurrDevDescSz));

            /* Increment the ClassId for the next occurrence */
            pdev->classId++;
            pdev->NumClasses++;
        }

        Idx_Instance++;
    }

    /* Check if there is a composite class and update device class */
    if(pdev->NumClasses > 1)
    {
        pDevDesc->bDeviceClass    = 0xEF;
        pDevDesc->bDeviceSubClass = 0x02;
        pDevDesc->bDeviceProtocol = 0x01;
    }
    else
    {
        /* Check if the CDC ACM class is set and update device class */
        if(UserClassInstance[0] == CLASS_TYPE_CDC_ACM)
        {
            pDevDesc->bDeviceClass    = 0x02;
            pDevDesc->bDeviceSubClass = 0x02;
            pDevDesc->bDeviceProtocol = 0x00;
        }
    }

    return pDevFrameWorkDesc;
}

/**
  * @brief  USBD_FrameWork_AddToConfDesc
  *         Add a new class to the configuration descriptor
  * @param  pdev: device instance
  * @param  Speed: device speed
  * @param  pCmpstConfDesc: to composite device configuration descriptor
  * @retval status
  */
uint8_t USBD_FrameWork_AddToConfDesc(USBD_DevClassHandleTypeDef *pdev, uint8_t Speed, uint8_t *pCmpstConfDesc)
{
    uint8_t interface = 0U;

    pdev->Speed       = Speed;

    if(pdev->classId == 0U)
    {
        /* Add configuration and IAD descriptors */
        USBD_FrameWork_AddConfDesc((uint32_t)pCmpstConfDesc, &pdev->CurrConfDescSz);
    }

    switch(pdev->tclasslist[pdev->classId].ClassType)
    {
    case CLASS_TYPE_TMC:
        switch(pdev->tclasslist[pdev->classId].InterfaceType)
        {
        case INTERFACE_TMC_USB488:

            /* Find the first available interface slot and Assign number of interfaces */
            interface                              = USBD_FrameWork_FindFreeIFNbr(pdev);
            pdev->tclasslist[pdev->classId].NumIf  = 1U;
            pdev->tclasslist[pdev->classId].Ifs[0] = interface;

            /* Assign endpoint numbers */
            pdev->tclasslist[pdev->classId].NumEps = 2U; /* EP_IN, EP_OUT */

            /* Check the current speed to assign endpoints */
            if(pdev->Speed == USBD_HIGH_SPEED)
            {
                /* Assign IN Endpoint */
                USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPIN_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPIN_HS_MPS);

                /* Assign OUT Endpoint */
                USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPOUT_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPOUT_HS_MPS);
            }
            else
            {
                USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPIN_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPIN_FS_MPS);

                USBD_FrameWork_AssignEp(pdev, USBD_TMC_NONE_EPOUT_ADDR, USBD_EP_TYPE_BULK, USBD_TMC_NONE_EPOUT_FS_MPS);
            }

            USBD_FrameWork_TMC_Desc(pdev, (uint32_t)pCmpstConfDesc, &pdev->CurrConfDescSz);

            break;

        default:
            break;
        }
        break;

    default:
        break;
    }

    return UX_SUCCESS;
}

/**
  * @brief  USBD_FrameWork_AddClass
  *         Register a class in the class builder
  * @param  pdev: device instance
  * @param  class: type of the class to be added (from USBD_CompositeClassTypeDef)
  * @param  cfgidx: configuration index
  * @param  speed: device speed
  * @param  pCmpstConfDesc: to composite device configuration descriptor
  * @retval status
  */
uint8_t USBD_FrameWork_AddClass(USBD_DevClassHandleTypeDef *pdev, USBD_CompositeClassTypeDef class, uint8_t cfgidx, uint8_t Speed, uint8_t *pCmpstConfDesc)
{
    static uint8_t interface_idx = 0U;

    if((pdev->classId < USBD_MAX_SUPPORTED_CLASS) && (pdev->tclasslist[pdev->classId].Active == 0U))
    {
        /* Store the class parameters in the global tab */
        pdev->tclasslist[pdev->classId].ClassId   = pdev->classId;
        pdev->tclasslist[pdev->classId].Active    = 1U;
        pdev->tclasslist[pdev->classId].ClassType = class;

        if(class == CLASS_TYPE_TMC)
        {
            pdev->tclasslist[pdev->classId].InterfaceType = UserTMCInterface[interface_idx];

            interface_idx++;

            if(interface_idx == sizeof(UserTMCInterface))
            {
                interface_idx = 0U;
            }
        }

        /* Call configuration descriptor builder and endpoint configuration builder */
        if(USBD_FrameWork_AddToConfDesc(pdev, Speed, pCmpstConfDesc) != UX_SUCCESS)
        {
            return UX_ERROR;
        }
    }

    UNUSED(cfgidx);

    return UX_SUCCESS;
}

/**
  * @brief  USBD_FrameWork_FindFreeIFNbr
  *         Find the first interface available slot
  * @param  pdev: device instance
  * @retval The interface number to be used
  */
static uint8_t USBD_FrameWork_FindFreeIFNbr(USBD_DevClassHandleTypeDef *pdev)
{
    uint32_t idx = 0U;

    /* Unroll all already activated classes */
    for(uint32_t i = 0U; i < pdev->NumClasses; i++)
    {
        /* Unroll each class interfaces */
        for(uint32_t j = 0U; j < pdev->tclasslist[i].NumIf; j++)
        {
            /* Increment the interface counter index */
            idx++;
        }
    }

    /* Return the first available interface slot */
    return (uint8_t)idx;
}

/**
  * @brief  USBD_FrameWork_AddConfDesc
  *         Add a new class to the configuration descriptor
  * @param  Conf: configuration descriptor
  * @param  pSze: pointer to the configuration descriptor size
  * @retval none
  */
static void USBD_FrameWork_AddConfDesc(uint32_t Conf, uint32_t *pSze)
{
    /* Intermediate variable to comply with MISRA-C Rule 11.3 */
    USBD_ConfigDescTypedef *ptr = (USBD_ConfigDescTypedef *)Conf;

    ptr->bLength                = (uint8_t)sizeof(USBD_ConfigDescTypedef);
    ptr->bDescriptorType        = UX_CONFIGURATION_DESCRIPTOR_ITEM;
    ptr->wDescriptorLength      = 0x20;
    ptr->bNumInterfaces         = 1U;
    ptr->bConfigurationValue    = 1U;
    ptr->iConfiguration         = USBD_CONFIG_STR_DESC_IDX;
    ptr->bmAttributes           = USBD_CONFIG_BMATTRIBUTES;
    ptr->bMaxPower              = USBD_CONFIG_MAXPOWER;
    *pSze += sizeof(USBD_ConfigDescTypedef);
}

/**
  * @brief  USBD_FrameWork_AssignEp
  *         Assign and endpoint
  * @param  pdev: device instance
  * @param  Add: Endpoint address
  * @param  Type: Endpoint type
  * @param  Sze: Endpoint max packet size
  * @retval none
  */
static void USBD_FrameWork_AssignEp(USBD_DevClassHandleTypeDef *pdev, uint8_t Add, uint8_t Type, uint32_t Sze)
{
    uint32_t idx = 0U;

    /* Find the first available endpoint slot */
    while(((idx < (pdev->tclasslist[pdev->classId]).NumEps) && ((pdev->tclasslist[pdev->classId].Eps[idx].is_used) != 0U)))
    {
        /* Increment the index */
        idx++;
    }

    /* Configure the endpoint */
    pdev->tclasslist[pdev->classId].Eps[idx].add     = Add;
    pdev->tclasslist[pdev->classId].Eps[idx].type    = Type;
    pdev->tclasslist[pdev->classId].Eps[idx].size    = (uint16_t)Sze;
    pdev->tclasslist[pdev->classId].Eps[idx].is_used = 1U;
}

/**
  * @brief  USBD_FrameWork_TMC_Desc
  *         Configure and Append the TMC Descriptor
  * @param  pdev: device instance
  * @param  pConf: Configuration descriptor pointer
  * @param  Sze: pointer to the current configuration descriptor size
  * @retval None
  */
static void USBD_FrameWork_TMC_Desc(USBD_DevClassHandleTypeDef *pdev, uint32_t pConf, uint32_t *Sze)
{
    static USBD_IfDescTypedef *pIfDesc;
    static USBD_EpDescTypedef *pEpDesc;

    switch(pdev->tclasslist[pdev->classId].InterfaceType)
    {
    case INTERFACE_TMC_USB488:

        /* Append Interface descriptor to Configuration descriptor */
        __USBD_FRAMEWORK_SET_IF(pdev->tclasslist[pdev->classId].Ifs[0], 0U, (uint8_t)(pdev->tclasslist[pdev->classId].NumEps), UX_DEVICE_CLASS_TMC_CLASS, UX_DEVICE_SUB_CLASS_USB488_CLASS, INTERFACE_TMC_USB488, 0U);

        if(pdev->Speed == USBD_HIGH_SPEED)
        {
            /* Append Endpoint descriptor to Configuration descriptor */
            __USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[0].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[0].size, USBD_TMC_NONE_EPIN_FS_BINTERVAL, USBD_TMC_NONE_EPIN_HS_BINTERVAL);

            __USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[1].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[1].size, USBD_TMC_NONE_EPOUT_HS_BINTERVAL, USBD_TMC_NONE_EPOUT_FS_BINTERVAL);
        }
        else
        {
            /* Append Endpoint descriptor to Configuration descriptor */
            __USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[0].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[0].size, USBD_TMC_NONE_EPIN_FS_BINTERVAL, USBD_TMC_NONE_EPIN_HS_BINTERVAL);

            __USBD_FRAMEWORK_SET_EP(pdev->tclasslist[pdev->classId].Eps[1].add, USBD_EP_TYPE_BULK, (uint16_t)pdev->tclasslist[pdev->classId].Eps[1].size, USBD_TMC_NONE_EPOUT_HS_BINTERVAL, USBD_TMC_NONE_EPOUT_FS_BINTERVAL);
        }

        break;

    default:
        break;
    }
}
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    ux_device_descriptors.h
  * @author  MCD Application Team
  * @brief   USBX Device descriptor header file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020-2021 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __UX_DEVICE_DESCRIPTORS_H__
#define __UX_DEVICE_DESCRIPTORS_H__

#ifdef __cplusplus
extern "C"
{
#endif

/* Includes ------------------------------------------------------------------*/
#include "ux_api.h"
#include "ux_stm32_config.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "main.h"
/* USER CODE END Includes */

/* Private defines -----------------------------------------------------------*/
#define USBD_MAX_NUM_CONFIGURATION            1U
#define USBD_MAX_SUPPORTED_CLASS              3U
#define USBD_MAX_CLASS_ENDPOINTS              9U
#define USBD_MAX_CLASS_INTERFACES             12U

#define USBD_HID_CUSTOM_ACTIVATED             1U

#define USBD_CONFIG_MAXPOWER                  0x64 // bMaxPower: 200 mA (0x64 * 2 mA)
#define USBD_COMPOSITE_USE_IAD                1U
#define USBD_DEVICE_FRAMEWORK_BUILDER_ENABLED 1U

#define USBD_FRAMEWORK_MAX_DESC_SZ            200U
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Enum Class Type */
typedef enum
{
    CLASS_TYPE_NONE     = 0,
    CLASS_TYPE_HID      = 1,
    CLASS_TYPE_CDC_ACM  = 2,
    CLASS_TYPE_MSC      = 3,
    CLASS_TYPE_CDC_ECM  = 4,
    CLASS_TYPE_DFU      = 5,
    CLASS_TYPE_VIDEO    = 6,
    CLASS_TYPE_PIMA_MTP = 7,
    CLASS_TYPE_CCID     = 8,
    CLASS_TYPE_PRINTER  = 9,
    CLASS_TYPE_RNDIS    = 10,
    CLASS_TYPE_AUDIO_10 = 11,
    CLASS_TYPE_AUDIO_20 = 12,
    CLASS_TYPE_TMC      = 40,
} USBD_CompositeClassTypeDef;

/* Enum NONE Interface Type */
typedef enum
{
    INTERFACE_NONE_DEFIN = 0
} USBD_NONEInterfaceTypeDef;

/* Enum HID Interface Type */
typedef enum
{
    INTERFACE_HID_CUSTOM   = 0,
    INTERFACE_HID_KEYBOARD = 1,
    INTERFACE_HID_MOUSE    = 2,
} USBD_HIDInterfaceTypeDef;

typedef enum
{
    INTERFACE_TMC_USB488 = 1,
} USBD_TMCInterfaceTypeDef;

/* USB Endpoint handle structure */
typedef struct
{
    uint32_t status;
    uint32_t total_length;
    uint32_t rem_length;
    uint32_t maxpacket;
    uint16_t is_used;
    uint16_t bInterval;
} USBD_EndpointTypeDef;

/* USB endpoint handle structure */
typedef struct
{
    uint8_t  add;
    uint8_t  type;
    uint16_t size;
    uint8_t  is_used;
} USBD_EPTypeDef;

/* USB Composite handle structure */
typedef struct
{
    USBD_CompositeClassTypeDef ClassType;
    uint32_t                   ClassId;
    uint8_t                    InterfaceType;
    uint32_t                   Active;
    uint32_t                   NumEps;
    uint32_t                   NumIf;
    USBD_EPTypeDef             Eps[USBD_MAX_CLASS_ENDPOINTS];
    uint8_t                    Ifs[USBD_MAX_CLASS_INTERFACES];
} USBD_CompositeElementTypeDef;

/* USB Device handle structure */
typedef struct _USBD_DevClassHandleTypeDef
{
    uint8_t                      Speed;
    uint32_t                     classId;
    uint32_t                     NumClasses;
    USBD_CompositeElementTypeDef tclasslist[USBD_MAX_SUPPORTED_CLASS];
    uint32_t                     CurrDevDescSz;
    uint32_t                     CurrConfDescSz;
} USBD_DevClassHandleTypeDef;

/* USB Device endpoint direction */
typedef enum
{
    OUT = 0x00,
    IN  = 0x80,
} USBD_EPDirectionTypeDef;

/* USB Device descriptors structure */
typedef struct
{
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t bcdUSB;
    uint8_t  bDeviceClass;
    uint8_t  bDeviceSubClass;
    uint8_t  bDeviceProtocol;
    uint8_t  bMaxPacketSize;
    uint16_t idVendor;
    uint16_t idProduct;
    uint16_t bcdDevice;
    uint8_t  iManufacturer;
    uint8_t  iProduct;
    uint8_t  iSerialNumber;
    uint8_t  bNumConfigurations;
} __PACKED USBD_DeviceDescTypedef;

/* USB Iad descriptors structure */
typedef struct
{
    uint8_t bLength;
    uint8_t bDescriptorType;
    uint8_t bFirstInterface;
    uint8_t bInterfaceCount;
    uint8_t bFunctionClass;
    uint8_t bFunctionSubClass;
    uint8_t bFunctionProtocol;
    uint8_t iFunction;
} __PACKED USBD_IadDescTypedef;

/* USB interface descriptors structure */
typedef struct
{
    uint8_t bLength;
    uint8_t bDescriptorType;
    uint8_t bInterfaceNumber;
    uint8_t bAlternateSetting;
    uint8_t bNumEndpoints;
    uint8_t bInterfaceClass;
    uint8_t bInterfaceSubClass;
    uint8_t bInterfaceProtocol;
    uint8_t iInterface;
} __PACKED USBD_IfDescTypedef;

/* USB endpoint descriptors structure */
typedef struct
{
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint8_t  bEndpointAddress;
    uint8_t  bmAttributes;
    uint16_t wMaxPacketSize;
    uint8_t  bInterval;
} __PACKED USBD_EpDescTypedef;

/* USB Config descriptors structure */
typedef struct
{
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t wDescriptorLength;
    uint8_t  bNumInterfaces;
    uint8_t  bConfigurationValue;
    uint8_t  iConfiguration;
    uint8_t  bmAttributes;
    uint8_t  bMaxPower;
} __PACKED USBD_ConfigDescTypedef;

/* USB Qualifier descriptors structure */
typedef struct
{
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t bcdDevice;
    uint8_t  Class;
    uint8_t  SubClass;
    uint8_t  Protocol;
    uint8_t  bMaxPacketSize;
    uint8_t  bNumConfigurations;
    uint8_t  bReserved;
} __PACKED USBD_DevQualiDescTypedef;

typedef struct
{
    uint8_t  bLength;
    uint8_t  bDescriptorType;
    uint16_t bcdTMC;
    uint16_t bCountryCode;
    uint8_t  bNumDescriptors;
    uint8_t  bTMCDescriptorType;
    uint16_t wDescriptor;
} __PACKED USBD_TMCDescTypedef;
/* Exported functions prototypes ---------------------------------------------*/
/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

uint8_t *USBD_Get_Device_Framework_Speed(uint8_t Speed, ULONG *Length);
uint8_t *USBD_Get_String_Framework(ULONG *Length);
uint8_t *USBD_Get_Language_Id_Framework(ULONG *Length);
uint16_t USBD_Get_Interface_Number(uint8_t class_type, uint8_t interface_type);
uint16_t USBD_Get_Configuration_Number(uint8_t class_type, uint8_t interface_type);

/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private_defines */

/* USER CODE END Private_defines */

#define USBD_VID                         0x0A69
#define USBD_PID                         0x088A
#define USBD_LANGID_STRING               1033 /* English (United States) */
#define USBD_MANUFACTURER_STRING         "YONO"
#define USBD_PRODUCT_STRING              "yonoDevice"
#define USBD_SERIAL_NUMBER               "123456789"

#define USB_DESC_TYPE_INTERFACE          0x04U
#define USB_DESC_TYPE_ENDPOINT           0x05U

#define USBD_EP_TYPE_CTRL                0x00U
#define USBD_EP_TYPE_ISOC                0x01U
#define USBD_EP_TYPE_BULK                0x02U
#define USBD_EP_TYPE_INTR                0x03U

#define USBD_FULL_SPEED                  0x00U
#define USBD_HIGH_SPEED                  0x01U

#define USB_BCDUSB                       0x0200U
#define LANGUAGE_ID_MAX_LENGTH           2U

#define USBD_IDX_MFC_STR                 0x01U
#define USBD_IDX_PRODUCT_STR             0x02U
#define USBD_IDX_SERIAL_STR              0x03U

#define USBD_MAX_EP0_SIZE                64U
#define USBD_DEVICE_QUALIFIER_DESC_SIZE  0x0AU

#define USBD_STRING_FRAMEWORK_MAX_LENGTH 256U

/* */
#define UX_DEVICE_CLASS_TMC_CLASS        0xFEU
#define UX_DEVICE_SUB_CLASS_USB488_CLASS 0x03U

/* Device TMC none */
#define USBD_TMC_NONE_EPIN_ADDR          0x81U
#define USBD_TMC_NONE_EPOUT_ADDR         0x01U
#define USBD_TMC_NONE_EPIN_FS_MPS        0x40U
#define USBD_TMC_NONE_EPIN_HS_MPS        0x40U
#define USBD_TMC_NONE_EPIN_FS_BINTERVAL  5U
#define USBD_TMC_NONE_EPIN_HS_BINTERVAL  0U
#define USBD_TMC_NONE_EPOUT_FS_MPS       0x40U
#define USBD_TMC_NONE_EPOUT_HS_MPS       0x40U
#define USBD_TMC_NONE_EPOUT_FS_BINTERVAL 0U
#define USBD_TMC_NONE_EPOUT_HS_BINTERVAL 5U

#ifndef USBD_CONFIG_STR_DESC_IDX
#define USBD_CONFIG_STR_DESC_IDX 0x00
#endif /* USBD_CONFIG_STR_DESC_IDX */

#ifndef USBD_CONFIG_BMATTRIBUTES
#define USBD_CONFIG_BMATTRIBUTES 0x80
#endif /* USBD_CONFIG_BMATTRIBUTES */

/* Private macro -----------------------------------------------------------*/
/* USER CODE BEGIN Private_macro */

/* USER CODE END Private_macro */
#define __USBD_FRAMEWORK_SET_EP(epadd, eptype, epsize, HSinterval, FSinterval)        \
    do                                                                                \
    {                                                                                 \
        /* Append Endpoint descriptor to Configuration descriptor */                  \
        pEpDesc                   = ((USBD_EpDescTypedef *)((uint32_t)pConf + *Sze)); \
        pEpDesc->bLength          = (uint8_t)sizeof(USBD_EpDescTypedef);              \
        pEpDesc->bDescriptorType  = USB_DESC_TYPE_ENDPOINT;                           \
        pEpDesc->bEndpointAddress = (epadd);                                          \
        pEpDesc->bmAttributes     = (eptype);                                         \
        pEpDesc->wMaxPacketSize   = (epsize);                                         \
        if(pdev->Speed == USBD_HIGH_SPEED)                                            \
        {                                                                             \
            pEpDesc->bInterval = (HSinterval);                                        \
        }                                                                             \
        else                                                                          \
        {                                                                             \
            pEpDesc->bInterval = (FSinterval);                                        \
        }                                                                             \
        *Sze += (uint32_t)sizeof(USBD_EpDescTypedef);                                 \
    }                                                                                 \
    while(0)

#define __USBD_FRAMEWORK_SET_IF(ifnum, alt, eps, class, subclass, protocol, istring)    \
    do                                                                                  \
    {                                                                                   \
        /* Interface Descriptor */                                                      \
        pIfDesc                     = ((USBD_IfDescTypedef *)((uint32_t)pConf + *Sze)); \
        pIfDesc->bLength            = (uint8_t)sizeof(USBD_IfDescTypedef);              \
        pIfDesc->bDescriptorType    = USB_DESC_TYPE_INTERFACE;                          \
        pIfDesc->bInterfaceNumber   = (ifnum);                                          \
        pIfDesc->bAlternateSetting  = (alt);                                            \
        pIfDesc->bNumEndpoints      = (eps);                                            \
        pIfDesc->bInterfaceClass    = (class);                                          \
        pIfDesc->bInterfaceSubClass = (subclass);                                       \
        pIfDesc->bInterfaceProtocol = (protocol);                                       \
        pIfDesc->iInterface         = (istring);                                        \
        *Sze += (uint32_t)sizeof(USBD_IfDescTypedef);                                   \
    }                                                                                   \
    while(0)
#ifdef __cplusplus
}
#endif
#endif /* __UX_DEVICE_DESCRIPTORS_H__ */

送信と受信の usbx における実践#

コードを見てください。合計 3 つのバッファを開きました。これは必須です。USB 伝送ラインが 1 つのバッファを占有し、受信した完全な文の格納に 1 つ、送信格納に 1 つが必要です。注意すべきは、_ux_device_class_dpump_tmc_read 関数は、私の改造を経てもなおブロッキング関数であるため、TMC_Engine () の TMC 送受信は独立したスレッドを占有する必要があります。

TMC_Dpump ポインタは、TMC_Device の activate と deactivate の中でバインドおよびアンバインドされます。

/********************************************************************************


 **** Copyright (C), 2024, Yuanlong Xu <[email protected]>    ****
 **** All rights reserved                                       ****

 ********************************************************************************
 * File Name     : ux_device_tmc.c
 * Author        : yono
 * Date          : 2025-01-07
 * Version       : 1.0
********************************************************************************/
/**************************************************************************/
/*
    tmcユーザー解析
*/

/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ux_api.h"
#include "ux_device_class_dpump.h"
/* Private types -------------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
#define UX_HOST_CLASS_DPUMP_PACKET_SIZE 255
extern UX_SLAVE_CLASS_DPUMP *TMC_Dpump;

/* チャンネルバッファ */
uint8_t device_buffer[UX_HOST_CLASS_DPUMP_PACKET_SIZE];
/* フレーム情報 */
uint8_t TransferEMO = 0; // 伝送終了フラグ 0x00続行 0x01終了
uint8_t TransferTag = 0; // 伝送トークン、応答時にコピー
/* 情報バッファ */
uint8_t  SCPI_Read[255];
uint32_t SCPI_Read_Length = 0;
uint8_t  SCPI_Write[255];
uint32_t SCPI_Write_Length = 0;

/* Private Constants ---------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
uint32_t    TMC_Write(uint8_t *data, uint32_t len);
extern UINT _ux_device_class_dpump_tmc_read(UX_SLAVE_CLASS_DPUMP *dpump, UCHAR *buffer, ULONG requested_length, ULONG *actual_length);
/* Private functions ---------------------------------------------------------*/

void TMC_Engine(void)
{
    UINT  status;
    ULONG actual_length;

    if(TMC_Dpump != UX_NULL)
    {
        /* 受信部分  */
        status = _ux_device_class_dpump_tmc_read(TMC_Dpump, device_buffer, UX_HOST_CLASS_DPUMP_PACKET_SIZE, &actual_length);
        if(status == UX_SUCCESS)
        {
            if((0xff ^ device_buffer[1] ^ device_buffer[2]) || device_buffer[3] != 0)
                return; // 不良メッセージ

            switch(device_buffer[0]) // コマンド
            {
            case 0x01:
                // 受信分岐
                ux_utility_memory_copy(&SCPI_Read[0 + SCPI_Read_Length],  // 互換性のある分割伝送、以前の伝送の長さを開始点とする
                                       &device_buffer[12],                // 実際のデータの開始位置
                                       *(uint32_t *)(&device_buffer[4])); // 4〜7バイトはリトルエンディアンでデータ長を構成
                SCPI_Read_Length += *(uint32_t *)(&device_buffer[4]);

                TransferEMO = device_buffer[8] & 0x01;

                if(TransferEMO)
                {
                    /* ループバックテスト */
                    TMC_Write(SCPI_Read, SCPI_Read_Length);
                    /* 終了互換性 */
                    // 伝送終了 解析をトリガー
                    SCPI_Read_Length = 0;
                }
                else
                {
                    // 伝送未終了 処理しない
                }
                break;

            case 0x02:
                TransferTag = device_buffer[1]; // トークンを保存

                /* 0〜3はリクエストおよびトークンであり、変更する必要はありません */
                *(uint32_t *)(&device_buffer[4]) = SCPI_Write_Length; // 4〜7バイトはリトルエンディアンでデータ長を構成

                /* 成功固定データ */
                device_buffer[8]  = 0x1; // 成功応答
                device_buffer[9]  = 0x00;
                device_buffer[10] = 0x00;
                device_buffer[11] = 0x00;

                /* 送信データを埋める */
                ux_utility_memory_copy(&device_buffer[12], SCPI_Write, SCPI_Write_Length);

                /* 0x00を埋める この時点で元々4バイト整列されている場合、4つの0が追加されます */
                for(uint32_t i = 12 + SCPI_Write_Length; i < ((12 + SCPI_Write_Length + 0x3) & ~0x3U); i++)
                {
                    device_buffer[i] = 0x00;
                }

                /* 送信リクエストを行う */
                status = _ux_device_class_dpump_write(TMC_Dpump, device_buffer, ((12 + SCPI_Write_Length + 0x3) & ~0x3U), &actual_length);

                /* 送信リクエストをクリア */
                SCPI_Write_Length = 0;
                break;
            default:
                break;
            }
        }
    }
}

uint32_t TMC_Write(uint8_t *data, uint32_t len)
{
    if(sizeof(SCPI_Write) < (SCPI_Write_Length + len))
        return 1;

    ux_utility_memory_copy(&SCPI_Write[0 + SCPI_Write_Length], // 互換性のある分割埋め
                           data,
                           len);
    SCPI_Write_Length += len;

    return 0;
}
/**************************************************************************/
/*                                                                        */
/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*                                                                        */
/**************************************************************************/

/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** USBX Component                                                        */
/**                                                                       */
/**   Device DPUMP Class
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。