主機環境:Windows 7 SP1
開發環境:MDK5.18
目標板:STM32F103C8T6
開發庫:STM32F1Cube庫和STM32_USB_Device_Library
距離之前的STM32 USB學習又過去了N個月,想起最初想學習USB的初衷就是學習一下UVC協議,瞭解一下圖像的傳輸,在逛STM32社區的時候有看到一句話:以前使用單片機必須熟悉I2C、SPI、UART等通信協議,但現在也必須熟悉USB通信協議了,因爲目前主流的設備幾乎都是USB接口的,在USB接口上又可以實現I2C、SPI、UART等協議,USB不可謂不強大。廢話就到這吧。
經過之前的USB學習,基本上了解了STM32的USB外設,因此開始了USB接口之上的UVC的學習,中間可以說是一波三折,其中芯片還燒壞了一次,又買的新的芯片焊上去的,歷史的經驗告訴我們,學習一個新的東西,總不是一帆風順的,UVC全程爲USB Video Class--USB視頻設備類,用於實現在USB接口上傳輸視頻的功能,目前的UVC規範版本是1.5,可以從USB官網下載,其清單如下:
其中我們需要熟悉的是UVC 1.5 Class Specification文檔,USB_Video_Example 1.5文檔以及USB_Video_Payload_XX_1.5(一種)文檔,由於是初學者因此這裏我看的是USB_Video_Payload_MJPEG_1.5文檔,也建議大家先學習該文檔,畢竟新東西要從簡單的開始學起。USB_Video_Example_1.5文檔給出了使用MJPEG負載的示例,我們敲代碼時可以用到其中的描述符。
UVC規範中說明了一個UVC設備需要實現一個VC(Video Control)接口和若干個VS(Video Streaming)接口,其中VC接口用於控制設備的功能,而VS接口用於傳輸視頻數據流。在最簡單的情況下,有一個VC接口和一個VS接口,這就是接下來我們需要實現的。在未熟悉UVC規範的情況下我們也可以把代碼框架搭建起來,STM32_USB_Device_Library庫是一個很方便擴展的庫,因爲它把內核和設備類區分出來了,我們要想實現UVC就要新建一個設備類文件夾,剛好UVC和UAC有那麼一點類似之處,我們可以把AUDIO中的文件拷貝一份到UVC文件夾下並修改文件名,這樣我們就有了usbd_uvc以及usbd_uvc_if文件了,至於usbd_conf,usbd_desc文件只要把之前的VCP例程中的文件稍作修改就可以使用了,這樣我們的UVC代碼框架就算完成了,在代碼實現中就要參看Example文檔一步步來完善就可以了,由於是首次學習,UVC的代碼框架並不是很好,因此usbd_uvc_if文件其實是沒有用到的,在以後再修改吧。因爲在實現中,我們只需要把視頻流數據發往USB主機即可,沒有什麼其他的功能要實現,當然在以後有更多功能要求時類接口文件就需要好好實現了,其中usbd_uvc_if.h文件內容如下:
- /**
- ******************************************************************************
- * @file USB_Device/UVC_Standalone/Inc/usbd_uvc_if.h
- * @author MCD Application Team
- * @version V1.2.0
- * @date 19-June-2015
- * @brief Header for usbd_uvc_if.c file.
- ******************************************************************************
- * @attention
- *
- * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
- *
- * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.st.com/software_license_agreement_liberty_v2
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************
- */
-
- /* Define to prevent recursive inclusion -------------------------------------*/
- #ifndef __USBD_UVC_IF_H
- #define __USBD_UVC_IF_H
-
- /* Includes ------------------------------------------------------------------*/
- #include "usbd_uvc.h"
-
- /* Exported types ------------------------------------------------------------*/
- /* Exported constants --------------------------------------------------------*/
-
- extern USBD_UVC_ItfTypeDef USBD_UVC_fops;
-
- /* Exported macro ------------------------------------------------------------*/
- /* Exported functions ------------------------------------------------------- */
-
- #endif /* __USBD_UVC_IF_H */
-
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
可以看到文件基本上是空的,而usbd_uvc_if.c文件內容如下:
- /**
- ******************************************************************************
- * @file usbd_uvc_if.c
- * @author MCD Application Team
- * @version V2.4.1
- * @date 19-June-2015
- * @brief Generic media access Layer.
- ******************************************************************************
- * @attention
- *
- * <h2><center>© COPYRIGHT 2015 STMicroelectronics</center></h2>
- *
- * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.st.com/software_license_agreement_liberty_v2
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************
- */
-
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
-
- /** @addtogroup STM32_USB_DEVICE_LIBRARY
- * @{
- */
-
-
- /** @defgroup USBD_UVC
- * @brief usbd core module
- * @{
- */
-
- /** @defgroup USBD_UVC_Private_TypesDefinitions
- * @{
- */
- /**
- * @}
- */
-
-
- /** @defgroup USBD_UVC_Private_Defines
- * @{
- */
- /**
- * @}
- */
-
- /** @defgroup USBD_UVC_Private_Macros
- * @{
- */
- /**
- * @}
- */
-
-
- /** @defgroup USBD_UVC_Private_FunctionPrototypes
- * @{
- */
-
- static int8_t UVC_Itf_Init (void);
- static int8_t UVC_Itf_DeInit (void);
- static int8_t UVC_Itf_Control (uint8_t cmd, uint8_t* pbuf, uint16_t length);
-
-
- USBD_UVC_ItfTypeDef USBD_UVC_fops =
- {
- UVC_Itf_Init,
- UVC_Itf_DeInit,
- UVC_Itf_Control,
- };
-
- /* TIM handler declaration */
- /* USB handler declaration */
- /* Private functions ---------------------------------------------------------*/
-
- /**
- * @brief TEMPLATE_Init
- * Initializes the UVC media low layer
- * @param None
- * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
- */
- static int8_t UVC_Itf_Init(void)
- {
- /*
- Add your initialization code here
- */
-
- return (0);
- }
-
- /**
- * @brief TEMPLATE_DeInit
- * DeInitializes the UVC media low layer
- * @param None
- * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
- */
- static int8_t UVC_Itf_DeInit(void)
- {
- /*
- Add your deinitialization code here
- */
- return (0);
- }
-
-
- /**
- * @brief TEMPLATE_Control
- * Manage the UVC class requests
- * @param Cmd: Command code
- * @param Buf: Buffer containing command data (request parameters)
- * @param Len: Number of data to be sent (in bytes)
- * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
- */
- static int8_t UVC_Itf_Control (uint8_t cmd, uint8_t* pbuf, uint16_t length)
- {
-
- return (0);
- }
-
- /**
- * @}
- */
-
- /**
- * @}
- */
-
- /**
- * @}
- */
-
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
該文件也是空,但我們在UVC類文件中需要用到這個接口(爲了日後擴展使用),當然如果閒麻煩的話這兩個文件可以直接刪除的。代碼實現還是從最簡單的開始即usbd_desc文件,頭文件當然不需要改動了,我們改動的只有usbd_desc.c文件,在該文件中我們改動的也只是描述符而已,如下:
- /**
- ******************************************************************************
- * @file USB_Device/UVC_Standalone/Src/usbd_desc.c
- * @author MCD Application Team
- * @version V1.2.0
- * @date 19-June-2015
- * @brief This file provides the USBD descriptors and string formating method.
- ******************************************************************************
- * @attention
- *
- * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
- *
- * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.st.com/software_license_agreement_liberty_v2
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************
- */
-
- /* Includes ------------------------------------------------------------------*/
- #include "usbd_core.h"
- #include "usbd_desc.h"
- #include "usbd_conf.h"
-
- /* Private typedef -----------------------------------------------------------*/
- /* Private define ------------------------------------------------------------*/
- #define USBD_VID 0x1985
- #define USBD_PID 0x1707
- #define USBD_LANGID_STRING 0x409 //English(United States)
- #define USBD_MANUFACTURER_STRING "ANOBODYKEY"
- #define USBD_PRODUCT_FS_STRING "STM32 Video Streaming in FS Mode"
- #define USBD_CONFIGURATION_FS_STRING "UVC Config"
- #define USBD_INTERFACE_FS_STRING "UVC Interface"
-
- /* Private macro -------------------------------------------------------------*/
- /* Private function prototypes -----------------------------------------------*/
- uint8_t *USBD_UVC_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- uint8_t *USBD_UVC_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
- #ifdef USB_SUPPORT_USER_STRING_DESC
- uint8_t *USBD_UVC_USRStringDesc(USBD_SpeedTypeDef speed, uint8_t idx, uint16_t *length);
- #endif /* USB_SUPPORT_USER_STRING_DESC */
-
- /* Private variables ---------------------------------------------------------*/
- USBD_DescriptorsTypeDef UVC_Desc = {
- USBD_UVC_DeviceDescriptor,
- USBD_UVC_LangIDStrDescriptor,
- USBD_UVC_ManufacturerStrDescriptor,
- USBD_UVC_ProductStrDescriptor,
- USBD_UVC_SerialStrDescriptor,
- USBD_UVC_ConfigStrDescriptor,
- USBD_UVC_InterfaceStrDescriptor,
- };
-
- const uint8_t hUSBDDeviceDesc[USB_LEN_DEV_DESC]= {
- 0x12, /* bLength */
- USB_DESC_TYPE_DEVICE, /* bDescriptorType */
- 0x00, /* bcdUSB */
- 0x02,
- 0xEF, /* bDeviceClass */
- 0x02, /* bDeviceSubClass */
- 0x01, /* bDeviceProtocol */
- USB_MAX_EP0_SIZE, /* bMaxPacketSize*/
- LOBYTE(USBD_VID), /* idVendor */
- HIBYTE(USBD_VID), /* idVendor */
- LOBYTE(USBD_PID), /* idVendor */
- HIBYTE(USBD_PID), /* idVendor */
- 0x00, /* bcdDevice rel. 2.00 */
- 0x02,
- USBD_IDX_MFC_STR, /* Index of manufacturer string */
- USBD_IDX_PRODUCT_STR, /* Index of product string */
- USBD_IDX_SERIAL_STR, /* Index of serial number string */
- USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */
- }; /* USB_DeviceDescriptor */
-
- /* USB Standard Device Descriptor */
- const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC]= {
- USB_LEN_LANGID_STR_DESC,
- USB_DESC_TYPE_STRING,
- LOBYTE(USBD_LANGID_STRING),
- HIBYTE(USBD_LANGID_STRING),
- };
-
- uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] =
- {
- USB_SIZ_STRING_SERIAL,
- USB_DESC_TYPE_STRING,
- };
-
- uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ];
-
- /* Private functions ---------------------------------------------------------*/
- static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len);
- static void Get_SerialNum(void);
- /**
- * @brief Returns the device descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- *length = sizeof(hUSBDDeviceDesc);
- return (uint8_t *)hUSBDDeviceDesc;
- }
-
- /**
- * @brief Returns the LangID string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- *length = sizeof(USBD_LangIDDesc);
- return (uint8_t *)USBD_LangIDDesc;
- }
-
- /**
- * @brief Returns the product string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc, length);
- return USBD_StrDesc;
- }
-
- /**
- * @brief Returns the manufacturer string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
- return USBD_StrDesc;
- }
-
- /**
- * @brief Returns the serial number string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- *length = USB_SIZ_STRING_SERIAL;
-
- /* Update the serial number string descriptor with the data from the unique ID*/
- Get_SerialNum();
-
- return USBD_StringSerial;
- }
-
- /**
- * @brief Returns the configuration string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- USBD_GetString((uint8_t *)USBD_CONFIGURATION_FS_STRING, USBD_StrDesc, length);
- return USBD_StrDesc;
- }
-
- /**
- * @brief Returns the interface string descriptor.
- * @param speed: Current device speed
- * @param length: Pointer to data length variable
- * @retval Pointer to descriptor buffer
- */
- uint8_t *USBD_UVC_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
- {
- USBD_GetString((uint8_t *)USBD_INTERFACE_FS_STRING, USBD_StrDesc, length);
- return USBD_StrDesc;
- }
-
- /**
- * @brief Create the serial number string descriptor
- * @param None
- * @retval None
- */
- static void Get_SerialNum(void)
- {
- uint32_t deviceserial0, deviceserial1, deviceserial2;
-
- deviceserial0 = *(uint32_t*)DEVICE_ID1;
- deviceserial1 = *(uint32_t*)DEVICE_ID2;
- deviceserial2 = *(uint32_t*)DEVICE_ID3;
-
- deviceserial0 += deviceserial2;
-
- if (deviceserial0 != 0)
- {
- IntToUnicode (deviceserial0, &USBD_StringSerial[2] ,8);
- IntToUnicode (deviceserial1, &USBD_StringSerial[18] ,4);
- }
- }
-
- /**
- * @brief Convert Hex 32Bits value into char
- * @param value: value to convert
- * @param pbuf: pointer to the buffer
- * @param len: buffer length
- * @retval None
- */
- static void IntToUnicode (uint32_t value , uint8_t *pbuf , uint8_t len)
- {
- uint8_t idx = 0;
-
- for( idx = 0 ; idx < len ; idx ++)
- {
- if( ((value >> 28)) < 0xA )
- {
- pbuf[ 2* idx] = (value >> 28) + '0';
- }
- else
- {
- pbuf[2* idx] = (value >> 28) + 'A' - 10;
- }
-
- value = value << 4;
-
- pbuf[ 2* idx + 1] = 0;
- }
- }
-
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
這是從之前的VCP例程中的文件修改過來的,其中VID和PID自己隨便修改吧,其中的設備描述符就需要我們注意了,這裏面的內容就是我從Example文件中拷貝的,Example文檔中提供了2個示例,這裏我參考的是第一個示例Destop Video Camera Example,當然參考哪個示例都是可以的,該示例的框架圖如下所示:
圖中的概念縮寫大家可以去看UVC1.5的協議規範,都有介紹的,該圖只是作爲參考,我在實現中省略了很多中間環節的,爲了簡潔而已。設備描述符的簡介如下:
可以看到代碼中的實現跟示例是一致的,接下來就是配置描述符,也是重中之重,也是我們修改較多的地方,描述符如下
- /* USB UVC device Configuration Descriptor */
- static uint8_t USBD_UVC_CfgDesc[USB_UVC_CONFIG_DESC_SIZ] =
- {
- /*Configuration Descriptor*/
- 0x09, /* bLength: Configuation Descriptor size */
- USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
- LOBYTE(USB_UVC_CONFIG_DESC_SIZ), /* wTotalLength: Bytes returned */
- HIBYTE(USB_UVC_CONFIG_DESC_SIZ),
- 0x02, /*bNumInterfaces: 2 interface*/
- 0x01, /*bConfigurationValue: Configuration value*/
- USBD_IDX_CONFIG_STR, /*iConfiguration: Index of string descriptor describing the configuration*/
- 0x80, /*bmAttributes: bus powered device */
- 0xFA, /*MaxPower 500 mA: this current is used for detecting Vbus*/
- /* 09 */
- /*---------------------------------------------------------------------------*/
-
- /*Interface Association Descriptor*/
- 0x08, /* bLength: Interface Association Descriptor size */
- USB_DESC_TYPE_INTERFACE_ASSOCIATION, /* bDescritorType: Interface Association */
- 0x00, /* bFirstInterface: VideoControl Interface ID */
- 0x02, /* bInterfaceCount: 2 Video Interface */
- 0x0E, /* bFunctionClass: CC_VIDEO */
- 0x03, /* bFunctionSubClass: SC_VIDEO_INTERFACE_COLLECTION */
- 0x00, /* bFunctionProtocol: PC_PROTOCOL_UNDEFINED */
- USBD_IDX_INTERFACE_STR, /* iFunction: Index of string descriptor descripting the function */
-
- /*Standard VC Interface Descriptor*/
- 0x09, /* bLenght: Standard VC Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /*bDescriptorType: interface */
- 0x00, /* bInterfaceNumber: interface ID */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x00, /* bNumEndpoints: no endpoint */
- 0x0E, /* bInterfaceClass: CC_VIDEO */
- 0x01, /* bInterfaceSubClass: SC_VIDEOCONTROL */
- 0x00, /* bInterfacePortocol: PC_PROTOCOL_15 */
- USBD_IDX_INTERFACE_STR, /*iInterface: Index of string descriptor descripting the interface */
-
- /*Class-specific VC Interface Header Descriptor*/
- 0x0D, /* bLength: Class-specific VC Interface Header Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x01, /* bDescriptorSubType: VC_HEADER */
- 0x10, /* bcdUVC: UVC1.5 revision */
- 0x01,
- 0x27, /* wTotalLength: total size of class-specific descriptors */
- 0x00,
- 0x80, /* dwClockFrequency: deprecated */
- 0x8D,
- 0x5B,
- 0x00,
- 0x01, /* bInCollection: number of streaming interfaces */
- 0x01, /* baInterfaceNr(1): VideoStreaming interface 1 belongs to VC interface */
-
- /*Input Terminal Descriptor Composite*/
- 0x11, /* bLength: Input Terminal Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubType: VC_INPUT_TERMINAL */
- 0x01, /* bTerminalID: Terminal ID */
- 0x01, /* wTerminalType: ITT_CAMERA */
- 0x02,
- 0x00, /* bAssocTerminal: no association */
- 0x00, /*iTerminal: index of string descriptor descripting the terminal */
- 0x00, /* wObjectiveFocalLengthMin: No optical zoom supported */
- 0x00,
- 0x00, /* wObjectiveFocalLengthMax: No optical zoom supported */
- 0x00,
- 0x00, /* wOcularFocalLength: No optical zoom supported */
- 0x00,
- 0x02, /* bControlSize: this terminal doesn't implement any controls */
- 0x00, /* bmControls: No controls are supported */
- 0x00,
-
- /*Output Terminal Descriptor*/
- 0x09, /* bLength: Output Terminal Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x03, /* bDescriptorSubType: VC_OUTPUT_TERMINAL */
- 0x02, /* bTerminalID: Terminal ID */
- 0x01, /* wTerminalType: TT_STREAMING */
- 0x01,
- 0x00, /* bAssocTerminal: no association */
- 0x01, /* bSourceID: connect to the input terminal output pin */
- 0x00, /*iTerminal: index of string descriptor descripting the terminal */
-
- /*---------------------------------------------------------------------------*/
- /*Standard VS Interface Alternate 0 setting Descriptor*/
- 0x09, /* bLength: Standard VS Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: INTERFACE */
- 0x01, /* bInterfaceNumber: Interface ID */
- 0x00, /* bAlternateSetting: index of this alternate setting */
- 0x00, /* bNumEndpoints: 0 endpoint */
- 0x0E, /* bInterfaceClass: CC_VIDEO */
- 0x02, /* bInterfaceSubClass: SC_VIDEOSTREAMING */
- 0x00, /* bInterfaceProtocol: PC_PROTOCOL_15 */
- USBD_IDX_INTERFACE_STR, /*iInterface: Index of string descriptor descripting the interface */
-
- /*Class-specific VS Header Descriptor*/
- 0x0E, /* bLength: Class-specific VS Header Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x01, /* bDescriptorSubType: VS_INPUT_HEADER */
- 0x01, /* bNumFormats: one format descriptor follows */
- 0x3F,//0x45, /* wTotalLength: Total size of class-specific VS interface descriptors */
- 0x00,
- UVC_ISO_DATA_EP, /* bEndpointAddress: address of isochronour video data endpoint */
- 0x00, /* bmInfo: no dynamic format change supported */
- 0x02, /* bTerminalLink: the terminal ID of output Terminal */
- 0x00, /* bStillCaptureMethod: no supports still image capture method */
- 0x00, /* bTriggerSupport: no supports hardware trigger */
- 0x00, /* bTriggerUsage: initiate still image capture */
- 0x01, /* bControlSize: the size of bmaControls field */
- 0x00, /* bmaControls: no VS specific controls are supported */
-
- /*Payload Format Descriptor*/
- 0x0B, /* blength: Payload Format Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubType: VS_FORMAT_MJPEG */
- 0x01, /* bFormatIndex: index of the format descriptor */
- 0x01, /* bNumFrameDescriptor: number of frame descriptors */
- 0x01, /* bmFlags: FixedSizeSamples */
- 0x01, /* bDefaultFrameIndex: */
- 0x00, /* bAspectRatioX: not required */
- 0x00, /* bAspectRatioY: not required */
- 0x00, /* bInterlaceFlags: non-interlaced stream */
- 0x00, /* bCopyProtect: no restrictions */
-
- /*Class-specific VS Frame Descriptor*/
- 0x26, /* bLength: Class-specific VS Frame Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x07, /* bDescriptorSubType: VS_FRAME_MJPEG */
- 0x01, /* bFrameIndex: index of frame descriptor */
- 0x02, /* bmCapabilities:still image capture method 0 supported,fixed frame-rate */
- 0x80, /* wWidth: frame width */
- 0x00,
- 0xA0, /* wHeight: frame height */
- 0x00,
- 0x00, /* dwMinBitRate: */
- 0xB0,
- 0x04,
- 0x00,
- 0x00, /* dwMaxBitRate: */
- 0x00,
- 0x4B,
- 0x00,
- 0x00, /* dwMaxVideoFrameBufSize: in bytes */
- 0xA0,
- 0x00,
- 0x00,
- 0x2A, /* dwDefaultFrameInterval: */
- 0x2C,
- 0x0A,
- 0x00,
- 0x00, /* bFrameIntervalType: Continuous frame interval */
- 0x2A, /* dwMinFrameInterval: */
- 0x2C,
- 0x0A,
- 0x00,
- 0x2A, /* dwMaxFrameInterval: */
- 0x2C,
- 0x0A,
- 0x00,
- 0x00, /* dwFrameIntervalSetp: no frame interval step supported */
- 0x00,
- 0x00,
- 0x00,
-
- /*---------------------------------------------------------------------------*/
- /*Standard VS Interface Alternate setting 1 Descriptor*/
- 0x09, /* bLength: Standard VS Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: INTERFACE */
- 0x01, /* bInterfaceNumber: Interface ID */
- 0x01, /* bAlternateSetting: index of this alternate setting */
- 0x01, /* bNumEndpoints: 1 endpoint */
- 0x0E, /* bInterfaceClass: CC_VIDEO */
- 0x02, /* bInterfaceSubClass: SC_VIDEOSTREAMING */
- 0x00, /* bInterfaceProtocol: PC_PROTOCOL_15 */
- USBD_IDX_INTERFACE_STR, /*iInterface: Index of string descriptor descripting the interface */
-
- /*Endpoint Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: ENDPOINT */
- UVC_ISO_DATA_EP, /* bEndpointAddress: address of isochronour video data endpoint */
- 0x05, /* bmAttributes: Isochronous transfer Asynchronous data type */
- LOBYTE(UVC_ISO_DATA_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(UVC_ISO_DATA_PACKET_SIZE),
- 0x01, /* bInterval: */
- /********** Descriptor of UVC interface 0 Alternate setting 0 **************/
-
- };
對比之前的拓撲圖就可以知道這裏使用到了CT(相機終端),去掉了SU(選擇單元)和PU(處理單元),CT直接連至OT(輸出終端),這裏雖然是參考的UVC1.5的文檔,但最後我給改成了UVC1.1,UVC1.5其實和UVC1.1相差不多,可以使用UVCView軟件來檢查描述符是否正確,配置的具體含義看代碼中的註釋即可明白。目前UVCView只能檢查UVC1.1協議,如果使用的是UVC1.5協議,該軟件會提示錯誤,但其實並沒有錯誤。這裏是使用端點0作爲默認的控制端點,使用端點1來發送視頻數據流,且端點配置成同步端點。端點大小的取值在後面會解釋,這裏需要注意的是使用同步端點的話就一定要使用兩種接口配置,默認情況下使用接口配置0,默認接口配置中不能包含數據負載非0的同步端點,只能在交替配置中包含同步端點,關於USB不同的傳輸類型的詳細說明可以查看USB2.0協議規範的第5章節,該章節我可是仔細閱讀了一下,還試着翻譯了一下便於理解。也希望大家可以看看該章節以便對各個傳輸類型有所瞭解。STM32的USB模塊支持全速模式即通信速率爲12MBit/s,也就是1.5MByte/s,由於我的單板啥外設也沒得,因此圖像數據只能存儲在單片機中,從STM32F1Cube中找了一張128*160的圖片,來實現以15fps幀傳輸該圖片到USB主機,該圖片經過壓縮成jpeg格式大概爲9K字節,每秒15幀的話也就是每秒傳輸9K*15=135K字節,USB2.0全速模式下把1S分成了1000個微幀,即1ms爲一個微幀,視頻數據流在微幀內傳輸,全速設備在每個微幀內可以進行一次同步傳輸,由此得知,一次微幀我們需要傳輸135個字節,這裏我把同步端點的最大包大小設置成150字節,足以傳輸這張圖片了,有關爲什麼這裏是那一張JPEG圖片來傳輸可以查看USB_Video_Payload_MJPEG文檔,MJPEG就是把一幅幅JPEG格式的幀連續的播放,因此爲了簡介,這裏就是把一張JPEG圖片連續發送到USB主機。對了,在配置描述符中有用到一個接口連接描述符,在USB設備庫中是沒有定義,因此把該定義加到USB設備庫中,即usbd_def.h文件中,如下:
- #define USB_DESC_TYPE_DEVICE 1
- #define USB_DESC_TYPE_CONFIGURATION 2
- #define USB_DESC_TYPE_STRING 3
- #define USB_DESC_TYPE_INTERFACE 4
- #define USB_DESC_TYPE_ENDPOINT 5
- #define USB_DESC_TYPE_DEVICE_QUALIFIER 6
- #define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7
- #define USB_DESC_TYPE_BOS 0x0F
- #define USB_DESC_TYPE_INTERFACE_ASSOCIATION 11
至此,我們知道了所用的端點數:2個,端點類型:一個控制端點和一個同步端點,我們就可以修改usbd_conf文件了,usbd_conf.h文件如下:
- /**
- ******************************************************************************
- * @file USB_Device/UVC_Standalone/Inc/usbd_conf.h
- * @author MCD Application Team
- * @version V1.2.0
- * @date 19-June-2015
- * @brief General low level driver configuration
- ******************************************************************************
- * @attention
- *
- * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
- *
- * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.st.com/software_license_agreement_liberty_v2
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************
- */
-
- /* Define to prevent recursive inclusion -------------------------------------*/
- #ifndef __USBD_CONF_H
- #define __USBD_CONF_H
-
- /* Includes ------------------------------------------------------------------*/
- #include "stm32f1xx_hal.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- /* Exported types ------------------------------------------------------------*/
- /* Exported constants --------------------------------------------------------*/
- /* Common Config */
- #define USBD_MAX_NUM_INTERFACES 1
- #define USBD_MAX_NUM_CONFIGURATION 1
- #define USBD_MAX_STR_DESC_SIZ 0x100
- #define USBD_SUPPORT_USER_STRING 0
- #define USBD_SELF_POWERED 0
- #define USBD_DEBUG_LEVEL 0
-
- /* VIDEO Class Config */
- #define USBD_VIDEO_FPS 15
- #define ENDP1_BUF0ADDR (0xC0)
- #define ENDP1_BUF1ADDR (0xC0)
- #define VIDEO_IN_TX_ADDRESS (ENDP1_BUF0ADDR | (ENDP1_BUF1ADDR<<16))
-
- /* Exported macro ------------------------------------------------------------*/
- /* Memory management macros */
-
- /* For footprint reasons and since only one allocation is handled in the AUDIO class
- driver, the malloc/free is changed into a static allocation method */
-
- void *USBD_static_malloc(uint32_t size);
- void USBD_static_free(void *p);
-
- #define MAX_STATIC_ALLOC_SIZE 40 /*Video Class Driver Structure size*/
-
- #define USBD_malloc (uint32_t *)USBD_static_malloc
- #define USBD_free USBD_static_free
- #define USBD_memset /* Not used */
- #define USBD_memcpy /* Not used */
-
- /* DEBUG macros */
- #if (USBD_DEBUG_LEVEL > 0)
- #define USBD_UsrLog(...) printf(__VA_ARGS__);\
- printf("\n");
- #else
- #define USBD_UsrLog(...)
- #endif
-
- #if (USBD_DEBUG_LEVEL > 1)
-
- #define USBD_ErrLog(...) printf("ERROR: ") ;\
- printf(__VA_ARGS__);\
- printf("\n");
- #else
- #define USBD_ErrLog(...)
- #endif
-
- #if (USBD_DEBUG_LEVEL > 2)
- #define USBD_DbgLog(...) printf("DEBUG : ") ;\
- printf(__VA_ARGS__);\
- printf("\n");
- #else
- #define USBD_DbgLog(...)
- #endif
-
- /* Exported functions ------------------------------------------------------- */
-
- #endif /* __USBD_CONF_H */
-
- /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
該文件修改不大,首先是修改端點的緩衝區地址,這裏是參考UAC文件來修改的,使用雙緩衝,因爲STM32在把端點配置成同步端點時就自動啓用了雙緩衝,一開始我還想着,不使用雙緩衝而使用單緩衝結構發送視頻數據流,結果在測試時一直有包相同的數據跟在我發送的有效數據後面,後來在查看STM32F103C8T6參考文檔時才發現,使用同步端點必須要使用雙緩衝,因此,這裏要配置BUF0和BUF1地址,這兩個地址配成一樣或不一樣都不影響,因爲Cube庫中只對外提供了一個接口USBD_LL_Transmit,因此在USB模塊使用其中一個緩衝時,應用程序也無法靈活使用另一段緩衝,再有一個修改就是MAX_STATIC_ALLOC_SIZE把值改小了,因爲這裏是把數據發往USB主機並不需要接口很多數據,因此,UVC的類結構並不需要使用太多的內存,usbd_conf.c文件如下:
- /**
- ******************************************************************************
- * @file USB_Device/UVC_Standalone/Src/usbd_conf.c
- * @author MCD Application Team
- * @version V1.2.0
- * @date 31-July-2015
- * @brief This file implements the USB Device library callbacks and MSP
- ******************************************************************************
- * @attention
- *
- * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
- *
- * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
- * You may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.st.com/software_license_agreement_liberty_v2
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- ******************************************************************************
- */
-
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
-
- /* Private typedef -----------------------------------------------------------*/
- /* Private define ------------------------------------------------------------*/
- /* Private macro -------------------------------------------------------------*/
- /* Private variables ---------------------------------------------------------*/
- PCD_HandleTypeDef hpcd;
-
- /* Private function prototypes -----------------------------------------------*/
- /* Private functions ---------------------------------------------------------*/
-
- /*******************************************************************************
- PCD BSP Routines
- *******************************************************************************/
-
- /**
- * @brief Initializes the PCD MSP.
- * @param hpcd: PCD handle
- * @retval None
- */
- void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
-
- /* Enable the GPIOA clock */
- __HAL_RCC_GPIOA_CLK_ENABLE();
-
- /* Configure USB DM/DP pins */
- GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12);
- GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
- GPIO_InitStruct.Pull = GPIO_PULLUP;
- GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
-
- /* Enable USB Clock */
- __HAL_RCC_USB_CLK_ENABLE();
-
- /* Set USB Interrupt priority */
- HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 7, 0);
-
- /* Enable USB Interrupt */
- HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
- }
-
- /**
- * @brief De-Initializes the PCD MSP.
- * @param hpcd: PCD handle
- * @retval None
- */
- void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd)
- {
- /* Disable USB FS Clock */
- __HAL_RCC_USB_CLK_DISABLE();
- }
-
- /*******************************************************************************
- LL Driver Callbacks (PCD -> USB Device Library)
- *******************************************************************************/
-
- /**
- * @brief SetupStage callback.
- * @param hpcd: PCD handle
- * @retval None
- */
- void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
- {
- USBD_LL_SetupStage((USBD_HandleTypeDef*)hpcd->pData, (uint8_t *)hpcd->Setup);
- }
-
- /**
- * @brief DataOut Stage callback.
- * @param hpcd: PCD handle
- * @param epnum: Endpoint Number
- * @retval None
- */
- void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
- {
- USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
- }
-
- /**
- * @brief DataIn Stage callback.
- * @param hpcd: PCD handle
- * @param epnum: Endpoint Number
- * @retval None
- */
- void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
- {
- USBD_LL_DataInStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff);
- }
-
- /**
- * @brief SOF callback.
- * @param hpcd: PCD handle
- * @retval None
- */
- void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
- {
- USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData);
- }
-
- /**
- * @brief Reset callback.
- * @param hpcd: PCD handle
- {
- USBD_LL_SOF((USBD_HandleTypeDef*)hpcd->pData);
- }
-
- /**
- * @brief Reset callback.
- * @param hpcd: PCD handle
- * @retval None
- */
- void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
- {
- USBD_LL_SetSpeed((USBD_HandleTypeDef*)hpcd->pData, USBD_SPEED_FULL);
- /* Reset Device */
- USBD_LL_Reset((USBD_HandleTypeDef*)hpcd->pData);
- }
-
- /**
- * @brief&