庐山派,摄像头实验-捕获一张图像,无法完成

Viewed 58

问题描述


采用rtos+linux sdk,参考东山派的相关教程,发现无法复现

在程序运行上出现问题

  1. 正确的显示:
    image.png
  2. 我的显示:
    image.png

查看运行时的输出,请问有问题吗

image.png

考虑两个开发板不同型号,但不知道如何下手

相应的的代码

  1. sample_vicap_100ask.c文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>

#include "k_module.h"
#include "k_type.h"
#include "k_vb_comm.h"
#include "k_vicap_comm.h"
#include "k_video_comm.h"
#include "k_sys_comm.h"
#include "mpi_vb_api.h"
#include "mpi_vicap_api.h"
#include "mpi_isp_api.h"
#include "mpi_sys_api.h"

#include "mpi_sensor_api.h"

#define VICAP_OUTPUT_BUF_NUM 6
#define VICAP_INPUT_BUF_NUM 4

#define DISPLAY_WITDH  1088
#define DISPLAY_HEIGHT 1920

typedef struct {
    k_vicap_dev dev_num;
    k_bool dev_enable;
    k_vicap_sensor_type sensor_type;
    k_vicap_sensor_info sensor_info;

    k_u16 in_width;
    k_u16 in_height;

    //for mcm
    k_vicap_work_mode mode;
    k_u32 in_size;
    k_pixel_format in_format;

    k_vicap_input_type input_type;
    k_vicap_image_pattern pattern;
    const char *file_path;//input raw image file
    const char *calib_file;
    void *image_data;
    k_u32 dalign;

    k_bool ae_enable;
    k_bool awb_enable;
    k_bool dnr3_enable;
    k_bool hdr_enable;

    k_vicap_chn chn_num[VICAP_CHN_ID_MAX];

    k_bool chn_enable[VICAP_CHN_ID_MAX];
    k_pixel_format out_format[VICAP_CHN_ID_MAX];
    
    k_bool crop_enable[VICAP_CHN_ID_MAX];

    k_vicap_window out_win[VICAP_CHN_ID_MAX];

    k_vicap_window crop_win[VICAP_CHN_ID_MAX];

    k_u32 buf_size[VICAP_CHN_ID_MAX];

    k_video_frame_info dump_info[VICAP_CHN_ID_MAX];

    k_bool preview[VICAP_CHN_ID_MAX];
    k_u16 rotation[VICAP_CHN_ID_MAX];
    k_u8 fps[VICAP_CHN_ID_MAX];
    k_bool dw_enable;
    k_vicap_mirror sensor_mirror;
} vicap_device_obj;

//初始化视频缓存区
static k_s32 sample_vicap_vb_init(vicap_device_obj *dev_obj)
{
    k_s32 ret = 0;
    k_vb_config config;
    k_vb_supplement_config supplement_config;

    memset(&config, 0, sizeof(config));
    config.max_pool_cnt = 64;

    int k = 0;
    int i = 0;
        printf("%s, enable dev(%d)\n", __func__, i);

    int j = 0;
    printf("%s, enable chn(%d), k(%d)\n", __func__, j, k);
    config.comm_pool[k].blk_cnt = VICAP_OUTPUT_BUF_NUM; //缓冲区数量
    config.comm_pool[k].mode = VB_REMAP_MODE_NOCACHE; //无缓存重映射

    k_u16 out_width = dev_obj[i].out_win[j].width; //输出宽度
    k_u16 out_height = dev_obj[i].out_win[j].height; //输出高度


    config.comm_pool[k].blk_size = VICAP_ALIGN_UP((out_width * out_height * 3 / 2), VICAP_ALIGN_1K);//计算每个缓冲区的大小,并且进行对齐

    dev_obj[i].buf_size[j] = config.comm_pool[k].blk_size;//每个缓冲区大小
    printf("%s, dev(%d) chn(%d) pool(%d) buf_size(%d) blk_cnt(%d)\n", __func__, i, j, k ,dev_obj[i].buf_size[j], config.comm_pool[k].blk_cnt);
    
    ret = kd_mpi_vb_set_config(&config); //设置MPP视频缓存池属性
    if (ret) {
        printf("vb_set_config failed ret:%d\n", ret);
        return ret;
    }

    memset(&supplement_config, 0, sizeof(supplement_config));
    supplement_config.supplement_config |= VB_SUPPLEMENT_JPEG_MASK;

    ret = kd_mpi_vb_set_supplement_config(&supplement_config);//设置 VB 内存的附加信息,这里增加的是DCF信息,如拍摄的时间、拍摄的时候是否有闪光灯、数码缩放倍数等
    if (ret) {
        printf("vb_set_supplement_config failed ret:%d\n", ret);
        return ret;
    }

    ret = kd_mpi_vb_init();//初始化 MPP 视频缓存池
    if (ret) {
        printf("vb_init failed ret:%d\n", ret);
        return ret;
    }

    return 0;
}



#define VICAP_MIN_PARAMETERS (2)

static void usage(void)
{
    printf("usage: ./sample_vicap  -dev 0\n");
    printf("Options:\n");
    printf(" -dev:          vicap device id[0,1,2]\tdefault 0\n");


    printf(" -help:         print this help\n");

    exit(1);
}

extern k_u32 display_cnt;
extern k_u32 drop_cnt;
extern uint32_t hdr_buf_base_phy_addr;
extern void *hdr_buf_base_vir_addr;


static void vb_exit() {
    kd_mpi_vb_exit();
}

static uint64_t get_ticks()
{
    static volatile uint64_t time_elapsed = 0;
    __asm__ __volatile__(
        "rdtime %0"
        : "=r"(time_elapsed));
    return time_elapsed;
}

int main(int argc, char *argv[])
{
    // _set_mod_log(K_ID_VI, 6);
    k_s32 ret = 0;

    vicap_device_obj device_obj[VICAP_DEV_ID_MAX];
    memset(&device_obj, 0 , sizeof(device_obj));

    k_vicap_dev_attr dev_attr;
    k_vicap_chn_attr chn_attr;

    k_video_frame_info dump_info;

    k_u8 dev_count = 0, cur_dev = 0;
    k_u8 chn_count = 0, cur_chn = 0;

    k_u32 pipe_ctrl = 0xFFFFFFFF;
    memset(&dev_attr, 0, sizeof(k_vicap_dev_attr));

    if (argc < VICAP_MIN_PARAMETERS) {
        printf("sample_vicap requires some necessary parameters:\n");
        usage();
    }
    
    if (strcmp(argv[1], "-help") == 0)
    {
        usage();
    }
    else if (strcmp(argv[1], "-dev") == 0)
    {
        chn_count = 0;
        cur_dev =  atoi(argv[2]);;
        dev_count++;
        printf("cur_dev(%d), dev_count(%d)\n", cur_dev, dev_count);
        
        device_obj[cur_dev].dev_num = cur_dev; //sensor设备号
        device_obj[cur_dev].dev_enable = K_TRUE; //是否启用设备
        device_obj[cur_dev].ae_enable = K_TRUE;//default enable ae 自动曝光
        device_obj[cur_dev].awb_enable = K_TRUE;//default enable awb 自动白平衡
        device_obj[cur_dev].dnr3_enable = K_FALSE;//default disable 3ndr 3D降噪
        device_obj[cur_dev].hdr_enable = K_FALSE;//default disable hdr 高动态范围
        device_obj[cur_dev].dw_enable = K_FALSE;//default disable dw 宽动态模式
        
        //parse dev paramters
        printf("cur_chn(%d), chn_count(%d)\n", cur_chn ,chn_count); 
        device_obj[cur_dev].chn_num[cur_chn] = cur_chn; //VI通道的编号
        device_obj[cur_dev].chn_enable[cur_chn] = K_TRUE; //是否启动通道
        device_obj[cur_dev].preview[cur_chn] = K_TRUE;//default enable preview 通道的预览功能
        device_obj[cur_dev].sensor_type =  GC2093_MIPI_CSI2_1920X1080_30FPS_10BIT_LINEAR;//传感器类型
    }
    else
    {
        printf("invalid paramters.\n");
        usage();
    }

    printf("sample_vicap: dev_count(%d), chn_count(%d)\n", dev_count, chn_count);

    int dev_num = 0;
    dev_attr.input_type = VICAP_INPUT_TYPE_SENSOR;//输入类型为sensor数据 
    //vicap get sensor info 根据指定的sensor配置类型获取sensor配置信息
    ret = kd_mpi_vicap_get_sensor_info(device_obj[dev_num].sensor_type, &device_obj[dev_num].sensor_info);
    if (ret) {
        printf("sample_vicap, the sensor type not supported!\n");
        return ret;
    }
    memcpy(&dev_attr.sensor_info, &device_obj[dev_num].sensor_info, sizeof(k_vicap_sensor_info));

    device_obj[dev_num].in_width = device_obj[dev_num].sensor_info.width; //1920
    device_obj[dev_num].in_height = device_obj[dev_num].sensor_info.height; //1080

    printf("sample_vicap, dev[%d] in size[%dx%d]\n", \
        dev_num, device_obj[dev_num].in_width, device_obj[dev_num].in_height);

    //vicap device attr set
    dev_attr.acq_win.h_start = 0; //采集窗口的水平起始位置
    dev_attr.acq_win.v_start = 0; //采集窗口的垂直起始位置
    dev_attr.acq_win.width = device_obj[dev_num].in_width; //采集窗口的宽度
    dev_attr.acq_win.height = device_obj[dev_num].in_height; //采集窗口的高度
    dev_attr.mode = VICAP_WORK_ONLINE_MODE; //设备工作模式为在线模式

    dev_attr.pipe_ctrl.data = pipe_ctrl; //设置ISP相关控制数据
    dev_attr.pipe_ctrl.bits.af_enable = 0; //禁用自动对焦
    dev_attr.pipe_ctrl.bits.ae_enable = device_obj[dev_num].ae_enable;
    dev_attr.pipe_ctrl.bits.awb_enable = device_obj[dev_num].awb_enable;
    dev_attr.pipe_ctrl.bits.dnr3_enable = device_obj[dev_num].dnr3_enable;
    dev_attr.pipe_ctrl.bits.ahdr_enable = device_obj[dev_num].hdr_enable;

    dev_attr.cpature_frame = 0; //捕获帧数
    dev_attr.dw_enable = device_obj[dev_num].dw_enable;

    dev_attr.mirror = device_obj[cur_dev].sensor_mirror; //设置镜像模式为设备对象中的传感器镜像模式

    ret = kd_mpi_vicap_set_dev_attr(dev_num, dev_attr);//设置VICAP设备属性
    if (ret) {
        printf("sample_vicap, kd_mpi_vicap_set_dev_attr failed.\n");
        return ret;
    }


    int chn_num = 0;
    //set default value 设置默认值
    if (!device_obj[dev_num].out_format[chn_num]) {
        device_obj[dev_num].out_format[chn_num] = PIXEL_FORMAT_YUV_SEMIPLANAR_420; //捕获图像格式为YUV420
    }

    
    if (!device_obj[dev_num].out_win[chn_num].width) {
        device_obj[dev_num].out_win[chn_num].width = device_obj[dev_num].in_width; //捕获图像宽度
    }

    if (!device_obj[dev_num].out_win[chn_num].height) {
        device_obj[dev_num].out_win[chn_num].height = device_obj[dev_num].in_height; //捕获图像高度
    }
    
    if ( device_obj[dev_num].out_win[chn_num].h_start || device_obj[dev_num].out_win[chn_num].v_start) {
        device_obj[dev_num].crop_enable[chn_num] = K_TRUE;
    }

    printf("sample_vicap, dev_num(%d), chn_num(%d), in_size[%dx%d], out_offset[%d:%d], out_size[%dx%d]\n", \
        dev_num, chn_num, device_obj[dev_num].in_width, device_obj[dev_num].in_height, \
        device_obj[dev_num].out_win[chn_num].h_start, device_obj[dev_num].out_win[chn_num].v_start, \
        device_obj[dev_num].out_win[chn_num].width, device_obj[dev_num].out_win[chn_num].height);

    ret = sample_vicap_vb_init(device_obj);//配置缓冲区
    if (ret) {
        printf("sample_vicap_vb_init failed\n");
        return -1;
    }
    atexit(vb_exit);//程序正常结束时,清理缓冲区

    //vicap channel attr set

    kd_mpi_vicap_set_dump_reserved(dev_num, chn_num, K_TRUE);//设置是否开启快速dump 模式

    memset(&chn_attr, 0, sizeof(k_vicap_chn_attr));

    chn_attr.out_win.width = device_obj[dev_num].out_win[chn_num].width; //设置输出图像宽度
    chn_attr.out_win.height = device_obj[dev_num].out_win[chn_num].height; //设置输出图像高度

    if (device_obj[dev_num].crop_enable[chn_num]) { //如果设置裁剪参数
        chn_attr.crop_win.width = device_obj[dev_num].crop_win[chn_num].width;  //chn_attr.out_win;1166;// 
        chn_attr.crop_win.height = device_obj[dev_num].crop_win[chn_num].height; //1944;//
        chn_attr.crop_win.h_start =device_obj[dev_num].out_win[chn_num].h_start;  //713;
        chn_attr.crop_win.v_start =device_obj[dev_num].out_win[chn_num].v_start;  //0;//
    } else {
        chn_attr.crop_win.width = device_obj[dev_num].in_width;
        chn_attr.crop_win.height = device_obj[dev_num].in_height;
    }

    chn_attr.scale_win = chn_attr.out_win;
    chn_attr.crop_enable = device_obj[dev_num].crop_enable[chn_num];
    chn_attr.scale_enable = K_FALSE; //缩放关闭
    chn_attr.chn_enable = K_TRUE; //通道使能

    chn_attr.pix_format = device_obj[dev_num].out_format[chn_num];//图像格式
    chn_attr.buffer_num = VICAP_OUTPUT_BUF_NUM;//缓冲区数量
    chn_attr.buffer_size = device_obj[dev_num].buf_size[chn_num];//缓冲区大小

    printf("sample_vicap, set dev(%d) chn(%d) attr, buffer_size(%d), out size[%dx%d]\n", \
        dev_num, chn_num, chn_attr.buffer_size, chn_attr.out_win.width, chn_attr.out_win.height);

    printf("sample_vicap out_win h_start is %d ,v_start is %d \n", chn_attr.out_win.h_start, chn_attr.out_win.v_start);

    ret = kd_mpi_vicap_set_chn_attr(dev_num, chn_num, chn_attr);//设置VICAP设备通道属性
    if (ret) {
        printf("sample_vicap, kd_mpi_vicap_set_chn_attr failed.\n");
        goto vb_exit;
    }

    printf("sample_vicap, vicap dev(%d) init\n", dev_num);
    ret = kd_mpi_vicap_init(dev_num);//VICAP设备初始化
    if (ret) {
        printf("sample_vicap, vicap dev(%d) init failed.\n", dev_num);
        goto app_exit;
    }



    printf("sample_vicap, vicap dev(%d) start stream\n", dev_num);
    ret = kd_mpi_vicap_start_stream(dev_num);//启动VICAP设备输出数据流
    if (ret) {
        printf("sample_vicap, vicap dev(%d) start stream failed.\n", dev_num);
        goto app_exit;
    }


    static k_u32 dump_count = 0;

    k_char select = 0;
    while(K_TRUE)
    {
        if(select != '\n')
        {
            printf("---------------------------------------\n");
            printf(" Input character to select test option\n");
            printf("---------------------------------------\n");
            printf(" d: dump data addr test\n");
            printf(" q: to exit\n");
            printf("---------------------------------------\n");
            printf("please Input:\n\n");
        }
        select = (k_char)getchar();
        switch (select)
        {
        case 'd': //获设备中转储(dump)一帧图像数据,并将其保存到文件中
            printf("sample_vicap... dump frame.\n");
            int dev_num = 0;
            int chn_num = 0;
            printf("sample_vicap, dev(%d) chn(%d) dump frame.\n", dev_num, chn_num);

            memset(&dump_info, 0 , sizeof(k_video_frame_info));
            uint64_t start = get_ticks(); //获取当前时间戳
            ret = kd_mpi_vicap_dump_frame(dev_num, chn_num, VICAP_DUMP_YUV, &dump_info, 1000);//根据指定的设备和输出通道dump vicap数据
            if (ret) {
                printf("sample_vicap, dev(%d) chn(%d) dump frame failed.\n", dev_num, chn_num);
                continue;
            }
            uint64_t end = get_ticks();//获取结束时间
            printf("dump cost %lu us\n", (end - start) / 27);

            k_char *suffix;
            k_u32 data_size = 0;
            k_u8 lbit = 0;
            k_u8 *virt_addr = NULL;
            k_char filename[256];

            if (dump_info.v_frame.pixel_format == PIXEL_FORMAT_YUV_SEMIPLANAR_420) {
                suffix = "yuv420sp";
                data_size = dump_info.v_frame.width * dump_info.v_frame.height * 3 /2;//计算一帧图像的大小
            }else {
                suffix = "unkown";
            }

            virt_addr = kd_mpi_sys_mmap(dump_info.v_frame.phys_addr[0], data_size);//memory 存储映射接口,将物理地址映射到虚拟地址
            if (virt_addr) {
                memset(filename, 0 , sizeof(filename));

                snprintf(filename, sizeof(filename), "dev_%02d_chn_%02d_%dx%d_%04d.%s", \
                    dev_num, chn_num, dump_info.v_frame.width, dump_info.v_frame.height, dump_count, suffix);//生成文件名

                printf("save dump data to file(%s)\n", filename);
                FILE *file = fopen(filename, "wb+");
                if (file) {
                    if (device_obj[dev_num].dalign && lbit) {
                        for (k_u32 index = 0; index < data_size; index += 2) {
                            k_u16 raw_data = (virt_addr[index + 1] << 8 ) | virt_addr[index];
                            raw_data = raw_data << lbit;
                            fwrite(&raw_data, sizeof(raw_data), 1, file);//保存文件
                        }
                    } else {
                        fwrite(virt_addr, 1, data_size, file);
                    }
                    fclose(file);//关闭文件
                } else {
                    printf("sample_vicap, open dump file failed(%s)\n", strerror(errno));
                }

                kd_mpi_sys_munmap(virt_addr, data_size);//解除映射
            } else {
                printf("sample_vicap, map dump addr failed.\n");
            }

            printf("sample_vicap, release dev(%d) chn(%d) dump frame.\n", dev_num, chn_num);

            ret = kd_mpi_vicap_dump_release(dev_num, chn_num, &dump_info);//释放dump数据帧
            if (ret) {
                printf("sample_vicap, dev(%d) chn(%d) release dump frame failed.\n", dev_num, chn_num);
            }
            dump_count++;
            break;
        case 'q':
            goto app_exit;
        default:
            break;
        }
        sleep(1);
    }

app_exit:

    for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
        if (!device_obj[dev_num].dev_enable)
            continue;

        printf("sample_vicap, vicap dev(%d) stop stream\n", dev_num);
        ret = kd_mpi_vicap_stop_stream(dev_num);
        if (ret) {
            printf("sample_vicap, vicap dev(%d) stop stream failed.\n", dev_num);
        }
        printf("display_cnt[%d], drop_cnt[%d]\n", display_cnt, drop_cnt);

        printf("sample_vicap, vicap dev(%d) deinit\n", dev_num);
        ret = kd_mpi_vicap_deinit(dev_num);
        if (ret) {
            printf("sample_vicap, vicap dev(%d) deinit failed.\n", dev_num);
        }

    }
    for (int dev_num = 0; dev_num < VICAP_DEV_ID_MAX; dev_num++) {
        if (!device_obj[dev_num].dev_enable)
            continue;

        for (int chn_num = 0; chn_num < VICAP_CHN_ID_MAX; chn_num++) {
            if (!device_obj[dev_num].chn_enable[chn_num])
                continue;
        }
    }


    printf("Press Enter to exit!!!!\n");
    getchar();

    /*Allow one frame time for the VO to release the VB block*/
    k_u32 display_ms = 1000 / 33;
    usleep(1000 * display_ms);

vb_exit:

    return ret;
}

  1. 相应的makefile文件
include $(MPP_SRC_DIR)/userapps/sample/mpp.mk
include $(MPP_SRC_DIR)/userapps/sample/rt-smart.mk

CURRECT_DIR_NAME=$(shell basename `pwd`)
LOCAL_SRC_DIR = $(shell pwd)
BIN = $(MPP_SRC_DIR)/userapps/sample/elf/$(CURRECT_DIR_NAME).elf
LIBPATH = $(MPP_LIB_PATH)
LIBS = $(MPP_LIBS)

LOCAL_CFLAGS = -I$(LOCAL_SRC_DIR) \

SRCS = $(wildcard $(LOCAL_SRC_DIR)/*.c) 

OBJS = $(patsubst %.c,%.o,$(SRCS))

all: $(BIN)
	@-rm -f $(OBJS)
	@echo "${PWD}/Makefile all"

$(OBJS): %.o : %.c
	@echo CC $@
	@$(CC) $(CC_CFLAGS) $(LOCAL_CFLAGS) $(BSP_CFLGAS) $(RTSMART_CFLAGS) $(MPP_USER_CFLGAS) -c $< -o $@

$(BIN): $(OBJS)
	@echo LD $@
	@$(CC) -o $(BIN) $(LINKFLAG) -Wl,--whole-archive -Wl,--no-whole-archive -n --static $(OBJS) -L$(LIBPATH) -Wl,--start-group $(LIBS) -Wl,--end-group

clean:
	echo "${PWD}/Makefile clean"
	-rm -rf $(BIN)
	-rm -f $(OBJS)

.PHONY: all clean

硬件板卡


庐山派

软件版本


CanMV-K230_LCKFB_sdcard__nncase_v2.10.0.img

硬件板卡


庐山派

软件版本


CanMV-K230_LCKFB_sdcard__nncase_v2.10.0.img

硬件板卡


软件版本


CanMV-K230_LCKFB_sdcard__nncase_v2.10.0.img

硬件板卡


软件版本


CanMV-K230_LCKFB_sdcard__nncase_v2.10.0.img

硬件板卡


庐山派

软件版本


CanMV-K230_LCKFB_sdcard__nncase_v2.10.0.img

2 Answers

你好,推荐使用纯RTOS SDK,这个对摄像头支持没有什么问题。

大佬,早就应该在这个时候听您的(„ಡωಡ„)栓Q

我编译出来的可以用,但我现在不用双系统了,你如果需要可以留邮箱,我把二进制文件发给你,我感觉源码好像是一样的

好的٩(•̤̀ᵕ•̤́๑)ᵒᵏᵎᵎᵎᵎ