• ARM Linux DIY(十四)摄像头捕获画面显示到屏幕


    前言

    前期已经调试好了摄像头和屏幕,今天我们将摄像头捕获的画面显示到屏幕上。

    原理

    摄像头对应 /dev/video0,屏幕对应 /dev/fb0,所以我们只要写一个应用程序,读取 video0 写入到 fb0 就可以了。

    应用程序代码实例

    camera_display.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define CAM_WIDTH 800
    #define CAM_HEIGHT 600
    #define YUVToRGB(Y) ((u16)((((u8)(Y) >> 3) << 11) | (((u8)(Y) >> 2) << 5) | ((u8)(Y) >> 3)))
    
    static char *dev_video;
    static char *dev_fb0;
    
    static char *yuv_buffer;
    static char *rgb_buffer;
    typedef unsigned int u32;
    typedef unsigned short u16;
    typedef unsigned char u8;
    struct v4l2_buffer video_buffer;
    int lcd_fd;
    int video_fd;
    unsigned char *lcd_mem_p = NULL; //保存LCD屏映射到进程空间的首地址
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    char *video_buff_buff[4]; /*保存摄像头缓冲区的地址*/
    int video_height = 0;
    int video_width = 0;
    unsigned char *lcd_display_buff; //LCD显存空间
    unsigned char *lcd_display_buff2; //LCD显存空间
    
    static void errno_exit(const char *s)
    {
    	fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
    	exit(EXIT_FAILURE);
    }
    
    static int video_init(void)
    {
    	struct v4l2_capability cap;
    	struct v4l2_fmtdesc dis_fmtdesc;
    	struct v4l2_format video_format;
    	struct v4l2_requestbuffers video_requestbuffers;
    	struct v4l2_buffer video_buffer;
    
    	ioctl(video_fd, VIDIOC_QUERYCAP, &cap);
    	
    	dis_fmtdesc.index = 0;
    	dis_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	// printf("-----------------------支持格式---------------------\n");
    	// while (ioctl(video_fd, VIDIOC_ENUM_FMT, &dis_fmtdesc) != -1) {
    	// 	printf("\t%d.%s\n", dis_fmtdesc.index + 1, dis_fmtdesc.description);
    	// 	dis_fmtdesc.index++;
    	// }
    
    	video_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	video_format.fmt.pix.width = CAM_WIDTH;
    	video_format.fmt.pix.height = CAM_HEIGHT;
    	video_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //使用JPEG格式帧,用于静态图像采集
    
    	ioctl(video_fd, VIDIOC_S_FMT, &video_format);
    
    	printf("当前摄像头支持的分辨率:%dx%d\n", video_format.fmt.pix.width, video_format.fmt.pix.height);
    	if (video_format.fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) {
    		printf("当前摄像头不支持YUYV格式输出.\n");
    		video_height = video_format.fmt.pix.height;
    		video_width = video_format.fmt.pix.width;
    		//return -3;
    	} else {
    		video_height = video_format.fmt.pix.height;
    		video_width = video_format.fmt.pix.width;
    		printf("当前摄像头支持YUYV格式输出.width %d height %d\n", video_height, video_height);
    	}
    
    	/*3. 申请缓冲区*/
    	memset(&video_requestbuffers, 0, sizeof(struct v4l2_requestbuffers));
    	video_requestbuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	video_requestbuffers.count = 4;
    	video_requestbuffers.memory = V4L2_MEMORY_MMAP;
    	if (ioctl(video_fd, VIDIOC_REQBUFS, &video_requestbuffers))
    		return -4;
    	printf("成功申请的缓冲区数量:%d\n", video_requestbuffers.count);
    
    	/*4. 得到每个缓冲区的地址: 将申请的缓冲区映射到进程空间*/
    	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
    	int i;
    	for (i = 0; i < video_requestbuffers.count; i++) {
    		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		video_buffer.index = i;
    		video_buffer.memory = V4L2_MEMORY_MMAP;
    		if (ioctl(video_fd, VIDIOC_QUERYBUF, &video_buffer))
    			return -5;
    		/*映射缓冲区的地址到进程空间*/
    		video_buff_buff[i] =
    			mmap(NULL, video_buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, video_fd, video_buffer.m.offset);
    		printf("第%d个缓冲区地址:%#X\n", i, video_buff_buff[i]);
    	}
    
    	/*5. 将缓冲区放入到采集队列*/
    	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
    	for (i = 0; i < video_requestbuffers.count; i++) {
    		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		video_buffer.index = i;
    		video_buffer.memory = V4L2_MEMORY_MMAP;
    		if (ioctl(video_fd, VIDIOC_QBUF, &video_buffer)) {
    			printf("VIDIOC_QBUF error\n");
    			return -6;
    		}
    	}
    
    	/*6. 启动摄像头采集*/
    	printf("启动摄像头采集\n");
    	int opt_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	if (ioctl(video_fd, VIDIOC_STREAMON, &opt_type)) {
    		printf("VIDIOC_STREAMON error\n");
    		return -7;
    	}
    
    	return 0;
    }
    
    int lcd_init(void)
    {
    	/*2. 获取可变参数*/
    	if (ioctl(lcd_fd, FBIOGET_VSCREENINFO, &vinfo))
    		return -2;
    	printf("屏幕X:%d   屏幕Y:%d  像素位数:%d\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
    	//分配显存空间,完成图像显示
    	lcd_display_buff = malloc(vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
    
    	/*3. 获取固定参数*/
    	if (ioctl(lcd_fd, FBIOGET_FSCREENINFO, &finfo))
    		return -3;
    	finfo.smem_len = 115200;
    	finfo.line_length = 480;
    	printf("smem_len=%d Byte,line_length=%d Byte\n", finfo.smem_len, finfo.line_length);
    
    	/*4. 映射LCD屏物理地址到进程空间*/
    	lcd_mem_p =
    		(unsigned char *)mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, lcd_fd, 0); //从文件的那个地方开始映射
    	memset(lcd_mem_p, 0xFFFFFFFF, finfo.smem_len);
    	printf("映射LCD屏物理地址到进程空间\n");
    
    	return 0;
    }
    
    static void close_device(void)
    {
    	if (-1 == close(video_fd))
    		errno_exit("close");
    	video_fd = -1;
    
    	if (-1 == close(lcd_fd))
    		errno_exit("close");
    	lcd_fd = -1;
    }
    
    static void open_device(void)
    {
    	video_fd = open(dev_video, O_RDWR /* required */ | O_NONBLOCK, 0);
    	if (-1 == video_fd) {
    		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_video, errno, strerror(errno));
    		exit(EXIT_FAILURE);
    	}
    
    	lcd_fd = open(dev_fb0, O_RDWR, 0);
    	if (-1 == lcd_fd) {
    		fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_fb0, errno, strerror(errno));
    		exit(EXIT_FAILURE);
    	}
    }
    
    /* 将YUV格式数据转为RGB */
    void yuv_to_rgb(unsigned char *yuv_buffer, unsigned char *rgb_buffer, int iWidth, int iHeight)
    {
    	int x;
    	int z = 0;
    	unsigned char *ptr = rgb_buffer;
    	unsigned char *yuyv = yuv_buffer;
    	int r, g, b;
    	int y, u, v;
    
    	for (x = 0; x < iWidth * iHeight; x++) {
    		if (!z)
    			y = yuyv[0] << 8;
    		else
    			y = yuyv[2] << 8;
    		u = yuyv[1] - 128;
    		v = yuyv[3] - 128;
    		r = (y + (359 * v)) >> 8;
    		g = (y - (88 * u) - (183 * v)) >> 8;
    		b = (y + (454 * u)) >> 8;
    		*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
    		*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
    		*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
    		if (z++) {
    			z = 0;
    			yuyv += 4;
    		}
    	}
    }
    
    void rgb24_to_rgb565(char *rgb24, char *rgb16)
    {
    	int i = 0, j = 0;
    
    	for (i = 0; i < 240 * 240 * 3; i += 3) {
    		rgb16[j] = rgb24[i] >> 3; // B
    		rgb16[j] |= ((rgb24[i + 1] & 0x1C) << 3); // G
    		rgb16[j + 1] = rgb24[i + 2] & 0xF8; // R
    		rgb16[j + 1] |= (rgb24[i + 1] >> 5); // G
    
    		j += 2;
    	}
    }
    
    int main(int argc, char **argv)
    {
    	struct pollfd video_fds;
    
    	dev_video = "/dev/video0";
    	dev_fb0 = "/dev/fb0";
    
    	open_device();
    	video_init();
    	lcd_init();
    
    	/* 读取摄像头的数据*/
    	video_fds.events = POLLIN;
    	video_fds.fd = video_fd;
    
    	memset(&video_buffer, 0, sizeof(struct v4l2_buffer));
    	rgb_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 3);
    	yuv_buffer = malloc(CAM_WIDTH * CAM_HEIGHT * 3);
    
    	while (1) {
    		/*等待摄像头采集数据*/
    		poll(&video_fds, 1, -1);
    		/*得到缓冲区的编号*/
    		video_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		video_buffer.memory = V4L2_MEMORY_MMAP;
    		ioctl(video_fd, VIDIOC_DQBUF, &video_buffer);
    		printf("当前采集OK的缓冲区编号:%d,地址:%#X num:%d\n", video_buffer.index, video_buff_buff[video_buffer.index],
    		       strlen(video_buff_buff[video_buffer.index]));
    		/*对缓冲区数据进行处理*/
    		yuv_to_rgb(video_buff_buff[video_buffer.index], yuv_buffer, video_height, video_width);
    		rgb24_to_rgb565(yuv_buffer, rgb_buffer);
    		printf("显示屏进行显示\n");
    		//显示屏进行显示: 将显存空间的数据拷贝到LCD屏进行显示
    		memcpy(lcd_mem_p, rgb_buffer, vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
    		/*将缓冲区放入采集队列*/
    		ioctl(video_fd, VIDIOC_QBUF, &video_buffer);
    		printf("将缓冲区放入采集队列\n");
    	}
    
    	/* 关闭设备*/
    	close_device();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273

    调试

    # ./camera_display2.out 
    当前摄像头支持的分辨率:800x600
    当前摄像头支持YUYV格式输出.width 600 height 600
    [   52.859327] sun6i-csi 1cb4000.csi: Unsupported pixformat: 0x56595559 with mbus code: 0x2006!
    成功申请的缓冲区数量:4
    第0个缓冲区地址:0XB6D6A000
    第1个缓冲区地址:0XB6C7F000
    第2个缓冲区地址:0XB6B94000
    第3个缓冲区地址:0XB6AA9000
    启动摄像头采集
    VIDIOC_STREAMON error
    屏幕X:240   屏幕Y:240  像素位数:16
    smem_len=115200 Byte,line_length=480 Byte
    映射LCD屏物理地址到进程空间
    当前采集OK的缓冲区编号:0,地址:0XB6D6A000 num:0
    显示屏进行显示
    将缓冲区放入采集队列
    当前采集OK的缓冲区编号:0,地址:0XB6D6A000 num:0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行报错 sun6i-csi 1cb4000.csi: Unsupported pixformat: 0x56595559 with mbus code: 0x2006!
    并且屏幕显示一片绿
    在这里插入图片描述

    猜测 1:会不会是设备树配置不对
    仔细检查设备树参数,还真发现了一处错误

    &i2c1 {
    	pinctrl-0 = <&i2c1_pins>;
    	pinctrl-names = "default";
    	clock-frequency = <400000>;
    	status = "okay";
    	
    	ov2640: camera@30 {
    		compatible = "ovti,ov2640";
    		reg = <0x30>;
    		pinctrl-names = "default";
    		pinctrl-0 = <&csi1_mclk_pin>;
    		clocks = <&ccu CLK_CSI1_MCLK>;
    		clock-names = "xvclk";
    		assigned-clocks = <&ccu CLK_CSI1_MCLK>;
    		assigned-clock-rates = <26000000>;
    		port {
    			ov2640_0: endpoint {
    				remote-endpoint = <&csi1_ep>;
    				bus-width = <10>;
    			};
    		};
    	};
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    时钟频率 26000000 是我之前使用 26MHz 晶振时改的,后来由于 USB 问题,晶振换成了 24MHz 的,这里没有同步修改,那就改成 24000000
    运行,还是报同样的错误。
    猜测 2:是不是应用程序中摄像头分辨率设置的是 800x600,而屏幕是 240x240 导致的
    应用程序摄像头分辨率改成 240x240

    #define CAM_WIDTH 240
    #define CAM_HEIGHT 240
    
    • 1
    • 2

    运行,结果还是报同样的错误

    方案 3:上网搜索
    并没有找到类似问题。

    方案 4:看代码
    没办法只能看代码了
    根据内核报错信息 Unsupported pixformat 找到
    drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c

    static int sun6i_video_link_validate(struct media_link *link)
    {
    	struct video_device *vdev = container_of(link->sink->entity,
    						 struct video_device, entity);
    	struct sun6i_video *video = video_get_drvdata(vdev);
    	struct v4l2_subdev_format source_fmt;
    	int ret;
    
    	video->mbus_code = 0;
    
    	if (!media_entity_remote_pad(link->sink->entity->pads)) {
    		dev_info(video->csi->dev,
    			 "video node %s pad not connected\n", vdev->name);
    		return -ENOLINK;
    	}
    
    	ret = sun6i_video_link_validate_get_format(link->source, &source_fmt);
    	if (ret < 0)
    		return ret;
    
    	if (!sun6i_csi_is_format_supported(video->csi,
    					   video->fmt.fmt.pix.pixelformat,
    					   source_fmt.format.code)) {
    		dev_err(video->csi->dev,
    			"Unsupported pixformat: 0x%x with mbus code: 0x%x!\n",
    			video->fmt.fmt.pix.pixelformat,
    			source_fmt.format.code);
    		return -EPIPE;
    	}
    
    	if (source_fmt.format.width != video->fmt.fmt.pix.width ||
    	    source_fmt.format.height != video->fmt.fmt.pix.height) {
    		dev_err(video->csi->dev,
    			"Wrong width or height %ux%u (%ux%u expected)\n",
    			video->fmt.fmt.pix.width, video->fmt.fmt.pix.height,
    			source_fmt.format.width, source_fmt.format.height);
    		return -EPIPE;
    	}
    
    	video->mbus_code = source_fmt.format.code;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    在判断 sun6i_csi_is_format_supported() 处出问题了,追了下代码,发现最后是个宏函数就不想追了,索性将这段注释掉,
    运行,

    # ./camera_display2.out 
    当前摄像头支持的分辨率:240x240[   64.538654] sun6i-csi 1cb4000.csi: Wrong width or height 240x240 (800x600 expected)
    
    • 1
    • 2

    结果又报 Wrong width or height 240x240 (800x600 expected) 错误,和上面一样,注释掉先让代码跑通,
    运行,不报错了,屏幕也开始显示图像了
    在这里插入图片描述
    但是这图像明显不对啊,不过至少有进步了
    那就继续追代码,先将那两处判断恢复。
    跟着代码,一路追到获取摄像头参数的地方
    drivers/media/i2c/ov2640.c

    static int ov2640_get_fmt(struct v4l2_subdev *sd,
    		struct v4l2_subdev_pad_config *cfg,
    		struct v4l2_subdev_format *format)
    {
    	struct v4l2_mbus_framefmt *mf = &format->format;
    	struct i2c_client  *client = v4l2_get_subdevdata(sd);
    	struct ov2640_priv *priv = to_ov2640(client);
    
    	if (format->pad)
    		return -EINVAL;
    
    	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
    #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
    		mf = v4l2_subdev_get_try_format(sd, cfg, 0);
    		format->format = *mf;
    		return 0;
    #else
    		return -ENOTTY;
    #endif
    	}
    
    	mf->width	= priv->win->width;
    	mf->height	= priv->win->height;
    	mf->code	= priv->cfmt_code; // 这行
    	mf->colorspace	= V4L2_COLORSPACE_SRGB;
    	mf->field	= V4L2_FIELD_NONE;
    	mf->ycbcr_enc	= V4L2_YCBCR_ENC_DEFAULT;
    	mf->quantization = V4L2_QUANTIZATION_DEFAULT;
    	mf->xfer_func	= V4L2_XFER_FUNC_DEFAULT;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    重点是 mf->code = priv->cfmt_code; 这行,报错信息 with mbus code: 0x2006 中的 0x2006 应该就是该值,
    继续追

    /*
     * i2c_driver functions
     */
    static int ov2640_probe(struct i2c_client *client,
    			const struct i2c_device_id *did)
    {
    	struct ov2640_priv	*priv;
    	struct i2c_adapter	*adapter = client->adapter;
    	int			ret;
    
    	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
    		dev_err(&adapter->dev,
    			"OV2640: I2C-Adapter doesn't support SMBUS\n");
    		return -EIO;
    	}
    
    	priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
    	if (!priv)
    		return -ENOMEM;
    
    	if (client->dev.of_node) {
    		priv->clk = devm_clk_get(&client->dev, "xvclk");
    		if (IS_ERR(priv->clk))
    			return PTR_ERR(priv->clk);
    		ret = clk_prepare_enable(priv->clk);
    		if (ret)
    			return ret;
    	}
    
    	ret = ov2640_probe_dt(client, priv);
    	if (ret)
    		goto err_clk;
    
    	priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT);
    	priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; // 这行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    cfmt_code 是在这里被赋值的,其中 MEDIA_BUS_FMT_UYVY8_2X8 值是 0x2006

    #define MEDIA_BUS_FMT_UYVY8_2X8			0x2006
    
    • 1

    而我们要使用的是 YUYV 格式,那就将这里改掉,改成 YUYV

    // priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8;
    priv->cfmt_code = MEDIA_BUS_FMT_YUYV8_2X8;
    
    • 1
    • 2

    运行,

    /root # ./camera_display2.out 
    当前摄像头支持的分辨率:240x240[   55.298859] sun6i-csi 1cb4000.csi: Wrong width or height 240x240 (800x600 expected)
    
    • 1
    • 2

    格式问题看起来解了,那就继续解分辨率的问题
    drivers/media/i2c/ov2640.c

    // mf->width	= win->width;
    	// mf->height	= win->height;
    	mf->width	= 240;
    	mf->height	= 240;
    
    • 1
    • 2
    • 3
    • 4

    将 ov2640.c 文件中关于分辨率的设置,都硬编码为 240x240
    运行
    在这里插入图片描述
    成功了,不过发现图像方向和屏幕方向不一致,
    没找到摄像头旋转的方法,最终旋转屏幕实现了方向一致
    在这里插入图片描述
    至此,摄像头捕获的画面可以实时显示到屏幕了。

  • 相关阅读:
    从HashMap的执行流程开始 揭开HashMap底层实现
    线程池工作流程-图示
    HTTP(0)-并发服务器
    撰写外文时怎样引用中文文献?
    Python中的numpy库
    JAVA:实现RabinKarpAlgorithm拉宾卡普算法(附完整源码)
    代码随想录1刷—数组篇
    MySQL数据库之存储引擎
    kafka基础(3):kafka 单机与集群安装
    软件测试测试常见分类有哪些?
  • 原文地址:https://blog.csdn.net/lyndon_li/article/details/133394081