1、设计概述 1.1设计目的 在学习单片机时,为避免枯燥无味的调试学习,把学习的目的作为制造一款游戏,既能增添学习的乐趣,又能学到单片机、计算机底层的知识。 1.2设计任务 在STC的数码管上做一条贪吃蛇,蛇可吃食物边长,碰到边界和自己死亡, 游戏玩法为:导航键向上移动,最左边按键向左移动,最右边按键向右移动,中间按键向下移动,食物是随机产生的, FPGA的六个按键分别代表一个速度,可以控制蛇的速度。 1.3设计要求 1、导航键控制蛇向上移动,当蛇向下移动时此按键无效。 2、K1控制蛇向右移动,当蛇向左移动时此按键无效。 3、K2控制蛇向下移动,当蛇向上移动时此按键无效。 4、K3控制蛇向左移动,当蛇向右移动时此按键无效。 5、开局播放提示音,为营造气氛,播放急促的敲击声音,提示游戏开始 6、每移动一格,要同步发出移动的提示音 7、随机产生食物,产生的食物不能在蛇的身体上出现 8、蛇从食物经过的时候,食物变成蛇的头,从而蛇变长 9、蛇会根据速度向前不断走 10、当蛇触碰到边界,游戏结束 11、当蛇碰到自己时游戏结束 12、当游戏结束时,想显示3s死亡前的 13、当游戏结束时,播放音乐 14、当游戏结束的时候,数码管显示分数,即蛇的长度,要求以流水灯的形式展示,分数从左边第一个数码管显示到右边最后一个数码管, 再从最后一个数码管显示到左边 第一个数码管,如此循环,此外要求,分数的跳动要同步音乐的节奏, 音乐播放一个 节拍,蛇移动一步,并且要求同步。 15、FPGA的六个按键分别代表一个速度,当按下时,发送数据给单片机,告诉单片机 改变蛇体的速度,当发送数据至单片机时, LED灯的右边六个要亮起对应的按键, 提示蛇的速度已经改变
2、总体方案设计 STC部分:
FPGA部分:
注: 目前还没有实现FPGA串口接收数据和显示
如流程图中,游戏进行时运行主要进程为实时运行显示蛇的状态,通过中断1,每隔1ms打断主函数的执行,然后判断更新蛇的的信息, 这样当中断结束以后,蛇就有了新的数据,利用这样的中断,可以类似实现多线程;如果导航按键有数据更新的就使用中断器更新寄存器的数值, STC应该是没有中断嵌套,因此当中断1结束的时候,就立马会更新;蜂鸣器的中断同理;用于数据接收的中断器也是如此,如果接收到FPGA的数据, 打开中断更改蛇的数据,在蛇的下一次移动的时候,改变响应的速度。
在游戏结束的时候,因为要同步播放背景音乐,相比之下,显示分数的速度会比显示蛇的速度快很多,因为音乐的播放会延时, 因此将音乐播放的程序放置主函数中,将分数显示的函数放置中断函数中,这样既能播放音乐又能显示分数,如果两个函数放置的地方想法, 那么中断会进行很长一段时间,因为音乐的节拍可能会很长,因此会出现数码管显示暗淡甚至不亮。
同理,当接收到FPGA改变速度的信号时,LED的不能显示太长时间,不然会使游戏出现卡顿,太多时间又不会亮,因此寻找一个临界点是关键方案。
3、硬件原理 首先,一个STC最重要的当然是引脚分配图:
P0口设置与工作原理: STC系列芯片有5组8位输入口,分别为P0到P5,其中P5口仅P5.0~P5.5用于输入输出。 STC芯片的所有I/O口都可以配置为四种工作模式之一:准双向口/上拉、推挽/上拉、输入/高阻和开漏模式。 STC15系列单片机上电复位后为准双向口/上拉工作模式。每个I/O口的工作模式由2个控制寄存器中的相应位控制(PnM0和PnM1,n=0、1、2、3、4、5)。 也就是说P0口的具体工作模式由P0M0和P0M1控制。
数码管和LED电路原理:
如图,工作原理: P0口的8位输出分别控制1个LED数码管的7段和一个小数点;而P2.3经反相器U4C控制74HC138的使能信号E3, 结合P2.0、P2.1、P2.2这3个位选控制信号确定8个LED数码管中的哪个被点亮;电阻R15~R22为限流电阻。当段选为高、使能信号有效时,对应的LED管将会发光。 通过以一定频率扫描位选信号,修改段选信号进行数码管点亮一段时间,从而给人视觉上几个数码管几乎同时显示的效果,因此蛇的形状可以通过这样显示。
LED数码管引脚定义:
给对应的引脚接1,就可以点亮数码管。
导航键及按键
这个导航最麻烦了,工程需要使用ADC进行导航按键的电压采集,并将采集后的转换结果送到数码管的显示。 导航按键电路及工作原理说明: 导航按键在上图的标注为MINI_KEY5,导航按键的每一个方向被按下,都会引起实际电压的改变,从而可以根据这个原理,与A/D转换器配合, 可以判断哪个方位被按下,获取按下后A/D转换的结果。
A/D转换后的高三位(Seg0)和低五位(Seg6-Seg7):
在工程中我只需要用到导航键按下和key3按下,所以我只需要考虑adc出现对应的两个值即可,即0 和 3。
无源蜂鸣器电路原理图:
无源蜂鸣器需要我们给提供振荡源。理想的振荡源为一定频率的方波。 实验板采用的是无源蜂鸣器,是电磁式蜂鸣器,电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。接通电源后, 接收到的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。 无源蜂鸣器只需改变Beep(引脚为P3.4)端口的电平,产生一个周期性的方波即可使蜂鸣器发生声音,不同的频率发出的声音不同。 其中,ULN2003是一个功放,用于放大电流。电阻R14和电容C21是用来保护电路的。
数据传输模块原理:
STC的右边有一个串口,从引脚图上可以看出P1.0 和 P1.1 口分别为它的接收和发送端口,通过传输不同的电压, 达到单片机能判断01的临界条件即可传输信息,再以一定的速度改变传输的电压,不同的机子间设置好对应的协议即可实现信息的传输。
4、软件设计与实现 通过分模块的思想,将游戏的运作分为很多模块,每一个模块对应实现一个功能,但是c语言的特性使得一些变量不好封装,而且还要用到中断, 于是采用类似封装的思想,以下是我的一些主要的代码的设计与实现。
变量和头文件定义定义:
#include <STC15F2K60S2.h>
#include <rand.h> #define uint unsigned int #define uchar unsigned char #define Sysclk 11059200L #define Baudrate 9600
uint speed[6] = {1000,900,800,700,600,500};
uint reset[15] =
{0,1908,1701,1515,1433,1276,1136,
1012,956,852,759,716,638,568,506};
// 设置频率
//0 1 2 3 4 5 6 7
xdata uchar music[204]=
{10,1, 10,1, 10,1, 10,1, 12,1, 12,3, 9,1, 9,1, 10,1, 9,1.5, 8,1,
8,1, 8,1, 8,1, 8,1, 10,2, 10,1, 10,1, 11,2, 10,3, 0,1, 8,1,
8,1, 9,1, 8,1, 9,2, 0,1, 8,1, 8,2, 6,1, 6,1, 5,3, 0,1, 9,1, 9,1,
10,1, 11,1, 11,2, 0,1, 9,1, 8,1, 10,1, 9,0.5, 9,2, 0,1, 10,1,
10,1, 10,1, 10,1, 12,1, 12,3, 0,4, 10,1, 10,1, 12,1, 12,2,
10,1.5, 0,6, 8,1, 8,1, 8,1, 8,1, 8,1, 10,2, 10,1, 0,1, 10,1,
10,1, 10,2, 10,1, 10,1, 11,1, 10,4, 0,2, 8,1, 8,1, 9,1, 8,1,
8,2, 6,1.5, 8,1, 8,2, 6,1, 5,3, 0,2, 9,1, 9,1, 10,1, 11,1, 11,2,
0,1, 10,1, 9,1, 8,1,10,1, 9,1 };
//
sbit LED_SEL = P2^3;
sbit key1 = P3 ^ 2;
sbit key2 = P3 ^ 3;
sbit beep=P3^4; // 蜂鸣器
//这里初始化值
uchar arrSegSelect[] =
{0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f,
0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71, 0x00};
uchar arrDigitSelect[] =
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
//数码管段选
uchar idf;
uint time;
uint cntKey;
uint cntMusic;
uint delayMusic;
int adc_value;
int foodTemp;
//记录游戏的结束开始 int isStart; int isEdge; int isPress; //判断是否按了按键,处理消抖动 int musicFlag = 0; int moveFlag = 0;
//*****记录snake的坐标位置,信息 int x[50], oldX[50]; int y[50], oldY[50]; int len = 3; int dir = 1; //上下左右 0 1 2 3 int cntScore = 0; int addScore = 1; int foodX[2]; int foodY[2]; uchar rdata = 0x00; uint curSpeed; uint rdataTemp; int displayOld;
函数的定义: //一些用到的函数 void delay_ms(uint n); //延时函数 单位为ms 这是一个延时函数,用STC的下载软件直接生成,作为一些时间的延时函数
void snakeInit(); 这里初始化蛇的形状,位置,和速度
void paintSnake(); 这个函数专门用来在数码管上显示蛇的形状。
void ADCInit(); 这个函数用来初始化ADC的使用,中断的开启
void time0Init(); 这里初始化定时器1
void primeEdge(); 这个函数用来,判断蛇的位置时候合理
void makeFood(); 这个用来随机生成食物,要注意的是食物不能够出现在蛇的身体上
void paintFood(); 这个函数用来将食物显示在数码管上
void eatFood(); 这个函数,在蛇碰到食物的时候,将食物吃掉
void primeHeader(); 这个判断蛇的头时候碰到了身体,如果蛇碰到了身体,那么游戏结束
void Delay100us(); 这是一个延时函数,数码管的延时,用一个较短的延时,使数码管刷新尽快,通过快速的刷新,使得显示的年度会增加
void time1Init(); 定时器3的初始化
void paintScore(); 游戏结束时,显示分数
void play(); 播放背景音乐函数的调用
void playMove(); 移动提示音的调用
void changeSpeed(); 接收FPGA的数据,并且改变速度值
void Uart_Init(); 初始化串口2用到的计时器
void Uart2_process(); 用来接收数据的函数 5、实验过程与测试 由于是分模块处理的,所以根据设计好的思路,一步一步加入新的功能。
首先当然是数码管显示蛇的形状,只在数码管上显示蛇的初始形状。
其次,蛇要移动,于是乎,在中断中尝试让蛇向右移动,测试时简单让蛇一直向右前进就行了。
接下来蛇会动了,那就得改变方向了,在这里要引用按键,先测试简单的三个按键,让蛇能够向左,向下,向右移动。
这时加入导航按键,此时主意key3的输入也会影响导航按键,于是在获取到adc的值得时候,直接使用key3的转化值,通过调试,蛇就可以移动啦!
好了能玩了,但是还远没有结束!
首先还有一个边界判定,如果蛇爬出了边界,就让游戏结束,并且让蛇停止在碰撞前的原地,因此,做到这里的时候加入,停止判断标志和数据更新的时候记录, 上一步的数据,这样就可以返回碰撞前的位置。
测试成功后,一个简单的蛇就完成,但是得加入点声音,于是乎,在数据更新的后面,顺带给蜂鸣器的定时器打开一个时间,按照一定的频率,震动一段时间, 这样就模拟出了移动时的声音。
开局的时候也来一个提示音乐吧! 调试来调试去,发现把显示放在主函数里,会被背景音乐影响,看来音乐是做不成的了,但是, 设计给蜂鸣器定时器设置一个开启时间和结束时间,这样可以播出一个持续的敲击声,顺带有紧张急促的感觉。
游戏结束了,也得让玩家看看分数,但是原来的设计是显示碰撞前的形状,有点冲突,呃~~,那就提示1s的时间,然后显示分数。通过调试成功。
最后播放分数的时候,也来一个音乐,前面没成功,是工程太难改了,这个把主函数放放入分数显示模块,同时中断播放音乐,到这里已经基本开发完了。
最后显示分数有点单调,于是再来一个流水形式展示分数,跳动的时候跟着音乐节奏一起来,一个节拍, 就跳动一次。
那么游戏其实已经可以到此结束,还有没有可以加入的功能呢?STC已经没有按键了,但是当思考的时候FPGA来了,于是想到结合一下数据传输, 最后想到给STC的蛇来定一个速度好了。
首先,要接收PPGA的信号,就首先要有信号发送,通过查看STC的开发手册,发现有串口2可以接收,波特率为9600的信号,于是,在FPGA上调试,发送数据位8位, 停止位为一位的数据,速度调试好,一秒钟发送9600个数据,此时STC也要设计程序接收信息,通过测试发现是不行的, FPGA的频率不对,导致协议不同, 于是通过同学那里得知正确的时间,最后才接收到数据,FPGA是只要键盘数据改变了就发送数据,STC接收到后,改变速度即可。
做到这里,我又想到了扩展模块,将蛇显示在FPGA上,由于如果真的这样改,我的整个工程的模块都要重新写,时间有点来不及,因此,扩展的实现算是一个遗憾。
6、设计总结与心得 6.1设计总结
做完这个综合设计,确实是遇到了不少的问题。首先,程序是不断调用中断,当一个主的中断程序越写越多的时候,运行时间会变长,还有可能会发生预想不到的事情, 因此,在中断写函数的时候,要尽可能的简短,如果中断运行时间过长,后面等待的中断会被阻塞;其次,调用数码管的时候,如果同步声音的时候, 声音的播放会中断数码挂的显示,因此在设计的时候就要考虑到这个问题,我在刚开始设计游戏的时候并没有考虑这么多问题,也不是很清楚硬件的发音显示的原理, 从而导致最后的功能没有完美实现;除此之外,我了解了很多的硬件的直接调用, 例如如何将简谱转化为歌曲的音符和节拍,中断的使用,数码管小数点的显示, 如何AD转化等等。 6.2设计心得 通过这次综合设计,我不仅开发出了一款可以用的游戏,而且我也学会很多软 件对接硬件的知识,并且对计算机的底层有了很多前所未有的了解。 更重要的是我 学会了如何一步步去发现问题,通过查找文献,或者请教同学老师,如何在一个 陌生的领域从完全不了解到入门的学习方法。 当然根据实验的过程,我还学会先仔 细分析硬件的底层原理,再做设计,这样才不会做到一般的时候发现不足之处。