首页 > 系统相关 >正点原子Linux Framebuffer编程:解决示例程序在开发板上LCD显示错位和颜色异常

正点原子Linux Framebuffer编程:解决示例程序在开发板上LCD显示错位和颜色异常

时间:2024-09-01 16:26:49浏览次数:10  
标签:示例 int unsigned height width LCD lcd Linux color

正点原子Linux Framebuffer编程:解决示例程序在开发板上运行7寸LCD显示错位和颜色异常

作者在学习【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.4时,发现其配套的程序在开发板上运行不正常。

使用的硬件版本:正点原子 I.MX6U ALPHA V2.4版本底板LCD:正点原子7寸1024*600,型号ATK-MD0700R-1024600

问题描述

经研究发现问题主要有

  1. 我开发板使用的LCD颜色格式是RGB888,而非RGB565,导致颜色错位
  2. 老师使用的LCD尺寸和笔者一样,但分辨率却不同(没仔细看直接用导致显示错位。。。。)

在手册587-588页可看到如下片段:

image
image

本人跟着左盟主从ubuntu入门操作开始学,软硬件的教程一直是没问题的。到C应用编程换了个主讲人,视频也是比较新的日期录制的。因为稍微更换了使用的硬件平台,示例程序自然会发生变动。

解决过程

下面是老师的示例程序:

/***************************************************************
 Copyright © ALIENTEK Co., Ltd. 1998-2021. All rights reserved.
 文件名 : lcd_test.c
 作者 : 邓涛
 版本 : V1.0
 描述 : FrameBuffer应用程序示例代码
 其他 : 无
 论坛 : www.openedv.com
 日志 : 初版 V1.0 2021/6/15 邓涛创建
 ***************************************************************/

#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);    //退出进程
}

直接编译运行会出现以下问题:

可以看到图像显示错位、颜色显示不正常

image

对比代码。问题主要出现在以下几个方面

  1. RGB颜色模型不同,导致颜色非正常
  2. 分辨率设置错误

听起来是不是很简单,分辨率虽不同,但是是通过如下代码动态获取的,我们不需要更改

    /* 获取参数信息 */
    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;

那是不是只需要把argb8888_to_rgb565这个宏去掉,就能改好了呢?

很遗憾并不能。

去掉宏之后颜色变了,但还是不对,有三个色块的颜色都是绿色。。。

别急,我已经摸索到了解决办法!

接下来是见证奇迹的时刻,如果你在使用vim,在普通模式下输入以下命令:

:%s/short/int/g

将短整型关键字全部替换为整形,在虚拟机中使用arm-linux-gnueabihf-gcc -o lcd_test lcd_test.c编译,在开发板上运行,解决!

image

问题所在

老师使用的7寸800*480屏幕的RGB格式是RGB565。

RGB565

  • 这种格式将红色和蓝色各分配了5位,绿色分配了6位,总共16位。
  • 红色和蓝色的强度可以有32级(从0到31),绿色可以有64级(从0到63)。
  • RGB565可以表示大约65,536种不同的颜色(2^16)。
  • 由于人眼对绿色的敏感度高于红色和蓝色,所以RGB565通过为绿色分配更多的位来优化颜色表现。

而我使用的是7寸1024*600屏幕是RGB888格式

RGB888

  • 这种格式为红色、绿色和蓝色各分配了8位(bit),总共24位。
  • 每种颜色的强度可以有256级(从0到255)。
  • 因此,RGB888可以表示大约1677万种不同的颜色(256 x 256 x 256)。
  • 由于每个颜色通道都有8位,所以它可以提供更平滑的颜色过渡和更丰富的颜色细节。

当然,屏幕在硬件上大多数是支持多种颜色格式的,如同上文所述可以通过修改设备树或通过ioctl更改,在此只介绍最简便的解决方法

这两种格式不仅在RGB每个颜色占的bit数量比例不一样,最重要的是占的总位数不一样

前者通常使用short短整型16位变量存储颜色数据,后者则需要24位,因为字节对齐和变量访问便利通常使用32位整型存储颜色数据

在老师的代码中,使用mmap将fb设备数据缓冲区的起始地址映射到screen_base (short类型指针变量)

static unsigned short *screen_base = NULL;      //映射后的显存基地址
/* 将显示缓冲区映射到进程地址空间 */
screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0);

然后在代码中填充颜色的时候访问都是按照short的长度依次访问,每次偏移16位,导致0xFFFFFF这样的32位RGB888数据填充错误,后来的数据会覆盖前数据的一部分,

/* 填充颜色 */
screen_base[y * width + x] = rgb565_color;

最后附上修改后的完整代码:

#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 UNUSED_argb8888_to_rgb565(color)   (color)

static int width;                   //LCD X分辨率
static int height;                      //LCD Y分辨率
static unsigned int *screen_base = NULL;      //映射后的显存基地址

/********************************************************************
 * 函数名称: lcd_draw_point
 * 功能描述: 打点
 * 输入参数: x, y, color
 * 返 回 值: 无
 ********************************************************************/
static void lcd_draw_point(unsigned int x, unsigned int y, unsigned int color)
{
    unsigned int rgb565_color = UNUSED_argb8888_to_rgb565(color);//不需要转换,本身就是RGB888

    /* 对传入参数的校验 */
    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 int rgb565_color = UNUSED_argb8888_to_rgb565(color);//不需要转换,本身就是RGB888
    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 int rgb565_color = UNUSED_argb8888_to_rgb565(color);//不需要转换,本身就是RGB888
    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);    //退出进程
}

标签:示例,int,unsigned,height,width,LCD,lcd,Linux,color
From: https://www.cnblogs.com/yuewusayuri/p/18391393

相关文章

  • Linux权限管理
    Linux权限管理1.Linux权限概述1.1rwx概述Linux权限含义r#read读取,是否可以查看文件内容w#write修改,是否可以修改文件内容x#exec可执行,一般是给命令或系统脚本使用,运行命令1.2查看文件,目录权限[root@Kylin-V10......
  • Linux抓包神器 tcpdump 使用指南
    tcpdump是一款强大的网络抓包工具,它使用libpcap库来抓取网络数据包,这个库在几乎所有的Linux/Unix系统中都有。熟悉tcpdump的使用能够帮助用户分析调试网络数据。以下是tcpdump的详细使用指南:一、安装tcpdump在Linux系统中,可以通过包管理器安装tcpdump。对于Debian系系统,可以......
  • 【Linux】用户和权限及实用操作------迅速了解用户和权限及其实用操作
    目录......
  • linux命令重启tomcat
    动一下小手点一下赞。谢谢!你的赞就是我更新的动力。Linux系统下,重启Tomcat服务器是一个常见的操作。Tomcat是一个开源的JavaServlet容器,用于实现JavaServlet和JavaServerPages(JSP)等技术。在Linux系统上,我们可以通过一些简单的命令来实现重启Tomcat服务器的操作。首先,我们需要打......
  • 【Linux系列】du命令详解
    ......
  • Linux实时查看GPU (NVIDIA),CPU及内存使用情况
    GPU方法一:需要用到一个工具gpustatpipinstallgpustat#安装工具gpustat-cp#输出当前状态gpustat-cp-i1#持续监视方法二:使用nvidia-smi命令nvidia-smi-lsecondsnvidia-smi--loop=seconds上述两个命令都可以,要把seconds换成你想刷新的时间间隔。按......
  • Linux命令fuser使用
    简介在Linux操作系统中,fuser是一个命令行工具,用于查找并显示使用指定文件、文件系统或套接字的进程的情况。它可以帮助我们找出哪些进程占用了某个文件或目录,以便我们能够进行相关操作,比如杀死这些进程或释放对应的资源。命令语法fuser命令的基本语法如下:fuser[选项]文件或目录......
  • 嵌入式全栈开发学习笔记---Linux网络编程(面试/开发重点)
    目录网络概述Linux网络基础网络模型TCP/IP协议族体系结构数据封装TCP协议TCP协议头部结构TCP三次握手TCP四次挥手UDP协议UDP协议头部结构套接字Socket端口号和IP地址地址转换字节序转换TCP服务器服务器建立步骤第一步,创建socket--socket()第二步,绑定信息Bin......
  • Metasploit Pro 4.22.3-2024082201 (Linux, Windows) - 专业渗透测试框架
    MetasploitPro4.22.3-2024082201(Linux,Windows)-专业渗透测试框架Rapid7Penetrationtesting,releaseAug22,2024请访问原文链接:https://sysin.org/blog/metasploit-pro-4/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org世界上最广泛使用的渗透测试框架......
  • linux文件——文件系统——学习、理解、应用软硬件链接
        前言:本篇内容主要讲解文件系统的软硬件链接。经过前两篇文件系统的文章——讲解硬件(磁盘)、讲解文件系统底层,inode,我们本节内容可以很好的理解我们要讲解的内容。并且本节内容较少,友友们学习本节的时候将会比前几节相对轻松一些。     ps:友友们务......