飞凌嵌入式OKMX8MP-C
开发板默认将三种显示接口LVDS/MIPI/HDMI全部打开,而我要对HDMI输出编程的话需要将MIPI显示关闭,关闭方式是进入uboot菜单的Display选项进行操作:
在串口终端启动uboot时狂按空格键,即可进入uboot菜单:
uboot菜单中的Display select选项就是设置输出接口的,可以分别对这三种接口进行设置,默认情况下三个输出接口都打开,而如果要进行FrameBuffer编程的话,我经过实际测试,需要将LVDS和HDMI选项都打开,HDMI接口才能正常输出/dev/fb0外设的映射图像:
如果将LVDS接口关闭的话,HDMI接口输出不正常,/dev/fb0也不会生成,也就无法对FrameBuffer进行编程:
在飞凌厂商提供的文档中,已经写明了HDMI接口输出的分辨率最大为1280*800*32,这个是由HDMI输出芯片决定的,比起瑞芯微主流主控的2K/4K差了不是一点半点(RK3399/RK3568),毕竟IMX8的定位是工业控制而不是多媒体应用,图形输出性能较差也是无可厚非的,对/dev/fb0外设进行ioctl,输出的分辨率也是1280*800,32位色彩:
使用命令
- x11vnc -rawfb /dev/fb0 -clip 1280*800
可搭建x11vnc服务器,将FrameBuffer画面输出至vnc软件客户端,也就可以在不使用HDMI输出设备的FrameBuffer分辨率只有1280*800,意味着,V4L2相机生成的流画面,分辨率高于或等于这个数无法生成显示,甚至在实测中,低于这个数一点点也无法生成(1000*750),会提示段错误终止进程,因此我经过反复调试,最终将V4L2生成的流分辨率设置为900*675,无法对相机物尽其用(相机最大分辨率为1080P),但这是板子的Framebuffer最大支持的输出分辨率,无法再设置更大了。
- #define IMAGEWIDTH 900
- #define IMAGEHEIGHT 675
这次的V4L2推流,我不采用之前已经熟练掌握的V4L2生成YUYV流,而是直接生成MJPEG流,生成此流有2个好处,一是生成MJPEG流无需经过YUYV转RGB的步骤,帧生成时间更短,相比起之前生成YUYV流的方式,流畅度有非常明显的提升;二是生成MJPEG流可直接保存为JPEG文件,在确保IO读取锁无冲突的前提下,可供外部程序进行访问。要使用V4L2驱动库生成MJPEG流,初始化步骤要写对:
- struct v4l2_format fmt;
- fmt.type = V4L2_CAP_VIDEO_CAPTURE;
- fmt.fmt.pix.width = IMAGEWIDTH;
- fmt.fmt.pix.height = IMAGEHEIGHT;
- fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
- if (ioctl (fd_video, VIDIOC_S_FMT, &fmt) == -1)
- {
- perror("ERROR camera VIDIOC_S_FMT Failed.");
- return -1;
- }
检查写入参数是否正确:
- if (ioctl (fd_video, VIDIOC_G_FMT, &fmt) == -1)
- {
- perror("ERROR camera VIDIOC_G_FMT Failed.");
- return -1;
- }
使用mmap()进行物理内存地址到用户内存地址的映射,即使用一个用户定义缓存来读取物理内存中的摄像头缓存数据:
- struct v4l2_buffer v4l2_buf;
- v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- v4l2_buf.memory = V4L2_MEMORY_MMAP;
- for(i = 0; i < 4; i++)
- {
- v4l2_buf.index = i;
- if(ioctl(fd_video, VIDIOC_QUERYBUF, &v4l2_buf) < 0)
- {
- perror("Unable to query buffer.");
- return -1;
- }
- pic.tmpbuffer[i] = (unsigned char*)mmap(NULL, v4l2_buf.length, PROT_READ, MAP_SHARED, fd_video, v4l2_buf.m.offset);
- if(pic.tmpbuffer[i] == MAP_FAILED)
- {
- perror("Unable to map buffer.");
- return -1;
- }
- if(ioctl(fd_video, VIDIOC_QBUF, &v4l2_buf) < 0)
- {
- perror("Unable to queue buffer.");
- return -1;
- }
- }
开启捕捉:
- int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if(ioctl(fd_video , VIDIOC_STREAMON , &type) < 0)
- {
- perror("Unable to start capture.");
- return -1;
- }
捕捉并生成JPEG文件,循环执行的函数:
int V4l2_Grab_Mjpeg(char * filename)
{
- struct v4l2_buffer buff;
- buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buff.memory = V4L2_MEMORY_MMAP;
- if(ioctl(fd_video , VIDIOC_DQBUF, &buff) < 0)
- {
- printf("camera VIDIOC_DQBUF Failed.n");
- usleep(1000*1000);
- return -1;
- }
- pic.tmpbytesused[buff.index] = buff.bytesused;
- printf("size : %dn",pic.tmpbytesused[buff.index]);
- int jpg_fd = open("1.jpeg" , O_RDWR | O_CREAT , 00700);
- if(jpg_fd == -1)
- {
- printf("open ipg Failed!n ");
- return -1;
- }
- int writesize = write(jpg_fd , pic.tmpbuffer[buff.index] , pic.tmpbytesused[buff.index]);
- printf("Write successfully size : %dn" , writesize);
- close(jpg_fd);
- //10、Queue the buffers.
- if(ioctl(fd_video , VIDIOC_QBUF, &buff) < 0)
- {
- printf("camera VIDIOC_QBUF Failed.");
- usleep(1000*1000);
- return -1;
- }
- return 0;
- }
主循环运行:
- while(1)
- {
- V4l2_Grab_Mjpeg(MJPEG_FILE_NAME);
- LCD_RGB888_Show_JPG_File(FB_DEV, 0 , 0 , MJPEG_FILE_NAME);
- }
运行效果: