fbtft使用的是framebuffer框架,这种框架将显示设备抽象为帧缓冲区,对framebuffer设备(/dev/fbx(0、1、2..))进行相关操作可以反应到LCD上。

现在尝试下在用户空间用 C 来操作 LCD 设备。

获取参数

要对 framebuffer 进行操作,首先要知道所操作设备的相关参数,因为要驱动一个屏,首先需要知道该屏尺寸、单色还是彩色等,Linux在用户空间提供了两个跟 framebuffer 参数相关的结构体(在文件 fb.h 中):

  • 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 */
};
  • 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 colorspace; /* colorspace for FOURCC-based modes */
__u32 reserved[4]; /* Reserved for future compatibility */
};

这两个参数都可以通过ioctl获得,首先定义3个变量,一个是打开的fb设备的句柄,剩下2个分别是fb_var_screeninfo、fb_fix_screeninfo:

int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;

打开fb设备:

    fbfd = open("/dev/fb0", O_RDWR);
if (!fbfd)
{
printf("Error: cannot open framebuffer device.\n");
exit(1);
}

然后分别获取两个结构体,并在main函数中调用:

    if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
{
printf("Error reading fixed information.\n");
exit(2);
} if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
{
printf("Error: reading variable information.\n");
exit(3);
}

创建两个函数分别输出这两个结构体元素:

void show_fb_fix_info(struct fb_fix_screeninfo info)
{
printf("fb's fix msg:\n");
printf("\tid is:%s\n",info.id);
printf("\tsmem_start is:%d\n",info.smem_start);
printf("\tsmem_len is:%d\n",info.smem_len);
printf("\ttype_aux is:%d\n",info.type_aux);
printf("\tvisual is:%d\n",info.visual);
printf("\txpanstep is:%d\n",info.xpanstep);
printf("\typanstep is:%d\n",info.ypanstep);
printf("\tywrapstep is:%d\n",info.ywrapstep);
printf("\tline_length is:%d\n",info.line_length);
printf("\tmmio_start is:%d\n",info.mmio_start);
} void show_fb_var_info(struct fb_var_screeninfo info)
{
printf("fb's var msg:\n");
printf("\txres is:%d\n",info.xres);
printf("\tyres is:%d\n",info.yres);
printf("\txres_virtual is:%d\n",info.xres_virtual);
printf("\tyres_virtual is:%d\n",info.yres_virtual);
printf("\txoffset is:%d\n",info.xoffset);
printf("\tyoffset is:%d\n",info.yoffset);
printf("\tbits_per_pixel is:%d\n",info.bits_per_pixel);
printf("\tgrayscale is:%d\n",info.grayscale);
printf("\tnonstd is:%d\n",info.nonstd);
printf("\tactivate is:%d\n",info.activate);
printf("\theight is:%d\n",info.height);
printf("\twidth is:%d\n",info.width);
printf("\taccel_flags is:%d\n",info.accel_flags);
printf("\tpixclock is:%d\n",info.pixclock);
printf("\tleft_margin is:%d\n",info.left_margin);
printf("\tright_margin is:%d\n",info.right_margin);
printf("\tupper_margin is:%d\n",info.upper_margin);
printf("\tlower_margin is:%d\n",info.lower_margin);
printf("\thsync_len is:%d\n",info.hsync_len);
printf("\tvsync_len is:%d\n",info.vsync_len);
printf("\tsync is:%d\n",info.sync);
printf("\tvmode is:%d\n",info.vmode);
printf("\trotate is:%d\n",info.rotate);
printf("\tcolorspace is:%d\n",info.colorspace);
}

编译、拷贝到目标板上,运行结果如下:

从上面运行结果看,成功的获取了屏的一系列参数。

详细代码framebuffer/show_fb_msg.c

填充颜色

​ 现在试下给整个屏填充颜色,要填充屏的话,需要几个参数,一个是framebuffer所需内存的大小,一个是屏的像素的个数,还有就是颜色深度。

​ 要对framebuffer进行操作首先需要做的是通过mmap进行地址映射,这里就要用到framebuffer所需内存的大小,framebuffer所需内存的大小可以从fb_fix_screeninfo获得,如下:

static char *fbp = 0;

fbp = (char *)mmap(0, finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
if ((int)fbp == -1)
{
printf("Error: failed to map framebuffer device to memory.\n");
exit(4);
}

​ 这里申请一段finfo.smem_len大小的连续内存,对这段内存进行操作就会反应到屏上了。

​ 对整个屏进行填充就是操作屏上的所有像素(也可以说遍历所有像素),这里就需要知道屏幕的像素的个数,这个参数可以从之前获取到的fb_var_screeninfo中的参数中的xres,yres。从fb.h中的注释翻译过来,xres,yres是可见分辨率,我理解为可见区域,应该就是对应屏幕显示区域的大小了。

int screensize = 0;

screensize = xres * yres;

​ 还有一个很关键的参数颜色深度(bpp:bits per pixel),也就是表示一个像素颜色所需的位数(bit),一般来说有这么几种:1位,8位,16位,24位,32位。,比如1位的屏,也就是单色屏,用1bit来表示颜色,如果是白色单色屏的话,0表示黑色,1表示白色。24bit屏,就是用24bit(3字节)表示颜色,也就是RGB888,R、G、B分别占8bit。

​ 从上面获取到的参数知道,该屏是16bit的,也就是RGB565,用两个字节表示。应为之前用mmap申请的内存是char指针,用该指针来操作像素的话不方便,索性就把char型转成short型,如下:

short *fb_s;
fb_s = (short *)fbp;

定义4个宏,分别表示红色、绿色、蓝色、黑色,

#define RED 0xf800
#define GREEN 0x07e0
#define BLUE 0x001f
#define BLACK 0x0000

创建一个写framebuffer内存的函数,如下:

void fill_screen(short *fb_men,short color,int pix_size)
{
for(int i=0;i<pix_size;i++)
{
*fb_men = color;
fb_men ++;
}
}

第一个参数是所要操作的framebuffer对应的内存,第二个是所要填充的颜色,第三个是屏幕的尺寸,然后就可以对屏幕进行填充了,如下:

	printf("Fill red\n");
fill_screen(fb_s,RED,screensize/2);
sleep(1);
printf("Fill green\n");
// fb_s = (short *)fbp;
fill_screen(fb_s,GREEN,screensize/2);
sleep(1);
printf("Fill blue\n");
// fb_s = (short *)fbp;
fill_screen(fb_s,BLUE,screensize/2);
sleep(1);
fill_screen(fb_s,BLACK,screensize/2);

上面的代码是先填充红色,等待1秒,然后是绿色、蓝色,最后填充黑色(清屏)。效果如下:

详细代码framebuffer/fb_fill_color.c

显示图片

现有的图片格式非常多,JPEG、TIFF、PNG、BMP,SVG等等,这些又分有压缩、无压缩。这里是为了学习如何操作framebuffer,就选择一种无压缩格式的格式的图片来操作。可以直接以二进制的方式读取图片数据,然后显示到屏上。比较常用的无压缩的图片是BMP,这里就选择BMP格式的图片。

​ BMP格式图片数据有以下4部分组成:

  • 位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
  • 位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
  • 调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
  • 位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

由以上信息可以知道24位的bmp图片,数据部分就是图片像素的数据,也就是说读取24位bmp图片的数据部分不用进行任何操作,就可以直接拿来用,所以这里选择使用24bit的BMP图片,以下是我用来测试的24bit图片中的一张图片的信息:

BMP文件的位图头跟位图信息部分占BMP图片数据的前54字节,24位BMP图片的图片数据部分就是剩下的所有数据了(第55字节开始)。要对BMP图片的位图数据进行读,还需要几个关键参数,所读的图片的宽、高。还有颜色深度(bpc),因为选用了24位的图片,颜色深度算是已近知道了。

获取BMP图片信息

首先获取BMP图片的信息,定义个结构体:

typedef struct
{
uint16_t bfType;
uint32_t bfSize;
uint32_t biWidth;
uint32_t biHeight;
uint16_t biBitCount;
}BMP_HEADER;

这个结构体里面的元素分别有图片类型,图片数据大小,图片的尺寸跟颜色深度。

创建个获取该结构图的函数:

BMP_HEADER TFTBmpGetHeadInfo(uint8_t *buf)
{
BMP_HEADER bmpHead; bmpHead.bfType = (buf[0] << 8) + buf[1];
bmpHead.bfSize = (buf[5]<<24) + (buf[4]<<16) + (buf[3]<<8) + buf[2];
bmpHead.biWidth = (buf[21]<<24) + (buf[20]<<16) + (buf[19]<<8) + buf[18];
bmpHead.biHeight = (buf[25]<<24) + (buf[24]<<16) + (buf[23]<<8) + buf[22];
bmpHead.biBitCount = (buf[29] << 8) + buf[28]; return bmpHead;
}

打开图片并读取前54字节,传给函数TFTBmpGetHeadInfo,然后判断图片格式是否为BMP,颜色深度是否为24bit,不是的话,程序不再继续运行:

	bmp_fd  = open(argv[1], O_RDWR);
if(bmp_fd <0)
{
printf("open file faile\n");
} if(read(bmp_fd,buffer,54) <0)
{
printf("read file \"%s\" faile\n",argv[1]);
return -1;
} bmp_header = TFTBmpGetHeadInfo(buffer);
printf("%s's msg:\n",argv[1]);
printf("\ttype:%2x\n",bmp_header.bfType);
printf("\tsize:%d\n",bmp_header.bfSize);
printf("\tWidth:%d\n",bmp_header.biWidth);
printf("\tHeight:%d\n",bmp_header.biHeight);
printf("\tBitCount:%d\n",bmp_header.biBitCount); if(bmp_header.bfType != 0x424d)
{
printf("The picture is not bmp file\n");
return -1;
} if(bmp_header.biBitCount != 24)
{
printf("Just support 24bit bmp file\n");
return -1;
}

把图片显示到屏上

​ 我这里实现的方法是,每次读取BMP图片的一行就写到Framebuffer对应的缓存中,虽然要求read读取指定个字节,可是程序运行的时候并不是每次都能够读取所要求的字节数。所以还需要加个判断,判断每次读取的字节数是否为达到要求,没达到要求就继续读,知道达到要求为止。具体代码如下:

	int perline_size = bmp_header.biWidth * bmp_header.biBitCount /8;

	char * readed_data = (char *)malloc(perline_size);
int i=0;
int line_count=0;
int read_size = perline_size;
int read_count=0;
int read_per_line=0;
int buf_offset=0;
while((ret = read(bmp_fd,readed_data+buf_offset,read_size)) !=0)
{ if(ret == -1)
{
if(errno == EINTR)
continue;
perror("read");
break;
}else if(ret !=perline_size )
{
read_per_line += ret;
read_size = perline_size - read_per_line;
buf_offset = read_per_line;
}else
read_per_line = ret; if(read_per_line == perline_size)
{
read_size = perline_size;
buf_offset =0; read_per_line = 0;
if(bits_per_pixel ==16)
{
short pixl = 0;
if(screan_width > bmp_header.biWidth)
{
for(i=0;i<bmp_header.biWidth;i++)
{
short blue = (readed_data[i*3]>>3) & 0x001f;
short green = (readed_data[i*3+1]>>2) & 0x003f;
short red = (readed_data[i*3+2]>>3) & 0x001f; pixl = (red << 11 ) + (green << 5) + blue;
*fb_s = pixl;
fb_s ++;
}
fb_s += screan_width -bmp_header.biWidth ;
}else if(screan_width <= bmp_header.biWidth)
{
for(i=0;i<screan_width;i++)
{
short blue = (readed_data[i*3]>>3) & 0x001f;
short green = (readed_data[i*3+1]>>2) & 0x003f;
short red = (readed_data[i*3+2]>>3) & 0x001f; pixl = (red << 11 ) + (green << 5) + blue;
*fb_s = pixl;
fb_s ++;
} }
}
}
line_count++;
if(line_count >= screan_height)
break;
}

这里还涉及到一个问题,由于使用的图片是24bit的,显示屏使用的却是16位的,那就需要把24bit转成16bit了,在做之前就有想过,转换的时候是取高位还是低位呢?一开始,我是取低位的,代码为:

short blue = (readed_data[i*3]) & 0x001f;
short green = (readed_data[i*3+1]) & 0x003f;
short red = (readed_data[i*3+2]) & 0x001f; pixl = (red << 11 ) + (green << 5) + blue;
*fb_s = pixl;
fb_s ++;

显示一张有红、绿、蓝色块的图片:

明显是不正常的,红色显示有问题,修改为获取高位:

short blue = (readed_data[i*3]>>3) & 0x001f;
short green = (readed_data[i*3+1]>>2) & 0x003f;
short red = (readed_data[i*3+2]>>3) & 0x001f; pixl = (red << 11 ) + (green << 5) + blue;
*fb_s = pixl;
fb_s ++;

编译运行,结果如下图,显示看起来是正常了,从实验结果来看,转换的时候是取高位。

还有显示一些其他图片的:

显示可爱的祢豆子:

最后附上一张显示图片时输出的信息:

详细代码:show_bmp.c

参考:PNG、JPEG、BMP等几种图片格式详解(三)—— BMP

玩转STM32MP157- 在应用层中使用 fbtft的更多相关文章

  1. 玩转Openstack之Nova中的协同并发(二)

    玩转Openstack之Nova中的协同并发(二) 昨天介绍了Python中的并发处理,主要介绍了Eventlet,今天就接着谈谈Openstack中Nova对其的应用. eventlet 在nova ...

  2. 玩转Openstack之Nova中的协同并发(一)

    玩转Openstack之Nova中的协同并发(一) 前不久参加了个Opnstack的Meetup,其中有一个来自EasyStack的大大就Nova中的协同并发做了一番讲解,有所感触,本想当天就总结一下 ...

  3. [Linux] PHP程序员玩转Linux系列-Nginx中的HTTPS

    1.PHP程序员玩转Linux系列-怎么安装使用CentOS 2.PHP程序员玩转Linux系列-lnmp环境的搭建 3.PHP程序员玩转Linux系列-搭建FTP代码开发环境 4.PHP程序员玩转L ...

  4. 玩转DWZ (一)---项目中怎么使用dwz

    最近一直在找一个完全开源的web客户端框架,看到了dwz,虽然不知道到底怎么样,但还是支持国产,先学习一下.这篇文章先说一下怎么在项目里使用dwz框架. 首先先下载dwz:https://code.c ...

  5. 玩转PHP(一)---php中处理汉字字符串长度:strlen和mb_strlen

    注:本文为小编原创,如若转载,请注明出处:http://blog.csdn.net/u012116457/article/details/42536039 今天正式开始学习PHP了,不过小编一不小心就 ...

  6. 玩转ASP.NET Core中的日志组件

    简介 日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息.ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如 Console ...

  7. 没想到 Hash 冲突还能这么玩,你的服务中招了吗?

    背景 其实这个问题我之前也看到过,刚好在前几天,洪教授在某个群里分享的一个<一些有意思的攻击手段.pdf>,我觉得这个话题还是有不少人不清楚的,今天我就准备来“实战”一把,还请各位看官轻拍 ...

  8. 【第1期】腾讯云的1001种玩法征集,Ipad mini和Kindle 等你拿!(文章评审中)

    版权声明:本文由阁主的小跟班原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/695994001482226944 来源:腾云 ...

  9. 玩转STM32MP157- 使用fbtft驱动 lcd st7735r

    什么是fbtft fbtft 在 github 中的介绍是" Linux Framebuffer drivers for small TFT LCD display modules,翻译过来 ...

随机推荐

  1. Unix下 压缩和解压缩命令

    范例: .tar 解包:tar -xvf FileName.tar 打包:tar -cvf FileName.tar DirName (注:tar是打包,不是压缩!) ---------------- ...

  2. 搞清楚 硬件环境 os环境 网络环境 搞清楚测试工具 测试步骤 自己搭测试环境 自测

    1,遇事的第一反应要从变化情绪转变为做出判断.判断什么?判断这一件事对自己是否重要,是否关乎我的个人利益,是否影响我的人际关系等等等等.如果答案都是否,那就没必要着急忙慌了.如果答案是是 冷静,其实是 ...

  3. [转载]虚拟化之KVM配置

    虚拟化之KVM配置 2017-11-06 [TOC] 虚拟化:通过虚拟化工具把cpu,内存,硬盘等真实硬件资源给模拟成更少的虚拟硬件资源.进行虚拟化的好处是,最大限度的利用硬件资源.也是云计算的基础. ...

  4. [Linux-网络性能测试] -- netperf测试

    [Linux-网络性能测试] -- netperf测试 2017.01.17 14:15:54字数 1599阅读 4400 简述 Netperf是一种网络性能的测量工具(由惠普公司开发的,测试网络栈. ...

  5. Https实践

    https实践 常用端口 ssh 22 telnet 23 ftp 21 rsync 873 http 80 mysql 3306 redis 6379 https 443 dns 53 php 90 ...

  6. Otter远程调试

    Otter远程调试 环境配置: 机器 172.16.0.2 172.16.0.3 172.16.0.4 ZK FOLLOWER FOLLOWER LEADER MySQL Manager MySQL ...

  7. nosql数据库之Redis集群

    Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施(installation). Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行这些命令需要在多个 ...

  8. C++ short/int/long/long long 等数据类型大小

    表 1 整型数据类型 数据类型 字节大小 数值范围 short int (短整型) 2 字节 -32 768 〜+32 767 unsigned short int(无符号短整型) 2 字节 0 〜+ ...

  9. MyBatis 各种参数传递方式

    MyBatis参数传递方式 情况一:Mapper映射器接口方法参数只有一个且为基本类型 接口方法: public List<UserEntity> selectUserByAge(int ...

  10. 10.16-17 mailq&mail:显示邮件传输队列&发送邮件

    mailq命令 是mail queue(邮件队列)的缩写,它会显示待发送的邮件队列,显示的条目包括邮件队列ID.邮件大小.加入队列时间.邮件发送者和接受者.如果邮件进行最后一次尝试后还没有将邮件投递出 ...