完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
USB协议简介
USB ,是英文 Universal Serial BUS(通用串行总线)的缩写,而其中文简称为“通串线“,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在 PC 领域的接口技术。 USB 接口支持设备的即插即用和热插拔功能。 USB 是在 1994 年底由英特尔、康柏、 IBM、 Microsoft 等多家公司联合提出的。 USB 发展到现在已经有 USB1.0/1.1/2.0/3.0/3.1 等多个版本。目前用的最多的就是USB1.1 和 USB2.0, USB3.0 目前已经开始普及。 STM32F407 自带的 USB 符合USB2.0 规范。标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+和 D-,这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上, D-和 D+都是接了 15K 的电阻到地的,所以在没有设备接入的时候, D+、 D-均是低电平。而在USB 设备中,如果是高速设备,则会在 D+上接一个 1.5K 的电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的电阻到 VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。 USB 通信的详细过程是很复杂的,本书篇幅有限,不可能在这里详细介绍,有兴趣的朋友可以去看看电脑圈圈的《圈圈教你玩 USB》这本书 。 USB 设备基础概念
USB 设备使用各种描述符来说明其设备架构,包括设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符,他们通常被保存在 USB 设备的固件程序中。 一个设备对应一个设备描述符,支持视频的功能对应一个接口描述符,支持音频功能的对应一个接口描述符。 为了支持视频,在下层有多个端口同时工作为提供视频数据传输的支持,所以有多个端点描述符。 USB 设备使用各种描述符来说明其设备架构,包括设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符,他们通常被保存在 USB 设备的固件程序中。 设备描述符 设备代表一个 USB 设备,它由一个或多个配置组成。设备描述符用于说明设备的总体信息,并指明其所含的配置的个数。一个 USB 设备只能有一个设备描述符。 struct u***_device_descriptor { _ _u8 bLength; //描述符长度 _ _u8 bDescriptorType; //描述符类型编号 _ _le16 bcdUSB; //USB 版本号 _ _u8 bDeviceClass; //USB 分配的设备类 code _ _u8 bDeviceSubClass;// USB 分配的子类 code _ _u8 bDeviceProtocol; //USB 分配的协议 code _ _u8 bMaxPacketSize0; //endpoint0 最大包大小 _ _le16 idVendor; //厂商编号 _ _le16 idProduct; //产品编号 _ _le16 bcdDevice; //设备出厂编号 _ _u8 iManufacturer; //描述厂商字符串的索引 _ _u8 iProduct; //描述产品字符串的索引 _ _u8 iSerialNumber; //描述设备序列号字符串的索引 _ _u8 bNumConfigurations; //可能的配置数量 } _ _attribute_ _ ((packed)); 配置描述符 一个 USB 设备可以包含一个或多个配置,如 USB 设备的低功耗模式和高功耗模式可分别对应一个配置。在使用 USB 设备前,必须为其选择一个合适的配置。配置描述符用于说明 USB 设备中各个配置的特性,如配置所含接口的个数等。 USB 设备的每一个配置都必须有一个配置描述符。 struct u***_config_descriptor { _ _u8 bLength; //描述符长度 _ _u8 bDescriptorType; //描述符类型编号 _ _le16 wTotalLength; //配置所返回的所有数据的大小 _ _u8 bNumInterfaces; // 配置所支持的接口数 _ _u8 bConfigurationValue; //Set_Configuration 命令需要的参数值 _ _u8 iConfiguration; //描述该配置的字符串的索引值 _ _u8 bmAttributes; //供电模式的选择 _ _u8 bMaxPower; //设备从总线提取的最大电流 } _ _attribute_ _ ((packed)); 接口描述符 一个配置可以包含一个或多个接口,例如对一个光驱来说,当用于文件传输时,使用其大容量存储接口;而当用于播放 CD 时,使用其音频接口。 接口是端点的集合,可以包含一个或多个可替换设置,用户能够在 USB 处于配置状态时改变当前接口所含的个数和特性。接口描述符用于说明设备中各个接口的特性,如接口所属的设备类及其子类等。 USB 设备的每个接口都必须有一个接口描述符。 struct u***_interface_descriptor { _ _u8 bLength; //描述符长度 _ _u8 bDescriptorType; //描述符类型 _ _u8 bInterfaceNumber; // 接口的编号 _ _u8 bAlternateSetting; //备用的接口描述符编号 _ _u8 bNumEndpoints; //该接口使用的端点数,不包括端点 0 _ _u8 bInterfaceClass; //接口类型 _ _u8 bInterfaceSubClass; //接口子类型 _ _u8 bInterfaceProtocol; //接口所遵循的协议 _ _u8 iInterface; //描述该接口的字符串索引值 } _ _attribute_ _ ((packed)); 端点描述符 端点是 USB 设备中的实际物理单元, USB 数据传输就是在主机和 USB 设备各个端点之间进行的。端点一般由 USB 接口芯片提供,例如 Freescale 公司 MC68HC908JB8和 MC9S12UF32。 USB 设备中的每一个端点都有唯一的端点号,每个端点所支持的数据传输方向一般而言也是确定的:或是输入( IN),或是输出( OUT)。也有些芯片提供的端点的数据方向是可以配置的,例如 MC68HC908JB8 包含有两个用于数据收发的端点:端点 1 和端点 2。其中端点 1 只能用于数据发送,即支持输入( IN)操作;端点 2 既能用于数据发送,也可用于数据接收,即支持输入( IN)和输出( OUT)操作。而 MC9S12UF32 具有 6 个端点。利用设备地址、端点号和传输方向就可以指定一个端点,并与它进行通信。 端点的传输特性还决定了其与主机通信是所采用的传输类型,例如控制端点只能使用控制传输。根据端点的不同用途,可将端点分为两类: 0 号端点和非 0 号端点。0 号端点比较特殊,它有数据输入 IN 和数据输出 OUT 两个物理单元,且只能支持控制传输。所有的 USB 设备都必须含有一个 0 号端点,用作默认控制管道。 USB系统软件就是使用该管道与 USB 逻辑设备进行配置通信的。 0 号端点在 USB 设备上的以后就可以使用,而非 0 号端点必须要在配置以后才可以使用。根据具体应用的需要, USB 设备还可以含有多个除 0 号端点以外的其他端点。对于低速设备,其附加的端点数最多为 2 个;对于全速/高速设备,其附加的端点数最多为 15 个。 struct u***_endpoint_descriptor { _ _u8 bLength; //描述符长度 _ _u8 bDescriptorType; //描述符类型 _ _u8 bEndpointAddress; //端点地址: 0~3 位是端点号,第 7 位是方向(0-OUT,1-IN) _ _u8 bmAttributes; //端点属性: bit[0:1] 的值为 00 表示控制,为 01 表示同步,为 02 表示批量,为 03 表示中断 _ _le16 wMaxPacketSize; 本端点接收或发送的最大信息包的大小 _ _u8 bInterval;//轮询数据传送端点的时间间隔 //对于批量传送的端点以及控制传送的端点,此域忽略 //对于同步传送的端点,此域必须为 1 _ _u8 bRefresh; _ _u8 bSynchAddress; } _ _attribute_ _ ((packed)); 字符串描述符 在 USB 设备中通常还含有字符串描述符,以说明一些专用信息,如制造商的名称、设备的序列号等。它的内容以 UNICODE 的形式给出,且可以被客户软件所读取。对 USB 设备来说,字符串描述符是可选的。 struct u***_string_descriptor { _ _u8 bLength; //描述符长度 _ _u8 bDescriptorType; //描述符类型 _ _le16 wData[1]; } _ _attribute_ _ ((packed)); 管道 在 USB 系统结构中,可以认为数据传输时在 USB 主机软件与 USB 设备的各个端点之间直接进行的,它们之间的连接称为管道。管道是在 USB 设备的配置过程中建立的。管道是对 USB 主机与 USB 设备间通信流的抽象,表示 USB 主机的数据缓冲区与 USB 设备的端点之间存在着逻辑数据传输,而实际的数据传输是由 USB 总线接口层来完成的。管道与 USB 设备中的端点一一对应。一个 USB 设备含有多少个端点,其与USB 主机进行通信时就可以使用多少条管道,且端点的类型决定了管道中数据的传输类型,例如中断端点对应中断管道,且该管道只能进行中断传输。不论存在着多少条管道,在各个管道中进行的数据传输都是相互独立的。 USB 端点分类 USB 通讯的最基本形式是通过端点。一个 USB 端点只能向一个方向传输数据(从主机到设备(称为输出端点)或者从设备到主机(称为输入端点))。端点可被看作一个单向的管道。 USB 端点有 4 种不同类型, 分别具有不同的数据传送方式:
控制和批量端点用于异步数据传送,而中断和等时端点是周期性的。这意味着这些端点被设置来在固定的时间连续传送数据, USB 核心为它们保留了相应的带宽。 struct u***_host_endpoint{ struct u***_endpoint_descriptor desc;//端点描述符 struct list_head urb_list;//此端点的 URB 对列,由 USB 核心维护 void *hcpriv; struct ep_device *ep_dev; /* For sysfs info */ unsigned char*extra;/* Extra descriptors */ int extralen; int enabled; }; 当调用 USB 设备驱动调用 u***_submit_urb 提交 urb 请求时,将调用 int u***_hcd_link_urb_to_ep(struct u***_hcd *hcd, struct urb *urb)把此 urb 增加到 urb_list的尾巴上。 (hcd: Host Controller Driver,对应数据结构 struct u***_hcd ) USB 总线驱动概念 在 linux 系统中,现在一般存在 3 中 USB 总线。
1、识别 USB 设备。
3、提供 USB 读写函数 驱动程序讲解 在 linux 内核中基本集成了所有 USB 设备的驱动,一般研发出一个新型的 USB 设备,都会申请专利,会加载到内核之中,所以一般 USB 设备的驱动是需要编写的。为了让大家深刻了解USB 驱动的流程,下面是一个把鼠标当做按键的例子。为了上系统匹配到我们的驱动程序,我们先要在内核中关闭两个配置。(在配置菜单中可以输入‘ /’查找配置的具体位置) 在内核的配置文件中关闭USB_HID和HID_GENERIC CONFIG_USB_HID │ -> Device Drivers │ -> HID support │ (1) -> USB HID support CONFIG_HID_GENERIC │ -> Device Drivers │ -> HID support │ (1) -> HID bus support (HID [=y]) support (HID [=y]) 备注:关闭HID非常重要 USB 鼠标用作按键: (相当于输入子系统) 左键 -- L 右键 -- S 中键 -- Enter 在“.probe” 函数里做下面 4 件事情:
|
|
|
|
分配/设置 u***_driver 结构体 id_table :表示能支持的设备. probe :表示“USB 总线驱动程序”发现一个新设备后,就会与 driver 比较,若 id_table 表示能支持它,就调用.probe 函数。 disconnect :拔掉 USB 设备时调用这个函数 代码编写: 函数入口 /* 1. 分配/设置u***_driver */ static struct u***_driver u***mouse_as_key_driver = { .name = "u***mouse_as_key_", .probe = u***mouse_as_key_probe, .disconnect = u***mouse_as_key_disconnect, .id_table = u***mouse_as_key_id_table, }; static int u***mouse_as_key_init(void) { /* 2. 注册 */ u***_register(&u***mouse_as_key_driver); return 0; } probe函数 static int u***mouse_as_key_probe(struct u***_interface *intf, const struct u***_device_id *id) { struct u***_device *dev = interface_to_u***dev(intf); struct u***_host_interface *interface; struct u***_endpoint_descriptor *endpoint; int pipe; interface = intf->cur_altsetting; endpoint = &interface->endpoint[0].desc; /* a. 分配一个input_dev */ uk_dev = input_allocate_device(); /* b. 设置 */ /* b.1 能产生哪类事件 */ set_bit(EV_KEY, uk_dev->evbit); set_bit(EV_REP, uk_dev->evbit); /* b.2 能产生哪些事件 */ set_bit(KEY_L, uk_dev->keybit); set_bit(KEY_S, uk_dev->keybit); set_bit(KEY_ENTER, uk_dev->keybit); /* c. 注册 */ if (input_register_device(uk_dev)) { printk("input register device errorn"); return -1; } /* d. 硬件相关操作 */ /* 数据传输3要素: 源,目的,长度 */ /* 源: USB设备的某个端点 */ pipe = u***_rcvintpipe(dev, endpoint->bEndpointAddress); /* 长度: */ len = endpoint->wMaxPacketSize; /* 目的: */ u***_buf = u***_alloc_coherent(dev, len, GFP_ATOMIC, &u***_buf_phys); /* 使用"3要素" */ /* 分配u*** request block */ uk_urb = u***_alloc_urb(0, GFP_KERNEL); /* 使用"3要素设置urb" */ u***_fill_int_urb(uk_urb, dev, pipe, u***_buf, len, u***mouse_as_key_irq, NULL, endpoint->bInterval); uk_urb->transfer_dma = u***_buf_phys; uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* 使用URB */ u***_submit_urb(uk_urb, GFP_KERNEL); return 0; } USB设备中断接口 static void u***mouse_as_key_irq(struct urb *urb) { static unsigned char pre_val; #if 1 int i; static int cnt = 0; printk("data cnt %d: ", ++cnt); for (i = 0; i < len; i++) { printk("%02x ", u***_buf); } printk("n"); #endif /* USB鼠标数据含义 * data[0]: bit0-左键, 1-按下, 0-松开 * bit1-右键, 1-按下, 0-松开 * bit2-中键, 1-按下, 0-松开 * */ if ((pre_val & (1<<0)) != (u***_buf[0] & (1<<0))) { /* 左键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_L, (u***_buf[0] & (1<<0)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<1)) != (u***_buf[0] & (1<<1))) { /* 右键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_S, (u***_buf[0] & (1<<1)) ? 1 : 0); input_sync(uk_dev); } if ((pre_val & (1<<2)) != (u***_buf[0] & (1<<2))) { /* 中键发生了变化 */ input_event(uk_dev, EV_KEY, KEY_ENTER, (u***_buf[0] & (1<<2)) ? 1 : 0); input_sync(uk_dev); } pre_val = u***_buf[0]; /* 重新提交urb */ u***_submit_urb(uk_urb, GFP_KERNEL); } USB设备列表 static struct u***_device_id u***mouse_as_key_id_table [] = { { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_MOUSE) }, //{USB_DEVICE(0x1234,0x5678)}, { } /* Terminating entry */ }; 测试程序 #include #include #include #include #include #include #include #include int main(int argc, char * const argv[]) { if(argc<2) { printf("Fomat error need with parametern"); return -1; } int fd = 0; struct input_event event[4] = {0}; //3!!!,驱动上传了3个事件,第4个用来装空元素 int ret = 0; fd = open(argv[1],O_RDONLY); while(1){ ret = read(fd,&event,sizeof(event)); printf("value = %dn",event[0].value); sleep(1); } return 0; } Makefiel文件 obj-m += mouse_drv.o KERNELDIR:=/file/RK3399Pro/rk3399pro_git_repo/kernel PWD:=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules test: aarch64-linux-gnu-gcc mouse_test.c -o mouse_test clean: rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions 测试步骤 编译源码 在ubuntu中输入: make 得到驱动目标文件mouse_drv.ko 输入: make test 得到测试目标文件:mouse_test 加载驱动 在开发板命令终端输入: insmod mouse_drv.ko 执行测试程序 在开发板命令终端输入: chmod 777 mouse_test ./mouse_test /dev/input/by-id/u***-PixArt_USB_Optical_Mouse-event-mouse 实验现象 按下或者滚动中键时打印的数字发生变化,并通过 input 子系统上报给应用层。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
828 浏览 1 评论
RK3588 GStreamer调试四路鱼眼摄像头四宫格显示报错
2006 浏览 1 评论
【飞凌嵌入式OK3576-C开发板体验】RKNN神经网络-YOLO图像识别
254 浏览 0 评论
【飞凌嵌入式OK3576-C开发板体验】SSH远程登录网络配置及CAN通讯
1336 浏览 0 评论
2287 浏览 3 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-10-21 03:06 , Processed in 2.733422 second(s), Total 75, Slave 58 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号