本示例展示了如何使用视频设备驱动接口来配置和捕获视频数据流,基于gc0328摄像头。
int video_get_caps (const struct device * dev, enum video_endpoint_id ep, struct video_caps * caps)
参数说明
字段 | 说明 |
---|---|
dev | 指向驱动程序实例的设备结构的指针 |
ep | 终结点 ID |
caps | 指向要填充的video_caps结构的指针 |
返回值说明
0 成功,否则为 -ERRNO 代码。
int video_get_caps (const struct device * dev, enum video_endpoint_id ep, struct video_caps * caps)
参数说明
字段 | 说明 |
---|---|
dev | 指向驱动程序实例的设备结构的指针 |
ep | 终结点 ID |
fmt | 指向视频格式结构的指针 |
返回值说明
字段 | 说明 |
---|---|
dev | 成功 |
-EINVAL | 参数无效 |
-ENOTSUP | 不支持格式 |
-EIO | 输入/输出错误 |
struct video_buffer * video_buffer_alloc (size_t size)
参数说明
字段 | 说明 |
---|---|
size | 视频缓冲区的大小(以字节为单位) |
返回值说明
指针到分配的视频缓冲区
更多Video Interface Driver API接口可查看Zephyr官网Video Interface Driver APIs。
SDK 中提供了 video 的示例。
{SDK}\.sdk\csk\samples\driver\video\gc0328
展示了如何使用视频设备驱动接口来配置和捕获视频数据流,基于gc0328摄像头。
适用开发板:大模型开发套件
编译版型:csk6_duomotai_devkit
在 SDK 根目录(duomotai_ap
)下可通过执行以下指令进行对该示例工程的编译:
lisa zep build -b csk6_duomotai_devkit .sdk/csk/samples/driver/video/gc0328/ -p
编译完成后,编译产物二进制文件位于 build\zephyr\zephyr.bin
使用 Type-C 数据线连接开发套件的 DAP_USB
接口,选中以下其中一种方式对固件进行烧录:
cskburn desktop
是一款聆思推出的桌面烧录工具,在下载并安装 cskburn desktop 烧录工具后,双击图标运行软件:
1.点击串口下拉框,选择连接开发套件后识别到的串口编号;
2.将编译输出的.bin
文件拖拽进烧录区域;
3.点击开始烧录,等待烧录完成。
若您已按照 《环境搭建》 教程完成开发环境的安装,可在编译完成后执行 lisa zep exec cskburn
指令完成烧录。
lisa zep exec cskburn -s \\.\COMxx -C 6 -b 1500000 0x000000 --verify-all .\build\zephyr\zephyr.bin
请将命令行中的的 COMx 替换为开发套件在 PC 上对应的串口号(可通过设备管理器查看)。例如:
COM3
。
lisa zep exec cskburn -s PORT -C 6 0x000000 --verify-all ./build/zephyr/zephyr.bin -b 1500000
请将命令行中的 PORT 替换为开发套件连接在 PC 上对应的串口号。例如:
/dev/ttyUSB0
。
烧录完成后,连接串口终端,按下开发板复位按钮,可看到串口有对应的信息输出。
以下代码与注释已省略一部分非关键接口代码,主要呈现示例的主业务流程与主要接口的使用。
# 设备设置
CONFIG_GPIO=y
CONFIG_I2C=y
CONFIG_VIDEO=y
CONFIG_VIDEO_CSK6_DVP=y
CONFIG_VIDEO_GC0328=y
#log 设置
CONFIG_LOG=y
CONFIG_LOG_PRINTK=y
# 缓冲区空间设置
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=800000
CONFIG_VIDEO_CUSTOM_SECTION=y
CONFIG_VIDEO_CUSTOM_SECTION_NAME=".psram_section"
CONFIG_CSK6_PSRAM=y
设备树文件csk6_duomotai_devkit.overlay
配置如下:
/*
* Copyright (c) 2023 Anhui(Shenzhen) Listenai Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
/delete-node/ &psram_ap;
/delete-node/ &psram_share;
&psram0 {
psram_ap: psram_ap@30600000 {
compatible = "zephyr,memory-region","listenai,csk6-psram-partition";
reg = <0x30600000 0x700000>;
status = "okay";
zephyr,memory-region = "PSRAMAP";
};
psram_cp: psram_cp@30000000 {
compatible = "listenai,csk6-psram-partition";
reg = <0x30000000 0x100000>;
status = "okay";
};
psram_share: psram_share@307b0000 {
compatible = "listenai,csk6-psram-partition";
reg = <0x307b0000 0x50000>;
status = "okay";
};
};
1.从设备树中获取设备并初始化视频设备
video = DEVICE_DT_GET(DT_NODELABEL(dvp));
2.获取视频设备的能力
video_get_caps(video, VIDEO_EP_OUT, &caps)
3.设置视频格式
ret = kscan_config(kscan_dev, kb_callback);
4.分配视频缓冲区并将其加入捕获队列
buffers[i] = video_buffer_alloc(bsize)
5.开始视频捕获
video_start(video, VIDEO_EP_OUT)
6.取出视频帧判断是否需要下采样并发送
frame_send(vbuf->buffer, fmt.width, fmt.height, 0);
7.打印日志
LOG_INF("Got frame %u! size: %u; timestamp %u ms fps %d", frame++,vbuf->bytesused, vbuf->timestamp, calculate_fps(vbuf))
按下开发板复位按钮,运行程序
int main(void)
{
struct video_buffer *buffers[VBUF_NUM], *vbuf;
struct video_format fmt;
struct video_caps caps;
unsigned int frame = 0;
size_t bsize;
int i = 0;
video = DEVICE_DT_GET(DT_NODELABEL(dvp));
if (video == NULL) {
LOG_ERR("Video device %s not found, "
"fallback to software generator.",
"dvp");
return 0;
}
printk("- Device name: %s\n", "dvp");
// /* Get capabilities */
if (video_get_caps(video, VIDEO_EP_OUT, &caps)) {
LOG_ERR("Unable to retrieve video capabilities");
return 0;
}
printk("- Capabilities:\n");
while (caps.format_caps[i].pixelformat) {
const struct video_format_cap *fcap = &caps.format_caps[i];
/* fourcc to string */
printk(" %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]\n",
(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++;
}
fmt.pixelformat = VIDEO_PIX_FMT_VYUY;
fmt.width = IMAGE_WIDTH;
fmt.height = IMAGE_HEIGHT;
fmt.pitch = fmt.width * PIXEL_BYTES_SIZE;
if (video_set_format(video, VIDEO_EP_OUT, &fmt)) {
LOG_ERR("Unable to set video format");
return 0;
}
/* Size to allocate for each buffer */
bsize = fmt.height * fmt.pitch;
/* Alloc video buffers and enqueue for capture */
for (i = 0; i < ARRAY_SIZE(buffers); i++) {
LOG_INF("Alloc vide buffer: %d\n", bsize);
buffers[i] = video_buffer_alloc(bsize);
if (buffers[i] == NULL) {
LOG_ERR("Unable to alloc video buffer");
return 0;
}
video_enqueue(video, VIDEO_EP_OUT, buffers[i]);
}
/* Start video capture */
if (video_stream_start(video)) {
LOG_ERR("Unable to start capture (interface)");
return 0;
}
/* Grab video frames */
while (1) {
int err;
err = video_dequeue(video, VIDEO_EP_OUT, &vbuf, K_FOREVER);
if (err) {
LOG_ERR("Unable to dequeue video buf");
return 0;
}
#if USE_SERIAL_PROTOCOL
#ifdef WEBUSB_IMAGE_DOWNSAMPLING
uint32_t dst_width = IMAGE_WIDTH * WEBUSB_IMAGE_SCALE;
uint32_t dst_height = IMAGE_HEIGHT * WEBUSB_IMAGE_SCALE;
downsample(vbuf->buffer, resample_img,
IMAGE_WIDTH, IMAGE_HEIGHT, WEBUSB_IMAGE_DOWNSAMPLING, 0);
frame_send(resample_img, dst_width, dst_height, 0);
#else
frame_send(vbuf->buffer, fmt.width, fmt.height, 0);
#endif
#endif
LOG_INF("Got frame %u! size: %u; timestamp %u ms fps %d", frame++,
vbuf->bytesused, vbuf->timestamp, calculate_fps(vbuf));
err = video_enqueue(video, VIDEO_EP_OUT, vbuf);
if (err) {
LOG_ERR("Unable to requeue video buf");
return 0;
}
}
}