最近又在学习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
控制蛇运动方向,我使用了按键,外部中断的方式来控制,当按下按键,产生外部中断,执行中断服务函数,改变方向。下面是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);
}
}
下面是程序运行的图片。
源码文件源码文件