关键字: 正点原子,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
#
- 显示效果
本篇由两个程序合作,完成了静态照片的显示,下篇会完成动态实时预览~
离线