ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

spi-lcd-st7789-驱动开发-v4l2接口介绍(3)

2021-02-18 22:06:03  阅读:268  来源: 互联网

标签:index fmt VIDIOC spi lcd printf st7789 buf APP


关于v4l2 接口介绍的可以参考这篇博文:

https://blog.csdn.net/zx3517288/article/details/51682530

 

测试程序的v4l2的主要参考代码

摄像头的设置

static int start_capture(int fd)
{
	enum v4l2_buf_type type;
			
	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    //开始捕捉图像数据
    return ioctl(fd, VIDIOC_STREAMON, &type);

}


struct v4l2_capability cap;  
struct v4l2_fmtdesc fmtdesc;
struct v4l2_streamparm streamparm;
struct v4l2_format fmt;
struct v4l2_requestbuffers req;

//setup camera

	//查看设备功能
    ret = ioctl(fd_video, VIDIOC_QUERYCAP, &cap);
    if (ret < 0) {
        APP_PRINT("requre VIDIOC_QUERYCAP fialed! \n");
        return -1;
    } else {
		//APP_PRINT("requre VIDIOC_QUERYCAP ok! \n");
	}	
	printf("\nCameravCapabilityInfo:\n");
    printf("\tdriver:%s\n",cap.driver);
    printf("\tcard:%s\n",cap.card);
    printf("\tbus_info:%s\n",cap.bus_info);
    printf("\tversion:%d\n",cap.version);
    printf("\tcapabilities:%x\n",cap.capabilities);
	
    if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) {
        printf("\tCamera supports capture.\n");
    } else {
		printf("\tCamera didn't support capture.\n");
	}
 
    if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) {
        printf("\tCamera supports streaming.\n");
    } else {
		printf("\tCamera didn't support streaming.\n");
	}
	

	// 得到摄像头所支持的所有格式
    fmtdesc.index = 0;
    fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("\nCamera Support video formats:\n");
    while (ioctl(fd_video, VIDIOC_ENUM_FMT, &fmtdesc) != -1) {
        printf("\t%d.%s\n", fmtdesc.index+1, fmtdesc.description);
        fmtdesc.index++;
    }
	
	//set video format
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = VIDEO_WIDTH;
    fmt.fmt.pix.height = VIDEO_HEIGHT;
    fmt.fmt.pix.pixelformat = VIDEO_PIXEL_FORMAT;
    fmt.fmt.pix.field = V4L2_FIELD_NONE;
    if(ioctl(fd_video, VIDIOC_S_FMT, &fmt) < 0){//设置图片格式
        APP_PRINT("Ioctl VIDIOC_S_FMT error!!!\n");  
        return -1;
    } else {
		//APP_PRINT("Ioctl VIDIOC_S_FMT ok!!!\n");  
	}
	
	
    if(ioctl(fd_video, VIDIOC_G_FMT, &fmt) < 0){//得到图片格式
        APP_PRINT("Ioctl VIDIOC_G_FMT error!!!\n");  
        return -1;
    } else {		
		//APP_PRINT("Ioctl VIDIOC_G_FMT ok!!!\n");  
	}

	printf("\nV4l2FmtInfo:\n");
    printf("\t fmt.type:\t\t%d\n",fmt.type);
    printf("\t pix.pixelformat:\t%c%c%c%c\n", \
            fmt.fmt.pix.pixelformat & 0xFF,\
            (fmt.fmt.pix.pixelformat >> 8) & 0xFF, \
            (fmt.fmt.pix.pixelformat >> 16) & 0xFF,\
            (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
    printf("\t pix.width:\t\t%d\n",fmt.fmt.pix.width);
    printf("\t pix.height:\t\t%d\n",fmt.fmt.pix.height);
    printf("\t pix.field:\t\t%d\n",fmt.fmt.pix.field);
	printf("\n");
	
	//设置帧格式
   
    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    streamparm.parm.capture.timeperframe.numerator = 1;
    streamparm.parm.capture.timeperframe.denominator = 30;
    streamparm.parm.capture.capturemode = 0;

    if (ioctl(fd_video, VIDIOC_S_PARM, &streamparm) < 0) {
		APP_PRINT("Ioctl VIDIOC_S_PARM error!!!\n");  
        return -1;
    } else {
		//APP_PRINT("Ioctl VIDIOC_S_PARM ok!!!\n");  
	}
	
	//申请v4l2 buf
	req.count = n_map_buffers;//申请缓冲数量
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    ioctl(fd_video, VIDIOC_REQBUFS, &req);//申请缓冲
    if (req.count < 2) {
        APP_PRINT("Ioctl VIDIOC_REQBUFS error!!!\n");  
        return -1;
    } else {
		n_map_buffers = req.count;
		//APP_PRINT("Ioctl VIDIOC_REQBUFS ok, req.count:%d !!!\n", req.count);  
	}

	//映射v4l2 buf
	memset(img_bufs, 0, (sizeof(img_bufs)/sizeof(img_bufs[9]))*sizeof(struct app_v4l2_buf));
	for (buf_index = 0; buf_index < req.count; ++buf_index){
        struct v4l2_buffer buf;//驱动中的一帧
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = buf_index;
 
        if (ioctl(fd_video, VIDIOC_QUERYBUF, &buf) < 0){//映射用户空间
			APP_PRINT("Ioctl VIDIOC_QUERYBUF error, index:%d!!!\n", buf_index);  
            return -1;
        } else {	
			//APP_PRINT("Ioctl VIDIOC_QUERYBUF ok, index:%d!!!\n", buf_index);	
		}
 
        img_bufs[buf_index].length = buf.length;
        img_bufs[buf_index].start =(unsigned char*) mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd_video, buf.m.offset);
 
        if (MAP_FAILED == img_bufs[buf_index].start){
			APP_PRINT("mmap error, index:%d!!!\n", buf_index);  
            return -1;
        } else {
			//APP_PRINT("mmap ok, index:%d!!!\n", buf_index);	
		}
 
        //APP_PRINT("v4l2 buffer %d: address = 0x%x, length = %d \n",req.count, (unsigned int)img_bufs[buf_index].start, img_bufs[buf_index].length);
    }
	
	printf("V4l2 buf info:\n");
	printf("\tBuf count:%d\n", req.count);
	for (buf_index=0; buf_index<req.count; ++buf_index) {
		printf("\tindex:%d map_address:0x%x length:%d \n", buf_index, (unsigned int)img_bufs[buf_index].start, img_bufs[buf_index].length);
	}
	printf("\n");
	
    //申请到的缓冲进入队列
	for (buf_index=0; buf_index<req.count; ++buf_index) {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(struct v4l2_buffer));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = buf_index;
 
        //申请到的缓冲进入队列
        if (ioctl(fd_video, VIDIOC_QBUF, &buf) < 0) {
			APP_PRINT("Ioctl VIDIOC_QBUF error, index:%d!!!\n", buf_index);  
            return -1;
        } else {
			//APP_PRINT("Ioctl VIDIOC_QBUF ok, index:%d!!!\n", buf_index);	
		}
    }   

//开始捕捉图像数据
	ret  = start_capture(fd_video);

初始化成功会有如下信息输出:

CameravCapabilityInfo:
        driver:uvcvideo
        card:PC Camera
        bus_info:usb-ci_hdrc.1-1.3
        version:264536
        capabilities:84200001
        Camera supports capture.
        Camera supports streaming.

Camera Support video formats:
        1.Motion-JPEG
        2.YUYV 4:2:2

V4l2FmtInfo:
         fmt.type:              1
         pix.pixelformat:       MJPG
         pix.width:             320
         pix.height:            240
         pix.field:             1

V4l2 buf info:
        Buf count:16
        index:0 map_address:0x76e2f000 length:230400
        index:1 map_address:0x76df6000 length:230400
        index:2 map_address:0x76dbd000 length:230400
        index:3 map_address:0x76d84000 length:230400
        index:4 map_address:0x76d4b000 length:230400
        index:5 map_address:0x76d12000 length:230400
        index:6 map_address:0x76cd9000 length:230400
        index:7 map_address:0x76ca0000 length:230400
        index:8 map_address:0x76c67000 length:230400
        index:9 map_address:0x76c2e000 length:230400
        index:10 map_address:0x76bf5000 length:230400
        index:11 map_address:0x76bbc000 length:230400
        index:12 map_address:0x76b83000 length:230400
        index:13 map_address:0x76b4a000 length:230400
        index:14 map_address:0x76b11000 length:230400
        index:15 map_address:0x76ad8000 length:230400

这里进行摄像头数据格式设置之前首先要明确摄像头的参数:

  1. 支持数据格式
  2. 支持分辨率
  3. 支持帧率

摄像头数据获取

测试代码里面创建了一个线程专门获取摄像头数据主要代码如下:

void *process_data_thread_fn(void*  arg)
{
	struct v4l2_buffer buf;
	int ret = 0;
	
	
	FILE *fp_save_file = NULL;
	fd_set fds;  
	struct timeval tv;  
	FD_ZERO(&fds);  
	FD_SET(fd_video, &fds);  
	/* Timeout. */  
	tv.tv_sec = 10;  
	tv.tv_usec = 0; 
	unsigned char *ptr_write_fb = NULL;
	char rgb888_file_name[128];
	char local_sys_cmd[256];
	unsigned int filesize;
	struct stat statbuff;  
  
	while (1) {

		usleep(1000 * 5);

		if (!quit_flag) {


			*/
			
			//读取摄像头数据
			//memset(&buf, 0, sizeof(struct v4l2_buffer));
			buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
			buf.memory = V4L2_MEMORY_MMAP;		 
			ret = ioctl(fd_video, VIDIOC_DQBUF, &buf);//出列采集的帧缓冲,成功返回0
			
			if (!ret) {
				//要保存图像数据我们就保存 
				if (save_img_flag) {
					if (index_save_image_file <= cnt_save_images) {
						//将摄像头采集得到的数据写入文件中
						sprintf(save_file_name, "save_%dx%d_%d.%s", VIDEO_WIDTH,  VIDEO_HEIGHT, index_save_image_file, SUFFIX_SAVE_IMAGE(VIDEO_PIXEL_FORMAT));
						fp_save_file = fopen(save_file_name, "w+");
						if (NULL == fp_save_file) {
							APP_PRINT("Open %s error!!!\n", save_file_name);
							continue;
						}
	 					fwrite(img_bufs[buf.index].start, buf.bytesused , 1, fp_save_file); //buf.index 为获取到的v4l2 buf 队列中有数据buf的索引,我们可以通过这个索引获取到图像数据
						fclose(fp_save_file);
						APP_PRINT("Save %s ok!!!\n", save_file_name);
					} else {
						APP_PRINT("Alread save %d pics quit!!!\n", cnt_save_images);
						break;
					}
					index_save_image_file++;
					
				} else {


					if (VIDEO_PIXEL_FORMAT == V4L2_PIX_FMT_JPEG) {
						//将摄像头采集得到的数据写入文件中
						sprintf(save_file_name, "save_%dx%d_%d.%s", VIDEO_WIDTH,  VIDEO_HEIGHT, index_save_image_file, SUFFIX_SAVE_IMAGE(VIDEO_PIXEL_FORMAT));
						fp_save_file = fopen(save_file_name, "w+");
						if (NULL == fp_save_file) {
							APP_PRINT("Open %s error!!!\n", save_file_name);
							continue;
						}
	 					fwrite(img_bufs[buf.index].start, buf.bytesused , 1, fp_save_file);
						fclose(fp_save_file);
						
						
						//调用djpeg 解码     240x240 jpeg->rgb888 并且缩放至 240x180 保存到一个临时文件中
						sprintf(rgb888_file_name, "save_%dx%d_%d.rgb888", 240, 180);
						//./djpeg -rgb -scale 3/4  -outfile  save_240x180_1.rgb888 save_320x240_1.jpeg
						sprintf(local_sys_cmd, "./djpeg -rgb -scale 3/4  -outfile  %s %s", rgb888_file_name, save_file_name);	
						system(local_sys_cmd);
						sprintf(local_sys_cmd, "rm %s", save_file_name);
						system(local_sys_cmd);
						
						//将缩放后的    rgb888 读到buf里面来
						fp_save_file = fopen(rgb888_file_name, "r");
						if (NULL == fp_save_file) {
							APP_PRINT("Open %s error!!!\n", save_file_name);
							continue;
						}

						stat(rgb888_file_name, &statbuff);
					    filesize = statbuff.st_size;
										
	 					fread(rgb888_buf_full,  filesize, 1, fp_save_file);
						//APP_PRINT("Index:%d file size:%u\n", index_save_image_file, filesize);
						fclose(fp_save_file);
						
						sprintf(local_sys_cmd, "rm %s", rgb888_file_name);
						system(local_sys_cmd);
						
						index_save_image_file++;

						//fb 显示目前只支持  240x180
						ptr_write_fb = (unsigned char *)(fbp + ((VIDEO_HEIGHT-180)/2)*VIDEO_WIDTH*2);
						cvt_rgb888_2_rgb555_lcd_order(rgb888_buf_full, rgb565_buf_full, 240, 180);
						memcpy(ptr_write_fb, rgb565_buf_full, 240 * 180 *2);
						ret = ioctl(fd_fb, FBIOPAN_DISPLAY, &vinfo);
						if (ret < 0) {	
							APP_PRINT("FBIOPAN_DISPLAY frame_num:%d error \n", index_save_image_file);
						} 			
					}			
				}
			} else {	
				APP_PRINT("VIDIOC_DQBUF error!!!\n");
			}
			
			ret = ioctl(fd_video,VIDIOC_QBUF, &buf); 
			if (ret < 0) {
				APP_PRINT("VIDIOC_QBUF error, buf_index:%d, errno:%d !!!\n", buf.index, errno);
			}
			
		} else {
			APP_PRINT("Recevie quit flag,quit!!!\n");
			break;
		}
	}
	return 0;
}

通过上面的设置,我们可以连续的从摄像头获取到320x240格式的jpeg格式的视频帧数据。

摄像头jpeg数据的显示

有个问题就是我们lcd分辨率为240x240且数据格式rgb555,摄像头最小分辨率为320x240,为了让摄像头图像能在lcd上显示,我们对320x240 jpeg摄像头数据进行如下处理:

320x240-jpeg-buf---->保存为320x240 jpeg图像--借助libjpeg-turbo-cjpeg进行处理->240x180 rgb888格式图像->读出240x180 rgb888格式图片到内存->240x180 rgb888buf------>240x180-rgb565 buf->更新framebuf->数据通过spi发送到lcd

代码中用到的libjpeg-turbo在git地址:

https://gitee.com/wllw7176/self_100ask_imx6ull/tree/master/self_dir/third_part/libjpeg-turbo-1.5.3

编译这个库只要执行里面的mybuild.sh就可以了,上面测的代码用到的cjpeg和库就在编译后产生的文件中,将这些文件复制到test_spi_lcd_st7789_jpeg所在目录就可以了。

 

test_spi_lcd_st7789_jpeg 为测试使用的执行程序,编译后直接产生。


 

标签:index,fmt,VIDIOC,spi,lcd,printf,st7789,buf,APP
来源: https://blog.csdn.net/wllw7176/article/details/113853024

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有