New file |
| | |
| | | /******************************************************************************** |
| | | * 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; |
| | | } |
| | | |