From b5cd673330fc87f2981ecf139b24940df5cb3cfe Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Jan 2024 22:55:55 +0700 Subject: rename and add more video descriptors use struct to define uvc descriptor for video_capture since uvc is rather too complicated to use macro templates --- src/class/video/video.h | 224 ++++++++++++++++++++++++++++++----------- src/class/video/video_device.c | 20 ++-- 2 files changed, 173 insertions(+), 71 deletions(-) (limited to 'src/class') diff --git a/src/class/video/video.h b/src/class/video/video.h index d9880c291..162d4e4c4 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -29,6 +29,10 @@ #include "common/tusb_common.h" +enum { + VIDEO_BCD_1_50 = 0x0150, +}; + // Table 3-19 Color Matching Descriptor typedef enum { VIDEO_COLOR_PRIMARIES_UNDEFINED = 0x00, @@ -198,55 +202,98 @@ typedef enum { } video_terminal_type_t; //--------------------------------------------------------------------+ -// Descriptors +// Video Control (VC) Descriptors //--------------------------------------------------------------------+ /* 2.3.4.2 */ +#define tusb_desc_video_control_header_nitf_t(_nitf) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint8_t bDescriptorSubType; \ + uint16_t bcdUVC; \ + uint16_t wTotalLength; \ + uint32_t dwClockFrequency; /* deprecated */ \ + uint8_t bInCollection; \ + uint8_t baInterfaceNr[_nitf]; \ + } + +typedef tusb_desc_video_control_header_nitf_t() tusb_desc_video_control_header_t; +typedef tusb_desc_video_control_header_nitf_t(1) tusb_desc_video_control_header_1itf_t; +typedef tusb_desc_video_control_header_nitf_t(2) tusb_desc_video_control_header_2itf_t; +typedef tusb_desc_video_control_header_nitf_t(3) tusb_desc_video_control_header_3itf_t; +typedef tusb_desc_video_control_header_nitf_t(4) tusb_desc_video_control_header_4itf_t; + typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDescriptorSubType; - uint16_t bcdUVC; - uint16_t wTotalLength; - uint32_t dwClockFrequency; - uint8_t bInCollection; - uint8_t baInterfaceNr[]; -} tusb_desc_cs_video_ctl_itf_hdr_t; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t iTerminal; +} tusb_desc_video_control_input_terminal_t; + +TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_input_terminal_t) == 8, "size is not correct"); -/* 2.4.3.3 */ typedef struct TU_ATTR_PACKED { - uint8_t bHeaderLength; - union { - uint8_t bmHeaderInfo; - struct { - uint8_t FrameID: 1; - uint8_t EndOfFrame: 1; - uint8_t PresentationTime: 1; - uint8_t SourceClockReference: 1; - uint8_t PayloadSpecific: 1; - uint8_t StillImage: 1; - uint8_t Error: 1; - uint8_t EndOfHeader: 1; - }; - }; -} tusb_video_payload_header_t; + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t bSourceID; + uint8_t iTerminal; +} tusb_desc_video_control_output_terminal_t; + +TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_output_terminal_t) == 9, "size is not correct"); -/* 3.9.2.1 */ typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDescriptorSubType; - uint8_t bNumFormats; - uint16_t wTotalLength; - uint8_t bEndpointAddress; - uint8_t bmInfo; - uint8_t bTerminalLink; - uint8_t bStillCaptureMethod; - uint8_t bTriggerSupport; - uint8_t bTriggerUsage; + uint8_t bTerminalID; + uint16_t wTerminalType; + uint8_t bAssocTerminal; + uint8_t iTerminal; + + uint16_t wObjectiveFocalLengthMin; + uint16_t wObjectiveFocalLengthMax; + uint16_t wOcularFocalLength; uint8_t bControlSize; - uint8_t bmaControls[]; -} tusb_desc_cs_video_stm_itf_in_hdr_t; + uint8_t bmControls[3]; +} tusb_desc_video_control_camera_terminal_t; + +TU_VERIFY_STATIC(sizeof(tusb_desc_video_control_camera_terminal_t) == 18, "size is not correct"); + +//--------------------------------------------------------------------+ +// Video Streaming (VS) Descriptors +//--------------------------------------------------------------------+ + +/* 3.9.2.1 */ +#define tusb_desc_video_streaming_input_header_nbyte_t(_nb) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint8_t bDescriptorSubType; \ + uint8_t bNumFormats; /* Number of video payload Format descriptors for this interface */ \ + uint16_t wTotalLength; \ + uint8_t bEndpointAddress; \ + uint8_t bmInfo; /* Bit 0: dynamic format change supported */ \ + uint8_t bTerminalLink; \ + uint8_t bStillCaptureMethod; \ + uint8_t bTriggerSupport; /* Hardware trigger supported */ \ + uint8_t bTriggerUsage; \ + uint8_t bControlSize; /* sizeof of each control item */ \ + uint8_t bmaControls[_nb]; \ + } + +typedef tusb_desc_video_streaming_input_header_nbyte_t() tusb_desc_video_streaming_input_header_t; +typedef tusb_desc_video_streaming_input_header_nbyte_t(1) tusb_desc_video_streaming_input_header_1byte_t; +typedef tusb_desc_video_streaming_input_header_nbyte_t(2) tusb_desc_video_streaming_input_header_2byte_t; +typedef tusb_desc_video_streaming_input_header_nbyte_t(3) tusb_desc_video_streaming_input_header_3byte_t; +typedef tusb_desc_video_streaming_input_header_nbyte_t(4) tusb_desc_video_streaming_input_header_4byte_t; /* 3.9.2.2 */ typedef struct TU_ATTR_PACKED { @@ -259,7 +306,7 @@ typedef struct TU_ATTR_PACKED { uint8_t bTerminalLink; uint8_t bControlSize; uint8_t bmaControls[]; -} tusb_desc_cs_video_stm_itf_out_hdr_t; +} tusb_desc_video_streaming_output_header_t; typedef struct TU_ATTR_PACKED { uint8_t bLength; @@ -285,14 +332,33 @@ typedef struct TU_ATTR_PACKED { uint8_t bmaControls[]; } output; }; -} tusb_desc_cs_video_stm_itf_hdr_t; +} tusb_desc_video_streaming_inout_header_t; +// 3.9.2.6 Color Matching Descriptor +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bColorPrimaries; + uint8_t bTransferCharacteristics; + uint8_t bMatrixCoefficients; +} tusb_desc_video_streaming_color_matching_t; + +TU_VERIFY_STATIC(sizeof(tusb_desc_video_streaming_color_matching_t) == 6, "size is not correct"); + +//--------------------------------------------------------------------+ +// Format and Frame Descriptor +// Note: bFormatIndex & bFrameIndex are 1-based index +//--------------------------------------------------------------------+ + +//------------- Uncompressed -------------// +// Uncompressed payload specs: 3.1.1 format descriptor typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; uint8_t bDescriptorSubType; uint8_t bFormatIndex; - uint8_t bNumFrameDescriptors; + uint8_t bNumFrameDescriptors; // Number of frame descriptors for this format uint8_t guidFormat[16]; uint8_t bBitsPerPixel; uint8_t bDefaultFrameIndex; @@ -300,8 +366,39 @@ typedef struct TU_ATTR_PACKED { uint8_t bAspectRatioY; uint8_t bmInterlaceFlags; uint8_t bCopyProtect; -} tusb_desc_cs_video_fmt_uncompressed_t; - +} tusb_desc_video_format_uncompressed_t; + +// Uncompressed payload specs: 3.1.2 frame descriptor +#define tusb_desc_video_frame_uncompressed_nint_t(_nint) \ + struct TU_ATTR_PACKED { \ + uint8_t bLength; \ + uint8_t bDescriptorType; \ + uint8_t bDescriptorSubType; \ + uint8_t bFrameIndex; \ + uint8_t bmCapabilities; \ + uint16_t wWidth; \ + uint16_t wHeight; \ + uint32_t dwMinBitRate; \ + uint32_t dwMaxBitRate; \ + uint32_t dwMaxVideoFrameBufferSize; /* deprecated in 1.5 */ \ + uint32_t dwDefaultFrameInterval; \ + uint8_t bFrameIntervalType; \ + uint32_t dwFrameInterval[_nint]; \ + } + +typedef tusb_desc_video_frame_uncompressed_nint_t() tusb_desc_video_frame_uncompressed_t; +typedef tusb_desc_video_frame_uncompressed_nint_t(1) tusb_desc_video_frame_uncompressed_1int_t; +typedef tusb_desc_video_frame_uncompressed_nint_t(2) tusb_desc_video_frame_uncompressed_2int_t; +typedef tusb_desc_video_frame_uncompressed_nint_t(3) tusb_desc_video_frame_uncompressed_3int_t; +typedef tusb_desc_video_frame_uncompressed_nint_t(4) tusb_desc_video_frame_uncompressed_4int_t; + +// continuous = 3 intervals: min, max, step +typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompressed_continuous_t; + +TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct"); + +//------------- MJPEG -------------// +// MJPEG payload specs: 3.1.1 formatt descriptor typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; @@ -314,8 +411,12 @@ typedef struct TU_ATTR_PACKED { uint8_t bAspectRatioY; uint8_t bmInterlaceFlags; uint8_t bCopyProtect; -} tusb_desc_cs_video_fmt_mjpeg_t; +} tusb_desc_video_format_mjpeg_t; +// MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed) +typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t; + +// DV payload specs: 3.1.1 typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; @@ -323,8 +424,9 @@ typedef struct TU_ATTR_PACKED { uint8_t bFormatIndex; uint32_t dwMaxVideoFrameBufferSize; /* deprecated */ uint8_t bFormatType; -} tusb_desc_cs_video_fmt_dv_t; +} tusb_desc_video_format_dv_t; +// Frame Based payload specs: 3.1.1 typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; @@ -339,25 +441,7 @@ typedef struct TU_ATTR_PACKED { uint8_t bmInterlaceFlags; uint8_t bCopyProtect; uint8_t bVaribaleSize; -} tusb_desc_cs_video_fmt_frame_based_t; - -typedef struct TU_ATTR_PACKED { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDescriptorSubType; - uint8_t bFrameIndex; - uint8_t bmCapabilities; - uint16_t wWidth; - uint16_t wHeight; - uint32_t dwMinBitRate; - uint32_t dwMaxBitRate; - uint32_t dwMaxVideoFrameBufferSize; /* deprecated */ - uint32_t dwDefaultFrameInterval; - uint8_t bFrameIntervalType; - uint32_t dwFrameInterval[]; -} tusb_desc_cs_video_frm_uncompressed_t; - -typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t; +} tusb_desc_video_format_framebased_t; typedef struct TU_ATTR_PACKED { uint8_t bLength; @@ -373,12 +457,30 @@ typedef struct TU_ATTR_PACKED { uint8_t bFrameIntervalType; uint32_t dwBytesPerLine; uint32_t dwFrameInterval[]; -} tusb_desc_cs_video_frm_frame_based_t; +} tusb_desc_video_frame_framebased_t; //--------------------------------------------------------------------+ // Requests //--------------------------------------------------------------------+ +/* 2.4.3.3 */ +typedef struct TU_ATTR_PACKED { + uint8_t bHeaderLength; + union { + uint8_t bmHeaderInfo; + struct { + uint8_t FrameID: 1; + uint8_t EndOfFrame: 1; + uint8_t PresentationTime: 1; + uint8_t SourceClockReference: 1; + uint8_t PayloadSpecific: 1; + uint8_t StillImage: 1; + uint8_t Error: 1; + uint8_t EndOfHeader: 1; + }; + }; +} tusb_video_payload_header_t; + /* 4.3.1.1 */ typedef struct TU_ATTR_PACKED { union { diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index c16a45fcd..1affd615e 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -50,17 +50,17 @@ typedef struct { tusb_desc_interface_t std; - tusb_desc_cs_video_ctl_itf_hdr_t ctl; + tusb_desc_video_control_header_t ctl; } tusb_desc_vc_itf_t; typedef struct { tusb_desc_interface_t std; - tusb_desc_cs_video_stm_itf_hdr_t stm; + tusb_desc_video_streaming_inout_header_t stm; } tusb_desc_vs_itf_t; typedef union { - tusb_desc_cs_video_ctl_itf_hdr_t ctl; - tusb_desc_cs_video_stm_itf_hdr_t stm; + tusb_desc_video_control_header_t ctl; + tusb_desc_video_streaming_inout_header_t stm; } tusb_desc_video_itf_hdr_t; typedef struct TU_ATTR_PACKED { @@ -78,9 +78,9 @@ typedef union { uint8_t bFormatIndex; uint8_t bNumFrameDescriptors; }; - tusb_desc_cs_video_fmt_uncompressed_t uncompressed; - tusb_desc_cs_video_fmt_mjpeg_t mjpeg; - tusb_desc_cs_video_fmt_frame_based_t frame_based; + tusb_desc_video_format_uncompressed_t uncompressed; + tusb_desc_video_format_mjpeg_t mjpeg; + tusb_desc_video_format_framebased_t frame_based; } tusb_desc_cs_video_fmt_t; typedef union { @@ -93,9 +93,9 @@ typedef union { uint16_t wWidth; uint16_t wHeight; }; - tusb_desc_cs_video_frm_uncompressed_t uncompressed; - tusb_desc_cs_video_frm_mjpeg_t mjpeg; - tusb_desc_cs_video_frm_frame_based_t frame_based; + tusb_desc_video_frame_uncompressed_t uncompressed; + tusb_desc_video_frame_mjpeg_t mjpeg; + tusb_desc_video_frame_framebased_t frame_based; } tusb_desc_cs_video_frm_t; /* video streaming interface */ -- cgit v1.3.1 From 0daf8ec51b9513fbab9601fedb2150dda3927b05 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 26 Jan 2024 23:23:38 +0700 Subject: update example to work bulk mjpeg --- .../device/video_capture/src/usb_descriptors.c | 27 +++++++++++++++++++++- src/class/video/video.h | 10 +++++++- 2 files changed, 35 insertions(+), 2 deletions(-) (limited to 'src/class') diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c index 8e54d3afd..90a6b27a2 100644 --- a/examples/device/video_capture/src/usb_descriptors.c +++ b/examples/device/video_capture/src/usb_descriptors.c @@ -109,6 +109,10 @@ uint8_t const* tud_descriptor_device_cb(void) { #define EPNUM_VIDEO_IN 0x81 #endif +#if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG) + #define USE_MJPEG +#endif + // For mcus that does not have enough SRAM for frame buffer, we use fixed frame data. // To further reduce the size, we use MJPEG format instead of YUY2. // Select interface descriptor and length accordingly. @@ -197,8 +201,15 @@ typedef struct TU_ATTR_PACKED { typedef struct TU_ATTR_PACKED { tusb_desc_interface_t itf; tusb_desc_video_streaming_input_header_1byte_t header; + +#ifdef USE_MJPEG + tusb_desc_video_format_mjpeg_t format; + tusb_desc_video_frame_mjpeg_continuous_t frame; +#else tusb_desc_video_format_uncompressed_t format; tusb_desc_video_frame_uncompressed_continuous_t frame; +#endif + tusb_desc_video_streaming_color_matching_t color; tusb_desc_endpoint_t ep; } uvc_streaming_desc_t; @@ -316,14 +327,22 @@ const uvc_cfg_desc_t config_desc = { .bmaControls = { 0 } }, .format = { +#ifdef USE_MJPEG + .bLength = sizeof(tusb_desc_video_format_mjpeg_t), + .bDescriptorType = TUSB_DESC_CS_INTERFACE, + .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_MJPEG, + .bFormatIndex = 1, // 1-based index + .bNumFrameDescriptors = 1, + .bmFlags = 0, +#else .bLength = sizeof(tusb_desc_video_format_uncompressed_t), .bDescriptorType = TUSB_DESC_CS_INTERFACE, .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, - .bFormatIndex = 1, // 1-based index .bNumFrameDescriptors = 1, .guidFormat = { TUD_VIDEO_GUID_YUY2 }, .bBitsPerPixel = 16, +#endif .bDefaultFrameIndex = 1, .bAspectRatioX = 0, .bAspectRatioY = 0, @@ -331,9 +350,15 @@ const uvc_cfg_desc_t config_desc = { .bCopyProtect = 0 }, .frame = { +#ifdef USE_MJPEG + .bLength = sizeof(tusb_desc_video_frame_mjpeg_continuous_t), + .bDescriptorType = TUSB_DESC_CS_INTERFACE, + .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_MJPEG, +#else .bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t), .bDescriptorType = TUSB_DESC_CS_INTERFACE, .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, +#endif .bFrameIndex = 1, // 1-based index .bmCapabilities = 0, diff --git a/src/class/video/video.h b/src/class/video/video.h index 162d4e4c4..bed8eeecf 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -405,7 +405,7 @@ typedef struct TU_ATTR_PACKED { uint8_t bDescriptorSubType; uint8_t bFormatIndex; uint8_t bNumFrameDescriptors; - uint8_t bmFlags; + uint8_t bmFlags; // Bit 0: fixed size samples (1 = yes) uint8_t bDefaultFrameIndex; uint8_t bAspectRatioX; uint8_t bAspectRatioY; @@ -415,7 +415,15 @@ typedef struct TU_ATTR_PACKED { // MJPEG payload specs: 3.1.2 frame descriptor (same as uncompressed) typedef tusb_desc_video_frame_uncompressed_t tusb_desc_video_frame_mjpeg_t; +typedef tusb_desc_video_frame_uncompressed_1int_t tusb_desc_video_frame_mjpeg_1int_t; +typedef tusb_desc_video_frame_uncompressed_2int_t tusb_desc_video_frame_mjpeg_2int_t; +typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_mjpeg_3int_t; +typedef tusb_desc_video_frame_uncompressed_4int_t tusb_desc_video_frame_mjpeg_4int_t; +// continuous = 3 intervals: min, max, step +typedef tusb_desc_video_frame_mjpeg_3int_t tusb_desc_video_frame_mjpeg_continuous_t; + +//------------- DV -------------// // DV payload specs: 3.1.1 typedef struct TU_ATTR_PACKED { uint8_t bLength; -- cgit v1.3.1 From 55950656cbee915662a9a33477ddd549489ffcf0 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 29 Jan 2024 23:23:06 +0700 Subject: add freertos build support for video_capture example --- examples/device/video_capture/src/CMakeLists.txt | 4 + examples/device/video_capture/src/main.c | 131 ++++++++++++++++++--- examples/device/video_capture/src/tusb_config.h | 8 ++ .../device/video_capture/src/usb_descriptors.c | 29 +++-- hw/bsp/board_api.h | 19 ++- src/class/video/video.h | 2 +- 6 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 examples/device/video_capture/src/CMakeLists.txt (limited to 'src/class') diff --git a/examples/device/video_capture/src/CMakeLists.txt b/examples/device/video_capture/src/CMakeLists.txt new file mode 100644 index 000000000..cef2b46ee --- /dev/null +++ b/examples/device/video_capture/src/CMakeLists.txt @@ -0,0 +1,4 @@ +# This file is for ESP-IDF only +idf_component_register(SRCS "main.c" "usb_descriptors.c" + INCLUDE_DIRS "." + REQUIRES boards tinyusb_src) diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c index e63335e67..091eb8d0c 100644 --- a/examples/device/video_capture/src/main.c +++ b/examples/device/video_capture/src/main.c @@ -48,13 +48,25 @@ enum { static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; -void led_blinking_task(void); -void video_task(void); +void led_blinking_task(void* param); +void usb_device_task(void *param); +void video_task(void* param); -/*------------- MAIN -------------*/ +#if CFG_TUSB_OS == OPT_OS_FREERTOS +void freertos_init_task(void); +#endif + + +//--------------------------------------------------------------------+ +// Main +//--------------------------------------------------------------------+ int main(void) { board_init(); + // If using FreeRTOS: create blinky, tinyusb device, video task +#if CFG_TUSB_OS == OPT_OS_FREERTOS + freertos_init_task(); +#else // init device stack on configured roothub port tud_init(BOARD_TUD_RHPORT); @@ -64,10 +76,10 @@ int main(void) { while (1) { tud_task(); // tinyusb device task - led_blinking_task(); - - video_task(); + led_blinking_task(NULL); + video_task(NULL); } +#endif } //--------------------------------------------------------------------+ @@ -169,7 +181,7 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) { #endif -void video_task(void) { +void video_send_frame(void) { static unsigned start_ms = 0; static unsigned already_sent = 0; @@ -213,6 +225,21 @@ void video_task(void) { #endif } + +void video_task(void* param) { + (void) param; + + while(1) { + video_send_frame(); + + #if CFG_TUSB_OS == OPT_OS_FREERTOS + vTaskDelay(interval_ms / portTICK_PERIOD_MS); + #else + return; + #endif + } +} + void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { (void) ctl_idx; (void) stm_idx; @@ -231,16 +258,92 @@ int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, } //--------------------------------------------------------------------+ -// BLINKING TASK +// Blinking Task //--------------------------------------------------------------------+ -void led_blinking_task(void) { +void led_blinking_task(void* param) { + (void) param; static uint32_t start_ms = 0; static bool led_state = false; - // Blink every interval ms - if (board_millis() - start_ms < blink_interval_ms) return; // not enough time - start_ms += blink_interval_ms; + while (1) { + #if CFG_TUSB_OS == OPT_OS_FREERTOS + vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); + #else + if (board_millis() - start_ms < blink_interval_ms) return; // not enough time + #endif + + start_ms += blink_interval_ms; + board_led_write(led_state); + led_state = 1 - led_state; // toggle + } +} + +//--------------------------------------------------------------------+ +// FreeRTOS +//--------------------------------------------------------------------+ +#if CFG_TUSB_OS == OPT_OS_FREERTOS + +#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE +#define VIDEO_STACK_SIZE (configMINIMAL_STACK_SIZE*4) + +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + #define USBD_STACK_SIZE 4096 + int main(void); + void app_main(void) { + main(); + } +#else + // Increase stack size when debug log is enabled + #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) +#endif + +// static task +#if configSUPPORT_STATIC_ALLOCATION +StackType_t blinky_stack[BLINKY_STACK_SIZE]; +StaticTask_t blinky_taskdef; + +StackType_t usb_device_stack[USBD_STACK_SIZE]; +StaticTask_t usb_device_taskdef; + +StackType_t video_stack[VIDEO_STACK_SIZE]; +StaticTask_t video_taskdef; +#endif + +// USB Device Driver task +// This top level thread process all usb events and invoke callbacks +void usb_device_task(void *param) { + (void) param; + + // init device stack on configured roothub port + // This should be called after scheduler/kernel is started. + // Otherwise, it could cause kernel issue since USB IRQ handler does use RTOS queue API. + tud_init(BOARD_TUD_RHPORT); + + if (board_init_after_tusb) { + board_init_after_tusb(); + } - board_led_write(led_state); - led_state = 1 - led_state; // toggle + // RTOS forever loop + while (1) { + // put this thread to waiting state until there is new events + tud_task(); + } } + +void freertos_init_task(void) { + #if configSUPPORT_STATIC_ALLOCATION + xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); + xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); + xTaskCreateStatic(video_task, "cdc", VIDEO_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, video_stack, &video_taskdef); + #else + xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); + xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); + xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL); + #endif + + // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 + #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + vTaskStartScheduler(); + #endif +} +#endif diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h index d563c6f5c..bdfc37d87 100644 --- a/examples/device/video_capture/src/tusb_config.h +++ b/examples/device/video_capture/src/tusb_config.h @@ -57,6 +57,11 @@ #define CFG_TUSB_OS OPT_OS_NONE #endif +// Espressif IDF requires "freertos/" prefix in include path +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) +#define CFG_TUSB_OS_INC_PATH freertos/ +#endif + #ifndef CFG_TUSB_DEBUG #define CFG_TUSB_DEBUG 0 #endif @@ -103,6 +108,9 @@ // use bulk endpoint for streaming interface #define CFG_TUD_VIDEO_STREAMING_BULK 1 +//#define CFG_EXAMPLE_VIDEO_READONLY +//#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG + #ifdef __cplusplus } #endif diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c index 17e11229d..f308c75a7 100644 --- a/examples/device/video_capture/src/usb_descriptors.c +++ b/examples/device/video_capture/src/usb_descriptors.c @@ -364,7 +364,22 @@ const uvc_cfg_desc_t desc_fs_configuration = { }; #if TUD_OPT_HIGH_SPEED -uvc_cfg_desc_t desc_hs_configuration = desc_fs_configuration; +uvc_cfg_desc_t desc_hs_configuration; + +static uint8_t * get_hs_configuration_desc(void) { + static bool init = false; + + if (!init) { + desc_hs_configuration = desc_fs_configuration; + // change endpoint bulk size to 512 if bulk streaming + if (CFG_TUD_VIDEO_STREAMING_BULK) { + desc_hs_configuration.video_streaming.ep.wMaxPacketSize = 512; + } + } + init = true; + + return (uint8_t *) &desc_hs_configuration; +} // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed tusb_desc_device_qualifier_t const desc_device_qualifier = { @@ -395,7 +410,11 @@ uint8_t const* tud_descriptor_device_qualifier_cb(void) { uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { (void) index; // for multiple configurations // if link speed is high return fullspeed config, and vice versa - return (uint8_t const*) ((tud_speed_get() == TUSB_SPEED_HIGH) ? &desc_fs_configuration : &desc_hs_configuration); + if (tud_speed_get() == TUSB_SPEED_HIGH) { + return (uint8_t const*) &desc_fs_configuration; + } else { + return get_hs_configuration_desc(); + } } #endif // highspeed @@ -408,11 +427,7 @@ uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { #if TUD_OPT_HIGH_SPEED // Although we are highspeed, host may be fullspeed. if (tud_speed_get() == TUSB_SPEED_HIGH) { - // change endpoint bulk size to 512 if bulk streaming - if (CFG_TUD_VIDEO_STREAMING_BULK) { - desc_hs_configuration.video_streaming.ep.wMaxPacketSize = 512; - } - return (uint8_t const*) &desc_hs_configuration; + return get_hs_configuration_desc(); } else #endif { diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h index 404509a28..0c8a35924 100644 --- a/hw/bsp/board_api.h +++ b/hw/bsp/board_api.h @@ -33,9 +33,26 @@ extern "C" { #include #include - #include "tusb.h" +#if CFG_TUSB_OS == OPT_OS_FREERTOS +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) + // ESP-IDF need "freertos/" prefix in include path. + // CFG_TUSB_OS_INC_PATH should be defined accordingly. + #include "freertos/FreeRTOS.h" + #include "freertos/semphr.h" + #include "freertos/queue.h" + #include "freertos/task.h" + #include "freertos/timers.h" +#else + #include "FreeRTOS.h" + #include "semphr.h" + #include "queue.h" + #include "task.h" + #include "timers.h" +#endif +#endif + // Define the default baudrate #ifndef CFG_BOARD_UART_BAUDRATE #define CFG_BOARD_UART_BAUDRATE 115200 ///< Default baud rate diff --git a/src/class/video/video.h b/src/class/video/video.h index bed8eeecf..fb0d0af1d 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -398,7 +398,7 @@ typedef tusb_desc_video_frame_uncompressed_3int_t tusb_desc_video_frame_uncompre TU_VERIFY_STATIC(sizeof(tusb_desc_video_frame_uncompressed_continuous_t) == 38, "size is not correct"); //------------- MJPEG -------------// -// MJPEG payload specs: 3.1.1 formatt descriptor +// MJPEG payload specs: 3.1.1 format descriptor typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; -- cgit v1.3.1