前言
了解 framebuffer 字符设备
了解 bmp图片格式
通过操作 /dev/fb0 字符设备来实现在 RGB LCD 屏幕上显示 bmp 图片。
一、如何在 linux 下驱动 LCD
显示设备例如 LCD,在 Linux 中用 Framebuffer 来表征, Framebuffer 翻译过来就是帧缓冲,简称 fb,在 /dev 目录下显示设备一般表示成这样:/dev/fbn,应用程序通过访问这个设备来访问 LCD,实际上应用程序通过操作显存来操作显示设备,显存由驱动程序设置。说白了,我们要在 linux 下操作屏幕进行显示那么直接对 /dev/fbn 进行操作即可。
1、什么是 Framebuffer 设备
Framebuffer 是用一个视频输出设备从包含完整的帧数据的一个内存缓冲区中来驱动一个视频显示设备。也就是说 Framebuffer 是一块内存保存着一帧的图像,向这块内存写入数据就相当于向屏幕中写入数据,如果使用 32 位的数据来表示一个像素点(使用 BBP 表示),假设屏幕的显示频分辨率为 1920x1080,那么 Framebuffer 所需要的内存为 1920x1080x32/8=8,294,400 字节约等于 7.9M。简单来说 Framebuffer 把屏幕上的每个点映射成一段线性内存空间,程序可以简单的改变这段内存的值来改变屏幕上某一点的颜色。Framebuffer 子系统为用户空间操作显示设备提供了统一的接口,屏蔽了底层硬件之间的差异,用户只需要操作一块内存缓冲区即可把需要的图像显示到 LCD 设备上。
2、如何确保 Framebuffer 设备已存在
官方的 Linux 内核默认已经开启了 LCD 驱动。
因此在系统下我们是可以看到 /dev/fb0 这样一个设备,如图:
/dev/fb0 就是 LCD 对应的设备文件,/dev/fb0 是个字符设备,因此肯定有file_operations 操作集,fb 的 file_operations 操作集定义在 drivers/video/fbdev/core/fbmem.c 文件中,如下所示:
static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, .unlocked_ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif .mmap = fb_mmap, .open = fb_open, .release = fb_release, #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ !defined(CONFIG_MMU)) .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif .llseek = default_llseek, };
3、Frame_buffer 设备结构体
<1>、fb_info 详解
Linux 中使用 fb_info 结构体来表示 Framebuffer 设备,其内容如下:
struct fb_info { atomic_t count; int node; int flags; struct mutex lock; /* 互斥锁 */ struct mutex mm_lock; /* 互斥锁,用于 fb_mmap 和 smem_*域*/ struct fb_var_screeninfo var; /* 当前可变参数 */ struct fb_fix_screeninfo fix; /* 当前固定参数 */ struct fb_monspecs monspecs; /* 当前显示器特性 */ struct work_struct queue; /* 帧缓冲事件队列 */ struct fb_pixmap pixmap; /* 图像硬件映射 */ struct fb_pixmap sprite; /* 光标硬件映射 */ struct fb_cmap cmap; /* 当前调色板 */ struct list_head modelist; /* 当前模式列表 */ struct fb_videomode *mode; /* 当前视频模式 */ #ifdef CONFIG_FB_BACKLIGHT /* 如果 LCD 支持背光的话 */ /* assigned backlight device */ /* set before framebuffer registration, remove after unregister */ struct backlight_device *bl_dev; /* 背光设备 */ /* Backlight level curve */ struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; #endif ...... struct fb_ops *fbops; /* 帧缓冲操作函数集 */ struct device *device; /* 父设备 */ struct device *dev; /* 当前 fb 设备 */ int class_flag; /* 私有 sysfs 标志 */ ...... char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */ unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */ void *pseudo_palette; /* 伪 16 位调色板 */ ...... };
其中最重要成员有:var,fix,fbops(LCD设备的操作集合),screen_base(显存基地址),screen_size(显存大小),pseudo_palette。
struct fb_fix_screeninfo 和 struct fb_var_screeninfo 都定义在系统的 /usr/include/linux/fb.h 文件内。下面分别结构体定义、结构体字段说明两部分来详细解释这两个结构体。
<2>、struct fb_fix_screeninfo 详解
struct fb_fix_screeninfo 主要用于获取 FrameBuffer 的固定信息, 这些信息无法在应用层被更改,只能通过 ioctl 函数使用 FBIOGET_FSCREENINFO 去获取。
①、struct fb_fix_screeninfo 定义:
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_fix_screeninfo 字段说明:
表格中提到的宏如 FB_TYPE_PACKED_PIXELS、FB_VISUAL_TRUECOLOR、FB_ACCEL_NONE 请查看 /usr/include/linux/fb.h 的相关定义。
字段名称 | 描述 | 附加说明 |
id | 设备驱动名称 | |
smem_start | 显存起始物理地址 | |
smem_len | 显存大小 | |
type | 显卡类型 | 一般为 FB_TYPE_PACKED_PIXELS(值为0,表示像素值紧密排 列),查看fb.h的 FB_TYPE_* |
type_aux | 附加类型 | 查看fb.h的 FB_AUX_TEXT_MDA |
visual | 色彩模式 | 一般为 FB_VISUAL_TRUECOLOR(值为2,真彩色) |
xpanstep | 支持水平方向上的 PAN 显示: 0:不支持 非 0:支持,此时该值用于表示在水平方向上每步进的像素值 |
默认为1 |
ypanstep | 支持垂直方向上的 PAN 显示: 0:不支持。 非 0:支持,此时该值用于表示在垂直方向上每步进的像素值。 |
默认为 1 |
ywrapstep | 该方式类似于 ypanstep,不同之处在于:当其显示到底部时,能回到显存的开始处进行显示。 | 默认为 0 |
line_length | 每行字节数 | |
mmio_start | 显存映射 I/O 首地址 | 默认为不支持 |
mmio_len | 显存映射 I/O 长度 | 默认为不支持 |
accel | 显示所支持的硬件加速设备 | 默认为 FB_ACCEL_NONE |
<3>、struct fb_var_screeninfo 详解
struct fb_var_screeninfo 主要用于获取和设置 FrameBuffer 的可变屏幕信息,包括分辨率、像素位深、像素格式等。这些信息可以通过ioctl函数使用 FBIOGET_VSCREENINFO 获取,也可以通过 FBIOPUT_VSCREENINFO 修改。这些参数都是可以在设备树中进行修改的,具体的参数是多少要根据 LCD 的数据手册来进行修改,LCD 的驱动程序会从设备树中读取这些参数。
①、struct fb_var_screeninfo 定义:
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 reserved[5]; /* Reserved for future compatibility */ };
②、struct fb_var_screeninfo 字段说明:
表格中提到的宏和结构体请查看 /usr/include/linux/fb.h 的相关定义。
字段名称 | 描述 | 其他 |
xres | 可见屏幕宽度(每行像素数) | 分辨率 |
yres | 可见屏幕高度(每列像素数) | 分辨率 |
xres_virtual | 虚拟屏幕宽度(显存中图像宽度) | 每行像素数,一般会设置其与xres相等 |
yres_virtual | 虚拟屏幕高度(显存中图像高度),每像素数 | 每列像素数,一般会设置其与yres相等虚拟屏幕 |
xoffset | 虚拟到可见(实际)之间的行方向偏移 | |
yoffset | 虚拟到可见(实际)之间的列方向偏移 | |
bits_per_pixel | 每个像素有多少bit | 这个值除以8,表示每个像素的字节数 |
grayscale | 灰度级 | 默认为 0 |
red、green、blue、transp | 颜色分量中红色、绿色、蓝色、透明度的位域信息 | struct fb_bitfield{ __u32 offset;//颜色分量起始比特位 __u32 length;//颜色分量所占比特长度。 __u32 msb_right;//右边的比特是否为最高有效位 |
nonstd | 是否为标准像素格式 | |
activate | 设置生效的时刻 | 默认为 FB_ACTIVATE_NOW |
height、width | 屏幕高、宽,单位为 mm | 默认为不支持(-1) |
accel_flags | 加速标志 | 默认不支持,查看fb_info.flags |
pixclock | 显示一个点需要的时间,单位为ns | |
left_margin、right_margin、hsync_len | 分别是左消隐信号、右消隐信号、水平同步时长,这三个值之和等于水平回扫时间,单位为点时钟 | |
upper_margin、lower_margin、vsync_len | 分别是上消隐信号、下消隐信号、垂直同步时长,这三个值之和等于垂直回扫时间,单位为点时钟 | |
sync | 同步信号方式 | 查看宏 FB_SYNC_* |
vmode | vmode | 查看宏 FB_VMODE_* |
rotate | 顺时针旋转的角度 |
pixclock | 显示一个点需要的时间,单位为ns |
4、设备树中有关 framebuffer 的信息
sun8i-v3s.dtsi
二、bmp 图片格式
1、bmp 文件组成部分
一个 bmp 文件由四部分组成:
2、代码表现形式
对应的代码表现形式如下:
//BMP文件头(14字节) typedef struct { char cfType[2]; //文件类型,"BM"(0x4D42) int cfSize; //文件大小(字节) int cfReserved; //保留,值为0 int cfoffBits; //数据区相对于文件头的偏移量(字节) }__attribute__((packed)) BITMAPFILEHEADER; //__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐 //位图信息头(40字节) typedef struct { char ciSize[4]; //BITMAPFILEHEADER所占的字节数 int ciWidth; //宽度 int ciHeight; //高度 char ciPlanes[2]; //目标设备的位平面数,值为1 int ciBitCount; //每个像素的位数 char ciCompress[4]; //压缩说明 char ciSizeImage[4]; //用字节表示的图像大小,该数据必须是4的倍数 char ciXPelsPerMeter[4]; //目标设备的水平像素数/米 char ciYPelsPerMeter[4]; //目标设备的垂直像素数/米 char ciClrUsed[4]; //位图使用调色板的颜色数 char ciClrImportant[4]; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要 }__attribute__((packed)) BITMAPINFOHEADER;
3、bmp 图片的编码方式(存储格式)
54头字节,每个像素占3字节BGR顺序,上下颠倒存储,bmp图片的宽度占用的字节数如果不能被4整除,window系统会给每一行填充垃圾数凑够4字节整除。
图片的编码方式(存储格式)如下图:
bmp 图片的编码方式(存储格式)
4、bmp 图片数据与 LCD 屏幕对应的方式
bmp 图片数据与 LCD 屏幕对应的方式
bmp 图片数据与 LCD 屏幕对应的方式
由上述可以看到 bmp 图片数据和 LCD 屏幕所对应的关系是 第1行 -> 倒数第1行、第2行 -> 倒数第2行,以此类推
三、操作步骤
在应用层中,用户可以将 fb 设备看成是显存的一个映像,将其映射到进程空间后,就可以直接进行读写操作,写操作会直接反映在屏幕上。
在应用程序中,操作 /dev/fbn 的一般步骤如下:
打开 /dev/fbn 设备文件;
用 ioctl() 操作取得当前显示屏幕的参数,如屏幕分辨率、每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小;
用 mmap() 函数,将屏幕缓冲区映射到用户空间;
映射后就可以直接读/写屏幕缓冲区,进行绘图和图片显示;
使用完帧缓冲设备后需要将其释放;
关闭文件。
四、程序源码
以下为在 800 * 480 分辨率的 RGB LCD 屏幕上显示图片的 c 程序及 Makefile 文件
程序有读取 LCD 屏幕相关信息以及校验是否为 bmp 图片,具有良好的容错处理
imageshow.c
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <linux/fb.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <arpa/inet.h> #include <errno.h> //14byte文件头 typedef struct { char cfType[2]; //文件类型,"BM"(0x4D42) int cfSize; //文件大小(字节) int cfReserved; //保留,值为0 int cfoffBits; //数据区相对于文件头的偏移量(字节) }__attribute__((packed)) BITMAPFILEHEADER; //__attribute__((packed))的作用是告诉编译器取消结构在编译过程中的优化对齐 //40byte信息头 typedef struct { char ciSize[4]; //BITMAPFILEHEADER所占的字节数 int ciWidth; //宽度 int ciHeight; //高度 char ciPlanes[2]; //目标设备的位平面数,值为1 int ciBitCount; //每个像素的位数 char ciCompress[4]; //压缩说明 char ciSizeImage[4]; //用字节表示的图像大小,该数据必须是4的倍数 char ciXPelsPerMeter[4]; //目标设备的水平像素数/米 char ciYPelsPerMeter[4]; //目标设备的垂直像素数/米 char ciClrUsed[4]; //位图使用调色板的颜色数 char ciClrImportant[4]; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要 }__attribute__((packed)) BITMAPINFOHEADER; typedef struct { unsigned char blue; unsigned char green; unsigned char red; unsigned char reserved; }__attribute__((packed)) PIXEL; //颜色模式RGB typedef struct { int fbfd; int *fbp; unsigned int xres; unsigned int yres; unsigned int xres_virtual; unsigned int yres_virtual; unsigned int xoffset; unsigned int yoffset; unsigned int bpp; unsigned long line_length; unsigned long size; struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; } FB_INFO; typedef struct { unsigned int width; unsigned int height; unsigned int bpp; unsigned long size; unsigned int data_offset; } IMG_INFO; FB_INFO fb_info; IMG_INFO img_info; int show_bmp(char *bmpname, int x, int y) { int i, j, k = 0; int w, h; // 图片的宽和高 int headbuf[2] = {0}; //headbuf[0] 宽 headbuf[1] 高 // 定义数组存放转换得到的ARGB数据 int lcdbuf[fb_info.xres * fb_info.yres]; int bmpfd; int ret = 0; BITMAPFILEHEADER FileHead; BITMAPINFOHEADER InfoHead; if(bmpname == NULL) { printf("img_name is null\n"); return -1; } // 打开要显示的bmp图片 bmpfd = open(bmpname, O_RDWR); if(bmpfd == -1) { perror("打开bmp失败!\n"); close(bmpfd); return -1; } /* 移位到文件头部 */ lseek(bmpfd, 0, SEEK_SET); ret = read(bmpfd, &FileHead, sizeof(BITMAPFILEHEADER)); if ( ret < 0) { printf("img read failed\n"); close(bmpfd); return ret; } //检测是否是bmp图像 if (memcmp(FileHead.cfType, "BM", 2) != 0) { printf("it's not a BMP file[%c%c]\n", FileHead.cfType[0], FileHead.cfType[1]); close(bmpfd); return ret; } ret = read(bmpfd, (char *)&InfoHead, sizeof(BITMAPFILEHEADER)); if ( ret < 0) { printf("read infoheader error!\n"); close(bmpfd); return ret; } img_info.width = InfoHead.ciWidth; img_info.height = InfoHead.ciHeight; img_info.bpp = InfoHead.ciBitCount; img_info.size = FileHead.cfSize; img_info.data_offset = FileHead.cfoffBits; printf("img info w[%d] h[%d] bpp[%d] size[%ld] offset[%d]\n", img_info.width, img_info.height, img_info.bpp, img_info.size, img_info.data_offset); lseek(bmpfd, 18, SEEK_SET); //读头信息 read(bmpfd, headbuf, 8); w = headbuf[0]; h = headbuf[1]; // 判断图像是否在合理范围内 if(x + w > fb_info.xres || y + h > fb_info.yres ) { perror("图片超出范围!\n"); return -1; } //定义数组存放读取到的颜色值 char bmpbuf[w * h * 3]; //BGR数据 //定义一个临时数组 int tempbuf[w * h]; //跳过54字节的头信息,从55开始读取 lseek(bmpfd, 28, SEEK_CUR); if((3 * w) % 4 == 0){ read(bmpfd, bmpbuf, w * h * 3); //从55字节开始读取 } else { for(int k = 0; k < h; k++){ read(bmpfd, &bmpbuf[k * w * 3], w * 3); lseek(bmpfd, 4 - (3 * w) % 4, SEEK_CUR ); } } for(i = 0; i < w * h; i++) //保证每个像素点都能转换 lcdbuf[i] = 0x00 << 24 | bmpbuf[3 * i + 2] << 16 | bmpbuf[3 * i + 1] << 8 | bmpbuf[3 * i]; //把颠倒的图片翻转过来 for(i = 0; i < w; i++) for(int j = 0; j < h; j++) tempbuf[(h - j) * w + i] = lcdbuf[j * w + i]; //确定坐标 for(i = 0; i < h; i++) for(int j = 0; j < w; j++) { *(fb_info.fbp + fb_info.xres * (i + y) + j + x) = tempbuf[k]; k++; } return ret; } int show_picture(char *img_name, int xpos, int ypos) { struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; long screen_size = 0; int rgb_type = 0; if (fb_info.fbfd <= -1) { printf("fb open fialed\n"); return -1; } /*******************第2步:获取设备的硬件信息********************/ if (ioctl(fb_info.fbfd, FBIOGET_FSCREENINFO, &finfo)) { printf("fb ioctl fialed\n"); return -1; } if (ioctl(fb_info.fbfd, FBIOGET_VSCREENINFO, &vinfo)) { printf("fb ioctl fialed\n"); return -1; } fb_info.xres = vinfo.xres; fb_info.yres = vinfo.yres; fb_info.xres_virtual = vinfo.xres_virtual; fb_info.yres_virtual = vinfo.yres_virtual; fb_info.xoffset = vinfo.xoffset; fb_info.yoffset = vinfo.yoffset; fb_info.bpp = vinfo.bits_per_pixel; fb_info.line_length = finfo.line_length; fb_info.size = finfo.smem_len; memcpy(&fb_info.red, &vinfo.red, sizeof(struct fb_bitfield)); memcpy(&fb_info.green, &vinfo.green, sizeof(struct fb_bitfield)); memcpy(&fb_info.blue, &vinfo.blue, sizeof(struct fb_bitfield)); printf("fb info x[%d] y[%d] x_v[%d] y_v[%d] xoffset[%d] yoffset[%d] bpp[%d] line_length[%ld] size[%ld]\n", fb_info.xres, fb_info.yres, fb_info.xres_virtual, fb_info.yres_virtual, fb_info.xoffset, fb_info.yoffset, fb_info.bpp, fb_info.line_length, fb_info.size); printf("fb info red off[%d] len[%d] msb[%d]\n", fb_info.red.offset, fb_info.red.length, fb_info.red.msb_right); printf("fb info green off[%d] len[%d] msb[%d]\n", fb_info.green.offset, fb_info.green.length, fb_info.green.msb_right); printf("fb info blue off[%d] len[%d] msb[%d]\n", fb_info.blue.offset, fb_info.blue.length, fb_info.blue.msb_right); if (fb_info.bpp != 16 && fb_info.bpp != 24 && fb_info.bpp != 32) { printf("fb bpp is not 16,24 or 32\n"); return -1; } if (fb_info.red.length > 8 || fb_info.green.length > 8 || fb_info.blue.length > 8) { printf("fb red|green|blue length is invalid\n"); return -1; } /*************************第3步:进行mmap***********************/ // 内存映射 fb_info.fbp = mmap(0, fb_info.size, PROT_READ | PROT_WRITE, MAP_SHARED, fb_info.fbfd, 0); //fb_info.fbp = mmap(NULL, 800 * 480 * 4, PROT_READ |PROT_WRITE,MAP_SHARED, lcdfd, 0); if (fb_info.fbp == (int *)-1) { printf("mmap fialed\n"); return -1; } /* 获取RGB的颜色颜色格式,比如RGB8888、RGB656 */ rgb_type = vinfo.bits_per_pixel / 8; /* 屏幕的像素点 */ screen_size = vinfo.xres * vinfo.yres * rgb_type; printf("vinfo.bits_per_pixel = %d\n", vinfo.bits_per_pixel); printf("rgb_type = %d\n", rgb_type); printf("screen_size = %d\n", screen_size); /********************第4步:进行lcd相关的操作********************/ /* 刷黑屏幕 */ memset(fb_info.fbp, 0xff, screen_size); show_bmp(img_name, xpos, ypos); /***********************第五步:释放显存************************/ //删除映射 munmap(fb_info.fbp, fb_info.size); return 0; } int main(int argc, char **argv) { char img_name[64]; if (argc != 4) { printf("please input 4 args:\n"); printf("./imageshow xx.bmp xpos ypos\n"); return 0; } int x = atoi(argv[2]); // atoi()将字符型转换成整形 int y = atoi(argv[3]); snprintf(img_name, sizeof(img_name), "%s", argv[1]); printf("img_name = %s\n", img_name); /************************第1步:打开设备**************************/ fb_info.fbfd = open("/dev/fb0", O_RDWR); if (!fb_info.fbfd) { printf("Error: cannot open framebuffer device(/dev/fb0).\n"); return -1; } show_picture(img_name, x, y); /***********************第六步:关闭文件************************/ close(fb_info.fbfd); return 0; }
Makefile
src = $(wildcard *.c) obj = $(patsubst %.c, %.o, $(src)) CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld ALL:imageshow imageshow.o : imageshow.c $(CROSS_COMPILE)arm-linux-gnueabihf-gcc -c $< -o $@ clean: -rm -rf $(obj) imageshow
五、编译
make 后将 testshow 文件以及四张 bmp 图片通过 tftp 命令上传到板子上
其中 1.bmp 和 2.bmp 分辨率为 800 * 480,3.bmp 和 4.bmp 分辨率为 480 *272
六、效果展示
1、风景照(800 * 480,从(0,0)开始显示)
./testshow 1.bmp 0 0
2、马里奥(800 * 480,从(0,0)开始显示)
./testshow 2.bmp 0 0
2、宇航员(480* 272,从(0,0)开始显示)
./testshow 3.bmp 0 0
2、女孩和白狐(480* 272,从(100,100)开始显示)
./testshow 4.bmp 100 100
七、资源自取
方式1:github链接
https://github.com/Gnepuil79/licheepi.git
方式2:百度网盘
链接:https://pan.baidu.com/s/1h9R32EOcHR_-ZXEkfXrtGg
提取码:r3jj