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

关于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 为测试使用的执行程序,编译后直接产生。


 

上一篇:用3.5寸和4.3寸TFT-LCD显示图片


下一篇:501【毕设课设】基于单片机的模拟波形发生器信号发生器设计