Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

samples: drivers: video: add capture to lvgl sample #71463

Merged
merged 1 commit into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boards/weact/mini_stm32h743/doc/index.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.. mini_stm32h743:
.. _mini_stm32h743:

WeAct Studio MiniSTM32H743 Core Board
#####################################
Expand Down
1 change: 1 addition & 0 deletions boards/weact/mini_stm32h743/mini_stm32h743.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ supported:
- watchdog
- usb
- qspi
- video
vendor: weact
8 changes: 8 additions & 0 deletions samples/drivers/video/capture_to_lvgl/CMakeLists.txt
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})
22 changes: 22 additions & 0 deletions samples/drivers/video/capture_to_lvgl/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# VIDEO resolution settings

# Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
# SPDX-License-Identifier: Apache-2.0

source "Kconfig.zephyr"

config VIDEO_WIDTH
int "Define the width of the video"
default 320

config VIDEO_HEIGHT
int "Define the height of the video"
default 240

config VIDEO_HFLIP
bool "Horizontal flip"
default n

config VIDEO_VFLIP
bool "Vertical flip"
default n
73 changes: 73 additions & 0 deletions samples/drivers/video/capture_to_lvgl/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.. zephyr:code-sample:: video-capture-to-lvgl
:name: Video capture to LVGL
:relevant-api: video_interface

Capture video frames and display them on an LCD using LVGL.

Description
***********

The application uses the :ref:`Video API <video_api>` to retrieve video frames from
a video capture device, write a frame count message to the console, and then send
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>`.
josuah marked this conversation as resolved.
Show resolved Hide resolved

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
11 changes: 11 additions & 0 deletions samples/drivers/video/capture_to_lvgl/boards/mini_stm32h743.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
#
# SPDX-License-Identifier: Apache-2.0
#

CONFIG_LOG_BUFFER_SIZE=2048

CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=102400
CONFIG_VIDEO_WIDTH=160
CONFIG_VIDEO_HEIGHT=120
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
*
* SPDX-License-Identifier: Apache-2.0
*
*/

&st7735r_160x80 {
madctl = <184>; /* Rotate the image 180 degrees. */
};
17 changes: 17 additions & 0 deletions samples/drivers/video/capture_to_lvgl/prj.conf
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
12 changes: 12 additions & 0 deletions samples/drivers/video/capture_to_lvgl/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
sample:
name: Video capture to LVGL
tests:
sample.video.capture_to_lvgl:
tags:
- video
- samples
platform_allow:
- mini_stm32h743
depends_on: video
erwango marked this conversation as resolved.
Show resolved Hide resolved
integration_platforms:
- mini_stm32h743
162 changes: 162 additions & 0 deletions samples/drivers/video/capture_to_lvgl/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
*
* 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;
}
}
}
Loading