第5节 在第4章我们逐步开发了Mindows操作系统的一些功能,实现了实时抢占式调度,并拥有了信号量和队列等功能。在上一节,我们又将Mindows成功的移植到了具有cortex内核的LM3S8962和STM32F103VB芯片上,本节,我们将在这两款芯片的开发板上使用Mindows现有的功能开发一个俄罗斯方块的小游戏。
在5.2节我曾经介绍过我所使用的这2款芯片的开发板都有LCD液晶屏和方向按键,本节我们将使用这些资源做一个俄罗斯方块的小游戏。 俄罗斯方块的游戏我们应该都玩过,先将这个游戏的基本功能整理一下。我们最容易想到的是按键功能,需要能控制图形向下、向左和向右移动,还需要能旋转图形,当我们不按向下按键时图形还需要能自动向下走,当游戏结束时,我们需要一个按键可以重新开始游戏。在游戏过程中还需要遵守一些规则,比如说不同的图形互相之间不能重叠,图形不能移出屏幕的范围,当屏幕的一行被图形填满时本行需要被删除,并且上面所有的图像向下移动,还有最重要的一点,我们需要使用LCD将游戏的过程显示出来。 我们将这些需求做一个表格整理一下:
表 表12中列出了我们需要做的功能,接下来我们就要想办法来实现这些功能。我们首先要解决的一个问题是我们必须要有一个办法能使用处理器来表示这些图形,这样才能对它们进行控制,并将它们显示到LCD上。LCD中的每一个像素就是一个数据,代表着这个像素的颜色,因此我们可以在内存中用一个二维数组来对应LCD中的像素,数组的两个维度对应着LCD的X轴和Y轴,数组中元素的数值就对应着LCD中对应位置的像素的数值,数组元素为黑色像素的数值时代表LCD中对应的位置没有图形,其它数值代表有图形,这样就可以把对LCD中图形的操作转换为对数组数据的操作了,然后再以一定的频率将数组中的数据输出到LCD中,这样就实现用处理器控制LCD中的图形了。 经过上面的分析,我们就可以将图形映射为数组数据,我们只要专注于处理数组数据,至于LCD中的图形则只需要将数组数据刷到LCD上就可以了。由此可以想到的一个简单的处理方法是使用一个任务专门来处理数组数据,使用另一个任务专门来将数组数据刷到LCD上,至于按键功能则可以用另一个任务专门来检测按键的输入。这样的3个任务相互之间的耦合性很小,很适合使用并行的任务实现,按键任务负责检测按键输入,如有按键输入则向处理数据的任务传送按键值,处理数据的任务接收到按键值就对数组数据进行处理,而刷新LCD的任务只需要以固定的频率将数组数据刷新到LCD上就可以了。至于打印任务切换的功能还可以使用我们以前的方式实现,在任务切换时使用任务切换钩子函数将切换信息打印到内存中,再使用一个打印任务将内存中的数据打印到串口。
图 KeyTask任务周而复始的检测按键是否按下,由于对按键按下的频率有要求,因此这个任务需要以一定的间隔来读取按键值,在间隔期间内不读取按键值,这样也可以防止按键抖动的情况发生。当该任务读取到一个有效的按键值时,就将这个按键值压入队列发送给ProcessTask任务。 ProcessTask任务周而复始的从队列里取消息,当队列为空时,该任务就会被队列阻塞,处于pend状态,直到队列里有了消息才被激活,然后从队列里取出按键值,根据按键值来对图形数组的数据做相应的操作。 FlushScn任务则以一定的频率将图形数组中的数据刷新到LCD屏幕上。每个图形是由多个像素组成的,对图形处理、刷新的过程就是对图形中像素的处理、刷新过程,需要对多个像素进行多次操作才能完成对一个图形的操作。FlushScn任务和ProcessTask任务是并行执行的,为避免这两个任务在对同一个图形的处理过程中相互干扰,就需要在这2个任务中使用信号量锁住对同一个图形的处理过程,使这两个任务对同一个图形的操作保持串行性,保证图形处理、显示时的完整性。 这3个任务的结构大致如下所示:
void {
}
void {
}
void {
} KeyTask任务和FlushScn任务比较简单,都是以固定的频率执行重复的动作,ProcessTask任务比较复杂,是实现俄罗斯方块游戏的关键,下面我们再重点介绍一下这个任务。 ProcessTask任务的主要功能是根据按键值来对数组数据做相应的处理,这其中涉及到图形移动、变形、冲突检测、删除等操作,实现了这些操作也就基本实现了俄罗斯方块这个游戏,我们先以LM3S8962为例讲解这些过程。
图 这些图形中每个基本的小方块占4×4个像素,最长的图形长度为5×4=20个像素,考虑到变形,我们需要使用20×20个像素的一块空间才能存放下任意一个图形,因此,我们只需要在内存中开辟一个20×(20÷2)大小的字符型二维数组就可以表示任意一个图形了,更新屏幕右侧的下一个图形窗口只需要这么大小的一块内存就可以了。 当图形在游戏区域内向下移动的时候,我们还需要开辟一个20×(20÷2)大小的字符型二维数组来表示当前的图形,当图形向下走一行时,先从游戏区域数组里将该图形清除掉,然后将当前图形数组在游戏区域数组内下移一行,如果下移后的当前图形数组与游戏区域数组没有图形数据冲突的话,就将下移后的当前图形数组写入游戏区域数组内,完成本次下移操作。如果有冲突的话,则说明当前图形已经不能再下移动了,将当前图形数组原地恢复到游戏区域数组内,再去判断当前图形所在的所有行内是否有填充满的整行数据,如果有的话就删除这些行,在删除的时候每隔一段时间变换一下这些行的颜色,以表现出删行时闪烁的效果。当删除一行后,需要将该行上面的所有图形数据都向下移动一行,并将下个图像数组复制到当前图形数组内,将当前数组的位置恢复到图形出现的最上方,再随机选择一个图形,将下个图形数组更新为该图形,完成本次操作。 其它的向左向右和变形操作也需要先从游戏区域数组内将当前图形清除掉,然后判断相应操作后是否会有图形冲突,如果没有冲突的话就将当前图形数组数据复制到操作后所在的游戏区域数组内的位置,完成本次操作,如果有冲突的话就将当前图形数组原地恢复到游戏区域数组内,结束本次操作。
上面就是有关俄罗斯方块游戏的基本介绍,代码就不具体介绍了,代码里都有注释,通过本节的介绍并结合代码应该就可以理解这个游戏的实现方法了。如果你感兴趣的话,可以再增加一个按键声音的功能,无非就是再创建一个播放声音的任务,当按键任务检测到按键时向声音任务发送一个消息激活声音任务,由声音任务播放按键声音,原来的软件结构基本不用改动。
图 LM3S8962单板使用的LCD是128×96的分辨率,每个像素用4bits表示颜色,而STM32F103VB板子上使用的LCD是160×128的分辨率,每个像素需要用16bits表示颜色,这就是说STM32F103VB板子会比LM3S8962板子耗费更多的内存,而STM32F103VB芯片的内存又比较少,将俄罗斯方块游戏从LM3S8962单板移植到STM32F103VB单板上时内存就显得有些不够用了。在我们上述的设计中每个图形都是由4×4像素的基本小方块组成的,每次操作都是针对这个基本单元进行的,因此我们可以将这16个像素看做是一个像素,这样128×96个像素就可以缩减为32×24个像素了,所使用的内存也就大大减少了。 前面我们已经将Mindows操作系统移植到STM32F103VB板子上了,因此在这里仅需要修改LCD驱动程序以及与LCD操作相关的程序就可以将俄罗斯方块游戏从LM3S8962单板移植到STM32F103VB单板上,至于程序的整体结构则完全不用修改。另外,STM32F103VB单板的LCD可以显示多种色彩,我们为每种图形分配一种颜色,这样看起来会更漂亮一些,代码很简单,不详细介绍了,请自行阅读代码。
图 |
|
来自: 败败0619 > 《ST-STM32F1xx》