

【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](https://zh.purasbar.com/post.php?t=32782 "https://zh.purasbar.com/post.php?t=32782")