瑞芯微RV1106通过MIPI CSI-2 D-PHY接口驱动OV5640摄像头并拍摄照片

【sysdrv\source\kernel\arch\arm\boot\dts\rv1106-luckfox-pico-pro-max-ipc.dtsi】

csi_dphy_input0: endpoint@0里面,将remote-endpoint = <&sc3336_out>;改成remote-endpoint = <&ov5640_out>;

&i2c4里面的最后添加:

cpp 复制代码
    ov5640: ov5640@3c {
        compatible = "ovti,ov5640";
        status = "okay";
        reg = <0x3c>;
        clocks = <&cru MCLK_REF_MIPI0>;
        clock-names = "xclk";
        reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_LOW>;
        pinctrl-names = "default";
        pinctrl-0 = <&mipi_refclk_out0>;
        port {
            ov5640_out: endpoint {
                remote-endpoint = <&csi_dphy_input0>;
                clock-lanes = <0>;
                data-lanes = <1 2>;
            };
        };
    };

【sysdrv\source\kernel\arch\arm\configs\luckfox_rv1106_linux_defconfig】

在CONFIG_VIDEO_RK_IRCUT=y下方添加CONFIG_VIDEO_OV5640=y

【sysdrv\source\kernel\drivers\media\i2c\ov5640.c】

包含头文件:

#include <linux/rk-camera-module.h>

ov5640_init_controls里面:

cpp 复制代码
static int ov5640_init_controls(struct ov5640_dev *sensor)
{
    const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops;
    struct ov5640_ctrls *ctrls = &sensor->ctrls;
    struct v4l2_ctrl_handler *hdl = &ctrls->handler;
    int ret;

********************************添加内容开始**************************************************

#define SC3336_LINK_FREQ_253  253125000
#define SC3336_LINK_FREQ_255  255000000
    static const s64 link_freq_menu_items[] = {SC3336_LINK_FREQ_253, SC3336_LINK_FREQ_255};
    struct v4l2_ctrl *link_freq;

********************************添加内容结束**************************************************

    v4l2_ctrl_handler_init(hdl, 32);

    /* we can use our own mutex for the ctrl lock */
    hdl->lock = &sensor->lock;

********************************添加内容开始**************************************************

    link_freq = v4l2_ctrl_new_int_menu(hdl, NULL,
            V4L2_CID_LINK_FREQ,
            ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items);
    if (link_freq != NULL)
        link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
********************************添加内容结束**************************************************

添加ov5640_ioctl函数:

cpp 复制代码
static long ov5640_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
    struct ov5640_dev *sensor = to_ov5640_dev(sd);
    struct rkmodule_channel_info *ch_info;
    long ret = 0;

    switch (cmd) {
    case RKMODULE_GET_CHANNEL_INFO:
        ch_info = (struct rkmodule_channel_info *)arg;
        ch_info->width = ov5640_mode_data[OV5640_NUM_MODES - 1].hact;
        ch_info->height = ov5640_mode_data[OV5640_NUM_MODES - 1].vact;
        ch_info->bus_fmt = sensor->fmt.code;
        break;
    default:
        ret = -ENOIOCTLCMD;
        break;
    }

    return ret;
}

static const struct v4l2_subdev_core_ops ov5640_core_ops里面添加.ioctl = ov5640_ioctl,

添加ov5640_mbus_config函数:

cpp 复制代码
#define OV5640_LANES 2
static int ov5640_mbus_config(struct v4l2_subdev *sd,
                unsigned int pad_id,
                struct v4l2_mbus_config *config)
{
    config->type = V4L2_MBUS_CSI2_DPHY;
    config->flags = 1 << (OV5640_LANES - 1) |
        V4L2_MBUS_CSI2_CHANNEL_0 |
        V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;

    return 0;
}

static const struct v4l2_subdev_pad_ops ov5640_pad_ops里面添加.get_mbus_config = ov5640_mbus_config,

【sysdrv\source\kernel\drivers\media\platform\rockchip\cif\mipi-csi2.c】

添加函数:

cpp 复制代码
static int csi2_g_frame_interval(struct v4l2_subdev *sd,
                                    struct v4l2_subdev_frame_interval *fi)
{
    struct v4l2_subdev *sensor = get_remote_sensor(sd);

    if (sensor)
        return v4l2_subdev_call(sensor, video, g_frame_interval, fi);

    return -EINVAL;
}

static int csi2_s_frame_interval(struct v4l2_subdev *sd,
                                    struct v4l2_subdev_frame_interval *fi)
{
    struct v4l2_subdev *sensor = get_remote_sensor(sd);

    if (sensor)
        return v4l2_subdev_call(sensor, video, s_frame_interval, fi);

    return -EINVAL;
}

static int csi2_get_set_fmt(struct v4l2_subdev *sd,改成static int csi2_get_fmt(struct v4l2_subdev *sd,

然后在下面添加csi2_set_fmt函数:

cpp 复制代码
static int csi2_set_fmt(struct v4l2_subdev *sd,
                            struct v4l2_subdev_pad_config *cfg,
                            struct v4l2_subdev_format *fmt)
{
    int ret;
    struct csi2_dev *csi2 = sd_to_dev(sd);
    struct v4l2_subdev *sensor = get_remote_sensor(sd);
    
    ret = v4l2_subdev_call(sensor, pad, set_fmt, NULL, fmt);
    if (!ret)
        csi2->format_mbus = fmt->format;

    return ret;
}

static const struct v4l2_subdev_video_ops csi2_video_ops添加:

.g_frame_interval = csi2_g_frame_interval,

.s_frame_interval = csi2_s_frame_interval,

static const struct v4l2_subdev_pad_ops csi2_pad_ops添加:

.get_fmt = csi2_get_fmt,

.set_fmt = csi2_set_fmt,

【sysdrv\source\kernel\drivers\phy\rockchip\phy-rockchip-csi2-dphy.c】

在csi2_dphy_g_frame_interval函数下面添加函数:

cpp 复制代码
static int csi2_dphy_s_frame_interval(struct v4l2_subdev *sd,
                                    struct v4l2_subdev_frame_interval *fi)
{
    struct v4l2_subdev *sensor = get_remote_sensor(sd);

    if (sensor)
        return v4l2_subdev_call(sensor, video, s_frame_interval, fi);

    return -EINVAL;
}

static int csi2_dphy_get_set_fmt(struct v4l2_subdev *sd,改成static int csi2_dphy_get_fmt(struct v4l2_subdev *sd,

并在下面添加

cpp 复制代码
static int csi2_dphy_set_fmt(struct v4l2_subdev *sd,
                struct v4l2_subdev_pad_config *cfg,
                struct v4l2_subdev_format *fmt)
{
    struct csi2_dphy *dphy = to_csi2_dphy(sd);
    struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
    struct csi2_sensor *sensor;
    int ret;
    if (!sensor_sd)
        return -ENODEV;
    sensor = sd_to_sensor(dphy, sensor_sd);
    if (!sensor)
        return -ENODEV;
    ret = v4l2_subdev_call(sensor_sd, pad, set_fmt, NULL, fmt);
    if (!ret && fmt->pad == 0 && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
        sensor->format = fmt->format;
    return ret;
}

static const struct v4l2_subdev_video_ops csi2_dphy_video_ops添加

.s_frame_interval = csi2_dphy_s_frame_interval,

static const struct v4l2_subdev_pad_ops csi2_dphy_subdev_pad_ops里面修改

.set_fmt = csi2_dphy_set_fmt,

.get_fmt = csi2_dphy_get_fmt,

【拍照程序:test\ov5640\ov5640.c】

cpp 复制代码
#include <fcntl.h>
#include <linux/v4l2-subdev.h>
#include <linux/videodev2.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "bmp.h"

int yuv2rgb(int y, int u, int v, uint8_t *_r, uint8_t *_g, uint8_t *_b)
{
    int r, g, b;
    
    // https://blog.csdn.net/u012294613/article/details/141095964
    r = y + 1403 * (v - 128) / 1000;
    g = y - 343 * (u - 128) / 1000 - 714 * (v - 128) / 1000;
    b = y + 1770 * (u - 128) / 1000;

    if (r > 255)
        r = 255;
    else if (r < 0)
        r = 0;
    
    if (g > 255)
        g = 255;
    else if (g < 0)
        g = 0;
    
    if (b > 255)
        b = 255;
    else if (b < 0)
        b = 0;
    
    *_r = (uint8_t)r;
    *_g = (uint8_t)g;
    *_b = (uint8_t)b;
    return (r << 16) | (g << 8) | b;
}

void convert_to_bitmap(const uint8_t *data, long size, const char *filename, int width, int height)
{
    int bmpsize, stride;
    int i, j, m, n;
    int y1, u, y2, v;
    uint8_t *bmpdata;
    BITMAPFILEHEADER bmpfilehdr = {0};
    BITMAPINFOHEADER bmphdr = {0};
    FILE *fp;

    fp = fopen(filename, "wb");
    if (fp == NULL)
        return;

    memcpy(&bmpfilehdr.bfType, "BM", 2);
    bmpfilehdr.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    bmphdr.biSize = sizeof(BITMAPINFOHEADER);
    bmphdr.biWidth = width;
    bmphdr.biHeight = -height;
    bmphdr.biPlanes = 1;
    bmphdr.biBitCount = 24;
    bmphdr.biCompression = BI_RGB;

    stride = ((((width * bmphdr.biBitCount) + 31) & ~31) >> 3);
    bmpsize = height * stride;
    bmpfilehdr.bfSize = bmpfilehdr.bfOffBits + bmpsize;
    fwrite(&bmpfilehdr, sizeof(BITMAPFILEHEADER), 1, fp);
    fwrite(&bmphdr, bmphdr.biSize, 1, fp);

    bmpdata = malloc(bmpsize);
    if (bmpdata != NULL)
    {
        m = 0;
        n = 0;
        for (i = 0; i < height; i++)
        {
            for (j = 0; j < width; j += 2)
            {
                if (m + 4 > size)
                    break;

                y1 = data[m];
                u = data[m + 1];
                y2 = data[m + 2];
                v = data[m + 3];
                yuv2rgb(y1, u, v, &bmpdata[n + j * 3 + 2], &bmpdata[n + j * 3 + 1], &bmpdata[n + j * 3]);
                yuv2rgb(y2, u, v, &bmpdata[n + j * 3 + 5], &bmpdata[n + j * 3 + 4], &bmpdata[n + j * 3 + 3]);
                m += 4;
            }
            n += stride;
        }

        fwrite(bmpdata, 1, bmpsize, fp);
        free(bmpdata);
    }

    fclose(fp);
}

int main()
{
    char filename[50];
    int fd;
    int i, ret;
    struct v4l2_subdev_frame_interval subdev_interval;
    struct v4l2_subdev_format subdev_format;
    struct v4l2_capability cap;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_format format;
    struct v4l2_requestbuffers reqbufs;
    struct v4l2_buffer buf;
    struct v4l2_plane plane;
    unsigned char *mem[4];
    unsigned int memsize[4];
    
    system("rm *.bin *.bmp photos/*.bmp 2>/dev/null");
    
    fd = open("/dev/v4l-subdev0", O_RDWR);
    if (fd == -1)
    {
        printf("failed to open /dev/v4l-subdev0\n");
        return -1;
    }
    
    subdev_interval.interval.numerator = 1;
    subdev_interval.interval.denominator = 15;
    subdev_interval.pad = 0;
    ret = ioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &subdev_interval);
    if (ret == -1)
        printf("VIDIOC_SUBDEV_S_FRAME_INTERVAL failed\n");
    else
        printf("interval=%d/%d\n", subdev_interval.interval.numerator, subdev_interval.interval.denominator);
    
    subdev_format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
    subdev_format.pad = 0;
    ret = ioctl(fd, VIDIOC_SUBDEV_G_FMT, &subdev_format);
    if (ret == -1)
        printf("VIDIOC_SUBDEV_G_FMT failed\n");
    else
        printf("code=0x%x, field=%d\n", subdev_format.format.code, subdev_format.format.field);
    
    subdev_format.format.width = 2592;
    subdev_format.format.height = 1944;
    ret = ioctl(fd, VIDIOC_SUBDEV_S_FMT, &subdev_format);
    if (ret == -1)
        printf("VIDIOC_SUBDEV_S_FMT failed\n");
    close(fd);

    fd = open("/dev/video0", O_RDWR);
    if (fd == -1)
    {
        printf("failed to open /dev/video0\n");
        return -1;
    }

    ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
    if (ret == -1)
        printf("VIDIOC_QUERYCAP failed\n");
    else
    {
        printf("Driver: %s\n", cap.driver);
        printf("Card: %s\n", cap.card);
        printf("Bus info: %s\n", cap.bus_info);
        printf("Version: %d\n", cap.version);
    }

    printf("VIDIOC_ENUM_FMT start\n");
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1)
    {
        printf("\t%d.%s\n", fmtdesc.index + 1, fmtdesc.description);
        fmtdesc.index++;
    }
    printf("VIDIOC_ENUM_FMT end\n");

    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    format.fmt.pix_mp.width = subdev_format.format.width;
    format.fmt.pix_mp.height = subdev_format.format.height;
    format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUYV;
    printf("Size: %dx%d\n", format.fmt.pix_mp.width, format.fmt.pix_mp.height);
    ret = ioctl(fd, VIDIOC_S_FMT, &format);
    if (ret == -1)
        printf("VIDIOC_S_FMT failed\n");
    printf("Size: %dx%d\n", format.fmt.pix_mp.width, format.fmt.pix_mp.height);

    reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    reqbufs.count = sizeof(mem) / sizeof(mem[0]);
    reqbufs.memory = V4L2_MEMORY_MMAP;
    ret = ioctl(fd, VIDIOC_REQBUFS, &reqbufs);
    if (ret == -1)
        printf("VIDIOC_REQBUFS failed\n");

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.m.planes = &plane;
    buf.length = 1;
    for (i = 0; i < reqbufs.count; i++)
    {
        // allocate buffers to have somewhere to store the images
        buf.index = i;
        ret = ioctl(fd, VIDIOC_QUERYBUF, &buf);
        if (ret == -1)
        {
            printf("VIDIOC_QUERYBUF failed: i=%d\n", i);
            break;
        }

        // query the physical address of each allocated buffer in order to mmap() those
        printf("i=%d, buf.length=%d, buf.m.planes[0]=%d, buf.m.planes[0].m.mem_offset=%d\n", i, buf.length, buf.m.planes[0].length, buf.m.planes[0].m.mem_offset);
        mem[i] = mmap(NULL, buf.m.planes[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.planes[0].m.mem_offset);
        memsize[i] = buf.m.planes[0].length;
        if (mem[i] == MAP_FAILED)
            printf("map failed: i=%d\n", i);
        memset(mem[i], 0, memsize[i]);

        // Before the buffers can be filled with data, the buffers has to be enqueued.
        // Enqueued buffers will lock the memory pages used so that those cannot be swapped out during usage.
        // The buffers remain locked until that are dequeued.
        ret = ioctl(fd, VIDIOC_QBUF, &buf);
        if (ret == -1)
            printf("VIDIOC_QBUF failed: i=%d\n", i);
    }

    // start acquire video frames and use the queued buffers to store them
    ret = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    ret = ioctl(fd, VIDIOC_STREAMON, &ret);
    if (ret == -1)
        printf("VIDIOC_STREAMON failed\n");

    for (i = 0; i < 50; i++)
    {
        // Once buffers are filled with video data, those are ready to be dequeued and consumed by the application.
        // This ioctl will be blocking (unless O_NONBLOCK is used) until a buffer is available.
        // VIDIOC_DQBUF works similar to VIDIOC_QBUF but it populates the v4l2_buffer.index field with the index number
        // of the buffer that has been dequeued.
        buf.index = i % reqbufs.count;
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        ret = ioctl(fd, VIDIOC_DQBUF, &buf);
        if (ret == -1)
            printf("VIDIOC_DQBUF failed: i=%d, buf.index=%d\n", i, buf.index);
        else
        {
            printf("i=%d, buf.index=%d, buf.length=%d, buf.m.planes[0].length=%d\n", i, buf.index, buf.length, buf.m.planes[0].length);
            if (access("photos", 0) == -1)
                mkdir("photos", 0755);
            snprintf(filename, sizeof(filename), "photos/photo%d.bmp", i);
            convert_to_bitmap(mem[buf.index], buf.m.planes[0].length, filename, format.fmt.pix_mp.width, format.fmt.pix_mp.height);
        }

        // As soon the buffer is dequeued and processed, the application has to immediately queue back the buffer
        // so that the driver layer can fill it with new frames.
        // This is usually part of the application main-loop.
        ret = ioctl(fd, VIDIOC_QBUF, &buf);
        if (ret == -1)
            printf("VIDIOC_QBUF failed: buf.index=%d\n", buf.index);
    }

    // Once we are done with the video capturing, we can stop the streaming.
    // This will unlock all enqueued buffers and stop capture frames.
    ret = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    ret = ioctl(fd, VIDIOC_STREAMOFF, &ret);
    if (ret == -1)
        printf("VIDIOC_STREAMOFF failed\n");

    for (i = 0; i < reqbufs.count; i++)
    {
        if (mem[i] != NULL && mem[i] != MAP_FAILED)
            munmap(mem[i], memsize[i]);
    }

    close(fd);
    return ret;
}

    

【test\ov5640\bmp.h】

cpp 复制代码
#define MAX_PATH 260
#define BI_RGB 0
typedef int32_t LONG;
typedef uint16_t WORD;
typedef uint32_t DWORD;

#pragma pack(push, 1)
typedef struct
{
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER;

typedef struct
{
    DWORD biSize;
    LONG biWidth;
    LONG biHeight;
    WORD biPlanes;
    WORD biBitCount;
    DWORD biCompression;
    DWORD biSizeImage;
    LONG biXPelsPerMeter;
    LONG biYPelsPerMeter;
    DWORD biClrUsed;
    DWORD biClrImportant;
} BITMAPINFOHEADER;
#pragma pack(pop)

    

【test\ov5640\Makefile】

bash 复制代码
CC=../../tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc

all: ov5640

【串口输出】

bash 复制代码
luckfox login: root
Password:

Login incorrect
luckfox login: root
Password:
[root@luckfox root]#
[root@luckfox root]#
[root@luckfox root]# ls
ov5640  photos
[root@luckfox root]# rm -rf photos
[root@luckfox root]# ls
ov5640
[root@luckfox root]# ./ov5640
interval=1/15
code=0x2006, field=1
Driver: rkcif
Card: rkcif
Bus info: platform:rkcif-mipi-lvds
Version: 330400
VIDIOC_ENUM_FMT start
        1.Y/CbCr 4:2:2
        2.Y/CrCb 4:2:2
        3.Y/CbCr 4:2:0
        4.Y/CrCb 4:2:0
        5.YUYV 4:2:2
        6.YVYU 4:2:2
        7.UYVY 4:2:2
        8.VYUY 4:2:2
VIDIOC_ENUM_FMT end
Size: 2592x1944
Size: 2592x1944
i=0, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=0
i=1, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=101212
16
i=2, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=202424
32
i=3, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=303636
48
i=0, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=1, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=2, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=3, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=4, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=5, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=6, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=7, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=8, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=9, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=10, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=11, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=12, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=13, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=14, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=15, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=16, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=17, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=18, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=19, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=20, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=21, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=22, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=23, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=24, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=25, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=26, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=27, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=28, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=29, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=30, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=31, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=32, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=33, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=34, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=35, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=36, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=37, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=38, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=39, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=40, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=41, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=42, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=43, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=44, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=45, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=46, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=47, buf.index=3, buf.length=1, buf.m.planes[0].length=10119168
i=48, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=49, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
[root@luckfox root]#

 

照片的采集速度很快,但是板载flash的读写速度很慢。

2592x1944分辨率下,每张bmp照片的大小是14.4MB,保存一张照片到flash上要好几秒。

板载flash空间有限,最多只能采集13张照片(photo0.bmp~photo12.bmp),而且摄像头刚启动时采集的四张照片(photo0~3)是不正确的,从photo4开始才是正确的照片。到最后flash空间满了,photo13.bmp不完整,所以在电脑上无法查看。

【电路图】

注意13脚MCLK0必须外接24MHz有源晶振,不能用RV1106的I/O口输出的24MHz时钟,否则OV5640 I2C接口无响应。

如果启动的是自己的根文件系统,那么要先加载下面的ko模块,才能运行ov5640拍照程序。

insmod /root/test/ov5640/drivers/video_rkcif.ko

insmod /root/test/ov5640/drivers/phy-rockchip-csi2-dphy-hw.ko

insmod /root/test/ov5640/drivers/phy-rockchip-csi2-dphy.ko

echo 1 > /sys/module/video_rkcif/parameters/clr_unready_dev

这些驱动文件来自幸狐内核镜像里面生成的output/out/sysdrv_out/kernel_drv_ko文件夹。

加载完这些ko模块,并且执行了echo 1指令后,就会有/dev/video0和/dev/v4l-subdev0这两个设备文件出现。

加载完video_rkcif.ko模块后就会出现/dev/video0设备文件。

执行完echo 1命令后就会出现/dev/v4l-subdev0设备文件。

启动ov5640拍照程序后,会有下面的串口打印,不用管。

38.503231 mipi-csi2-hw ERR1:0x1000000 (crc,vc: 0)

38.503945 mipi-csi2-hw ERR1:0x100 (f_seq,vc: 0)

38.570015 rkcif-mipi-lvds: ERROR: csi size err, intstat:0x1000000, size:0x7c50a20,0x0,0x0,0x0, cnt 1

bash 复制代码
udhcpc: broadcasting discover
udhcpc: broadcasting select for 192.168.4.57, server 192.168.1.1
udhcpc: lease of 192.168.4.57 obtained from 192.168.1.1, lease time 3600
Setting IP address 192.168.4.57 on wlan0
Deleting routers
route: SIOCDELRT: No such process
Adding router 192.168.1.1
Recreating /etc/resolv.conf
 Adding DNS server 223.5.5.5
 Adding DNS server 114.114.114.114

Please press Enter to activate this console.

Processing /etc/profile... Done

[root@luckfox-rv1106 /]# 29 Aug 09:25:50 ntpdate[117]: step time server 139.199.215.251 offset +146955211.658819 sec
insmod /root/test/ov5640/drivers/video_rkcif.ko
[   20.123480] rkcifhw ffa10000.rkcif: no iommu attached, using non-iommu buffers
[root@luckfox-rv1106 /]# [   20.123509] rkcifhw ffa10000.rkcif: No reserved memory region assign to CIF
[   20.123880] rkcif rkcif-mipi-lvds: rkcif driver version: v00.02.00
[   20.123982] rkcif rkcif-mipi-lvds: attach to cif hw node
[   20.123995] rkcif rkcif-mipi-lvds: failed to get dphy hw node
[   20.124005] rkcif rkcif-mipi-lvds: rkcif wait line 0
[   20.124015] rkcif rkcif-mipi-lvds: rkcif fastboot reserve bufs num 3
[   20.124027] : terminal subdev does not exist
[   20.124039] : terminal subdev does not exist
[   20.124046] : terminal subdev does not exist
[   20.124054] : terminal subdev does not exist
[   20.124063] : get_remote_sensor: video pad[0] is null
[   20.124074] : rkcif_update_sensor_info: stream[0] get remote sensor_sd failed!
[   20.124086] : rkcif_scale_set_fmt: req(80, 60) src out(0, 0)
[   20.124094] : get_remote_sensor: video pad[0] is null
[   20.124101] : rkcif_update_sensor_info: stream[0] get remote sensor_sd failed!
[   20.124109] : rkcif_scale_set_fmt: req(80, 60) src out(0, 0)
[   20.124117] : get_remote_sensor: video pad[0] is null
[   20.124124] : rkcif_update_sensor_info: stream[0] get remote sensor_sd failed!
[   20.124132] : rkcif_scale_set_fmt: req(80, 60) src out(0, 0)
[   20.124139] : get_remote_sensor: video pad[0] is null
[   20.124147] : rkcif_update_sensor_info: stream[0] get remote sensor_sd failed!
[   20.124155] : rkcif_scale_set_fmt: req(80, 60) src out(0, 0)
[   20.125711] rkcif rkcif-mipi-lvds: No memory-region-thunderboot specified
[   20.126707] rockchip-mipi-csi2-hw ffa20000.mipi-csi2-hw: enter mipi csi2 hw probe!
[   20.126870] rockchip-mipi-csi2-hw ffa20000.mipi-csi2-hw: probe success, v4l2_dev:mipi-csi2-hw!
[   20.126978] rockchip-mipi-csi2-hw ffa30000.mipi-csi2-hw: enter mipi csi2 hw probe!
[   20.127100] rockchip-mipi-csi2-hw ffa30000.mipi-csi2-hw: probe success, v4l2_dev:mipi-csi2-hw!
[   20.127445] rockchip-mipi-csi2 mipi0-csi2: attach to csi2 hw node
[   20.127504] rkcif rkcif-mipi-lvds: Entity type for entity rockchip-mipi-csi2 was not initialized!
[   20.127521] rockchip-mipi-csi2: Async registered subdev
[   20.127533] rockchip-mipi-csi2: probe success, v4l2_dev:rkcif-mipi-lvds!

[root@luckfox-rv1106 /]# insmod /root/test/ov5640/drivers/phy-rockchip-csi2-dphy
-hw.ko
[   23.284804] rockchip-csi2-dphy-hw ff3e8000.csi2-dphy-hw: csi2 dphy hw probe successfully!
[root@luckfox-rv1106 /]# insmod /root/test/ov5640/drivers/phy-rockchip-csi2-dphy
.ko
[   25.523068] rockchip-csi2-dphy csi2-dphy0: dphy0 matches ov5640 4-003c:bus type 5
[root@luckfox-rv1106 /]# [   25.523097] rockchip-csi2-dphy csi2-dphy0: csi2 dphy0 probe successfully!

[root@luckfox-rv1106 /]# echo 1 > /sys/module/video_rkcif/parameters/clr_unready
_dev
[   29.449807] rkcif rkcif-mipi-lvds: clear unready subdev num: 1
[root@luckfox-rv1106 /]# [   29.450402] rkcif-mipi-lvds: Async subdev notifier completed

[root@luckfox-rv1106 /]# cd /root/test
[root@luckfox-rv1106 ~/test]# ls
ngroups_max  ov5640
[root@luckfox-rv1106 ~/test]# cd ov5640/
[root@luckfox-rv1106 ~/test/ov5640]# ls
Makefile  bmp.h     drivers   ov5640    ov5640.c  photos2
[root@luckfox-rv1106 ~/test/ov5640]# ./ov5640
interval=1/15
code=0x2006, field=1
[   38.490087] stream_cif_mipi_id0: open video, entity use_countt 1
Driver: rkcif
[   38.490794] cma: cma_alloc: rk-dma-heap-cma: alloc failed, req-size: 2471 pages, ret: -12
Card: rkcif
[   38.490813] vb2_cma_sg_alloc_contiguous: cma_en:1 alloc pages fail
Bus info: platform:rkcif-mipi-lvds
Version: 330400
VIDIOC_ENUM_FMT start
[   38.516456] rkcif-mipi-lvds: stream[0] start streaming
        1.Y/CbCr 4:2:2
[   38.516600] rockchip-mipi-csi2 mipi0-csi2: stream on, src_sd: 822e2933, sd_name:rockchip-csi2-dphy0
        2.Y/CrCb 4:2:2
[   38.516614] rockchip-mipi-csi2 mipi0-csi2: stream ON
        3.Y/CbCr 4:2:0
[   38.516665] rockchip-csi2-dphy0: dphy0, data_rate_mbps 506
        4.Y/CrCb 4:2:0
[   38.516700] rockchip-csi2-dphy csi2-dphy0: csi2_dphy_s_stream stream on:1, dphy0, ret 0
        5.YUYV 4:2:2
        6.YVYU 4:2:2
        7.UYVY 4:2:2
        8.VYUY 4:2:2
VIDIOC_ENUM_FMT end
Size: 2592x1944
Size: 2592x1944
i=0, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=0
i=1, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=10121216
i=2, buf.length=1, buf.m.planes[0]=10119168, buf.m.planes[0].m.mem_offset=20242432
i=0, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=1, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=2, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=3, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=4, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
[   38.553498] mipi-csi2-hw ERR1:0x1010 (fs/fe mis,vc: 0) (err_data,vc: 0)
i=5, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
[   39.022789] rkcif-mipi-lvds: ERROR: csi size err, intstat:0x1000000, size:0x8010a20,0x0,0x0,0x0, cnt 1
** 12288 console messages dropped **
i=6, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=7, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=8, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=9, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=10, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=11, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=12, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=13, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=14, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=15, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=16, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=17, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=18, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=19, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=20, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=21, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=22, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=23, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=24, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=25, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=26, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=27, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=28, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=29, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=30, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=31, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=32, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=33, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=34, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=35, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=36, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=37, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=38, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=39, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=40, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=41, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=42, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=43, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=44, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=45, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=46, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
i=47, buf.index=2, buf.length=1, buf.m.planes[0].length=10119168
i=48, buf.index=0, buf.length=1, buf.m.planes[0].length=10119168
i=49, buf.index=1, buf.length=1, buf.m.planes[0].length=10119168
[  114.478528] rkcif-mipi-lvds: stream[0] start stopping, total mode 0x1, cur 0x1
[  114.478564] rkcif-mipi-lvds: get vblank fail, vblank_def 0, vblank_curr 0
[  114.582227] rockchip-mipi-csi2 mipi0-csi2: stream off, src_sd: 822e2933, sd_name:rockchip-csi2-dphy0
[  114.582245] rockchip-mipi-csi2 mipi0-csi2: stream OFF
[root@luckfox-rv1106 ~/test/ov5640]# [  114.582288] rockchip-csi2-dphy csi2-dphy0: csi2_dphy_s_stream_stop stream stop, dphy0
[  114.582305] rockchip-csi2-dphy csi2-dphy0: csi2_dphy_s_stream stream on:0, dphy0, ret 0
[  114.583549] rkcif-mipi-lvds: stream[0] stopping finished, dma_en 0x0
[  114.592130] stream_cif_mipi_id0: close video, entity use_count 0

[root@luckfox-rv1106 ~/test/ov5640]#

https://zh.purasbar.com/post.php?t=32782https://zh.purasbar.com/post.php?t=32782

相关推荐
2301_809051142 小时前
Linux 网络编程 学习笔记
linux·网络·学习
坤昱3 小时前
cfs调度类深入解刨——最新内核细节分析2
linux·服务器·cfs·cfs调度·eevdf调度·eevdf·kernel 7.1
艾莉丝努力练剑3 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
海市公约3 小时前
Linux核心基础命令与权限管理实战指南
linux·运维·服务器·vim·权限管理·系统监控·命令行
eggcode3 小时前
【Qt学习】Linux(ARM架构)在线安装Qt6.x
linux·qt·学习·arm
wkd_0073 小时前
Ubuntu 22.04 Samba 连接故障排查记:从“用户名或密码错误”到 NTLM 版本不兼容
linux·运维·ubuntu
mixboot4 小时前
Linux 进程工作目录查看利器:pwdx 命令详解
linux·运维·服务器
旺仔来了5 小时前
不联网的Linux下部署python环境
linux·开发语言·python
Irene19916 小时前
WSL 切换磁盘后验证完整性(MobaXterm、Powershell、WSL 的区别)
linux·wsl·mobaxterm
扛枪的书生7 小时前
Keepalived 学习总结
linux