STM32 UVC学习笔记3

主机环境:Windows 7 SP1

开发环境:MDK5.18

目标板:STM32F103C8T6

开发库:STM32F1Cube库和STM32_USB_Device_Library

继续昨天的学习笔记,之前提到从USBTrace软件中抓取的数据跟图像原始数据做对比是没有丢失数据的,只是在传输过程中有一半的数据包出现了错误,由此导致我们在PC上一直无法获取到正常的图像,从而一直显示黑屏,这个原因也找了好久,而且心浮气躁,调试一会儿就去干别的事了,对于调试来说分析出问题点才能更好的去解决问题,大家可以思考一下为什么是有一半的数据包出错,而不是个别数据包错误,也不是三分之一,四分之一出错等,联想到STM32中同步传输是使能了双缓冲特性,只有这一特性是跟我们的错误点二分之一相关的,因此,很有可能是双缓冲这里出现了问题,现在问题就来了,关于STM32 的同步传输的双缓冲传输特性,在上层应用中是没有跟其相关的,我们的传输函数只使用了USBD_LL_Transmit()函数,因此就从该函数查起,最后进入了stm32f1xx_ll_usb.c文件中的USB_EPStartXfer()函数,在这个函数中我们把数据写入PMA中,如下:


如果DTOG_TX置位则访问DBUF1,否则访问DBUF0,关于STM32中USB的双缓冲机制大家可以去阅读STM32F103C8T6参考手册的USB章节,里面会有详细讲解,我们知道STM32的USB提供了8个双向端点,而如果一个端点使能了某一方向的双缓冲机制,则TX和RX这两块区域就都由同一方向管理,可以看下分组缓冲区的的示例,如下:


重点可以看下双缓冲模式下IN端点3的缓冲区,此时,RX区域由TX_1接管了,在代码中由用于指定了TX_0和TX_1的地址,并通过PCD_SET_EP_DBUFx_CNT()指定这段缓冲中待发送数据的长度,而追溯PCD_SET_EP_DBUFx_CNT()的定义,就发现了问题,如下:

#define PCD_SET_EP_DBUF0_CNT(USBx, bEpNum, bDir, wCount)  { \
    if((bDir) == PCD_EP_DBUF_OUT)\
      /* OUT endpoint */ \
    {PCD_SET_EP_RX_DBUF0_CNT((USBx), (bEpNum),(wCount));} \
    else if((bDir) == PCD_EP_DBUF_IN)\
      /* IN endpoint */ \
      *PCD_EP_TX_CNT((USBx), (bEpNum)) = (uint32_t)(wCount);  \
  } /* SetEPDblBuf0Count*/

#define PCD_SET_EP_DBUF1_CNT(USBx, bEpNum, bDir, wCount)  { \
    if((bDir) == PCD_EP_DBUF_OUT)\
    {/* OUT endpoint */                                       \
      PCD_SET_EP_RX_CNT((USBx), (bEpNum),(wCount));           \
    }                                                         \
    else if((bDir) == PCD_EP_DBUF_IN)\
    {/* IN endpoint */                                        \
      *PCD_EP_TX_CNT((USBx), (bEpNum)) = (uint32_t)(wCount); \
    }                                                         \
  } /* SetEPDblBuf1Count */
可以看到这两个IN端点的缓冲长度设置居然是使用的同一个接口--*PCD_EP_TX_CNT((USBx), (bEpNum)) = (uint32_t)(wCount);所以这就是问题的所在,该接口定义如下:

#define PCD_EP_TX_ADDRESS(USBx, bEpNum) ((uint32_t *)(((USBx)->BTABLE+(bEpNum)*8)*2+     ((uint32_t)(USBx) + 0x400)))
#define PCD_EP_TX_CNT(USBx, bEpNum) ((uint32_t *)(((USBx)->BTABLE+(bEpNum)*8+2)*2+  ((uint32_t)(USBx) + 0x400)))
#define PCD_EP_RX_ADDRESS(USBx, bEpNum) ((uint32_t *)(((USBx)->BTABLE+(bEpNum)*8+4)*2+  ((uint32_t)(USBx) + 0x400)))
#define PCD_EP_RX_CNT(USBx, bEpNum) ((uint32_t *)(((USBx)->BTABLE+(bEpNum)*8+6)*2+  ((uint32_t)(USBx) + 0x400)))
在前面那个分组缓冲的示例中我们就知道了,TX_1是接管的RX区域,因此,DBUF1_CNT应该是使用的PCD_EP_RX_CNT()接口而不是PCD_EP_TX_CNT()接口,因此ST的库代码也不一定是完好的,至少这个问题点还没有修复,修改库代码,如下:

#define PCD_SET_EP_DBUF0_CNT(USBx, bEpNum, bDir, wCount)  { \
    if((bDir) == PCD_EP_DBUF_OUT)\
      /* OUT endpoint */ \
    {PCD_SET_EP_RX_DBUF0_CNT((USBx), (bEpNum),(wCount));} \
    else if((bDir) == PCD_EP_DBUF_IN)\
      /* IN endpoint */ \
      *PCD_EP_TX_CNT((USBx), (bEpNum)) = (uint32_t)(wCount);  \
  } /* SetEPDblBuf0Count*/

#define PCD_SET_EP_DBUF1_CNT(USBx, bEpNum, bDir, wCount)  { \
    if((bDir) == PCD_EP_DBUF_OUT)\
    {/* OUT endpoint */                                       \
      PCD_SET_EP_RX_CNT((USBx), (bEpNum),(wCount));           \
    }                                                         \
    else if((bDir) == PCD_EP_DBUF_IN)\
    {/* IN endpoint */                                        \
      *PCD_EP_RX_CNT((USBx), (bEpNum)) = (uint32_t)(wCount); \
    }                                                         \
  } /* SetEPDblBuf1Count */
然后重新编译代码烧录进单板中,运行后就终于得到我们想要的图像了,结果如下:


就是图像小了一些,大家可以自行换个大一些的图像,至此,使用STM32 的USB进行MJPEG的UVC简单传输到此就结束了。万里长征的第一步已经完成,下面就可以着手进行下一步了研究MJPEG编码或研究其他负载格式的传输,毕竟我们传输单张图片太静态了,如果研究了编码图像就可以动起来了也更直观,MJPEG是基于帧格式的传输,这个学习起来容易一些,还有一些基于流的格式,大家有时间可以研究一下,UVC文档中提到了8种负载格式,有兴趣的可以挨个试试。同时UVC协议中有一些其他的特性这里都没有用到,诸如:图像捕获、以及一些控制请求,文档中也提到不仅可以使用同步传输来传输视频还可以使用批量传输来传输视频,UVC的很多特性有待大家去挖掘。最后,我们的UVC库还有待完善,可以改善成跟AUDIO、CDC、HID、MSC库相等的接口以便于使用。

下面是工程代码下载地址:http://download.csdn.net/detail/key123zhangxing/9551843
PS:昨天上传的代码没有清理干净,编译的话会出错,需要把main.c中去掉uart_init()函数调用,以及去掉main.h文件中的包含的uart.h头文件。


©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值