小白笔记

目录

前言

一、lcd.c代码

lcd屏幕初始化

在lcd屏幕上任意一点显示颜色

关闭lcd屏幕

lcd.h

二、bmp.c代码

bmp格式

bmp图片读取

bmp.h

三、work.c部分源码

触摸屏

2048方块移动算法

在随机位置上生成2或者4

绘出4*4矩阵

四、流程框图和界面功能描述


前言

板子是6818粤嵌开发板,为800*480,触摸屏幕大小为1024*600

需要自己配好相应数字图片,将其图片命名为数字.bmp的形式

在VMware虚拟机下进行arm-linux-gcc bla进行编译,然后传输可执行文件和图片至开发板中,最后在开发板上运行。


一、lcd.c代码

lcd屏幕初始化

在linux系统中打开文件为open(“文件名/位置”,权限),其中权限包括只读,只写还有可读可写。open打开会得到一个返回值,打开失败则返回-1,成功打开则返回正数,所以判断是否打开成功,我们需要进行判断。

打开屏幕后,我们需要用write来进行写入像素(rgb),但是用write来进行操作的话,中间会浪费太多时间,导致数据无法一次性快速地完整被写入到屏幕中(多次运行可解决)。

所以我们需要用到mmap函数,功能为将地址映射出来,就比write函数省去了很多中间过程,可以让数据一次性完整迅速地写入到屏幕中。

其中函数原型为mmap( void* address , size_t length , int port , int flag , int fd , off_t off_set )。

其参数分别为

映射区首地址,一般存放NULL

映射区大小,系统会自动调整为4的整数倍,不能为0,一般文件多大就填多大。

映射权限,映射区必须要有权限,我们要可读也要可写,所以为PROT_READ | PROT_WRITE

标志位参数,判断是否为私有或者公有,修改内存数据的话,share的会同步要硬盘中,而private不会。

要映射的文件,为我们打开文件的返回值。

映射文件的偏移量,表示从该文件的哪里开始进行映射。必须为4的整数倍,一般设置成0。

将其用函数来封装,代码如下:

// lcd屏幕的初始化// 打开屏幕int lcd_init(){lcd_fd = open( "/dev/fb0" , O_RDWR );// 判断打开是否成功if( lcd_fd == -1 ){// 返回打开失败的原因perror("open failed!");return -1;}// 内存映射// MaxSize = 800 * 480plcd = mmap( NULL , MaxSize*4 , PROT_READ | PROT_WRITE , MAP_SHARED , lcd_fd , 0 );return 0;}

在lcd屏幕上任意一点显示颜色

我们要在lcd屏幕上显示颜色,首先需要知道其坐标

屏幕左上角为( 0 , 0 ),右下角为(lcd_width-1 , lcd_height-1 )。

// 因为是从0开始的,所以最后一个是n-1

其中屏幕上我们用到的是指针,所以随机一点位置为( x , y )的话,那么其地址就为

首地址+x+y*lcd_width.

首地址为我们映射所得得返回值plcd,所以随机一点地址为plcd+x+y*lcd_width.

然后我们需要知道将该点显示的颜色color,我们就可以将其进行赋值,就可以完成我们对该点显示颜色。

将其用函数封装,代码如下:

// 在任意的点上 显示任意的一个颜色void display_point(int x, int y, int color){if( x >= 0 && x = 0 && y < 480 )*(plcd + x + y*800) = color;}

关闭lcd屏幕

在结束操作后我们需要记得关闭文件。

关闭文件需要用到我们open的返回值lcd_fd,来确定文件,然后我们用close函数来进行关闭文件。

当然我们还用到了映射,我们也要取消掉映射。取消映射为munmap函数

函数原型为munmap( void* addr , size_t length )

第一个为文件,第二个为字节大小。

如果返回值为-1,则代表关闭失败,则关闭成功。

将其用函数封装,代码如下:

// 关闭屏幕 int lcd_close(){// 关闭文件 close(lcd_fd);// 解除映射// Maxsize = 800 * 480int res = munmap( plcd , MaxSize*4 );if( res == -1 ){perror("Removal failed!");return -1;}return 0;}

lcd.h

最后用.c文件保存,然后创建.h文件,我们就可以需要打开屏幕的时候直接调用头文件和函数即可。

#ifndef __LCD_H__#define __LCD_H__// lcd屏幕的初始化int lcd_init();// 在任意的点上 显示任意的一个颜色void display_point(int x, int y, int color);// 关闭屏幕 int lcd_close();#endif

二、bmp.c代码

bmp格式

bmp文件时有固定格式的,我们需要了解其格式,才能准确的读取到图片的数据

/** 00-01 文件标识,为字母ASCII码"BM"2byte * 02-05 文件大小4byte * 06-09 位图文件保留字,必须为0 4byte* 0A-0D 文件开始到位图数据开始之间的偏移量4byte* 0E-11 图像描述信息块的大小,常为28H4byte* 12-15 图片高度4byte * 16-19 图片宽度 4byte * 1A-1B 图像plane总数,恒为 12byte* 1C-1D 记录颜色的位数2byte* 1E-21 数据压缩方式2byte* 22-25 图像区数据大小,必须为4的倍数4byte* 26-29 水平像素点个数(在设备无关位图中,00H)4byte * 2A-2D 垂直像素点个数 (在设备无关位图中,00H)4byte* 2E-31 图像所用颜色数(不用,固定为0)4byte* 32-35 重要颜色数(不用,固定为0)4byte **/

bmp图片读取

首先打开图片,我们需要知道图片的文件名或者文件路径,当然我们一般需要跟代码存放到一起,直接给文件名来进行打开。然后我们需要知道图片打开的起始位置。

所以我们图片读取需要知道三个参数,文件名,起始坐标x,起始坐标y

我们再根据bmp的格式来进行一一读取,我们就可以得到图片的宽度,高度,色深和像素数组等数据,最后运用lcd屏幕画点的函数来进行描绘图片。

我们读取的时候需要读取到特定的一些位置的数据,我们这个时候可以用lseek函数来改变我们光标位置,用read函数来选择我们读取数据的大小。

我们得到的图片宽度高度有的时候可能是正值也有可能是负值。

比如高度,正值就是从上到下,负值就是从下到上的像素点,所以需要进行正负的判断来改变描绘。

最后将打开图片封装成函数,代码如下:

// 显示图片void show_picture(char * pathname ,int x ,int y){int fd = open(pathname,O_RDONLY);if(fd == -1){perror("open error\n");return ;}int width,height;short depth;unsigned char buf[4] ;//读取宽度lseek(fd,0x12,SEEK_SET);read(fd,buf,4);width = buf[3]<<24 | buf[2]<< 16 | buf[1] << 8 | buf[0];//读取高度read(fd,buf,4);height= buf[3]<<24 | buf[2]<< 16 | buf[1] << 8 | buf[0];//读取色深lseek(fd,0x1c,SEEK_SET);read(fd,buf,2);depth = buf[1] << 8| buf[0];//像素数组 int line_valid_bytes = abs(width) * depth / 8 ; //一行本有的有效字节int laizi = 0; //填充字节, 文件大小为 54 + wedth*height + height*n 为4的倍数 if( (line_valid_bytes % 4) !=0 ) laizi =4 - line_valid_bytes%4;int line_bytes = line_valid_bytes + laizi; //一行所有的字节数int total_bytes = line_bytes * abs(height); //整个像素数组的大小unsigned char * p1= malloc(total_bytes); // 获取动态数组// 像素为54字节之后,所以调到54读完lseek(fd,54,SEEK_SET);read(fd,p1,total_bytes);// 画点,画图unsigned char a ,r ,g, b ;int i = 0;//用来做指针运动的int x0=0,y0=0; //用来循环计数int color;for( y0 = 0 ; y0 < abs(height) ; y0 ++ ) { // 列 for( x0 = 0 ; x0 < abs(width) ; x0 ++ ) { // 行 //一字节一字节读入RGBA// 读取后,图片顺序会反过来,需要调整b = p1[i++];g = p1[i++];r = p1[i++];if(depth == 32){a=p1[i++];}if(depth == 24){a = 0;}color = a << 24 | r << 16 | g <0?x+x0:abs(width)+x-1-x0, height>0? y+height-1-y0 : y+y0,color);}// 一行弄完需要进行填充过滤 i = i +laizi;}// 释放指针 free(p1);close(fd);}

bmp.h

最后用.c文件保存,然后创建.h文件,我们就可以需要显示图片的时候直接调用头文件和函数即可。

#ifndef __BMP_H__#define __BMP_H__// 显示图片void show_picture( char* pathname , int x , int y );#endif

三、work.c部分源码

触摸屏

我们需要知道触摸屏的文件为”/dev/input/event0″,然后我们无需更改,所以我们就就可以权限赋为只读。然后会有一个结构体struct input_event

其中有type种类,判断接触,有code值判断类型,有value值,根据type和code类型有不同的意思,如果是按压,则是压力值,滑动则是坐标值。

因为滑动过程中的坐标是不断在改变的,所以我们需要一直读入,设置x1,y1表示起始点坐标,x2,y2表示滑动的给出的坐标。如果没有读全则一直读入数据,然后我们不滑动离开的话,我们需要对结构体的值进行判断,滑动则更新数值,如果这个时候type类型为EV_ABS并且code为ABS_PRESSURE时,为按压,并且压力值(value)为0时,则可以视作离开,或者是type类型为EV_KEY并且code为BTN_TOUCH时,为滑动,且value为0时,也可以视作离开,这个时候我们就需要来进行判断我们滑动的方向了。

用相对位置来进行判定,若在x方向走的路程比在y方向走的路程两倍多,则是x方向上的移动,若在y方向走的路程比在x方向走的路程两倍多,则是y方向上的移动。

这个时候就可能会有误差,触击得到的相对位置也会在两倍以上,所以我们需要增加一个误差判定,我设置的x方向至少40,y方向上至少32。

判断滑动方向我们就可以关闭触屏文件,进行滑动操作的功能了。

下面是触摸屏代码:

// 移动 int get_movement(){int fd = open("/dev/input/event0", O_RDONLY);int res;if (fd == -1) {printf("open /dev/event0 failed\n");return -1; }int x1 = -1, y1 = -1; // 接触时候的坐标点int x2, y2; // 离开后的坐标点struct input_event ev;while( 1 ){res = read( fd , &ev, sizeof(ev) );if( res != sizeof(ev) ) continue;if( ev.type == EV_ABS && ev.code == ABS_X ) {if( x1 == -1 ) x1 = ev.value;x2 = ev.value;} if (ev.type == EV_ABS && ev.code == ABS_Y) {if (y1 == -1) y1 = ev.value;y2 = ev.value;}if( (ev.type == EV_ABS && ev.code == ABS_PRESSURE && ev.value == 0 ) || ( ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0 ) ) {printf("x2 = %d\ty2 = %d\n",x2,y2);printf("flag = %d\n",flag);printf("\n\n\n\n");if( flag ) return 0;printf("x1 = %d\ty1 = %d\nx2 = %d\ty2 = %d\n",x1,y1,x2,y2);int opposite_x = abs(x2-x1); // 左右 int opposite_y = abs(y2-y1); // 上下 printf("opposite_x = %d\topposite_y = %d\n",opposite_x,opposite_y);if( x2 >= 830 && y2 < 80 && opposite_x <= 40 && opposite_y <= 32 ) {flag = 1;return 0;}if( opposite_x <= 40 && opposite_y  2 * opposite_y ){ // 左右移动 if( x2 > x1 ){ // 方块向右边移动 close(fd);return MOVE_RIGHT;}else{ // 方块向左边移动 close(fd);return MOVE_LEFT;}} else if( opposite_x  y1 ){ // 方块向下移动 close(fd);return MOVE_DOWN;}else{ // 方块向上移动 close(fd);return MOVE_UP;}}else x1 = -1, y1 = -1;}}close(fd);}

2048方块移动算法

2048是一个4*4的矩阵,滑动可以将方块移动并且合并,然后结束一次滑动后可以生成一个新的数字,可能是2或者4。

我们将方块移动,是需要先进行相加,再最后移动。

以向上滑动为例子,需要在每一列进行算(如果向右,则在每一行进行计算)

首先我们设置pos1和pos2,我们需要再这一列找到一个数字,将其位置赋值给pos1,然后再在这数字之后进行寻找下一个数字,第一个有数的数字如果跟pos1位置数字相同,则将pos1位置的数翻倍,且让pos2位置的数变为0。然后pos1变为pos2+1。这样我们就可以将两个数字相加。

最后移动,我们需要在顶端为pos1,然后让pos2进行寻找数字,如果pos1 != pos2的话,则交换两位置的数字,然后pos1向下移动一个位置,pos2继续按顺序寻找,这样我们就可以完成数字的移动了。

以向上滑动的代码为例子:

// 向上移动void move_up(){int i, j;int x, y;for (i = 0; i < ITEM_NUM; i++) {for (x = 0; x < ITEM_NUM; ) {if (matrix_2048[x][i] != 0) {for (y = x + 1; y = ITEM_NUM) break;}else x++;} x = 0;for (y = 0; y < ITEM_NUM; y++) {if (matrix_2048[y][i] != 0) { if (x != y) {matrix_2048[x][i] = matrix_2048[y][i];matrix_2048[y][i] = 0;}x++;}}}}

在随机位置上生成2或者4

滑动完之后,方格会随机生成2或者4,我们需要判断滑动完之后哪些空格是没有数字的,我们需要进行统计,然后用rand函数来随机到一个位置上。

我们生成2或者4的话就用res = rand() % 2,0为2,1为4就可以了。

我们使用rand()函数需要生成随机数种子,即需要在最开始写上srand(time(NULL))

代码如下:

int get_zeronum(){int i, j, n = 0;for( i = 0 ; i < ITEM_NUM ; i ++ )for( j = 0 ; j  [0,zero_Num) 整数int i, j;for( i = 0 ; i < ITEM_NUM ; i ++ )for( j = 0 ; j < ITEM_NUM ; j ++ )sum += matrix_2048[i][j];for( i = 0 ; i < ITEM_NUM ; i ++ )for( j = 0 ; j =2200 ) {int res = rand()%100000;if( res == 99999 ) {matrix_2048[i][j] = 16384;return;}}int res = rand()%3;if( res < 2 ) matrix_2048[i][j] = 2;else matrix_2048[i][j] = 4;return;}else n++;}}

绘出4*4矩阵

这个比较简单,就用数字图片来进行绘画就可以了。

代码如下:

void LCD_draw_matrix(){int i, j;int x0, y0;for( i = 0 ; i < ITEM_NUM ; i ++ ){for( j = 0 ; j < ITEM_NUM ; j ++ ){x0 = MATRIX_X0 + ( ITEM_WIDTH + BLACK_LINE ) * j + 5;y0 = MATRIX_Y0 + ( ITEM_HEIGHT + BLACK_LINE ) * i + 5;if( matrix_2048[i][j] == 0 ) {int k, z;for( k = 0 ; k < ITEM_WIDTH ; k ++ )for( z = 0 ; z < ITEM_HEIGHT ; z ++ )display_point( x0 + k , y0 + z , 0x4682b4 );}else{// 加载那个数字的图片 char pathname[32];sprintf( pathname , "%d.bmp" , matrix_2048[i][j] );show_picture( pathname , x0 , y0 );}}}}

四、流程框图和界面功能描述

接下来就是在此基础上添加一些功能了,下面是流程图和界面功能介绍

2048游戏项目功能