stm32贪吃蛇tftlcd显示实验

最近又在学习LCD,以前也看过一点LCD,但看lcd.c源文件2000多行,说来也惭愧,当时就放弃了,就想先学其他的吧,但是后来发现不学不行呀,LCD太重要了,LCD是输出设配,好多实验都是要显示出来才能看到效果的,哎没办法,后来只好硬着头皮去看啦,咦,我发现好像好多事可以不需要的,厂家为了能兼容更多的LCD,把很多液晶控制芯片都写了进去,由于我用到LCD的控制芯片是ILI9331,我就把其他无用的都删掉了,最后只有六七百行了,哈哈哈,好开心啊,看着心情都好些。熟悉了一些基本函数之后,我想可不可以写一个贪吃蛇的小游戏,说着容易做起来可就没那么简单了,从早到晚做了一整天,才勉强算一个贪吃蛇小游戏,还是有很多的小BUG,希望大家不吝指正。闲话少说,下面直接看游戏。

首先游戏中的点要是随机的,所以要先产生一些随机的数,在C语言中有函数srand和rand()配合使用产生伪随机数序列。rand函数在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数。如果系统提供的种子没有变化,每次调用rand函数生成的伪随机数序列都是一样的,只要用一直变化的数做种子就能能实现真正的随机,通常可以利用系统时间来改变系统的种子值,我看好多博客是这样写的,srand(time(0)),我试了发现不行,LCD都不显示了,后来我发现可以用STM32 的实时时钟(RTC)来获取系统时间,首先初始化RTC,然后就可以产生真的随机数。

srand(RTC_GetCounter());
food.x=rand()%28+1;            
food.y=rand()%33+1; 

接下来是snake.h文件里面定义了一个蛇的结构体,还有一个事物的结构体,方便操作。

#ifndef __SNAKE_H
#define __SNAKE_H
#include "stm32f10x.h"
#define MAX_Longth    100 //定义蛇的最大长度
#define straight  0
#define left   1
#define right  2
#define down   3

//定义一个蛇的结构体
typedef struct
{
	u16 XPos[MAX_Longth]; 
	u16 YPos[MAX_Longth];
	u8 longth;   
	u8 score;
	u8 islive;   //判断蛇是否或者
}Snake;

//定义一个食物的结构体
typedef struct
{
	u16 x;
	u16 y;
	u8 flag;  //标志位是否要产生食物标志位
}Food;

void Trun_On_Point (u16 x,u16 y);	
void Trun_Off_Point (u16 x,u16 y);
void Snake_Run(u8 dir) ;
void Snake_Init(void) ;
void Is_Snake_Dead(void);
void Display_Dead(void);
void Eat_Food(void);
u8 Is_Alive(Snake snake);
#endif

接下来是snake.c文件

#include "stm32f10x.h"
#include "lcd.h"
#include "snake.h"
#include "stdlib.h"
#include "time.h"

Snake  snake;
Food  food;

//画一个蛇的最小单位
void Trun_On_Point (u16 x,u16 y)
{
	LCD_Fill(x*8, y*8, x*8+8, y*8+8, RED);
}

//擦去一个蛇的最小单位
void Trun_Off_Point (u16 x,u16 y)
{
	LCD_Fill(x*8, y*8, x*8+8, y*8+8, WHITE);
}


//蛇初始化函数
void Snake_Init(void)  
{
	u8 i;
	snake.longth = 3;	    			//初始化长度为3 
	snake.islive = 1;	    			//蛇是活着的
	snake.score = 0;                   //当前的分为0
	food.flag = 1;					    //
	
	for(i = 0; i < snake.longth; i++) 
	{
		snake.XPos[i] = 14 + i;
		snake.YPos[i] = 17;
	}
	
	for(i = 0; i < snake.longth ; i++)
	{
		Trun_On_Point(snake.XPos[i], snake.YPos[i]);
	}
}

//显示游戏结束界面
void Display_Dead()
{
		snake.islive = 0;
		LCD_Clear(BLACK);
		LCD_Fill(0,0,8,280,DARKBLUE);
		LCD_Fill(232,0,240,280,DARKBLUE);
		LCD_Fill(0,0,240,8,DARKBLUE);
		LCD_Fill(0,272,240,280,DARKBLUE);
		LCD_ShowString(60,100,240,24,24,"GAME OVER! "); 
	    LCD_ShowString(70,140,240,24,24,"score: ");
	    LCD_ShowNum (145,140,snake.score,3,24);
	    while(1){}
}	

//判断蛇是否死亡函数
void Is_Snake_Dead()
{
	u8 i;
	if(snake.XPos[snake.longth-1] <= 1 || snake.XPos[snake.longth-1] >= 28 
		|| snake.YPos[snake.longth-1] <= 1 || snake.YPos[snake.longth-1] >= 33  )
	{
		snake.islive = 0; //ÉßËÀÍö
	}
	
	
	if(snake.islive == 0) //Èç¹ûÉßËÀÍöÓÎÏ·½áÊø
	{
		Display_Dead();
	}
}

//蛇运动函数
void Snake_Run(u8 dir)  
{
	u8 i;
	Trun_Off_Point(snake.XPos[0],snake.YPos[0]); //ÏÈϨÃðÉßβ
	
    if(dir == right)
	{
		for(i = 0; i < snake.longth - 1 ; i++)
		{
			snake.XPos[i] = snake.XPos[i+1];
			snake.YPos[i] = snake.YPos[i+1];
		}
		snake.XPos[snake.longth-1] = snake.XPos[snake.longth-2] + 1; //ÉßÍ·Òƶ¯µ½ÏÂһλÖÃ
		snake.YPos[snake.longth-1] = snake.YPos[snake.longth-2] - 0;
	}
	
	else if(dir == left)
	{
		for(i = 0; i < snake.longth - 1 ; i++)
		{
			snake.XPos[i] = snake.XPos[i+1];
			snake.YPos[i] = snake.YPos[i+1];
		}
		snake.XPos[snake.longth-1] = snake.XPos[snake.longth-2] - 1;
		snake.YPos[snake.longth-1] = snake.YPos[snake.longth-2] + 0;
	}
	
	else if(dir == down)
	{
		for(i = 0; i < snake.longth - 1 ; i++)
		{
			snake.XPos[i] = snake.XPos[i+1];
			snake.YPos[i] = snake.YPos[i+1];
		}
		snake.XPos[snake.longth-1] = snake.XPos[snake.longth-2] + 0;
		snake.YPos[snake.longth-1] = snake.YPos[snake.longth-2] + 1;
	}
	
	else if(dir == straight)
	{
		for(i = 0; i < snake.longth - 1 ; i++)
		{
			snake.XPos[i] = snake.XPos[i+1];
			snake.YPos[i] = snake.YPos[i+1];
		}
		snake.XPos[snake.longth-1] = snake.XPos[snake.longth-2] + 0;
		snake.YPos[snake.longth-1] = snake.YPos[snake.longth-2] - 1;
	}
	
	for(i=0; i<snake.longth; i++)			            //ÈÃÉßÏÔʾ³öÀ´
    {
		Trun_On_Point(snake.XPos[i],snake.YPos[i]);
    }   
	
	Is_Snake_Dead();
	Eat_Food();
}

void Eat_Food()
{ 
	u8 length[20]; //用于保存要显示的分数和长度
	u8 i;
	/*************************判断蛇是否吃到食物**********************/	
	if(snake.XPos[snake.longth -1] == food.x && snake.YPos[snake.longth -1] == food.y)
	{
		snake.longth ++;
		snake.score += 5;
		snake.XPos[snake.longth -1] = food.x;
		snake.YPos[snake.longth -1] = food.y;
		sprintf((char*)length,"%d  length:%d",snake.score,snake.longth);//将分数长度打印到length数组中
		LCD_ShowString(75,300,200,12,12,length);//Show Score
		food.flag = 1;
	}
	
	//放食物
	if(food.flag == 0)  //标志位为0时放置食物
	{
		Trun_On_Point (food.x, food.y);
	}
	
	if(food.flag==1)  
	{
		srand(RTC_GetCounter());
		while(1)
		{  
			food.x=rand()%28+1;      			//产生真随机数
			food.y=rand()%33+1; 

		    for(i=0;i<snake.longth ;i++) //判断产生的食物是否和蛇身重合			
			{
				if((food.x==snake.XPos[i]) && (food.y==snake.YPos[i])) break;
			}
			if(i==snake.longth)   
			{
				food.flag=0;
				LCD_ShowNum(150,290,food.x,3,12);  
				LCD_ShowNum(150,300,food.y,3,12);  
				break;							
			}
		}
	}	
}


控制蛇运动方向,我使用了按键,外部中断的方式来控制,当按下按键,产生外部中断,执行中断服务函数,改变方向。下面是exti.c函数

#include "stm32f10x.h"
#include "exti.h"
#include "lcd.h"
#include "delay.h"
extern u8 dir;
void KEY_GPIO_Config()
{
	GPIO_InitTypeDef  GPIO_Initstructure;
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE );
	GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
	GPIO_Initstructure.GPIO_Mode  = GPIO_Mode_IPU;
	GPIO_Init(GPIOA, &GPIO_Initstructure);
}

static void KEY_NVIC_Config()
{
	NVIC_InitTypeDef    NVIC_InitStructure;
	NVIC_PriorityGroupConfig (NVIC_PriorityGroup_1);
	
	//配置中断源 按键 1 
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ;
	NVIC_Init(&NVIC_InitStructure);
	
	//配置中断源 按键 2
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ;
	NVIC_Init(&NVIC_InitStructure);
	
	//配置中断源 按键 3
	NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ;
	NVIC_Init(&NVIC_InitStructure);
	
	//配置中断源 按键 4
	NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE ;
	NVIC_Init(&NVIC_InitStructure);
}

//EXTI中都配置
void EXTI_Key_Config()
{
	EXTI_InitTypeDef   EXTI_InitStructure;
	
	//按键配置
	 KEY_GPIO_Config();
	//打开AFIO 复用时钟
	RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO, ENABLE );
	//配置NVIC中断
	KEY_NVIC_Config();
	
	
	//选择EXTI信号源
	GPIO_EXTILineConfig (GPIO_PortSourceGPIOA, GPIO_PinSource0|GPIO_PinSource1|GPIO_PinSource2|GPIO_PinSource3);
	
	//中断线 0
	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	//中断模式
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	//触发类型 下降沿
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	//使能中断
	EXTI_InitStructure.EXTI_LineCmd = ENABLE ;
	EXTI_Init(&EXTI_InitStructure);
	
	//中断线 1
	EXTI_InitStructure.EXTI_Line = EXTI_Line1;
	EXTI_Init(&EXTI_InitStructure);
	
	//中断线 2
	EXTI_InitStructure.EXTI_Line = EXTI_Line2;
	EXTI_Init(&EXTI_InitStructure);
	
	//中断线 3
	EXTI_InitStructure.EXTI_Line = EXTI_Line3;
	EXTI_Init(&EXTI_InitStructure);
}




void EXTI0_IRQHandler(void)
{
	//È·ÈÏÊÇ·ñ²úÉúEXTI LINE ÖжÏ
     if(EXTI_GetITStatus(EXTI_Line0) != RESET )
	{
		dir = 0;
		//LCD_ShowNum(180,290,dir,3,12); 
		LCD_ShowString(190,290,200,12,12,"straight"); //显示当前蛇的运动方向
		EXTI_ClearITPendingBit(EXTI_Line0);
		 
	}
}

void EXTI1_IRQHandler(void)
{
	//È·ÈÏÊÇ·ñ²úÉúEXTI LINE ÖжÏ
   if(EXTI_GetITStatus(EXTI_Line1) != RESET )
	{
			dir = 1;
			LCD_ShowString(190,290,200,12,12,"left    "); //显示当前蛇的运动方向
			EXTI_ClearITPendingBit(EXTI_Line1);
	}
}
void EXTI2_IRQHandler(void)
{
	//È·ÈÏÊÇ·ñ²úÉúEXTI LINE ÖжÏ
   if(EXTI_GetITStatus(EXTI_Line2) != RESET )
	{
			dir = 2;
			LCD_ShowString(190,290,200,12,12,"right      ");  //ÏÔʾµ±Ç°ÉßµÄÇ°½ø·½Ïò
			EXTI_ClearITPendingBit(EXTI_Line2);
	}
}

void EXTI3_IRQHandler(void)                                                                                                               
{
	//È·ÈÏÊÇ·ñ²úÉúEXTI LINE ÖжÏ
	if(EXTI_GetITStatus(EXTI_Line3) != RESET )
	{
			dir = 3;
			LCD_ShowString(190,290,200,12,12,"    down");  //显示当前蛇的运动方向
			EXTI_ClearITPendingBit(EXTI_Line3);
	}
}

接下来是主函数

int main(void)
 {  
	POINT_COLOR=BLUE;  //画笔颜色
	RTC_Init();
	Snake_Init();
    EXTI_Key_Config();
	LCD_Init();
	LCD_Fill(0,0,8,280,YELLOW);//Rectangle(8,8-1,85,120,0xaa);//绘制界面
	LCD_Fill(232,0,240,280,YELLOW);//
	LCD_Fill(0,0,240,8,YELLOW);//
	LCD_Fill(0,272,240,280,YELLOW);//
		
	LCD_ShowString(0,290,200,24,24,"Score:");//Show Score
	LCD_ShowString(75,300,200,12,12,"0  length:3");//Show Score
  	while(1) 	
	{  
	    Snake_Run(dir);
		delay_ms(200);
	} 
}

下面是程序运行的图片。

 

 

源码文件源码文件

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值