/********************************************************************************
|
* Copyright: (C) 2021 Guo Wenxue<Email:guowenxue@gmail.com QQ:281143292>
|
* All rights reserved.
|
*
|
* Filename: fbtest.c
|
* Description: This program used to:
|
* 1, Show framebuffer device information
|
* 2, Show RGB color on LCD full screen
|
* 3, Show BMP file on LCD;
|
*
|
* Version: 1.0.0(08/26/22)
|
* Author: Guo Wenxue <wenxue.guo@avnet.com>
|
* ChangeLog: 1, Release initial version on "08/26/22 12:56:31"
|
*
|
********************************************************************************/
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdint.h>
|
#include <string.h>
|
#include <errno.h>
|
#include <unistd.h>
|
#include <fcntl.h>
|
#include <getopt.h>
|
#include <libgen.h>
|
#include <sys/ioctl.h>
|
#include <sys/stat.h>
|
#include <sys/types.h>
|
#include <linux/fb.h>
|
#include <sys/mman.h>
|
|
#define PROG_VERSION "1.0.0"
|
|
enum
|
{
|
CMD_SHOW_INFO, /* show LCD info */
|
CMD_SHOW_RGB, /* show RGB color */
|
CMD_SHOW_BMP, /* show BMP file */
|
};
|
|
typedef struct fb_ctx_s
|
{
|
int fd; /* framebuffer file descriptor */
|
char dev[64]; /* framebuffer device name */
|
long fb_size; /* framebuffer size */
|
int pix_size; /* lcd screen pix size */
|
struct fb_var_screeninfo vinfo; /* framebuffer var information */
|
char *fbp; /* framebuffer mmap() address */
|
} fb_ctx_t;
|
|
|
int fb_init(fb_ctx_t *fb_ctx);
|
int fb_term(fb_ctx_t *fb_ctx);
|
int show_rgb(fb_ctx_t *fb_ctx, int times);
|
int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file);
|
|
static void program_usage(char *progname)
|
{
|
printf("Usage: %s [OPTION]...\n", progname);
|
printf(" %s is a program to show RGB color or BMP file on LCD screen\n", progname);
|
|
printf("\nMandatory arguments to long options are mandatory for short options too:\n");
|
printf(" -d[device ] Specify framebuffer device, such as: /dev/fb0\n");
|
printf(" -c[color ] Display RGB corlor on LCD screen for some times, such as: -c 3\n");
|
printf(" -b[bmp ] Display BMP file, such as: -b bg.bmp\n");
|
printf(" -h[help ] Display this help information\n");
|
printf(" -v[version ] Display the program version\n");
|
|
printf("\n%s version %s\n", progname, PROG_VERSION);
|
return;
|
}
|
|
int main (int argc, char **argv)
|
{
|
fb_ctx_t fb_ctx;
|
char *progname=NULL;
|
char *bmp_file=NULL;
|
char *fb_dev="/dev/fb0";
|
int cmd = CMD_SHOW_INFO;
|
int opt, times;
|
|
struct option long_options[] = {
|
{"device", required_argument, NULL, 'd'},
|
{"color", required_argument, NULL, 'c'},
|
{"bmp", required_argument, NULL, 'b'},
|
{"version", no_argument, NULL, 'v'},
|
{"help", no_argument, NULL, 'h'},
|
{NULL, 0, NULL, 0}
|
};
|
|
progname = (char *)basename(argv[0]);
|
|
/* Parser the command line parameters */
|
while ((opt = getopt_long(argc, argv, "d:c:b:vh", long_options, NULL)) != -1)
|
{
|
switch (opt)
|
{
|
case 'd': /* Set framebuffer device */
|
fb_dev = optarg;
|
break;
|
|
case 'b': /* Set BMP file */
|
bmp_file = optarg;
|
cmd = CMD_SHOW_BMP;
|
break;
|
|
case 'c': /* Show RGB color */
|
times = atoi(optarg);
|
cmd = CMD_SHOW_RGB;
|
break;
|
|
case 'v': /* Get software version */
|
printf("%s version %s\n", progname, PROG_VERSION);
|
return 0;
|
|
case 'h': /* Get help information */
|
program_usage(progname);
|
return 0;
|
|
default:
|
break;
|
}
|
|
}
|
|
memset(&fb_ctx, 0, sizeof(fb_ctx));
|
strncpy(fb_ctx.dev, fb_dev, sizeof(fb_ctx.dev));
|
|
if( fb_init(&fb_ctx) < 0 )
|
{
|
printf("ERROR: Initial framebuffer device '%s' failure.\n", fb_ctx.dev);
|
return 1;
|
}
|
|
switch( cmd )
|
{
|
case CMD_SHOW_RGB:
|
show_rgb(&fb_ctx, times);
|
break;
|
|
case CMD_SHOW_BMP:
|
show_bmp(&fb_ctx, bmp_file);
|
break;
|
|
default:
|
break;
|
}
|
|
fb_term(&fb_ctx);
|
|
return 0;
|
}
|
|
|
int fb_get_var_screeninfo(int fd, struct fb_var_screeninfo *vinfo)
|
{
|
if( fd<0 || !vinfo )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
if(ioctl(fd, FBIOGET_VSCREENINFO, vinfo))
|
{
|
printf("ERROR: ioctl() get variable info failure: %s\n", strerror(errno));
|
return -2;
|
}
|
|
printf("LCD information : %dx%d, bpp:%d rgba:%d/%d,%d/%d,%d/%d,%d/%d\n", vinfo->xres, vinfo->yres, vinfo->bits_per_pixel,
|
vinfo->red.length, vinfo->red.offset, vinfo->green.length, vinfo->green.offset,
|
vinfo->blue.length,vinfo->blue.offset, vinfo->transp.length, vinfo->transp.offset);
|
|
return 0;
|
}
|
|
int fb_init(fb_ctx_t *fb_ctx)
|
{
|
struct fb_var_screeninfo *vinfo;
|
|
if( !fb_ctx || !strlen(fb_ctx->dev) )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
if( (fb_ctx->fd=open(fb_ctx->dev, O_RDWR)) < 0 )
|
{
|
printf("ERROR: Open framebuffer device '%s' failure: %s\n", fb_ctx->dev, strerror(errno));
|
return -2;
|
}
|
|
fb_get_var_screeninfo(fb_ctx->fd, &fb_ctx->vinfo);
|
|
vinfo = &fb_ctx->vinfo;
|
fb_ctx->fb_size = vinfo->xres * vinfo->yres * vinfo->bits_per_pixel / 8;
|
fb_ctx->pix_size = vinfo->xres * vinfo->yres;
|
|
fb_ctx->fbp =(char *)mmap(0, fb_ctx->fb_size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_ctx->fd ,0);
|
if ( !fb_ctx->fbp )
|
{
|
printf ("ERROR: Framebuffer mmap() failure: %s\n", strerror(errno));
|
return -2;
|
}
|
|
vinfo->xoffset = 0;
|
vinfo->yoffset = 0;
|
|
return 0;
|
}
|
|
int fb_term(fb_ctx_t *fb_ctx)
|
{
|
if( !fb_ctx )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
munmap(fb_ctx->fbp, fb_ctx->fb_size);
|
close (fb_ctx->fd);
|
|
return 0;
|
}
|
|
int show_rgb_screen(fb_ctx_t *fb_ctx)
|
{
|
struct fb_var_screeninfo *vinfo;
|
char *fb_addr;
|
int interval = 1;
|
int i, color;
|
int bpp_bytes;
|
|
if( !fb_ctx )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
vinfo = &fb_ctx->vinfo;
|
bpp_bytes = vinfo->bits_per_pixel / 8;
|
|
printf("show full screen on red\n");
|
color = (1<<vinfo->red.length-1) << vinfo->red.offset;
|
fb_addr = fb_ctx->fbp;
|
for(i=0; i<fb_ctx->pix_size; i++)
|
{
|
memcpy(fb_addr, &color, bpp_bytes);
|
fb_addr += bpp_bytes;
|
}
|
sleep(interval);
|
|
printf("show full screen on green\n");
|
color = (1<<vinfo->green.length-1) << vinfo->green.offset;
|
fb_addr = fb_ctx->fbp;
|
for(i=0; i<fb_ctx->pix_size; i++)
|
{
|
memcpy(fb_addr, &color, bpp_bytes);
|
fb_addr += bpp_bytes;
|
}
|
sleep(interval);
|
|
printf("show full screen on blue\n");
|
color = (1<<vinfo->blue.length-1) << vinfo->blue.offset;
|
fb_addr = fb_ctx->fbp;
|
for(i=0; i<fb_ctx->pix_size; i++)
|
{
|
memcpy(fb_addr, &color, bpp_bytes);
|
fb_addr += bpp_bytes;
|
}
|
sleep(interval);
|
|
return 0;
|
}
|
|
int show_rgb(fb_ctx_t *fb_ctx, int times)
|
{
|
int i;
|
|
if( !fb_ctx )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
for(i=0; i<times; i++)
|
{
|
show_rgb_screen(fb_ctx);
|
}
|
|
return 0;
|
}
|
|
|
/* BMP format: https://blog.csdn.net/weixin_39756273/article/details/110585675 */
|
typedef struct bmp_file_head_s
|
{
|
uint8_t bfType[2]; /* BMP file type: "BM" */
|
int32_t bfSize; /* BMP file size */
|
int32_t bfReserved; /* reserved */
|
int32_t bfoffBits; /* image data offset */
|
}__attribute__((packed)) bmp_file_head_t;
|
|
|
typedef struct bmp_bitmap_info_s
|
{
|
int32_t biSize; /* this struture size */
|
int32_t biWidth; /* image width in pix */
|
int32_t biHeight; /* image height in pix */
|
uint16_t biPlanes; /* display planes, always be 1 */
|
uint16_t biBitCount; /* bpp: 1,4,8,16,24,32 */
|
int32_t biCompress; /* compress type */
|
int32_t biSizeImage; /* image size in byte */
|
int32_t biXPelsPerMeter; /* x-res in pix/m */
|
int32_t biYPelsPerMeter; /* y-res in pix/m */
|
int32_t biClrUsed;
|
int32_t biClrImportant;
|
}__attribute__((packed)) bmp_bitmap_info_t;
|
|
/*
|
* BMP image file format:
|
* +----------------+------------------+--------------------+---------------+
|
* | File Head(14B) | Bitmap Info(40B) | color palette(OPT) | bitmap data |
|
* +----------------+------------------+--------------------+---------------+
|
*/
|
int show_bmp(fb_ctx_t *fb_ctx, char *bmp_file)
|
{
|
struct fb_var_screeninfo *vinfo;
|
char *fb_addr;
|
bmp_file_head_t *file_head;
|
bmp_bitmap_info_t *bitmap_info;
|
struct stat statbuf;
|
char *f_addr;
|
char *d_addr;
|
char *p_addr;
|
int fd;
|
int bpp_bytes;
|
int i, j;
|
int width, height;
|
|
if( !fb_ctx || !bmp_file )
|
{
|
printf("ERROR: Invalid input arguments\n");
|
return -1;
|
}
|
|
if( (fd=open(bmp_file, O_RDONLY)) < 0 )
|
{
|
printf("ERROR: Open file '%s' failure: %s\n", bmp_file, strerror(errno));
|
return -2;
|
}
|
|
fstat(fd, &statbuf);
|
f_addr =(char *)mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fd ,0);
|
if ( !f_addr )
|
{
|
printf ("ERROR: BMP file mmap() failure: %s\n", strerror(errno));
|
return -2;
|
}
|
|
/* BMP file header */
|
file_head = (bmp_file_head_t *)f_addr;
|
if( memcmp(file_head->bfType, "BM", 2) != 0)
|
{
|
printf("ERROR: It's not a BMP file\n");
|
return -3;
|
}
|
|
/* BMP bitmap information header */
|
bitmap_info = (bmp_bitmap_info_t *)(f_addr+sizeof(bmp_file_head_t));
|
|
/* BMP bitmap data */
|
d_addr = f_addr + file_head->bfoffBits;
|
|
vinfo = &fb_ctx->vinfo;
|
fb_addr = fb_ctx->fbp;
|
bpp_bytes = vinfo->bits_per_pixel / 8;
|
|
width = bitmap_info->biWidth>vinfo->xres ? vinfo->xres : bitmap_info->biWidth;
|
height = bitmap_info->biHeight>vinfo->yres ? vinfo->yres : bitmap_info->biHeight;
|
|
printf("BMP file '%s': %dx%d and display %dx%d\n", bmp_file, bitmap_info->biWidth, bitmap_info->biHeight, width, height);
|
|
/* if biHeight is positive, the bitmap is a bottom-up DIB */
|
for(i=0; i<height; i++)
|
{
|
p_addr=d_addr+(height-1-i)*bitmap_info->biWidth*bpp_bytes;
|
memcpy(fb_addr, p_addr, bpp_bytes*width);
|
fb_addr += bpp_bytes * vinfo->xres;
|
}
|
|
munmap(f_addr, statbuf.st_size);
|
close (fd);
|
|
return 0;
|
}
|