瑞芯微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

相关推荐
用户9718356334661 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪1 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩2 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈2 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
凡人叶枫2 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
2601_961875242 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant
java_cj2 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes