瑞芯微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://i-blog.csdnimg.cn/direct/38520e1eeec6423ba1a3ba237106b496.png) [https://zh.purasbar.com/post.php?t=32782![](https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png)https://zh.purasbar.com/post.php?t=32782](https://zh.purasbar.com/post.php?t=32782 "https://zh.purasbar.com/post.php?t=32782")

相关推荐
2023自学中2 小时前
从键盘输入 ./aaa 到程序运行,中间发生了什么?
linux·嵌入式
keyipatience2 小时前
13.系统调用与进程管理全解析
linux
你今天努力了吗?*—*2 小时前
4.1 Linux 日志排查
linux·运维·服务器
小狗爱吃黄桃罐头2 小时前
宋宝华:原理和实战解析Linux中如何正确地使用内存屏障
linux·内存屏障
山上三树2 小时前
操作系统如何实现各种功能
linux·运维·服务器
妹妹够啦2 小时前
PyCharm创建venv环境
linux·运维·服务器
我爱小疯喵喵2 小时前
5 Linux apt下载软件包
linux·运维·服务器
坚持就完事了2 小时前
Linux中的tr命令
linux·运维·服务器
普通young man2 小时前
谈Linux下编译和动静态链接
linux·运维·服务器