51单片机——IIC协议(附带AT24C02和OLED)

文章目录

  • ​​一.IIC协议概述
  • 二.特点
    • 2.1简单性和有效性。
    • 2.2多主控(multimastering)
    • 2.3构成
  • 三.如何实现IIC协议
    • 3.1IIC协议总时序图
    • 3.2设备(单片机)发送数据(写数据帧)的流程
    • 3.3设备(单片机)接收数据(读数据帧)的流程
    • 3.4起始信号代码
    • 3.5停止信号代码
    • 3.6发送数据代码
    • 3.7接收数据的代码
    • 3.7应答信号代码
    • 3.8总结发送数据和接收数据SCL流程图
  • 四.AT24C02
    • 4.1设备地址
    • 4.2AT24C02与单片机进行I2C通信最后读取到lcd1602上的代码
    • 4.3展示效果
  • 五.OLED
    • 5.1 AT24C02与OLED有什么关系?
    • 5.2(OLED)I2C写模式的时序解析
    • 5.3 OLED写指令和写数据代码
    • 5.4 OLED显示一个点的思路
    • 5.5 OLED指令大全解析
    • 5.5 OLED的初始化和代码
    • 5.6 OLED显示一个点和爱心的代码
    • 5.7 字模库app使用方法和详解
    • 5.8 OLED显示中文跟英文
    • 5.9 OLED显示图片方法
    • 5.10 OLED显示图片代码
    • 5.11 OLED显示数字并与DHT11温湿度模块结合代码
    • 5.12 OLED与超声波模块结合

​​一.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都有唯一的地址。
51单片机——IIC协议(附带AT24C02和OLED)_第1张图片

三.如何实现IIC协议

3.1IIC协议总时序图

1.下图2是IIC总时序图,图1的MSB是数据的最高有效位,图2中间的部分就是由很多个图1的scl的1-9部分组成,1-8是数据位,9是应答位,图3为带参数的时序图,具体往下继续看。

图1
51单片机——IIC协议(附带AT24C02和OLED)_第2张图片
图2
51单片机——IIC协议(附带AT24C02和OLED)_第3张图片

图3
51单片机——IIC协议(附带AT24C02和OLED)_第4张图片

2.下图为IIC协议通信时,设备(单片机)通过数据线(SDA)接收数据(读数据帧)和发送数据(写数据帧)的流程图,红色格子s为起始位表示开始传输数据,而红色格子p是停止位,表示停止接收数据,黄色格子为读/写位,当写数据时就置0,读数据时就置1,白色格子为应答信号,这个信号是从机(24C02)发给主机(单片机)的或者主机发给从机,如果从机接收到之前的信息它会回复0,表示接收器已经成功地接收了该字节;如果从机没有接收到或者主机读取接收到会回复1;A6-A0+R/W是设备地址的格式。

51单片机——IIC协议(附带AT24C02和OLED)_第5张图片
在这里插入图片描述

51单片机——IIC协议(附带AT24C02和OLED)_第6张图片
3.图一为读数据帧,为什么上面的读数据帧要读两次设备地址,根据手册中可知,一次是读写位为0,一位是读写位为1,图二是写数据帧的时序图。

图一
在这里插入图片描述
图二
51单片机——IIC协议(附带AT24C02和OLED)_第7张图片

4.当IIC总线处于空闲状态的时序图
51单片机——IIC协议(附带AT24C02和OLED)_第8张图片

3.2设备(单片机)发送数据(写数据帧)的流程

1.起始信号:SCL始终为高电平,而SDA是高到低一个电平的跳转。
51单片机——IIC协议(附带AT24C02和OLED)_第9张图片

2.在下图当单片机想与24C02进行通信,就要先知道它的地址,也就是1010 000;这里往下讲的都是单片机发送数据的时序图。
51单片机——IIC协议(附带AT24C02和OLED)_第10张图片
51单片机——IIC协议(附带AT24C02和OLED)_第11张图片
4.之后完成IIC协议以后就开始通信(下图)
51单片机——IIC协议(附带AT24C02和OLED)_第12张图片
5.(下图2)24C02是一个存储器,它可以存储存储256个字节,而我们(单片机)发送的8位寄存器的地址正好可以访问这256个字节,假如我们写的寄存器地址是0x01,它就会往24C02的里面写入数据(下图2),然后(图1)单片机需要存储器返回一个应答信号(此时为0,表示成功接受)。

图1
51单片机——IIC协议(附带AT24C02和OLED)_第13张图片
图2
51单片机——IIC协议(附带AT24C02和OLED)_第14张图片

6.接下来单片机要发送数据00001111给存储器那么数据就会存储到存储器的0x01的位置(图2),即使后来断电,它的信息也会永久保存,之后从机存储器需要给主机单片机发送一个应答信号0,告诉主机(单片机)写入成功(图3)。

图1
51单片机——IIC协议(附带AT24C02和OLED)_第15张图片
图2
51单片机——IIC协议(附带AT24C02和OLED)_第16张图片
图3
在这里插入图片描述

8.停止位:SCL始终为高电平,而SDA是低到高一个电平的跳转,这样一个标准的写数据帧就完成了。
51单片机——IIC协议(附带AT24C02和OLED)_第17张图片

3.3设备(单片机)接收数据(读数据帧)的流程

1.接收的过程跟发送数据差不多,多了在寄存器地址后又要有一个起始位,和再一次输入设备的地址,但是此时是读设备地址,所以黄格子写1,最后(绿色格子)接收数据跟发送一样,之后需要一个应答信号(此时为主机发给从机,表示接收成功,所以为1)。
51单片机——IIC协议(附带AT24C02和OLED)_第18张图片

3.4起始信号代码

51单片机——IIC协议(附带AT24C02和OLED)_第19张图片
在这里插入图片描述

通过起始信号时序图,我们可以看出。在虚线框之内,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停止信号代码

51单片机——IIC协议(附带AT24C02和OLED)_第20张图片
在这里插入图片描述

通过终止信号时序图,我们可以看出。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,一直移动八次。
51单片机——IIC协议(附带AT24C02和OLED)_第21张图片
3.scl为低电平时想要变为高电平之前需要一段时间(tDXCX),但是scl为低电平时也要有一段时间(tCLCH),从下图可知tDXCX是tCLCH的子集,所以scl=0时只需要延迟tCLCH的时间,也就是至少4.7us,才能scl=1。
51单片机——IIC协议(附带AT24C02和OLED)_第22张图片
在这里插入图片描述
4.之后scl=1,高电平的时候开始发送数据,需要的时间为tCHCL,也就是至少4us
51单片机——IIC协议(附带AT24C02和OLED)_第23张图片
在这里插入图片描述

5.时钟周期为下图
51单片机——IIC协议(附带AT24C02和OLED)_第24张图片

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)。
51单片机——IIC协议(附带AT24C02和OLED)_第25张图片

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流程图

上诉代码无论是发送还是接收代码,每次循环后效果如下图
51单片机——IIC协议(附带AT24C02和OLED)_第26张图片

四.AT24C02

4.1设备地址

1.AT24C02介绍
51单片机——IIC协议(附带AT24C02和OLED)_第27张图片
2.AT24C02原理图
51单片机——IIC协议(附带AT24C02和OLED)_第28张图片

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的时间(下图),读的时候不需要时间。
51单片机——IIC协议(附带AT24C02和OLED)_第29张图片
在这里插入图片描述

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 
#include 

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 
#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 
#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 
#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 
#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展示效果

51单片机——IIC协议(附带AT24C02和OLED)_第30张图片

五.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,剩下的数据字节就看传什么数据了。
51单片机——IIC协议(附带AT24C02和OLED)_第31张图片
51单片机——IIC协议(附带AT24C02和OLED)_第32张图片

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 点阵显示,如下图所示。
51单片机——IIC协议(附带AT24C02和OLED)_第33张图片
2.GDDRAM其实相当于LCD1602的DDRAM(数据显示区),就是在哪里显示的意思,一个PAGE分为128列和8行,每一列对应了8行,从上到下分别从低位一直到高位(LSB D0-MSB D7),每一个PAGE称为页。
51单片机——IIC协议(附带AT24C02和OLED)_第34张图片
3.如果想写数据,就确定好位置以后,就在确定的位置写,相当于LCD1602的显示数据,想在PAGE2的第一列写0x80,之后就是写1的位置亮,也就是一个点,就如下图。
51单片机——IIC协议(附带AT24C02和OLED)_第35张图片
4.GDDRAM就如下图
51单片机——IIC协议(附带AT24C02和OLED)_第36张图片
5.如果在下图位置写了0x80,之后如果还是写0x80,他会自动向右(红框位置)补充0x80一直到最右在重新回到第0列位置或者一直向下(绿框位置)补充0x80一直到最下面,此时就涉及到了oled的寻址模式。

51单片机——IIC协议(附带AT24C02和OLED)_第37张图片
6.oled的寻址模式如下图:

页地址模式
51单片机——IIC协议(附带AT24C02和OLED)_第38张图片
水平寻址模式
51单片机——IIC协议(附带AT24C02和OLED)_第39张图片
垂直寻址模式
51单片机——IIC协议(附带AT24C02和OLED)_第40张图片
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);//页地址模式

51单片机——IIC协议(附带AT24C02和OLED)_第41张图片

5.5 OLED指令大全解析

1.0x80是设置对比度,直观来说就是亮度,发送完0x80以后在发送一条亮度的多少指令即可设置,范围是0x00-0xFF;0xA4是显示GDDRAM中的内容,0XA5是显示白屏;0xA6是oled正常的显示,0xA7是反色显示,正常和反色如下图;0xAE是设置oled屏幕显示关,0xAF是设置oled屏幕显示开。
51单片机——IIC协议(附带AT24C02和OLED)_第42张图片

51单片机——IIC协议(附带AT24C02和OLED)_第43张图片
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.

展示效果:

51单片机——IIC协议(附带AT24C02和OLED)_第44张图片
51单片机——IIC协议(附带AT24C02和OLED)_第45张图片
51单片机——IIC协议(附带AT24C02和OLED)_第46张图片
51单片机——IIC协议(附带AT24C02和OLED)_第47张图片
51单片机——IIC协议(附带AT24C02和OLED)_第48张图片
51单片机——IIC协议(附带AT24C02和OLED)_第49张图片

51单片机——IIC协议(附带AT24C02和OLED)_第50张图片
3.
设置低四位开始地址作为页地址模式(00H-0FH)——就是页地址模式才用这个

设置(高四位)高列开始地址作为页地址模式(00H-0FH)——就是页地址模式才用这个

之后如果想设置第0列,就是0x00(0000 0000),高四位为A7-A4,也就是0000,低四位A3-A0也就是0000,最后高位固定为0001和0000组成00010000,也就是发指令0x10,然后在低位固定为0000和0000组成00000000,也就是发指令0x00,就算是设置了页地址模式下的列了。
51单片机——IIC协议(附带AT24C02和OLED)_第51张图片
51单片机——IIC协议(附带AT24C02和OLED)_第52张图片


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
51单片机——IIC协议(附带AT24C02和OLED)_第53张图片
51单片机——IIC协议(附带AT24C02和OLED)_第54张图片
51单片机——IIC协议(附带AT24C02和OLED)_第55张图片

图2
51单片机——IIC协议(附带AT24C02和OLED)_第56张图片
图3
51单片机——IIC协议(附带AT24C02和OLED)_第57张图片
图4
51单片机——IIC协议(附带AT24C02和OLED)_第58张图片
图5
51单片机——IIC协议(附带AT24C02和OLED)_第59张图片
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。
51单片机——IIC协议(附带AT24C02和OLED)_第60张图片

指令DA:com引脚硬件配置,不同设备需要不同的参数,才能让画面显示正常;比如我们这个oled用的是12864,首先发0xDA,在发0x12,如果oled是12832,首先发0xDA,然后在发0x02,如下图.
51单片机——IIC协议(附带AT24C02和OLED)_第61张图片

指令D5:屏幕时钟分频和振荡频率设置(屏幕刷新率),,发送完指令D5以后,然后发送决定刷新率的指令第四位固定的0000,高四位是0000-1111,越大,刷新越快,如下图。
51单片机——IIC协议(附带AT24C02和OLED)_第62张图片

51单片机——IIC协议(附带AT24C02和OLED)_第63张图片
指令A8:多路复用比例;这个命令转换默认的 63 复用模式到任何复用率,范围从 16 到 63。输出 pads COM0~COM63将会转换为相关的 COM 信号。

指令D3:屏幕垂直偏移量。

指令D9:预充电周期。

指令DB:设置VcomMH反压值,com最低高电平值,复位设置为0.77*vcc。

指令8D:设置电荷泵开启。

51单片机——IIC协议(附带AT24C02和OLED)_第64张图片
51单片机——IIC协议(附带AT24C02和OLED)_第65张图片

5.5 OLED的初始化和代码

oled跟LCD1602一样,显示之前都需要一个对屏幕初始化过程,下图1为官方初始化流程图,图2为优化后的流程图,本文使用优化后的流程图。

图1
51单片机——IIC协议(附带AT24C02和OLED)_第66张图片

//官方初始化流程
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

51单片机——IIC协议(附带AT24C02和OLED)_第67张图片
51单片机——IIC协议(附带AT24C02和OLED)_第68张图片

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 
#include 

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 
#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 
#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 
#include "OLED.H"


void main()
{
	Oled_Init();
	Oled_clear();//一上电会全亮,用清屏函数清空
		
	Oledshow();
	while(1)
	{
	
	}
}

展示效果
51单片机——IIC协议(附带AT24C02和OLED)_第69张图片
51单片机——IIC协议(附带AT24C02和OLED)_第70张图片

5.7 字模库app使用方法和详解

1.需要的app——字模提取v2.2
2.点击参数设置,其他选项,然后按照以下图片设置
51单片机——IIC协议(附带AT24C02和OLED)_第71张图片

3.接着·参数设置里面点击文字输入区字体选择,按照以下图片设置
51单片机——IIC协议(附带AT24C02和OLED)_第72张图片
4…点击图片红圈的位置,然后在下面输入字符,ctrl+回车生成
51单片机——IIC协议(附带AT24C02和OLED)_第73张图片
5.之后点击红圈位置即可生成
51单片机——IIC协议(附带AT24C02和OLED)_第74张图片
6.生成英文是8*16点阵的,也就是8列,16行,由于是页地址模式,所以首先上面八行(PAGE)一直到8列,之后在到下面的8行(PAGE)到八列,所以红色划线部分对应红色部分,黄色划线对应黄色部分,

51单片机——IIC协议(附带AT24C02和OLED)_第75张图片
7.生成汉字的话,是16*16点阵的,然后红色对应红,黄对黄,紫对紫,绿对绿。
51单片机——IIC协议(附带AT24C02和OLED)_第76张图片
8.需要改变大小调这里
51单片机——IIC协议(附带AT24C02和OLED)_第77张图片

5.8 OLED显示中文跟英文

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay5us(void);
void Delay100ms(void);

#endif

Delay.c

#include 
#include 

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 
#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 
#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 
#include "OLED.H"



void main()
{
	Oled_Init();
	Oled_clear();//一上电会全亮,用清屏函数清空
	
	Oledshow();
	
	while(1)
	{
	
	}
}

展示效果

5.9 OLED显示图片方法

1.把需要的图片下载到电脑桌面
2.用画图打开51单片机——IIC协议(附带AT24C02和OLED)_第78张图片
3.打开红圈地方,修改照片比例
51单片机——IIC协议(附带AT24C02和OLED)_第79张图片

4.如下,注意:不能超过屏幕的像素大小。
51单片机——IIC协议(附带AT24C02和OLED)_第80张图片

5.使用这样打开
51单片机——IIC协议(附带AT24C02和OLED)_第81张图片
6.选择单色保存
51单片机——IIC协议(附带AT24C02和OLED)_第82张图片
7.之后变成这样,但是有些图片可能并成功51单片机——IIC协议(附带AT24C02和OLED)_第83张图片

8.打开图像图标找到需要的图片
51单片机——IIC协议(附带AT24C02和OLED)_第84张图片
9.每次退出app后需要重新回来设置参数(因为我的会变)
51单片机——IIC协议(附带AT24C02和OLED)_第85张图片

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 
#include 

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 
#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 
#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 
#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 
#include 

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 
#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 
#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 
#include "OLED.h"
#include 

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);
		}
	
}

展示效果
51单片机——IIC协议(附带AT24C02和OLED)_第86张图片

5.12 OLED与超声波模块结合

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay5us(void);
void Delay100ms(void);

#endif

Delay.c

#include 
#include 

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 
#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 
#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 
#include "OLED.h"
#include 

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
		}
	
}

展示效果
51单片机——IIC协议(附带AT24C02和OLED)_第87张图片

你可能感兴趣的:(51单片机,嵌入式硬件,单片机)