目录

基于STC8G2K64S4单片机通过I2C硬件控制OLED屏幕

基于STC8G2K64S4单片机通过I2C硬件控制OLED屏幕

#前言

本文主要是讲解了如何使用STC8G2K64S4单片机自带I2C硬件来控制OLED屏幕

写这篇文章主要是为了之后的模拟量光敏调节参数,使参数直接显示在OLED屏幕中更方便调节

在刚学习之时,查遍很多关于I2C通讯的文章,基本都说的很笼统,甚至还有的翻译错的,这里就根据个人的学习经历来讲解

硬件方面

https://i-blog.csdnimg.cn/blog_migrate/e5578fea5c7792f44dc6197527785141.jpeg

1、STC8G2K64S4单片机主控板一块 (需要有P24和P25引脚,用来I2C通讯,芯片手册中有写)

2、5v输出可调电源(也可以用电池接稳压模块)

3、0.96寸OLED显示屏模块(SCL接P25 SDA接P24 )

OLED屏幕可以理解成由很多的LED组成,我用的是12864的,所以就是12864个LED,如下图所示

https://i-blog.csdnimg.cn/blog_migrate/b35c3df8fc85aeefab3a3e29d954c216.png

OLED发光的原理就是亮灯,把这个128*64个灯分组,分为8大行和128列,所以每组就是8个LED灯,所以之后就是发送8位二进制数来控制屏幕输出。通过取模软件可以直接生成数组

https://i-blog.csdnimg.cn/blog_migrate/cd0091119b34d9e7683ca0858e1f95de.png

软件方面

I2C通讯

I2C是由 Philips 公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C总线简单而有效,占用的 PCB空间很小,芯片引脚数量少,设计成本低.I2C总线是一种多主机总线,连接在 I2C总线上的器件分为主机和从机。之后用到的函数需要OLED的从机地址。

组成 I2C 总线的两个信号为数据线 SDA 和时钟 SCL,如果使用软件I2C通讯则可以选用任意引脚。

SCL为高电平时,SDA由高变低表示起始信号;SCL为高电平时,SDA由低变高表示停止信号;起始信号和停止信号都是由主机发出,起始信号产生后总线处于占用状态,停止信号产生后总线被释放,处于空闲状态。

https://i-blog.csdnimg.cn/blog_migrate/8ff840101b9df4faae658136bb607d1d.png

下面是数据传播的结构图

https://i-blog.csdnimg.cn/blog_migrate/8ab944b6e79f4d81c1d6d864b05d2763.png

这里做几点说明

Slave Address(从机地址):从属地址为0111100或者0111101,其中这段二进制字节中最后的0或1是由硬件SA0决定的。最后一位R/W#是读写位,当此位是0 时,IIC为写模式

因为函数里写了向左移位,所以地址是0x3c(111100)

Control byte:Co为0时,那么接下来的信息将包含数据字节。D/C#位决定数据字节时命令还是数据。当D/C#为0时,紧接着的数据字节为命令,当D/C#为1时,紧接着的数据字节为储存在GDDRAM的数据

所以命令是:0x80;数据时0x40

所以说每次发送的单个的信息是有三个数组成的,如下

iic_write_reg(0x3C, 0x80, 0x20);    //单个信息

SSD1306命令

SSD1306是屏幕里的芯片,主要是处理单片机通过I2C协议发送过去的信息,然后来判断并执行命令

https://i-blog.csdnimg.cn/blog_migrate/0e71d1a96637d34c5e8192fffe3441fb.png

1、 0x81:调节屏幕对比度,也就是亮度,范围是0x00~0xff,用法是给从机发送连续的两个数据,如下

iic_write_reg(0x3C, 0x80, 0x81);    //开启调节亮度
iic_write_reg(0x3C, 0x80, 0xff);    //调节亮度为0xff	

2、0xa4/a5: 控制输出内容,当选择0xa4时,屏幕显示的是内部存储的RAM值;当选择0xa5时,屏幕将输出显示,不论内部存储RAM的值是多少,也就是全亮

iic_write_reg(0x3C, 0x80, 0xa4);    //选择输出RAM值
iic_write_reg(0x3C, 0x80, 0xa5);    //选择不输出RAM值	

3、0xa6/a7:控制内部存储的RAM值像素点开或者关,当选择0XA6时,RAM处存储的0,是像素点关,1是像素点开;当选择0XA7时,RAM处存储的0,是像素点开,1是像素点关

就是说如果RAM里存储值全是0,那么发送0xa6就是黑屏的状态,发送0xa7,那么屏幕就是点亮的状态,就好比相反一下,亮变暗,暗变亮

iic_write_reg(0x3C, 0x80, 0xa6);    //数据为0不亮
iic_write_reg(0x3C, 0x80, 0xa7);    //数据为1不亮	

4、0xae/af:控制屏幕开关,ae就是打开屏幕,af就是关闭屏幕;这是显示前必不可少的一条命令

iic_write_reg(0x3C, 0x80, 0xae);    //打开屏幕
iic_write_reg(0x3C, 0x80, 0xaf);    //关闭屏幕	

接下是寻址模式的选择,主要有三种,水平寻址模式,垂直寻址模式,页寻址模式。这个也需要通过发送命令去选择

选址命令为0x20,在每次发送选址类型时前都要发送这个,要告诉从机我要进行选址

iic_write_reg(0x3C, 0x80, 0x20);    //打开选址

1、 0x02页选址模式:当处于此模式时, 在GDDRAM访问后,列地址指针将自动增加1。如果列地址指针到达列终止地址,列地址指针将复位到列起始地址,但页地址指针不会改变。如果需要下一页访问则需再发送一条选择页指令

https://i-blog.csdnimg.cn/blog_migrate/bb3af5ec4cc27c0f4bc7e2eccdae95a5.png

iic_write_reg(0x3C, 0x80, 0x20);     
iic_write_reg(0x3C, 0x80, 0x02);    //选择页寻址模式

2、0x01垂直寻址模式:当处于此模式时, 在GDDRAM访问后,页地址指针将自动增加1。如果页地址指针到达页终止地址,页地址指针将复位到页起始地址,且列地址指针将自动增加1

https://i-blog.csdnimg.cn/blog_migrate/7484a1ba38c3757c819f646d6ee79745.png

iic_write_reg(0x3C, 0x80, 0x20);     
iic_write_reg(0x3C, 0x80, 0x01);    //选择垂直寻址模式

3、0x00水平寻址模式:当处于此模式时,在GDDRAM访问后,列地址指针将自动增加1。如果列地址指针到达列终止地址, 列地址指针将复位到列起始地址, 且页地址指针将自动增加1

https://i-blog.csdnimg.cn/blog_migrate/d49a55144f60b563d668b809a8f5d13b.png

iic_write_reg(0x3C, 0x80, 0x20);     
iic_write_reg(0x3C, 0x80, 0x00);    //选择水平寻址模式

然后是如何选择对应的页和列

0x21:选择列,接着继续发送选择的列数据,例子如下:

https://i-blog.csdnimg.cn/blog_migrate/7fbb46972fa7d29cb6355952f95d16e3.png

iic_write_reg(0x3C, 0x80, 0x21);    //开启选列
iic_write_reg(0x3C, 0x80, 0x07);    //从第7列开始
iic_write_reg(0x3C, 0x80, 0x20);    //到32列结束

0x22:选择页,接着继续发送选择的列数据,例子如下:

https://i-blog.csdnimg.cn/blog_migrate/8a06cf79a4fb538ce122f55c47e92fe7.png

iic_write_reg(0x3C, 0x80, 0x22);    //开启选列
iic_write_reg(0x3C, 0x80, 0x02);    //从第2页开始
iic_write_reg(0x3C, 0x80, 0x04);    //到4页列结束

这些都是要在对应的寻址模式下来使用

还有一些滚动什么的指令因为没有用到所以这里就不一一解释,有兴趣的可以搜别的博文或者芯片手册去了解

取模软件

这个csdn或者百度一搜索就有,有很多种,都可以使用。这个是方便于生成文字的数组或者数字以及图片的数组,这样不用一个个去敲代码了

https://i-blog.csdnimg.cn/blog_migrate/7d71c8d41d79bba5b1c3d041d61bd416.png

keil程序

程序中使用了逐飞科技的库函数,所以就直接调用了,大家可以去git上下载。经过优化,每个字符的函数都可以随机的选取位置显示。在前面一定要初始化屏幕,其实就是类似打开屏幕和调节亮度这些必不可少的操作,少了的话屏幕亮不起来的

屏幕初始化

uint8 init_flag=1;

void pingmu_init() //屏幕初始化
{
if(init_flag==1) //初始化标志位
{
iic_write_reg(0x3C, 0x80, 0x81);
iic_write_reg(0x3C, 0x80, 0xff); //调节亮度
iic_write_reg(0x3C, 0x80, 0xa4); //使用存储数据显示
iic_write_reg(0x3C, 0x80, 0xaf); //打开显示屏幕
clean(); //清洁屏幕
}
init_flag = 0;
}

屏幕清洁

void clean() //清屏
{
for(e=0;e<8;e++)
{
iic_write_reg(0x3C, 0x80, 0x20);  
 iic_write_reg(0x3C, 0x80, 0x00); //选择行寻址模式
iic_write_reg(0x3C, 0x80, 0x22);
iic_write_reg(0x3C, 0x80, e);
iic_write_reg(0x3C, 0x80, e+1); //选择显示行数
iic_write_reg(0x3C, 0x80, 0x21);
iic_write_reg(0x3C, 0x80, 0x00);
iic_write_reg(0x3C, 0x80, 0x7f); //选择显示列数
for(a=0;a<128;a++)
{
iic_write_reg(0x3C, 0x40, 0x00); //发送数组数据
}
}
}

通过取模软件生成的字符数组

uint32 word1[]=  
{0x20,0x20,0xFC,0x28,0x08,0xFC,0xA8,0xBC,0xE8,0x08,0x08,0x07,0x7F,0x89,0x48,0x2B
,0x1E,0x2A,0x4B,0x48};/_"模",0_/

这里要注意,由于我用的单片机内存大小问题,不能创建很多数组,所以图片显示不了了。大家若是用其他单片机可以尝试显示图片

字符“模”的显示

void mo(int hangshu, int lieshu)
{
iic_write_reg(0x3C, 0x80, 0x20);  
 iic_write_reg(0x3C, 0x80, 0x00); //选择行寻址模式
iic_write_reg(0x3C, 0x80, 0x22);
iic_write_reg(0x3C, 0x80, hangshu);
iic_write_reg(0x3C, 0x80, hangshu+1); //选择显示行数
iic_write_reg(0x3C, 0x80, 0x21);
iic_write_reg(0x3C, 0x80, lieshu);
iic_write_reg(0x3C, 0x80, lieshu+9); //选择显示列数
for(a=0;a<20;a++)
{
iic_write_reg(0x3C, 0x40, word1[a]); //发送数组数据
}
}

屏幕显示内容

void xianshi() //显示
{
mo(0,0); //模
ni(0,10); //拟
liang(0,20); //量
shu(0,30); //输
chu(0,40); //出
zhi(0,50); //值
biaodian(0,60); //:
}

这样写完屏幕就会亮了,就能显示我们所要的内容了

https://i-blog.csdnimg.cn/blog_migrate/4c8789cdd138a23577046a7f07b5ff84.jpeg

#总结

做 OLED 的初衷是用来显示模拟量光敏的模拟值,从而在竞赛准备时候更快的调试。因为前几次比赛中经常因为赛场当地的光线导致循迹不稳或者来不调节,之后会写结合模拟量光敏的文章。最初以为跟普通串口通讯一样,只要发送字符就行。没想到这么多操作,后来学了才发现,希望这些能对大家有用

本人小白,有问题交流讨论,虚心受教