引言:
入门Linux系统学习也有一点时间了,需要做一些简单项目来巩固所学知识,这个项目较为简单,通过触摸屏实现手动播放相册,自动播放相册,控制LED亮灭,控制蜂鸣器发声或,不发声。
效果图:
分析:
首先电子相册第一个要点:利用显示屏显示
可以通过在显示屏的像素点写入颜色数据
//画点函数
void display_point(int x, int y,int color)
{
if(x>=0 && x<800 && y>=0 && y<480)//防止越界
{
*(plcd + 800 * y + x) = color;
}
}
for (i=0;i<480;i++)
{
for(j=0;j<800;j++)
{
display_point(j,i,0xf0f0f0);
}
}
但是这种方法效率低下,刷新速度慢不说,还会出现一些像素点没刷到
所以我们需要mmap
mmap将磁盘上的文件映射到虚拟内存中,使得文件内容可以直接被CPU访问,而不需要复制到用户空间的缓冲区,我们称之为显存,这极大地提升了运行的效率。
//1.打开lcd屏幕文件并且映射
void open_lcd(void)
{
fd = open("/dev/fb0",O_RDWR);
if(fd == -1)
{
printf("open failed\n");
return ;
}
plcd = mmap(NULL,800*480*4,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
if(plcd == MAP_FAILED)
{
return ;
printf("mmap failed\n");
}
}
用完mmap后必须要解除映射
//2.解除映射,关闭文件
void close_lcd(void)
{
munmap(plcd,800*480*4);
close(fd);
}
第二个要点:显示图片
我们以BMP文件为例,BMP文件有着自己独有的格式,我们必须了解其格式才能得到数据,按照下图解析
解析代码:
/*
bmp_display: 在屏幕的坐标(x0,y0)处显示一张指定的bmp图片
@bmp_file: 要显示的bmp图片的文件名
@x0: 显示位置左上顶点的x轴坐标
@y0: 显示位置左上顶点的y轴坐标
返回值:
无。
*/
void bmp_display(const char * bmp_file,
int x0, int y0)
{
int fd;
fd = open(bmp_file, O_RDONLY);
if (fd == -1)
{
perror("failed to open bmp_file");
return ;
}
int width, height;
short depth;
lseek(fd, 0x12, SEEK_SET);
read(fd, &width, 4);
lseek(fd, 0x16, SEEK_SET);
read(fd, &height, 4);
lseek(fd, 0x1C, SEEK_SET);
read(fd, &depth, 2);
printf("%d x %d\n", width, height);
if ( !(depth == 24 || depth == 32))
{
printf("Sorry, Not Supported Bmp Format!\n");
close(fd);
return ;
}
int valid_bytes_per_line; //每一行有效的数据字节数
int laizi = 0; // 每一行末尾的填充的“赖子”数
int total_bytes_per_line; //每一行实际的字节数.
int total_bytes; //整个像素数组的字节数
valid_bytes_per_line = abs(width) * (depth / 8);
if (valid_bytes_per_line % 4)
{
laizi = 4 - valid_bytes_per_line % 4;
}
total_bytes_per_line = valid_bytes_per_line + laizi;
total_bytes = abs(height) * total_bytes_per_line;
unsigned char * pixel = (unsigned char*) malloc( total_bytes );
lseek(fd, 54, SEEK_SET);
read(fd, pixel, total_bytes);
// 解析像素数据,并在屏幕上显示
unsigned char a,r, g,b;
int color;
int i = 0;
int x,y;
for (y = 0; y < abs(height); y++)
{
for (x = 0; x < abs(width); x++)
{
b = pixel[i++];
g = pixel[i++];
r = pixel[i++];
if (depth == 32)
{
a = pixel[i++];
}
else
{
a = 0;
}
color = (a << 24) | (r << 16) | (g << 8) | b;
int x1, y1; //该像素点在屏幕上显示的 坐标
x1 = (width > 0) ? (x0 + x) : (x0 + abs(width) - 1 - x);
y1 = (height > 0) ? (y0 + height - 1 - y) : y0 + y;
display_point(x1, y1, color);
}
i += laizi; //跳过
}
free(pixel);
}
第三个要点:触摸
在触摸屏中点击某一点控制事件,
//获取触摸屏点击事件
void get_ts_point(ts_point* p)
{
int ret;
int fd;
fd = open("/dev/input/event0", O_RDONLY);
if (fd == -1)
{
perror("failed to open /dev/input/event0");
return ;
}
int x1 = -1, y1 = -1; //记录点击事件中第一个点的坐标
int x2, y2; //记录点出事件中最后一个点的坐标
while (1)
{
struct input_event ev;
ret = read(fd, &ev, sizeof(ev));
if (ret != sizeof(ev))
{
continue;
}
// printf("type: %x code: %x value: %x\n", ev.type, ev.code, ev.value);
//触摸屏的x轴事件
if (ev.type == EV_ABS && ev.code == ABS_X)
{
if (x1 == -1)
{
x1 = ev.value;
}
x2 = ev.value;
}
//触摸屏的y轴事件
if (ev.type == EV_ABS && ev.code == ABS_Y)
{
if (y1 == -1)
{
y1 = ev.value;
}
y2 = ev.value;
}
//触摸屏弹起事件
if ( (ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0) ||
(ev.type == EV_ABS && ev.code == ABS_PRESSURE && ev.value ==0) )
{
x1 = x1 * 800.0 / 1024;
x2 = x2 * 800.0 / 1024;
y1 = y1 * 480.0 / 600;
y2 = y2 * 480.0 / 600;
p->x = x2;
p->y = y2;
break;
}
}
close(fd);
}
上下滑动切换图片,利用当前点与下一点作比较,当Y轴移动明显大于X轴时判断向上移动还是向下移动,同理判断左右移动
//获取手指在触摸屏上的滑动方向
move_dir_t get_ts_direction(void)
{
int ret;
int fd;
move_dir_t dir = MOVE_UNKNOWN;
fd = open("/dev/input/event0", O_RDONLY);
if (fd == -1)
{
perror("failed to open /dev/input/event0");
return -1;
}
int x1 = -1, y1 = -1; //记录点击事件中第一个点的坐标
int x2, y2; //记录点出事件中最后一个点的坐标
while (1)
{
struct input_event ev;
ret = read(fd, &ev, sizeof(ev));
if (ret != sizeof(ev))
{
continue;
}
// printf("type: %x code: %x value: %x\n", ev.type, ev.code, ev.value);
//触摸屏的x轴事件
if (ev.type == EV_ABS && ev.code == ABS_X)
{
if (x1 == -1)
{
x1 = ev.value;
}
x2 = ev.value;
}
//触摸屏的y轴事件
if (ev.type == EV_ABS && ev.code == ABS_Y)
{
if (y1 == -1)
{
y1 = ev.value;
}
y2 = ev.value;
}
//触摸屏弹起事件
if ( (ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0) ||
(ev.type == EV_ABS && ev.code == ABS_PRESSURE && ev.value ==0) )
{
int delt_x = abs(x2 - x1);
int delt_y = abs(y2 - y1);
if (delt_x >= 2 * delt_y)
{
if (x2 > x1)
{
dir = MOVE_RIGHT;
}
else
{
dir = MOVE_LEFT;
}
break;
}
else if (delt_y >= 2 * delt_x)
{
if (y2 > y1)
{
dir = MOVE_DOWN;
}
else
{
dir = MOVE_UP;
}
break;
}
else
{
//方向不明,请继续
x1 = -1;
y1 = -1;
}
}
}
close(fd);
return dir;
}
最后将所有功能结合
int main()
{
open_lcd();
int i = 0;
int k,j;
char * bmp[3] = {"./1.bmp","./2.bmp","./3.bmp"};
ts_point *p = (ts_point*) malloc(sizeof(p));
while(1)
{
get_ts_point(p);
if(p->x<200&&p->x>=0&&p->y>=0&&p->y<480)
{
bmp_display("./1.bmp",0,0);
sleep(2);
bmp_display("./2.bmp",0,0);
sleep(2);
bmp_display("./3.bmp",0,0);
sleep(2);
bmp_display("./1.bmp",0,0);
sleep(2);
bmp_display("./2.bmp",0,0);
}
if(p->x<400&&p->x>=200&&p->y>=0&&p->y<480)
{
bmp_display(bmp[i],0,0);
move_dir_t dir = get_ts_direction();
if(dir == MOVE_DOWN)
{
if(i == 0)
{
i = 2;
}
else
{
i--;
}
bmp_display(bmp[i],0,0);
}
if(dir == MOVE_UP)
{
if(i == 2)
{
i = 0;
}
else
{
i++;
}
bmp_display(bmp[i],0,0);
}
}
//led
if(p->x<600&&p->x>=400&&p->y>=0&&p->y<480)
{
int fd = open("/sys/kernel/gec_ctrl/led_all",O_RDWR);
if(fd == -1)
{
printf("open failed led\n");
return -1;
}
k = !k;
write(fd,&k,4);
close(fd);
}
//beep
if(p->x<800&&p->x>=600&&p->y>=0&&p->y<480)
{
int fd = open("/sys/kernel/gec_ctrl/beep",O_RDWR);
if(fd == -1)
{
printf("open failed beep\n");
return -1;
}
j = !j;
write(fd,&j,4);
close(fd);
}
}
close_lcd();
return 0;
}
效果视频:
Linux--电子相册视频