Skip to content

Commit 57a8a7c

Browse files
CharlesDiasnashif
authored andcommitted
samples: drivers: video: add capture to lvgl sample
Add sample application to capture an image frame from a camera and send it for display on LCD via the LVGL library. Signed-off-by: Charles Dias <[email protected]>
1 parent dd15aff commit 57a8a7c

File tree

10 files changed

+317
-1
lines changed

10 files changed

+317
-1
lines changed

boards/weact/mini_stm32h743/doc/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.. mini_stm32h743:
1+
.. _mini_stm32h743:
22

33
WeAct Studio MiniSTM32H743 Core Board
44
#####################################

boards/weact/mini_stm32h743/mini_stm32h743.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ supported:
1515
- backup_sram
1616
- watchdog
1717
- qspi
18+
- video
1819
vendor: weact
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(video_capture)
6+
7+
FILE(GLOB app_sources src/*.c)
8+
target_sources(app PRIVATE ${app_sources})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# VIDEO resolution settings
2+
3+
# Copyright (c) 2024 Charles Dias <[email protected]>
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
source "Kconfig.zephyr"
7+
8+
config VIDEO_WIDTH
9+
int "Define the width of the video"
10+
default 320
11+
12+
config VIDEO_HEIGHT
13+
int "Define the height of the video"
14+
default 240
15+
16+
config VIDEO_HFLIP
17+
bool "Horizontal flip"
18+
default n
19+
20+
config VIDEO_VFLIP
21+
bool "Vertical flip"
22+
default n
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
.. zephyr:code-sample:: video-capture-to-lvgl
2+
:name: Video capture to LVGL
3+
:relevant-api: video_interface
4+
5+
Capture video frames and display them on an LCD using LVGL.
6+
7+
Description
8+
***********
9+
10+
The application uses the :ref:`Video API <video_api>` to retrieve video frames from
11+
a video capture device, write a frame count message to the console, and then send
12+
the frame to an LCD display.
13+
14+
Requirements
15+
************
16+
17+
This sample requires a supported :ref:`video capture device <video_api>` (e.g., a camera)
18+
and a :ref:`display <display_api>`.
19+
20+
Wiring
21+
******
22+
23+
On the `WeAct Studio STM32H743`_, connect the OV2640 camera module and the 0.96" ST7735
24+
TFT LCD display. Connect a USB cable from a host to the micro USB-C connector on the
25+
board to receive console output messages.
26+
27+
Building and Running
28+
********************
29+
30+
For :ref:`mini_stm32h743`, build this sample application with the following commands:
31+
32+
.. zephyr-app-commands::
33+
:zephyr-app: samples/drivers/video/capture_to_lvgl/
34+
:board: mini_stm32h743
35+
:shield: weact_ministm32h7xx_ov2640
36+
:goals: build flash
37+
:gen-args: -DCONFIG_BOOT_DELAY=2000
38+
:compact:
39+
40+
Sample Output
41+
=============
42+
43+
.. code-block:: console
44+
45+
[00:00:02.779,000] <inf> main: - Device name: dcmi@48020000
46+
[00:00:02.779,000] <inf> main: - Capabilities:
47+
[00:00:02.779,000] <inf> main: RGBP width [160; 160; 0] height [120; 120; 0]
48+
[00:00:02.779,000] <inf> main: RGBP width [176; 176; 0] height [144; 144; 0]
49+
[00:00:02.780,000] <inf> main: RGBP width [240; 240; 0] height [160; 160; 0]
50+
[00:00:02.780,000] <inf> main: RGBP width [320; 320; 0] height [240; 240; 0]
51+
[00:00:02.780,000] <inf> main: RGBP width [352; 352; 0] height [288; 288; 0]
52+
[00:00:02.780,000] <inf> main: RGBP width [640; 640; 0] height [480; 480; 0]
53+
[00:00:02.780,000] <inf> main: RGBP width [800; 800; 0] height [600; 600; 0]
54+
[00:00:02.780,000] <inf> main: RGBP width [1024; 1024; 0] height [768; 768; 0]
55+
[00:00:02.780,000] <inf> main: RGBP width [1280; 1280; 0] height [1024; 1024; 0]
56+
[00:00:02.780,000] <inf> main: RGBP width [1600; 1600; 0] height [1200; 1200; 0]
57+
[00:00:02.780,000] <inf> main: JPEG width [160; 160; 0] height [120; 120; 0]
58+
[00:00:02.780,000] <inf> main: JPEG width [176; 176; 0] height [144; 144; 0]
59+
[00:00:02.780,000] <inf> main: JPEG width [240; 240; 0] height [160; 160; 0]
60+
[00:00:02.780,000] <inf> main: JPEG width [320; 320; 0] height [240; 240; 0]
61+
[00:00:02.780,000] <inf> main: JPEG width [352; 352; 0] height [288; 288; 0]
62+
[00:00:02.780,000] <inf> main: JPEG width [640; 640; 0] height [480; 480; 0]
63+
[00:00:02.780,000] <inf> main: JPEG width [800; 800; 0] height [600; 600; 0]
64+
[00:00:02.780,000] <inf> main: JPEG width [1024; 1024; 0] height [768; 768; 0]
65+
[00:00:02.780,000] <inf> main: JPEG width [1280; 1280; 0] height [1024; 1024; 0]
66+
[00:00:02.780,000] <inf> main: JPEG width [1600; 1600; 0] height [1200; 1200; 0]
67+
[00:00:02.852,000] <inf> main: - Format: RGBP 160x120 320
68+
[00:00:02.854,000] <inf> main: - Capture started
69+
70+
References
71+
**********
72+
73+
.. _WeAct Studio STM32H743: https://github.com/WeActStudio/MiniSTM32H7xx
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#
2+
# Copyright (c) 2024 Charles Dias <[email protected]>
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
CONFIG_LOG_BUFFER_SIZE=2048
8+
9+
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=102400
10+
CONFIG_VIDEO_WIDTH=160
11+
CONFIG_VIDEO_HEIGHT=120
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* Copyright (c) 2024 Charles Dias <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
*/
7+
8+
&st7735r_160x80 {
9+
madctl = <184>; /* Rotate the image 180 degrees. */
10+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CONFIG_VIDEO=y
2+
CONFIG_VIDEO_SW_GENERATOR=y
3+
4+
CONFIG_PRINTK=y
5+
CONFIG_LOG=y
6+
7+
CONFIG_MAIN_STACK_SIZE=4096
8+
9+
CONFIG_DISPLAY=y
10+
CONFIG_DISPLAY_LOG_LEVEL_ERR=y
11+
12+
CONFIG_LVGL=y
13+
CONFIG_LV_CONF_MINIMAL=y
14+
CONFIG_LV_MEM_CUSTOM=y
15+
CONFIG_LV_USE_IMG=y
16+
CONFIG_LV_Z_MEM_POOL_SIZE=16384
17+
CONFIG_LV_USE_PERF_MONITOR=y
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
sample:
2+
name: Video capture to LVGL
3+
tests:
4+
sample.video.capture_to_lvgl:
5+
tags:
6+
- video
7+
- samples
8+
platform_allow:
9+
- mini_stm32h743
10+
depends_on: video
11+
integration_platforms:
12+
- mini_stm32h743
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright (c) 2024 Charles Dias <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/device.h>
9+
#include <zephyr/drivers/display.h>
10+
#include <zephyr/drivers/video.h>
11+
#include <lvgl.h>
12+
13+
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
14+
#include <zephyr/logging/log.h>
15+
LOG_MODULE_REGISTER(main);
16+
17+
#define VIDEO_DEV_SW "VIDEO_SW_GENERATOR"
18+
19+
int main(void)
20+
{
21+
struct video_buffer *buffers[2], *vbuf;
22+
const struct device *display_dev;
23+
struct video_format fmt;
24+
struct video_caps caps;
25+
const struct device *video_dev;
26+
size_t bsize;
27+
int i = 0;
28+
29+
display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
30+
if (!device_is_ready(display_dev)) {
31+
LOG_ERR("Device not ready, aborting test");
32+
return 0;
33+
}
34+
35+
#if DT_HAS_CHOSEN(zephyr_camera)
36+
video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
37+
if (!device_is_ready(video_dev)) {
38+
LOG_ERR("%s device is not ready", video_dev->name);
39+
return 0;
40+
}
41+
#else
42+
video_dev = device_get_binding(VIDEO_DEV_SW);
43+
if (video_dev == NULL) {
44+
LOG_ERR("%s device not found", VIDEO_DEV_SW);
45+
return 0;
46+
}
47+
#endif
48+
49+
LOG_INF("- Device name: %s", video_dev->name);
50+
51+
/* Get capabilities */
52+
if (video_get_caps(video_dev, VIDEO_EP_OUT, &caps)) {
53+
LOG_ERR("Unable to retrieve video capabilities");
54+
return 0;
55+
}
56+
57+
LOG_INF("- Capabilities:");
58+
while (caps.format_caps[i].pixelformat) {
59+
const struct video_format_cap *fcap = &caps.format_caps[i];
60+
/* four %c to string */
61+
LOG_INF(" %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]",
62+
(char)fcap->pixelformat, (char)(fcap->pixelformat >> 8),
63+
(char)(fcap->pixelformat >> 16), (char)(fcap->pixelformat >> 24),
64+
fcap->width_min, fcap->width_max, fcap->width_step, fcap->height_min,
65+
fcap->height_max, fcap->height_step);
66+
i++;
67+
}
68+
69+
/* Get default/native format */
70+
if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) {
71+
LOG_ERR("Unable to retrieve video format");
72+
return 0;
73+
}
74+
75+
/* Set format */
76+
fmt.width = CONFIG_VIDEO_WIDTH;
77+
fmt.height = CONFIG_VIDEO_HEIGHT;
78+
fmt.pitch = fmt.width * 2;
79+
fmt.pixelformat = VIDEO_PIX_FMT_RGB565;
80+
81+
if (video_set_format(video_dev, VIDEO_EP_OUT, &fmt)) {
82+
LOG_ERR("Unable to set up video format");
83+
return 0;
84+
}
85+
86+
LOG_INF("- Format: %c%c%c%c %ux%u %u", (char)fmt.pixelformat, (char)(fmt.pixelformat >> 8),
87+
(char)(fmt.pixelformat >> 16), (char)(fmt.pixelformat >> 24), fmt.width, fmt.height,
88+
fmt.pitch);
89+
90+
/* Size to allocate for each buffer */
91+
bsize = fmt.pitch * fmt.height;
92+
93+
/* Alloc video buffers and enqueue for capture */
94+
for (i = 0; i < ARRAY_SIZE(buffers); i++) {
95+
buffers[i] = video_buffer_alloc(bsize);
96+
if (buffers[i] == NULL) {
97+
LOG_ERR("Unable to alloc video buffer");
98+
return 0;
99+
}
100+
101+
video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]);
102+
}
103+
104+
#ifdef CONFIG_VIDEO_HFLIP
105+
/* Video flip image horizontally */
106+
if (video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1)) {
107+
LOG_ERR("Unable to set video control (HFLIP)");
108+
return 0;
109+
}
110+
#endif
111+
112+
#ifdef CONFIG_VIDEO_VFLIP
113+
/* Video flip image vertically */
114+
if (video_set_ctrl(video_dev, VIDEO_CID_VFLIP, (void *)1)) {
115+
LOG_ERR("Unable to set video control (VFLIP)");
116+
return 0;
117+
}
118+
#endif
119+
120+
/* Start video capture */
121+
if (video_stream_start(video_dev)) {
122+
LOG_ERR("Unable to start capture (interface)");
123+
return 0;
124+
}
125+
126+
display_blanking_off(display_dev);
127+
128+
const lv_img_dsc_t video_img = {
129+
.header.always_zero = 0,
130+
.header.w = CONFIG_VIDEO_WIDTH,
131+
.header.h = CONFIG_VIDEO_HEIGHT,
132+
.data_size = CONFIG_VIDEO_WIDTH * CONFIG_VIDEO_HEIGHT * sizeof(lv_color_t),
133+
.header.cf = LV_IMG_CF_TRUE_COLOR,
134+
.data = (const uint8_t *)buffers[0]->buffer,
135+
};
136+
137+
lv_obj_t *screen = lv_img_create(lv_scr_act());
138+
139+
LOG_INF("- Capture started");
140+
141+
/* Grab video frames */
142+
while (1) {
143+
int err;
144+
145+
err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER);
146+
if (err) {
147+
LOG_ERR("Unable to dequeue video buf");
148+
return 0;
149+
}
150+
151+
lv_img_set_src(screen, &video_img);
152+
lv_obj_align(screen, LV_ALIGN_BOTTOM_LEFT, 0, 0);
153+
154+
lv_task_handler();
155+
156+
err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf);
157+
if (err) {
158+
LOG_ERR("Unable to requeue video buf");
159+
return 0;
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)