【拍照程序: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;
}