手册是ssd1306
void ICC_Start()
{
scl=0;//防止stop后scl没有变成低电平
sda=1;
scl=1;
_nop_();//a->c
sda=0;//b->c
_nop_();//c->d
}
void ICC_Stop()
{
scl=0;
sda=0;
scl = 1;
_nop_();//a->b
sda=1;//b->c
_nop_();//c->d
}
应答信号
char ICC_ACK()
{
char flag;
sda = 1;//初始拉高
_nop_();//延时等待
scl = 1;//拉高
_nop_();//等待sda变化
flag = sda;//获得sda值
_nop_();//确保获得完毕
scl=0;//拉低
_nop_();//这里我认为不用,但应该是确保scl拉低了
return flag;
}
数据发送时序分析
数据发送是1bit1bit的发送的
void ICC_Send_Byte(char datas)
{
int i=0;
for(;i<8;i++)//有8位需要发送
{
scl=0;//拉低电平
sda=datas & 0x80;//取得最高位
_nop_();//数据建立延时
scl=1;//拉高
_nop_();//数据发送延时
scl=0;//发送完毕
_nop_();
datas = datas<<1;
}
}
写命令操作分析
由图片可知
- 先发送开始信号
- slave Address:由手册可知,有两种结果:“b0111100”或“b0111101”其中若R/W位为0,才是写入模式
- ACK
- 确定发送的是地址还是数据:根据手册可以知道,如果Co位需要为0,D/C位如果为0那么就是发送的是命令/地址否则为数据
- ACK
- 写入相应的地址/数据
- ACK
- STOP
void Oled_Write_Cmd(char cmd){
//开始
ICC_Start();
//Slave Address 0111 1000
//对于SSD1306,
//通过将SA0更改为LOW(低)或HIGH(高),
//从属地址为“b0111100”或“b0111101”
//(D/C引脚用作SA0)。
ICC_Send_Byte(0x78);
//ACK
ICC_ACK();
//指定写入的是命令还是数据
//xx00 0000
//写入命令:0000 0000
ICC_Send_Byte(0x00);
//ACK
ICC_ACK();
//写入命令
ICC_Send_Byte(cmd);
//ACK
ICC_ACK();
ICC_Stop();
}
//写数据
void Oled_Write_Data(char datas){
//开始
ICC_Start();
//Slave Address 0111 1000
//对于SSD1306,
//通过将SA0更改为LOW(低)或HIGH(高),
//从属地址为“b0111100”或“b0111101”
//(D/C引脚用作SA0)。
ICC_Send_Byte(0x78);
//ACK
ICC_ACK();
//指定写入的是命令还是数据
//xx00 0000
//指定为写入数据:0100 0000
ICC_Send_Byte(0x40);
//ACK
ICC_ACK();
//写入数据
ICC_Send_Byte(datas);
//ACK
ICC_ACK();
ICC_Stop();
}
在GME12864上显示一句话
#include "reg52.h"
#include "intrins.h"
sbit sda = P0^3;
sbit scl = P0^1;
void ICC_Start()
{
scl=0;
sda=1;
scl=1;
_nop_();
sda=0;
_nop_();
}
void ICC_Stop()
{
scl=0;
sda=0;
scl=1;
_nop_();
sda=1;
_nop_();
}
char ICC_ACK()
{
char flag;
sda = 1;//初始拉高
_nop_();//延时等待
scl = 1;//拉高
_nop_();//等待sda变化
flag = sda;
_nop_();
scl=0;
_nop_();
return flag;
}
void ICC_Send_Byte(char datas)
{
int i=0;
for(;i<8;i++)
{
scl=0;
sda=datas & 0x80;
_nop_();
scl=1;
_nop_();
scl=0;
_nop_();
datas = datas<<1;
}
}
//写命令操作
void Oled_Write_Cmd(char cmd){
//开始
ICC_Start();
//Slave Address 0111 1000
//对于SSD1306,
//通过将SA0更改为LOW(低)或HIGH(高),
//从属地址为“b0111100”或“b0111101”
//(D/C引脚用作SA0)。
ICC_Send_Byte(0x78);
//ACK
ICC_ACK();
//指定写入的是命令还是数据
//xx00 0000
//写入命令:0000 0000
ICC_Send_Byte(0x00);
//ACK
ICC_ACK();
//写入地址
ICC_Send_Byte(cmd);
//ACK
ICC_ACK();
ICC_Stop();
}
//写数据
void Oled_Write_Data(char datas){
//开始
ICC_Start();
//Slave Address 0111 1000
//对于SSD1306,
//通过将SA0更改为LOW(低)或HIGH(高),
//从属地址为“b0111100”或“b0111101”
//(D/C引脚用作SA0)。
ICC_Send_Byte(0x78);
//ACK
ICC_ACK();
//指定写入的是命令还是数据
//xx00 0000
//指定为写入数据:0100 0000
ICC_Send_Byte(0x40);
//ACK
ICC_ACK();
//写入数据
ICC_Send_Byte(datas);
//ACK
ICC_ACK();
ICC_Stop();
}
//oled初始化函数,手册上面有
void Oled_Init()
{
Oled_Write_Cmd(0xAE);//--display off
Oled_Write_Cmd(0x00);//---set low column address
Oled_Write_Cmd(0x10);//---set high column address
Oled_Write_Cmd(0x40);//--set start line address
Oled_Write_Cmd(0xB0);//--set page address
Oled_Write_Cmd(0x81); // contract control
Oled_Write_Cmd(0xFF);//--128
Oled_Write_Cmd(0xA1);//set segment remap
Oled_Write_Cmd(0xA6);//--normal / reverse
Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
Oled_Write_Cmd(0x3F);//--1/32 duty
Oled_Write_Cmd(0xC8);//Com scan direction
Oled_Write_Cmd(0xD3);//-set display offset
Oled_Write_Cmd(0x00);//
Oled_Write_Cmd(0xD5);//set osc division
Oled_Write_Cmd(0x80);//
Oled_Write_Cmd(0xD8);//set area color mode off
Oled_Write_Cmd(0x05);//
Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
Oled_Write_Cmd(0xF1);//
Oled_Write_Cmd(0xDA);//set com pin configuartion
Oled_Write_Cmd(0x12);//
Oled_Write_Cmd(0xDB);//set Vcomh
Oled_Write_Cmd(0x30);//
Oled_Write_Cmd(0x8D);//set charge pump enable
Oled_Write_Cmd(0x14);//
Oled_Write_Cmd(0xAF);//--turn on oled panel
}
//清屏函数
void Oled_Clear()
{
unsigned char i,j; //-128 --- 127
for(i=0;i<8;i++){
Oled_Write_Cmd(0xB0 + i);//page0--page7
//每个page从0列
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
//0到127列,依次写入0,每写入数据,列地址自动偏移
for(j = 0;j<128;j++){
Oled_Write_Data(0);
}
}
}
//点阵液晶工具自动生成
/*-- 文字: 学 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char x1[16]={0x40,0x30,0x11,0x96,0x90,0x90,0x91,0x96,0x90,0x90,0x98,0x14,0x13,0x50,0x30,0x00};
code char x2[16]={0x04,0x04,0x04,0x04,0x04,0x44,0x84,0x7E,0x06,0x05,0x04,0x04,0x04,0x04,0x04,0x00};
/*-- 文字: 习 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char xx1[16]={0x00,0x02,0x02,0x02,0x12,0x22,0xC2,0x02,0x02,0x02,0x02,0x02,0xFE,0x00,0x00,0x00};
code char xx2[16]={0x00,0x08,0x18,0x08,0x04,0x04,0x04,0x02,0x02,0x41,0x81,0x40,0x3F,0x00,0x00,0x00};
/*-- 文字: 嵌 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char q1[16]={0x80,0x80,0xEE,0x88,0x88,0x88,0xE8,0x8F,0x08,0x88,0x78,0x48,0x4E,0x40,0xC0,0x00};
code char q2[16]={0x00,0x00,0x7F,0x24,0x24,0x24,0x7F,0x00,0x81,0x40,0x30,0x0F,0x30,0x41,0x80,0x00};
/*-- 文字: 入 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char r1[16]={0x00,0x00,0x00,0x00,0x00,0x01,0xE2,0x1C,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
code char r2[16]={0x80,0x40,0x20,0x10,0x0C,0x03,0x00,0x00,0x00,0x03,0x0C,0x30,0x40,0x80,0x80,0x00};
/*-- 文字: 式 --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=16x16 --*/
code char s1[16]={0x10,0x10,0x90,0x90,0x90,0x90,0x90,0x10,0x10,0xFF,0x10,0x10,0x11,0x16,0x10,0x00};
code char s2[16]={0x00,0x20,0x60,0x20,0x3F,0x10,0x10,0x10,0x00,0x03,0x0C,0x10,0x20,0x40,0xF8,0x00};
void main(){
int i;
//初始化
Oled_Init();
//设置为页寻址模式
Oled_Write_Cmd(0x20);
Oled_Write_Cmd(0x02);
Oled_Clear();
Oled_Write_Cmd(0xB0);
//这两个数据拼接起来就是第几列:
Oled_Write_Cmd(0x00);//低4位
Oled_Write_Cmd(0x10);//高4位
for(i=0; i<16;++i){
Oled_Write_Data(x1[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(xx1[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(q1[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(r1[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(s1[i]);
}
Oled_Write_Cmd(0xB1);
Oled_Write_Cmd(0x00);//指定位置
Oled_Write_Cmd(0x10);//指定位置
for(i=0; i<16;++i){
Oled_Write_Data(x2[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(xx2[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(q2[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(r2[i]);
}
for(i=0; i<16;++i){
Oled_Write_Data(s2[i]);
}
while(1);
}