原子学习笔记7——FrameBuffer 应用编程

Frame 是帧的意思,buffer 是缓冲的意思,所以 Framebuffer 就是帧缓冲,这意味着 Framebuffer 就是一块内存,里面保存着一帧图像。
应用程序通过对 LCD 设备节点/dev/fb0(假设 LCD 对应的设备节点是/dev/fb0)进行 I/O 操作即可实现对 LCD 的显示控制。
在应用程序中,操作/dev/fbX 的一般步骤如下:
①、首先打开/dev/fbX 设备文件。
②、使用 ioctl()函数获取到当前显示设备的参数信息,譬如屏幕的分辨率大小、像素格式,根据屏幕参数计算显示缓冲区的大小。
③、通过存储映射 I/O 方式将屏幕的显示缓冲区映射到用户空间(mmap)。
④、映射成功后就可以直接读写屏幕的显示缓冲区,进行绘图或图片显示等操作了。
⑤、完成显示后,调用 munmap()取消映射、并调用 close()关闭设备文件。

1、获取 LCD 屏幕的参数信息

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    int fd;
    // /* 打开 framebuffer 设备 
    if (0 > (fd = open("/dev/fb0", O_WRONLY))) {
        perror("open error");
        exit(-1);
    }
    // /* 获取参数信息 
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
    printf("分辨率: %d*%d\n"
            "像素深度 bpp: %d\n"
            "一行的字节数: %d\n"
            "像素格式: R<%d %d> G<%d %d> B<%d %d>\n",
            fb_var.xres, fb_var.yres, fb_var.bits_per_pixel,
            fb_fix.line_length,
            fb_var.red.offset, fb_var.red.length,
            fb_var.green.offset, fb_var.green.length,
            fb_var.blue.offset, fb_var.blue.length);
    // /* 关闭设备文件退出程序 
    close(fd);
    exit(0);
}

可以看出fb_fix_screeninfo、fb_var_screeninfo结构体与FBIOGET_VSCREENINFO、FBIOGET_FSCREENINFO通过ioctl()函数显示出设备参数信息。
开发板上使用的是7 寸 1024*600的显示屏
在这里插入图片描述

2、LCD 基本操作

编写应用程序,在 LCD 上实现画点(俗称打点)、画线、画矩形等基本 LCD 操作。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#define argb8888_to_rgb565(color) ({ \
    unsigned int temp = (color); \
    ((temp & 0xF80000UL) >> 8) | \
    ((temp & 0xFC00UL) >> 5) | \
    ((temp & 0xF8UL) >> 3); \
 })
static int width; //LCD X 分辨率
static int height; //LCD Y 分辨率
static unsigned short *screen_base = NULL; //映射后的显存基地址
/********************************************************************
* 函数名称: lcd_draw_point
* 功能描述: 打点
* 输入参数: x, y, color
* 返 回 值: 无
********************************************************************/
static void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color)
{
    unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
    /* 对传入参数的校验 */
    if (x >= width)
        x = width - 1;
    if (y >= height)
        y = height - 1;
    /* 填充颜色 */
    screen_base[y * width + x] = rgb565_color;
}
/********************************************************************
* 函数名称: lcd_draw_line
* 功能描述: 画线(水平或垂直线)
* 输入参数: x, y, dir, length, color
* 返 回 值: 无
********************************************************************/
static void lcd_draw_line(unsigned int x, unsigned int y, int dir,
 unsigned int length, unsigned int color)
{
    unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
    unsigned int end;
    unsigned long temp;
    /* 对传入参数的校验 */
    if (x >= width)
        x = width - 1;
    if (y >= height)
        y = height - 1;
    /* 填充颜色 */
    temp = y * width + x;//定位到起点
    if (dir) { //水平线
        end = x + length - 1;
        if (end >= width)
            end = width - 1;
        for ( ; x <= end; x++, temp++)
            screen_base[temp] = rgb565_color;
    }
    else { //垂直线
        end = y + length - 1;
        if (end >= height)
            end = height - 1;
        for ( ; y <= end; y++, temp += width)
            screen_base[temp] = rgb565_color;
    }
}
/********************************************************************
* 函数名称: lcd_draw_rectangle
* 功能描述: 画矩形
* 输入参数: start_x, end_x, start_y, end_y, color
* 返 回 值: 无
********************************************************************/
static void lcd_draw_rectangle(unsigned int start_x, unsigned int end_x,
 unsigned int start_y, unsigned int end_y,
 unsigned int color)
{
    int x_len = end_x - start_x + 1;
    int y_len = end_y - start_y - 1;
    lcd_draw_line(start_x, start_y, 1, x_len, color);//上边
    lcd_draw_line(start_x, end_y, 1, x_len, color); //下边
    lcd_draw_line(start_x, start_y + 1, 0, y_len, color);//左边
    lcd_draw_line(end_x, start_y + 1, 0, y_len, color);//右边
}
/********************************************************************
* 函数名称: lcd_fill
* 功能描述: 将一个矩形区域填充为参数 color 所指定的颜色
* 输入参数: start_x, end_x, start_y, end_y, color
* 返 回 值: 无
********************************************************************/
static void lcd_fill(unsigned int start_x, unsigned int end_x,
 unsigned int start_y, unsigned int end_y,
 unsigned int color)
{
    unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值
    unsigned long temp;
    unsigned int x;
    /* 对传入参数的校验 */
    if (end_x >= width)
        end_x = width - 1;
    if (end_y >= height)
        end_y = height - 1;
    /* 填充颜色 */
    temp = start_y * width; //定位到起点行首
    for ( ; start_y <= end_y; start_y++, temp+=width) {
        for (x = start_x; x <= end_x; x++)
            screen_base[temp + x] = rgb565_color;
    }
}
int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;
    /* 打开 framebuffer 设备 */
    if (0 > (fd = open("/dev/fb0", O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }
    /* 获取参数信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
    screen_size = fb_fix.line_length * fb_var.yres;//一帧数据的数据量大小
    width = fb_var.xres;
    height = fb_var.yres;
    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }
    /* 画正方形方块 */
    int w = height * 0.25;//方块的宽度为 1/4 屏幕高度
    lcd_fill(0, width-1, 0, height-1, 0x0); //清屏(屏幕显示黑色)
    lcd_fill(0, w, 0, w, 0xFF0000); //红色方块
    lcd_fill(width-w, width-1, 0, w, 0xFF00); //绿色方块
    lcd_fill(0, w, height-w, height-1, 0xFF); //蓝色方块
    lcd_fill(width-w, width-1, height-w, height-1, 0xFFFF00);//黄色方块
    /* 画线: 十字交叉线 */
    lcd_draw_line(0, height * 0.5, 1, width, 0xFFFFFF);//白色线
    lcd_draw_line(width * 0.5, 0, 0, height, 0xFFFFFF);//白色线
    /* 画矩形 */
    unsigned int s_x, s_y, e_x, e_y;
    s_x = 0.25 * width;
    s_y = w;
    e_x = width - s_x;
    e_y = height - s_y;
    for ( ; (s_x <= e_x) && (s_y <= e_y);s_x+=5, s_y+=5, e_x-=5, e_y-=5)
        lcd_draw_rectangle(s_x, e_x, s_y, e_y, 0xFFFFFF);
    /* 退出 */
    munmap(screen_base, screen_size); //取消映射
    close(fd); //关闭文件
    exit(EXIT_SUCCESS); //退出进程
}
  • 首先调用 open()打开 LCD 设备文件得到文件描述符 fd;
  • 接着使用 ioctl 函数获取 LCD 的可变参数信息和固定参数信息,通过得到的信息计算 LCD 显存大小、得到 LCD 屏幕的分辨率,ALPHA I.MX6U 开发板出厂系统将 LCD 实现为一个 RGB565 显示设备,所以程序中自定义的 4 个函数在操作 LCD 像素点时、都是以 RGB565的格式写入颜色值。
  • 接着使用 mmap 建立映射;
  • 映射成功之后就可以在应用层直接操作 LCD 显存了,调用自定义的函数在 LCD 上画线、画矩形、画方块;
  • 操作完成之后,调用 munmap 取消映射,调用 close 关闭 LCD 设备文件,退出程序。

3、显示 BMP 图片

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <linux/fb.h>
#include <sys/mman.h>
/**** BMP 文件头数据结构 ****/
typedef struct {
    unsigned char type[2]; //文件类型
    unsigned int size; //文件大小
    unsigned short reserved1; //保留字段 1
    unsigned short reserved2; //保留字段 2
    unsigned int offset; //到位图数据的偏移量
} __attribute__ ((packed)) bmp_file_header;
/**** 位图信息头数据结构 ****/
typedef struct {
    unsigned int size; //位图信息头大小
    int width; //图像宽度
    int height; //图像高度
    unsigned short planes; //位面数
    unsigned short bpp; //像素深度
    unsigned int compression; //压缩方式
    unsigned int image_size; //图像大小
    int x_pels_per_meter; //像素/米
    int y_pels_per_meter; //像素/米
    unsigned int clr_used;
    unsigned int clr_omportant;
} __attribute__ ((packed)) bmp_info_header;
/**** 静态全局变量 ****/
static int width; //LCD X 分辨率
static int height; //LCD Y 分辨率
static unsigned short *screen_base = NULL; //映射后的显存基地址
static unsigned long line_length; //LCD 一行的长度(字节为单位)
/********************************************************************
* 函数名称: show_bmp_image
* 功能描述: 在 LCD 上显示指定的 BMP 图片
* 输入参数: 文件路径
* 返 回 值: 成功返回 0, 失败返回-1
********************************************************************/
static int show_bmp_image(const char *path)
{
    bmp_file_header file_h;
    bmp_info_header info_h;
    unsigned short *line_buf = NULL; //行缓冲区
    unsigned long line_bytes; //BMP 图像一行的字节的大小
    unsigned int min_h, min_bytes;
    int fd = -1;
    int j;
    /* 打开文件 */
    if (0 > (fd = open(path, O_RDONLY))) {
        perror("open error");
        return -1;
    }
    /* 读取 BMP 文件头 */
    if (sizeof(bmp_file_header) != read(fd, &file_h, sizeof(bmp_file_header))) {
        perror("read error");
        close(fd);
        return -1;
    }
    if (0 != memcmp(file_h.type, "BM", 2)) {
        fprintf(stderr, "it's not a BMP file\n");
        close(fd);
        return -1;
    }
    /* 读取位图信息头 */
    if (sizeof(bmp_info_header) != read(fd, &info_h, sizeof(bmp_info_header))) {
        perror("read error");
        close(fd);
        return -1;
    }
    /* 打印信息 */
    printf("文件大小: %d\n"
        "位图数据的偏移量: %d\n"
        "位图信息头大小: %d\n"
        "图像分辨率: %d*%d\n"
        "像素深度: %d\n", file_h.size, file_h.offset,
        info_h.size, info_h.width, info_h.height,
        info_h.bpp);
    /* 将文件读写位置移动到图像数据开始处 */
    if (-1 == lseek(fd, file_h.offset, SEEK_SET)) {
        perror("lseek error");
        close(fd);
        return -1;
    }
    /* 申请一个 buf、暂存 bmp 图像的一行数据 */
    line_bytes = info_h.width * info_h.bpp / 8;
    line_buf = malloc(line_bytes);
    if (NULL == line_buf) {
        fprintf(stderr, "malloc error\n");
        close(fd);
        return -1;
    }
    if (line_length > line_bytes)
        min_bytes = line_bytes;
    else
        min_bytes = line_length;
    /**** 读取图像数据显示到 LCD ****/
    /*******************************************
     * 为了软件处理上方便,这个示例代码便不去做兼容性设计了
     * 如果你想做兼容, 可能需要判断传入的 BMP 图像是 565 还是 888
     * 如何判断呢?文档里边说的很清楚了
     * 我们默认传入的 bmp 图像是 RGB565 格式
     *******************************************/
    if (0 < info_h.height) {//倒向位图
        if (info_h.height > height) {
            min_h = height;
            lseek(fd, (info_h.height - height) * line_bytes, SEEK_CUR);
            screen_base += width * (height - 1); //定位到屏幕左下角位置
        }
        else {
            min_h = info_h.height;
            screen_base += width * (info_h.height - 1); //定位到....不知怎么描述 懂的人自然懂!
        }
        for (j = min_h; j > 0; screen_base -= width, j--) {
            read(fd, line_buf, line_bytes); //读取出图像数据
            memcpy(screen_base, line_buf, min_bytes);//刷入 LCD 显存
        }
    }
    else { //正向位图
        int temp = 0 - info_h.height; //负数转成正数
        if (temp > height)
            min_h = height;
        else
            min_h = temp;
        for (j = 0; j < min_h; j++, screen_base += width) {
            read(fd, line_buf, line_bytes);
            memcpy(screen_base, line_buf, min_bytes);
        }
    }
    /* 关闭文件、函数返回 */
    close(fd);
    free(line_buf);
    return 0;
}
int main(int argc, char *argv[])
{
    struct fb_fix_screeninfo fb_fix;
    struct fb_var_screeninfo fb_var;
    unsigned int screen_size;
    int fd;
    /* 传参校验 */
    if (2 != argc) {
        fprintf(stderr, "usage: %s <bmp_file>\n", argv[0]);
        exit(-1);
    }
    /* 打开 framebuffer 设备 */
    if (0 > (fd = open("/dev/fb0", O_RDWR))) {
        perror("open error");
        exit(EXIT_FAILURE);
    }
    /* 获取参数信息 */
    ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);
    screen_size = fb_fix.line_length * fb_var.yres;
    line_length = fb_fix.line_length;
    width = fb_var.xres;
    height = fb_var.yres;
    /* 将显示缓冲区映射到进程地址空间 */
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);
    if (MAP_FAILED == (void *)screen_base) {
        perror("mmap error");
        close(fd);
        exit(EXIT_FAILURE);
    }
    /* 显示 BMP 图片 */
    memset(screen_base, 0xFF, screen_size);
    show_bmp_image(argv[1]);
    /* 退出 */
    munmap(screen_base, screen_size); //取消映射
    close(fd); //关闭文件
    exit(EXIT_SUCCESS); //退出进程
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【时隙ALOHA,CSMA(载波侦听多路访问)carrier sense mltiple access,无线局域网: CSMA/CA】

文章目录 时隙ALOHA时隙ALOHA的效率( Efficiency )纯ALOHA(非时隙)----效率低CSMA(载波侦听多路访问)carrier sense mltiple accessCSMA冲突CSMA/CD(冲突检测)边说边听&#xff08;提高了信道利用率&#xff09;以太网就是用的这个无线局域网: CSMA/CA无线局域网中的 MAC&#…

同为科技详解智能PDU所应用的通信协议与接口

现如今&#xff0c;信息服务、AI人工智能的飞速发展与增长&#xff0c;全球正经历信息数据的爆炸。不仅数据量以惊人的速度增长&#xff0c;而且全球社会各行业对数据的依赖的程度也在日益增加。这些趋势使数据中心在全球都享有关键基础架构的地位。假设某个数据中心发生严重的…

【知识拓展】大白话说清楚:IP地址、子网掩码、网关、DNS等

前言 工作中常听别人说的本地网络是什么意思&#xff1f;同一网段又是什么意思&#xff1f;它俩有关系吗&#xff1f; 在工作中内经常会遇到相关的网络问题&#xff0c;涉及网络通信中一些常见的词汇&#xff0c;如IP地址、子网掩码、网关和DNS等。具体一点&#xff1a;经常会…

JavaEE之线程(5)——Java内存模型、内存可见性、volatile关键字

前言 volatile可以理解成轻量级的 synchronized&#xff0c; 它在多CPU开发中保证了共享变量的“可见性”&#xff0c;可见性我们可以理解成是&#xff1a;当一个线程修改一个共享变量时&#xff0c;另一个线程可以读到这个修改的值。由于它不会引起线程的上下文切换和调度&am…

【JavaWeb】Day77.Spring——SpringBoot原理(一)

SpringBoot原理 Spring是目前世界上最流行的Java框架&#xff0c;它可以帮助我们更加快速、更加容易的来构建Java项目。而在Spring家族当中提供了很多优秀的框架&#xff0c;而所有的框架都是基于一个基础框架的SpringFramework(也就是Spring框架)。而如果我们直接基于Spring框…

【Shell】Shell编程之函数

目录 1.Shell函数定义 2.Shell函数的作用 3.函数返回值 4.函数传参 5.函数变量的作用范围 案例 1.Shell函数定义 格式1 function 函数名 { 命令序列 } 格式2 函数名() { 命令序列 } 2.Shell函数的作用 使用函数可以避免代码重复 使用函数可以将大的工程分割为若…

springcloud简单了解及上手

springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…

【ARM Cortex-M 系列 2.3 -- Cortex-M7 Debug event 详细介绍】

请阅读【嵌入式开发学习必备专栏】 文章目录 Cortex-M7 Debug eventDebug events Cortex-M7 Debug event 在ARM Cortex-M7架构中&#xff0c;调试事件&#xff08;Debug Event&#xff09;是由于调试原因而触发的事件。一个调试事件会导致以下几种情况之一发生&#xff1a; 进…

部署管理征信链码

一 . 链码准备 需要删除上面后面标记的文件&#xff0c;之后拖入 二. 打包链码 注意需要先启动链 打包测试链码 export FABRIC_CFG_PATH${PWD}/config peer lifecycle chaincode package ./chaincode/chaincode_basic.tar.gz --path ./chaincode/credit_chaincode --lang n…

vue3.0(五) reactive全家桶

文章目录 1 reactive1.1 reactive的应用1.2 reactive的特点1.3 reactive的注意1.4 reactive的局限性 2 toRefs3 isReactive4 shallowReactive5 readonly5.1 readonly 详细信息5.2 readonly函数创建一个只读的响应式对象5.3 如何修改嵌套在只读响应式对象中的对象? 6 isReadonl…

考研数学|李林《880》做不动,怎么办!?看这一篇!

在考研数学的备考过程中&#xff0c;遇到难题是很常见的情况&#xff0c;尤其是当你尝试解决李林880习题集中的问题时。他以其难度和深度著称&#xff0c;旨在帮助考生深入理解数学分析的复杂概念。 如果你在解题过程中感到困难&#xff0c;这并不是你个人的问题&#xff0c;而…

《无畏契约》游戏画面出现“撕裂感“,你清楚背后的原理吗?

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

安全风险 - 如何解决 setAccessible(true) 带来的安全风险?

可能每款成熟的金融app上架前都会经过层层安全检测才能执行上架&#xff0c;所以我隔三差五就能看到安全检测报告中提到的问题&#xff0c;根据问题的不同级别&#xff0c;处理的优先级也有所不同&#xff0c;此次讲的主要是一个 “轻度问题” &#xff0c;个人认为属于那种可改…

[译文] 恶意代码分析:2.LNK文件伪装成证书传播RokRAT恶意软件(含无文件攻击)

这是作者新开的一个专栏&#xff0c;主要翻译国外知名安全厂商的技术报告和安全技术&#xff0c;了解它们的前沿技术&#xff0c;学习它们威胁溯源和恶意代码分析的方法&#xff0c;希望对您有所帮助。当然&#xff0c;由于作者英语有限&#xff0c;会借助LLM进行校验和润色&am…

idea控制台日志控制

1.清除控制台log日志 测试的时候&#xff0c;控制台打印的日志比较多&#xff0c;速度有点慢而且不利于查看运行结果&#xff0c;所以接下来我们把这个日志处理下: 取消初始化spring日志打印&#xff0c;resources目录下添加logback.xml&#xff0c;名称固定&#xff0c;内容如…

Transformer+Classification学习笔记

论文名称&#xff1a;An Image is Worth 16x16 Words:Transformers for Image Recognition at Scale [2112.11010] MPViT: Multi-Path Vision Transformer for Dense Prediction (arxiv.org) 参考博客与视频&#xff1a; Vision Transformer 超详细解读 (原理分析代码解读) …

[动画详解]LeetCode151.翻转字符串里的单词

&#x1f496;&#x1f496;&#x1f496;欢迎来到我的博客&#xff0c;我是anmory&#x1f496;&#x1f496;&#x1f496; 又和大家见面了 欢迎来到动画详解LeetCode算法系列 用通俗易懂的动画让算法题不再神秘 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低成…

589.N叉树的前序遍历

刷算法题&#xff1a; 第一遍&#xff1a;1.看5分钟&#xff0c;没思路看题解 2.通过题解改进自己的解法&#xff0c;并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步&#xff0c;下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

解决el-upload组件上传文件403 Forbidden的问题

话不多说&#xff0c;上错误。网络显示&#xff1a; 控制台显示&#xff1a; 并且后端也没接收到任何的请求。 只需要把前端中的组件&#xff1a; action的路径修改为&#xff1a; 也就是不写前面的localhost&#xff0c;而是拼接上发送请求拼接的‘api’即可 可以看到&#x…

架构每日一学 6:作为架构师,你必须学会寻找商业模式

本文首发于公众平台&#xff1a;腐烂的橘子 在前面的文章中&#xff0c;我们已经讲了架构师的两条生存法则&#xff0c;第一条是有且仅有一个目标&#xff0c;感兴趣的可以看一下原文&#xff1a; 架构每日一学 2&#xff1a;架构师六个生存法则之一&#xff1a;架构必须有且仅…