I2C協(xié)議的超簡(jiǎn)單解讀,小學(xué)生都能學(xué)得會(huì)
I2C是由兩根線組成的一種串行通信線路,如何用兩根線約定傳輸信號(hào)呢?
I2C兩根線分別為SCL時(shí)鐘同步線,SDA數(shù)據(jù)傳輸線,兩根線通過(guò)上拉電阻拉高電勢(shì),這個(gè)拉高有個(gè)好處就是可以釋放總線。這些先不說(shuō),先講如何標(biāo)記不同的信號(hào):
規(guī)定SCL為高的時(shí)候切換SDA跳變記作開(kāi)始信號(hào)與結(jié)束信號(hào);
規(guī)定SCL為低的時(shí)候切換SDA跳變記作數(shù)據(jù)傳輸信號(hào)。
記SDA釋放后拉低信號(hào)為響應(yīng)信號(hào)(應(yīng)答ACK),不拉低信號(hào)為非響應(yīng)信號(hào)(非應(yīng)答NACK)。
于是乎我們就可以用兩根線在SCL不同狀態(tài)下的跳變傳輸信號(hào)了。
開(kāi)始信號(hào):SCL為高電平時(shí)候SDA產(chǎn)生一個(gè)下降沿
????SCL_H
????????SDA_H
????????SDA_L
結(jié)束信號(hào):SCL為高電平時(shí)候產(chǎn)生一個(gè)上升沿
? ? SCL_H
????? ? SDA_L
????? ? SDA_H
數(shù)據(jù)傳輸信號(hào):SCL低電平時(shí)候切換SDA到要發(fā)送的電平,然后拉高準(zhǔn)備下一位發(fā)送。
????傳輸一個(gè)0
????SCL_L
????????SDA_L
? ? SCL_H
? ? 傳輸一個(gè)1
? ? SCL_L
????????SDA_H
? ? SCL_H
應(yīng)答與非應(yīng)答:SCL拉低釋放SDA,SCL再拉高后讀取SDA的狀態(tài),如果為高則非應(yīng)答,如果為低則為應(yīng)答。最后拉低SCL確保在停止信號(hào)前保證為時(shí)鐘低電平。
SCL_L
? ? ? ? SDA_H
SCL_H
讀取SDA狀態(tài)判斷是否有應(yīng)答
SCL_L
以上所有的信號(hào)都解讀完成了,你看懂了嗎?
接下來(lái)就是利用上述的I2C基礎(chǔ)信號(hào)組合成能發(fā)送字節(jié)的信號(hào)。
#include<REG52.h>
sbit OLED_SCL=P2^0;//時(shí)鐘 D0(SCLK)時(shí)鐘
sbit OLED_SDIN=P2^1;//D1(MOSI) 數(shù)據(jù)
#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 //寫(xiě)命令
#define OLED_DATA 1 //寫(xiě)數(shù)據(jù)
/*
I2C Start
起始信號(hào):時(shí)鐘信號(hào)在高電平狀態(tài)時(shí)候,數(shù)據(jù)線產(chǎn)生一個(gè)下降沿表示開(kāi)始信號(hào)
*/
void I2C_Start()
{
IO_SCL_SET_H();
IO_SDA_SET_H();
IO_SDA_SET_L();
}
/*
I2C Stop
停止信號(hào):時(shí)鐘信號(hào)在高電平狀態(tài)時(shí)候,數(shù)據(jù)線產(chǎn)生一個(gè)上升沿表示停止
*/
void I2C_Stop()
{
IO_SCL_SET_H();
IO_SDA_SET_L();
IO_SDA_SET_H();
}
/*
I2C ACK/NACK
應(yīng)答信號(hào):第九個(gè)時(shí)鐘周期的時(shí)鐘高電平時(shí)候讀取SDA數(shù)據(jù),如果SDA此時(shí)為0,則表示應(yīng)答,如果為1則表示非應(yīng)答。
*/
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 上發(fā)送一個(gè)字節(jié)數(shù)據(jù)
發(fā)送字節(jié):發(fā)送字節(jié)是時(shí)鐘信號(hào)的低電平時(shí)候修改數(shù)據(jù),發(fā)送完要讓時(shí)鐘總線保持在低電平。
*/
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:讀寫(xiě)位,1表示讀,0表示寫(xiě)
dat:數(shù)據(jù)字節(jié)
命令或數(shù)據(jù):1表示發(fā)送的字節(jié)是數(shù)據(jù),0表示發(fā)送的字節(jié)是命令
*/
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();
}
//坐標(biāo)設(shè)置
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);
}?
//開(kāi)啟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
}
//關(guān)閉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
}
//清屏函數(shù),清完屏,整個(gè)屏幕是黑色的!和沒(méi)點(diǎn)亮一樣!!! ??
void OLED_Clear(void)??
{??
unsigned char i,n; ? ??
for(i=0;i<4;i++)??
{??
OLED_WR_Byte (0xb0+i,OLED_CMD);? ? //設(shè)置頁(yè)地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD);? ? ? //設(shè)置顯示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD);? ? ? //設(shè)置顯示位置—列高地址? ?
for(n=0;n<128;n++)OLED_WR_Byte(0,OLED_DATA);?
} //更新顯示
}
//反顯函數(shù)
void OLED_ColorTurn(unsigned char i)
{
if(i==0)
{
OLED_WR_Byte(0xA6,OLED_CMD);//正常顯示
}
if(i==1)
{
OLED_WR_Byte(0xA7,OLED_CMD);//反色顯示
}
}
//屏幕旋轉(zhuǎn)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);//反轉(zhuǎn)顯示
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:顯示的數(shù)字,0-9
x:坐標(biāo):0-127
y:坐標(biāo):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);
? ? ? ? }
? ? }
}