一.IIC协议概述
IIC协议全称inter-integrated Circuit(集成电路总线),又称I2C协议,是由PHILP公司在80年代开发的两线式串行总线,用于连接微控制器及其外围设备,IIC属于半双工同步通信方式。
二.特点
2.1简单性和有效性。
由于接口真接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降作低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。
2.2多主控(multimastering)
其中任何能够进行发送和接收的设备都可以成为主总线,一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
2.3构成
IIC串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,其时钟信号是由主控器件产生,数据线是用来传输数据的,时钟线是用来使双方通信的时钟同步。所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟钱SCL接到总线的SCL上,对于并联在一条总线上的每个IC都有唯一的地址。
三.如何实现IIC协议
3.1IIC协议总时序图
1.下图2是IIC总时序图,图1的MSB是数据的最高有效位,图2中间的部分就是由很多个图1的scl的1-9部分组成,1-8是数据位,9是应答位,图3为带参数的时序图,具体往下继续看。
图1
图2
图3
2.下图为IIC协议通信时,设备(单片机)通过数据线(SDA)接收数据(读数据帧)和发送数据(写数据帧)的流程图,红色格子s为起始位表示开始传输数据,而红色格子p是停止位,表示停止接收数据,黄色格子为读/写位,当写数据时就置0,读数据时就置1,白色格子为应答信号,这个信号是从机(24C02)发给主机(单片机)的或者主机发给从机,如果从机接收到之前的信息它会回复0,表示接收器已经成功地接收了该字节;如果从机没有接收到或者主机读取接收到会回复1;A6-A0+R/W是设备地址的格式。
3.图一为读数据帧,为什么上面的读数据帧要读两次设备地址,根据手册中可知,一次是读写位为0,一位是读写位为1,图二是写数据帧的时序图。
图一
图二
4.当IIC总线处于空闲状态的时序图
3.2设备(单片机)发送数据(写数据帧)的流程
1.起始信号:SCL始终为高电平,而SDA是高到低一个电平的跳转。
2.在下图当单片机想与24C02进行通信,就要先知道它的地址,也就是1010 000;这里往下讲的都是单片机发送数据的时序图。
4.之后完成IIC协议以后就开始通信(下图)
5.(下图2)24C02是一个存储器,它可以存储存储256个字节,而我们(单片机)发送的8位寄存器的地址正好可以访问这256个字节,假如我们写的寄存器地址是0x01,它就会往24C02的里面写入数据(下图2),然后(图1)单片机需要存储器返回一个应答信号(此时为0,表示成功接受)。
图1
图2
6.接下来单片机要发送数据00001111给存储器那么数据就会存储到存储器的0x01的位置(图2),即使后来断电,它的信息也会永久保存,之后从机存储器需要给主机单片机发送一个应答信号0,告诉主机(单片机)写入成功(图3)。
图1
图2
图3
8.停止位:SCL始终为高电平,而SDA是低到高一个电平的跳转,这样一个标准的写数据帧就完成了。
3.3设备(单片机)接收数据(读数据帧)的流程
1.接收的过程跟发送数据差不多,多了在寄存器地址后又要有一个起始位,和再一次输入设备的地址,但是此时是读设备地址,所以黄格子写1,最后(绿色格子)接收数据跟发送一样,之后需要一个应答信号(此时为主机发给从机,表示接收成功,所以为1)。
3.4起始信号代码
通过起始信号时序图,我们可以看出。在虚线框之内,SCL始终为高电平。
而SDA是高到低一个电平的跳转。SDA高电平时间持续大于4.7us,低电平持续时间大于4us。
void IIC_start()//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
3.5停止信号代码
通过终止信号时序图,我们可以看出。SCL始终为高电平。
而SDA是低到高一个电平的跳转。SDA高电平时间持续大于4.7us,低电平持续时间大于4us。
void IIC_stop()//停止信号
{
scl = 1;//首先将scl拉高
sda = 0;//拉低sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
3.6发送数据代码
1.(下图),当SCL为高电平时(蓝色部分),数据不允许变化,当SCL为低电平的时,数据允许变化(红色部分),也就是scl为高电平时开始发送数据。
2.(下图),scl的12345678都是数据位,9是应答位,MSB是数据的最高有效位,也就是假如1010 0000,最高位1才有效,所以每次最高位输入以后就向左移动一位,然后就是0100 0000,一直移动八次。
3.scl为低电平时想要变为高电平之前需要一段时间(tDXCX),但是scl为低电平时也要有一段时间(tCLCH),从下图可知tDXCX是tCLCH的子集,所以scl=0时只需要延迟tCLCH的时间,也就是至少4.7us,才能scl=1。
4.之后scl=1,高电平的时候开始发送数据,需要的时间为tCHCL,也就是至少4us
5.时钟周期为下图
6.发送数据的代码
void IIC_Senddata(unsigned char datasend)//发送数据
{
unsigned char i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us;//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us;//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
3.7接收数据的代码
unsigned char IIC_Readdata(void)//接收数据
{
int i,value;
sda=1; //释放总线
scl=0; //scl为0时是发送数据前,让sda做好准备
Delay5us();//第一个tCLCH时间
for(i=0;i<8;i++)
{
value=value<<1;//无论value是什么,先向左移动一位,向左移动一位以后,最后一位是0
scl=1;//开始接收数据
Delay5us();//接收数据的时间-tCHCL时间
if(sda)//sda为1的时候才能带入if语句,sda为0的时候则不能带入
{
//每次向左移动一位以后,如果sda=1的时候就把最后一位置1,sda=0的时候则不用置,因为向左移动就有一个0了
value=value|0x01;
}
scl=0;
Delay5us();//scl为低电平的时间tCLCH时间
}
return value;
}
3.7应答信号代码
1.发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号(ACK),释放数据线期间sda为高电平(主机接收完字节后,从机释放总线,主机接过总线,发送应答;而主机发送完字节后,先释放总线,从机接过总线,通过总线向主机发送应答)。
2.从图可知释放总线时,sda=1(高电平),根据上面所学如果从机接收到之前的信息它会回复0,表示接收器已经成功地接收了该字节;如果从机没有接收到或者主机读取接收到会回复1;主机发送数据后,从机接收到会发送应答(0)给主机,而主机读取数据后,会发送非应答(1)。
5.应答信号代码
//发送完数据或者接收完数据以后sda为高电平释放总线,然后才能发送应答信号,我们这里只需要看scl的变化即可,sda过程不用写
void IIC_Ack()//应答信号,第九周期
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;
}
}
```c
在这里插入代码片
3.8总结发送数据和接收数据SCL流程图
上诉代码无论是发送还是接收代码,每次循环后效果如下图
四.AT24C02
4.1设备地址
1.AT24C02介绍
2.AT24C02原理图
3.下图为设备的地址格式,由于AT24C02的固定地址为1010(A6-A3),剩下的A2-A0为可配置地址,根据开发板原理图才可知晓,由上可知A2-A0为000,R/W=1/0(读/写),如果是A6-A0+R就是0xA0,如果是A6-A0+W就是0xA1。
4.写入AT24C02的时序图所需要的时间,也就是start到stop的时间(下图),读的时候不需要时间。
4.2AT24C02与单片机进行I2C通信最后读取到lcd1602上的代码
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay10ms(void);
void Delay1ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay10ms() //@11.0592MHz
{
unsigned char i, j;
i = 18;
j = 235;
do
{
while (--j);
} while (--i);
}
void Delay1ms(void) //@11.0592MHz
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(unsigned char datasend);
unsigned char IIC_Readdata(void);
void IIC_Ack();
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P2^1;
sbit sda = P2^0;
sbit led4=P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
unsigned char IIC_Readdata(void)//接收数据
{
int i,value;
sda=1; //释放总线
scl=0; //scl为0时是发送数据前,让sda做好准备
Delay5us();
for(i=0;i<8;i++)
{
value=value<<1;//无论value是什么,先向左移动一位,向左移动一位以后,最后一位是0
scl=1;//开始接收数据
Delay5us();//接收数据的时间-tCHCL时间
if(sda)//sda为1的时候才能带入if语句,sda为0的时候则不能带入
{
//每次向左移动一位以后,如果sda=1的时候就把最后一位置1,sda=0的时候则不用置,因为向左移动就有一个0了
value=value|0x01;
}
scl=0;
Delay5us();//scl为低电平的时间tCLCH时间
}
return value;
}
void IIC_Ack()//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
AT24C02.h
#ifndef __AT24C02_H__
#define __AT04C02_H__
void AT24C02_WriteByte(unsigned char dev_address,unsigned char wordAddress,unsigned char Data);
unsigned char AT24C02_ReadByte(char wordAddress);
#endif
AT24C02.c
#include <REGX52.H>
#include "IIC.h"
//写数据帧函数,第一个是设备地址,第二个参数是寄存器(存储单元)地址,第二个参数是字节数据
void AT24C02_WriteByte(unsigned char dev_address,unsigned char wordAddress,unsigned char Data)
{
IIC_start();//开始信号
IIC_Senddata(dev_address);//设备地址0xA0
IIC_Ack();//应答信号
IIC_Senddata(wordAddress);//寄存器地址
IIC_Ack();//应答信号
IIC_Senddata(Data);//发送数据
IIC_Ack();//应答信号
IIC_stop();//停止信号
}
//读数据帧函数,第一个参数是寄存器(存储单元)地址
unsigned char AT24C02_ReadByte(char wordAddress)
{
unsigned char Dat;
IIC_start();//开始信号
IIC_Senddata(0xA0);//设备地址0xA0
IIC_Ack();//应答信号
IIC_Senddata(wordAddress);//寄存器(存储单元)地址
IIC_Ack();//应答信号
IIC_start();//开始信号
IIC_Senddata(0xA1);//设备地址0xA1
IIC_Ack();//应答信号
Dat=IIC_Readdata();//接收数据
IIC_Ack();//应答信号
IIC_stop();//停止信号
return Dat;
}
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
void LCD_Init(void);
void LCD_Shownum(unsigned char line,unsigned char column,unsigned int number,unsigned char length);
#endif
LCD1602.c
#include <REGX52.H>
#include "Delay.h"
#define LCE_Dataport P0
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
void LCD_WriteCommand(unsigned char Command)//写指令
{
LCD_RS=0;
LCD_RW=0;
LCE_Dataport=Command;
LCD_EN=1;
Delay1ms();
LCD_EN=0;
Delay1ms();
}
void LCD_WriteData(unsigned char Data)//写数据
{
LCD_RS=1;
LCD_RW=0;
LCE_Dataport=Data;
LCD_EN=1;
Delay1ms();
LCD_EN=0;
Delay1ms();
}
void LCD_Init()//初始化
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
void LCD_Setcursor(unsigned char line,unsigned char column)
{
if(line==1)//设置光标位置
{
//第一列是数字1,但是位置是0x00,要把数字1转换0x00,就把数字1-1就0,所以转十六进制就是0x00
LCD_WriteCommand(0x80|(column-1));
}
else
{
//第二行的第一列是从0x40开始,第二列是0x41,相当于0x40+0x01,0x01就是与上面一致
LCD_WriteCommand(0x80|(column-1)+0x40);
}
}
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
void LCD_Shownum(unsigned char line,unsigned char column,unsigned int number,unsigned char length)
{
unsigned char i;
LCD_Setcursor(line,column);
for(i=length;i>0;i--)
{
LCD_WriteData('0'+number/LCD_Pow(10,i-1) %10);
}
}
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "IIC.h"
#include "AT24C02.h"
#include "Delay.h"
sbit led4=P2^3;
unsigned char Data;
void main()
{
LCD_Init();
AT24C02_WriteByte(0xA0,0X00,68);
Delay10ms();//写入时间需要10ms时间,手册可知,也就是start到stop,读的时候不需要时间,tWR
Data=AT24C02_ReadByte(0X00);
LCD_Shownum(1,1,Data,3);
while(1)
{
}
}
4.3展示效果
五.OLED
5.1 AT24C02与OLED有什么关系?
oled需要AT24C02。主机通过IIC模块与AT24C02器件进行通信。
5.2(OLED)I2C写模式的时序解析
1.(通过下图可知)Slave Address是从机地址,Control byte是寄存器地址,Data byte是数据字节,跟AT24C02的发数据差不多,都是需要起始信号S,然后从机地址8-3位是固定的0111 10,然后第2位为SA0位,SA0可以是1或是0,这样子就可以有两个oled进行i2c通信了,第一位是R/W位(读/写)=1/0,所以最后需要从机地址有两个0111 1010(0x7A)或者0111 1000(0x78),然后就是寄存器地址第八位是Co位,如果Co为0的时候,后面传输的信息就只包含数据字节,所以我们设置成0,第七位是D/C(Data-数据/Command-指令)位,D/C位决定了数据字节是指令还是数据,是1的时候是数据,是0的时候是指令,也就是D/C=1/0,剩下的6位为固定的000000,所以想写指令的话就是0000 0000(0x00),想写数据的话就是0100 0000(0x40,剩下的数据字节就看传什么数据了。
5.3 OLED写指令和写数据代码
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
5.4 OLED显示一个点的思路
1.oled是由128*68个led组成的,给高电平1就亮,给低电平0就灭,oled里面有一个图形显示数据RAM(GDDRAM),RAM是内存的意思,GDDRAM 是一个为映射静态 RAM 保存位模式来显示。该 RAM 的大小为 128 *64 位,RAM分为 8 页,从 PAGE0 到 PAGE7,用于单色 128 * 64 点阵显示,如下图所示。
2.GDDRAM其实相当于LCD1602的DDRAM(数据显示区),就是在哪里显示的意思,一个PAGE分为128列和8行,每一列对应了8行,从上到下分别从低位一直到高位(LSB D0-MSB D7),每一个PAGE称为页。
3.如果想写数据,就确定好位置以后,就在确定的位置写,相当于LCD1602的显示数据,想在PAGE2的第一列写0x80,之后就是写1的位置亮,也就是一个点,就如下图。
4.GDDRAM就如下图
5.如果在下图位置写了0x80,之后如果还是写0x80,他会自动向右(红框位置)补充0x80一直到最右在重新回到第0列位置或者一直向下(绿框位置)补充0x80一直到最下面,此时就涉及到了oled的寻址模式。
6.oled的寻址模式如下图:
页地址模式
水平寻址模式
垂直寻址模式
7.寻址模式配置:首先要先发送固定的0x20(红线位置),之后在发送绿线的位置,第8-3位是随机都可以,我们都给0,之后A1和A0就是模式的选择了——Horizontal Addressing Mode(水平寻址模式),Vertical Addressing Mode(垂直寻址模式),Page Addressing Mode(页地址模式),一般我们都用页地址模式,也就是黄线处10,所以发送完0x20后发送0x02(0000 0010)代码如下。
Oled_Writecommand(0x20);
Oled_Writecommand(0x02);//页地址模式
5.5 OLED指令大全解析
1.0x80是设置对比度,直观来说就是亮度,发送完0x80以后在发送一条亮度的多少指令即可设置,范围是0x00-0xFF;0xA4是显示GDDRAM中的内容,0XA5是显示白屏;0xA6是oled正常的显示,0xA7是反色显示,正常和反色如下图;0xAE是设置oled屏幕显示关,0xAF是设置oled屏幕显示开。
2.
水平滚动设置:是由五个连续字节来设置的,分别为水平滚动参数,开始页,结束页,滚动速度,在设置之前必须先发0x2E(关闭滚屏),否则,GDDRAM中的内容可能会被破坏,然后发送向左移动还是向右移动,向左0x27,向右0x26,之后在发送固定A(0x00),之后发送B(开始页),B的前第8-4位随意设置,一般写0,如果想设置从PAGE0开始就设置0x00,就是开始页0,然后C是滚动速度,我们设置为0x00,就是5帧,,之后D是结束页,我们设置成PAGE7,也就是0x07,然后在发送0x00和0xFF,然后发要展示的数据,最后发送0x2F(激活滚动)。
垂直和水平滚动设置:是由六个连续字节组成的命令,用来设置持续水平滚动参数和决定滚动开始页,结束页,滚动速度和垂直滚动偏移的,也是先发送停止滚动指令0x2E,之后发送向右下滚动(0x29)或者左下滚动(0x2A),然后发固定的A(0x00),然后发开始页B,这里我们选择PAGE0,也就是0x00,接着发滚动速度C(0x00-5帧),在发结束页D,这里我们选择PAGE7也就是0x07,接着E是用来设置垂直滚动偏移字节,如果E[5:0]设为0,则效果只有水平移动,我们这里设置为0x3F(63行),然后发要展示的数据,最后在设置激活滚动0x2F.
展示效果:
3.
设置低四位开始地址作为页地址模式(00H-0FH)——就是页地址模式才用这个
设置(高四位)高列开始地址作为页地址模式(00H-0FH)——就是页地址模式才用这个
之后如果想设置第0列,就是0x00(0000 0000),高四位为A7-A4,也就是0000,低四位A3-A0也就是0000,最后高位固定为0001和0000组成00010000,也就是发指令0x10,然后在低位固定为0000和0000组成00000000,也就是发指令0x00,就算是设置了页地址模式下的列了。
4·
oled有三种模式:页地址模式(0x10),水平地址模式(0x00),垂直地址模式(0x01),先发送固定指令0x20后在继续发送需要哪个模式的指令。
页地址模式(下图2):
在页地址模式下,在显示RAM读写之后,列地址指针自动+1。如果列地址达到了列结束地址,列地址指针重置为列开始地址并且列地址指针不会改变。用户需要设置新的页和列地址来访问下一页的GDDRAM的内容。
在正常显示数据GDDRAM读或者写和页地址模式下,要求使用以下步骤来定义开始GDDRAM访问的位置:通过命令B0H-B7H来设置目标显示位置的页开始地址,然后通过00H-0FH来设置低开始列地址的指针,最后通过10H-1F来设置高开始列地址,比如说页地址设置为B2H,低列地址是03H,高列地址是00H,那么就是第二页PAGE2,SEG3到图5的位置。
水平寻址模式(下图3)
在水平寻址模式下,当显示GDDRAM 被读写之后,列地址指针自动加一。如果列地址指针达到列的结束地址,列地址指针重置为列的开始地址,并且页地址指针自动加 1。水平寻址模式下页和列地址的移动顺序如下图所示。当列地址和页地址都达到了结束地址,指针重设为列地址和页地址的开始地址。
垂直地址模式(下图4):
在垂直寻址模式下,当显示GDDRAM 被读写之后,页地址指针自动加一。如果页地址达到了页的结束地址,页地址自动重置为页的开始地址,列地址自动加一。页地址和列地址的移动顺序如下图所示。当列地址和页地址都达到结束地址后,指针自动重置为开始地址。
在正常显示 RAM 读或写,水平/垂直寻址模式下,要求用下面的步骤来定义 RAM 访问指针位置:用 21H 命令设置目标显示位置的列的开始和结束地址;用命令 22H 设置目标显示位置的页的开始和结束地址。
总结:凡是后面带作为页地址模式的,都是需要页地址模式下才使用这个指令
图1
图2
图3
图4
图5
5.
指令40-7F:设置显示起始行;这个命令设置显示开始行寄存器来决定显示 RAM 的开始地址,通过选择 0 到 63 的值。当值为 0 时,RAM 行 0 映射到 COM0,当值为 1 时,RAM 行 1 映射到 COM0,以此类推。
指令A0/A1:水平镜像/水平正常;这个命令修改显示数据列地址和segment 驱动器之间的映射,允许在 OLED 模块设置上的灵活性。这个命令只影响后续的数据输出。早已存储在 GDDRAM 中的数据不会改变。
指令C0/C8:上下镜像/正常显示。
下图左边的上张图是指令A1,右边上张是A0,左边的下张图是C8,右边下张图是C0。
指令DA:com引脚硬件配置,不同设备需要不同的参数,才能让画面显示正常;比如我们这个oled用的是12864,首先发0xDA,在发0x12,如果oled是12832,首先发0xDA,然后在发0x02,如下图.
指令D5:屏幕时钟分频和振荡频率设置(屏幕刷新率),,发送完指令D5以后,然后发送决定刷新率的指令第四位固定的0000,高四位是0000-1111,越大,刷新越快,如下图。
指令A8:多路复用比例;这个命令转换默认的 63 复用模式到任何复用率,范围从 16 到 63。输出 pads COM0~COM63将会转换为相关的 COM 信号。
指令D3:屏幕垂直偏移量。
指令D9:预充电周期。
指令DB:设置VcomMH反压值,com最低高电平值,复位设置为0.77*vcc。
指令8D:设置电荷泵开启。
5.5 OLED的初始化和代码
oled跟LCD1602一样,显示之前都需要一个对屏幕初始化过程,下图1为官方初始化流程图,图2为优化后的流程图,本文使用优化后的流程图。
图1
//官方初始化流程
void Oled_Init(void)
{
Oled_Writecommand(0xA8);//设置MUX(多路复用器)比例为N+1 MUX(A8H)
Oled_Writecommand(0x3F);//MUX 比例为3FH+1 MUX(默认值)
Oled_Writecommand(0x20);
Oled_Writecommand(0x02);//页地址模式
Oled_Writecommand(0xD3);//设置屏垂直偏移量(D3H)
Oled_Writecommand(0x00);//偏移量0(范围0-63D)
Oled_Writecommand(0x40);//设置屏幕起始行(40H范围:40H—7FH对应0—63行)
Oled_Writecommand(0xA1);//设置划分重映射(A0H(默认值):列地址0映射到SEG0(左右镜像),A1H列地址127映射到SEG0
Oled_Writecommand(0xC8);//设置com扫描方向C0(默认值):com0—com7(上下镜像)C8:com7—com0(正常)
//此时MUX为3F+1 C8时:从com[3F-1]扫描到com0 C0时:从com0扫描到com[3F-1]
Oled_Writecommand(0xDA);//设置com引脚硬件配置
Oled_Writecommand(0x12);//使能镜像配置
Oled_Writecommand(0x81);//设置屏幕对比度(亮度)
Oled_Writecommand(0xFF);//设置为7F(范围00H-FFH) (1-256)
Oled_Writecommand(0xA4);//恢复显示RAM内容
Oled_Writecommand(0xA7);//设置为普通显示模式(A6H:正常显示 A7:反色显示)
Oled_Writecommand(0xD5);//设置为震荡频率和分屏(刷新率)
Oled_Writecommand(0xF0);//10H-F0H,越大速度越快
Oled_Writecommand(0x8D);//使能电荷泵稳压器
Oled_Writecommand(0x14);//模式14H
Oled_Writecommand(0xAF);//打开屏幕
}
图2
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0x80); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA7); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
5.6 OLED显示一个点和爱心的代码
(oledshow打//的就是爱心的代码,使用之前,先屏蔽掉一个点的代码)。
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay100ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(char datasend);
void IIC_Ack(void);
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P1^1;
sbit sda = P1^2;
sbit led4= P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
void IIC_Ack(void)//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
OLED.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Oled_Writecommand(unsigned char command);
void Oled_WriteData(unsigned char Data);
void Oled_setpos(unsigned char x,unsigned char y);
void Oled_clear(void);
void Oled_Init(void);
void Oledshow(void);
#endif
OLED.c
#include <REGX52.H>
#include "Delay.h"
#include "IIC.h"
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
void Oled_setpos(unsigned char x,unsigned char y)//OLED设置坐标(列,行)
{
Oled_Writecommand(0xB0 + y);//"页地址"从0xB0开始(一共8页)
Oled_Writecommand(((x & 0xF0) >> 4) | 0x10);//高四位
Oled_Writecommand((x & 0x0f) );//低四位
}
void Oled_clear()// 清屏函数
{
int i,j;
for(i=0;i<8;i++)//PAGE0-PAGE7都给0
{
Oled_Writecommand(0xB0+i);//PAGE0-PAGE7
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);
for(j=0;j<128;j++)
{
Oled_WriteData(0);
}
}
}
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0x80); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA6); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
void Oledshow()//展示需要的效果
{
Oled_setpos(0,0);//(列,行)
Oled_WriteData(0x08);//点
//unsigned char i;
//unsigned char Xin1[]={0x0C,0x12,0x21,0x42,0x84,0x42,0x21,0x12,0x0C};//爱心
//Oled_setpos(68,4);//(列,行)
//for(i=0;i<9;i++)
//{
//Oled_WriteData(Xin1[i]);
//}
}
main.c
#include <REGX52.H>
#include "OLED.H"
void main()
{
Oled_Init();
Oled_clear();//一上电会全亮,用清屏函数清空
Oledshow();
while(1)
{
}
}
展示效果
5.7 字模库app使用方法和详解
1.需要的app——字模提取v2.2
2.点击参数设置,其他选项,然后按照以下图片设置
3.接着·参数设置里面点击文字输入区字体选择,按照以下图片设置
4…点击图片红圈的位置,然后在下面输入字符,ctrl+回车生成
5.之后点击红圈位置即可生成
6.生成英文是8*16点阵的,也就是8列,16行,由于是页地址模式,所以首先上面八行(PAGE)一直到8列,之后在到下面的8行(PAGE)到八列,所以红色划线部分对应红色部分,黄色划线对应黄色部分,
7.生成汉字的话,是16*16点阵的,然后红色对应红,黄对黄,紫对紫,绿对绿。
8.需要改变大小调这里
5.8 OLED显示中文跟英文
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay100ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(char datasend);
void IIC_Ack(void);
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P1^1;
sbit sda = P1^2;
sbit led4= P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
void IIC_Ack(void)//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
OLED.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Oled_Writecommand(unsigned char command);
void Oled_WriteData(unsigned char Data);
void Oled_setpos(unsigned char x,unsigned char y);
void Oled_clear(void);
void Oled_Init(void);
void Oledshow(void);
void Oled_8_16_L(unsigned char x,unsigned char y,unsigned char N);
void Oled_8_16_R(unsigned char x,unsigned char y,unsigned char N);
#endif
OLED.c
#include <REGX52.H>
#include "Delay.h"
#include "IIC.h"
unsigned char code F16_16[]=
{
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
0x00,0x80,0x60,0xF8,0x07,0x08,0x48,0x48,0x48,0xFF,0x48,0x48,0x48,0x48,0x08,0x00,
0x01,0x00,0x00,0xFF,0x00,0x02,0x02,0x02,0x02,0xFF,0x02,0x02,0x12,0x22,0x1E,0x00,//伟,1
0x10,0x10,0x10,0xFF,0x10,0x10,0xF0,0x00,0x00,0xF8,0x08,0x08,0x08,0xF8,0x00,0x00,
0x80,0x40,0x30,0x0F,0x40,0x80,0x7F,0x00,0x00,0x7F,0x20,0x20,0x20,0x7F,0x00,0x00,//加,2
0x10,0x60,0x02,0x8C,0x00,0xF0,0x10,0x10,0x10,0xFF,0x10,0x10,0x10,0xF0,0x00,0x00,
0x04,0x04,0x7E,0x01,0x00,0xFF,0x42,0x42,0x42,0x7F,0x42,0x42,0x42,0xFF,0x00,0x00, //油,3
0x00,0x08,0x08,0xF8,0xF8,0x00,0x00,0x00,0x00,0xC0,0xE0,0x20,0x20,0xE0,0xC0,0x00,//l的上边,o的上边
0x00,0x08,0x08,0x0F,0x0F,0x08,0x08,0x00,0x00,0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,//l的下边,o的下边,4
0x00,0xE0,0xE0,0x00,0x00,0xE0,0xE0,0x00,0x00,0xC0,0xE0,0x20,0x20,0xE0,0xC0,0x00,//v的上边,e的上边
0x00,0x03,0x07,0x0C,0x0C,0x07,0x03,0x00,0x00,0x07,0x0F,0x09,0x09,0x09,0x01,0x00 //v的下边,e的下边,5
};
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
void Oled_setpos(unsigned char x,unsigned char y)//OLED设置坐标(列,行)
{
Oled_Writecommand(0xB0 + y);//"页地址"从0xB0开始(一共8页)
Oled_Writecommand(((x & 0xF0) >> 4) | 0x10);//高四位
Oled_Writecommand((x & 0x0f) );//低四位
}
void Oled_clear()// 清屏函数
{
int i,j;
for(i=0;i<8;i++)//PAGE0-PAGE7都给0
{
Oled_Writecommand(0xB0+i);//PAGE0-PAGE7
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);
for(j=0;j<128;j++)
{
Oled_WriteData(0);
}
}
}
void Oled_8_16_L(unsigned char x,unsigned char y,unsigned char N)//需要16*16点阵的左边
{
unsigned char i;
unsigned int adder=32*N; //16*16=256位,8位为一个字节,所以256/8=32个字节,一个汉字就是32字节,所以这里adder=32*N的意思就是N代表第几个汉字,比如第0个就是小,然后一开始就是数组从0开始
Oled_setpos(x,y); //(列,行)
for(i=0;i<8;i++) //汉字的左上部分
{
Oled_WriteData(F16_16[adder]); //当第一个汉字的时候,32*0=0,所以F16*16[adder]的adder从0开始,然后从0开始到7就是左上半部分
adder++;
}
Oled_setpos(x,y+1); //(列,行+1)
for(i=0;i<8;i++) //汉字的左下部分
{
Oled_WriteData(F16_16[adder+8]);//之后adder的8-15是右上部分,所以想取左下部分就+8
adder++;
}
}
void Oled_8_16_R(unsigned char x,unsigned char y,unsigned char N)//需要16*16点阵的右边
{
unsigned char i;
unsigned int adder=32*N+8; //16*16=256位,8位为一个字节,所以256/8=32个字节,一个汉字就是32字节,所以这里adder=32*N的意思就是N代表第几个汉字,比如第0个就是小,然后一开始就是数组从8开始
Oled_setpos(x,y); //(列,行)
for(i=0;i<8;i++) //汉字的右上部分
{
Oled_WriteData(F16_16[adder]); //当第一个汉字的时候,32*0=0,所以F16*16[adder]的adder从8开始,然后从8开始到15就是右上半部分
adder++;
}
Oled_setpos(x,y+1); //(列,行+1)
for(i=0;i<8;i++) //汉字的右下部分
{
Oled_WriteData(F16_16[adder+8]);//之后adder的16-23是左下部分,所以想取右下部分就+8
adder++;
}
}
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0xF0); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA6); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
void Oledshow()//展示需要的效果
{
unsigned char i;
unsigned char Xin1[]={0x0C,0x12,0x21,0x42,0x84,0x42,0x21,0x12,0x0C};//爱心
Oled_Writecommand(0x2E);//关闭滚动
Oled_Writecommand(0x26);//向右滚动
Oled_Writecommand(0x00);//A:固定指令0
Oled_Writecommand(0x00);//B:起始页0
Oled_Writecommand(0x07);//C:刷新帧
Oled_Writecommand(0x07);//D:结束页
Oled_Writecommand(0x00);//虚拟字节
Oled_Writecommand(0xFF);//虚拟字节
Oled_setpos(68,4);//(列,行)
for(i=0;i<9;i++)
{
Oled_WriteData(Xin1[i]);
}
//小
Oled_8_16_L(0,0,0);
Oled_8_16_R(8,0,0);
//伟
Oled_8_16_L(16,0,1);
Oled_8_16_R(24,0,1);
//加
Oled_8_16_L(32,0,2);
Oled_8_16_R(40,0,2);
//油
Oled_8_16_L(48,0,3);
Oled_8_16_R(56,0,3);
//lo
Oled_8_16_L(35,4,4);
Oled_8_16_R(43,4,4);
//ve
Oled_8_16_L(51,4,5);
Oled_8_16_R(59,4,5);
Oled_Writecommand(0x2F);//开启滚动
}
main.c
#include <REGX52.H>
#include "OLED.H"
void main()
{
Oled_Init();
Oled_clear();//一上电会全亮,用清屏函数清空
Oledshow();
while(1)
{
}
}
展示效果
5.9 OLED显示图片方法
1.把需要的图片下载到电脑桌面
2.用画图打开
3.打开红圈地方,修改照片比例
4.如下,注意:不能超过屏幕的像素大小。
5.使用这样打开
6.选择单色保存
7.之后变成这样,但是有些图片可能并成功
8.打开图像图标找到需要的图片
9.每次退出app后需要重新回来设置参数(因为我的会变)
10.然后ctrl+回车生成,在点击c51取模,变成以下图片。
5.10 OLED显示图片代码
OLEDfont.h
#ifndef __OLEDFONT_H__
#define __OLEDFONT_H__
unsigned char code BMP0[]=
{
/*-- 调入了一幅图像:D:\照片\tos-cn-avt-0015_4ece3827092ba816cf7f5a93aed210e4~c5_300x300.bmp --*/
/*-- 宽度x高度=128x64 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xC0,0xC0,
0xE0,0x60,0x60,0x60,0x60,0x20,0x30,0x30,0x30,0x70,0xF0,0xF0,0xF0,0xB0,0x30,0x30,
0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x20,0x60,0x60,0x60,0x60,
0x60,0x60,0x60,0x60,0x60,0x40,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0x80,0x80,0x80,0x80,
0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,
0xC0,0xC0,0xC0,0xC0,0x40,0x40,0xC0,0xC0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7C,0xFE,0xFF,0xC7,0x83,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0xC0,0xE0,0xFB,0x7F,0x3F,0x0E,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,
0x01,0x01,0x03,0x83,0x83,0x83,0x03,0x03,0x06,0x06,0x06,0x06,0x06,0x0C,0x0C,0x0C,
0x0C,0x18,0x18,0x18,0x18,0x30,0x30,0x30,0x70,0x78,0x7C,0xEE,0xC7,0xC3,0xC1,0x81,
0x80,0x80,0x00,0x80,0x80,0xC0,0xE0,0x78,0x3F,0x1F,0x87,0xC0,0xC0,0xE0,0x60,0x60,
0x70,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x70,0xF0,0xE0,0xC0,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x07,0x07,
0x0F,0x0F,0x9B,0x9B,0xDB,0xF3,0xF3,0x73,0x71,0x31,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xC0,0xE0,0xF0,0xF0,0xF8,0xF8,0xF8,0xFC,0xFC,
0xFC,0xFE,0xFE,0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x7F,0x7F,0xFF,0xCF,0xCF,0x8F,0x8F,0x0F,0x0F,0x0F,0x0F,0x8F,0x8F,0xCF,0xCF,
0xDF,0xDF,0x5F,0x7F,0x7E,0x7E,0x7E,0xFE,0xFE,0xFC,0xFC,0xFC,0xF8,0xF8,0xF9,0xF1,
0xE1,0xE3,0xC3,0xC7,0x87,0x0F,0x0C,0x1E,0x1F,0x1F,0x3F,0x71,0x61,0xE0,0xC0,0xC0,
0x80,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0x7C,0x3F,0x1F,0x0F,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0x78,0x3C,
0x1E,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF0,
0xF8,0xFC,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,0x7F,0x3F,0x3F,0x3F,0x3F,0x1F,0x3F,0x3F,0x7F,0x7F,
0x7F,0x7E,0x7C,0x7C,0x7C,0x39,0x13,0x07,0xFF,0xFE,0xFE,0x9F,0x07,0x01,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x3C,0x3F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x3F,
0x3F,0x1F,0x3F,0x3F,0x3F,0xFF,0xFE,0xFE,0xFC,0xF8,0xF0,0xC0,0x80,0x00,0x01,0x03,
0x0F,0x1F,0x7F,0xFB,0xF3,0xE1,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0xFF,0xFF,0x0F,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,
0xF3,0xF3,0xF7,0xF7,0xFF,0xF3,0xF1,0xF1,0xF1,0xF1,0xF1,0xE1,0xE3,0xC3,0x87,0x07,
0x06,0x0E,0x0C,0x0C,0x0C,0x18,0x18,0x18,0xD8,0xD8,0xF8,0xF8,0xF8,0xB8,0x98,0x18,
0x18,0x0C,0x0C,0x0C,0x0E,0x0E,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0E,0x0C,0x0C,0x0C,
0x18,0x18,0x18,0x38,0x38,0x78,0x78,0xF8,0xD8,0xD8,0x98,0x98,0x18,0x18,0x18,0x18,
0x0C,0xCC,0xEE,0xE7,0xF7,0xF3,0xF9,0xF8,0xF9,0xF1,0xFF,0xFF,0xFF,0x3C,0x00,0x00,
0x00,0x00,0x00,0x87,0xFF,0xFF,0xFF,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x41,0x47,0x5F,0x3F,0x3E,0x78,0x70,
0xE0,0xC0,0xC0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,
0x07,0x0F,0x1F,0x1F,0x3F,0x3F,0x7F,0x7F,0x7F,0x7F,0xDF,0xDF,0xCF,0xC7,0x83,0x80,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x01,0x03,0x03,0x07,0x07,0x0F,0x0D,0x0D,0x0F,
0x1B,0x1B,0x1B,0x1B,0x1B,0x3B,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x3B,0x1B,0x1B,0x1F,0x0F,0x0F,0x07,0x07,0x03,0x01,0x00,0x00,0x00,0x00,
0x00,0x83,0x87,0xC7,0xCF,0xEF,0x7F,0x7F,0x3F,0x1F,0x07,0x03,0x81,0x80,0xC0,0xE0,
0xF0,0x78,0x3C,0x1F,0x0F,0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xF0,0x18,0x5C,0x36,0x32,0x61,0x45,0xDF,0xB9,0xE0,0x70,0x70,0x78,0x6C,0x66,0x66,
0x63,0x63,0x61,0x21,0x21,0x23,0x33,0x36,0x36,0x36,0x1C,0x1C,0x1C,0x1C,0x18,0x98,
0xD8,0xD8,0xF8,0xF0,0xF0,0xF0,0xF0,0xF0,0xD0,0x90,0x90,0x90,0xB0,0x30,0x21,0x61,
0x61,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE1,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,0xE3,
0xE3,0xE3,0xE3,0xE3,0xE3,0xE2,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,
0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE6,0xE2,0xE3,0xE3,0xE3,0x73,0x13,
0x13,0x13,0x91,0xF9,0xF9,0xD8,0x9C,0x0C,0x0E,0x06,0x06,0x07,0x03,0x03,0x01,0x01,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x07,0x1E,0x16,0x26,0x42,0x42,0x82,0x7E,0xFF,0xC5,0x0D,0x0B,0x1B,0x32,0x66,0x46,
0xC4,0x84,0x0C,0x0C,0x08,0x08,0x08,0x18,0x18,0xD0,0xF0,0xF0,0xF8,0xFE,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0xFE,0xFC,0xF8,0xF1,0x81,0x03,0x07,0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x00,0x00,
0xF0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,0xF8,0xF0,0xE0,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
#endif
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay100ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(char datasend);
void IIC_Ack(void);
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P1^1;
sbit sda = P1^2;
sbit led4= P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
void IIC_Ack(void)//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
OLED.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Oled_Writecommand(unsigned char command);
void Oled_WriteData(unsigned char Data);
void Oled_clear(void);
void Oled_Init(void);
void Oled_picture(unsigned char*pt);
#endif
OLED.c
#include <REGX52.H>
#include "Delay.h"
#include "IIC.h"
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
void Oled_clear()// 清屏函数
{
int i,j;
for(i=0;i<8;i++)//PAGE0-PAGE7都给0
{
Oled_Writecommand(0xB0+i);//PAGE0-PAGE7
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);
for(j=0;j<128;j++)
{
Oled_WriteData(0);
}
}
}
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0xF0); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA7); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
void Oled_picture(unsigned char*pt)
{
unsigned char i;
unsigned int j;//这里必须用int,因为unsigned char是八位二进制数,范围是0到255,一直加1,超过255就会从0开始。缓冲区是一行128根小竖棍,用unsigned char最多数完两行就又重新从头数。unsigned int是十六位二进制数,范围从0到65535,所以足够数完从0到1023一共1024根小竖棍,从而能把整个缓冲区的数据都读一遍。
for(i=0;i<8;i++)
{
Oled_Writecommand(0xB0+i);//第0页开始
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);//第0列开始
for(j=128*i;j<(128*(i+1));j++)//每128列就会到下一页,比如第二页让开始为128,而不是为0开始
{
Oled_WriteData(*pt);
pt++;
}
}
}
main.c
#include <REGX52.H>
#include "OLED.H"
#include "Delay.h"
#include "IIC.h"
#include "OLEDfont.H"
extern unsigned char code BMP0[];
void main()
{
Oled_Init();
Oled_clear();//一上电会全亮,用清屏函数清空
Oled_picture(BMP0);
while(1)
{
}
}
5.11 OLED显示数字并与DHT11温湿度模块结合代码
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay100ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(char datasend);
void IIC_Ack(void);
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P1^1;
sbit sda = P1^2;
sbit led4= P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
void IIC_Ack(void)//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
OLED.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Oled_Writecommand(unsigned char command);
void Oled_WriteData(unsigned char Data);
void Oled_setpos(unsigned char x,unsigned char y);
void Oled_clear(void);
void Oled_Init(void);
void Oled_8_16(unsigned char x,unsigned char y,unsigned char N);
#endif
OLED.c
#include <REGX52.H>
#include "Delay.h"
#include "IIC.h"
unsigned char code F8_16[]=
{
0x00,0x00,0xF0,0xF8,0x08,0x68,0xF8,0xF0,0x00,0x00,0x07,0x0F,0x0B,0x08,0x0F,0x07,//数字0,0
0x00,0x20,0x20,0x30,0xF8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,//数字1,1
0x00,0x30,0x38,0x08,0x88,0xF8,0x70,0x00,0x00,0x0C,0x0E,0x0B,0x09,0x08,0x08,0x00,
0x00,0x30,0x38,0x88,0x88,0xF8,0x70,0x00,0x00,0x06,0x0E,0x08,0x08,0x0F,0x07,0x00,
0x00,0x00,0xF8,0xF8,0x00,0xE0,0xE0,0x00,0x00,0x03,0x03,0x02,0x02,0x0F,0x0F,0x02,
0x00,0xF8,0xF8,0x88,0x88,0x88,0x08,0x00,0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,
0x00,0xC0,0xE0,0x78,0x58,0xC8,0x80,0x00,0x00,0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,
0x00,0x08,0x08,0x88,0xE8,0x78,0x18,0x00,0x00,0x00,0x0E,0x0F,0x01,0x00,0x00,0x00,
0x00,0x70,0xF8,0xC8,0x88,0xF8,0x70,0x00,0x00,0x07,0x0F,0x08,0x09,0x0F,0x07,0x00,
0x00,0xF0,0xF8,0x08,0x08,0xF8,0xF0,0x00,0x00,0x00,0x09,0x0D,0x0F,0x03,0x01,0x00,//数字9,9
/*-- 文字: H --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0xF8,0xF8,0x80,0x80,0xF8,0xF8,0x00,0x00,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,//10
/*-- 文字: T --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x08,0x08,0xF8,0xF8,0x08,0x08,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,//11
/*-- 文字: : --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x0C,0x00,0x00,//12
/*-- 文字: . --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x0C,0x00,0x00,//13
};
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
void Oled_setpos(unsigned char x,unsigned char y)//OLED设置坐标(列,行)
{
Oled_Writecommand(0xB0 + y);//"页地址"从0xB0开始(一共8页)
Oled_Writecommand(((x & 0xF0) >> 4) | 0x10);//高四位
Oled_Writecommand((x & 0x0f) );//低四位
}
void Oled_8_16(unsigned char x,unsigned char y,unsigned char N)//显示数字
{
unsigned char i;
unsigned int adder=16*N;
Oled_setpos(x,y); //(列,行)
for(i=0;i<8;i++)
{
Oled_WriteData(F8_16[adder]);
adder++;
}
Oled_setpos(x,y+1);
for(i=0;i<8;i++)
{
Oled_WriteData(F8_16[adder]);
adder++;
}
}
void Oled_clear()// 清屏函数
{
int i,j;
for(i=0;i<8;i++)//PAGE0-PAGE7都给0
{
Oled_Writecommand(0xB0+i);//PAGE0-PAGE7
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);
for(j=0;j<128;j++)
{
Oled_WriteData(0);
}
}
}
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0xF0); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA6); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
main.c
//DHT11测温湿度发送到串口和OLED上
#include <REGX52.H>
#include "OLED.h"
#include <intrins.h>
sbit AUXR=0x8E;
sbit dht = P1^0; //温湿度传感器信号线
char datas[5]; //数组分别代表,湿度,湿度小数,温度,温度小数,校验
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay40us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 15;
while (--i);
}
void Delay20ms() //@11.0592MHz
{
unsigned char i, j;
i = 36;
j = 217;
do
{
while (--j);
} while (--i);
}
void Uart_Init()
{
AUXR = 0x01;
SCON = 0x40;
PCON &= 0x7F;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
void Uart_Sendchar(char Char)
{
SBUF=Char;
while(TI==0);
TI=0;
}
void Uart_Sendstring(char*string)
{
while(*string!='\0')
{
Uart_Sendchar(*string);
string++;
}
}
//初始化模块(检测模块是否存在)每次传输数据都要初始化
void DHT_Start()
{
//根据时序进行高低电平变化
dht = 1; //对应初始化时序start
dht = 0;
Delay20ms();
dht=1;
while(dht);
while(!dht);
while(dht);
}
void DHT11_Read()
{
char value = 0;
char flag;
char i = 0;
char j = 0;
DHT_Start();
for(j = 0;j<5;j++)//分别读取五组数据
{
for(i=0;i<8;i++)//每组八位分别处理
{
while(!dht); //高电平开始计时
Delay40us();
if(dht == 1) //如果还为高则是“1”
{
flag = 1;
while(dht);
}
else //否则“0”
{
flag = 0;
}
value = value << 1; //移位
value |= flag; //赋值
}
datas[j] = value; //赋值
}
}
void main()
{
Oled_Init();
Oled_clear();//一上电会全亮,用清屏函数清空
Delay1000ms();Delay1000ms();
Uart_Init();
while(1)
{
Delay1000ms();
DHT11_Read();
Uart_Sendstring("H:");
Uart_Sendchar(datas[0]/10+0x30);
Uart_Sendchar(datas[0]%10+0x30);
Uart_Sendchar('.');
Uart_Sendchar(datas[1]/10+0x30);
Uart_Sendchar(datas[1]%10+0x30);
Uart_Sendstring("\r\n");
Uart_Sendstring("T:");
Uart_Sendchar(datas[2]/10+0x30);
Uart_Sendchar(datas[2]%10+0x30);
Uart_Sendchar('.');
Uart_Sendchar(datas[3]/10+0x30);
Uart_Sendchar(datas[3]%10+0x30);
Uart_Sendstring("\r\n");
Oled_8_16(0,0,10);//H
Oled_8_16(8,0,12);//:
Oled_8_16(16,0,datas[0]/10);
Oled_8_16(25,0,datas[0]%10);
Oled_8_16(33,0,13);//.
Oled_8_16(41,0,datas[1]/10);
Oled_8_16(49,0,datas[1]%10);
Oled_8_16(0,2,11);//T
Oled_8_16(8,2,12);//:
Oled_8_16(16,2,datas[2]/10);
Oled_8_16(25,2,datas[2]%10);
Oled_8_16(33,2,13);//.
Oled_8_16(41,2,datas[3]/10);
Oled_8_16(49,2,datas[3]%10);
}
}
展示效果
5.12 OLED与超声波模块结合
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay5us(void);
void Delay100ms(void);
#endif
Delay.c
#include <REGX52.H>
#include <INTRINS.H>
void Delay5us(void)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
void Delay100ms() //@11.0592MHz
{
unsigned char i, j;
i = 180;
j = 73;
do
{
while (--j);
} while (--i);
}
IIC.h
#ifndef __IIC_H__
#define __IIC_H__
void IIC_start(void);
void IIC_stop(void);
void IIC_Senddata(char datasend);
void IIC_Ack(void);
#endif
IIC.c
#include <REGX52.H>
#include "Delay.h"
sbit scl = P1^1;
sbit sda = P1^2;
sbit led4= P2^3;
void IIC_start(void)//起始信号
{
scl = 1;//首先将scl拉高
sda = 1;//拉高sda
Delay5us();//SDA高电平时间持续大于4.7us
sda = 0;//低电平持续时间大于4us。
Delay5us();
}
void IIC_stop(void)//结束信号
{
sda = 0;//拉低sda
scl = 1;//首先将scl拉高
Delay5us();//SDA高电平时间持续大于4.7us
sda = 1;//低电平持续时间大于4us。
Delay5us();
}
void IIC_Senddata(char datasend)//发送数据
{
int i;
scl = 0;//scl为0时是发送数据前,让sda做好准备
for(i=0;i<8;i++)//这里是配置sda的过程,数据转化过程,sda不需要延迟
{
//if语句真,else假,0x80(1000 0000),datasend如果最高位是1,最后的结果就是非0,就是真就是if语句
//如果最高位是0,最后的结果就是0,0是假的就是else语句。
if(datasend & 0x80)
{
sda=1;
}
else //最高位都是0,所以最高位都是0的,sda给0
{
sda=0;
}
datasend=datasend<<1;//循环8次,datasend的最高位一直循环给完数据为止
Delay5us();//scl为低电平时的时间也就是tCLCH时间
scl=1; //当scl等于1的时候是数据不允许发生改变的时候,也就是它开始发送数据的时候
Delay5us();//tCHCL的时间
scl=0;//之后发送完以后scl置为低电平,为下一个时钟周期做准备
}
}
void IIC_Ack(void)//应答信号
{
sda=1;//释放总线
scl=0;
Delay5us();//tCLCH
scl=1;
Delay5us();//tCHCL
scl=0;
led4 = 0;//判断是否产生应答信号
}
OLED.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Oled_Writecommand(unsigned char command);
void Oled_WriteData(unsigned char Data);
void Oled_setpos(unsigned char x,unsigned char y);
void Oled_clear(void);
void Oled_Init(void);
void Oled_8_16(unsigned char x,unsigned char y,unsigned char N);
#endif
OLED.c
#include <REGX52.H>
#include "Delay.h"
#include "IIC.h"
unsigned char code F8_16[]=
{
0x00,0x00,0xF0,0xF8,0x08,0x68,0xF8,0xF0,0x00,0x00,0x07,0x0F,0x0B,0x08,0x0F,0x07,//数字0,0
0x00,0x20,0x20,0x30,0xF8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,//数字1,1
0x00,0x30,0x38,0x08,0x88,0xF8,0x70,0x00,0x00,0x0C,0x0E,0x0B,0x09,0x08,0x08,0x00,
0x00,0x30,0x38,0x88,0x88,0xF8,0x70,0x00,0x00,0x06,0x0E,0x08,0x08,0x0F,0x07,0x00,
0x00,0x00,0xF8,0xF8,0x00,0xE0,0xE0,0x00,0x00,0x03,0x03,0x02,0x02,0x0F,0x0F,0x02,
0x00,0xF8,0xF8,0x88,0x88,0x88,0x08,0x00,0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,
0x00,0xC0,0xE0,0x78,0x58,0xC8,0x80,0x00,0x00,0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,
0x00,0x08,0x08,0x88,0xE8,0x78,0x18,0x00,0x00,0x00,0x0E,0x0F,0x01,0x00,0x00,0x00,
0x00,0x70,0xF8,0xC8,0x88,0xF8,0x70,0x00,0x00,0x07,0x0F,0x08,0x09,0x0F,0x07,0x00,
0x00,0xF0,0xF8,0x08,0x08,0xF8,0xF0,0x00,0x00,0x00,0x09,0x0D,0x0F,0x03,0x01,0x00,//数字9,9
/*-- 文字: D --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0xF8,0xF8,0x08,0x18,0xF0,0xE0,0x00,0x00,0x0F,0x0F,0x08,0x0C,0x07,0x03,0x00,//10
/*-- 文字: : --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0x00,0x00,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x0C,0x00,0x00,//11
/*-- 文字: c --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0xC0,0xE0,0x20,0x20,0x60,0x40,0x00,0x00,0x07,0x0F,0x08,0x08,0x0C,0x04,0x00,//12
/*-- 文字: m --*/
/*-- Fixedsys12; 此字体下对应的点阵为:宽x高=8x16 --*/
0x00,0xE0,0xE0,0x20,0xE0,0x20,0xE0,0xC0,0x00,0x0F,0x0F,0x00,0x07,0x00,0x0F,0x0F,//13
};
void Oled_Writecommand(unsigned char command) //写指令函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x00); //control byte:(0)(0)000000 写入命令
IIC_Ack();
IIC_Senddata(command); //写指令
IIC_Ack();
IIC_stop();
}
void Oled_WriteData(unsigned char Data) //写数据函数
{
IIC_start(); //起始信号
IIC_Senddata(0x78); //从机地址 b0111 1000 0x78
IIC_Ack(); //应答信号
IIC_Senddata(0x40); //control byte:(0)(1)000000 写入数据
IIC_Ack();
IIC_Senddata(Data); //写数据
IIC_Ack();
IIC_stop();
}
void Oled_setpos(unsigned char x,unsigned char y)//OLED设置坐标(列,行)
{
Oled_Writecommand(0xB0 + y);//"页地址"从0xB0开始(一共8页)
Oled_Writecommand(((x & 0xF0) >> 4) | 0x10);//高四位
Oled_Writecommand((x & 0x0f) );//低四位
}
void Oled_8_16(unsigned char x,unsigned char y,unsigned char N)
{
unsigned char i;
unsigned int adder=16*N;
Oled_setpos(x,y); //(列,行)
for(i=0;i<8;i++)
{
Oled_WriteData(F8_16[adder]);
adder++;
}
Oled_setpos(x,y+1);
for(i=0;i<8;i++)
{
Oled_WriteData(F8_16[adder]);
adder++;
}
}
void Oled_clear()// 清屏函数
{
int i,j;
for(i=0;i<8;i++)//PAGE0-PAGE7都给0
{
Oled_Writecommand(0xB0+i);//PAGE0-PAGE7
Oled_Writecommand(0x00);
Oled_Writecommand(0x10);
for(j=0;j<128;j++)
{
Oled_WriteData(0);
}
}
}
void Oled_Init(void)
{
Delay100ms(); //上电后给点延时,等待单片机系统稳定在配置,否则有的型号的屏幕芯片会初始化失败
Oled_Writecommand(0xAE); // 关闭OLED,准备配置
//开始配置
Oled_Writecommand(0x20); //
Oled_Writecommand(0x02); // 设置为页地址模式
Oled_Writecommand(0xB0);//设置页开始地址作为页地址模式
Oled_Writecommand(0x18);//设置低一点的列的开始地址作为页地址模式
Oled_Writecommand(0x01);//设置列的高地址作为页的开始地址
Oled_Writecommand(0xD5); // 设置显示时钟分频因子/振荡器频率(屏幕刷新率)
Oled_Writecommand(0xF0); //
Oled_Writecommand(0xA8); // 设置多路传输比率 -- set multiplex ratio (16 to 63)
Oled_Writecommand(0x3F); // 1 / 64 duty
Oled_Writecommand(0xDA); // 设置列引脚硬件配置
Oled_Writecommand(0x12); //
Oled_Writecommand(0xA1); // 指令A0/A1:水平镜像/水平正常
Oled_Writecommand(0xC8); // 指令C0/C8:上下镜像/正常显示
Oled_Writecommand(0x40); // 设置设置屏幕(GDDRAM)起始行 -- Set Display Start Line (0x40~0x7F)
Oled_Writecommand(0xD3); // 设置显示偏移 -- set display offset (0x00~0x3F)
Oled_Writecommand(0x00); // not offset 偏移值是 0
Oled_Writecommand(0x81); // 设置对比度(亮度)
Oled_Writecommand(0xCF); // Set SEG Output Current Brightness
Oled_Writecommand(0xD9); // 设置预充电期间的持续时间 -- set pre-charge period
Oled_Writecommand(0xF1); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
Oled_Writecommand(0xDB); // 调整VCOMH调节器的输出 -- set vcomh (0x00 / 0x20 / 0x30)
Oled_Writecommand(0x20); // Set VCOM Deselect Level
Oled_Writecommand(0x8D); // 电荷泵设置
Oled_Writecommand(0x14); // 启用电荷泵
Oled_Writecommand(0xA4); // 0xA4是显示GDDRAM中的内容,0XA5是显示白屏
Oled_Writecommand(0xA6); // 0xA6是oled正常的显示,0xA7是反色显示
Oled_Writecommand(0xAF); // 打开OLED
}
main.c
#include <REGX52.H>
#include "OLED.h"
#include <intrins.h>
sbit Echo=P1^6;
sbit Trig=P1^5;
void Timer1_Init()
{
TMOD&=0x0F;
TMOD|=0x10;
TH1=0;
TL1=0;
//设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void HC_Start()
{
Trig=0;
Trig=1;
Delay10us();
Trig=0;
}
int Get_distance()
{
double time;
TH1=0;
TL1=0;//让下一次测距,时间为0开始
//给trig端口至少10us的高电平
HC_Start();
//由低电平跳转高电平,表示开始发送波,波出去的一下就开始定时器
while(Echo==0);
TR1=1;
//由高电平跳转低电平,表示波回来了,停止定时器
while(Echo==1);
TR1=0;
//计算出中间经过多少时间
time=(TH1*258+TL1)*1.085;
//距离等于时间*速度(340m/s)/2 =(34000cm/s)/2 = (34cm/ms)/2 =(0.034cm/us)/2
return (time*0.017);//返回整形
}
void main()
{
int distance;
Timer1_Init();
Oled_Init();
Oled_clear();//一上电会全亮,用清屏函数清空
while(1)
{
distance=Get_distance();
Oled_8_16(0,0,10);//D
Oled_8_16(8,0,11);//:
Oled_8_16(16,0,distance/100%10);//百位
Oled_8_16(25,0,distance/10%10);//十位
Oled_8_16(33,0,distance/1%10);//个位
Oled_8_16(41,0,12);//c
Oled_8_16(49,0,13);//m
}
}
展示效果