IIC协议
IIC协议概述
- IIC全称Inter-Integrated Circuit (集成电路总线)
-
是由 PHILIPS 公司在 80 年代开发的两线式串行总线,用于连接微控制器及其外围设备。 IIC属于半双工同步通信方式 (同一时间,只能发送或接收),而串口是全双工的通讯方式(通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通讯方式的结合 )
特点
- 简单性和有效性。
- 多主控(multimastering)
构成
OLED
之前学习的LCD1602和dht11都属于非标协议的元件,即他们有自己独特的激活方式,需要针对他们不同的器件来写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);//防止程序退出
}