16.IIC 协议 和 OLED

IIC协议

IIC协议概述

  1. IIC全称Inter-Integrated Circuit (集成电路总线)
  2. 是由 PHILIPS 公司在 80 年代开发的两线式串行总线,用于连接微控制器及其外围设备。 IIC属于半双工同步通信方式 (同一时间,只能发送或接收),而串口是全双工的通讯方式(通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通讯方式的结合

特点

  • 简单性和有效性。
        由于接口直接在组件之上,因此IIC 总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降 低了互联成本。总线的长度可高达25 英尺,并且能够以 10Kbps 的最大传输速率支持 40 个组件(串口通信是20Kbps)
  • 多主控(multimastering)
其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控

构成

IIC 串行总线一般有两根信号线,一根是双向的数据线 SDA ,另一根是时钟线 SCL ,其时钟信号是由主控 器件产生。所有接到IIC 总线设备上的串行数据 SDA 都接到总线的 SDA 上,各设备的时钟线 SCL 接到总线 的SCL 上。对于并联在一条总线上的每个 IC 都有唯一的地址。

OLED

之前学习的LCD1602dht11都属于非标协议的元件,即他们有自己独特的激活方式,需要针对他们不同的器件来写IO口的高低从而进行控制。OLED属于标准协议的元件,即其使用的协议就是IIC,只要使用IIC就可以控制它,因此IIC的学习就从OLED的使用开始。

IIC通信协议

IIC传输数据时一共有三种类型的信号:开始信号结束信号应答信号

如果单片机封装了IIC,那么IIC的初始化将会和串口的类似,即操控寄存器的值即可,但是C51没有封装这个协议,那么实现的方法就类似前几节对LCD或dht的初始化编程类似,即通过时序图来实现IO口的变化从而模拟IIC协议。

  • 起始信号和终止信号

关注虚线过程中,SCL始终是高电平,起始信号中关注SDA由高变低的过程;终止信号关注SDA由低变高的过程

void IIC_Start()//SCL为高时,SDA由高变
{
	SDA = 1;
	SCL = 1;
	_nop_();//约5微秒
	SDA = 0;
	_nop_();
	
}

void IIC_Stop()//SCL为低时,SDA由低变高
{
	SDA = 0;
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
	
}

  •  应答信号

发送器每发送一个字节( 8 bit ),就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位( ACK ,简称应答位),表示接收器已经成功地接收了该字节; 应答信号为高电平时,规定为非应答位(NACK ),一般表示接收器接收该字节没有成功
char IIC_ACK()//应答信号,在起始信号之后
{
	char flag;
	SDA = 1;//在时钟脉冲9期间释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}

  •  数据发送的时序

 从左到右,在SCL为高时,SDA由高变低为起始信号,之后时钟线SCL就开始发送脉冲,数据线SDA开始相应发送数据,在SCL脉冲为高电平时,SDA的数据不允许改变,要么0要么1,只有在SCL变为低电平时,即在脉冲的间隔中,SDA才可以反转。

        如果在SCL高电平时翻转会被误以为是起始信号或者终止信号

void IIC_SendByte(char datasend)
{ 
	int i;
	for(i = 0;i < 8;i++)
	{
		SCL = 0;//SCL拉低,让SDA做好数据准备
		SDA = datasend & 0x80;//1000 0000获得datasend的最高位
		_nop_();//发送数据建立时间
		SCL = 1;//SCL拉高开始传输
		_nop_();//数据发送时间
		SCL = 0;//发送完毕拉低
		_nop_();//
		datasend = datasend << 1;
	}
}

OLED

OLED使用的是IIC协议,所以上面写的4个函数都适用,但是OLED也有自己写命令的一些格式需要学习:

根据上图所示,OLED的控制流程大概是:

1. Start:即IIC的start()

2. Slave address: 011110+SA0+R/W#

所以SA0的值可高可低, 它是用于区分多个OLED屏幕的。我只有一块屏幕,不用管他就好。

 同时根据手册,在写模式下,R/W# 位应该置0。

综上,Slave address应该可以设置为 0111 1000(0x78) 或 0111 1010(0x7A)

3. ACK:即IIC的ack()

4. Control byte:Co+D/C+000000

综上,如果我想传输命令,则应该将Control byte设置为 0000 0000(0x00)(写命令)或传输数据则设置为 0100 0000(0x40)写数据)

5. ACK:即IIC的ack()

6. 写入指令/数据

7. ACK:即IIC的ack()

8. STOP即IIC的stop()


void OLED_writecmd(char cmd)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x00);//写入命令
	IIC_ACK();
	IIC_SendByte(cmd);
	IIC_ACK();
	IIC_Stop();
}

void OLED_writedata(char wdata)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x40);//写入数据
	IIC_ACK();
	IIC_SendByte(wdata);
	IIC_ACK();
	IIC_Stop();
}

OLED显示

OLED的控制手法的学习就像掌握了工具,OLED归根结底还是一块显示屏,所有的控制和协议都是为了最后在屏幕上显示让人读懂的手段:

其实,学习过LCD1602之后,显示的方式已经大概熟悉,主要的流程就是初始化,然后决定“在哪里显示什么

初始化:

void OLED_Init()
{
	OLED_writecmd(0xAE);//--display off
	OLED_writecmd(0x00);//---set low column address
	OLED_writecmd(0x10);//---set high column address
	OLED_writecmd(0x40);//--set start line address  
	OLED_writecmd(0xB0);//--set page address
	OLED_writecmd(0x81); // contract control
	OLED_writecmd(0xFF);//--128   
	OLED_writecmd(0xA1);//set segment remap 
	OLED_writecmd(0xA6);//--normal / reverse
	OLED_writecmd(0xA8);//--set multiplex ratio(1 to 64)
	OLED_writecmd(0x3F);//--1/32 duty
	OLED_writecmd(0xC8);//Com scan direction
	OLED_writecmd(0xD3);//-set display offset
	OLED_writecmd(0x00);//
	
	OLED_writecmd(0xD5);//set osc division
	OLED_writecmd(0x80);//
	
	OLED_writecmd(0xD8);//set area color mode off
	OLED_writecmd(0x05);//
	
	OLED_writecmd(0xD9);//Set Pre-Charge Period
	OLED_writecmd(0xF1);//
	
	OLED_writecmd(0xDA);//set com pin configuartion
	OLED_writecmd(0x12);//
	
	OLED_writecmd(0xDB);//set Vcomh
	OLED_writecmd(0x30);//
	
	OLED_writecmd(0x8D);//set charge pump enable
	OLED_writecmd(0x14);//
	
	OLED_writecmd(0xAF);//--turn on oled panel		
}

在哪里? 

即确定“显示的位置”,OLED其实就是一块128x64的点阵。OLED的屏幕在竖直方向上被分为8个Page,而竖直方向上一共有64个显示点,所以1个Page就是128x8的排列。

每一列有八个数据,OLED的高字节在下,所以从上往下依次是D0、D1……D7,传输0x30(0011 0000),即点亮D5、D4两个点。

对于Page的管理方式有3种:

1. 页地址模式 

在这种模式下,如果对于PAGE0不断写数据,会从第0列一直写到第127列,然后重新写PAGE0的第0列

2. 水平地址模式

在这种模式下,如果对于PAGE0不断写数据,会从第0列一直写到第127列,然后写PAGE1的第0列,再写128列之后切换到PAGE3...一直写到PAGE7的127列之后,会回到PAGE0的第0列。

3. 垂直地址模式

在这种模式下,如果对于PAGE0不断写数据,PAGE0的第0列写完会写PAGE1的第0列,然后是PAGE2的第0列....直到PAGE7的第0列也写完,才会开始写PAGE0的第1列,再把所有PAGE的第1列写完....一直写到PAGE7的127列之后,会回到PAGE0的第0列。

那么问题又来了,如何配置这三种地址模式呢?

1. 在说明列表的红框处这一行,写出了配置的方法: 需要两个命令来设置(因为ssd1306的命令数据为16位,需要两个值来控制),其中第二个命令的A1和A0的值的变化可以决定使用哪种PAGE的管理方式。

选择页寻址模式:即要发送1. 0x20的cmd  2. 0x02的cmd (0000 0010)

2. 另外,前两行里面,可以选择从哪一列开始写,有两组X3,X2,X1,X0(低4位和高4位)来决定,如果我想要在第0列开始写,则应该发送决定低四位的 0x00的cmd(0000 0000)和 决定高四位的 0x10的cmd(0001 0000)

那么最后一个问题来了,配置完PAGE的模式,并可以决定从哪一列开始写了之后,如何选择从哪个PAGE开始写?

右下角的方框里提到了,for page addressing mode,所以在页寻址的模式下,X2,X1,X0三位可以确定开始的PAGE,如果配置成0 0 0即从PAGE0开始,即写入0xB0

的cmd (1011 0000)

显示什么? 

即决定“显示的内容”,正如上面的说明,1个Page就是128x8的排列,所以每一列正好可以使用1个Byte来控制:例如这个Byte的值为0x30,即这一列从上往下8个显示点依次是0000 1100, 即从上往下数第5、6个显示点被点亮。 

在了解如何点亮一个点了之后,结合刚刚选择PAGE和列的方法,就可以先实现“清屏函数”,因为如果不清屏,OLED的屏幕会显示所有显示过的历史信息,这显然是不对的:

void OLED_Clear()
{
	int i,j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);//选择0列
		OLED_writecmd(0x10);//选择0列
				//由于地址会自动偏移,所以只要重复写128次全0,就可以清一个PAGE
			for(j = 0;j < 128;j++)
			{
				OLED_writedata(0x00);
			}
				
	}

}

以上就是显示的基础逻辑,但是这只可以显示一个点,一条线,如何显示一个字符?

理论上,只要花时间研究哪一行显示哪几个点,就可以推出一个字符如何显示,但是这样未免太过复杂,因此,可以使用字模软件

打开字模软件

1. 选择左下角的“参数设置”,然后选择“文字输入区字体选择”,进行设置

2. 同样选择左下角的“参数设置”,然后选择“其他选项”,进行设置

3. 选择右下角的“文字输入区”,输入大写字符A,然后按下“Ctrl”+"回车"

3. 选择左下角的“取模方式” ,然后选择“C51格式”,再点击右下角的点阵生成区:

 这时,就可以生成显示A需要的位置信息!


/*--  文字:  A  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=8x16   --*/
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20


可见,生成的宽x高=8x16的A字符,要在纵向占用2个PAGE,因为1个PAGE是128x8,因此需要将以上16个数据拆分成两个,8个一组:0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00 和 0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20。

之后,根据PAGE和列的选择,分别打印A的上半部分和下半部分,就可以完成A的完整显示:

	OLED_writecmd(0xB0);//选择从PAGE0开始
	OLED_writecmd(0x00);//选择列
	OLED_writecmd(0x10);//选择列
	
	for(i = 0;i<8;i++)
	{
		OLED_writedata(A1[i]);//绘制字符A的上半部分
	}
	OLED_writecmd(0xB1);//选择从PAGE1开始
	OLED_writecmd(0x00);//选择列
	OLED_writecmd(0x10);//选择列
	for(i = 0;i<8;i++)
	{
		OLED_writedata(A2[i]);//绘制字符A的下半部分
	}
	

以上就是打印一个字符的逻辑,由此,可以打印各种字符,那么如何打印图像?

首先:由于屏幕是128x64的大小,所以图片也必须是128x64的像素点大小,并且由于屏幕不是彩色的,所以必须保存为单色位图的bmp格式

准备好图片之后,再次打开字模软件

1.点击左上角的“基本操作”,然后点击“打开图像图标”,并选择刚刚准备好的图片:

2. 然后同样选择左下角的“取模方式” ,然后选择“C51格式”,再点击右下角的点阵生成区:

3. 最后,图片的显示过程其实和清屏的过程类似:

void OLED_Pic(unsigned char *image)
{
	unsigned char i;
	unsigned int j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);
		OLED_writecmd(0x10);
				//0到127列,以此写入0,每写入数据,列地址自动偏移
			for(j = 64*i;j < 64*(i+1);j++)
			{
				OLED_writedata(image[j]);
			}
				
	}

}

 

最终代码实现

1. 如果我现在想要选择页寻址模式从PAGE0开始显示一个点的话,代码应该如下:

#include "reg52.h"
#include "intrins.h"
sbit SCL = P1^1;
sbit SDA = P1^3;


void IIC_Start()//SCL为高时,SDA由高变低
{
	SDA = 1;
	SCL = 1;
	_nop_();//约5微秒
	SDA = 0;
	_nop_();
	
}

void IIC_Stop()//SCL为低时,SDA由低变高
{
	SDA = 0;
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
	
}

char IIC_ACK()//应答信号,在起始信号之后
{
	char flag;
	SDA = 1;//在时钟脉冲9期间释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}


//在SCL高电平时翻转会被误以为是起始信号或者终止信号
void IIC_SendByte(char datasend)
{ 
	int i;
	for(i = 0;i < 8;i++)
	{
		SCL = 0;//SCL拉低,让SDA做好数据准备
		SDA = datasend & 0x80;//1000 0000获得datasend的最高位
		_nop_();//发送数据建立时间
		SCL = 1;//SCL拉高开始传输
		_nop_();//数据发送时间
		SCL = 0;//发送完毕拉低
		_nop_();//
		datasend = datasend << 1;
	}
}


void OLED_writecmd(char cmd)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x00);//写入命令
	IIC_ACK();
	IIC_SendByte(cmd);
	IIC_ACK();
	IIC_Stop();
}

void OLED_writedata(char wdata)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x40);//写入数据
	IIC_ACK();
	IIC_SendByte(wdata);
	IIC_ACK();
	IIC_Stop();
}

void OLED_Init()
{
	OLED_writecmd(0xAE);//--display off
	OLED_writecmd(0x00);//---set low column address
	OLED_writecmd(0x10);//---set high column address
	OLED_writecmd(0x40);//--set start line address  
	OLED_writecmd(0xB0);//--set page address
	OLED_writecmd(0x81); // contract control
	OLED_writecmd(0xFF);//--128   
	OLED_writecmd(0xA1);//set segment remap 
	OLED_writecmd(0xA6);//--normal / reverse
	OLED_writecmd(0xA8);//--set multiplex ratio(1 to 64)
	OLED_writecmd(0x3F);//--1/32 duty
	OLED_writecmd(0xC8);//Com scan direction
	OLED_writecmd(0xD3);//-set display offset
	OLED_writecmd(0x00);//
	
	OLED_writecmd(0xD5);//set osc division
	OLED_writecmd(0x80);//
	
	OLED_writecmd(0xD8);//set area color mode off
	OLED_writecmd(0x05);//
	
	OLED_writecmd(0xD9);//Set Pre-Charge Period
	OLED_writecmd(0xF1);//
	
	OLED_writecmd(0xDA);//set com pin configuartion
	OLED_writecmd(0x12);//
	
	OLED_writecmd(0xDB);//set Vcomh
	OLED_writecmd(0x30);//
	
	OLED_writecmd(0x8D);//set charge pump enable
	OLED_writecmd(0x14);//
	
	OLED_writecmd(0xAF);//--turn on oled panel		
}

void OLED_Clear()
{
	int i,j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);//选择0列
		OLED_writecmd(0x10);//选择0列
				//由于地址会自动偏移,所以只要重复写128次全0,就可以清一个PAGE
			for(j = 0;j < 128;j++)
			{
				OLED_writedata(0x00);
			}
				
	}

}

void main()
{
//	1.OLED初始化
	OLED_Init();
	OLED_Clear();
	//2.选择一个位置
//	2.1确认页寻址模式
	OLED_writecmd(0x20);
	OLED_writecmd(0x02);
//	2.2选择page0  1011 0000
	OLED_writecmd(0xB0);
//	3.显示一个点
	OLED_writedata(0x08);
	OLED_writedata(0x08);
	OLED_writedata(0x08);
	OLED_writedata(0x08);
	OLED_writedata(0x08);
	OLED_writedata(0x08);
	
	while(1);//防止程序退出
}

显示效果 

2. 如果我现在想要选择页寻址模式在左上角显示字符’紫苑'的话,代码应该如下:

#include "reg52.h"
#include "intrins.h"
sbit SCL = P1^1;
sbit SDA = P1^3;


void IIC_Start()//SCL为高时,SDA由高变低
{
	SDA = 1;
	SCL = 1;
	_nop_();//约5微秒
	SDA = 0;
	_nop_();
	
}

void IIC_Stop()//SCL为低时,SDA由低变高
{
	SDA = 0;
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
	
}

char IIC_ACK()//应答信号,在起始信号之后
{
	char flag;
	SDA = 1;//在时钟脉冲9期间释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}


//在SCL高电平时翻转会被误以为是起始信号或者终止信号
void IIC_SendByte(char datasend)
{ 
	int i;
	for(i = 0;i < 8;i++)
	{
		SCL = 0;//SCL拉低,让SDA做好数据准备
		SDA = datasend & 0x80;//1000 0000获得datasend的最高位
		_nop_();//发送数据建立时间
		SCL = 1;//SCL拉高开始传输
		_nop_();//数据发送时间
		SCL = 0;//发送完毕拉低
		_nop_();//
		datasend = datasend << 1;
	}
}


void OLED_writecmd(char cmd)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x00);//写入命令
	IIC_ACK();
	IIC_SendByte(cmd);
	IIC_ACK();
	IIC_Stop();
}

void OLED_writedata(char wdata)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x40);//写入数据
	IIC_ACK();
	IIC_SendByte(wdata);
	IIC_ACK();
	IIC_Stop();
}

void OLED_Init()
{
	OLED_writecmd(0xAE);//--display off
	OLED_writecmd(0x00);//---set low column address
	OLED_writecmd(0x10);//---set high column address
	OLED_writecmd(0x40);//--set start line address  
	OLED_writecmd(0xB0);//--set page address
	OLED_writecmd(0x81); // contract control
	OLED_writecmd(0xFF);//--128   
	OLED_writecmd(0xA1);//set segment remap 
	OLED_writecmd(0xA6);//--normal / reverse
	OLED_writecmd(0xA8);//--set multiplex ratio(1 to 64)
	OLED_writecmd(0x3F);//--1/32 duty
	OLED_writecmd(0xC8);//Com scan direction
	OLED_writecmd(0xD3);//-set display offset
	OLED_writecmd(0x00);//
	
	OLED_writecmd(0xD5);//set osc division
	OLED_writecmd(0x80);//
	
	OLED_writecmd(0xD8);//set area color mode off
	OLED_writecmd(0x05);//
	
	OLED_writecmd(0xD9);//Set Pre-Charge Period
	OLED_writecmd(0xF1);//
	
	OLED_writecmd(0xDA);//set com pin configuartion
	OLED_writecmd(0x12);//
	
	OLED_writecmd(0xDB);//set Vcomh
	OLED_writecmd(0x30);//
	
	OLED_writecmd(0x8D);//set charge pump enable
	OLED_writecmd(0x14);//
	
	OLED_writecmd(0xAF);//--turn on oled panel		
}

void OLED_Clear()
{
	int i,j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);
		OLED_writecmd(0x10);
				//0到127列,以此写入0,每写入数据,列地址自动偏移
			for(j = 0;j < 128;j++)
			{
				OLED_writedata(0x00);
			}
				
	}

}

/*--  文字:  紫  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char z1[16] = {0x20,0x20,0x3E,0x20,0x1F,0x94,0x54,0x00,0x1F,0x24,0xA4,0x24,0x22,0x38,0x00,0x00};
char z2[16] = {0x00,0x80,0x48,0x29,0x09,0x4D,0x8D,0x7B,0x0B,0x09,0x28,0x4C,0x98,0x00,0x00,0x00};

/*--  文字:  苑  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char y1[16] = {0x04,0x04,0x84,0x74,0x24,0x2F,0xE4,0x04,0xE4,0x2F,0x24,0x24,0xE4,0x04,0x04,0x00};
char y2[16] = {0x04,0x82,0x41,0x22,0x14,0x0C,0x03,0x00,0x3F,0x40,0x42,0x44,0x43,0x40,0x78,0x00};

void print_shion()
{
	int i;
	OLED_writecmd(0xB0);
	OLED_writecmd(0x00);
	OLED_writecmd(0x10);
	
	for(i = 0;i<16;i++)
	{
		OLED_writedata(z1[i]);
	}
	for(i = 0;i<16;i++)
	{
		OLED_writedata(y1[i]);
	}
	
	OLED_writecmd(0xB1);
	OLED_writecmd(0x00);
	OLED_writecmd(0x10);
	for(i = 0;i<16;i++)
	{
		OLED_writedata(z2[i]);
	}
	for(i = 0;i<16;i++)
	{
		OLED_writedata(y2[i]);
	}
	
}

void main()
{
	
//	1.OLED初始化
	OLED_Init();
	OLED_Clear();
	//2.选择一个位置
//	2.1确认页寻址模式
	OLED_writecmd(0x20);
	OLED_writecmd(0x02);
//	2.2选择page0  1011 0000
	print_shion();
	
	while(1);//防止程序退出
}
















 

 3. 如果我现在想要选择页寻址模式显示以下图像的话,代码应该如下

#include "reg52.h"
#include "intrins.h"
sbit SCL = P1^1;
sbit SDA = P1^3;


void IIC_Start()//SCL为高时,SDA由高变低
{
	SDA = 1;
	SCL = 1;
	_nop_();//约5微秒
	SDA = 0;
	_nop_();
	
}

void IIC_Stop()//SCL为低时,SDA由低变高
{
	SDA = 0;
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
	
}

char IIC_ACK()//应答信号,在起始信号之后
{
	char flag;
	SDA = 1;//在时钟脉冲9期间释放数据线
	_nop_();
	SCL = 1;
	_nop_();
	
	flag = SDA;
	_nop_();
	SCL = 0;
	_nop_();
	
	return flag;
}


//在SCL高电平时翻转会被误以为是起始信号或者终止信号
void IIC_SendByte(char datasend)
{ 
	int i;
	for(i = 0;i < 8;i++)
	{
		SCL = 0;//SCL拉低,让SDA做好数据准备
		SDA = datasend & 0x80;//1000 0000获得datasend的最高位
		_nop_();//发送数据建立时间
		SCL = 1;//SCL拉高开始传输
		_nop_();//数据发送时间
		SCL = 0;//发送完毕拉低
		_nop_();//
		datasend = datasend << 1;
	}
}


void OLED_writecmd(char cmd)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x00);//写入命令
	IIC_ACK();
	IIC_SendByte(cmd);
	IIC_ACK();
	IIC_Stop();
}

void OLED_writedata(char wdata)
{
	IIC_Start();
	IIC_SendByte(0x78);
	IIC_ACK();
	IIC_SendByte(0x40);//写入数据
	IIC_ACK();
	IIC_SendByte(wdata);
	IIC_ACK();
	IIC_Stop();
}

void OLED_Init()
{
	OLED_writecmd(0xAE);//--display off
	OLED_writecmd(0x00);//---set low column address
	OLED_writecmd(0x10);//---set high column address
	OLED_writecmd(0x40);//--set start line address  
	OLED_writecmd(0xB0);//--set page address
	OLED_writecmd(0x81); // contract control
	OLED_writecmd(0xFF);//--128   
	OLED_writecmd(0xA1);//set segment remap 
	OLED_writecmd(0xA6);//--normal / reverse
	OLED_writecmd(0xA8);//--set multiplex ratio(1 to 64)
	OLED_writecmd(0x3F);//--1/32 duty
	OLED_writecmd(0xC8);//Com scan direction
	OLED_writecmd(0xD3);//-set display offset
	OLED_writecmd(0x00);//
	
	OLED_writecmd(0xD5);//set osc division
	OLED_writecmd(0x80);//
	
	OLED_writecmd(0xD8);//set area color mode off
	OLED_writecmd(0x05);//
	
	OLED_writecmd(0xD9);//Set Pre-Charge Period
	OLED_writecmd(0xF1);//
	
	OLED_writecmd(0xDA);//set com pin configuartion
	OLED_writecmd(0x12);//
	
	OLED_writecmd(0xDB);//set Vcomh
	OLED_writecmd(0x30);//
	
	OLED_writecmd(0x8D);//set charge pump enable
	OLED_writecmd(0x14);//
	
	OLED_writecmd(0xAF);//--turn on oled panel		
}

void OLED_Clear()
{
	int i,j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);//选择0列
		OLED_writecmd(0x10);//选择0列
				//由于地址会自动偏移,所以只要重复写128次全0,就可以清一个PAGE
			for(j = 0;j < 128;j++)
			{
				OLED_writedata(0x00);
			}
				
	}

}

/*--  文字:  紫  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char z1[16] = {0x20,0x20,0x3E,0x20,0x1F,0x94,0x54,0x00,0x1F,0x24,0xA4,0x24,0x22,0x38,0x00,0x00};
char z2[16] = {0x00,0x80,0x48,0x29,0x09,0x4D,0x8D,0x7B,0x0B,0x09,0x28,0x4C,0x98,0x00,0x00,0x00};

/*--  文字:  苑  --*/
/*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
char y1[16] = {0x04,0x04,0x84,0x74,0x24,0x2F,0xE4,0x04,0xE4,0x2F,0x24,0x24,0xE4,0x04,0x04,0x00};
char y2[16] = {0x04,0x82,0x41,0x22,0x14,0x0C,0x03,0x00,0x3F,0x40,0x42,0x44,0x43,0x40,0x78,0x00};

void print_shion()
{
	int i;
	OLED_writecmd(0xB0);
	OLED_writecmd(0x00);
	OLED_writecmd(0x10);
	
	for(i = 0;i<16;i++)
	{
		OLED_writedata(z1[i]);
	}
	for(i = 0;i<16;i++)
	{
		OLED_writedata(y1[i]);
	}
	
	OLED_writecmd(0xB1);
	OLED_writecmd(0x00);
	OLED_writecmd(0x10);
	for(i = 0;i<16;i++)
	{
		OLED_writedata(z2[i]);
	}
	for(i = 0;i<16;i++)
	{
		OLED_writedata(y2[i]);
	}
	
}


code unsigned char image[] = {
/*--  宽度x高度=64x64  64*8*8(16进制)--*/
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,0xBF,0x9F,0xDF,0xDF,0xEF,0xEF,0xEF,0xE7,0xEF,0xEF,
0xFF,0xEF,0xEF,0xFF,0xFF,0xDF,0xDF,0xBF,0xBF,0x7F,0x7F,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,0x7F,0x3F,0xCF,
0xF3,0xF9,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,
0x9F,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,0x4F,0xF8,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,0xFF,0xFF,0xFF,0xFF,0x5F,0x1F,0x1F,0x1F,0x1F,
0x3F,0x0F,0x3F,0x3F,0xFF,0xBF,0xBF,0xBF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFC,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,0x32,0x87,0x03,0xD9,
0xFF,0xF9,0x81,0x07,0x01,0x00,0x0F,0x1F,0x11,0x11,0x1F,0x06,0x00,0x00,0x80,0x80,
0x80,0x80,0x80,0x04,0x0F,0x17,0x11,0x17,0x0F,0x06,0x00,0x03,0x80,0xFD,0xFF,0x1F,
0xF3,0x87,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x81,0xFC,0xFF,0xFF,0xFF,
0x00,0xFF,0xFF,0xFC,0xF0,0xE0,0xA0,0x60,0x40,0x40,0x40,0x40,0x00,0x83,0x84,0x80,
0x80,0x04,0x02,0xC0,0x40,0x40,0x40,0x20,0xE0,0xF0,0xF0,0xF8,0xFF,0xFF,0xFF,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xC3,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xF1,0xBF,0xFF,0xFF,0xFF,0x3F,0x9E,0xCC,0xE8,0xE0,0xE8,0xE9,0xE0,0xCA,0xC8,
0xC8,0xCA,0xC8,0xC9,0xC8,0xC4,0xE6,0x27,0x27,0xE7,0xEF,0xCF,0x8F,0x3F,0xFF,0xFE,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xC3,0xFC,0xFF,0xCF,0x87,0x03,0x07,0x0F,0x8F,
0x07,0x07,0x03,0x87,0x7F,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xFC,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x81,
0xE1,0xC1,0xA3,0x03,0x01,0x00,0x91,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0x7F,0xFF,0xFF,0xFF,0xFF,0xFC,0xFC,0xFC,0xFE,
0xFF,0xFF,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0x1F,0x0F,
0x0F,0x0F,0x1F,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
};



void OLED_Pic(unsigned char *image)
{
	unsigned char i;
	unsigned int j;
	for(i = 0;i<8;i++)
	{
		OLED_writecmd(0xB0 + i); //page 0 --page7
		//每个page从列
		OLED_writecmd(0x00);
		OLED_writecmd(0x10);
				//0到127列,以此写入0,每写入数据,列地址自动偏移
			for(j = 64*i;j < 64*(i+1);j++)
			{
				OLED_writedata(image[j]);
			}
				
	}

}

void main()
{
	
//	1.OLED初始化
	OLED_Init();
	OLED_Clear();
	//2.选择一个位置
//	2.1确认页寻址模式
	OLED_writecmd(0x20);
	OLED_writecmd(0x02);
//	2.2选择page0  1011 0000
	OLED_Pic(image);
	
	while(1);//防止程序退出
}
















 

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值