打印
[应用方案]

修完484跑"linux",秀个基础一些的,跑USB设备

[复制链接]
1307|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
vsfopen|  楼主 | 2019-12-25 15:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 vsfopen 于 2019-12-25 21:47 编辑

实现一个CDC,大部分开发方式下,还需要去研究很多CDC的细节,特别是各种描述符,一有错误,还无法识别。好吧,其实,描述符啥的,从来就不是个事,直接上代码,非官方USB协议栈:

#include "vsf.h"

struct usrapp_const_t {
    struct {
        uint8_t dev_desc[USB_DT_DEVICE_SIZE];
        uint8_t config_desc[USB_DT_CONFIG_SIZE + USB_DESC_CDC_ACM_IAD_LEN];
        uint8_t str_lanid[4];
        uint8_t str_vendor[20];
        uint8_t str_product[26];
        uint8_t str_cdc[14];
        vk_usbd_desc_t std_desc[6];
    } usbd;
};
typedef struct usrapp_const_t usrapp_const_t;

struct usrapp_t {
    struct {
        struct {
            vk_usbd_cdcacm_t param;
        } cdc;

        vk_usbd_ifs_t ifs[USB_CDC_IFS_NUM];
        vk_usbd_cfg_t config[1];
        vk_usbd_dev_t dev;
    } usbd;
};
typedef struct usrapp_t usrapp_t;

static const usrapp_const_t usrapp_const = {
    .usbd                       = {
        .dev_desc               = {
            USB_DESC_DEV_IAD(64, APP_CFG_USBD_VID, APP_CFG_USBD_PID, 1, 2, 0, 1)
        },
        .config_desc            = {
            USB_DESC_CFG(sizeof(usrapp_const.usbd.config_desc), 2 * USRAPP_CFG_CDC_NUM, 1, 0, 0x80, 100)
            USB_DESC_CDC_UART_HS_IAD(0, 4, 2, 1, 1)
        },
        .str_lanid              = {
            USB_DESC_STRING(2, 0x09, 0x04)
        },
        .str_vendor             = {
            USB_DESC_STRING(18,
                'S', 0, 'i', 0, 'm', 0, 'o', 0, 'n', 0, 'Q', 0, 'i', 0, 'a', 0,
                'n', 0
            )
        },
        .str_product            = {
            USB_DESC_STRING(24,
                'V', 0, 'S', 0, 'F', 0, '-', 0, 'M', 0, 'u', 0, 'l', 0, 't', 0,
                'i', 0, 'C', 0, 'D', 0, 'C', 0
            )
        },
        .str_cdc                = {
            USB_DESC_STRING(12,
                'V', 0, 'S', 0, 'F', 0, 'C', 0, 'D', 0, 'C', 0
            )
        },
        .std_desc               = {
            VSF_USBD_DESC_DEVICE(0, usrapp_const.usbd.dev_desc, sizeof(usrapp_const.usbd.dev_desc)),
            VSF_USBD_DESC_CONFIG(0, 0, usrapp_const.usbd.config_desc, sizeof(usrapp_const.usbd.config_desc)),
            VSF_USBD_DESC_STRING(0, 0, usrapp_const.usbd.str_lanid, sizeof(usrapp_const.usbd.str_lanid)),
            VSF_USBD_DESC_STRING(0x0409, 1, usrapp_const.usbd.str_vendor, sizeof(usrapp_const.usbd.str_vendor)),
            VSF_USBD_DESC_STRING(0x0409, 2, usrapp_const.usbd.str_product, sizeof(usrapp_const.usbd.str_product)),
            VSF_USBD_DESC_STRING(0x0409, 4, usrapp_const.usbd.str_cdc, sizeof(usrapp_const.usbd.str_cdc)),
        },
    },
};

static usrapp_t usrapp = {
    .usbd                       = {
        .cdc.param              = {
            USB_CDC_ACM_PARAM(2, 1, 1, NULL, NULL, USB_CDC_ACM_115200_8N1)
        },

        USB_CDC_IFS(.ifs, 0, usrapp.usbd.cdc.param)
        USB_CONFIG(.config, 0, &usrapp.usbd.ifs)
        USB_DEVICE(.dev, usrapp.usbd.config, usrapp_const.usbd.std_desc, USB_DC_SPEED_HIGH, &VSF_USB_DC0)
    },
};

void main(void)
{
    vk_usbd_init(&usrapp.usbd.dev);
    vk_usbd_connect(&usrapp.usbd.dev);
}


好吧,我承认,用户需要自己定义字符串描述符。
目前,需要用户了解的信息量还有一些大,我们会继续进一步优化。
目标:没玩过USB的人,通过文档,也可以自己实现USB设备,而不是只能改改demo。
而且,由于我们的USB协议栈的专门设计,用户层代码,甚至连USB的EP初始化都不用考虑,用户层只需要知道要实现什么设备就行,其他的都交给软件,自己只需要在意业务逻辑。

使用特权

评论回复
沙发
xixi2017| | 2019-12-25 19:31 | 只看该作者
NIU B。楼主的头文件哪儿的

使用特权

评论回复
板凳
vsfopen|  楼主 | 2019-12-25 21:09 | 只看该作者
xixi2017 发表于 2019-12-25 19:31
NIU B。楼主的头文件哪儿的

是我们自己的系统的头文件,虽然目前还没完成最简的用户层封装,不过相比很多USB的demo,已经简化了非常多了。让代码去处理细节,人只需要指定功能(通过各种参数等等)

使用特权

评论回复
地板
vsfopen|  楼主 | 2019-12-25 21:12 | 只看该作者
本帖最后由 vsfopen 于 2019-12-25 21:21 编辑

简单说明一下原理,其实很简单:
这里,USB设备描述符是IAD的方式,所以,后面各种东西,都可以简单的追加。比如,如果要实现一个CDC串口+U盘,就是定义了CDC描述符后,再定义一个MSC描述符就行。而且,定义的方式也是类似的,调用一个宏,提供一些关键参数,其他的细节都不用考虑了。

#define USB_DESC_CDC_ACM_IAD(__IFS_START, __I_FUNC, __INT_IN_EP, __BULK_IN_EP, __BULK_OUT_EP, __BULK_EP_SIZE, __INT_EP_INTERVAL)\
            USB_DESC_IAD((__IFS_START), 2, USB_CLASS_COMM, 0x02, 0x01, (__I_FUNC))\
            USB_DESC_IFS((__IFS_START), 0, 1, USB_CLASS_COMM, 0x02, 0x01, (__I_FUNC))\
                                                /* Header Functional Descriptor */\
            0x05,                               /* bLength: Endpoint Descriptor size */\
            0x24,                               /* bDescriptorType: CS_INTERFACE */\
            0x00,                               /* bDescriptorSubtype: Header Func Desc */\
            USB_DESC_WORD(0x0110),              /* bcdCDC: spec release number */\
                                                /* Call Managment 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 */\
            (__IFS_START),                      /* bMasterInterface: Communication class interface */\
            1 + (__IFS_START),                  /* bSlaveInterface0: Data Class Interface */\
                                                                                \
            USB_DESC_EP(USB_DIR_IN | (__INT_IN_EP), USB_ENDPOINT_XFER_INT, 8, __INT_EP_INTERVAL)\
            USB_DESC_IFS((__IFS_START) + 1, 0, 2, USB_CLASS_CDC_DATA, 0x00, 0x00, (__I_FUNC))\
            USB_DESC_EP(USB_DIR_IN | (__BULK_IN_EP), USB_ENDPOINT_XFER_BULK, __BULK_EP_SIZE, 0x00)\
            USB_DESC_EP(USB_DIR_OUT | (__BULK_OUT_EP), USB_ENDPOINT_XFER_BULK, __BULK_EP_SIZE, 0x00)


MSC也有类似的宏,所以,如果定义一个CDC的配置描述符,这样处理就行了:
uint8_t config_desc[USB_DT_CONFIG_SIZE + USB_DESC_CDC_ACM_IAD_LEN] = {
    USB_DESC_CFG(sizeof(usrapp_const.usbd.config_desc), USB_CDC_IFS_NUM, 1, 0, 0x80, 100)
    USB_DESC_CDC_UART_HS_IAD(0, 4, 2, 1, 1)
};

如果要定义一个CDC+U盘,就在后面,跟一个MSC的宏:
uint8_t config_desc[USB_DT_CONFIG_SIZE + USB_DESC_CDC_ACM_IAD_LEN + USB_DESC_MSCBOT_IAD_LEN] = {
    USB_DESC_CFG(sizeof(usrapp_const.usbd.config_desc), USB_CDC_IFS_NUM, 1, 0, 0x80, 100)
    USB_DESC_CDC_UART_HS_IAD(0, 4, 2, 1, 1)
    USB_DESC_MSCBOT_IAD(USB_CDC_IFS_NUM, 5, 3, 3, 512)
};

只要根据手册填入宏的参数(描述符的关键数据,比如端点号等等),其他描述符的细节,就让代码自动生成吧。

使用特权

评论回复
5
21mengnan| | 2019-12-26 11:44 | 只看该作者
有图有真相吗

使用特权

评论回复
6
vsfopen|  楼主 | 2019-12-26 13:32 | 只看该作者

CDC需要咋样的截图?

使用特权

评论回复
7
vsfopen|  楼主 | 2019-12-26 13:45 | 只看该作者
CDC和MSC,用户实际使用的时候,除了一些设置外,只需要关心用户层的接口。

对于CDC,用户层接口就是一个输入流和一个输出流;对于MSC,用户层接口就是一个scsi设备。

#define USB_CDC_ACM_PARAM(__INT_IN_EP, __BULK_IN_EP, __BULK_OUT_EP, __STREAM_RX, __STREAM_TX, __BAUDRATE)\
            .ep = {                                                             \
                .notify         = __INT_IN_EP,                                  \
                .out            = __BULK_OUT_EP,                                \
                .in             = __BULK_IN_EP,                                 \
            },                                                                  \
            .line_coding        = __BAUDRATE,                                   \
            .stream.tx.stream   = (vsf_stream_t *)__STREAM_TX,                  \
            .stream.rx.stream   = (vsf_stream_t *)__STREAM_RX,
这里,__STREAM_RX和__STREAM_TX就是用户层的2个流接口。

我们正在评估去掉USB的端点号设置和接口号设置,完全通过宏自动分配,这样,用户估计真的也就只需要处理用户层的接口(CDC的2个流,MSC的scsi设备)

使用特权

评论回复
8
21mengnan| | 2019-12-26 15:06 | 只看该作者
不懂,这个怎么玩啊。。。

使用特权

评论回复
9
vsfopen|  楼主 | 2019-12-26 15:24 | 只看该作者
21mengnan 发表于 2019-12-26 15:06
不懂,这个怎么玩啊。。。

我们文档还需要不少时间,现在在处理其他文档。
以后有文档的话,照着文档天参数就行了

使用特权

评论回复
10
21mengnan| | 2019-12-26 16:27 | 只看该作者
vsfopen 发表于 2019-12-26 15:24
我们文档还需要不少时间,现在在处理其他文档。
以后有文档的话,照着文档天参数就行了 ...

好,坐等楼主大作。

使用特权

评论回复
11
vsfopen|  楼主 | 2019-12-27 13:21 | 只看该作者
21mengnan 发表于 2019-12-26 16:27
好,坐等楼主大作。

这周会准备一个更加简单的,20来行代码实现用户层的CDC,加几行可以实现2路CDC,在加几行还可以多一个MSC,实现的USB设备,用户简单可以配置。

使用特权

评论回复
12
東南博士| | 2019-12-27 13:37 | 只看该作者
USB转串口吗?

使用特权

评论回复
13
vsfopen|  楼主 | 2019-12-27 13:52 | 只看该作者

目前的demo是USB转串口,不过,这种设计方式在其他设备上一样可以用

使用特权

评论回复
14
americ| | 2020-1-17 18:03 | 只看该作者
仍然需要一个inf 在电脑上?  我在看HID, 键盘,这样完全不需要电脑的配置设置。只是应用程序配合。

使用特权

评论回复
15
vsfopen|  楼主 | 2020-1-21 12:03 | 只看该作者
americ 发表于 2020-1-17 18:03
仍然需要一个inf 在电脑上?  我在看HID, 键盘,这样完全不需要电脑的配置设置。只是应用程序配合。 ...

这个看PC端了,不过有一种方式,可以不需要驱动,加上winusb的描述符
上位机使用winusb或者libusb。

我这里弄的也只是简化USB设备端的开发,最新的在这里:
https://bbs.21ic.com/icview-2892282-1-3.html

使用特权

评论回复
16
598330983| | 2020-1-23 12:51 | 只看该作者
希望不用额外的驱动就行

使用特权

评论回复
17
gejigeji521| | 2020-1-25 15:48 | 只看该作者
多谢分享经

使用特权

评论回复
18
gejigeji521| | 2020-1-25 15:48 | 只看该作者
经验不错。

使用特权

评论回复
19
vsfopen|  楼主 | 2020-1-28 13:30 | 只看该作者
598330983 发表于 2020-1-23 12:51
希望不用额外的驱动就行

我们实验过的集中不需要额外驱动的USB通信方式:
1. HID,上位机使用libhid
2. winusb,需要加入特定的描述符,上位机使用winusb
3. U盘,模拟一个FAT文件系统,通过一个模拟的文件来通信(使用directIO方式)。这个相对最简单,上位机用脚本开发(我们测试的时候,脚本使用python,甚至是集成在模拟U盘的文件系统里的)

使用特权

评论回复
20
xixi2017| | 2020-1-28 16:14 | 只看该作者
来看大佬秀肌肉。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

90

主题

325

帖子

8

粉丝