-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]>
- Loading branch information
1 parent
ca48767
commit b91521a
Showing
9 changed files
with
337 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(video_capture) | ||
|
||
FILE(GLOB app_sources src/*.c) | ||
target_sources(app PRIVATE ${app_sources}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# VIDEO resolution settings | ||
|
||
# Copyright (c) 2024 Charles Dias <[email protected]> | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
source "Kconfig.zephyr" | ||
|
||
config VIDEO_WIDTH | ||
int "Define the width of the video" | ||
default 160 | ||
|
||
config VIDEO_HEIGHT | ||
int "Define the height of the video" | ||
default 120 | ||
|
||
config VIDEO_HFLIP | ||
bool "Horizontal flip" | ||
default n | ||
|
||
config VIDEO_VFLIP | ||
bool "Vertical flip" | ||
default n |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
.. zephyr:code-sample:: video-capture-to-lvgl | ||
:name: Video capture to LVGL | ||
:relevant-api: video_interface | ||
|
||
This sample application demonstrates how to use the video API to retrieve video | ||
frames from a capture device and display them on an LCD via the LVGL library. | ||
|
||
Description | ||
*********** | ||
|
||
The application uses the :ref:`Video API <video_api>` to retrieve video frames from | ||
a video capture device, writes a frame count message to the console, and then sends | ||
the frame to an LCD display. | ||
|
||
Requirements | ||
************ | ||
|
||
This sample requires a supported :ref:`video capture device <video_api>` (e.g., a camera) | ||
and a :ref:`display <display_api>`. | ||
|
||
Wiring | ||
****** | ||
|
||
On the `WeAct Studio STM32H743`_, connect the OV2640 camera module and the 0.96" ST7735 | ||
TFT LCD display. Connect a USB cable from a host to the micro USB-C connector on the | ||
board to receive console output messages. | ||
|
||
Building and Running | ||
******************** | ||
|
||
For :ref:`mini_stm32h743`, build this sample application with the following commands: | ||
|
||
.. zephyr-app-commands:: | ||
:zephyr-app: samples/drivers/video/capture_to_lvgl/ | ||
:board: mini_stm32h743 | ||
:shield: weact_ministm32h7xx_ov2640 | ||
:goals: build flash | ||
:gen-args: -DCONFIG_BOOT_DELAY=2000 | ||
:compact: | ||
|
||
Sample Output | ||
============= | ||
|
||
.. code-block:: console | ||
[00:00:02.779,000] <inf> main: - Device name: dcmi@48020000 | ||
[00:00:02.779,000] <inf> main: - Capabilities: | ||
[00:00:02.779,000] <inf> main: RGBP width [160; 160; 0] height [120; 120; 0] | ||
[00:00:02.779,000] <inf> main: RGBP width [176; 176; 0] height [144; 144; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [240; 240; 0] height [160; 160; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [320; 320; 0] height [240; 240; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [352; 352; 0] height [288; 288; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [640; 640; 0] height [480; 480; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [800; 800; 0] height [600; 600; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [1024; 1024; 0] height [768; 768; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [1280; 1280; 0] height [1024; 1024; 0] | ||
[00:00:02.780,000] <inf> main: RGBP width [1600; 1600; 0] height [1200; 1200; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [160; 160; 0] height [120; 120; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [176; 176; 0] height [144; 144; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [240; 240; 0] height [160; 160; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [320; 320; 0] height [240; 240; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [352; 352; 0] height [288; 288; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [640; 640; 0] height [480; 480; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [800; 800; 0] height [600; 600; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [1024; 1024; 0] height [768; 768; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [1280; 1280; 0] height [1024; 1024; 0] | ||
[00:00:02.780,000] <inf> main: JPEG width [1600; 1600; 0] height [1200; 1200; 0] | ||
[00:00:02.852,000] <inf> main: - Format: RGBP 160x120 320 | ||
[00:00:02.854,000] <inf> main: - Capture started | ||
References | ||
********** | ||
|
||
.. _WeAct Studio STM32H743: https://github.com/WeActStudio/MiniSTM32H7xx |
12 changes: 12 additions & 0 deletions
12
samples/drivers/video/capture_to_lvgl/boards/mini_stm32h743.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# | ||
# Copyright (c) 2024 Charles Dias <[email protected]> | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
# | ||
|
||
CONFIG_CLOCK_STM32_MCO1_SRC_HSI48=y | ||
CONFIG_CLOCK_STM32_MCO1_DIV=4 | ||
|
||
CONFIG_LOG_BUFFER_SIZE=2048 | ||
|
||
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=102400 |
31 changes: 31 additions & 0 deletions
31
samples/drivers/video/capture_to_lvgl/boards/mini_stm32h743.overlay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright (c) 2024 Charles Dias <[email protected]> | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
*/ | ||
|
||
&pll { | ||
div-m = <5>; | ||
mul-n = <96>; | ||
div-p = <2>; | ||
div-q = <2>; | ||
div-r = <2>; | ||
clocks = <&clk_hse>; | ||
status = "okay"; | ||
}; | ||
|
||
&rcc { | ||
clocks = <&pll>; | ||
clock-frequency = <DT_FREQ_M(240)>; | ||
d1cpre = <1>; | ||
hpre = <1>; | ||
d1ppre = <2>; | ||
d2ppre1 = <2>; | ||
d2ppre2 = <2>; | ||
d3ppre = <2>; | ||
}; | ||
|
||
&st7735r_160x80 { | ||
madctl = <184>; /* Rotate the image 180 degrees. */ | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CONFIG_VIDEO=y | ||
CONFIG_VIDEO_SW_GENERATOR=y | ||
|
||
CONFIG_PRINTK=y | ||
CONFIG_LOG=y | ||
|
||
CONFIG_MAIN_STACK_SIZE=4096 | ||
|
||
CONFIG_DISPLAY=y | ||
CONFIG_DISPLAY_LOG_LEVEL_ERR=y | ||
|
||
CONFIG_LVGL=y | ||
CONFIG_LV_CONF_MINIMAL=y | ||
CONFIG_LV_MEM_CUSTOM=y | ||
CONFIG_LV_USE_IMG=y | ||
CONFIG_LV_Z_MEM_POOL_SIZE=16384 | ||
CONFIG_LV_USE_PERF_MONITOR=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
sample: | ||
name: Video capture to LVGL | ||
tests: | ||
sample.video.capture_to_lvgl: | ||
build_only: true | ||
tags: | ||
- video | ||
platform_allow: | ||
- mini_stm32h743 | ||
depends_on: video |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright (c) 2024 Charles Dias <[email protected]> | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <zephyr/kernel.h> | ||
#include <zephyr/device.h> | ||
#include <zephyr/drivers/display.h> | ||
#include <zephyr/drivers/video.h> | ||
#include <lvgl.h> | ||
|
||
#define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL | ||
#include <zephyr/logging/log.h> | ||
LOG_MODULE_REGISTER(main); | ||
|
||
#define VIDEO_DEV_SW "VIDEO_SW_GENERATOR" | ||
|
||
int main(void) | ||
{ | ||
struct video_buffer *buffers[2], *vbuf; | ||
const struct device *display_dev; | ||
struct video_format fmt; | ||
struct video_caps caps; | ||
const struct device *video_dev; | ||
size_t bsize; | ||
int i = 0; | ||
|
||
display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); | ||
if (!device_is_ready(display_dev)) { | ||
LOG_ERR("Device not ready, aborting test"); | ||
return 0; | ||
} | ||
|
||
#if DT_HAS_CHOSEN(zephyr_camera) | ||
video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera)); | ||
if (!device_is_ready(video_dev)) { | ||
LOG_ERR("%s device is not ready", video_dev->name); | ||
return 0; | ||
} | ||
#else | ||
video_dev = device_get_binding(VIDEO_DEV_SW); | ||
if (video_dev == NULL) { | ||
LOG_ERR("%s device not found", VIDEO_DEV_SW); | ||
return 0; | ||
} | ||
#endif | ||
|
||
LOG_INF("- Device name: %s", video_dev->name); | ||
|
||
/* Get capabilities */ | ||
if (video_get_caps(video_dev, VIDEO_EP_OUT, &caps)) { | ||
LOG_ERR("Unable to retrieve video capabilities"); | ||
return 0; | ||
} | ||
|
||
LOG_INF("- Capabilities:"); | ||
while (caps.format_caps[i].pixelformat) { | ||
const struct video_format_cap *fcap = &caps.format_caps[i]; | ||
/* four %c to string */ | ||
LOG_INF(" %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]", | ||
(char)fcap->pixelformat, (char)(fcap->pixelformat >> 8), | ||
(char)(fcap->pixelformat >> 16), (char)(fcap->pixelformat >> 24), | ||
fcap->width_min, fcap->width_max, fcap->width_step, fcap->height_min, | ||
fcap->height_max, fcap->height_step); | ||
i++; | ||
} | ||
|
||
/* Get default/native format */ | ||
if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) { | ||
LOG_ERR("Unable to retrieve video format"); | ||
return 0; | ||
} | ||
|
||
/* Set format */ | ||
fmt.width = CONFIG_VIDEO_WIDTH; | ||
fmt.height = CONFIG_VIDEO_HEIGHT; | ||
fmt.pitch = fmt.width * 2; | ||
fmt.pixelformat = VIDEO_PIX_FMT_RGB565; | ||
|
||
if (video_set_format(video_dev, VIDEO_EP_OUT, &fmt)) { | ||
LOG_ERR("Unable to set up video format"); | ||
return 0; | ||
} | ||
|
||
LOG_INF("- Format: %c%c%c%c %ux%u %u", (char)fmt.pixelformat, (char)(fmt.pixelformat >> 8), | ||
(char)(fmt.pixelformat >> 16), (char)(fmt.pixelformat >> 24), fmt.width, fmt.height, | ||
fmt.pitch); | ||
|
||
/* Size to allocate for each buffer */ | ||
bsize = fmt.pitch * fmt.height; | ||
|
||
/* Alloc video buffers and enqueue for capture */ | ||
for (i = 0; i < ARRAY_SIZE(buffers); i++) { | ||
buffers[i] = video_buffer_alloc(bsize); | ||
if (buffers[i] == NULL) { | ||
LOG_ERR("Unable to alloc video buffer"); | ||
return 0; | ||
} | ||
|
||
video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]); | ||
} | ||
|
||
#ifdef CONFIG_VIDEO_HFLIP | ||
/* Video flip image horizontally */ | ||
if (video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1)) { | ||
LOG_ERR("Unable to set video control (HFLIP)"); | ||
return 0; | ||
} | ||
#endif | ||
|
||
#ifdef CONFIG_VIDEO_VFLIP | ||
/* Video flip image vertically */ | ||
if (video_set_ctrl(video_dev, VIDEO_CID_VFLIP, (void *)1)) { | ||
LOG_ERR("Unable to set video control (VFLIP)"); | ||
return 0; | ||
} | ||
#endif | ||
|
||
/* Start video capture */ | ||
if (video_stream_start(video_dev)) { | ||
LOG_ERR("Unable to start capture (interface)"); | ||
return 0; | ||
} | ||
|
||
display_blanking_off(display_dev); | ||
|
||
const lv_img_dsc_t video_img = { | ||
.header.always_zero = 0, | ||
.header.w = CONFIG_VIDEO_WIDTH, | ||
.header.h = CONFIG_VIDEO_HEIGHT, | ||
.data_size = CONFIG_VIDEO_WIDTH * CONFIG_VIDEO_HEIGHT * sizeof(lv_color_t), | ||
.header.cf = LV_IMG_CF_TRUE_COLOR, | ||
.data = (const uint8_t *)buffers[0]->buffer, | ||
}; | ||
|
||
lv_obj_t *screen = lv_img_create(lv_scr_act()); | ||
|
||
LOG_INF("- Capture started"); | ||
|
||
/* Grab video frames */ | ||
while (1) { | ||
int err; | ||
|
||
err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER); | ||
if (err) { | ||
LOG_ERR("Unable to dequeue video buf"); | ||
return 0; | ||
} | ||
|
||
lv_img_set_src(screen, &video_img); | ||
lv_obj_align(screen, LV_ALIGN_BOTTOM_LEFT, 0, 0); | ||
|
||
lv_task_handler(); | ||
|
||
err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf); | ||
if (err) { | ||
LOG_ERR("Unable to requeue video buf"); | ||
return 0; | ||
} | ||
} | ||
} |