完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
屏幕显示英文字符
有个屏幕,掌机的可玩性可以大大增强,打地鼠玩腻了,可以玩个贪吃蛇,俄罗斯方块,以及其他需要开动想象力的游戏。并且,以前总是玩别人的游戏,现在可以自己写游戏,岂不美哉。 后来我感觉0.96的OLED屏幕太小了,搞个更大的全彩屏,换成STM32F4系列单片机,跑个ucos,运行NES模拟器,然后我是不是就能拳打任天堂,脚踢PSP了?哈哈哈 打地鼠的游戏,可以显示生命值,得分,关卡或难度。所以要定义几个变量。 //main.c //默认参数 #define LIFE_NUM 3 // 默认几条命 //全局变量定义 u8 life = LIFE_NUM; //生命 u32 score = 0; //得分记录 u8 level = 1; //当前难度,数字越大难度越高 一般来说,显示英文字符都会有配套的显示函数。我参考的代码也提供了这些函数。直接调用即可。formatScreen用于清屏,就像老师板书之前要擦黑板一样。showString用于在指定的坐标显示英文字符,每个参数的含义可以跳转,看函数说明。 int main(void) { LED_Init(); KEY_Init(); delay_init(); initIIC(); initOLED(); formatScreen(0x00); showString(0,0,“yoodao”,FONT_16_EN); showString(0,2,“life:”,FONT_16_EN); showString(0,4,“level:”,FONT_16_EN); showString(0,6,“score:”,FONT_16_EN); showNumber(56,2,life,DEC,8,FONT_16_EN); showNumber(56,4,level,DEC,8,FONT_16_EN); showNumber(56,6,score,DEC,8,FONT_16_EN); while(1) { score++; showNumber(56,6,score,DEC,8,FONT_16_EN); delay_ms(1000); } } 屏幕显示汉字 汉字的显示可能就稍微复杂些,因为我们选用的屏幕没有中文字库,所以要自行取模。在取模之前,我先试了试人家显示汉字的函数showCNString与显示图片的函数showImage’’,成功。 在此感谢风媒电子。 //main.c formatScreen(0x00); showImage(0,0,128,8,FM_LOGO_ENUM); delay_ms(1000); // showString(0,0,“yoodao”,FONT_16_EN); showCNString(0,0,“风媒电子”,FONT_16_CN); 然后把显示的LOGO和汉字改一改。 formatScreen(0x00); // showImage(0,0,128,8,Y_LOGO_ENUM); // delay_ms(1000); // formatScreen(0x00); // showString(0,0,“yoodao”,FONT_16_EN); showCNString(0,0,“小极客打地鼠掌机”,FONT_16_CN); 然后用取模软件,生成“小极客打地鼠掌机”的字模 取模软件的使用: 取得字模以后,替换原先的汉字数组。 /************************************16*16 汉字************************************/ const unsigned char CN1616[8][32] = { {0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00,0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00},/*“小”,0*/ {0x10,0x10,0xD0,0xFF,0x90,0x10,0x02,0x02,0xFE,0x02,0x02,0x62,0x5A,0xC6,0x00,0x00,0x04,0x03,0x00,0xFF,0x00,0x43,0x30,0x8F,0x80,0x43,0x2C,0x10,0x2C,0x43,0x80,0x00},/*“极”,1*/ {0x10,0x0C,0x84,0x44,0xB4,0xA4,0x25,0x26,0x24,0xA4,0x64,0x24,0x04,0x14,0x0C,0x00,0x04,0x04,0x04,0xFA,0x4A,0x4A,0x49,0x49,0x49,0x4A,0x4A,0xFA,0x04,0x04,0x04,0x00},/*“客”,2*/ {0x10,0x10,0x10,0xFF,0x10,0x90,0x04,0x04,0x04,0x04,0xFC,0x04,0x04,0x04,0x04,0x00,0x04,0x44,0x82,0x7F,0x01,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},/*“打”,3*/ {0x20,0x20,0x20,0xFF,0x20,0x20,0x80,0xF8,0x80,0x40,0xFF,0x20,0x10,0xF0,0x00,0x00,0x10,0x30,0x10,0x0F,0x08,0x08,0x00,0x3F,0x40,0x40,0x5F,0x42,0x44,0x43,0x78,0x00},/*“地”,4*/ {0x00,0x00,0x7E,0x4A,0x4A,0x49,0x40,0x40,0x40,0x4A,0x4A,0x4A,0x7E,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x49,0x12,0x00,0xFF,0x80,0x49,0x12,0x00,0x3F,0x40,0xF0,0x00},/*“鼠”,5*/ {0x00,0x10,0x0C,0x05,0x76,0x54,0x54,0x57,0xD4,0xD4,0xF6,0x85,0x14,0x0C,0x00,0x00,0x00,0x10,0x15,0x15,0x15,0x55,0x95,0x7F,0x14,0x14,0x14,0x14,0x14,0x10,0x00,0x00},/*“掌”,6*/ {0x10,0x10,0xD0,0xFF,0x90,0x10,0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x04,0x03,0x00,0xFF,0x00,0x83,0x60,0x1F,0x00,0x00,0x00,0x3F,0x40,0x40,0x78,0x00},/*“机”,7*/ }; unsigned char CN1616_Index[] = “小极客打地鼠掌机”; //16*16中文字库索引表 编译,下载程序,可以看出汉字能够正常显示出来。 这里有个注意事项,工程中所有文件设置的编码格式必须统一。因为编译器不认识汉字,只认识汉字的编码,同一个汉字,其UTF-8与GB2312的编码是不一样。不同的编码在编译器看来,就是不同的汉字。所以要保证,工程里所有文件的编码方式都一致。我发的源码工程采用UTF-8的编码。 图片的显示 显示图片与显示汉字的原理是一样的,毕竟,,,,汉字也是被当成图片处理的嘛。话说,外国人眼里的汉字,可能就是图片。 我打算显示的图片是我自己的LOGO,像是刻在石碑上的字母Y。也需要借助取模软件把图片变成一个数组。 然后模仿人家的程序,把数组名字和枚举类型修改下。 //main.c showImage(0,0,128,8,Y_LOGO_ENUM); //OLED.c /** * 功能:在制定区域显示图片 * 参数: * x:x轴坐标 0-127 * y:y轴坐标 0-7 * x_len:显示区域横坐标长度 0-128 * y_len:显示区域纵坐标长度 0-8 * image_index:图片枚举索引 * 说明:该函数一般用于显示全屏LOGO,另外灵活运用可以显示PPT切换特效 * * 返回值:None */ void showImage(u8 xpos, u8 ypos,u8 x_len, u8 y_len,IMAGE_INDEX image_index) { u16 i,j; for(i=0;i《y_len;++i) //页地址控制 { setPos(xpos,ypos++); for(j=i*128+xpos;j《i*128+x_len;++j) //列地址控制 { switch(image_index) { case Y_LOGO_ENUM :writeData(Y_LOGO[j]); break; default : break; } } } } 显示大LOGO一次成功。 接着我设计了几个位图,都是32像素见方的,分别是上下左右,ABCD,圆和圆圈。万一以后要扩展炫舞或者太鼓达人的游戏呢, 代码也相应修改了下。数组里内容太长,就不列出来了。数组和位图都上传了。 //main.c //显示几个小LOGO formatScreen(0x00); showImage(0,0,32,4,UP_LOGO_ENUM); showImage(32,0,32,4,DOWN_LOGO_ENUM); showImage(64,0,32,4,LEFT_LOGO_ENUM); showImage(96,0,32,4,RIGHT_LOGO_ENUM); showImage(0,4,32,4,A_LOGO_ENUM); showImage(32,4,32,4,B_LOGO_ENUM); showImage(64,4,32,4,C_LOGO_ENUM); showImage(96,4,32,4,D_LOGO_ENUM); delay_ms(1000); 显示图片的函数也做了修改 void showImage(u8 xpos, u8 ypos,u8 x_len, u8 y_len,IMAGE_INDEX image_index) { u16 i,j; for(i=0;i《y_len;++i) //页地址控制 { setPos(xpos,ypos++); for(j=i*128+xpos;j《i*128+x_len;++j) //列地址控制 { switch(image_index) { case Y_LOGO_ENUM :writeData(Y_LOGO[j]); break; case UP_LOGO_ENUM :writeData(UP_LOGO[j]); break; case DOWN_LOGO_ENUM :writeData(DOWN_LOGO[j]); break; case LEFT_LOGO_ENUM :writeData(LEFT_LOGO[j]); break; case RIGHT_LOGO_ENUM :writeData(RIGHT_LOGO[j]); break; case A_LOGO_ENUM :writeData(A_LOGO[j]); break; case B_LOGO_ENUM :writeData(B_LOGO[j]); break; case C_LOGO_ENUM :writeData(C_LOGO[j]); break; case D_LOGO_ENUM :writeData(D_LOGO[j]); break; case CIRCLE_LOGO_ENUM :writeData(CIRCLE_LOGO[j]); break; case EMPTY_LOGO_ENUM :writeData(EMPTY_LOGO[j]); break; default : break; }//switch }//for j }//for i } 结果却出现了乱码,这是怎么回事? 解决乱码 我首先分析了showImage函数。函数的说明中,这句话引起了我的注意: 说明:该函数一般用于显示全屏LOGO,另外灵活运用可以显示PPT切换特效 全屏LOGO?全屏是128×64像素 ,我的Y-LOGO显示没有问题。而箭头是32×32像素的图片,显示出来却有问题,这说明,此函数可以显示128×64的图片,不能显示32×32的图片。 然后我开始分析显示函数的坐标体系。屏幕是黑白的,共有128×64个像素,每个像素有黑白两种状态,正好对应0和1两种状态,所以一个图片需要128×64个bit(二进制位),也就是需要128×8个byte(字节)来表示。x坐标取值范围是0-127,y坐标的取值范围是0-7,所以能猜出来,竖直方向上的8个二进制位,组成了一个字节。这对应了在使用取模软件时的一个细节:取模方式为列行式。先取最左边一列上的8个点,再往右取一列上的8个点。 还有一个需要说明的点:取字方向是低位在前。为了说明位图与数组的关系,我们来分析一下上箭头的图与数组的关系。 取模结果的前16位是 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFC,0xFC, 为了看的更清楚,我把位图与取模得到的数组结合在一起了。已经具体到这种地步了,就不用再说明图片是怎么变成数组了吧? 接下来把数组还原为图片。 首先要明确一个问题:数组是一维的,图片是二维的。是否可以使用二维数组?可以,但没有必要。用一维的数组储存二维的图片,假设图片的尺寸是x×y,那么图片中个点的坐标系是这样的。 最后一个点也可以写为(y-1)x+(x-1),与xy-1在数学上等价。 储存到数组中是这样的: 就像大家去操作做广播体操,做操时,排成阵列。然而操场的门很小,只能让一个人通过,因此离开操场时,要排成一队。 明白这一点以后,来分析代码 for(i=0;i《y_len;++i) //1 //页地址控制 { setPos(xpos,ypos++); //2 for(j=i*128+xpos;j《i*128+x_len;++j) //3 //列地址控制 { switch(image_index) { case Y_LOGO_ENUM :writeData(Y_LOGO[j]); break;//4 }//switch }//for j 代码1,i《y_len 说明一次显示一横行。但由于竖直方向上的8个二进制位,组成了一个字节,为了方便描述,我们假设数值方向上的8个二进制位是一个排,所以i《y_len 实际上显示了一横“排”。 代码2,设置原点,直接使用了传入的坐标系,没毛病 代码4,把参数j作为数组索引来显示,如果想显示第一个“像素排”,那么j = 0,如果想显示第二排的第一个“像素排”,那么j = 0+x_len。 那么代码3,为什么i要×128? 问题就出现在这里了。显示大LOGO的时候,这个函数是可以用的,因为大LOGO的宽度正好是128。显示32×32的小图片的时候,函数就用不了了,因为,图片的宽度是32啊! 稍作修改,用传入的图片宽度参数替换掉128。大功告成。 //for(j=i*128+xpos;j《i*128+x_len;++j) //3 //列地址控制for(j=i*x_len;j《i*x_len+x_len;++j) //列地址控制 代码其实还可以优化 我发现屏幕刷新的速度太慢了,玩游戏的朋友都知道,FPS太低,画面看起来就卡顿。这怎么能忍,FPS太低会影响我超神的啊! 只显示分数当然没问题,但如果需要玩太鼓达人,或者炫舞之类,依赖屏幕刷新图片的游戏,肯定会卡顿。我分析了一下,可能是IIC总线太慢。代码里IIC操作的延迟都是1us,OLED屏幕最小支持350ns。但是STM32做纳秒级的延时,理论计算并不可靠,需要实测,且提升有限,未做尝试。 也考虑过使用SPI,理论上快不少。用一个大数组把屏幕上所有点的信息都记下来,数组也就1024个元素就能存储所有的的信息,然后使用定时器+DMA+SPI,每隔一小段时间就刷新一下屏幕,只修改有变化的像素点,理论上速度快很多。暂时先做打地鼠的游戏,有空再折腾吧。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1802 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1629 浏览 1 评论
1096 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
735 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1685 浏览 2 评论
1944浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
746浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
580浏览 3评论
602浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
565浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 23:54 , Processed in 0.826439 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (威廉希尔官方网站 图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号