UVC摄像头枚举过程分析——D881HD720P
<div class='showpostmsg'>计划在单片机(主要是Cortex-M系列)上驱动USB UVC摄像头,本帖是对Windows下枚举过程的分析。摄像头插入后的枚举,一开始由usbh core来负责,此过程与其他类设备相同,不再赘述。UVC类驱动主要负责获得摄像头状态,根据要求设置摄像头参数以及获得数据。
在win7系统上,当应用有读取摄像头的请求是,类驱动便按照一定流程开始操作,我的目的便是按照这一流程在单片机上激活特定摄像头并获得数据。(UVC标准类驱动太过复杂,暂无搞通用驱动的必要)
传输类型总览:
其中bRequest意义参考此表:
整个流程中使用了GET_CUR,GET_MIN,GET_MAX以及SET_CUR四种请求。
Transfer分析:在上图所有transfer中wIndex都是0x0001,即指定了entity0及interface1。由配置描述符可知interface1为VideoStreaming,所以根据下表分析:
bRequest中缩写的意义: Current setting attribute (GET_CUR) Minimum setting attribute (GET_MIN) Maximum setting attribute (GET_MAX) Default setting attribute (GET_DEF) Resolution attribute (GET_RES) Data length attribute (GET_LEN) Information attribute (GET_INFO)
wValue是选择控制器,除一个0x0200外,都是0x0100,意义如下 VS_CONTROL_UNDEFINED 0x00 VS_PROBE_CONTROL 0x01 VS_COMMIT_CONTROL 0x02 VS_STILL_PROBE_CONTROL 0x03 VS_STILL_COMMIT_CONTROL 0x04 VS_STILL_IMAGE_TRIGGER_CONTROL 0x05 VS_STREAM_ERROR_CODE_CONTROL 0x06 VS_GENERATE_KEY_FRAME_CONTROL 0x07 VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08 VS_SYNCH_DELAY_CONTROL 0x09 wLength指定附件参数长度,都是26
这样分析后,就可以清楚的看到,系统对视频流接口的参数进行了probe和commit两种操作,先通过probe进行参数协商,最后commit激活。也可以看下协议中对这个过程的描述:
关于data中26字节数据的分析,请看下表,如果表格显示不完整,请看附件。
在在协商完成后,设置interface altr setting,随后启动同步传输,开始搬运数据。至此,针对D881HD720P摄像头的枚举分析完毕。
附上分析表及原始数据。
</div><script> var loginstr = '<div class="locked">查看精华帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
}
</script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> 预备怎么做?用那种单片机? dcexpert 发表于 2015-9-23 14:06
预备怎么做?用那种单片机?
新唐的505(OHCI)核心已经弄好了
所以先拿它做类驱动
后面支持STM32的IP核心(DWCOTG) 正好说一下,这个uvc已经能在stm32f7上面跑了
在1024 * 1 同步传输设置下,能稳定获得5.4Mb/s的图像流
速度上应该还有提升空间,估计能到15Mb的样子,但是这种大数据流stm32f7压根没法处理,最多转发下,咔咔 Hi,le062,这两天一直根据您的帖子学习UVC枚举。我是在stm32F2系列芯片上实现UVC host驱动,接一个USB摄像头获取视频,由于技术底子薄,目前很不顺利,但是任务又很紧(要求两个星期内完成),不知道能否将您的代码发给我学习一下,gdcwj2003@163.com。多谢啦! gdcwj2012 发表于 2015-10-16 10:04
Hi,le062,这两天一直根据您的帖子学习UVC枚举。我是在stm32F2系列芯片上实现UVC host驱动,接一个USB摄像 ...
usb这上面的麻烦很大的
我建议你使用st的usb驱动程序
我可以把uvc部分的简单驱动逻辑发给你
#include "app_cfg.h"
#include "app_type.h"
#include "interfaces.h"
#include "tool/buffer/buffer.h"
#include "stack/usb/core/vsfusbh.h"
#include "stack/usb/class/host/UVC/vsfusbh_UVC.h"
#define UVC_PROBE_CRTL_DATA_SIZE 36
enum uav_evt_t
{
UAV_RESET_STREAM_PARAM = VSFSM_EVT_USER_LOCAL + 1,
UAV_ISO_ENABLE,
UAV_ISO_DISABLE,
};
enum uav_request_t
{
RC_UNDEFINED = 0x00,
SET_CUR = 0x01,
GET_CUR = 0x81,
GET_MIN = 0x82,
GET_MAX = 0x83,
GET_RES = 0x84,
GET_LEN = 0x85,
GET_INFO = 0x86,
GET_DEF = 0x87,
};
struct vsfusbh_uvc_t
{
struct vsfusbh_t *usbh;
struct vsfusbh_device_t *dev;
struct vsfsm_t init_sm;
struct vsfsm_pt_t init_pt;
struct vsfsm_t ctrl_sm;
struct vsfsm_pt_t ctrl_pt;
struct vsfsm_t video_sm;
//struct vsfsm_t audio_sm;
struct vsfusbh_urb_t ctrl_urb;
struct vsfusbh_urb_t video_urb;
//struct vsfusbh_urb_t audio_urb;
uint8_t *ctrl_urb_buf;
uint8_t *video_urb_buf;
//uint8_t *audio_urb_buf;
struct vsfusbh_uvc_payload_t video_payload;
//struct vsfusbh_uvc_payload_t audio_payload;
struct vsfusbh_uvc_param_t set_param;
struct vsfusbh_uvc_param_t cur_param;
uint16_t video_iso_packet_len;
//uint16_t audio_iso_packet_len;
uint8_t video_iso_ep;
//uint8_t audio_iso_ep;
};
void (*vsfusbh_uvc_report)(void *dev_data, struct vsfusbh_uvc_param_t *param,
struct vsfusbh_uvc_payload_t *payload) = NULL;
static vsf_err_t uvc_init_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
vsf_err_t err;
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)pt->user_data;
struct vsfusbh_urb_t *vsfurb = &hdata->ctrl_urb;
vsfsm_pt_begin(pt);
// reset interfaces 1 (video)
vsfurb->transfer_buffer = NULL;
vsfurb->transfer_length = 0;
err = vsfusbh_set_interface(hdata->usbh, vsfurb, 1, 0);
if (err != VSFERR_NONE)
return err;
vsfsm_pt_wfe(pt, VSFSM_EVT_URB_COMPLETE);
if (vsfurb->status != URB_OK)
return VSFERR_FAIL;
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
static struct vsfsm_state_t *uvc_evt_handler_init(struct vsfsm_t *sm,
vsfsm_evt_t evt)
{
vsf_err_t err;
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)sm->user_data;
switch (evt)
{
case VSFSM_EVT_INIT:
case VSFSM_EVT_URB_COMPLETE:
case VSFSM_EVT_DELAY_DONE:
err = hdata->init_pt.thread(&hdata->init_pt, evt);
if (err < 0)
{
// TODO
}
else if (err == 0)
{
hdata->ctrl_urb.sm = &hdata->ctrl_sm;
if (vsfusbh_uvc_report)
vsfusbh_uvc_report(hdata, &hdata->cur_param, NULL);
}
break;
default:
break;
}
return NULL;
}
const uint8_t negotiate_temp =
{
0x00, 0x00, 0x01, 0x03, 0x15, 0x16, 0x05, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
static vsf_err_t uvc_ctrl_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
vsf_err_t err;
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)pt->user_data;
struct vsfusbh_urb_t *vsfurb = &hdata->ctrl_urb;
vsfsm_pt_begin(pt);
if (hdata->set_param.video_enable)
{
// negotiate
hdata->video_iso_packet_len = 1024;
hdata->video_iso_ep = 1;
// commit param
vsfurb->transfer_buffer = hdata->ctrl_urb_buf;
memcpy(vsfurb->transfer_buffer, negotiate_temp, 26);
vsfurb->transfer_length = 26;
vsfurb->pipe = usb_sndctrlpipe(vsfurb->vsfdev, 0);
err =vsfusbh_control_msg(hdata->usbh, vsfurb,
USB_TYPE_CLASS |USB_RECIP_INTERFACE | USB_DIR_OUT, SET_CUR,
0x0200, 0x0001);
if (err != VSFERR_NONE)
return err;
vsfsm_pt_wfe(pt, VSFSM_EVT_URB_COMPLETE);
if (vsfurb->status != URB_OK)
return VSFERR_FAIL;
// set interfaces
vsfurb->transfer_buffer = NULL;
vsfurb->transfer_length = 0;
err = vsfusbh_set_interface(hdata->usbh, vsfurb, 1, 4);
if (err != VSFERR_NONE)
return err;
vsfsm_pt_wfe(pt, VSFSM_EVT_URB_COMPLETE);
if (vsfurb->status != URB_OK)
return VSFERR_FAIL;
// enable video
vsfsm_post_evt_pending(&hdata->video_sm, UAV_ISO_ENABLE);
}
else
{
vsfurb->transfer_buffer = NULL;
vsfurb->transfer_length = 0;
err = vsfusbh_set_interface(hdata->usbh, vsfurb, 1, 0);
if (err != VSFERR_NONE)
return err;
vsfsm_pt_wfe(pt, VSFSM_EVT_URB_COMPLETE);
if (vsfurb->status != URB_OK)
return VSFERR_FAIL;
}
vsf_bufmgr_free(hdata->ctrl_urb_buf);
hdata->ctrl_urb_buf = NULL;
memcpy(&hdata->cur_param, &hdata->set_param,
sizeof(struct vsfusbh_uvc_param_t));
vsfusbh_uvc_report(hdata, &hdata->cur_param, NULL);
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
static struct vsfsm_state_t *uvc_evt_handler_ctrl(struct vsfsm_t *sm,
vsfsm_evt_t evt)
{
vsf_err_t err;
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)sm->user_data;
switch (evt)
{
case VSFSM_EVT_INIT:
break;
case UAV_RESET_STREAM_PARAM:
hdata->ctrl_pt.state = 0;
if (hdata->ctrl_urb_buf == NULL)
{
hdata->ctrl_urb_buf = vsf_bufmgr_malloc(UVC_PROBE_CRTL_DATA_SIZE);
if (hdata->ctrl_urb_buf == NULL)
return NULL;
}
case VSFSM_EVT_URB_COMPLETE:
case VSFSM_EVT_DELAY_DONE:
err = hdata->ctrl_pt.thread(&hdata->ctrl_pt, evt);
if (err < 0)
{
// TODO
vsf_bufmgr_free(hdata->ctrl_urb_buf);
hdata->ctrl_urb_buf = NULL;
}
else if (err == 0)
{
vsf_bufmgr_free(hdata->ctrl_urb_buf);
hdata->ctrl_urb_buf = NULL;
}
break;
default:
break;
}
return NULL;
}
static struct vsfsm_state_t *uvc_evt_handler_video(struct vsfsm_t *sm,
vsfsm_evt_t evt)
{
vsf_err_t err;
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)sm->user_data;
struct vsfusbh_t *usbh = hdata->usbh;
struct vsfusbh_urb_t *vsfurb = &hdata->video_urb;
switch (evt)
{
case VSFSM_EVT_INIT:
break;
case UAV_ISO_ENABLE:
if (hdata->video_urb_buf == NULL)
{
vsfurb->vsfdev->epmaxpacketin = hdata->video_iso_packet_len;
hdata->video_urb_buf = vsf_bufmgr_malloc(hdata->video_iso_packet_len);
if (hdata->video_urb_buf == NULL)
return NULL;
vsfurb->transfer_buffer = hdata->video_urb_buf;
vsfurb->transfer_length = hdata->video_iso_packet_len;
vsfurb->pipe = usb_rcvisocpipe(vsfurb->vsfdev, hdata->video_iso_ep);
vsfurb->transfer_flags |= USB_ISO_ASAP;
vsfurb->number_of_packets = 1;
vsfurb->iso_frame_desc.offset = 0;
vsfurb->iso_frame_desc.length = hdata->video_iso_packet_len;
err = vsfusbh_submit_urb(usbh, vsfurb);
if (err != VSFERR_NONE)
goto error;
}
break;
case UAV_ISO_DISABLE:
// TODO
break;
case VSFSM_EVT_URB_COMPLETE:
if (vsfurb->status == URB_OK)
{
hdata->video_payload.len = vsfurb->actual_length;
vsfusbh_uvc_report(hdata, &hdata->cur_param, &hdata->video_payload);
}
else
{
goto error;
}
err = vsfusbh_relink_urb(usbh, vsfurb);
if (err != VSFERR_NONE)
goto error;
break;
default:
break;
}
return NULL;
error:
vsf_bufmgr_free(hdata->video_urb_buf);
hdata->video_urb_buf = NULL;
return NULL;
}
static void *vsfusbh_uvc_init(struct vsfusbh_t *usbh, struct vsfusbh_device_t *dev)
{
struct vsfusbh_class_data_t *cdata;
struct vsfusbh_uvc_t *hdata;
cdata = vsf_bufmgr_malloc(sizeof(struct vsfusbh_class_data_t));
if (NULL == cdata)
return NULL;
hdata = vsf_bufmgr_malloc(sizeof(struct vsfusbh_uvc_t));
if (NULL == hdata)
{
vsf_bufmgr_free(cdata);
return NULL;
}
memset(cdata, 0, sizeof(struct vsfusbh_class_data_t));
memset(hdata, 0, sizeof(struct vsfusbh_uvc_t));
cdata->dev = dev;
hdata->dev = dev;
hdata->usbh = usbh;
cdata->param = hdata;
hdata->video_payload.type = VSFUSBH_UVC_PAYLOAD_VIDEO;
hdata->video_payload.buf = hdata->video_urb_buf;
hdata->init_sm.init_state.evt_handler = uvc_evt_handler_init;
hdata->init_sm.user_data = (void*)hdata;
hdata->init_pt.thread = uvc_init_thread;
hdata->init_pt.user_data = hdata;
hdata->init_pt.sm = &hdata->init_sm;
hdata->init_pt.state = 0;
hdata->ctrl_sm.init_state.evt_handler = uvc_evt_handler_ctrl;
hdata->ctrl_sm.user_data = (void*)hdata;
hdata->ctrl_pt.thread = uvc_ctrl_thread;
hdata->ctrl_pt.user_data = hdata;
hdata->ctrl_pt.sm = &hdata->ctrl_sm;
hdata->ctrl_pt.state = 0;
hdata->ctrl_urb.vsfdev = dev;
hdata->ctrl_urb.timeout = 200;
hdata->ctrl_urb.sm = &hdata->init_sm;
hdata->video_sm.init_state.evt_handler = uvc_evt_handler_video;
hdata->video_sm.user_data = (void*)hdata;
hdata->video_urb.vsfdev = dev;
hdata->video_urb.timeout = 200;
hdata->video_urb.sm = &hdata->video_sm;
vsfsm_init(&hdata->init_sm);
vsfsm_init(&hdata->ctrl_sm);
vsfsm_init(&hdata->video_sm);
return cdata;
}
static vsf_err_t vsfusbh_uvc_match(struct vsfusbh_device_t *dev)
{
if ((dev->descriptor.idVendor == 0x04f2) &&
(dev->descriptor.idProduct == 0xb130))
{
return VSFERR_NONE;
}
return VSFERR_FAIL;
}
static void vsfusbh_uvc_free(struct vsfusbh_device_t *dev)
{
struct vsfusbh_class_data_t *cdata = (struct vsfusbh_class_data_t *)(dev->priv);
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)cdata->param;
// TODO
// free urb
vsf_bufmgr_free(hdata);
vsf_bufmgr_free(cdata);
}
const struct vsfusbh_class_drv_t vsfusbh_uvc_drv = {
vsfusbh_uvc_init,
vsfusbh_uvc_free,
vsfusbh_uvc_match,
};
vsf_err_t vsfusbh_uvc_set(void *dev_data, struct vsfusbh_uvc_param_t *param)
{
struct vsfusbh_uvc_t *hdata = (struct vsfusbh_uvc_t *)dev_data;
hdata->set_param = *param;
vsfsm_post_evt_pending(&hdata->ctrl_sm, UAV_RESET_STREAM_PARAM);
return VSFERR_NONE;
}
在枚举之后,直接commit 协商结果,接着set interface
这样就能开始同步传输了 gdcwj2012 发表于 2015-10-16 10:04
Hi,le062,这两天一直根据您的帖子学习UVC枚举。我是在stm32F2系列芯片上实现UVC host驱动,接一个USB摄像 ...
vsfusb中ohci部分是完全开源的,st的dwcotg暂时未开源
如果你们能换用新唐的芯片做uvc,这部分是现成的 路过围观 你好,我想问下用stm32F4开发平台实现UVC HOST后接USB摄像头采集图像,然后做图像处理可行吗? anzyelay 发表于 2015-11-16 17:08
你好,我想问下用stm32F4开发平台实现UVC HOST后接USB摄像头采集图像,然后做图像处理可行吗?
接摄像头没问题,图像处理不清楚 楼主用746实现的UVC是用的FS模式还是HS模式?FS模式的话我这边从摄像头获取的端点描述符中最大端点大小是1020Bytes, 同步传输时1ms传输一个packet的话速率最高1020*1000B/s=996KB/s基本达到FS的总带宽了(12Mbit/s).
但是在用746的HS模式的时候746的OTG_HCCHAR寄存器的MPSIZ只有11位也就是一个packet最大2047字节。而从摄像头获取的Ep描述符来看最大的端点有5KB大小。如果用2047来算的话同步传输带宽最大也只有2047*8*1000≈16MB/s?看到OTG_HCCHAR有个MCNT可设置一帧传输3次,这样的话倒是基本能占满HS的带宽,但没实际测试过是否可行。以上不知道我的理解有没有问题。
另外HS模式默认是使用DMA传输数据,DMA传输时都是传输已知量的数据,而对于摄像头同步传输在采集视频流的时候我们无法确定一次能从摄像头接收多少数据,这个时候该怎么处理,请教一下楼主 xijiele 发表于 2015-12-23 09:55
楼主用746实现的UVC是用的FS模式还是HS模式?FS模式的话我这边从摄像头获取的端点描述符中最大端点大小是10 ...
HS模式
摄像头上面包大小是可以设置的,选个比f7小的就行。
在一个微帧里面,可以启动多次同步传输,这样可以速度更快,我的驱动里面没有这样做,毕竟这个驱动是做着玩的,并没有实际项目需要用这个。
DMA我不知道,这个dwcotg自带DMA的把,好像高速模式必须启动内部dma的,不能手工搬运,至于数据长度,是有一个寄存器指定的,传输完成后,读一下寄存器就知道了 le062 发表于 2015-12-23 11:39
HS模式
摄像头上面包大小是可以设置的,选个比f7小的就行。
在一个微帧里面,可以启动多次同步传输,这 ...
重新看了下ST的HCD驱动里的HCD_HC_IN_IRQHandler,昨晚眼花了,确实可以通过寄存器读到实际DMA传输的数据量
else if ((USBx_HC(chnum)->HCINT) &USB_OTG_HCINT_XFRC)
{
if (hhcd->Init.dma_enable)
{
hhcd->hc.xfer_count = hhcd->hc.xfer_len - \
(USBx_HC(chnum)->HCTSIZ & USB_OTG_HCTSIZ_XFRSIZ);
}
。。。。
}
今天再试试 gdcwj2012 发表于 2015-10-16 10:04
Hi,le062,这两天一直根据您的帖子学习UVC枚举。我是在stm32F2系列芯片上实现UVC host驱动,接一个USB摄像 ...
兄弟,我跟你的情况一样,现在也是要用F4系列的芯片上实现连接一个USB摄像头获取视频,现在是一头雾水,能不能把你的代码让我学习一下!万分感激。。。(wuxianghua1988@126.com) 和马克思握手 发表于 2016-1-7 10:31
兄弟,我跟你的情况一样,现在也是要用F4系列的芯片上实现连接一个USB摄像头获取视频,现在是一头雾水,能 ...
你好。相同的内容啊。STM32F407通过USB Host驱动一个USB摄像头然后将数据上传。哥们,搞到哪一步了 qscdianzhi 发表于 2016-7-13 11:33
你好。相同的内容啊。STM32F407通过USB Host驱动一个USB摄像头然后将数据上传。哥们,搞到哪一步了
你好,你用407驱动uvc摄像头,做的怎么样了,能不能给点思路 le062 发表于 2015-10-16 12:25
usb这上面的麻烦很大的
我建议你使用st的usb驱动程序
我可以把uvc部分的简单驱动逻辑发给你
在 ...
大神 我也想用F4的板子做一个UVC的主机能否把资料也发我一份呢 谢谢 360101770@qq.com 独自等待1 发表于 2016-11-21 14:08
大神 我也想用F4的板子做一个UVC的主机能否把资料也发我一份呢 谢谢
https://github.com/versaloon/vsf/tree/master/vsf/example/vsfusbh/proj/EWARM7.40_STM32F746G
https://github.com/versaloon/vsf/tree/master/vsf/stack/usb/class/host/UVC
原来的工程没了,相关代码还在。已全部开源 还是下不来???{:1_101:} 楼主你好,
https://github.com/versaloon/vsf ... WARM7.40_STM32F746G
https://github.com/versaloon/vsf ... /usb/class/host/UVC
这个两个链接失效了。
d881_all.usb你的使用LeCroy USB trace版本的是多少。我使用Software Version 2.15, Build 358 。
页:
[1]
2