基于小华例程3.2版本USB之usb_dev_cdc_msc工程深入代码详解

本文介绍了USB区分CDC和MSC功能的原理,包括通过描述符、设备类、接口描述符和端点来区分。还对同时具有MSC和CDC功能的USB设备配置描述符代码进行详细解释,阐述了接口和端点的关系,最后提及处理数据输入输出及后续计划写fat32移植与USB结合的程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有了前面两篇的基础,再看这个例程,就轻松多了。很多项目中,USB需要cdc串口和msc功能同时有效,而官方给的这个例程,简直就是救命一样。

第一个疑问就是USB如何区分CDC和MSC的呢?

USB(通用串行总线)设备可以通过其设备类和子类来区分不同的功能,例如CDC(通信设备类)和MSC(大规模存储设备类)。以下是USB设备如何区分CDC和MSC功能的基本原理:
1. **描述符**:USB设备通过其描述符来告诉主机它的功能和属性。描述符是一系列数据结构,包括设备描述符、配置描述符、接口描述符和端点描述符等。
2. **设备类**:在设备描述符中,有一个字段指定了设备的类别。CDC和MSC分别有不同的类代码。
   - CDC(Communication Device Class):CDC类代码是0x02。它用于各种通信设备,如调制解调器、网络适配器等。
   - MSC(Mass Storage Class):MSC类代码是0x08。它用于存储设备,如U盘、外置硬盘等。
3. **接口描述符**:在接口描述符中,会进一步定义该接口的子类和协议。例如,CDC设备可能有多个接口,每个接口都有自己的子类和协议代码,用于描述其具体功能(如AT命令接口、数据接口等)。
4. **端点**:端点是设备与主机之间进行数据传输的通道。CDC和MSC设备会使用不同的端点进行数据传输。例如,CDC设备可能有一个中断端点用于传输控制信息,而MSC设备则可能使用批量端点进行大量数据的传输。
当USB设备连接到主机时,主机通过查询设备描述符、配置描述符、接口描述符等,来识别设备的功能并加载适当的驱动程序。这样,主机就能根据设备的类和子类来区分它是CDC设备还是MSC设备,并为用户提供相应的功能。

 

言归正传,首先来看一下项目主要文件:

还是挺熟悉的,USB分为两个部分,source里面放每个工程不一样的东西,usb里面放置共性的东西,虽然是共性,但是移植到自己工程中的时候,有些仍然是需要修改的。

从mian函数开始,最大的不同是这个地方:usb_dev_composite_cbk,定义了一个复合类。

extern usb_dev_class_func usb_dev_composite_cbk;
usb_core_instance  usb_dev;

int32_t main(void)
{
    stc_usb_port_identify stcPortIdentify;
    stcPortIdentify.u8CoreID = USBFS_CORE_ID;
    usb_dev_init(&usb_dev, &stcPortIdentify, &user_desc, &usb_dev_composite_cbk, &user_cb);
    for (;;) {
    }
}

这个符合类的定义:

/* USB设备复合功能回调函数结构体定义 */
usb_dev_class_func usb_dev_composite_cbk = {
    /* 初始化USB设备MSC/CDC类 */
    &usb_dev_msc_cdc_init,
    /* 销毁USB设备MSC/CDC类 */
    &usb_dev_msc_cdc_deinit,
    /* 处理USB设备MSC/CDC类的设置请求 */
    &usb_dev_msc_cdc_setup,
    /* 预留的NULL指针,用于未来的扩展 */
    NULL,
    /* 处理USB设备MSC/CDC类的Control Endpoint接收就绪事件 */
    &usb_dev_msc_cdc_ctrlep_rxready,
    /* 获取USB设备MSC/CDC类的配置描述符 */
    &usb_dev_msc_cdc_getcfgdesc,
    /* 处理USB设备MSC/CDC类的SOF事件 */
    &usb_dev_msc_cdc_sof,
    /* 处理USB设备MSC/CDC类的数据IN事件 */
    &usb_dev_msc_cdc_datain,
    /* 处理USB设备MSC/CDC类的数据OUT事件 */
    &usb_dev_msc_cdc_dataout,
    /* 预留的NULL指针,用于未来的扩展 */
    NULL,
    /* 预留的NULL指针,用于未来的扩展 */
    NULL,
};

里面最大的不同是:usb_dev_msc_cdc_getcfgdesc这个描述符函数:

/**
 * @brief  get the configuration descriptor
 * @param  [in] length      length of data butter in bytes
 * @retval buffer pointer
 */
uint8_t *usb_dev_msc_cdc_getcfgdesc(uint16_t *length)
{
    *length = (uint16_t)sizeof(usb_dev_msc_cdc_cfgdesc);
    return usb_dev_msc_cdc_cfgdesc;
}

里面使用了usb_dev_msc_cdc_cfgdesc的描述定义:根据注释能看到详细的含义,基本就是将CDC和MSC两个描述合在了一起。

__USB_ALIGN_BEGIN static uint8_t usb_dev_msc_cdc_cfgdesc[USB_MSC_CDC_CONFIG_DESC_SIZ] = {
    0x09,                          /* bLength: Configuration Descriptor size */
    USB_CFG_DESCRIPTOR_TYPE,       /* bDescriptorType: Configuration */
    USB_MSC_CDC_CONFIG_DESC_SIZ,   /* wTotalLength: Bytes returned */
    0x00,
    0x03,                          /* bNumInterfaces: 2 interface */
    0x01,                          /* bConfigurationValue: Configuration value */
    0x00,                          /* iConfiguration: Index of string descriptor describing the configuration */
    0xC0,                          /* bmAttributes: bus powered and Support Remote Wake-up */
    0x32,                          /* MaxPower 100 mA: this current is used for detecting Vbus */

    /* IAD for CDC configuration descriptor */
    0x08,                          /* bLength */
    0x0B,                          /* bDescriptorType */
    0x00,                          /* bFirstInterface */
    0x02,                          /* bInterfaceCount */
    0x02,                          /* bFunctionClass */
    0x02,                          /* bFunctionSubClass */
    0x01,                          /* bFunctionProtocol */
    0x00,                          /* iFunction (Index of string descriptor describing this function) */
    /* CDC configuration descriptor */
    /* interface descriptor */
    0x09,                          /* bLength: Interface Descriptor size */
    0x04,                          /* bDescriptorType: Interface */
    0x00,                          /* bInterfaceNumber: Number of Interface */
    0x00,                          /* bAlternateSetting: Alternate setting */
    0x01,                          /* bNumEndpoints: One endpoints used */
    0x02,                          /* bInterfaceClass: Communication Interface Class */
    0x02,                          /* bInterfaceSubClass: Abstract Control Model */
    0x01,                          /* bInterfaceProtocol: Common AT commands */
    0x00,                          /* iInterface: */
    /* Header Functional Descriptor */
    0x05,                          /* bLength: Endpoint Descriptor size */
    0x24,                          /* bDescriptorType: CS_INTERFACE */
    0x00,                          /* bDescriptorSubtype: Header Func Desc */
    0x10,                          /* bcdCDC: spec release number */
    0x01,
    /* Call Management Functional Descriptor */
    0x05,                          /* bFunctionLength */
    0x24,                          /* bDescriptorType: CS_INTERFACE */
    0x01,                          /* bDescriptorSubtype: Call Management Func Desc */
    0x00,                          /* bmCapabilities: D0+D1 */
    0x01,                          /* bDataInterface: 1 */
    /* ACM Functional Descriptor */
    0x04,                          /* bFunctionLength */
    0x24,                          /* bDescriptorType: CS_INTERFACE */
    0x02,                          /* bDescriptorSubtype: Abstract Control Management desc */
    0x02,                          /* bmCapabilities */
    /* Union Functional Descriptor */
    0x05,                          /* bFunctionLength */
    0x24,                          /* bDescriptorType: CS_INTERFACE */
    0x06,                          /* bDescriptorSubtype: Union func desc */
    0x00,                          /* bMasterInterface: Communication class interface */
    0x01,                          /* bSlaveInterface0: Data Class Interface */
    /* Endpoint 2 Descriptor */
    0x07,                           /* bLength: Endpoint Descriptor size */
    USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
    CDC_CMD_EP,                     /* bEndpointAddress */
    0x03,                           /* bmAttributes: Interrupt */
    LOBYTE(CDC_CMD_PACKET_SIZE),     /* wMaxPacketSize: */
    HIBYTE(CDC_CMD_PACKET_SIZE),
    0xFF,                           /* bInterval: */
    /* Data class interface descriptor */
    0x09,                           /* bLength: Endpoint Descriptor size */
    USB_INTERFACE_DESCRIPTOR_TYPE,  /* bDescriptorType: */
    0x01,                           /* bInterfaceNumber: Number of Interface */
    0x00,                           /* bAlternateSetting: Alternate setting */
    0x02,                           /* bNumEndpoints: Two endpoints used */
    0x0A,                           /* bInterfaceClass: CDC */
    0x00,                           /* bInterfaceSubClass: */
    0x00,                           /* bInterfaceProtocol: */
    0x00,                           /* iInterface: */
    /* Endpoint OUT Descriptor */
    0x07,                           /* bLength: Endpoint Descriptor size */
    USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
    CDC_OUT_EP,                     /* bEndpointAddress */
    0x02,                           /* bmAttributes: Bulk */
    LOBYTE(MAX_CDC_PACKET_SIZE),    /* wMaxPacketSize: */
    HIBYTE(MAX_CDC_PACKET_SIZE),
    0x00,                           /* bInterval: ignore for Bulk transfer */
    /* Endpoint IN Descriptor */
    0x07,                           /* bLength: Endpoint Descriptor size */
    USB_ENDPOINT_DESCRIPTOR_TYPE,   /* bDescriptorType: Endpoint */
    CDC_IN_EP,                      /* bEndpointAddress */
    0x02,                           /* bmAttributes: Bulk */
    LOBYTE(MAX_CDC_PACKET_SIZE),    /* wMaxPacketSize: */
    HIBYTE(MAX_CDC_PACKET_SIZE),
    0x00,

    /* Mass Storage interface ********************/
    0x09,                           /* bLength: Interface Descriptor size */
    0x04,                           /* bDescriptorType: */
    0x02,                           /* bInterfaceNumber: Number of Interface */
    0x00,                           /* bAlternateSetting: Alternate setting */
    0x02,                           /* bNumEndpoints*/
    0x08,                           /* bInterfaceClass: MSC Class */
    0x06,                           /* bInterfaceSubClass : SCSI transparent*/
    0x50,                           /* nInterfaceProtocol */
    0x05,                           /* iInterface: */
    /* Mass Storage Endpoints ********************/
    0x07,                           /* Endpoint descriptor length = 7 */
    0x05,                           /* Endpoint descriptor type */
    MSC_IN_EP,                      /* Endpoint address (IN, address 1) */
    0x02,                           /* Bulk endpoint type */
    LOBYTE(MSC_MAX_PACKET),
    HIBYTE(MSC_MAX_PACKET),
    0x00,                           /* Polling interval in milliseconds */

    0x07,                           /* Endpoint descriptor length = 7 */
    0x05,                           /* Endpoint descriptor type */
    MSC_OUT_EP,                     /* Endpoint address (OUT, address 1) */
    0x02,                           /* Bulk endpoint type */
    LOBYTE(MSC_MAX_PACKET),
    HIBYTE(MSC_MAX_PACKET),
    0x00                            /* Polling interval in milliseconds*/
} ;

以下是上面代码的详细解释(需要结合USB协议):

这段代码定义了一个USB配置描述符数组,用于描述一个同时具有MSC(大规模存储设备类)和CDC(通信设备类)功能的USB设备的配置。下面是对代码中每个字段的解释:
1. **配置描述符**:
   - `0x09`:配置描述符的大小(9字节)。
   - `USB_CFG_DESCRIPTOR_TYPE`:配置描述符类型(0x02)。
   - `USB_MSC_CDC_CONFIG_DESC_SIZ`:整个配置描述符集合的大小。
   - `0x00`:配置的索引。
   - `0x03`:此配置中的接口数(一个用于MSC,一个用于CDC)。
   - `0x01`:配置的编号。
   - `0x00`:字符串描述符的索引,用于描述这个配置。
   - `0xC0`:配置的属性(总线供电,支持远程唤醒)。
   - `0x32`:最大功耗(以2mA为单位)。
2. **IAD(接口关联描述符)**:
   - `0x08`:IAD描述符的大小(8字节)。
   - `0x0B`:IAD描述符类型(0x0B)。
   - `0x00`:此IAD的起始接口编号。
   - `0x02`:在此IAD中包含的接口数。
   - `0x02`:类代码(CDC)。
   - `0x02`:子类代码。
   - `0x01`:协议代码。
   - `0x00`:字符串描述符的索引,用于描述这个功能。
3. **CDC配置描述符**:
   - `0x09`:接口描述符的大小(9字节)。
   - `0x04`:接口描述符类型(0x04)。
   - `0x00`:接口的编号。
   - `0x00`:备用设置编号。
   - `0x01`:端点的数量。
   - `0x02`:接口的类代码(通信接口类)。
   - `0x02`:接口的子类代码(抽象控制模型)。
   - `0x01`:接口的协议代码(通用AT命令)。
   - `0x00`:字符串描述符的索引,用于描述这个接口。
4. **CDC功能描述符**:
   - 接下来的几个描述符(类型为0x24)是CDC类特定的功能描述符,包括头功能描述符、呼叫管理功能描述符、ACM功能描述符和联盟功能描述符。
5. **CDC端点描述符**(命令端点):
   - `0x07`:端点描述符的大小(7字节)。
   - `USB_ENDPOINT_DESCRIPTOR_TYPE`:端点描述符类型(0x05)。
   - `CDC_CMD_EP`:命令端点的地址。
   - `0x03`:端点的属性(中断传输)。
   - `LOBYTE(CDC_CMD_PACKET_SIZE)` 和 `HIBYTE(CDC_CMD_PACKET_SIZE)`:端点的最大包大小。
   - `0xFF`:端点的间隔(以帧为单位)。
6. **数据类接口描述符**:
   - `0x09`:接口描述符的大小(9字节)。
   - `USB_INTERFACE_DESCRIPTOR_TYPE`:接口描述符类型(0x04)。
   - `0x01`:接口的编号。
   - `0x00`:备用设置编号。
   - `0x02`:端点的数量。
   - `0x0A`:接口的类代码(CDC)。
   - `0x00`:接口的子类代码。
   - `0x00`:接口的协议代码。
   - `0x00`:字符串描述符的索引,用于描述这个接口。
7. **数据类端点描述符**(输出和输入):
   - `0x07`:端点描述符的大小(7字节)。
   - `USB_ENDPOINT_DESCRIPTOR_TYPE`:端点描述符类型(0x05)。
   - `CDC_OUT_EP` 和 `CDC_IN_EP`:输出和输入端点的地址。
   - `0x02`:端点的属性(批量传输)。
   - `LOBYTE(MAX_CDC_PACKET_SIZE)` 和 `HIBYTE(MAX_CDC_PACKET_SIZE)`:端点的最大包大小。
   - `0x00`:端点的间隔(对于批量端点总是0)。
8. **MSC接口描述符**:
   - `0x09`:接口描述符的大小(9字节)。
   - `0x04`:接口描述符类型(0x04)。
   - `0x02`:接口的编号。
   - `0x00`:备用设置编号。
   - `0x02`:端点的数量。
   - `0x08`:接口的类代码(MSC)。
   - `0x06`:接口的子类代码(SCSI透明)。
   - `0x50`:接口的协议代码。
   - `0x05`:字符串描述符的索引,用于描述这个接口。
9. **MSC端点描述符**(输入和输出):
   - `0x07`:端点描述符的大小(7字节)。
   - `0x05`:端点描述符类型(0x05)。
   - `MSC_IN_EP` 和 `MSC_OUT_EP`:输入和输出端点的地址。
   - `0x02`:端点的属性(批量传输)。
   - `LOBYTE(MSC_MAX_PACKET)` 和 `HIBYTE(MSC_MAX_PACKET)`:端点的最大包大小。
   - `0x00`:端点的间隔(对于批量端点总是0)。
这个配置描述符定义了两个接口:一个用于MSC设备,用于存储设备的读写操作;另一个用于CDC设备,用于数据通信。每个接口都有自己的端点描述符,用于定义数据如何通过USB传输。MSC设备通常使用批量端点进行大量数据的传输,而CDC设备使用中断端点进行命令和控制信息的传输。

继续看,基本的初始化和deinit:就是将cdc和msc都操作一下。

/**
 * @brief  Initialize the composite device of MSC-HID interface
 * @param  [in] pdev        device instance
 * @retval None
 */
void usb_dev_msc_cdc_init(void *pdev)
{
    usb_dev_cdc_init(pdev);
    usb_dev_msc_init(pdev);
}

/**
 * @brief  de-initialize the composite device of MSC-HID interface
 * @param  [in] pdev        device instance
 * @retval None
 */
void usb_dev_msc_cdc_deinit(void *pdev)
{
    usb_dev_cdc_deinit(pdev);
    usb_dev_msc_deinit(pdev);
}

然后是setup函数,就是主机给设备设置需要做的事情:

/**
 * @brief  process the setup requests
 * @param  [in] pdev        device instance
 * @param  [in] req         setup request
 * @retval status
 */
uint8_t usb_dev_msc_cdc_setup(void *pdev, USB_SETUP_REQ *req)
{
    uint8_t u8Res = USB_DEV_OK;
    switch (req->bmRequest & USB_REQ_RECIPIENT_MASK) {
        case USB_REQ_RECIPIENT_INTERFACE:
            if (req->wIndex == 0x02U) {
                u8Res = usb_dev_msc_setup(pdev, req);
            } else {
                u8Res = usb_dev_cdc_setup(pdev, req);
            }
            break;

        case USB_REQ_RECIPIENT_ENDPOINT:
            if (req->wIndex == CDC_IN_EP) {
                u8Res = usb_dev_cdc_setup(pdev, req);
            } else {
                u8Res = usb_dev_msc_setup(pdev, req);
            }
            break;
        default:
            break;
    }
    return u8Res;
}

该C函数名为usb_dev_msc_cdc_setup(), 其主要功能是处理USB设备接收到的设置请求(setup requests)。函数通过判断请求类型及特定参数,将请求分发到相应的处理函数进行进一步处理,并返回处理结果的状态码。以下是该函数的具体工作流程:

1. 输入参数

  • void *pdev: 指向设备实例的指针,通常包含设备的相关状态信息和操作接口。
  • USB_SETUP_REQ *req: 指向USB设置请求结构体的指针,该结构体包含请求的详细信息,如请求类型、接收方、索引值等。

2. 请求接收方判断 首先,函数检查请求中的bmRequest字段与USB_REQ_RECIPIENT_MASK进行按位与操作,以确定请求的目标接收方。根据USB标准,该字段可以标识请求是针对设备、接口、端点还是其他类型的接收方。在此函数中,关注以下两种情况:

  • USB_REQ_RECIPIENT_INTERFACE: 请求目标为某个接口。
  • USB_REQ_RECIPIENT_ENDPOINT: 请求目标为某个端点。

3. 接口索引判断 当请求目标为接口(USB_REQ_RECIPIENT_INTERFACE)时,函数进一步检查请求的wIndex字段。该字段通常表示接口或端点的索引值。

  • 如果wIndex等于0x02,则将请求转发给usb_dev_msc_setup()函数处理,此函数负责处理针对指定接口(索引为0x02)的USB MSC(Mass Storage Class)相关设置请求。
  • 否则,将请求转发给usb_dev_cdc_setup()函数处理,此函数负责处理除上述接口外的其他接口的USB CDC(Communication Device Class)相关设置请求。

4. 端点索引判断 当请求目标为端点(USB_REQ_RECIPIENT_ENDPOINT)时,同样检查请求的wIndex字段。

  • 如果wIndex等于CDC_IN_EP常量(表示特定的CDC类输入端点),则将请求转发给usb_dev_cdc_setup()函数处理。
  • 否则,将请求转发给usb_dev_msc_setup()函数处理,处理非特定CDC类输入端点的设置请求。

5. 返回处理结果 无论请求被哪个处理函数处理,都会返回一个状态码。这个状态码由处理函数生成并返回给usb_dev_msc_cdc_setup()函数。该函数最终返回这个状态码,作为对原始设置请求的响应结果。

总结来说,usb_dev_msc_cdc_setup()函数作为USB设备设置请求的分发器,根据请求的接收方(接口或端点)及其索引值,将请求定向发送到对应的MSC或CDC类设置请求处理函数,并返回处理结果的状态码。

补充一下接口和端口有什么关系?

在USB(Universal Serial Bus)架构中,端点(Endpoint)和接口(Interface)是两个核心概念,它们分别在数据传输和设备功能组织方面起着关键作用。下面是它们的异同点:

相同点:

  1. 共同构成设备结构: 端点和接口都是USB设备描述的一部分,共同构建了设备的逻辑结构,使得主机能够理解和与设备进行有效的交互。

  2. 服务于设备功能: 无论是端点还是接口,其存在的目的是为了实现特定的设备功能。例如,一个音频设备可能包含多个接口来分别支持麦克风输入、扬声器输出等不同功能,而每个接口下又有端点负责实际的数据传输。

不同点:

端点(Endpoint):

  1. 数据传输实体: 端点是USB数据传输的基本单位,是设备内部与主机进行数据交换的具体通道。每个端点都有一个唯一的标识(端点号),并具有特定的方向(IN或OUT),用于表示数据是从主机流向设备(IN)还是从设备流向主机(OUT)。

  2. 支持传输模式: 端点支持不同的传输模式,如控制传输、中断传输、批量传输和同步传输,以适应不同类型的数据传输需求(如命令交互、周期性状态更新、大数据块传输、定时敏感数据流等)。

  3. 硬件资源关联: 端点通常与设备内部的硬件资源(如FIFO缓冲区、DMA控制器等)紧密关联,用于暂存和管理与主机之间传输的数据。

  4. 配置可选: 在同一接口内,端点的数量、类型和特性可以灵活配置,以满足特定接口功能的需求。

接口(Interface):

  1. 功能单元划分: 接口是设备功能的逻辑分组,它将设备的不同功能模块(如音频输入、视频输出、人机接口设备等)进行隔离和区分。每个接口代表一种独立的功能集,并且可以被单独启用、禁用或重置。

  2. 包含端点集合: 接口下包含一个或多个端点,这些端点协同工作以实现接口所定义的功能。端点在接口内的编号通常是连续的,并且端点的类型和特性应与接口的功能相匹配。

  3. 类/协议/描述符: 接口与USB设备类规范关联,定义了设备属于哪个类别(如音频、HID、CDC等),遵循何种类特定协议,并通过接口描述符详细描述其属性、端点配置等信息。

  4. 多接口设备支持: 单个USB设备可以包含多个接口,这些接口通过不同的接口编号加以区分。多接口设计允许设备提供多种互不干扰的功能,主机可以根据需要选择启用其中的一个或多个接口。

综上所述,端点和接口在USB体系中各自承担不同的职责。端点作为数据传输的实际通道,关注于如何有效地收发数据;而接口则更侧重于组织和描述设备的功能模块,它将相关的端点集合在一起,形成具有特定功能的服务单元。在代码片段 case USB_REQ_RECIPIENT_INTERFACE: 中,这表明处理的是针对某个接口的USB请求,可能是查询接口状态、修改接口设置或者与接口关联的端点进行交互等操作。

接下来是处理数据的输入输出:这个就是根据端点来判断是cdc还是msc。

/**
 * @brief  process data IN DATA
 * @param  [in] pdev        device instance
 * @param  [in] epnum       endpoint index
 * @retval None
 */
void usb_dev_msc_cdc_datain(void *pdev, uint8_t epnum)
{
    if (epnum == (uint8_t)(MSC_IN_EP & ((uint8_t)~0x80U))) {
        usb_dev_msc_datain(pdev, epnum);
    } else {
        usb_dev_cdc_datain(pdev, epnum);
    }
}

/**
 * @brief  process data OUT DATA
 * @param  [in] pdev        device instance
 * @param  [in] epnum       endpoint index
 * @retval None
 */
void usb_dev_msc_cdc_dataout(void *pdev, uint8_t epnum)
{
    if (epnum == (uint8_t)(MSC_OUT_EP & ((uint8_t)~0x80U))) {
        usb_dev_msc_dataout(pdev, epnum);
    } else {
        usb_dev_cdc_dataout(pdev, epnum);
    }
}

 在usb_app_conf.h中定义了这些端点,这个端点在USB配置描述里面引用。

/* USB DEVICE ENDPOINT CONFIGURATION */
#define MSC_IN_EP             (0x81U)
#define MSC_OUT_EP            (0x01U)

#define CDC_IN_EP             (0x82U)
#define CDC_OUT_EP            (0x02U)
#define CDC_CMD_EP            (0x83U)

其余就跟另外两个例程差不多了。下一步就是根据例程,实现自己的功能了。后面再写一篇fat32的移植和USB一起使用的程序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值