以前看到有些同学的项目,在ARM板上接着的LCD屏幕上播放动画,效果十分酷炫。后来自己也实现了,其基本无非就是控制LCD屏幕显示图片。以下就以24位图为例,介绍让LCD显示图片的方法。24位图是指由分别用8位二进制来表示图像B、G、R值的图片,亦即每个像素点都由24位组成。
环境:ARM Cortex开发板(预装有Linux系统),PC(ubuntu)
首先准备一张图片,可以菜单栏打开画图工具,调整图片大小至适合LCD尺寸,由于我手上的是800*480的LCD,故选择800*480的画布作图,并保存为24位图。
示例图片:
要注意的是,LCD屏幕显示是32位的,即αRGB(多了一个透明度),而我们所要显示的bmp位图是24位且为BGR的,这么一来,我们至少就要解决两个问题:
一个是思考 *如何将24位扩展到32位,另一个问题则是**如何将BGR调转顺序为RGB。
按计算所用位图的文件大小本应该是800*480*4=1152000字节的,但是由于图片实际上还存储着文件头信息,所以实际大小要再多出54个字节,这是读取图片时需要注意的。文件头信息跟我们需要显示的像素点无关,故在读取图片信息并显示时我们需要思考***如何只截取像素点信息。
另外,LCD的显示与图片的存储是上下颠倒的,所以还要****将图片上下调转再显示。
总体来说,实现思路如下:
①访问图片及LCD
②处理图片,包括截取有用的数据、扩展位、调换RGB存储顺序、上下调转
③将图片写入LCD
④回收图片及屏幕资源
首先从IO目录操作开始:(打开目录 -> 进入目录 -> 读取目录 -> 关闭目录)
需要调用到的IO函数:
目录IO
opendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
closedir
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
chdir
#include <unistd.h>
int chdir(const char *path);
readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
rewinddir
#include <sys/types.h>
#include <dirent.h>
void rewinddir(DIR *dp);
代码如下:
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc!=2)
{
printf("Usage: ./project dir\n"); //若输入参数错误,提示用法
return -1;
}
DIR *dp = opendir(argv[1]); //打开目录
if(dp == NULL) //打印出错信息便于调试
{
printf("opendir error!\n");
return -1;
}
int ret = chdir(argv[1]); //进入目录,类似于cd,注意chdir的参数是路径而不是目录指针!!
if(ret == -1) //打印出错信息便于调试
{
printf("chdir error!\n");
return -1;
}
struct dirent *ep = NULL;
while(1)
{
ep = readdir(dp); //循环读取目录项
if(ep == NULL)
{
break; //读取完成,退出循环
}
if(ep->d_name[0] == '.')
{
continue; //当前目录(.)以及上一级目录(..)不是我们想要的目录项,故跳过
}
printf("%s\n", ep->d_name); //打印出需要的目录项(即我们要显示的图片)
}
closedir(dp); //关闭目录,回收资源
return 0;
}
其次是尝试显示bmp图片:(设备资源使用系统IO操作,普通文件资源使用标准IO )
需要用到的IO函数有:
系统IO
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
close
#include <unistd.h>
int close(int fd);
write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
标准IO
fopen
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
fclose
#include <stdio.h>
int fclose(FILE *fp);
fseek
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int lcd;
int ret;
FILE *fp = NULL;
char bmp_buf[800*480*3];
int lcd_buf[800*480];
lcd = open("/dev/fb0",O_RDWR); //以可读写方式打开lcd(系统IO)
if(lcd < 0)
{
printf("open fb0 error!\n"); //打印出错信息便于调试
return -1;
}
fp = fopen("1.bmp","r+"); //以可读写方式打开图片(标准IO)
if(fp == NULL)
{
printf("fopen 1.bmp error!\n"); //打印出错信息便于调试
return -1;
}
fseek(fp,54,SEEK_SET); //***跳过图片头信息前54个字节(标准IO)
ret = fread(bmp_buf,sizeof(bmp_buf),1,fp);//读取图片信息(标准IO)
if(ret != 1)
{
printf("fread error!\n"); //打印出错信息便于调试
return -1;
}
int i,j,t;
for(i = 0; i < 800*480; i++)
{
//*&**将每3个char型(共24位)通过移位、位或操作 调整RGB顺序后再存储到 每个int型(32位)中
lcd_buf[i] = (bmp_buf[3*i])|(bmp_buf[3*i+1]<<8)|(bmp_buf[3*i+2]<<16);
}
for(j = 0; j <800;j++)
for(i =0;i<239;i++)
{
//****将图片上下调转
t = lcd_buf[800*i+j];
lcd_buf[800*i+j]=lcd_buf[(479-i)*800+j];
lcd_buf[(479-i)*800+j]=t;
}
write(lcd,lcd_buf,800*480*4); //将处理完成的图片写入lcd(系统IO)
close(lcd); //关闭lcd,回收资源(系统IO)
fclose(fp); //关闭图片,回收资源(标准IO)
return 0;
}
接下来是触屏部分,我们设定点击屏幕左半边为上一张,右半边为下一张。事件的结构体在头文件linux/input.h中已有定义,我们通过读取这个结构体中的信息来分析当前发生的事件,如是否触摸中、当前触摸坐标等。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/input.h> //定义了事件结构体的头文件
int main()
{
struct input_event buf; // 定义事件变量
int x;
int ts = open("/dev/event0",O_RDWR); //打开触摸屏节点
if(ts < 0)
{
printf("open ts error!\n"); //打印出错信息便于调试
}
while(1)
{
read(ts,&buf,sizeof(buf)); //循环不断读取触摸屏信息
/*
if(buf.type == EV_ABS && buf.code == ABS_PRESSURE && buf.value == 0) //通过读取对触摸屏的压力判断是否触摸中
{
printf("you leave LCD!\n");
}
*/
if(buf.type == EV_ABS && buf.code == ABS_X) //因为我们设计通过判定触摸左右边屏幕进行控制,所以这里读取x坐标
{
//printf("x = %d\n",buf.value);
x = buf.value;
}
if(buf.type == EV_ABS && buf.code == ABS_PRESSURE && buf.value == 0) //当且仅当离开触摸屏时,判断离开前的x坐标
{
if(x<400)
{
printf("left!\n");
}
if(x>400)
{
printf("right!\n");
}
}
}
close(ts);
return 0;
}
那么对以上代码进行整合修改,即可实现触控幻灯片:
#include <dirent.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdlib.h>
int show_bmp(char *name)
{
int lcd;
int ret;
FILE *fp = NULL;
char bmp_buf[800*480*3];
int lcd_buf[800*480];
lcd = open("/dev/fb0",O_RDWR);
if(lcd < 0)
{
printf("open fb0 error!\n");
return -1;
}
fp = fopen(name,"r+");
if(fp == NULL)
{
printf("fopen error!\n");
return -1;
}
fseek(fp,54,SEEK_SET);
ret = fread(bmp_buf,sizeof(bmp_buf),1,fp);
if(ret != 1)
{
printf("fread error!\n");
return -1;
}
int i,j,t;
for(i = 0; i < 800*480; i++)
{
lcd_buf[i] = (bmp_buf[3*i])|(bmp_buf[3*i+1]<<8)|(bmp_buf[3*i+2]<<16);
}
for(j = 0; j <800;j++)
for(i =0;i<239;i++)
{
t = lcd_buf[800*i+j];
lcd_buf[800*i+j]=lcd_buf[(479-i)*800+j];
lcd_buf[(479-i)*800+j]=t;
}
write(lcd,lcd_buf,800*480*4);
close(lcd);
fclose(fp);
}
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("arg error!\n");
printf("Usage: ./test $path\n");
return -1;
}
int count = 0;
int ret, i;
struct dirent *ep = NULL;
struct input_event buf;
int x;
DIR * dp = opendir(argv[1]);
if(dp == NULL)
{
printf("opendir error!\n");
return -1;
}
ret = chdir(argv[1]);
if(ret == -1)
{
printf("chdir error!\n");
}
for(count = 0;(ep = readdir(dp))!=NULL;count++)
{
}
printf("Total_count = %d\n",count-2);
char *pic_name[count-2];
rewinddir(dp);
for(i=0;i<count-2;)
{
ep = readdir(dp);
if(ep->d_name[0] == '.')
{
continue;
}
pic_name[i++] = ep->d_name;
}
//显示第一张
show_bmp(pic_name[i=0]);
while(1)
{
int ts = open("/dev/event0",O_RDWR);
if(ts < 0)
{
printf("open ts error!\n");
}
while(1)
{
read(ts,&buf,sizeof(buf));
if(buf.type == EV_ABS && buf.code == ABS_X)
{
//printf("x = %d\n",buf.value);
x = buf.value;
}
if(buf.type == EV_ABS && buf.code == ABS_PRESSURE && buf.value == 0)
{
if(x<400 && i>0)
{
printf("previous\n");
show_bmp(pic_name[--i]);
printf("%s\n",pic_name[i]);
}
if(x>400 && i<count-3)
{
printf("next\n");
show_bmp(pic_name[++i]);
printf("%s\n",pic_name[i]);
}
}
}
close(ts);
}
closedir(dp);
return 0;
}