屏幕显示英文字符

  有个屏幕,掌机的可玩性可以大大增强,打地鼠玩腻了,可以玩个贪吃蛇,俄罗斯方块,以及其他需要开动想象力的游戏。并且,以前总是玩别人的游戏,现在可以自己写游戏,岂不美哉。
  后来我感觉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.cformatScreen(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//显示几个小LOGOformatScreen(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,那么图片中个点的坐标系是这样的。

0 1 x-2
x x+1 2x-2
(y-1)x (y-1)x+1

  最后一个点也可以写为(y-1)x+(x-1),与xy-1在数学上等价。
  储存到数组中是这样的:

0 1 。。。 x-1 x x+1 。。。 (y-1)x (y-1)x+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,每隔一小段时间就刷新一下屏幕,只修改有变化的像素点,理论上速度快很多。暂时先做打地鼠的游戏,有空再折腾吧。
最终代码在这里。

STM32掌机教程4,STM32驱动OLED屏幕相关推荐

  1. STM32掌机教程2,掌机的原理

    本文用到的资源PDF格式的原理图和集成库资源 打地鼠的逻辑   打地鼠的逻辑很简单:我点亮某个LED,然后开始计时.如果在规定的时间内按下了LED对应的按键,那么加分,点亮下一个LED.按错按键,或者 ...

  2. STM32掌机教程1:我打算做一个开源掌机

    电子开发对我的影响 爱好作为工作,既能养家,又开心,不用上班像上坟. 动手实现自己的想法,成就感满满. 思维方式的影响:把如何解决问题放在第一位. 什么都能修,自带省钱属性    然而,我发现,很多学 ...

  3. STM32掌机教程9,完成掌机

      这是教程的最后一篇了,完成之前的任务,到这里一切都应当水到渠成,没什么好讲的了.结尾可能略显仓储,未尽之处,自己看代码吧,看不懂的地方可以在下边评论. 修改BGM   之前已经实现了根据不同的按键 ...

  4. STM32掌机教程3,工程模板与带灯按键测试

    我们需要"脚手架"   关于代码,我想体现出这么一个过程:我是如何一步一步修改代码的.我认为,从学习的角度来考虑,直接看最终的代码没有什么意义. 写代码就像工人盖房子,盖房子过程中 ...

  5. STM32掌机教程6,电子琴

      本节原来是想讲一讲无源蜂鸣器发声的原理,用于添加BGM功能.为了讲原理,就写了一些通俗的代码,没想到越写越多,后来,干脆就形成了一个小小的项目吧--基于STM32与无源蜂鸣器的电子琴. 灯光效果 ...

  6. STM32掌机教程7,演奏音乐

    使用定时器来计算时间   在电子琴这节中,我们已经讲述了蜂鸣器的原理,知道如何用蜂鸣器演示不同音调的音乐,本节改进根据频率计算周期的方法,改为定时器,精确度更高,且不再阻塞CPU.   首先,我们不再 ...

  7. ⑥ESP8266 开发学习笔记_By_GYC 【ESP8266 驱动 OLED屏幕】

    ⑥ESP8266 开发学习笔记_By_GYC [ESP8266 驱动 OLED屏幕] 目录 ⑥ESP8266 开发学习笔记_By_GYC [ESP8266 驱动 OLED屏幕] 适用范围 user_c ...

  8. ESP8266(NodeMCU)+Lua语言+IIC驱动OLED屏幕

    前言 本来打算用Arduino IDE驱动OLED屏幕,但是因为在家无法安装Arduino的第三方库,改用ESPlorer和Lua语言驱动. 硬件准备 1. NodeMCU开发板 2. OLED屏幕( ...

  9. STM32使用IIC总线通讯协议在OLED屏幕上显示字符串、汉字、图像(硬件IIC)

    参考:基于STM32-Oled(IIC)的使用 作者:奋斗的小殷 发布时间: 2021-05-07 13:09:26 网址:https://blog.csdn.net/boybs/article/de ...

最新文章

  1. 使用 Fries 创建性感的 Android 风格移动应用界面
  2. Ubuntu 18.x 环境下mysql创建用户并更改用户权限
  3. wireshark: there are no interfaces on which a capture can be done
  4. pmo与敏捷团队可以共存吗_匿名与问责制可以共存吗?
  5. android drawLayout抽屉布局
  6. 成信大c语言答案P239.C,2011秋季江苏省计算机二级C语言试题与答案.doc
  7. 湖南联通云计算与IDC掀发展浪潮
  8. HTML注册页面代码
  9. 今天懒一次 豆瓣电影Top250
  10. 八个经典的java多线程编程题目
  11. scrapy爬虫简单实例
  12. Alexa 世界网站排名研究(上)(转)
  13. html中颜色取样器工具,PS颜色取样器工具使用方法
  14. 第一台电子计算机诞生于1944,(),第一台数字电子计算机问世。A、1944年B、1945年C、1946年 - 众答网问答...
  15. 2014蓝桥杯--史丰收速算(C++)
  16. 台湾大学林轩田机器学习技法课程学习笔记8 -- Adaptive Boosting
  17. IRIS的镜像配置(2)
  18. 5 月计算机语言排行,TIOBE 5月编程语言排行榜
  19. 如何解决万能地图下载器下载的地图和选择范围不一致
  20. 计算机无法打开pdf文件夹,电脑为什么打不开PDF文件?

热门文章

  1. javaweb公交查询系统
  2. Revit学习笔记003-(Revit二次开发用的9个实践)
  3. JedisSentinelPool 连接 master
  4. 视频工具箱和硬件加速
  5. VeryCD挥别影视做游戏 月入亿元转型成功
  6. 数据结构(邓俊辉):栈与队列
  7. 句子反转(“hello xiao mi”- “mi xiao hello”)
  8. 优秀的 Verilog/FPGA开源项目介绍(十二)- 玩FPGA不乏味
  9. CTF之Avatar
  10. 供应链平台物流、售后管理功能详解:SCM系统软件撬动新能源汽车企业发展空间