Skip to main content

UVC_Jpeg_Display

一 软件配置

1.1 LCD相关配置

打开iconfigtool配置页面

以RD_AD100_EVB_V1.0开发板为例 , 屏幕为FW050(MIPI DSI interface), 使用ad100_nor_defconfig配置 , 实际根据硬件需要配置

1

lcd设备配置

2

1.2 设备电源管理配置

不配置tp可以不配置, 但需要打开

9

1.3 背光配置

3

1.4 framebuffer 配置

4

1.5 USB驱动配置

5

6

USB通过IO/USB_SW(PB05) , 控制主从切换

1.6 Jpeg编解码配置

7

保存配置

8

二 测试程序

2.1 编写测试程序

uvc代码位于freertos/example/usb/host/uvc_jpeg_display_ad100.c

#include <stdio.h>
#include <os.h>
#include <driver/backlight.h>
#include <driver/fb.h>
#include <driver/camera.h>
#include <common.h>

#include <usb/host_uvc.h>
#include <little_things.h>

#include <jpegd_decoder.h>
#include <malloc.h>
#include <driver/cache.h>
#include <limits.h>

/*
某些场景下解码和刷屏同时使用buf,可能会出现显示异常。
可尝试启用 DECODE_USE_OTHER_BUF,解码和刷屏将使用不同的buf。
*/
// #define DECODE_USE_OTHER_BUF

#ifdef DECODE_USE_OTHER_BUF
struct jpegd_decoder_output *nv12[2];
#else
struct jpegd_decoder_output *nv12[1];
#endif

static thread_ptr_t uvc_jpeg_display_thread;

static void uvc_insert_wakeup_display(u32 devices_bit)
{
thread_wakeup(uvc_jpeg_display_thread);
}

static int uvc_fb_init(struct fb_handle **fb, struct fb_info *fb_info)
{
struct backlight *fb_backlight;

fb_backlight = backlight_open("backlight_gpio0");
if (!fb_backlight)
printf("backlight_open fail.\n");
else
backlight_set_brightness(fb_backlight, fb_backlight->max_brightness);

*fb = fb_open("fb0");
if (!(*fb)) {
printf("open fb0 error!\n");
return -1;
}

fb_enable(*fb);
memset(fb_info, 0, sizeof(struct fb_info));
fb_get_info(*fb, fb_info);

printf("opened fb w[%d] h[%d]\n", fb_info->xres, fb_info->yres);
return 0;
}

static int get_fmt_first_index(struct uvc_host_video *video, unsigned int need_format, u8 *get_format_index, u8 *get_frame_index)
{
int w = 0;
int h = 0;
char *fmt = NULL;
int format_index = 0, frame_index = 0;
int format_firts_index = -1, frame_firts_index = -1;

/* 找到所需格式的首个分辨率 */
for (format_index = 0; format_index < video->nformats; format_index++) {
for (frame_index = 0; frame_index < video->formats[format_index].nframes; frame_index++) {
fmt = (char *)&(video->formats[format_index].fcc);
w = video->formats[format_index].frames[frame_index].wWidth;
h = video->formats[format_index].frames[frame_index].wHeight;

printf("\tformat[%d] frame[%d] w[%d] h[%d] fmt[%s]\n", format_index, frame_index, w, h, fmt);

if (video->formats[format_index].fcc != need_format)
continue;

if ((format_firts_index == -1) || (frame_firts_index == -1)) {
format_firts_index = format_index;
frame_firts_index = frame_index;
}
}
}

if ((format_firts_index == -1) || (frame_firts_index == -1)) {
/* 格式不匹配,导致没有记录分辨率 */
fmt = (char *)&need_format;
printf("[%s] format is not found in this uvc!\n", fmt);
return -1;
}

*get_format_index = format_firts_index;
*get_frame_index = frame_firts_index;
return 0;
}

static int uvc_init(u8 uvc_dev_index, struct uvc_host_video *video, struct uvc_video_format *video_format)
{
int ret = 0;
char *fmt = NULL;
int w = 0, h = 0;
u32 frame_size = 0;
u8 format_index = 0, frame_index = 0;

ret = usb_host_uvc_open(uvc_dev_index, NULL);
if (ret) {
printf("usb_host_uvc_open uvc[%d] fail!\n", uvc_dev_index);
return ret;
}

printf("opened dev[%d] video num %d\n", uvc_dev_index, usb_host_uvc_get_video_num(uvc_dev_index));

memset(video, 0, sizeof(struct uvc_host_video));
ret = usb_host_uvc_get_video(uvc_dev_index, 0, video);
if (ret) {
printf("uvc get video fail %d\n", ret);
goto err_close_uvc;
}

ret = get_fmt_first_index(video, V4L2_PIX_FMT_MJPEG, &format_index, &frame_index);
if (ret) {
printf("get_fmt_first_index fail!\n");
goto err_close_uvc;
}

w = video->formats[format_index].frames[frame_index].wWidth;
h = video->formats[format_index].frames[frame_index].wHeight;
printf("select format[%d] frame[%d] w[%d] h[%d]\n", format_index, frame_index, w, h);

ret = usb_host_uvc_set_format(uvc_dev_index, video, format_index, frame_index, 0);
if (ret) {
printf("uvc set format fail %d\n", ret);
goto err_close_uvc;
}

memset(video_format, 0, sizeof(struct uvc_video_format));
ret = usb_host_uvc_get_format(uvc_dev_index, video, video_format);
if (ret) {
printf("uvc get format fail %d\n", ret);
goto err_close_uvc;
}

fmt = (char *)&(video_format->fcc);
printf("using uvc format[%d] frame[%d] w[%d] h[%d] fmt[%s]\n", format_index,
frame_index, video_format->width, video_format->height, fmt);

if (!video_format->bpp)
video_format->bpp = 8;

frame_size = video_format->width * video_format->height * video_format->bpp / 8;

ret = usb_host_uvc_request_buffer(uvc_dev_index, video, 3, frame_size);
if (ret) {
printf("uvc request buffer fail %d\n", ret);
goto err_close_uvc;
}

ret = usb_host_uvc_stream_on(uvc_dev_index, video);
if (ret) {
printf("uvc stream on fail %d\n", ret);
goto err_free_buffer;
}
return ret;

err_free_buffer:
usb_host_uvc_free_buffer(uvc_dev_index, video);
err_close_uvc:
usb_host_uvc_close(uvc_dev_index);
printf("uvc%d close\n", uvc_dev_index);
return ret;
}

static void uvc_jpeg_to_nv12_display_test(void *pdata)
{
int i = 0;
int ret = 0;
u32 uvc_dev = 0;
u8 disconnect = 0;
u8 uvc_dev_index = 0;
struct uvc_buffer *buf = NULL;
struct uvc_host_video video = {0};
struct uvc_video_format video_format= {0};

int count = 0;
int buf_index = 0;
struct jpegd_decoder *decoder = NULL;
int buf_num = sizeof(nv12) / sizeof (void *);
struct jpegd_decoder_output *decode_out = NULL;
struct jpegd_decoder_param jpeg_decoder_param = {0};

struct fb_handle *fb = NULL;
struct fb_info fb_info = {0};

u8 *jpeg_code = NULL;

ret = uvc_fb_init(&fb, &fb_info);
if (ret) {
printf("uvc_fb_init fail!\n");
return ;
}

struct lcdc_layer layer_cfg = {
.fb_fmt = fb_fmt_NV12,
.xpos = 0,
.ypos = 0,

.layer_order = lcdc_layer_bottom,
.layer_enable = 1,

.alpha = {
.enable = 0,
.value = 0xff,
},

/* 将解码的图像内容拉伸显示到屏幕 */
.scaling.enable = 1,
.scaling.xres = fb_info.xres,
.scaling.yres = fb_info.yres,
};


i = 0;
while (1) {
uvc_dev = usb_host_uvc_get_devices_bit();
for (; i < 32; i++) {
if (uvc_dev & (1 << i)) {
break;
}
}

if (i == 32) {
i = 0;
goto wait_wakeup;
}

uvc_dev_index = i;
ret = uvc_init(uvc_dev_index, &video, &video_format);
if (ret) {
i++;
continue;
}

jpeg_decoder_param.width = video_format.width;
jpeg_decoder_param.height = video_format.height;
jpeg_decoder_param.out_fmt = JPEGD_PIX_FMT_NV12,

decoder = jpegd_decoder_init(&jpeg_decoder_param);
if (!decoder) {
printf("helix_jpeg_decoder_init fail!\n");
goto err_stop_uvc;
}
printf("jpeg decoder set [%dx%d]\n", jpeg_decoder_param.width, jpeg_decoder_param.height);

for (buf_index = 0; buf_index < buf_num; buf_index++) {
nv12[buf_index] = jpegd_decoder_alloc_output_buf(decoder);
if (!nv12[buf_index]) {
printf("malloc mem for nv12[%d] fail!\n", buf_index);
goto err_stop_decoder;
}
}
printf("using nv12 buf num[%d]!\n", buf_num);

count = 0;
disconnect = 0;
while (1) {
ret = usb_host_uvc_get_buffer(uvc_dev_index, &video, &buf, 5000);
if (ret) {
printf("uvc get buffer fail %d\n", ret);
goto err_stop_decoder;
}

if (buf->state == UVC_BUF_STATE_ERROR && buf->bytesused == 0) {
disconnect = 1;
} else if (buf->state == UVC_BUF_STATE_DONE) {
/* 交替使用不同buf解码刷屏 */
buf_num > 1 ? (decode_out = nv12[count%2]) : (decode_out = nv12[0]);

jpeg_code = buf->mem;
if (jpeg_code[0] != 0xff || jpeg_code[1] != 0xd8 || jpeg_code[buf->bytesused-2] != 0xff || jpeg_code[buf->bytesused-1] != 0xd9)
goto skip_display;

ret = jpegd_decoder_decode(decoder, buf->mem, buf->bytesused, decode_out);
if (ret) {
printf("jpeg decode frame fail, skip display!\n");
goto skip_display;
}

if (fb) {
layer_cfg.xres = video_format.width;
layer_cfg.yres = video_format.height;

layer_cfg.y.mem = (void *)virt_to_phys(decode_out->data);
layer_cfg.uv.mem = (void *)virt_to_phys(decode_out->data)+decode_out->width*decode_out->height;

layer_cfg.y.stride = decode_out->width;
layer_cfg.uv.stride = decode_out->width;

fb_set_config(fb, &layer_cfg);
fb_enable_config(fb);
fb_pan_display(fb, 0);

count++;
}
}

skip_display:
ret = usb_host_uvc_put_buffer(uvc_dev_index, &video, buf);
if (ret) {
printf("uvc put buffer fail %d\n", ret);
goto err_stop_decoder;
}

if (disconnect)
goto err_stop_decoder;
}

usb_host_uvc_put_buffer(uvc_dev_index, &video, buf);
err_stop_decoder:
jpegd_decoder_deinit(decoder);
for (buf_index--; buf_index >= 0; buf_index--)
jpegd_decoder_free_output_buf(nv12[buf_index]);
err_stop_uvc:
usb_host_uvc_stream_off(uvc_dev_index, &video);
usb_host_uvc_free_buffer(uvc_dev_index, &video);
usb_host_uvc_close(uvc_dev_index);
printf("uvc%d close\n", uvc_dev_index);
wait_wakeup:
thread_wait();
}
}

void uvc_jpeg_display_test(void)
{
uvc_jpeg_display_thread = thread_create("uvc_jpeg_display_thread", 8*1024, uvc_jpeg_to_nv12_display_test, NULL);
if (uvc_jpeg_display_thread)
usb_host_uvc_register_callback(uvc_insert_wakeup_display);
}

2.2 编译和使用测试程序

修改 vendor/vendor.c, 内容如下:

#include <stdio.h>
#include <../example/usb/host/uvc_jpeg_display_ad100.c>
#include <common.h>

void vendor_init(void *arg)
{
uvc_jpeg_display_test();
printf("vendor init...\n");
}

三 编译和烧录

bhu@bhu-PC:~/rtos$ cd freertos                      
bhu@bhu-PC:~/rtos/freertos$ source build/envsetup.sh //第一次编译需要初始化编译环境
bhu@bhu-PC:~/rtos/freertos$ make ad100_nor_defconfig
bhu@bhu-PC:~/rtos/freertos$ make
bhu@bhu-PC:~/rtos/freertos$ ls rtos-with-spl.bin 
rtos-with-spl.bin //编译出来的文件

请使用最新版烧录工具

ubuntu版本烧录工具请下载

windows版本烧录工具请下载

烧录配置

10

11

12

四 测试验证

开机后插入usb摄像头

U-Boot SPL 2013.07-00035-gc0c7a2ebd-dirty (Dec 27 2023 - 11:45:03)
ERROR EPC 80038ef4
CPA_CPAPCR:0320490d
CPM_CPMPCR:0320490d
CPM_CPEPCR:0190510d
CPM_CPCCR:9a073310
DDR: M14F5121632A type is : DDR2
GD25B512ME 00c8471a 00c8471a
[0.003658] sfc_nor: find chip:GD25B512ME id:0xc8471a
[0.005800] USB Core Release: 3.00a (snpsid=4f54300a)
[0.207081] Host Mode
[0.207160] new USB bus registered, assigned bus number 1
[0.207424] New USB device devnum 1, busnum 1
[0.207577] New USB device found, idVendor=0000, idProduct=0000
[0.207790] New USB device strings: Mfr=3, Product=2, SerialNumber=1
[0.208020] Product: DWC OTG Controller
[0.208155] Manufacturer: Ingenic dwc2_hsotg
[0.208305] SerialNumber: dwc2
[0.208410] configuration #1 chosen from 1 choice
[0.208583] devnum 1: adding (config #1, interface 0)
[0.208764] devnum 1: USB hub found
[0.208890] devnum 1: 1 port detected
[0.308149] vendor init...
[0.309376] ++OTG Interrupt gotgint=80000 [a_host]
[0.309546] ++OTG Interrupt: Debounce Done++
[0.558085] new high speed USB device using and address 2
[0.718566] New USB device devnum 2, busnum 1
[0.718719] New USB device found, idVendor=3350, idProduct=a488
[0.718932] New USB device strings: Mfr=1, Product=2, SerialNumber=3
[0.719162] Product: UHD 4K Camera
[0.719279] Manufacturer: SHENZHEN AONI ELECTRONIC LTD
[0.719462] SerialNumber: AN20210728001
[0.719597] configuration #1 chosen from 1 choice
[0.719875] devnum 2: adding (config #1, interface 0)
[0.720108] uvc_parse_format: Found format MJPG
[0.720268] - 320x240 (30.0 fps)
[0.720377] - 480x272 (30.0 fps)
[0.720487] - 640x360 (30.0 fps)
[0.720597] - 640x480 (30.0 fps)
[0.720707] - 800x600 (30.0 fps)
[0.720817] - 1024x576 (30.0 fps)
[0.720930] - 1280x720 (30.0 fps)
[0.721043] - 1440x1080 (30.0 fps)
[0.721160] - 1920x1080 (60.0 fps)
[0.721277] - 2560x1440 (30.0 fps)
[0.721393] - 2592x1944 (24.0 fps)
[0.721510] - 3840x2160 (14.0 fps)
[0.721628] uvc_parse_format: Found format YUYV
[0.721787] - 320x240 (30.0 fps)
[0.721897] - 480x272 (30.0 fps)
[0.722007] - 640x360 (30.0 fps)
[0.722117] - 640x480 (30.0 fps)
[0.722227] - 800x600 (15.0 fps)
[0.722337] - 1024x576 (10.0 fps)
[0.722450] - 1280x720 (10.0 fps)
[0.722565] uvc_parse_format: Found format H264
[0.722723] - 320x240 (30.0 fps)
[0.722833] - 480x272 (30.0 fps)
[0.722943] - 640x360 (30.0 fps)
[0.723053] - 640x480 (30.0 fps)
[0.723163] - 800x600 (30.0 fps)
[0.723273] - 1024x576 (30.0 fps)
[0.723387] - 1280x720 (30.0 fps)
[0.723500] - 1440x1080 (30.0 fps)
[0.723617] - 1920x1080 (60.0 fps)
[0.723733] - 2560x1440 (30.0 fps)
[0.723850] - 2592x1944 (24.0 fps)
[0.723967] - 3840x2160 (24.0 fps)
[0.724092] Found UVC 1.00 device UHD 4K Camera (3350:a488)
[0.724295] Found a valid video chain (1 -> 4)
[0.724661] UVC device initialized
[0.724825] devnum 2: adding (config #1, interface 1)
[0.725003] devnum 2: adding (config #1, interface 2)
[0.725184] devnum 2: adding (config #1, interface 3)
[0.781086] opened fb w[720] h[1280]
[0.781210] opened dev[0] video num 1
[0.781336] format[0] frame[0] w[320] h[240] fmt[MJPG]
[0.781525] format[0] frame[1] w[480] h[272] fmt[MJPG]
[0.781716] format[0] frame[2] w[640] h[360] fmt[MJPG]
[0.781906] format[0] frame[3] w[640] h[480] fmt[MJPG]
[0.782095] format[0] frame[4] w[800] h[600] fmt[MJPG]
[0.782285] format[0] frame[5] w[1024] h[576] fmt[MJPG]
[0.782479] format[0] frame[6] w[1280] h[720] fmt[MJPG]
[0.782672] format[0] frame[7] w[1440] h[1080] fmt[MJPG]
[0.782869] format[0] frame[8] w[1920] h[1080] fmt[MJPG]
[0.783065] format[0] frame[9] w[2560] h[1440] fmt[MJPG]
[0.783262] format[0] frame[10] w[2592] h[1944] fmt[MJPG]
[0.783462] format[0] frame[11] w[3840] h[2160] fmt[MJPG]
[0.783662] format[1] frame[0] w[320] h[240] fmt[YUYV]
[0.783849] format[1] frame[1] w[480] h[272] fmt[YUYV]
[0.784035] format[1] frame[2] w[640] h[360] fmt[YUYV]
[0.784222] format[1] frame[3] w[640] h[480] fmt[YUYV]
[0.784409] format[1] frame[4] w[800] h[600] fmt[YUYV]
[0.784595] format[1] frame[5] w[1024] h[576] fmt[YUYV]
[0.784785] format[1] frame[6] w[1280] h[720] fmt[YUYV]
[0.784975] format[2] frame[0] w[320] h[240] fmt[H264]
[0.785165] format[2] frame[1] w[480] h[272] fmt[H264]
[0.785356] format[2] frame[2] w[640] h[360] fmt[H264]
[0.785545] format[2] frame[3] w[640] h[480] fmt[H264]
[0.785735] format[2] frame[4] w[800] h[600] fmt[H264]
[0.785925] format[2] frame[5] w[1024] h[576] fmt[H264]
[0.786119] format[2] frame[6] w[1280] h[720] fmt[H264]
[0.786312] format[2] frame[7] w[1440] h[1080] fmt[H264]
[0.786509] format[2] frame[8] w[1920] h[1080] fmt[H264]
[0.786705] format[2] frame[9] w[2560] h[1440] fmt[H264]
[0.786902] format[2] frame[10] w[2592] h[1944] fmt[H264]
[0.787103] format[2] frame[11] w[3840] h[2160] fmt[H264]
[0.787303] select format[0] frame[0] w[320] h[240]
[0.787728] using uvc format[0] frame[0] w[320] h[240] fmt[MJPG@]
[0.788197] jpeg decoder set [320x240]
[0.788334] using nv12 buf num[1]!

13