I2C协议的超简单解读,小学生都能学得会|全球关注
来源:哔哩哔哩    时间:2023-06-19 11:07:53

I2C是由两根线组成的一种串行通信线路,如何用两根线约定传输信号呢?

I2C两根线分别为SCL时钟同步线,SDA数据传输线,两根线通过上拉电阻拉高电势,这个拉高有个好处就是可以释放总线。这些先不说,先讲如何标记不同的信号:


(资料图)

规定SCL为高的时候切换SDA跳变记作开始信号与结束信号;

规定SCL为低的时候切换SDA跳变记作数据传输信号。

记SDA释放后拉低信号为响应信号(应答ACK),不拉低信号为非响应信号(非应答NACK)。

于是乎我们就可以用两根线在SCL不同状态下的跳变传输信号了。

开始信号:SCL为高电平时候SDA产生一个下降沿

SCL_H

SDA_H

SDA_L

结束信号:SCL为高电平时候产生一个上升沿

SCL_H

SDA_L

SDA_H

数据传输信号:SCL低电平时候切换SDA到要发送的电平,然后拉高准备下一位发送。

传输一个0

SCL_L

SDA_L

SCL_H

传输一个1

SCL_L

SDA_H

SCL_H

应答与非应答:SCL拉低释放SDA,SCL再拉高后读取SDA的状态,如果为高则非应答,如果为低则为应答。最后拉低SCL确保在停止信号前保证为时钟低电平。

SCL_L

SDA_H

SCL_H

读取SDA状态判断是否有应答

SCL_L

以上所有的信号都解读完成了,你看懂了吗?

接下来就是利用上述的I2C基础信号组合成能发送字节的信号。

#include<REG52.h>

sbit OLED_SCL=P2^0;//时钟 D0(SCLK)时钟

sbit OLED_SDIN=P2^1;//D1(MOSI) 数据

#define IO_SCL_SET_L() OLED_SCL=0

#define IO_SCL_SET_H() OLED_SCL=1

#define IO_SDA_SET_H() OLED_SDIN=1

#define IO_SDA_SET_L() OLED_SDIN=0

#define Get_IO_SDA() OLED_SDIN

#define OLED_CMD  0//写命令

#define OLED_DATA 1//写数据

/*

I2C Start

起始信号:时钟信号在高电平状态时候,数据线产生一个下降沿表示开始信号

*/

void I2C_Start()

{

IO_SCL_SET_H();

IO_SDA_SET_H();

IO_SDA_SET_L();

}

/*

I2C Stop

停止信号:时钟信号在高电平状态时候,数据线产生一个上升沿表示停止

*/

void I2C_Stop()

{

IO_SCL_SET_H();

IO_SDA_SET_L();

IO_SDA_SET_H();

}

/*

I2C ACK/NACK

应答信号:第九个时钟周期的时钟高电平时候读取SDA数据,如果SDA此时为0,则表示应答,如果为1则表示非应答。

*/

void I2C_ACK()

{

IO_SDA_SET_H();

IO_SCL_SET_H();

if(Get_IO_SDA()==0)

IO_SCL_SET_L();

else

while(Get_IO_SDA()==1);

IO_SCL_SET_L();

}

/*

I2C 上发送一个字节数据

发送字节:发送字节是时钟信号的低电平时候修改数据,发送完要让时钟总线保持在低电平。

*/

void Write_I2C_Byte(unsigned char I2C_Byte)

{

unsigned char i;

for(i=0;i<8;i++)

{

IO_SCL_SET_L();

if((I2C_Byte<<i)&0x80)

IO_SDA_SET_H();

else

IO_SDA_SET_L();

IO_SCL_SET_H();

}

IO_SCL_SET_L();

}

/*

add:7位地址;

rwbit:读写位,1表示读,0表示写

dat:数据字节

命令或数据:1表示发送的字节是数据,0表示发送的字节是命令

*/

void OLED_WR_Byte(unsigned char dat,unsigned char cmd)

{

I2C_Start();

Write_I2C_Byte(0x78);

I2C_ACK();

if(cmd)

Write_I2C_Byte(0x40);

else

Write_I2C_Byte(0x00);

I2C_ACK();

Write_I2C_Byte(dat);

I2C_ACK();

I2C_Stop();

}

//坐标设置

void OLED_Set_Pos(unsigned char x, unsigned char y) 

{

OLED_WR_Byte(0xb0+y,OLED_CMD);

OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);

OLED_WR_Byte((x&0x0f),OLED_CMD);

//开启OLED显示    

void OLED_Display_On(void)

{

OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令

OLED_WR_Byte(0X14,OLED_CMD);  //DCDC ON

OLED_WR_Byte(0XAF,OLED_CMD);  //DISPLAY ON

}

//关闭OLED显示     

void OLED_Display_Off(void)

{

OLED_WR_Byte(0X8D,OLED_CMD);  //SET DCDC命令

OLED_WR_Byte(0X10,OLED_CMD);  //DCDC OFF

OLED_WR_Byte(0XAE,OLED_CMD);  //DISPLAY OFF

}

//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!  

void OLED_Clear(void)  

{  

unsigned char i,n;    

for(i=0;i<4;i++)  

{  

OLED_WR_Byte (0xb0+i,OLED_CMD);    //设置页地址(0~7)

OLED_WR_Byte (0x00,OLED_CMD);      //设置显示位置—列低地址

OLED_WR_Byte (0x10,OLED_CMD);      //设置显示位置—列高地址   

for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA); 

} //更新显示

}

//反显函数

void OLED_ColorTurn(unsigned char i)

{

if(i==0)

{

OLED_WR_Byte(0xA6,OLED_CMD);//正常显示

}

if(i==1)

{

OLED_WR_Byte(0xA7,OLED_CMD);//反色显示

}

}

//屏幕旋转180度

void OLED_DisplayTurn(unsigned char i)

{

if(i==0)

{

OLED_WR_Byte(0xC8,OLED_CMD);//正常显示

OLED_WR_Byte(0xA1,OLED_CMD);

}

if(i==1)

{

OLED_WR_Byte(0xC0,OLED_CMD);//反转显示

OLED_WR_Byte(0xA0,OLED_CMD);

}

}

//初始化    

void OLED_Init(void)

{

OLED_WR_Byte(0xAE,OLED_CMD); /*display off*/

OLED_WR_Byte(0x00,OLED_CMD); /*set lower column address*/ 

OLED_WR_Byte(0x10,OLED_CMD); /*set higher column address*/

OLED_WR_Byte(0x00,OLED_CMD); /*set display start line*/ 

OLED_WR_Byte(0xB0,OLED_CMD); /*set page address*/ 

OLED_WR_Byte(0x81,OLED_CMD); /*contract control*/ 

OLED_WR_Byte(0xff,OLED_CMD); /*128*/ 

OLED_WR_Byte(0xA1,OLED_CMD); /*set segment remap*/ 

OLED_WR_Byte(0xA6,OLED_CMD); /*normal / reverse*/ 

OLED_WR_Byte(0xA8,OLED_CMD); /*multiplex ratio*/ 

OLED_WR_Byte(0x1F,OLED_CMD); /*duty = 1/32*/ 

OLED_WR_Byte(0xC8,OLED_CMD); /*Com scan direction*/ 

OLED_WR_Byte(0xD3,OLED_CMD); /*set display offset*/ 

OLED_WR_Byte(0x00,OLED_CMD); 

OLED_WR_Byte(0xD5,OLED_CMD); /*set osc division*/ 

OLED_WR_Byte(0x80,OLED_CMD); 

OLED_WR_Byte(0xD9,OLED_CMD); /*set pre-charge period*/ 

OLED_WR_Byte(0x1f,OLED_CMD); 

OLED_WR_Byte(0xDA,OLED_CMD); /*set COM pins*/ 

OLED_WR_Byte(0x00,OLED_CMD); 

OLED_WR_Byte(0xdb,OLED_CMD); /*set vcomh*/ 

OLED_WR_Byte(0x40,OLED_CMD); 

OLED_WR_Byte(0x8d,OLED_CMD); /*set charge pump enable*/ 

OLED_WR_Byte(0x14,OLED_CMD);

OLED_Clear();

OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/ 

}

unsigned char code num[][16]={

{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/

{0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/

{0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/

{0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/

{0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/

{0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/

{0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/

{0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/

{0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/

{0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/

};

/*

numb:显示的数字,0-9

x:坐标:0-127

y:坐标:0-3

flag:0为正显示,1为反显示

*/

void numberx(unsigned char numb, unsigned char x, unsigned char y, unsigned char flag) {

unsigned char i;

for (i = 0; i < 16; i++) {

if (i == 0 || i == 15) {

OLED_Set_Pos(x + i, y + 0);

OLED_WR_Byte(flag ? ~0xFF : 0xFF, OLED_DATA);

OLED_Set_Pos(x + i, y + 1);

OLED_WR_Byte(flag ? ~0xFF : 0xFF, OLED_DATA);

} else if ((i > 0 && i < 4) || (i > 12 && i < 16)) {

OLED_Set_Pos(x + i, y + 0);

OLED_WR_Byte(flag ? ~0x01 : 0x01, OLED_DATA);

OLED_Set_Pos(x + i, y + 1);

OLED_WR_Byte(flag ? ~0x80 : 0x80, OLED_DATA);

} else {

OLED_Set_Pos(x + i, y + 0);

OLED_WR_Byte(flag ? ~(0x01 | num[numb][i - 4]) : (0x01 | num[numb][i - 4]), OLED_DATA);

OLED_Set_Pos(x + i, y + 1);

OLED_WR_Byte(flag ? ~(0x80 | num[numb][i + 4]) : (0x80 | num[numb][i + 4]), OLED_DATA);

}

}

}

关键词:

X 关闭

X 关闭