荔枝派Zero(全志V3S)驱动开发之RGB LCD屏幕显示bmp图片

news2024/11/23 11:22:09

文章目录

  • 前言
  • 一、如何在 linux 下驱动 LCD
    • 1、什么是 Framebuffer 设备
    • 2、如何确保 Framebuffer 设备已存在
    • 3、Frame_buffer 设备结构体
      • <1>、fb_info 详解
      • <2>、struct fb_fix_screeninfo 详解
      • <3>、struct fb_var_screeninfo 详解
    • 4、设备树中有关 framebuffer 的信息
  • 二、bmp 图片格式
    • 1、bmp 文件组成部分
    • 2、代码表现形式
    • 3、bmp 图片的编码方式(存储格式)
    • 4、bmp 图片数据与 LCD 屏幕对应的方式
  • 三、操作步骤
  • 四、程序源码
  • 五、编译
  • 六、效果展示
  • 七、资源自取


前言

  • 了解 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_*
vmodevmode查看宏 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


我的qq:2442391036,欢迎交流!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/507401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 Appium 进行 WPF 自动化

文章目录 关于1 环境准备2 集成单元测试3 新增基本测试代码4 测试 WPF 程序5 启动测试 关于 参考链接&#xff1a;Get Your WPF Apps Automated With Appium Appium官网&#xff1a;http://appium.io/docs/en/2.0/quickstart 1 环境准备 一、下载 Windows Application Driv…

JVM 虚拟机栈

虚拟机栈概述 背景: 由于跨平台性的设计&#xff0c;Java 的指令都是根据栈来设计的。不同平台 CPU 架构不同&#xff0c;所以不能设计为基于寄存器的优点是跨平台, 指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多的指令 …

CVE-2023-21839 Weblogic RCE

前言 刷B站的时候给我推的一个WebLogic的比较新的漏洞&#xff0c;可以通过此漏洞直接达到RCE进行getShell的效果&#xff0c;于是就简单复现和分析一下&#xff0c;做个记录。 视频链接 漏洞简单分析 此漏洞是属于WebLogic的JNDI注入漏洞&#xff0c;漏洞造成的原因是Weblo…

《创新者的基因》读书笔记

本书是企业创新管理的研究成果&#xff0c;针对个人和企业如何培养、提升商业创新能力给出了行动指南&#xff0c;每一种能力都提供了很多训练小技巧&#xff0c;在此不一一列举&#xff0c;只写自己的读书笔记、对策和思考。 破坏性创新者的基因 发问&#xff1a;目的是提出…

Java字符串详解:概念、特点与常见的使用场景

Java字符串是开发中经常使用到的一种数据类型&#xff0c;使用它可以处理文本、URL、文件路径等多种类型的数据。本文将对Java字符串的概念、特点以及常见使用场景进行详细解释。本文将分为以下几个部分&#xff1a; String的概念和特点Java字符串常量池字符串的不可变性使用e…

LeetCode - 3. 无重复字符的最长子串

写在前面&#xff1a; 题目链接&#xff1a;LeetCode - 3. 无重复字符的最长子串 题目难度&#xff1a;中等 编程语言&#xff1a;C 一、题目描述 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释…

【RabbitMQ】SpringAMQP

RabbitMQ 1.初识MQ 1.1.同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得到响应&am…

快速了解C语言的基本元素

C语言是一种编程语言&#xff0c;和其它语言一样&#xff0c;也定义了自己的语法和词汇。学习C语言&#xff0c;首先要学习C语言的词汇&#xff0c;再学习C语言的语法规则&#xff0c;然后由词汇构成语句&#xff0c;由语句构成源程序&#xff0c;源程序也称为源代码或代码&…

ORB-SLAM2的稠密重建实战(1) — 流程与函数功能说明

目录 0 系统整体流程 1 输出信息 2 初始化文件 3 初始化并运行追踪线程Tracking Step1&#xff1a;地图初始化 Step2&#xff1a;初始化成功&#xff08;mbOnlyTracking&#xff09; Step3&#xff1a;局部地图跟踪TrackLocalMap() Step4&#xff1a;跟踪成功 Step5&a…

【EKF】卡尔曼滤波的二维应用实例

前言 在上期&#xff0c;使用一个简单的一维应用实例来加深了卡尔曼滤波的印象后&#xff0c;使用一个二维的例子来看一下卡尔曼的效果。使用一个自由落体的例子来说明&#xff0c;假设一个物体在重力作用下&#xff0c;速度由0开始做自由落体运动&#xff0c;有观测装置对该物…

自动化部署编译部署【.net core】

自动化部署编译部署【.net core】 github 自动化编译部署 .NET 程序&#xff0c;程序有两个服务&#xff0c;一个是api&#xff0c;一个是admin. 需要部署到两台机器上(测试和正式)&#xff0c;所以采用两个Action来处理 项目目录结构 root ├── Config │ ├── deploy …

【Linux】进程间通信 —— 共享内存

文章目录 &#x1f4d5; 共享内存的原理&#x1f4d5; 代码实现 & 深入理解共享内存shmget() 函数shmctl() 、shmdt()、shmat()特点 &#x1f4d5; 源代码comm.hppserver.ccclient.cc &#x1f4d5; 共享内存的原理 我们知道&#xff0c;如果想实现进程间通信&#xff0c;…

Linux Shell 实现一键部署subversion

subversion SVN是subversion的缩写&#xff0c;是一个开放源代码的版本控制系统&#xff0c;通过采用分支管理系统的高效管理&#xff0c;简而言之就是用于多个人共同开发同一个项目&#xff0c;实现共享资源&#xff0c;实现最终集中式的管理。 TortoiseSVN TortoiseSVN 是…

C语言函数大全-- t 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- t 开头的函数 1. tan&#xff0c;tanf&#xff0c;tanl 1.1 函数说明 函数声明函数功能double tan(double x)计算 以弧度 x 为单位的角度的正切值&#xff08;double&#xff09;float tanf(float x)计算 以弧度 x 为单位的角度的正…

Spring Boot项目创建和使用

一、Spring Boot简介 1.概念 Spring Boot 就是 Spring 框架的脚⼿架&#xff0c;它就是为了快速开发 Spring 框架⽽诞⽣的。 2.优点 有快速集成框架&#xff0c;可以快速添加外部jar包内置web框架&#xff0c;可以直接运行可以快速部署&#xff0c;不依赖任何外部的web容器…

【牛客刷题专栏】0x26:JZ25 合并两个排序的链表(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录 前言问…

MATLAB 之 数值数据,矩阵的表示和变量及其操作

文章目录 一、数值数据1. 数值数据类型的分类1.1 整型1.2 浮点型1.3 复型 2. 数据的输出格式 二、矩阵的表示1. 矩阵的建立1.1 直接输入法建立矩阵1.2 已建好的矩阵建立更大的矩阵 2. 冒号表达式3. 矩阵元素的引用3.1 矩阵元素的引用方式3.2 利用冒号表达式获得子矩阵3.3 利用空…

Spring执行流程Bean生命周期

Spring执行流程 说明&#xff1a;这里只是说的大概流程&#xff0c;不是严格按照源码上一步一步说的。 简单来说&#xff0c;分为下边四个步骤&#xff1a; 启动Spring容器实例化Bean&#xff08;分配内存空间&#xff09;将Bean注册到Spring当中&#xff08;存操作&#xf…

【常用 Linux 命令的基本使用】总结篇(附必要的 Ubuntu 截图)

本文目录 1. 常用 Linux 命令的基本使用1.1 学习 Linux 终端命令的原因1.2 常用 Linux 命令的基本使用1.3 自动补全 2. Linux 终端命令格式2.1 终端命令格式2.2 查阅命令帮助信息&#xff08;了解&#xff09; 3. 文件和目录常用命令3.1 查看目录内容3.1.1 终端实用技巧3.1.2 l…

shell数组

目录 一&#xff1a;数组定义方法 1、方法一 ​ 2、方法二 ​3、方法三 ​4、方法四 5、判断数组是否完整 &#xff08;1&#xff09;方法一 &#xff08;2&#xff09;方法二&#xff1a;通过脚本 二&#xff1a;获取数组值 1、获取数组长度 2、获取数组数据列表 3、…