上次我们学习了如何使用IIC与AHT20(DHT20)进行通信,获取房间温度,并以此为例子,感受了状态机编程。本次我们就开始学习同样连接在I2C1总线上的OLED屏幕。
想必大家都有所耳闻,所谓的屏幕,其实就是由一个个的发光小灯组成,每个小灯称为1个像素,只要在屏幕上有选择地点亮一部分小灯,就可以显示我们想要的图案。屏幕上小灯排列的数目,就是我们常说的"分辨率"。例如我们开发板上的这块OLED屏幕的分辨率,就是单片机中常见的128*64,就是说有128列64行小灯,这么多的小灯,如果按照我们之前控制开发板上RGB小灯的方法,一个IO口控制一个小灯的话,那STM32得有8000多个引脚才能满足需求,这显然是不可能的。所以也就有了屏幕驱动芯片这等神器。我们只需要通过IIC或者SPI等通信协议与屏幕驱动芯片进行通信,就可以操纵屏幕上这8000多个小灯的亮灭了。
除了我们开发板上使用的CH1116芯片,常用的还有SSD1306等芯片,都可以驱动128*64的OLED屏幕。它们的功能与原理也大同小异,只是部分指令和特殊功能有些差异。这类芯片将整块屏幕划分为了8个page,或者我们称其为页 ,每页依旧是128列,但仅有8行。

我们都知道,一个字节正好就是8位0/1数据,所以划分页的目的,就是为了我们可以用一个字节的数据,表示某一页的某一列8个像素的亮灭,例如我们通过IIC通信告诉屏幕驱动芯片,将第0页的第3列,设置为0xC1,也就是1100 0001,那么效果就是这样的。第0页第3列最高位上的两个像素亮起,以及最低位上的一个像素亮起。
那么如何通过IIC通信操作屏幕驱动芯片呢?根据资料包中这本英文的,十分冗长的不想读,不需要读的CH1116手册。CH1116的IIC地址为0x7A,与CH1116的通信分为两类,一类为指令,除了IIC地址0x7A外,指令要以0x00开头,然后是一字节的指令,各种操作的指令在文档中有详细的描述。不过大家需要了解的其实只有如何设置页地址与列地址。
例如,如果我们需要将页地址设置为0,则指令是0xB0,而将页地址设置为7,则指令是0xB7。设置列地址的指令则分为两次,如果我们要将列地址设置为90,也就是0x5A,则要发送指令0x0A,将列地址低4位位置为A,然后发送指令0x15将列地址高4位设置为5。
另一种通信类型为数据,除了IIC地址0x7A外,数据要以0x40开头,然后发送任意数量的数据,屏幕驱动芯片会根据当前的页地址与列地址,将这些数据代表的像素亮灭表现在屏幕上。而且,CH1116等芯片有一个十分方便地特性,就是设置完一字节的8个像素后,列地址会自动+1。这样下一个数据就可以写到本页的下一列里。
举个例子,利用这一特性,我们只需要将页地址设置为0,列地址也设置为0,然后一股脑发送128个字节,就可以直接完成一页屏幕的像素设置。而将页地址递增,如此循环8次,就可以完成整个屏幕的像素设置。

理论知识总是有些枯燥的,我们不妨真正地去点亮屏幕上的几个像素。来到Cube IDE先来轻车熟路地新建一个工程,工程名称就叫做oled。首先我们将用来与CH1116通信的I2C1打开,设置为I2C模式。

由于一会的数据量会比较大,我们将I2C的速度从标准模式(Standard Mode)改为快速模式(Fast Mode),可以注意到I2C时钟速度从100K提升到了400K

另外,其实截止到当前,我们的STM32一直处于使用内部时钟8M速度运行,为了更快,我们可以使用开发板上准备的外部晶振给STM32提速。打开System Core中的RCC,将告诉时钟源HSE(High Speed Clock)设置为Crystal/Ceramic Resonator,也就是晶振。

随后打开Clock Configuration时钟设置,将HCLK这里设置为最大值72,输入+Enter,这个弹框是说使用当前的时钟源实现不了我们需要的频率,是否切换其他时钟源,我们点OK,稍等片刻Cube MX就自动将时钟源切换到了外部晶振,并实现了72MHz的时钟频率。

接下来我们点开Project Manager还是找到代码生成器Code Generator,勾选上为每个外设生成单独的.c/.h文件

一切准备就绪,保存并生成代码。
为了对OLED进行操作,我们不妨再来为OLED建一对.c与.h文件,还是在Inc文件夹右键->New->Header File,名称为oled.h


先来include一下待会会用到的i2c.h,随后Src文件夹右键->New->Source File,名称为oled.c


include一下oled.h,然后与之前一样,我们先define一下OLED的I2C地址为0x7A,免得写错以及方便修改,然后为了方便,我们不妨先来包一个发送指令的函数OLED_SendCmd。其参数是我们要发送的一字节的指令。然后其中我们定义一个两字节大小的数组sendBuffer,第0个字节是我们刚说的,代表指令通信的0x00,第1个字节则为指令。

然后就可以将这条指令信息,通过我们之前所学的I2C发送函数HAL_I2C_Master_Transmit,发送到CH1116芯片。第一个参数是外设句柄的指针,也就是&hi2c1,第2个参数是从机地址,也就是我们定义的OLED_ADDRESS,第3个参数是发送的数据的指针,第4个参数是数据长度2,最后一个参数是超时时间,我们填一个永久等待。
void OLED_SendCmd(uint8_t cmd)
{
uint8_t sendBuffer[2];
sendBuffer[0] = 0x00;
sendBuffer[1] = cmd;
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,sendBuffer,2,HAL_MAX_Delay);
}
接下来我们就可以利用这OLED_SendCmd函数对CH1116进行一些指令操作了。与AHT20一样,CH1116等屏幕驱动芯片也是需要进行初始化的,所以在真正地在屏幕上绘制图案前,我们需要先定义一个OLED的初始化函数OLED_Init。OLED初始化的过程比较地繁杂,所以在此处不得不使用秘技·复制粘贴大法。
void OLED_Init()
{
OLED_SendCmd(0xAE); /*关闭显示 display off*/
OLED_SendCmd(0x02); /*设置列起始地址 set lower column address*/
OLED_SendCmd(0x10); /*设置列起始地址 set higher column address*/
OLED_SendCmd(0x40); /*设置起始行 set display start line*/
OLED_SendCmd(0xB0); /*设置页地址 set page address*/
OLED_SendCmd(0x81); /*设置对比度 contract control*/
OLED_SendCmd(0xCF); /*128*/
OLED_SendCmd(0xA1); /*设置分段重映射 从右到左 set segment remap*/
OLED_SendCmd(0xA6); /*正向显示 normal/reverse*/
OLED_SendCmd(0xA8); /*多路复用率 multiplex ratio*/
OLED_SendCmd(0x3F); /*duty = 1/64*/
OLED_SendCmd(0xAD); /*设置启动电荷泵 set charge pump enable*/
OLED_SendCmd(0x8B); /*启动DC-DC*/
OLED_SendCmd(0x33); /*设置泵电压 set VPP 10V*/
OLED_SendCmd(0xC8); /*设置输出扫描方向 COM[N-1]到COM[0] Com scan direction*/
OLED_SendCmd(0xD3); /*设置显示偏移 set display offset*/
OLED_SendCmd(0x00); /*0x00*/
OLED_SendCmd(0xD5); /*设置内部时钟频率 set osc frequency*/
OLED_SendCmd(0xC0);
OLED_SendCmd(0xD9); /*设置放电/预充电时间 set pre-charge period*/
OLED_SendCmd(0x1F); /*0x22*/
OLED_SendCmd(0xDA); /*设置引脚布局 set COM pins*/
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB); /*设置电平 set vcomh*/
OLED_SendCmd(0x40);
OLED_SendCmd(0xAF); /*开启显示 display ON*/
}
是不是看起来很繁杂呢,不过没关系,这些代码通常会由屏幕的厂商为我们提供,我们使用时进行简单地移植即可。大家其实并不太需要在意它们具体是何含义。有想稍微了解的,我也对照了手册给每一条指令标注了注释。
接下来我们就试着控制CH1116点亮屏幕上的像素点,因为只是测试,我们就写一个OLED_Test函数。根据我们刚刚所讲述的。如果我们想让第0页第0列的8个像素,一亮一灭地间隔点亮,那么我们首先要发送0xB0指令,将页地址设置为0页,然后分别发送0x00与0x01指令,将列地址设置为0x00,然后我们定义一个长度为2字节的数组,其内容第0字节为0x40,表示接下来发的是要显示在屏幕上的数据,然后第1字节是0xAA,也就死二进制的1010 1010表示亮灭亮灭 亮灭亮灭。随后我们还是使用I2C发送函数将其发送出去,为了一会方便修改,发送长度这里我们用sizeof取一下sendBuffer的长度。
void OLED_Test()
{
OLED_SendCmd(0xB0);
OLED_SendCmd(0x00);
OLED_SendCmd(0x10);
uint8_t sendBuffer[] = {0x40, 0xAA};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, sendBuffer, sizeof(sendBuffer),HAL_MAX_DELAY);
}
那么这样就能让第0页第0列8个像素间隔点亮吗?其实这里还有一个我一直没有提到的小细节,我们开发板上使用的这块屏幕,为了可以方便地进行滚屏等操作,将列地址向左移动了2位,也就是说第0列的列地址,其实是0x02,所以为了真正地将第0页第0列8个像素点亮。我们需要通过发送0x02 ``0x10指令,将列地址设置为0x02,有些绕,不过这样我们也就算是写好了。
void OLED_Test()
{
OLED_SendCmd(0xB0);
OLED_SendCmd(0x02);
OLED_SendCmd(0x10);
uint8_t sendBuffer[] = {0x40, 0xAA};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, sendBuffer, sizeof(sendBuffer),HAL_MAX_DELAY);
}
我们将OLED_Init与OLED_Test函数在oled.h中进行一下声明,然后来到main.c,首先在include注释对中引入oled.h。随后在进入死循环之前,也就是UAER CODE 2注释对中,首先使用OLED_Init()函数对OLED屏幕进行初始化,然后使用OLED_Test()函数看看是否真的能点亮我们想要的像素点。
对了,有一点要注意的是,OLED屏幕的启动比STM32要慢一点,所以我们最好在OLED_Init前加个20ms延时。好了,编译下载一气呵成,恭喜,得到了一个花屏。好吧,这其实是因为刚启动的CH1116中的数据是随机的。这样数据为1的地方像素就被点亮,为0的地方像素就熄灭,也就造成了花屏,不过这至少证明我们的屏幕启动了,在第0页第0列的位置,好像确实间隔点亮了4个像素点,这说明我们成功控制了屏幕的第0页第0列。我们试试将发送的数据再增加几个0xAA
void OLED_Test()
{
OLED_SendCmd(0xB0);
OLED_SendCmd(0x02);
OLED_SendCmd(0x10);
uint8_t sendBuffer[] = {0x40, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, sendBuffer, sizeof(sendBuffer),HAL_MAX_DELAY);
}
可以看到,更多的像素被我们控制了,既然如此,只要我们将OLED屏幕的8页全部125*64个像素全部设置为0,屏幕就不会花屏了。
既然我们要一次性操作这么多的像素,不妨我们来搞一个"显存",定义一个二维数组GRAM,大小是8页乘以128列,这样正好可以缓存128*64个像素的值。
uint8_t GRAM[8][128];
然后我们来写一个叫做OLED_NewFrame的函数,意思是调用此函数后,开始画新的一帧屏幕,而其作用就是将显存清空为0。最容易想到的就是,我们可以用一个双层的for循环,遍历显存里的每一字节,将其设置为0。
void OLED_NewFrame()
{
for(uint8_t i = 0; i < 8; i++)
{
for(uint8_t j = 0; j < 128; j++)
{
GRAM[i][j] = 0;
}
}
}
当然我们也可以使用更高效的写法,memset函数,他可以将一整块内存都设置为同一个数,比如我们将从数组首地址GRAM往后的数据都设置为0,设置的数量,就取GRAM的大小。另外memset函数需要从string.h中引入,否则会报warning
#include "string.h"
void OLED_NewFrame()
{
memset(GRAM, 0, sizeof(GRAM));
}
然后我们再来写一个将显存显示到屏幕上的函数OLED_ShowFrame,首先要定义一个sendBuffer数组来存储将要发送的数据,sendBuffer中的第0字节设置为0x40,表示发送的是显示在屏幕上的数据。随后比较易等的方案是,首先写个for循环,循环8次表示第0页到第7页,再在每页的循环中嵌套一系列的循环,j从0到127,将sendBuffer的第j+1字节都赋值为第i页第j列的数据,随后将页地址与列地址移动到第i页的第0列,此处设置页地址到第i页的指令是0xB0+i,然后将我们准备好的数据发送出去。
void OLED_ShowFrame()
{
uint8_t sendBuffer[129];
sendBuffer[0] = 0x40;
for(uint8_t i = 0; i < 8; i++)
{
for(uint8_t j = 0; j < 128; j++)
{
sendBuffer[j+1] = GARM[i][j];
}
OLED_SendCmd(0xB0 + i);
OLED_SendCmd(0x02);
OLED_SendCmd(0x10);
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, sendBuffer, sizeof(sendBuffer),HAL_MAX_DELAY);
}
}
更高效的方案是将此处的列循环,直接用memcpy代替
void OLED_ShowFrame()
{
uint8_t sendBuffer[129];
sendBuffer[0] = 0x40;
for(uint8_t i = 0; i < 8; i++)
{
memcpy(sendBuffer + 1, GRAM[i],128);
OLED_SendCmd(0xB0 + i);
OLED_SendCmd(0x02);
OLED_SendCmd(0x10);
HAL_I2C_Master_Transmit(&hi2c1, OLED_ADDRESS, sendBuffer, sizeof(sendBuffer),HAL_MAX_DELAY);
}
}
将OLED_NewFrame与OLED_ShowFrame也通过oled.h进行声明,随后我们再次回到main.c,在OLED初始化之后,首先调用OLED_NewFrame清空显存,随后调用OLED_ShowFrame将显存刷新到屏幕上,再之后是我们的OLED_Test让第0页的前几列的像素间隔亮起。
编译下载,我们可以看到花屏已经不见了,前几列也是间隔亮起

回到代码,好像有哪里可以改进,既然ShowFrame函数是直接将显存里的数据显示在屏幕上,那我们是不是也就不必再去直接操作屏幕了,而是在NewFrame函数后将要显示的数据"画"在显存中,然后直接ShowFrame函数将显存显示在屏幕里就好了?
说干就干,我们来定义一个OLED_SetPixel函数,意思是设置某个像素亮起,描述屏幕是,我们通常使用这样一个坐标系------x,y代表此像素在屏幕上的位置,只要调用此函数,我们就将显存中(x,y)位置的像素设置为1,这个函数写起来也不算复杂,首先判断一下x与y的取值,防止数组越界,然后将显存第y/8页第x列的数据,或上一个0x01向左移y%8位。嗯~相对来说,确实《不是很复杂》。
void OLED_SetPixel(uint8_t x, uint8_t y)
{
if(x >= 128 || y >= 64) return;
GRAM[y/8][x] |= 0x01 << (y % 8);
}
举个例子,就比如说我们想让(15,12)这个像素亮起,则x=15 y=12。那么对于类型位uint8_t的y来说y/8 = 1 y%8 =4。所以也就是让第1页的第15列的显存或上了0x01向左移4位。其他位的数据都保留为原样,第四位的数据被设置为1。
好的,我们将OLED_SetPixel也在oled.h中声明,随后回到主战场main.c,这次我们试试在while循环中搞点事情。我们先来做个0到64的for循环。还是老样子,首先使用OLED_NewFrame清空显存,然后画一个像素点,x坐标是2倍的i,y的坐标是i。然后我们使用OLED_ShowFrame将显存显示到屏幕上。
编译下载看一下效果,我们可以看到在屏幕上游走的像素点。好了,现在已经可以在屏幕的任何地方画点了。俗话说得好,点连成线,线连成面。那么,试试在屏幕上为哥哥应援吧,好像有点难。
怎么把文字和图片显示到屏幕上呢?难道真的要一个点一个点对着画吗?其实也不必,只要我们可以搞到一个字或者一个图片,它的像素亮灭数据,将其按照约定好的顺序贴到显存的某个位置,再将显存数据发送到屏幕驱动芯片,我们想要的字或者图片不就显示在屏幕上了吗,这字或者图片的像素亮灭数据我们称其为字模和图模,而获得字模/图模的过程,我们称其为"取模"。我们使用的取模软件是PCTOLCD,这款软件虽然老久了,但确实为我们生产了不少的图模/字模,但时至今日,作为大佬2002年的毕设作品,PCTOLCD确实显得有些功能单一,不够现代化,甚至有些兼容性问题。
在本次学习中,我们使用新的LED取模助手。只要在浏览器中输入led.baud-dance.com,就可以来到取模助手了。大家只需要在输入框中输入想要取模的文字,就可以马上预览到取出的字模效果,并且立即就可以获取到字模数据,字模数据的样式是根据生成模板的字模模板生成,可以换用其他模板。

如果对所以的模板都不满意,也可以自定义自己的字模模板。另外,大家也可以切换不同的字体,自由设置每个字模的宽高。支持列行式、行列式、逐列式与逐行式全部4种取模方式,并且配有每种取模方式的动画演示。支持阴阳码的切换,除了取字模,同样也支持取图模,选择一张图片,可以自动获取图片名称作为图模的变量名,当然也可以自由修改,可以自由地缩放图模。支持调节对图片进行二值化的阈值。同样也支持切换或者自定义生成模板,每次修改都可以实时生成取模数据,并且立即预览到。此外波特律动取模助手与我们之前的波特律动串口助手一样,也是开源软件。
虽然取模助手采用了因为太过自由而导致不太自由的GPL协议,不过大家不用担心,按照GPL协议的规定,由GPL软件生成的内容并不需要遵守GPL协议,所以大家生成的字模/图模完全可以关源商用。这就完了?不,OLED屏幕作为一种数据的显示渠道往往并不是我们程序的核心所在。我们已经了解了屏幕控制的基本原理,但我们通常更希望直接告诉屏幕要在何处显示何物就好,而不是去繁琐地考虑要在哪里画点,所以大家可以直接使用波特律动OLED驱动库,只要点击这里就可以来到驱动库列表。此外驱动库同样也是开源的,遵循MIT开源协议,可以闭源商用。
我们要如何使用此驱动库与取模助手呢,可以看到详情里有Readme进行了介绍,首先我们点击下载按钮获取到示例工程,下载到的压缩包解压后,除了直接玩耍示例工程,也可以直接将Core/Src中的oled.c``font.c拖入到我们自己工程的Src中


Core/Inc中的oled.h``font.h拖入到我们自己工程的Inc中


打开oled.c,可以看到代码中的注释十分完善,这里就是我们刚刚提到的繁琐的初始化过程。

随后也有我们刚刚写的OLED_NewFrame与OLED_ShowFrame函数

OLED_SetPixe函数

此外还有画线函数,画矩形函数,画实心矩形函数,画三角和画实心三角函数,画圆和画实心圆的函数,甚至还有画椭圆的函数。我们可以来试一试。
回到main.c,保留延时20ms与OLED的初始化。

然后在for循环里,咱们来画几个圆玩玩,可以使用alt+/获取代码提示,画图形的函数都是OLED_DrawXxx系列的。将鼠标移到函数上可以看到各个参数的含义。参数x,y是圆心,r是半径,最后一个参数是画正常颜色还是反色,我们不妨将圆心设定在屏幕的中心(64,32)处,半径r就设定为i,最后一个参数是库中大多数函数都有的参数,颜色。可以在oled.h中看到其有两个取值,我们一般使用OLED_COLOR_NORMAL即可,OLED_COLOR_REVERSED则是反色模式,让像素熄灭。用于已经在画好的背景上进行镂刻。
回到main.c,我们就填OLED_COLOR_NORMAL。不妨在多画几个,半径依次成倍放大,看起来不错。编译下载一气呵成。除了画圆,大家也可以尝试一下各种组合画出各种图案。
while(1)
{
for(uint8_t i = 0; i < 64; i++)
{
OLED_NewFrame();
OLED_DrawCircle(64,32,i,OLED_COLOR_NORMAL);
OLED_DrawCircle(64,32,2*i,OLED_COLOR_NORMAL);
OLED_DrawCircle(64,32,3*i,OLED_COLOR_NORMAL);
OLED_ShowFrame();
}
}
在OLED_DrawXxx系列函数中,还有一个OLED_DrawImage函数,可以用来画一幅图片,它比你所见过的任何单片机画图库都要方便,因为他是配套波特律动取模助手使用的。

void OLED_DrawImage(uint8_t x,uint8_t y,const Image *img,OLED_ColorMode color)
{
OLED_SetBlock(x,y,img->data,img->w,img->h,color);
}
来到取模助手的图片取模功能,选择电脑上的一张图片,图片取模的整个功能都是在浏览器端完成的,并不会上传大家的图片,完全没有隐私问题,可以为图片改一个合适的变量名,然后我们将图片调一个合适的大小与二值化阈值。取模方式要选择默认的列行式,可以根据需要切换阴阳模。随后,我们就可以直接复制生成的图模数据,到font.c的最后进行粘贴。

当然,为了在main.c中调用,我们还得将其在font.h中进行extern导出,随后就可以回到main.c,还是在for函数中试试OLED_DrawImage,前两个参数还是绘制图片的位置,可以根据自己的需求填写,第3个参数就填写我们刚刚生成的图模的指针,最后一个参数还是OLED_COLOR__NORMAL。编译下载看一下。
接下来为大家隆重介绍波特律动OLED库的文字功能,在font.h中,大家可以看到有两种字体结构体,只有ASCII字符的ASCIIFont与支持任意文字的Font,针对字母数字以及常见的英文标点等ASCII字符。

驱动库为大家内置了四种大小的字体数据,可以在font.c中看到它们所包含的数据。

而另一种字体结构Font则支持任意UTF-8字符,可以看到Font字体中还带有一个ASCIIFont的指针,用于当在Font字体中找不到对应ASCII字符的字模时缺省寻找,可以使用上面这四种ASCII字体,这样我们不需要为Font字体准备ASCII字符的字模,也能直接显示字母数字以及常见的英文标点了,可以看到目前自带的Font字模中仅有"波特律动"这样的四个字,我们不妨先用这4个字试试。
来到main.c,在for循环中使用OLED_PrintString来显示文字,坐标可以这样填写,与大多数OLED驱动库显示汉字需要一个一个填写汉字在字模数组中的坐标不同,波特律动OLED驱动支持直接填写字符串,当然显示的文字必须是我们字体中有的,目前也就是"波特律动"。另外由于我们的字体有缺省的ASCIIFont字体,所以也可以填写ASCII字符,字体就填默认的这个字体,颜色依旧是OLED_COLOR_NORMAL。
while(1)
{
for(uint8_t i = 0; i < 64; i++)
{
OLED_NewFrame();
OLED_DrawImage(43, 0, &bilibiliImg, OLED_COLOR_NORMAL);
OLED_PrintString(64-2*i, 40, "波特律动,Hello!", &font16x16, OLED_COLOR_NORMAL);
OLED_ShowFrame();
}
}
编译下载一气呵成。
那么如何写下其他的文字呢?回到波特律动取模助手,字体取模功能,我们可以换个其他字体,搞个更大的文字,取模方式还是列行式,阴阳码选择阳码,然后再需求文字中写下我们可能会用到的文字,注意生成模板一定要选择波特律动OLED驱动,因为此模板生成的字模前四位是此字符的UTF-8编码,可以用来检索中文,这是我们在调用PrintString函数是可以直接写中文字符的关键。因为PCTOLCD以及其他任何取模软件都无法满足这一需求。
我们直接复制字模数据,回到font.c进行粘贴,因为大小与之前字模的大小不一样,所以不需要删除旧的字体,不过需要将新字体在font.h中extern导出一下。如果生成的新字体与原带的16x16的字体大小相同,则需要删除原字体,以防发生变量名重复,复制下字体名称,我们回到main.c,将字体换成我们的新字体font24x24,注意这个乘号其实是小写字母x,字符串也要改成我们新字体中有的文字。
编译下载可以看到!有点小,这是因为我们字体的缺省ASCII字体默认是16x8,我们将其改成稍微大一点的24x12,再次编译下载。
总结:
本次学习我们先了解了OLED屏幕的基本控制原理,然后向大家介绍了波特律动OLED驱动以及配套的波特律动取模助手,展示了如何在屏幕上绘制各种几何图形、图片以及文字。大家在日后的使用中只需要打开波特律动取模助手,寻找适合你单片机与驱动芯片的OLED驱动库,就可以获得统一而愉快的OLED操作体验啦。如果还没有你需要的单片机或者驱动芯片,可以试着进行移植。