关于GEC6818屏幕的驱动方法(显示屏、触摸屏)
GEC6818的开发板有一个480*800的显示屏,那么在我们编写程序时,如何驱动这块屏幕?
Linux下,一切皆文件,开发板的屏幕在系统中也就是相当于一个文件,对这个文件的内容进行操作,便可改变屏幕显示的内容。那么要怎么做呢?
首先说说显示屏:
文件位于/dev/fb0(路径仅供参考),使用open函数打开(需要包含sys/types.h、sys/stat.h、fcntl.h),对其进行操作就可以啦。
那么先简单写一个初始化屏幕的函数:
int *plcd;//全局变量,plcd是操作屏幕文件的指针
int lcd_init()
{
int fd=open("/dev/fb0",O_RDWR);//获取帧缓冲的文件描述符
if(fd==-1)//如果打开失败打印错误信息
{
perror("screen open error:");
return 0;
}
plcd=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//将屏幕文件映射到内存中,加快速度
return fd;
}
这里使用的perror函数需要包含errno.h和stdio.h,作用是将自定义的信息+上一个函数发生错误的原因输出,这里在屏幕文件打开失败时调用,可以帮助抓虫(
关于mmap函数(需要包含sys/mman.h)的用法这里不做讲解,其中第二个参数,映射到内存的文件长度为800*480*4,因为文件中存储的像素数据是16进制形式的int类型数据,因此文件大小就是分辨率*sizeof(int)。
有始有终,就顺便再把关闭屏幕文件的函数写好:
void lcd_uninit(int fd)
{
close(fd);//关闭帧缓冲文件
munmap(plcd,800*480*4);//解映射
}
有了初始化和关闭,就可以试着操作屏幕文件啦,我们先简单画一个像素点:
/*在屏幕上的(x,y)处写一个颜色为color的像素点*/
void lcd_draw_point(int x,int y,int color)
{
if(x>=0 && x<800 && y>=0 && y<480)//判断传入坐标是否越界
*(plcd+800*y+x)=color;//画点
}
(x,y)表示上面有y行像素点,前面有x个像素点,color就是透明度(直接00即可)+颜色代码,例如0x00ff0000(红),0x0000ff00(绿),0x000000ff(蓝)。
这样我们就可以对这块屏幕为所欲为♂了,比如说在屏幕上绘制一个圆:
/*以(x,y)为圆心,r为半径画color颜色的圆*/
void lcd_draw_circle(int x,int y,int r,int color)
{
int i,j;
for(j=0;j<480;j++)
{
for(i=0;i<800;i++)
{
if((i-x)*(i-x)+(j-y)*(j-y)<=r*r)//(i,j)这个点满足圆内的关系,就画圆
{
lcd_draw_point(i,j,color);
}
}
}
}
如果要绘制其他图形,只要修改if中判断的公式就好了,复杂一些的图形可能还要稍作修改,这里就不扩展啦qwq
但显然,这样还不够,那么重中之重来了,如何在屏幕上显示图片?
这里我们选择bmp格式的图片,因为其中存储的就直接是rgb数据,不需要我们做额外的处理,比较方便。
bmp文件的前54字节存储着图片信息,后面才是像素数据,因此我们在读取时需要偏移光标,但我们又需要获取图片的分辨率、色深数据,所以首先光标偏移到18字节处,读4字节,这是图片的横向分辨率(宽),然后偏移到22字节处,读4字节,这是图片的纵向分辨率(高),然后偏移到28字节处,读2字节,这是图片的色深。有关前54字节存储的其他的信息是什么,还请自行上网查阅相关资料,这里用到的信息就这些~
先写一个将屏幕初始化为白色的函数,以保证图片显示的效果:
void lcd_clear(int color)
{
int i,j;
for(j=0;j<480;j++)
{
for(i=0;i<800;i++)
{
lcd_draw_point(i,j,color);
}
}
}
这里引用一篇讲解bmp行字节数的文章:https://blog.csdn.net/u012313335/article/details/80432155
因此在下方函数中,我们不能忽视因为字节对齐所产生的的无效字节,所以在定义临时存放像素信息的数组时,应加上无效字节的大小,并在往屏幕写入像素信息时,忽略掉每行的无效字节。
void load_img(char *path,int x0,int y0)
{/*以(x,y)为原点(图片左上角)绘制图片(传进来的path为图片路径)*/
lcd_clear(0x00ffffff);//清屏
int bmp_fd=open(path,O_RDWR);
if(bmp_fd==-1)
{
perror("");
return;
}
int w,h;//宽,高
short depth;//色深
//光标偏移到18字节处,读宽
lseek(bmp_fd,18,SEEK_SET);
read(bmp_fd,&w,4);
//此时光标来到22字节处,读高
read(bmp_fd,&h,4);
//光标偏移到28字节处,读色深
lseek(bmp_fd,28,SEEK_SET);
read(bmp_fd,&depth,2);
int laizi=0;//表示无效字节数
if((w*depth/8)%4)
{
laizi=4-(w*depth/8)%4;
}
//总字节数
int total=(w*depth/8+laizi)*h;
unsigned char pix[total];//定义像素数组
lseek(bmp_fd,54,SEEK_SET);//光标移动到54字节处
read(bmp_fd,pix,total);//读像素信息,存入像素数组
int i,j,num=0;
unsigned char a=0,r,g,b;
for(j=0;j0?x0+i:abs(w)-1-i+x0;
y=h<0?y0+j:abs(h)-1-j+y0;
lcd_draw_point(x,y,color);
}
num+=laizi;//跳过每行末尾的laizi
}
}
关于int color = b|g<<8|r<<16|a<<24;因为屏幕文件中的argb数据和bmp中存储的bgra是相反的,因此需要通过位移来修正。abs函数为求绝对值函数,保证在读取到负的(翻转)分辨率信息时不会出错。
整理下代码,交叉编译将程序传到板子上运行,效果如下:
五月织姬~快去单推吧(
接下来讲一下触摸屏的驱动:
刚刚我们是打开显示屏的文件进行操作,同理,我们这次需要打开触摸屏的文件:/dev/input/event0
Linux系统IO库里提供了input.h,其中有一个叫input_event的结构体,内容对应触摸屏文件,我们只要不停的读取event0,匹配上input_event的大小时处理数据即可,我们需要用到结构体中的type、code、value。
当type==EV_ABS时为触摸屏绝对坐标事件,此时有code==ABS_X或code==ABS_Y,当code==ABS_X读取到value的值为x轴的坐标,以此类推。
当type==EV_KEY时为键盘事件,此时当value为0时是手指触摸、离开触摸屏的瞬间,此时对获取到的坐标进行运算就可以判断是上滑、下滑、左滑、右滑、点击操作。
关于input_event结构体的详解,可自行查找资料,这里不做赘述。
//返回:1 向上,2 向下,3 向左,4 向右,-1 点击
int get_touchscreen_index(int *x,int *y)
{
int ts_fd = open("/dev/input/event0", O_RDONLY);
if(ts_fd == -1)
{
perror("open event error:");
}
struct input_event ev;
int i, j, x1, y1;
int x0 = -1, y0 = -1;
while(1)
{
int r = read(ts_fd, &ev, sizeof(ev));
if(r != sizeof(ev))
{
continue;
}
if(ev.type == EV_ABS && ev.code == ABS_X)
{
if(x0 == -1)
{
x0 = ev.value;
}
x1 = ev.value;
*x = ev.value;
}
if(ev.type == EV_ABS && ev.code == ABS_Y)
{
if(y0 == -1)
{
y0 = ev.value;
}
y1 = ev.value;
*y = ev.value;
}
if(ev.type == EV_KEY && ev.value == 0)
{
int i = 0;
if(abs(x0-x1)>abs(y0-y1))
{
if(x0-x1>0)
{
i = 3;
printf("left\n");
}
else
{
i = 4;
printf("right\n");
}
}
else if(abs(x0-x1)0)
{
i = 1;
printf("up\n");
}
else
{
i = 2;
printf("down\n");
}
}
else
{
i = -1;
printf("click\n");
}
return i;
}
}
close(ts_fd);
}
printf用于抓虫,方便判断触摸是否工作正常,当需要用到触摸屏时,将其放到死循环中不停的读取返回的信息,就可以获取到触摸屏的状态,以及点击的位置坐标信息等。
那么接下来写一个简单的程序综合一下上面的成果~
int main()
{
float x,y;
int fd=lcd_init();
lcd_clear(0x00ffffff);
while(1)
{
int z=get_touchscreen_index(&x,&y);
if((x<400*1.28)&&(y<240*1.28)&&z==-1)
{
load_img("./1.bmp",0,0);
printf("loadimg1\n");
}
else if((x>400*1.28)&&(y>240*1.28)&&z==-1)
{
load_img("./2.bmp",0,0);
printf("loadimg2\n");
}
}
lcd_uninit(fd);
}
因为触摸屏的分辨率是1024*600,和显示屏不一样,因此在对触摸精度要求不高的情况下将坐标乘1.28即可
当点击左半边的屏幕时显示图片1,右边屏幕时显示图片2,同时打印信息到终端。
\五月/\五月/\五月/\五月/
\五月/\五月/\五月/\五月/
可以看到终端输出的debug信息,测试完成~
到这里,我们算是征服6818的这块屏幕啦~
- 感谢你赐予我前进的力量