OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 41|回复: 0

《ESP32-P4开发指南— V1.0》第五十章 USB摄像头实验

[复制链接]

1263

主题

1277

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5422
金钱
5422
注册时间
2019-5-8
在线时间
1407 小时
发表于 昨天 09:53 | 显示全部楼层 |阅读模式
第五十章 USB摄像头实验

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

在现代嵌入式系统中,USB摄像头的应用日益广泛,涵盖了从简单的图像采集到复杂的视频流处理等场景。USB摄像头作为一种即插即用的设备,具有兼容性强、传输效率高的优势,使其成为视频监控、机器视觉、图像识别等领域的理想选择。本章节将介绍如何在ESP32-P4平台上实现USB摄像头功能,并在图像数据实时显示在LCD上。
50.1 USB摄像头简介
50.2 硬件设计
50.3 程序设计
50.4 下载验证


50.1 USB摄像头简介
USB摄像头是一种通过USB接口连接计算机或其他设备,用于捕获图像和视频的数字化设备。其具备即插即用、便捷高效的特性,广泛应用于视频通信、监控、直播等领域,已成为相关场景中的主流选择。对于想深入了解USB摄像头协议与工作原理的读者,可以查阅相关技术文档和手册。本章节将专注于如何利用ESP32-P4的USB OTG接口实现USB摄像头通信。
ESP32-P4芯片的USB OTG HS接口支持USB视频设备(UVC)功能。Espressif官方在ESP-IDF中提供了USB HOST UVC的示例代码,路径为:esp-idf\examples\peripherals\usb\host\uvc。本实验将基于官方示例代码,通过ESP32-P4的USB HOST接口完成对USB摄像头设备的图像数据读取,并带领读者逐步实现这一功能。


50.2 硬件设计

50.2.1 程序功能
将USB2.0摄像头模组插入开发板上的USB HOST接口,然后根据用户接入的屏幕分辨率来获取USB摄像头图像数据,最后在LCD上显示。
本章节实验包含两个示例:
1)41_usb_camera实验:实现USB摄像头图像数据显示功能更。
2)42_usb_camera_phtot实验:实现USB摄像头图像数据显示和拍照功能。
读者可以根据实际需求选择对应的实验进行操作。

50.2.2 硬件资源

1)LED灯
       LED        0  - IO51
2)RGBLCD/MIPILCD(引脚太多,不罗列出来)
3)USB2.0 摄像头(USB HOST)
4)SD卡(42_usb_camera_phtot实验需要)
       CMD  -  IO44
       CLK   -  IO43
       D0      -  IO39
       D1      -  IO40
       D2      -  IO41
       D3      -  IO42

50.2.3 原理图
USB HOST原理图已在48.2.3小节中详细阐述,为避免重复,此处不再赘述。

50.3 程序设计

50.3.1 USB UVC的IDF驱动
usb_host_uvc组件驱动位于ESP-IDF在线组件注册表中。如果需要将该组件添加到项目工程中,可按照以下步骤操作:
1)打开ESP-IDF注册表。
2)搜索 “usb_host_uvc”组件。
3)将组件安装到项目中。
组件安装完成后,系统会自动更新main文件夹中的特殊组件清单文件idf_component.yml在项目编译时,系统会根据清单文件从注册表中下载并集成该组件到工程中。关于上述操作流程,可参考本书籍第八章的内容。
为了使用usb_host_uvc组件提供的功能,首先需要在代码中导入以下头文件:

  1. #include "libuvc/libuvc.h"
  2. #include "libuvc_helper.h"
  3. #include "libuvc_adapter.h"
  4. #include "usb/usb_host.h"
复制代码
接下来,作者将介绍本章节实验用到的usb_host_uvc函数,这些函数的描述及其作用如下:
1,为libuvc适配器配置参数libuvc_adapter_set_config

该函数用于为libuvc适配器配置参数,其函数原型如下:
  1. void libuvc_adapter_set_config(libuvc_adapter_config_t *config);
复制代码
函数形参:

1.png
表50.3.1.1 libuvc_adapter_set_config函数形参描述

返回值:
无。
config为指向配置libuvc初始化的结构体。接下来,笔者将详细介绍libuvc_adapter_config_t结构体中的各个成员变量,如下代码所示:

  1. /**
  2. * @brief 配置结构体
  3. */
  4. typedef struct {
  5. /* 当设置为 true 时,会创建事件处理的后台任务。
  6.        否则,用户需要通过调用 libuvc_adapter_handle_events 来处理事件 */
  7.     bool create_background_task;   
  8.     uint8_t task_priority;          /* 后台任务的优先级 */
  9.     uint32_t stack_size;            /* 后台任务的堆栈大小 */
  10.     libuvc_adapter_cb_t callback;   /* 用于通知连接和断开事件的回调函数 */
  11. } libuvc_adapter_config_t;
复制代码
上述结构体用于配置libuvc初始化参数,以下对各个成员做简单介绍。
1)create_background_task:
若该字段为true,则创建事件处理的后台任务;若该字段为false,则需手动调用libuvc_adapter_handle_events来处理libuvc事件。
2)task_priority:
若create_background_task为true时,该字段才有效。它用来配置libuvc后台任务的优先级。
3)stack_size:
若create_background_task为true时,该字段才有效。它用来配置libuvc后台任务的堆栈。
4)callback:
用于通知连接和断开事件的回调函数。
2,初始化UVC uvc_init
该函数用于初始化UVC,其函数原型如下:

  1. uvc_error_t uvc_init(uvc_context_t **ctx, struct libusb_context *usb_ctx);
复制代码
函数形参:

2.png
表50.3.1.2 uvc_init函数形参描述

返回值:
uvc_error_t错误码。
3,查找摄像头设备uvc_find_device
该函数用于查找摄像头设备,其函数原型如下:

  1. uvc_error_t uvc_find_device(uvc_context_t *ctx, uvc_device_t **dev,
  2.                                                     int vid, int pid, const char *sn);
复制代码
函数形参:

3.png
表50.3.1.3 uvc_find_device函数形参描述

返回值:
uvc_error_t错误码。
4,打开uvc设备uvc_open
该函数用于打开uvc设备,其函数原型如下:

  1. uvc_error_t uvc_open(uvc_device_t *dev,uvc_device_handle_t **devh);
复制代码
函数形参:

4.png
表50.3.1.4 uvc_open函数形参描述

返回值:
uvc_error_t错误码。
5,获取协商后的流控制块uvc_get_stream_ctrl_format_size
该函数用于获取协商后的流控制块,其函数原型如下:

  1. uvc_error_t uvc_get_stream_ctrl_format_size(        uvc_device_handle_t *devh,
  2.                                                                                             uvc_stream_ctrl_t *ctrl,
  3.                                                                                             enum uvc_frame_format cf,
  4.                                                                                             int width, int height,
  5.                                                                                             int fps)
复制代码
函数形参:

5.png
表50.3.1.5 uvc_get_stream_ctrl_format_size函数形参描述

返回值:
uvc_error_t错误码。
6,开启数据流uvc_start_streaming
该函数用于开启数据流,其函数原型如下:

  1. uvc_error_t uvc_start_streaming(uvc_device_handle_t *devh,
  2. uvc_stream_ctrl_t *ctrl,uvc_frame_callback_t *cb,
  3. void *user_ptr,uint8_t flags)
复制代码
函数形参:

6.png
表50.3.1.6 uvc_get_stream_ctrl_format_size函数形参描述

返回值:
uvc_error_t错误码。
7,关闭uvc设备 uvc_close
该函数用于关闭uvc设备,其函数原型如下:

  1. void uvc_close(uvc_device_handle_t *devh);
复制代码
函数形参:

7.png
表50.3.1.7 uvc_close函数形参描述

返回值:
无。
8,退出uvc设备 uvc_exit
该函数用于退出uvc设备,其函数原型如下:

  1. void uvc_exit(uvc_context_t *ctx);
复制代码
函数形参:

8.png
表50.3.1.8 uvc_close函数形参描述

返回值:
无。


50.3.2 程序流程图
第五十章 USB摄像头实验4268.png
图50.3.2.1 USB摄像头实验程序流程图

50.3.3 程序解析

1,UVC驱动
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。USB UVC驱动源码包括两个文件:usb_camera.c和usb_camera.h。
usb_camera.h主要用于声明usb_camera_init函数和USB消息等结构体,以便在其他文件中调用,具体内容不再赘述。
下面我们再解析usb_camera.c的程序,这里笔者分了几个部分来讲解,如下代码所示:
1,usb_camera_init
usb_camera_init 函数完成了USB摄像头的整个初始化过程。它首先初始化了UVC协议,以确保能够与USB摄像头建立通信,并配置了JPEG解码器来处理接收到的图像数据。随后,函数通过调用usb_host_lib_init启动USB主机库,配置相关的信号量和任务,确保USB设备能够正常连接并与系统交互。在设备连接后,函数进行设备查询并协商数据流格式,一旦流媒体传输开始,解码器便开始处理摄像头传输过来的JPEG图像数据,解码后将图像数据显示在LCD屏幕上。整个流程确保USB摄像头能够正常工作并显示实时图像。

  1. /**
  2. * @brief             USB摄像头拍照
  3. * [url=home.php?mod=space&uid=271674]@param[/url]            无
  4. * @retval            无
  5. */
  6. void usb_camera_init(void)
  7. {
  8.     uvc_context_t *ctx;
  9.     uvc_device_t *dev;
  10.     uvc_device_handle_t *devh;
  11.     uvc_stream_ctrl_t ctrl;
  12.     g_usb_camera.usb_state = USB_CAMERA_INVALID;

  13.     if (lcddev.id == 0X4342)        /* RGBLCD 0x4342 */
  14.     {
  15.         rgblcd_display_dir(1);      /* 必须设置为横屏 */
  16.         camera_width = 320;
  17.         camera_height = 240;
  18.     }
  19.     else if (lcddev.id == 0X4384)   /* RGBLCD 0x4384 */
  20.     {
  21.         rgblcd_display_dir(1);      /* 必须设置为横屏 */
  22.         camera_width = 640;
  23.         camera_height = 480;
  24.     }
  25.     else if (lcddev.id == 0x7084)   /* RGBLCD 0x7084 */
  26.     {
  27.         rgblcd_display_dir(1);      /* 必须设置为横屏 */
  28.         camera_width = 640;
  29.         camera_height = 480;
  30.     }
  31.     else if (lcddev.id == 0x7016)   /* RGBLCD 0x7016 */
  32.     {
  33.         rgblcd_display_dir(1);      /* 必须设置为横屏 */
  34.         camera_width = 800;
  35.         camera_height = 600;
  36.     }
  37.     else if (lcddev.id == 0x8394)   /* MIPILCD 0x8394 */
  38.     {
  39.         camera_width = 640;
  40.         camera_height = 480;
  41.     }
  42.     else if (lcddev.id == 0x8399)   /* MIPILCD 0x8399 */
  43.     {
  44.         camera_width = 800;
  45.         camera_height = 600;
  46.     }
  47.     else if (mipidev.id == 0x9881)
  48.     {
  49.         camera_width = 800;
  50.         camera_height = 600;
  51.     }

  52.     /* 配置JPEG硬件解码器 */
  53.     jpeg_decode_engine_cfg_t decode_eng_cfg = {
  54.         .intr_priority = 1, /* 优先级 */
  55.         .timeout_ms = 50,   /* 超时时间 */
  56.     };
  57.     /* 配置JPEG解码器 */
  58.     jpeg_new_decoder_engine(&decode_eng_cfg, &jpgd_handle);

  59.     /* 根据USB摄像头输出图像数据申请buf */
  60.     size_t rx_buffer_size = 0;
  61. rx_buf = (uint8_t*)jpeg_alloc_decoder_mem(camera_width * camera_height * 10,
  62. &rx_mem_cfg, &rx_buffer_size);
  63.    
  64.     if (rx_buf == NULL)
  65.     {
  66.         ESP_LOGE(__FUNCTION__, "alloc rx buffer error");
  67.         return ;
  68.     }

  69.     /* 创建事件组 */
  70.     app_flags = xEventGroupCreate();
  71.     assert(app_flags);

  72.     /* host初始化 */
  73.     ESP_ERROR_CHECK(usb_host_lib_init());

  74.     /* libuvc配置 */
  75.     libuvc_adapter_config_t config = {
  76.         .create_background_task = true, /* 开启libuvc回调任务 */
  77.         .task_priority = 5,             /* libuvc回调任务优先级 */
  78.         .stack_size = 4096,             /* 设置libuvc回调任务堆栈 */
  79.         .callback = libuvc_adapter_cb   /* libuvc回调函数 */
  80.     };
  81.     /* libuvc配置 */
  82.     libuvc_adapter_set_config(&config);
  83.     /* 初始化uvs */
  84.     UVC_CHECK(uvc_init(&ctx, NULL));
  85.     lcd_clear(BLACK);

  86.     while(1)
  87.     {
  88.         /* 等待设备连接 */
  89.         ESP_LOGI(TAG, "Waiting for USB UVC device connection ...");
  90.         wait_for_event(UVC_DEVICE_CONNECTED);
  91.         /* 查询设备? */
  92.         if (uvc_find_device(ctx, &dev, PID, VID, SERIAL_NUMBER) != UVC_SUCCESS)
  93.         {
  94.             ESP_LOGW(TAG, "UVC device not found");
  95.             continue; /* 继续等待UVC设备 */
  96.         }
  97.         /* 发现设备 */
  98.         ESP_LOGI(TAG, "UVC device found");
  99.         g_usb_camera.usb_state = USB_CAMERA_FIND_DEV;
  100.         /* 打开UVC设备 */
  101.         UVC_CHECK(uvc_open(dev, &devh));

  102.         /* 输出设备信息 */
  103.         uvc_print_diag(devh, stderr);
  104.         /* 协商数据流 */
  105.         if (UVC_SUCCESS == uvc_negotiate_stream_profile(devh, &ctrl))
  106.         {
  107.             /* 必须覆盖到MPS(最大数据包大小) */
  108.             ctrl.dwMaxPayloadTransferSize = 512;
  109.             /* 输出配置参数 */
  110.             uvc_print_stream_ctrl(&ctrl, stderr);
  111.             /* 开启数据流 */
  112.             UVC_CHECK(uvc_start_streaming(devh, &ctrl, frame_callback,NULL, 0));
  113.             ESP_LOGI(TAG, "Streaming...");
  114.             g_usb_camera.usb_state = USB_CAMERA_CONNET;
  115.             /* 等待关闭事件组 */
  116.             wait_for_event(UVC_DEVICE_DISCONNECTED);
  117.             /* 停止摄像头数据流传输 */
  118.             uvc_stop_streaming(devh);
  119.             ESP_LOGI(TAG, "Done streaming.");
  120.         }
  121.         else
  122.         {
  123.             g_usb_camera.usb_state = USB_CAMERA_DISCONNECT;
  124.             /* 等待摄像头连接 */
  125.             wait_for_event(UVC_DEVICE_DISCONNECTED);
  126.         }
  127.         /* 关闭uvs */
  128.         uvc_close(devh);
  129.     }
  130.     /* 退出uvs */
  131.     uvc_exit(ctx);
  132.     ESP_LOGI(TAG, "UVC exited");
  133.     /* 卸载usb host */
  134.     usb_host_lib_uinit();
  135. }
复制代码
2,uvc_negotiate_stream_profile
此函数用于协商USB摄像头的数据流配置,包括分辨率和帧率。

  1. /**
  2. * @brief             uvs协商
  3. * @param              devh:设备句柄
  4. * @param             ctrl:设备参数指针
  5. * @retval            UVC错误类型,请看uvc_error_t共用体
  6. */
  7. static uvc_error_t uvc_negotiate_stream_profile(uvc_device_handle_t *devh,
  8. uvc_stream_ctrl_t *ctrl)
  9. {
  10.     uvc_error_t res;

  11.     int attempt = 10;
  12.     /* 请求10次 */
  13.     while (attempt--)
  14.     {
  15.         /* 获取摄像头图像大小 */
  16.         res = uvc_get_stream_ctrl_format_size(devh, ctrl, FORMAT,
  17. camera_width, camera_height, FPS);

  18.         if (UVC_SUCCESS == res)
  19.         {
  20.             break;
  21.         }

  22.         ESP_LOGE(TAG, "Negotiation failed. Try again (%d) ...", attempt);
  23.     }

  24.     if (UVC_SUCCESS == res)
  25.     {
  26.         ESP_LOGI(TAG, "Negotiation complete.");
  27.     }
  28.     else
  29.     {
  30.       
  31.     }

  32.     return res;
  33. }
复制代码
3,wait_for_event
该函数用于等待特定的事件标志,直到事件发生。

  1. /**
  2. * @brief             等待事件
  3. * @param             event:事件标志位
  4. * @retval           触发事件
  5. */
  6. static EventBits_t wait_for_event(EventBits_t event)
  7. {
  8. return xEventGroupWaitBits(app_flags, event, pdTRUE, pdFALSE, portMAX_DELAY)
  9. & event;
  10. }
复制代码
4,libuvc_adapter_cb
这个函数用于接收libuvc事件,并设置相应的事件标志位。事件如连接、断开USB设备等。

  1. /**
  2. * @brief           libuvc回调函数
  3. * @param            event:uvs状态(连接/断开)
  4. * @retval            无
  5. */
  6. static void libuvc_adapter_cb(libuvc_adapter_event_t event)
  7. {
  8.     xEventGroupSetBits(app_flags, event);
  9. }
复制代码
5,frame_callback
该函数在每次接收到摄像头的图像帧时被调用。首先,使用jpeg_decoder_process函数将接收到的MJPEG图像数据解码为RGB格式。解码后的图像数据存储在指定的缓冲区中,准备进行显示。接着,调用esp_lcd_panel_draw_bitmap函数,将解码后的RGB图像数据绘制到LCD屏幕上,并根据屏幕大小和分辨率调整图像显示的位置。这样,图像就可以实时显示在LCD屏幕上,呈现出从USB摄像头获取的图像内容。

  1. /**
  2. * @brief             图像帧回调函数
  3. * @param             frame:图像帧指针
  4. * @param             ptr:无
  5. * @retval           无
  6. */
  7. void frame_callback(uvc_frame_t *frame, void *ptr)
  8. {
  9.     /* 计算居中绘制的起始坐标 */
  10.     int x_offset = (lcddev.width - camera_width) / 2;
  11.     int y_offset = (lcddev.height  - camera_height) / 2;
  12.     /* 确保坐标合法性 */
  13.     x_offset = x_offset < 0 ? 0 : x_offset;
  14.     y_offset = y_offset < 0 ? 0 : y_offset;

  15.     /* MJPEG解码 */
  16. esp_err_t ret = jpeg_decoder_process(jpgd_handle, &decode_cfg_rgb,
  17. frame->data, frame->data_bytes,
  18. rx_buf, camera_width * camera_height
  19. * 10, &out_size);
  20.    
  21.     if (ret != ESP_OK)
  22.     {
  23.         return;
  24.     }
  25.     /* LCD显示图像 */
  26. esp_lcd_panel_draw_bitmap(lcddev.lcd_panel_handle, x_offset, y_offset,
  27. camera_width + x_offset, camera_height
  28. + y_offset, rx_buf);
  29. }
复制代码
6,usb_host_lib_uinit
在usb_host_lib_uinit函数中,首先调用xSemaphoreTake等待信号量ready_to_uninstall_usb,直到USB设备卸载完成。这个信号量用于确保在卸载USB主机库之前,所有USB设备相关的操作已经处理完毕。然后,调用usb_host_uninstall卸载USB主机库,释放所有USB主机资源,以确保系统能够正常退出USB主机模式并恢复到其他操作状态。如果卸载失败,使用 ESP_LOGE 打印错误日志以便排查问题。

  1. static void usb_host_lib_uinit(void)
  2. {
  3.     xSemaphoreTake(ready_to_uninstall_usb, portMAX_DELAY);
  4.     vSemaphoreDelete(ready_to_uninstall_usb);
  5.     /* 卸载usb host */
  6.     if (usb_host_uninstall() != ESP_OK)
  7.     {
  8.         ESP_LOGE(TAG, "Failed to uninstall usb_host");
  9.     }
  10. }
复制代码
7,usb_host_lib_init
usb_host_lib_init函数中,首先调用usb_host_install安装USB主机库,初始化USB主机功能,使得系统能够与USB设备进行通信和交互。若安装成功,接着调用xSemaphoreCreateBinary创建一个二值信号量ready_to_uninstall_usb,该信号量用于同步卸载USB主机库的操作,确保在所有USB设备操作完成后再进行卸载。然后,使用xTaskCreate创建一个任务usb_lib_handler_task,该任务负责处理USB事件,如设备的插拔、设备状态的变化等。这个任务会持续运行,监听USB主机库的事件,并根据事件触发相应的处理逻辑,确保系统的USB功能正常运行。

  1. /**
  2. * @brief             usb host初始化
  3. * @param            无
  4. * @retval           ESP_OK:初始化成功,其他:初始化失败
  5. */
  6. static esp_err_t usb_host_lib_init(void)
  7. {
  8.     TaskHandle_t task_handle = NULL;
  9.     /* usb host配置 */
  10.     const usb_host_config_t host_config = {
  11.         .intr_flags = ESP_INTR_FLAG_LEVEL1
  12.     };
  13.     /* 初始化usb host */
  14.     esp_err_t err = usb_host_install(&host_config);

  15.     if (err != ESP_OK)
  16.     {
  17.         return err;
  18.     }
  19.     /* 创建二值信号量 */
  20.     ready_to_uninstall_usb = xSemaphoreCreateBinary();

  21.     if (ready_to_uninstall_usb == NULL)
  22.     {
  23.         usb_host_uninstall();
  24.         return ESP_ERR_NO_MEM;
  25.     }
  26.     /* 创建usb_events任务 */
  27. if (xTaskCreate(usb_lib_handler_task, "usb_events", 4096, NULL, 2,
  28. &task_handle) != pdPASS)
  29.     {
  30.         vSemaphoreDelete(ready_to_uninstall_usb);
  31.         usb_host_uninstall();
  32.         return ESP_ERR_NO_MEM;
  33.     }

  34.     return ESP_OK;
  35. }
复制代码
8,usb_lib_handler_task
该任务负责处理USB主机库的事件,包括设备连接和断开事件。

  1. /**
  2. * @brief            处理常见的USB主机lib事件
  3. * @param            args:未使用
  4. * @retval            无
  5. */
  6. static void usb_lib_handler_task(void *args)
  7. {
  8.     args = args;
  9.     while (1)
  10.     {
  11.         uint32_t event_flags;
  12.         usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  13.         /* 在所有客户端注销后释放设备 */
  14.         if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
  15.         {
  16.             usb_host_device_free_all();
  17.         }
  18.         /* USB主机库已释放所有设备 */
  19.         if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
  20.         {
  21.             xSemaphoreGive(ready_to_uninstall_usb);
  22.         }
  23.     }
  24.     vTaskDelete(NULL);
  25. }
复制代码

2,main.c驱动代码
在main.c里面编写如下代码。

  1. void app_main(void)
  2. {
  3.     esp_err_t ret;
  4.     ret = nvs_flash_init();         /* 初始化NVS */
  5.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  6.     {
  7.         ESP_ERROR_CHECK(nvs_flash_erase());
  8.         ESP_ERROR_CHECK(nvs_flash_init());
  9.     }
  10.     led_init();                         /* LED初始化 */
  11.     key_init();                         /* KEY初始化 */
  12.     lcd_init();                         /* LCD屏初始化 */
  13.     usb_camera_init();                  /* USB摄像头 */
  14. }
复制代码
这部分函数的主要功能是对LED、KEY和LCD进行初始化,然后进入USB摄像头操作。至于USB摄像头拍照的实验,可以参考“42_usb_camera_phtot”实验,区别在于本实验增加了SD卡挂载和图像数据保存的功能。

50.4 下载验证
下载程序后,将USB 2.0摄像头的USB A口插入到开发板上的HOST接口,此时MCU与USB摄像头经过协商后输出我们所需格式的图像数据,然后将图像数据经过JPEG硬件解码显示在LCD上。实验效果如下图所示。
1)如果运行的是41_usb_camera实验,需插入USB摄像头和LCD设备。
2)如果运行的是42_usb_camera_phtot实验,需插入USB摄像头、LCD和SD卡设备,方能对实时图像进行拍照。


第五十章 USB摄像头实验14245.png
图50.4.1 USB摄像头实验效果图
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-2-26 00:20

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表