 
                        关键字: 正点原子,i.MX6UL,linux, framebuffer
- 主机环境:ubuntu16.04-64bit
- 硬件版本:正点原子i.MX6UL emmc
固件帖在本站:[[正点原子i.MX6UL开发板] 编译uboot、linux、buildroot-rootfs](https://whycan.cn/t_3550.html)
---
#### 一、本篇目标
接上篇- [[正点原子i.MX6UL]v4l2+framebuffer预览USB摄像头(一):采集拍照](https://whycan.cn/t_3658.html)
本篇主要实现把拍照保存的bmp图片,显示到framebuffer上。
#### 二、相关概念
一些是我自己的基本理解,比较粗糙~
帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。framebuffer是LCD对应的一中HAL(硬件抽象层),提供抽象的,统一的接口操作,用户不必关心硬件层是怎么实施的。这些都是由 Framebuffer设备驱动来完成的。
- 帧缓冲设备对应的设备文件为/dev/fb*
- 使用时需要先获取到相关信息如屏幕尺寸,显存大小,RGB格式等
- 通常把帧缓冲显存映射至用户程序空间来使用,非常方便
##### s0:获取帧缓冲信息
主要是通过struct fb_var_screeninfo(表示可变信息)、struct fb_fix_screeninfo(表示固定信息)这2个结构体,在/usr/include/linux/fb.h中定义:
struct fb_fix_screeninfo {
	char id[16];			/* identification string eg "TT Builtin" */
	unsigned long smem_start;	/* Start of frame buffer mem */
					/* (physical address) */
	__u32 smem_len;			/* Length of frame buffer mem */
	__u32 type;			/* see FB_TYPE_*		*/
	__u32 type_aux;			/* Interleave for interleaved Planes */
	__u32 visual;			/* see FB_VISUAL_*		*/ 
	__u16 xpanstep;			/* zero if no hardware panning  */
	__u16 ypanstep;			/* zero if no hardware panning  */
	__u16 ywrapstep;		/* zero if no hardware ywrap    */
	__u32 line_length;		/* length of a line in bytes    */
	unsigned long mmio_start;	/* Start of Memory Mapped I/O   */
					/* (physical address) */
	__u32 mmio_len;			/* Length of Memory Mapped I/O  */
	__u32 accel;			/* Indicate to driver which	*/
					/*  specific chip/card we have	*/
	__u16 capabilities;		/* see FB_CAP_*			*/
	__u16 reserved[2];		/* Reserved for future compatibility */
};
struct fb_var_screeninfo {
	__u32 xres;			/* visible resolution		*/
	__u32 yres;
	__u32 xres_virtual;		/* virtual resolution		*/
	__u32 yres_virtual;
	__u32 xoffset;			/* offset from virtual to visible */
	__u32 yoffset;			/* resolution			*/
	__u32 bits_per_pixel;		/* guess what			*/
	__u32 grayscale;		/* 0 = color, 1 = grayscale,	*/
					/* >1 = FOURCC			*/
	struct fb_bitfield red;		/* bitfield in fb mem if true color, */
	struct fb_bitfield green;	/* else only length is significant */
	struct fb_bitfield blue;
	struct fb_bitfield transp;	/* transparency			*/	
	__u32 nonstd;			/* != 0 Non standard pixel format */
	__u32 activate;			/* see FB_ACTIVATE_*		*/
	__u32 height;			/* height of picture in mm    */
	__u32 width;			/* width of picture in mm     */
	__u32 accel_flags;		/* (OBSOLETE) see fb_info.flags */
	/* Timing: All values in pixclocks, except pixclock (of course) */
	__u32 pixclock;			/* pixel clock in ps (pico seconds) */
	__u32 left_margin;		/* time from sync to picture	*/
	__u32 right_margin;		/* time from picture to sync	*/
	__u32 upper_margin;		/* time from sync to picture	*/
	__u32 lower_margin;
	__u32 hsync_len;		/* length of horizontal sync	*/
	__u32 vsync_len;		/* length of vertical sync	*/
	__u32 sync;			/* see FB_SYNC_*		*/
	__u32 vmode;			/* see FB_VMODE_*		*/
	__u32 rotate;			/* angle we rotate counter clockwise */
	__u32 colorspace;		/* colorspace for FOURCC-based modes */
	__u32 reserved[4];		/* Reserved for future compatibility */
};
struct fb_bitfield {
	__u32 offset;			/* beginning of bitfield	*/
	__u32 length;			/* length of bitfield		*/
	__u32 msb_right;		/* != 0 : Most significant bit is right */ 
};- 获取可变信息:
    if (ioctl(screen_fbd, FBIOGET_VSCREENINFO, &fb_var) == -1) {
        printf("err read screen info FBIOGET_VSCREENINFO.\n");
        close(screen_fbd);
        return 0;
    }- 获取固定信息:
    if (ioctl(screen_fbd, FBIOGET_FSCREENINFO, &fb_fix) == -1) {
        printf("err read screen info FBIOGET_FSCREENINFO.\n");
        close(screen_fbd);
        return 0;
    }##### s1:把帧缓冲映射至用户空间
映射显存:
    if ((fb_addr = (char*)mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0)) < 0) {
        printf("fb mmap error\n");
        exit(1);
    }取消映射:
    munmap(fb_addr, fb_size);#### 三、代码实现
- 代码1
本代码主要实现了获取帧缓冲的信息,相当于帧缓冲版的"hello world"。
/*
 * @FilePath: /0317/fb-getinfo.c
 * @Version: 1.0.0
 * @Author: zys
 * @LastAuthor   : zys
 * @CreationDate: 2020-03-17 21:54:30
 * @LastEditTime : 2020-03-17 22:06:54
 * @Description :  获取framebuffer的信息,相当于framebuffer版的"hello world"。
 */
#include <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/*
yz imx6ul test ok.
-build:
    $ARMGCC fb-getinfo.c -o fb-getinfo
-tftp get:    
    cp fb-getinfo /tftpboot/
    tftp -g -r fb-getinfo 192.168.2.119
*/
int main(int argc, char* argv[])
{
    int                      screen_fbd = 0;
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    char*                    env = NULL;
    if (!(env = getenv("FRAMEBUFFER"))) {
        env = "/dev/fb0";
    }
    screen_fbd = open(env, O_RDWR);
    if (screen_fbd < 0) {
        printf("err open fb dev: %s \r\n", env);
        return 0;
    }
    else {
        printf("success open fb dev : %s \r\n", env);
    }
    if (ioctl(screen_fbd, FBIOGET_FSCREENINFO, &fb_fix) == -1) {
        printf("err read screen info FBIOGET_FSCREENINFO.\n");
        close(screen_fbd);
        return 0;
    }
    printf("fb_fix.id=\"%s\"\r\n", fb_fix.id);
    printf("fb_fix.smem_start=%#x\r\n", fb_fix.smem_start);
    printf("fb_fix.mem_len=%d\r\n", fb_fix.smem_len);
    printf("fb_fix.type=%d\r\n", fb_fix.type);
    printf("fb_fix.type_aux=%d\r\n", fb_fix.type_aux);
    printf("fb_fix.visual=%d\r\n", fb_fix.visual);
    printf("fb_fix.xpanstep=%s\r\n", fb_fix.xpanstep);
    printf("fb_fix.ypanstep=%d\r\n", fb_fix.ypanstep);
    printf("fb_fix.ywrapstep=%d\r\n", fb_fix.ywrapstep);
    printf("fb_fix.line_length=%d\r\n", fb_fix.line_length);
    printf("fb_fix.mmio_start=%#x\r\n", fb_fix.mmio_start);
    printf("fb_fix.mmio_len=%#x\r\n", fb_fix.mmio_len);
    printf("fb_fix.accel=%d\r\n", fb_fix.accel);
    printf("fb_fix.reserved[0]=%d\r\n", fb_fix.reserved[0]);
    printf("fb_fix.reserved[1]=%d\r\n", fb_fix.reserved[1]);
    printf("fb_fix.reserved[2]=%d\r\n", fb_fix.reserved[2]);
    if (ioctl(screen_fbd, FBIOGET_VSCREENINFO, &fb_var) == -1) {
        printf("err read screen info FBIOGET_VSCREENINFO.\n");
        close(screen_fbd);
        return 0;
    }
    printf("fb_var.xres=%d\r\n", fb_var.xres);
    printf("fb_var.yres=%d\r\n", fb_var.yres);
    printf("fb_var.xres_virtual=%d\r\n", fb_var.xres_virtual);
    printf("fb_var.yres_virtual=%d\r\n", fb_var.yres_virtual);
    printf("fb_var.xoffset=%d\r\n", fb_var.xoffset);
    printf("fb_var.yoffset=%d\r\n", fb_var.yoffset);
    printf("fb_var.bits_per_pixel=%d\r\n", fb_var.bits_per_pixel);
    printf("fb_var.grayscale=%d\r\n", fb_var.grayscale);
    printf("fb_bitfield red, offset = %u, length = %u, right = %u, \n\r", fb_var.red.offset, fb_var.red.length, fb_var.red.msb_right);
    printf("fb_bitfield green, offset = %u, length = %u, right = %u, \n\r", fb_var.green.offset, fb_var.green.length, fb_var.green.msb_right);
    printf("fb_bitfield blue, offset = %u, length = %u, right = %u, \n\r", fb_var.blue.offset, fb_var.blue.length, fb_var.blue.msb_right);
    printf("fb_bitfield transp, offset = %u, length = %u, right = %u, \n\r\n\r", fb_var.transp.offset, fb_var.transp.length, fb_var.transp.msb_right);
    printf("fb_var.red=%#x\r\n", fb_var.red);
    printf("fb_var.green=%#x\r\n", fb_var.green);
    printf("fb_var.blue=%#x\r\n", fb_var.blue);
    printf("fb_var.transp=%#x\r\n", fb_var.transp);
    printf("fb_var.nonstd=%d\r\n", fb_var.nonstd);
    printf("fb_var.activate=%d\r\n", fb_var.activate);
    printf("fb_var.height=%d\r\n", fb_var.height);
    printf("fb_var.width=%d\r\n", fb_var.width);
    printf("fb_var.accel_flags=%d\r\n", fb_var.accel_flags);
    printf("fb_var.pixclock=%d\r\n", fb_var.pixclock);
    printf("fb_var.left_margin=%d\r\n", fb_var.left_margin);
    printf("fb_var.right_margin=%d\r\n", fb_var.right_margin);
    printf("fb_var.upper_margin=%d\r\n", fb_var.upper_margin);
    printf("fb_var.lower_margin=%d\r\n", fb_var.lower_margin);
    printf("fb_var.hsync_len=%d\r\n", fb_var.hsync_len);
    printf("fb_var.vsync_len=%d\r\n", fb_var.vsync_len);
    printf("fb_var.sync=%d\r\n", fb_var.sync);
    printf("fb_var.vmode=%d\r\n", fb_var.vmode);
    printf("fb_var.rotate=%d\r\n", fb_var.rotate);
    printf("fb_var.reserved[0]=%d\r\n", fb_var.reserved[0]);
    printf("fb_var.reserved[1]=%d\r\n", fb_var.reserved[1]);
    printf("fb_var.reserved[2]=%d\r\n", fb_var.reserved[2]);
    printf("fb_var.reserved[3]=%d\r\n", fb_var.reserved[3]);
    close(screen_fbd);
    return 0;
}显示bmp还未实现,后续会更新上~
#### 四、测试程序
- 编译程序
$ARMGCC fb-getinfo.c -o fb-getinfo- 运行程序
先把程序复制到开发板,推荐用NF或TFTP。
运行程序:
# ./fb-getinfo
success open fb dev : /dev/fb0
fb_fix.id="mxs-lcdif"
fb_fix.smem_start=0x98100000
fb_fix.mem_len=33554432
fb_fix.type=0
fb_fix.type_aux=0
fb_fix.visual=2
fb_fix.xpanstep=(null)
fb_fix.ypanstep=1
fb_fix.ywrapstep=1
fb_fix.line_length=2048
fb_fix.mmio_start=0
fb_fix.mmio_len=0
fb_fix.accel=0
fb_fix.reserved[0]=0
fb_fix.reserved[1]=0
fb_fix.reserved[2]=0
fb_var.xres=1024
fb_var.yres=600
fb_var.xres_virtual=1024
fb_var.yres_virtual=600
fb_var.xoffset=0
fb_var.yoffset=0
fb_var.bits_per_pixel=16
fb_var.grayscale=0
fb_bitfield red, offset = 11, length = 5, right = 0,
fb_bitfield green, offset = 5, length = 6, right = 0,
fb_bitfield blue, offset = 0, length = 5, right = 0,
fb_bitfield transp, offset = 0, length = 0, right = 0,
fb_var.red=0xb
fb_var.green=0x5
fb_var.blue=0
fb_var.transp=0
fb_var.nonstd=0
fb_var.activate=128
fb_var.height=0
fb_var.width=0
fb_var.accel_flags=0
fb_var.pixclock=19607
fb_var.left_margin=140
fb_var.right_margin=160
fb_var.upper_margin=20
fb_var.lower_margin=12
fb_var.hsync_len=20
fb_var.vsync_len=3
fb_var.sync=1073741824
fb_var.vmode=0
fb_var.rotate=0
fb_var.reserved[0]=0
fb_var.reserved[1]=0
fb_var.reserved[2]=0
fb_var.reserved[3]=0
#可以看到,屏用的是RGB565格式,而显存居然达32M,NXP的驱动工程师很任性~
最近编辑记录 Jmhh247 (2020-03-17 23:08:25)
离线
#### 显示照片代码测试OK
0320
---
#### 代码2 实现显示bmp照片
本程序的功能单纯就是bmp文件解码,从bmp图片文件读取RGB数据显示到framebuffer上面。
- 实现源码。
/*
 * @FilePath: /0318/load-bmp-on-fb.c
 * @Version: 1.0.0
 * @Author: zys
 * @LastAuthor   : zys
 * @CreationDate: 2020-03-18 22:58:04
 * @LastEditTime : 2020-03-18 23:07:45
 * @Description :  读取bmp图片显示到framebuffer。
 */
/*----------------------------------------------------------------------------*/
/* change log ----------------------------------------------------------------*/
/*
0318
yz imx6ul test ok.
-build:
    $ARMGCC load-bmp-on-fb.c -o load-bmp-on-fb
-tftp get:    
    cp load-bmp-on-fb /tftpboot/
    tftp -g -r load-bmp-on-fb 192.168.2.119
*/
/*----------------------------------------------------------------------------*/
/* Includes ------------------------------------------------------------------*/
#include <arpa/inet.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/*_attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐  */
//14byte文件头
typedef struct
{
    unsigned char cfType[2];  //文件类型,"BM"(0x4D42)
    unsigned int  cfSize;     //文件大小(字节)
    unsigned int  cfReserved; //保留,值为0
    unsigned int  cfoffBits;  //数据区相对于文件头的偏移量(字节)
} __attribute__((packed)) BITMAPFILEHEADER;
//40byte信息头
typedef struct
{
    unsigned int       ciSize;            //BITMAPFILEHEADER所占的字节数
    unsigned int       ciWidth;           //宽度
    unsigned int       ciHeight;          //高度
    unsigned short int ciPlanes;          //目标设备的位平面数,值为1
    unsigned short int ciBitCount;        //每个像素的位数
    char               ciCompress[4];     //压缩说明
    unsigned int       ciSizeImage;       //用字节表示的图像大小,该数据必须是4的倍数
    unsigned int       ciXPelsPerMeter;   //目标设备的水平像素数/米
    unsigned int       ciYPelsPerMeter;   //目标设备的垂直像素数/米
    unsigned int       ciClrUsed;         //位图使用调色板的颜色数
    unsigned           intciClrImportant; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
} __attribute__((packed)) BITMAPINFOHEADER;
typedef struct
{
    unsigned char blue;
    unsigned char green;
    unsigned char red;
} __attribute__((packed)) PIXEL; //颜色模式RGB
/* Private function prototypes -----------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
BITMAPFILEHEADER FileHead;
BITMAPINFOHEADER InfoHead;
static int       xres           = 0;
static int       yres           = 0;
static int       bits_per_pixel = 0;
static char*     fb_addr        = NULL;
/* Global  variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
int show_bmp(char* picname)
{
    FILE*                 fp;
    int                   rc;
    int                   line_x, line_y;
    long int              location = 0;
    char                  tmp[1024 * 10];
    static unsigned short b, g, r;
    static unsigned short rgb; /*2个字节*/
    PIXEL                 pix;
    int                   total_length = 0;
    int                   width, height;
    int                   flen = 0;
    printf("into show_bmp function\n");
    fp = fopen(picname, "rb");
    if (fp == NULL) {
        printf("open errror\n");
        return (-1);
    }
    /* BITMAPFILEHEADER*/
    rc = fread(&FileHead, sizeof(BITMAPFILEHEADER), 1, fp);
    if (rc != 1) {
        printf("read header error!\n");
        fclose(fp);
        return (-2);
    }
    printf("Read BITMAPFILEHEADER  OK\n");
    //检测是否是bmp图像
    if (memcmp(FileHead.cfType, "BM", 2) != 0) {
        printf("it's not a BMP file\n");
        fclose(fp);
        return (-3);
    }
    /*BITMAPINFOHEADER*/
    rc = fread((char*)&InfoHead, sizeof(BITMAPINFOHEADER), 1, fp);
    if (rc != 1) {
        printf("read infoheader error!\n");
        fclose(fp);
        return (-4);
    }
    printf("Read BITMAPINFOHEADER  OK\n");
    /* 求解文件长度 */
    fseek(fp, 0, SEEK_SET);
    fseek(fp, 0, SEEK_END);
    flen = ftell(fp);
    /* 再移位到文件头部 */
    fseek(fp, 0, SEEK_SET);
    width  = InfoHead.ciWidth;
    height = InfoHead.ciHeight;
    printf("FileHead.cfSize =%d byte\n", FileHead.cfSize);
    printf("flen = %d\n", flen);
    printf("width = %d, height = %d\n", width, height);
    total_length = width * height * 3;
    printf("total_length = %d\n", total_length);
    //跳转的数据区
    fseek(fp, FileHead.cfoffBits, SEEK_SET);
    printf("FileHead.cfoffBits = %d\n", FileHead.cfoffBits);
    printf("InfoHead.ciBitCount = %d\n", InfoHead.ciBitCount);
    for (line_y = 0; line_y < height; line_y++) {
        // printf("read pix rgb...\n");
        for (line_x = 0; line_x < width; line_x++) {
            // printf("read pix rgb...\n");
            // rc = fread(&(pix.blue), 1, 1, fp);
            // rc = fread(&(pix.green), 1, 1, fp);
            // rc = fread(&(pix.red), 1, 1, fp);
            rc = fread(&pix, sizeof(PIXEL), 1, fp);
            // rgb565每像素占2个字节
            location = line_x * 2 + (height - 1 - line_y) * xres * 2;
            r   = ((pix.red >> 3) << 11) & 0xfb80;
            g   = ((pix.green >> 2) << 5) & 0x07e0;
            b   = (pix.blue >> 3) & 0x001f;
            rgb = r | g | b;
            // 写入显存
            *((unsigned short*)(fb_addr + location)) = rgb;
        }
    }
    fclose(fp);
    return 0;
}
int fb_init(void)
{
    int                      fbfd, fb_size;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    fbfd = open("/dev/fb0", O_RDWR | O_CREAT, S_IRWXU);
    if (fbfd < 0) {
        printf("open error\n");
        exit(1);
    }
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information.\n");
        exit(1);
    }
    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
        printf("Error reading variable information.\n");
        exit(1);
    }
    
    printf("R:%d,G:%d,B:%d \n", vinfo.red, vinfo.green, vinfo.blue);
    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
    xres           = vinfo.xres;
    yres           = vinfo.yres;
    bits_per_pixel = vinfo.bits_per_pixel;
    fb_size        = xres * yres * ((bits_per_pixel + 7) / 8);
    printf("fbsize: %d \n", fb_size);
    if ((fb_addr = (char*)mmap(0, fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0)) < 0) {
        printf("fb mmap error\n");
        exit(1);
    }
    close(fbfd);
    printf("fb init ok!\n");
    return fb_size;
}
int main(int argc, char* argv[])
{
    int fb_size;
    fb_size = fb_init();
    printf("show_bmp\n");
    show_bmp(argv[1]);
    munmap(fb_addr, fb_size);
    return 0;
}
/*************************** (C) COPYRIGHT 2020 ZYS ************END OF FILE****/- 测试程序
首于本程序只负责显示,所以拍照需要用到上篇的代码,分两步完成测试。
先拍照保存:
# ./av4l2grap-uvc-savebmp-x /dev/video1再显示出来:
# ./load-bmp-on-fb uvc_grap.bmp完整的运行程序log:
# ls
av4l2grap-uvc-savebmp-x  load-bmp-on-fb
# ./av4l2grap-uvc-savebmp-x /dev/video1
----- v4l2 savebmp app start -----
using:          /dev/video1
driver:         uvcvideo
card:           Aoni HD Camera
bus_info:       usb-ci_hdrc.1-1.4
version:        262415
capabilities:   84200001
/dev/video1:    supports capture.
/dev/video1:    supports streaming.
Support format:
        1.MJPEG
        2.YUV 4:2:2 (YUYV)
fmt.type:               1
pix.pixelformat:        YUYV
pix.height:             480
pix.width:              640
pix.field:              1
set fps OK!
get fps OK:
timeperframe.numerator  : 1
timeperframe.denominator: 25
set fps : 25
Supported format: MJPEG
        Supported size: 640x480
        Supported size: 1280x720
        Supported size: 1184x656
        Supported size: 1024x576
        Supported size: 960x720
        Supported size: 960x540
        Supported size: 864x486
        Supported size: 800x600
        Supported size: 752x423
        Supported size: 640x360
        Supported size: 320x240
Supported format: YUV 4:2:2 (YUYV)
        Supported size with the current format: 640x480
        Supported size with the current format: 1280x720
        Supported size with the current format: 1184x656
        Supported size with the current format: 1024x576
        Supported size with the current format: 960x720
        Supported size with the current format: 960x540
        Supported size with the current format: 864x486
        Supported size with the current format: 800x600
        Supported size with the current format: 752x423
        Supported size with the current format: 640x360
        Supported size with the current format: 320x240
init /dev/video1        [OK]
start capture
buf index: 0
yuyv to rgb888 done
save bmp  function
save ./uvc_grap.bmp done
app exit.
# ls
av4l2grap-uvc-savebmp-x  load-bmp-on-fb           uvc_grap.bmp
# ./load-bmp-on-fb uvc_grap.bmp
R:11,G:5,B:0
1024x600, 16bpp
fbsize: 1228800
fb init ok!
show_bmp
into show_bmp function
Read BITMAPFILEHEADER  OK
Read BITMAPINFOHEADER  OK
FileHead.cfSize =921654 byte
flen = 921654
width = 640, height = 480
total_length = 921600
FileHead.cfoffBits = 54
InfoHead.ciBitCount = 24
#- 显示效果

本篇由两个程序合作,完成了静态照片的显示,下篇会完成动态实时预览~
离线