摘 要: 基于Keil for ARM集成开发环境,设计了一种以单片机STM32F103C8T6为核心的LCD12864液晶显示系统,可以在屏幕任意位置实现字符显示和图形显示,同时实现了贪吃蛇游戏的功能。该系统操作便捷,界面友好,具有功耗低、速度快、性价比高等优点。
关键词: STM32F103C8T6;LCD12864;贪吃蛇游戏
0 引言
随着社会的不断发展,人们对终端显示器的要求越来越高,体积小、重量轻、功耗低、清晰度高的LCD显示器无疑成为大家心中理想的选择[1]。与传统的CRT相比,基于单片机的LCD显示系统具有硬件结构简单、控制方便、成本低等优点,在现代嵌入式系统中已得到广泛的运用。本文设计了一种基于单片机STM32F103C8T6和LCD12864的液晶显示系统,可以实现字符、图形的显示,并在此基础上设计了一个贪吃蛇游戏实例。
1 系统硬件设计
LCD显示系统主要由单片机、LCD显示模块、电源模块、4×4矩阵键盘、通信接口电路组成。系统方框图如图1所示。
系统电源部分可以使用5 V电源端口供电,再通过3.3 V稳压芯片ASM1117-3.3得到单片机与LCD显示模块所需电源。单片机采用ST公司的芯片STM32F103C8T6,4×4矩阵键盘作为系统的输入设备,采用LCD12864液晶显示模块作为系统的显示部分。单片机系统通过处理键盘输入数据后,根据不同的情况对显示屏发出不同的操作指令。通信接口电路主要是由芯片PL2303构成,它的功能主要是将电脑的USB接口转换为串口,同时还将PC的逻辑电平转换为与单片机系统相同的CMOS电平,方便程序的烧写以及与上位机的通信。
1.1 Keil MDK和单片机STM32F103C8T6简介
Keil MDK是德国Keil公司为基于Cortex-M、ARM7、ARM9等系列处理器提供的一款集成开发环境,支持程序的编辑、编译、链接以及在线调试等功能,可以自动配置启动代码,简单易学,性能优越,能满足大多数嵌入式应用[2]。
STM32F103C8T6是一款基于ARM 32位的Cortext M3内核的单片机,2.0~3.6 V的宽电压供电范围,CPU工作频率最大可达72 MHz,具有单周期的乘法指令和硬件除法,以及优先级可编程的中断系统。同时它还具有64 KB的Flash存储器与20 KB的SRAM存储器,另外还集成了非常丰富的片内外设,如看门狗、定时器、GPIO口、DMA控制器、ADC、UART、SPI接口、IIC接口等,具有成本低、速度快、性价比高等优点。
1.2 液晶LCD12864显示模块
LCD12864液晶屏的数据接口是串行的,工作电压为2.8~5.5 V,像素为128×64,显示颜色为黑白色,背光颜色为蓝色,不带字库。模块内部采用的控制器为ST7565P,功耗非常低,在省电模式下的工作电流最大仅为5 A,显示模块仅有8个引脚接口,占用主控制器的I/O数量少。LCD12864引脚说明如表1所示。
1.3 LCD12864与单片机STM32F103C8T6的硬件连接
单片机STM32F103C8T6与LCD显示模块的硬件连接非常简单,如图2所示。LCD12864引脚编号为1~5的控制端口可以直接与STM32F103C8T6单片机的GPIO口相连,第8引脚是LCD的背光电源端口,通过单片机的一个I/O口控制三极管的导通来控制背光电源的通断,从而节省功耗[3]。
2 系统软件设计
系统软件设计主要包括LCD初始化、写数据、写命令,以及画点、字符显示等程序设计。液晶显示模块正常工作前,需要对其进行初始化,主要包括复位液晶、确定显示对比度、偏压比等参数,然后才能实现字符、图形以及其他的显示功能。在向液晶显示模块写入数据或者命令时,一定要注意写入的时序,只有严格地按照时序编写驱动程序才能正常工作。设计程序时,为了方便移植,可以将相关引脚的操作用宏封装起来,如将片选引脚拉低的操作可以用宏LCD_CS_0()定义[4-5]。写数据命令的功能函数如下:
void LCD12864_WriteCommand(uint8 Cmd)
{……
LCD_CS_0();//打开片选
LCD_A0_0();//选择写命令
LCD_SCLK_0();//时钟线置低
for(i=0;i<8;i++){
if(Cmd &0x80)//如果输出为1
LCD_SI_1();//SI数据线输出为1
else
LCD_SI_0();//SI数据线输出为0
LCD_SCLK_1();//写入数据
Dat<<=1;
LCD_SCLK_0();//置低SCLK时钟线
} LCD_CS_1();//关闭片选
}
写数据功能函数void LCD12864_WriteData(uint8 Dat)实现方法与写命令功能函数相同,只是打开片选后,选择的是写数据LCD_A0_1()。
在设计LCD液晶驱动程序时,最基本的功能就是在屏幕的任意位置显示一个点,然后在此基础上实现更复杂的功能。但是每次写入的数据都包含了8个点的信息,而且该LCD显示模块没有读控制端口,不能通过读数据的命令了解其他7个点的状态,这样就不能达到最终的目的。为了解决这个问题,可以在单片机的内存区开辟一个数据空间,来记录显示器上每个点的显示情况,代码如下:
static uint8 LCD_RAM[128*8]={0};//开辟显示缓冲区
void LCD12864_DrawPoint(uint8 x,uint8 y,uint8 Point_Flag)
{
if(Point_Flag==0)
{LCD_RAM[x+128*(7-(y>>3))]&=(~(1<<(7-(y&0x07))));}
if(Point_Flag==1)
{LCD_RAM[x+128*(7-(y>>3))]|=(1<<(7-(y&0x07)));}LCD12864_WriteCommand(0xB0+(7-((y>>3)&0x0F)));//写页地址
LCD12864_WriteCommand(0x10+((x>>4)&0x0F));
//写列地址,高低字节两次写入
LCD12864_WriteCommand(0x00+(x&0x0F));
LCD12864_WriteData(LCD_RAM[x+128*(7-(y>>3))]);
//写入数据
}
因为液晶模块未带字库,在字符显示前,需要将其字模存入单片机的程序存储空间中,然后在任意坐标显示一个点的基础上,实现字符的显示。8×16字符显示的驱动程序如下:
void LCD12864_PutChar8x16(uint32 Char_x,uint32 Char_y,uint8 c)
{……
Temp=Font8x16[c*16+i];//获取字模
for(j=0;j<8;j++)
{if(Temp&0x80)
{LCD12864_DrawPoint(Char_x+j,Char_y-i,1);}
else{LCD12864_DrawPoint(Char_x+j,Char_y-i,0);}
Temp<<=1;}}}
3 实例设计
以单片机STM32F103C8T6作为控制核心,在LCD12864驱动程序的基础上,实现了贪吃蛇游戏的功能。贪吃蛇游戏程序主要分为3个子程序模块,即键盘扫描模块、LCD12864驱动模块和贪吃蛇游戏数据处理模块。贪吃蛇程序流程图如图3所示。
游戏中的食物和蛇可以看作两个不同的对象,它们的属性可以用两个结构体变量来描述,然后根据游戏功能将整个大模块合理地划分为各个子模块,并编写相应的功能函数,供主模块函数调用。在设计程序时,定义蛇的最长长度为15,蛇运动区域的长为20个单位,宽为15个单位,即横坐标的坐标范围为1~20,纵坐标的坐标范围为1~15。贪吃蛇游戏中定义了食物结构体和蛇结构体变量。食物结构体定义为:
struct{uint8 X;uint8 Y;}Food;
蛇体的最大长度宏定义为:
#define SNAKE_LONG_MAX 15
蛇结构体变量中定义了蛇体长度、蛇的等级和蛇的分数,具体定义为:
struct{
uint8 X[SNAKE_LONG_MAX_];
uint8 Y[SNAKE_LONG_MAX];
uint8 Long;
uint8 Level;
uint16 Score;}Snake;
显示无符号整形数据功能函数定义为:
void LCD12864_PutUintNumber(uint32 Num_x,uint32Num_y,uint32 Uint_Num,uint8 Num_N)
显示或者熄灭一个蛇体单位功能函数定义为:
void DiaplaySnakePoint(uint8 x,uint8 y,uint8 Point_Stu)
判断蛇是否死亡的程序如下:
if((Snake.X[0]<1)||(Snake.X[0]>20)||(Snake.Y[0]<1)||(Snake.Y[0]>15))
{Game_Fail();}
for(i=4;i<Snake.Long;i++){if((Snake.X[0]==Snake.X[i])&&(Snake.Y[0]==Snake.Y[i])){Game_Fail();}}
判断蛇是否吃到食物程序如下:
if((Snake.X[0]==Food.X)&&(Snake.Y[0]==Food.Y))
{Snake.Long++;//蛇身长度加1
Snake.Score+=10;//得分数增加
LCD12864_PutUintNumber(96,63-48,Snake.Score,4);
//屏幕上更新分数
Snake.X[Snake.Long-1]=Snake_End_x;
Snake.Y[Snake.Long-1]=Snake_End_y;
DiaplaySnakePoint(Snake_End_x,Snake_End_y,1);
//蛇体向后增长1个单位
Put_Food();//放置食物
}Delayms(3000-300*Snake.Level);//根据蛇体等级延时
在贪吃蛇游戏的程序设计中,放置食物的程序会涉及如何获取随机数的问题,为了增加游戏的随机性,本次设计不采用伪随机数产生的方法,而是利用单片机的AD采样引脚在悬空的情况下采样,获得的AD值作为随机种子,然后通过一系列的运算来得到随机数,经过实践得知,此种方式得到的随机数更真实,效果很好。食物放置程序如下:
void Put_Food(void){…..
AD_Temp=ADC_ConvertedValue;//获取随机种子
Food.X=(AD_Temp%20)+1;
//随机获取食物横坐标(1~20)
Food.Y=(AD_Temp%15)+1;
//随机获取食物纵坐标(1~15)
……
DiaplaySnakePoint(Food.X,Food.Y,1);//显示食物
}
4 结论
STM32F103C8T6是一款高性能的单片机,LCD12864硬件接口简单,所占用单片机的I/O口资源非常少,单片机有足够的硬件资源供其他外设使用,显示系统可以作为其他嵌入式系统的一部分,运用于不同的显示场合;系统的程序设计采用的是C语言,其移植性非常好,模块化的设计思想可以在系统中灵活地扩展其他功能,满足不同的显示要求。
参考文献
[1] 刘金星,李洪文.基于高速单片机的液晶显示模块控制[J].液晶与显示,2011,26(1):88-91.
[2] 韩敬海,王蕊.Cortext-M3开发技术与实现[M].西安:西安电子科技大学出版社,2013.
[3] 陈杨杨,陈梅.基于PIC18F458单片机的LCD显示[J].仪表技术,2005(4):52-53.
[4] 武俊鹏,张国印,姚爱红,等.基于ARM的嵌入式系统设计实验与实践教程[M].北京:清华大学出版社,2011.
[5] 石红晓,吴光敏.基于Nios II处理器的TFT-LCD图形显示设计[J].微型机与应用,2010,29(18):37-39.