|
|
瑞芯微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里面的最后添加: 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里面:
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函数:
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函数:
#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】 添加函数: 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函数: 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函数下面添加函数: 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, 并在下面添加 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】
#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】
#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】
CC=../../tools/linux/toolchain/arm-rockchip830-linux-uclibcgnueabihf/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc
all: ov5640
| |
一派掌門 二十級 |
【串口输出】
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接口无响应。

| |
一派掌門 二十級 |
| |
|
|
|