[转]使用STM32CubeMX:USB大容量存储设备

Posted 陌鉎こ城sHi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]使用STM32CubeMX:USB大容量存储设备相关的知识,希望对你有一定的参考价值。

原文地址http://qiita.com/mt08/items/fcc925fa47726bfc6c74

概要

  • STM32CubeMXを使って、USB MassStorageを使ってみる。
  • USBを使うときは、外付けのOscillator/Xtalが必要。(48MHzを作るのに、内部のやつは精度がでない?)
  • usbd_storage_if.cだけ変更. 今回は、ReadOnly.

Qiita-STM32_USBMS04.png

環境

  • STM32L1系
  • ビルド環境
    • Windows7 64bit
    • MDK-ARM Lite v5.20
    • STM32CubeMX v4.18.0
      • ボードが動くくらいの設定(クロックとか、GPIOとか)
      • FreeRTOS : [v] Enabled (Lチカにつかった程度)
      • USB_DEVICE : Class for FS IP Mass Storage Class
      • USB : [v] Device (FS)
        => コード生成
    • Firmware Package for Family STM32L1 v1.6.0

大体の説明

  • コールバック
    ホストに接続すると、コールバックが呼ばれるので、うまく応答すればよい。

    usbd_storage_if.c
    ...
    USBD_StorageTypeDef USBD_Storage_Interface_fops_FS =
    {
      STORAGE_Init_FS,
      STORAGE_GetCapacity_FS,
      STORAGE_IsReady_FS,
      STORAGE_IsWriteProtected_FS,
      STORAGE_Read_FS,
      STORAGE_Write_FS,
      STORAGE_GetMaxLun_FS,
      (int8_t *)STORAGE_Inquirydata_FS,
    };
    ...
    
  • ディスクの容量は、STORAGE_BLK_NBRにセクタ数定義する。
    今回、#define STORAGE_BLK_NBR 0x81000としたので、
    => 0x81000 * 512bytes/sector = 258MBくらいのディスク

  • コールバックは、だいたい、STORAGE_Read_FSで、セクタのデータを要求してくるので、そいつをかえせばいい。

    • ↓の実装では、_ReadSector()に飛ばしている。
    • _ReadSector()で、要求されたセクタ番号で、MBR, PBR, FAT, ROOT_DIR, DATAの領域で、処理を分けている。
      • MBR,PBRは、固定値を用意して、memcpy
      • FAT, ROOTDIR, DATAは、Offsetを引いて、処理関数(handleFatClusterChain,handleRoot,handleData)へ飛ばして、うまくデータを詰める

実際のコード

  • もともとのコードの変更箇所

    usbd_storage_if.c
    ...
    #define STORAGE_LUN_NBR                  1  
    #define STORAGE_BLK_NBR                  0x81000  //##mt08
    #define STORAGE_BLK_SIZ    
    
    ...
    
    int8_t  STORAGE_IsWriteProtected_FS (uint8_t lun)
    {
      /* USER CODE BEGIN 5 */ 
      return (USBD_FAIL); //##mt08: Read Only
      /* USER CODE END 5 */ 
    }
    
    ...
    int8_t STORAGE_Read_FS (uint8_t lun, 
                            uint8_t *buf, 
                            uint32_t blk_addr,                       
                            uint16_t blk_len)
    {
      /* USER CODE BEGIN 6 */ 
      _ReadSector(buf, blk_addr, blk_len); //##mt08
      return (USBD_OK);
      /* USER CODE END 6 */ 
    }
    

追加コード

  • 型宣言

    #include <stdint.h>
    typedef uint8_t Byte;
    typedef struct MasterBootRecord {
            Byte    checkRoutionOnx86[446];
            struct {
                    Byte    bootDescriptor;             /* 0x80: bootable device, 0x00: non-bootable */
                    Byte    firstPartitionSector[3];    /* 1st sector number */
                    Byte    fileSystemDescriptor;       /* 1:FAT12, 4:FAT16(less than 32MB), 5:Extended-DOS Partition,
                                                                                            6:FAT16(more 32MB), 0xb:FAT32(more 2GB),
                                                                                            0xc:FAT32 Int32h, 0xe:FAT16 Int32h,
                                                                                            0xf:5:Extended-DOS Partition Int32h */
                    Byte    lastPartitionSector[3];
                    Byte    firstSectorNumbers[4];      /* first sector number (link to BPB sector) */
                    Byte    numberOfSectors[4];
            }   partitionTable[4];
            Byte    sig[2];                         /* 0x55, 0xaa */
    }   MBRecord;
    
    typedef struct FAT16BPB_t {
            /* FAT16 or FAT12 BPB */
            Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
            Byte    OEMName[8];
            /* FAT16 */
            Byte    bytesPerSector[2];      /* bytes/sector */
            Byte    sectorsPerCluster;      /* sectors/cluster */
            Byte    reservedSectors[2];     /* reserved sector, beginning with sector 0 */
            Byte    numberOfFATs;           /* file allocation table */
            Byte    rootEntries[2];         /* root entry (512) */
            Byte    totalSectors[2];        /* partion total secter */
            Byte    mediaDescriptor;        /* 0xf8: Hard Disk */
            Byte    sectorsPerFAT[2];       /* sector/FAT (FAT32 always zero: see bigSectorsPerFAT) */
            Byte    sectorsPerTrack[2];     /* sector/track (not use) */
            Byte    heads[2];               /* heads number (not use) */
            Byte    hiddenSectors[4];       /* hidden sector number */
            Byte    bigTotalSectors[4];     /* total sector number */
                                                                            /* info */
            Byte    driveNumber;
            Byte    unused;
            Byte    extBootSignature;
            Byte    serialNumber[4];
            Byte    volumeLabel[11];
            Byte    fileSystemType[8];      /* "FAT16   " */
            Byte    loadProgramCode[448];
            Byte    sig[2];                 /* 0x55, 0xaa */
    }   BPBlock; // Bios Parameter Block
    
    typedef struct DirEntry_t {
            Byte    name[8];            /* file name */
            Byte    extension[3];       /* file name extension */
            Byte    attribute;          /* file attribute
                                                                    bit 4    directory flag
                                                                    bit 3    volume flag
                                                                    bit 2    hidden flag
                                                                    bit 1    system flag
                                                                    bit 0    read only flag */
            Byte    reserved;           /* use NT or same OS */
            Byte    createTimeMs;       /* VFAT 10millsec (0   199) */
            Byte    createTime[2];      /* VFAT */
            Byte    createDate[2];      /* VFAT */
            Byte    accessDate[2];      /* VFAT */
            Byte    clusterHighWord[2]; /* FAT32 MSB 16 bits */
            Byte    updateTime[2];
            Byte    updateDate[2];
            Byte    cluster[2];         /* start cluster number */
            Byte    fileSize[4];        /* file size in bytes (directory is always zero) */
    }   DirEntry;
    
    #pragma anon_unions
    typedef struct _DirEntTime {
            union {
                    uint16_t W;
                    struct {
                            uint16_t second : 5;
                            uint16_t minutes : 6;
                            uint16_t hour : 5;
                    } B;
            };
    } DirEntTime;
    
    typedef struct _DirEntDate {
            union {
                    uint16_t W;
                    struct {
                            uint16_t day : 5;
                            uint16_t month : 4;
                            uint16_t year : 7;
                    } B;
            };
    } DirEntDate;
    #pragma no_anon_unions
    
  • 固定値: MBRとか、PBSとか。
    (てきとうなUSBフラッシュメモリで、パーティション切って、フォーマットして、ダンプして、必要なとこを入力)

    const MBRecord sectMBR = {
        .checkRoutionOnx86 = { 0x00 },
        .partitionTable = {
            {
                .bootDescriptor = 0x00,
                .firstPartitionSector = { 0x02, 0x21, 0x00 },
                .fileSystemDescriptor = 0x06, //FAT16
                .lastPartitionSector = { 0xC2, 0x22, 0x20 },
                .firstSectorNumbers = { 0x00, 0x08, 0x00, 0x00 },
                .numberOfSectors = { 0x00, 0x00, 0x08, 0x00 },
            },//[0]
            { 0 },//[1]
            { 0 },//[2]
            { 0 },//[3]
    },
    .sig = { 0x55, 0xAA },
    };
    const BPBlock sectBPB = {
        .jmpOpeCode = { 0xEB, 0x00, 0x90 },
        .OEMName = { \' \',\' \',\' \',\' \',\' \',\' \',\' \',\' \' },
        .bytesPerSector = { 0x00, 0x02 },
        .sectorsPerCluster = 0x08, // 4KB/sectors
        .reservedSectors = { 0x08, 0x00 },
        .numberOfFATs = 0x02,
        .rootEntries = { 0x00, 0x02 },
        .totalSectors = { 0x00, 0x00 },
        .mediaDescriptor = 0xF8, // HDD
        .sectorsPerFAT = { 0x00, 0x01 },
        .sectorsPerTrack = { 0x3F,0x00 },
        .heads = { 0xFF,0x00 },
        .hiddenSectors = { 0x00, 0x08, 0x00, 0x00 },
        .bigTotalSectors = { 0x00,0x00,0x08, 0x00 },
        .driveNumber = 0x80,
        .unused = 0,
        .extBootSignature = 0x29,
        .serialNumber = { 0x78,0x56,0x34,0x12 },
        .volumeLabel = { \'N\',\'O\',\' \',\'N\',\'A\',\'M\',\'E\',\' \',\' \',\' \',\' \' },
        .fileSystemType = { \'F\',\'A\',\'T\',\'1\',\'6\',\' \',\' \',\' \' },
        .loadProgramCode = { 0 },
        .sig = { 0x55, 0xAA },
    };
    
    #define SECTOR_MBR  (0x0000)
    #define SECTOR_PBR  (0x0800)
    #define SECTOR_FAT1 (0x0808)
    #define SECTOR_FAT2 (0x0908)
    #define SECTOR_ROOT (0x0A08)
    #define SECTOR_DATA (0x0A28)
    
  • セクタ読み出しで、それっぽいデータをわたすとこ。

    void _handleFatClusterChain(uint32_t sect_offset, uint8_t *buf)
    {
        uint16_t *bufW = (uint16_t *)&buf[0];
        if (sect_offset == 0)
        {
            bufW[0] = 0xfff8;
            bufW[1] = 0xffff;
            bufW[2] = 0xffff; //最初のファイル. 1クラスタでおわり.
        }
    }

    void _handleRoot(uint32_t sect_offset, uint8_t *buf)
    {
        // 1 sector(512bytes) has 16 entries
        DirEntry *pDir = (DirEntry *)buf;
        if (sect_offset == 0)
        {
            memset(pDir, 0x00, sizeof(DirEntry));
            sprintf((char *)pDir->name, "TEXT_123");
            pDir->extension[0] = \'T\';
            pDir->extension[1] = \'X\';
            pDir->extension[2] = \'T\';
            pDir->attribute = 0x00;
            {
                DirEntTime *pT = (DirEntTime *)&pDir->updateTime[0];
                DirEntDate *pD = (DirEntDate *)&pDir->updateDate[0];
                pT->B.hour = 12;
                pT->B.minutes = 34;
                pT->B.second = 56 / 2;
                pD->B.year = 2017 - 1980;
                pD->B.month = 1;
                pD->B.day = 12;
            }

            *(uint16_t*)&pDir->cluster = 0x0002;
            *(uint32_t*)&pDir->fileSize = 123;
        }
    }

    void _handleData(uint32_t sect_offset, uint8_t *buf)
    {
        memset(buf, \'A\', 512);
        sprintf((char *)buf, "Hello World!\\r\\n");
        buf[14]=\'>\';
    }

    uint32_t _ReadSector(uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    {
        switch (blk_addr)
        {
        case SECTOR_MBR:
            memcpy(buf, (const void *)&sectMBR, 512);
            break;
        case SECTOR_PBR:
            memcpy(buf, (const void *)&sectBPB, 512);
            break;
        default:
            memset(buf, 0x00, 512);
            //FAT cluster chain
            if ((SECTOR_FAT1 <= blk_addr) && (blk_addr < SECTOR_ROOT))
            {
                if (blk_addr >= SECTOR_FAT2) { blk_addr -= (SECTOR_FAT2 - SECTOR_FAT1); }
                _handleFatClusterChain(blk_addr - SECTOR_FAT1, buf);

            }
            else if ((SECTOR_ROOT <= blk_addr) && (blk_addr < SECTOR_DATA))
            {
                _handleRoot(blk_addr - SECTOR_ROOT, buf);

            }
            else if (SECTOR_DATA <= blk_addr)
            {
                _handleData(blk_addr - SECTOR_DATA, buf);

            }
            break;
        }
        return 0;
    }

その他

    • 4KB/clusterにしてるのは、STM32の内蔵FLASHが4KB/sectorなので。
      で、256MBくらいのパーティションで、FAT16フォーマットすると、4KB/clusterになる。
      (ファイルシステムのクラスタサイズと、フラッシュメモリの物理セクタサイズがちがうと、管理が大変だよね...;_;)
    • EEPROMにファイル情報(ROOTDIRに入るような情報=>FATチェーン生成)、FLASHにデータのみ、という感じで使用しようかと。

 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

好了现在我们动手吧。现在只要修改usbd_storage_if.C文件

/**
  ******************************************************************************
  * @file           : usbd_storage_if.c
  * @brief          : Memory management layer
  ******************************************************************************
  * This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * Copyright (c) 2017 STMicroelectronics International N.V.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other
  *    contributors to this software may be used to endorse or promote products
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under
  *    this license is void and will automatically terminate your rights under
  *    this license.
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/
#include "usbd_storage_if.h"
/* USER CODE BEGIN INCLUDE */
/* USER CODE END INCLUDE */

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @{
  */

/** @defgroup USBD_STORAGE
  * @brief usbd core module
  * @{
  */

/** @defgroup USBD_STORAGE_Private_TypesDefinitions
  * @{
  */
/* USER CODE BEGIN PRIVATE_TYPES */
/* USER CODE END PRIVATE_TYPES */
/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Defines
  * @{
  */
#define STORAGE_LUN_NBR                  1
#define STORAGE_BLK_NBR                  0x81000
#define STORAGE_BLK_SIZ                  0x200

/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_Macros
  * @{
  */
/* USER CODE BEGIN PRIVATE_MACRO */
/* USER CODE END PRIVATE_MACRO */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_IF_Private_Variables
  * @{
  */
/* USER CODE BEGIN INQUIRY_DATA_FS */
/* USB Mass storage Standard Inquiry Data */
const int8_t  STORAGE_Inquirydata_FS[] =  /* 36 */
{

    /* LUN 0 */
    0x00,
    0x80,
    0x02,
    0x02,
    (STANDARD_INQUIRY_DATA_LEN - 5),
    0x00,
    0x00,
    0x00,
    \'S\', \'T\', \'M\', \' \', \' \', \' \', \' \', \' \', /* Manufacturer : 8 bytes */
    \'P\', \'r\', \'o\', \'d\', \'u\', \'c\', \'t\', \' \', /* Product      : 16 Bytes */
    \' \', \' \', \' \', \' \', \' \', \' \', \' \', \' \',
    \'0\', \'.\', \'0\' ,\'1\',                     /* Version      : 4 Bytes */
};
/* USER CODE END INQUIRY_DATA_FS */

/* USER CODE BEGIN PRIVATE_VARIABLES */
/* USER CODE END PRIVATE_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_IF_Exported_Variables
  * @{
  */
extern USBD_HandleTypeDef hUsbDeviceFS;
/* USER CODE BEGIN EXPORTED_VARIABLES */
/* USER CODE END EXPORTED_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_STORAGE_Private_FunctionPrototypes
  * @{
  */
static int8_t STORAGE_Init_FS (uint8_t lun);
static int8_t STORAGE_GetCapacity_FS (uint8_t lun,
                                      uint32_t *block_num,
                                      uint16_t *block_size);
static int8_t  STORAGE_IsReady_FS (uint8_t lun);
static int8_t  STORAGE_IsWriteProtected_FS (uint8_t lun);
static int8_t STORAGE_Read_FS (uint8_t lun,
                               uint8_t *buf,
                               uint32_t blk_addr,
                               uint16_t blk_len);
static int8_t STORAGE_Write_FS (uint8_t lun,
                                uint8_t *buf,
                                uint32_t blk_addr,
                                uint16_t blk_len);
static int8_t STORAGE_GetMaxLun_FS (void);

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
#include <stdint.h>
typedef uint8_t Byte;
typedef struct MasterBootRecord
{
    Byte    checkRoutionOnx86[446];
    struct
    {
        Byte    bootDescriptor;             /* 0x80: bootable device, 0x00: non-bootable */
        Byte    firstPartitionSector[3];    /* 1st sector number */
        Byte    fileSystemDescriptor;       /* 1:FAT12, 4:FAT16(less than 32MB), 5:Extended-DOS Partition,
                                                                                        6:FAT16(more 32MB), 0xb:FAT32(more 2GB),
                                                                                        0xc:FAT32 Int32h, 0xe:FAT16 Int32h,
                                                                                        0xf:5:Extended-DOS Partition Int32h */
        Byte    lastPartitionSector[3];
        Byte    firstSectorNumbers[4];      /* first sector number (link to BPB sector) */
        Byte    numberOfSectors[4];
    }   partitionTable[4];
    Byte    sig[2];                         /* 0x55, 0xaa */
}   MBRecord;

typedef struct FAT16BPB_t
{
    /* FAT16 or FAT12 BPB */
    Byte    jmpOpeCode[3];          /* 0xeb ?? 0x90 */
    Byte    OEMName[8];
    /* FAT16 */
    Byte    bytesPerSector[2];      /* bytes/sector */
    Byte    sectorsPerCluster;      /* sectors/cluster */
    Byte    reservedSectors[2];     /* reserved sector, beginning with sector 0 */
    Byte    numberOfFATs;           /* file allocation table */
    Byte    rootEntries[2];         /* root entry (512) */
    Byte    totalSectors[2];        /* partion total secter */
    Byte    mediaDescriptor;        /* 0xf8: Hard Disk */
    Byte    sectorsPerFAT[2];       /* sector/FAT (FAT32 always zero: see bigSectorsPerFAT) */
    Byte    sectorsPerTrack[2];     /* sector/track (not use) */
    Byte    heads[2];               /* heads number (not use) */
    Byte    hiddenSectors[4];       /* hidden sector number */
    Byte    bigTotalSectors[4];     /* total sector number */
    /* info */
    Byte    driveNumber;
    Byte    unused;
    Byte    extBootSignature;
    Byte    serialNumber[4];
    Byte    volumeLabel[11];
    Byte    fileSystemType[8];      /* "FAT16   " */
    Byte    loadProgramCode[448];
    Byte    sig[2];                 /* 0x55, 0xaa */
}   BPBlock; // BIOS Parameter Block

typedef struct DirEntry_t
{
    Byte    name[8];            /* file name */
    Byte    extension[3];       /* file name extension */
    Byte    attribute;          /* file attribute
                                                                bit 4    directory flag
                                                                bit 3    volume flag
                                                                bit 2    hidden flag
                                                                bit 1    system flag
                                                                bit 0    read only flag */
    Byte    reserved;           /* use NT or same OS */
    Byte    createTimeMs;       /* VFAT 10millsec (0   199) */
    Byte    createTime[2];      /* VFAT */
    Byte    createDate[2];      /* VFAT */
    Byte    accessDate[2];      /* VFAT */
    Byte    clusterHighWord[2]; /* FAT32 MSB 16 bits */
    Byte    updateTime[2];
    Byte    updateDate[2];
    Byte    cluster[2];         /* start cluster number */
    Byte    fileSize[4];        /* file size in bytes (directory is always zero) */
}   DirEntry;

#pragma anon_unions
typedef struct _DirEntTime
{
    union
    {
        uint16_t W;
        struct
        {
            uint16_t second : 5;
            uint16_t minutes : 6;
            uint16_t hour : 5;
        } B;
    };
} DirEntTime;

typedef struct _DirEntDate
{
    union
    {
        uint16_t W;
        struct
        {
            uint16_t day : 5;
            uint16_t month : 4;
            uint16_t year : 7;
        } B;
    };
} DirEntDate;
#pragma no_anon_unions


const MBRecord sectMBR =
{
    .checkRoutionOnx86 = { 0x00 },
    .partitionTable = {
        {
            .bootDescriptor = 0x00,
            .firstPartitionSector = { 0x02, 0x21, 0x00 },
            .fileSystemDescriptor = 0x06, //FAT16
            .lastPartitionSector = { 0xC2, 0x22, 0x20 },
            .firstSectorNumbers = { 0x00, 0x08, 0x00, 0x00 },
            .numberOfSectors = { 0x00, 0x00, 0x08, 0x00 },
        },//[0]
        { 0 },//[1]
        { 0 },//[2]
        { 0 },//[3]
    },
    .sig = { 0x55, 0xAA },
 };
const BPBlock sectBPB =
{
    .jmpOpeCode = { 0xEB, 0x00, 0x90 },
    .OEMName = { \' \',\' \',\' \',\' \',\' \',\' \',\' \',\' \' },
    .bytesPerSector = { 0x00, 0x02 },
    .sectorsPerCluster = 0x08, // 4KB/sectors
    .reservedSectors = { 0x08, 0x00 },
    .numberOfFATs = 0x02,
    .rootEntries = { 0x00, 0x02 },
    .totalSectors = { 0x00, 0x00 },
    .mediaDescriptor = 0xF8, // HDD
    .sectorsPerFAT = { 0x00, 0x01 },
    .sectorsPerTrack = { 0x3F,0x00 },
    .heads = { 0xFF,0x00 },
    .hiddenSectors = { 0x00, 0x08, 0x00, 0x00 },
    .bigTotalSectors = { 0x00,0x00,0x08, 0x00 },
    .driveNumber = 0x80,
    .unused = 0,
    .extBootSignature = 0x29,
    .serialNumber = { 0x78,0x56,0x34,0x12 },
    .volumeLabel = { \'N\',\'O\',\' \',\'N\',\'A\',\'M\',\'E\',\' \',\' \',\' \',\' \' },
    .fileSystemType = { \'F\',\'A\',\'T\',\'1\',\'6\',\' \',\' \',\' \' },
    .loadProgramCode = { 0 },
    .sig = { 0x55, 0xAA },
};

#define SECTOR_MBR  (0x0000)
#define SECTOR_PBR  (0x0800)
#define SECTOR_FAT1 (0x0808)
#define SECTOR_FAT2 (0x0908)
#define SECTOR_ROOT (0x0A08)
#define SECTOR_DATA (0x0A28)

void _handleFatClusterChain(uint32_t sect_offset, uint8_t *buf)
{
    uint16_t *bufW = (uint16_t *)&buf[0];
    if (sect_offset == 0STM32CubeMX USB CDC VCP?

STM32CubeMX学习笔记(45)——USB接口使用(HID鼠标)

STM32CubeMX学习笔记(44)——USB接口使用(HID按键)

STM32CubeMX学习笔记(44)——USB接口使用(HID按键)

STM32CubeMX学习笔记(45)——USB接口使用(HID鼠标)

利用STM32CubeMX来生成USB_HID_Mouse工程添加ADC