I2C總線與EEPROM

發(fā)布時間:2024-02-06
i2c總線是由philips公司開發(fā)的兩線式串行總線,多用于連接微處理器及其外圍設(shè)備。i2c總線的主要特點是接口方式簡單,兩條線可以掛多個參與通信的器件,即多機(jī)模式,而且任何一個器件都可以作為主機(jī),當(dāng)然同一時刻只能一個主機(jī)。
從原理上來講,uart屬于異步通信,比如電腦發(fā)送給單片機(jī),電腦只負(fù)責(zé)把數(shù)據(jù)通過txd發(fā)送出來即可,接收數(shù)據(jù)是單片機(jī)自己的事情。而i2c屬于同步通信,scl時鐘線負(fù)責(zé)收發(fā)雙方的時鐘節(jié)拍,sda數(shù)據(jù)線負(fù)責(zé)傳輸數(shù)據(jù)。i2c的發(fā)送方和接收方都以scl這個時鐘節(jié)拍為基準(zhǔn)進(jìn)行數(shù)據(jù)的發(fā)送和接收。
從應(yīng)用上來講,uart通信多用于板間通信,比如單片機(jī)和電腦,這個設(shè)備和另外一個設(shè)備之間的通信。而i2c多用于板內(nèi)通信,比如單片機(jī)和我們本章要學(xué)的eeprom之間的通信。
1、i2c時序初步認(rèn)識 在硬件上,i2c總線是由時鐘總線scl和數(shù)據(jù)總線sda兩條線構(gòu)成,連接到總線上的所有的器件的scl都連到一起,所有的sda都連到一起。i2c總線是開漏引腳并聯(lián)的結(jié)構(gòu),因此我們外部要添加上拉電阻。對于開漏電路外部加上拉電阻的話,那就組成了線“與”的關(guān)系??偩€上線“與”的關(guān)系,那所有接入的器件保持高電平,這條線才是高電平。而任意一個器件輸出一個低電平,那這條線就會保持低電平,因此可以做到任何一個器件都可以拉低電平,也就是任何一個器件都可以作為主機(jī),如圖1所示,我們添加了r63和r64兩個上拉電阻。
圖1i2c總線的上拉電阻
雖然說任何一個設(shè)備都可以作為主機(jī),但絕大多數(shù)情況下我們都是用微處理器,也就是我們的單片機(jī)來做主機(jī),而總線上掛的多個器件,每一個都像電話機(jī)一樣有自己唯一的地址,在信息傳輸?shù)倪^程中,通過這唯一的地址可以正常識別到屬于自己的信息,在我們的kst-51開發(fā)板上,就掛接了2個i2c設(shè)備,一個是24c02,一個是pcf8591。
我們在學(xué)習(xí)uart串行通信的時候,知道了我們的通信流程分為起始位、數(shù)據(jù)位、停止位這三部分,同理在i2c中也有起始信號、數(shù)據(jù)傳輸和停止信號,如圖2所示。
圖2i2c時序流程圖
從圖上可以看出來,i2c和uart時序流程有相似性,也有一定的區(qū)別。uart每個字節(jié)中,都有一個起始位,8個數(shù)據(jù)位和1位停止位。而i2c分為起始信號,數(shù)據(jù)傳輸部分,最后是停止信號。其中數(shù)據(jù)傳輸部分,可以一次通信過程傳輸很多個字節(jié),字節(jié)數(shù)是不受限制的,而每個字節(jié)的數(shù)據(jù)最后也跟了一位,這一位叫做應(yīng)答位,通常用ack表示,有點類似于uart的停止位。
下面我們一部分一部分的把i2c通信時序進(jìn)行剖析。之前我們學(xué)過了uart,所以學(xué)習(xí)i2c的過程我盡量拿uart來作為對比,這樣有助于更好的理解。但是有一點大家要理解清楚,就是uart通信雖然我們用了txd和rxd兩根線,但是實際一次通信,1條線就可以完成,2條線是把發(fā)送和接收分開而已,而i2c每次通信,不管是發(fā)送還是接收,必須2條線都參與工作才能完成,為了更方便的看出來每一位的傳輸流程,我們把圖2改進(jìn)成圖3。
圖3i2c通信流程解析
起始信號:uart通信是從一直持續(xù)的高電平出現(xiàn)一個低電平標(biāo)志起始位;而i2c通信的起始信號的定義是scl為高電平期間,sda由高電平向低電平變化產(chǎn)生一個下降沿,表示起始信號,如圖14-3中的start部分所示。
數(shù)據(jù)傳輸:首先,uart是低位在前,高位在后;而i2c通信是高位在前,低位在后。第二,uart通信數(shù)據(jù)位是固定長度,波特率分之一,一位一位固定時間發(fā)送完畢就可以了。而i2c沒有固定波特率,但是有時序的要求,要求當(dāng)scl在低電平的時候,sda允許變化,也就是說,發(fā)送方必須先保持scl是低電平,才可以改變數(shù)據(jù)線sda,輸出要發(fā)送的當(dāng)前數(shù)據(jù)的一位;而當(dāng)scl在高電平的時候,sda絕對不可以變化,因為這個時候,接收方要來讀取當(dāng)前sda的電平信號是0還是1,因此要保證sda的穩(wěn)定不變化,如圖14-3中的每一位數(shù)據(jù)的變化,都是在scl的低電平位置。8為數(shù)據(jù)位后邊跟著的是一位響應(yīng)位,響應(yīng)位我們后邊還要具體介紹。
停止信號:uart通信的停止位是一位固定的高電平信號;而i2c通信停止信號的定義是scl為高電平期間,sda由低電平向高電平變化產(chǎn)生一個上升沿,表示結(jié)束信號,如圖14-3中的stop部分所示。
2、i2c尋址模式
上面介紹的是i2c每一位信號的時序流程,而i2c通信在字節(jié)級的傳輸中,也有固定的時序要求。i2c通信的起始信號(start)后,首先要發(fā)送一個從機(jī)的地址,這個地址一共有7位,緊跟著的第8位是數(shù)據(jù)方向位(r/w),‘0’表示接下來要發(fā)送數(shù)據(jù)(寫),‘1’表示接下來是請求數(shù)據(jù)(讀)。
我們知道,打電話的時候,當(dāng)撥通電話,接聽方撿起電話肯定要回一個“喂”,這就是告訴撥電話的人,這邊有人了。同理,這個第九位ack實際上起到的就是這樣一個作用。當(dāng)我們發(fā)送完了這7位地址和1位方向位,如果我們發(fā)送的這個地址確實存在,那么這個地址的器件應(yīng)該回應(yīng)一個ack‘0’,如果不存在,就沒“人”回應(yīng)ack。
那我們寫一個簡單的程序,訪問一下我們板子上的eeprom的地址,另外在寫一個不存在的地址,看看他們是否能回一個ack,來了解和確認(rèn)一下這個問題。
我們板子上的eeprom器件型號是24c02,在24c02的數(shù)據(jù)手冊3.6部分說明了,24c02的7位地址中,其中高4位是固定的1010,而低3位的地址取決于我們電路的設(shè)計,由芯片上的a2、a1、a0這3個引腳的實際電平?jīng)Q定,來看一下我們的24c02的電路圖,如圖4所示。
圖424c02原理圖
從圖4可以看出來,我們的a2、a1、a0都是接的gnd,也就是說都是0,因此我們的7位地址實際上是二進(jìn)制的1010000,也就是0x50。我們用i2c的協(xié)議來尋址0x50,另外再尋址一個不存在的地址0x62,尋址完畢后,把返回的ack顯示到我們的1602液晶上,大家對比一下。
/***********************lcd1602.c文件程序源代碼*************************/
#include<reg52.h>
#definelcd1602_dbp0
sbitlcd1602_rs=p1^0;
sbitlcd1602_rw=p1^1;
sbitlcd1602_e=p1^5;
voidlcdwaitready()//等待液晶準(zhǔn)備好
{
unsignedcharsta;
lcd1602_db=0xff;
lcd1602_rs=0;
lcd1602_rw=1;
do
{
lcd1602_e=1;
sta=lcd1602_db;//讀取狀態(tài)字
lcd1602_e=0;
}while(sta&0x80);//bit7等于1表示液晶正忙,重復(fù)檢測直到其等于0為止
}
voidlcdwritecmd(unsignedcharcmd)//寫入命令函數(shù)
{
lcdwaitready();
lcd1602_rs=0;
lcd1602_rw=0;
lcd1602_db=cmd;
lcd1602_e=1;
lcd1602_e=0;
}
voidlcdwritedat(unsignedchardat)//寫入數(shù)據(jù)函數(shù)
{
lcdwaitready();
lcd1602_rs=1;
lcd1602_rw=0;
lcd1602_db=dat;
lcd1602_e=1;
lcd1602_e=0;
}
voidlcdshowstr(unsignedcharx,unsignedchary,constunsignedchar*str)//顯示字符串,屏幕起始坐標(biāo)(x,y),字符串指針str
{
unsignedcharaddr;
//由輸入的顯示坐標(biāo)計算顯示ram的地址
if(y==0)
addr=0x00+x;//第一行字符地址從0x00起始
else
addr=0x40+x;//第二行字符地址從0x40起始
//由起始顯示ram地址連續(xù)寫入字符串
lcdwritecmd(addr|0x80);//寫入起始地址
while(*str!='\0')//連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測到結(jié)束符
{
lcdwritedat(*str);
str++;
}
}
voidlcdinit()//液晶初始化函數(shù)
{
lcdwritecmd(0x38);//16*2顯示,5*7點陣,8位數(shù)據(jù)接口
lcdwritecmd(0x0c);//顯示器開,光標(biāo)關(guān)閉
lcdwritecmd(0x06);//文字不動,地址自動+1
lcdwritecmd(0x01);//清屏
}
/*************************main.c文件程序源代碼**************************/
#include<reg52.h>
#include<intrins.h>
#definei2cdelay(){_nop_();_nop_();_nop_();_nop_();}
sbiti2c_scl=p3^7;
sbiti2c_sda=p3^6;
biti2caddressing(unsignedcharaddr);
externvoidlcdinit();
externvoidlcdshowstr(unsignedcharx,unsignedchary,constunsignedchar*str);
voidmain()
{
bitack;
unsignedcharstr[10];
lcdinit();//初始化液晶
ack=i2caddressing(0x50);//查詢地址為0x50的器件
str[0]='5';//將地址和應(yīng)答值轉(zhuǎn)換為字符串
str[1]='0';
str[2]=':';
str[3]=(unsignedchar)ack+'0';
str[4]='\0';
lcdshowstr(0,0,str);//顯示到液晶上
ack=i2caddressing(0x62);//查詢地址為0x62的器件
str[0]='6';//將地址和應(yīng)答值轉(zhuǎn)換為字符串
str[1]='2';
str[2]=':';
str[3]=(unsignedchar)ack+'0';
str[4]='\0';
lcdshowstr(8,0,str);//顯示到液晶上
while(1)
{}
}
voidi2cstart()//產(chǎn)生總線起始信號
{
i2c_sda=1;//首先確保sda、scl都是高電平
i2c_scl=1;
i2cdelay();
i2c_sda=0;//先拉低sda
i2cdelay();
i2c_scl=0;//再拉低scl
}
voidi2cstop()//產(chǎn)生總線停止信號
{
i2c_scl=0;//首先確保sda、scl都是低電平
i2c_sda=0;
i2cdelay();
i2c_scl=1;//先拉高scl
i2cdelay();
i2c_sda=1;//再拉高sda
i2cdelay();
}
biti2cwrite(unsignedchardat)//i2c總線寫操作,待寫入字節(jié)dat,返回值為從機(jī)應(yīng)答位的值
{
bitack;//用于暫存應(yīng)答位的值
unsignedcharmask;//用于探測字節(jié)內(nèi)某一位值的掩碼變量
for(mask=0x80;mask!=0;mask>>=1)//從高位到低位依次進(jìn)行
{
if((mask&dat)==0)//該位的值輸出到sda上
i2c_sda=0;
else
i2c_sda=1;
i2cdelay();
i2c_scl=1;//拉高scl
i2cdelay();
i2c_scl=0;//再拉低scl,完成一個位周期
}
i2c_sda=1;//8位數(shù)據(jù)發(fā)送完后,主機(jī)釋放sda,以檢測從機(jī)應(yīng)答
i2cdelay();
i2c_scl=1;//拉高scl
i2cdelay();
ack=i2c_sda;//讀取此時的sda值,即為從機(jī)的應(yīng)答值
i2c_scl=0;//再拉低scl完成應(yīng)答位,并保持住總線
returnack;//返回從機(jī)應(yīng)答值
}
biti2caddressing(unsignedcharaddr)//i2c尋址函數(shù),即檢查地址為addr的器件是否存在,返回值為其應(yīng)答值,即應(yīng)答則表示存在,非應(yīng)答則表示不存在
{
bitack;
i2cstart();//產(chǎn)生起始位,即啟動一次總線操作
ack=i2cwrite(addr<<1);//器件地址需左移一位,因?qū)ぶ访畹淖畹臀粸樽x寫位,用于表示之后的操作是讀或?qū)?br> i2cstop();//不需進(jìn)行后續(xù)讀寫,而直接停止本次總線操作
returnack;
}
我們把這個程序在kst-51開發(fā)板上運行完畢,會在液晶上邊顯示出來我們預(yù)想的結(jié)果,主機(jī)發(fā)送一個存在的從機(jī)地址,從機(jī)會回復(fù)一個應(yīng)答位;主機(jī)如果發(fā)送一個不存在的從機(jī)地址,就沒有從機(jī)應(yīng)答。
前邊我有提到過有一個利用庫函數(shù)_nop_()來進(jìn)行精確延時,一個_nop_()的時間就是一個機(jī)器周期,這個庫函數(shù)是包含在了intrins.h這個庫文件中,我們?nèi)绻褂眠@個庫函數(shù),只需要在程序最開始,和包含reg52.h一樣,include<intrins.h>之后,我們程序就可以直接使用這個庫函數(shù)了。
還有一點要提一下,i2c通信分為低速模式100kbit/s,快速模式400kbit/s和高速模式3.4mbit/s。因為所有的i2c器件都支持低速,但卻未必支持另外兩種速度,所以作為通用的i2c程序我們選擇100k這個速率來實現(xiàn),也就是說實際程序產(chǎn)生的時序必須小于等于100k的時序參數(shù),很明顯也就是要求scl的高低電平持續(xù)時間都不短于5us,因此我們在時序函數(shù)中通過插入i2cdelay()這個總線延時函數(shù)(它實際上就是4個nop指令,用define在文件開頭做了定義),加上改變scl值語句本身占用的至少一個周期,來達(dá)到這個速度限制。如果以后需要提高速度,那么只需要減小這里的總線延時時間即可。
此外我們要學(xué)習(xí)一個發(fā)送數(shù)據(jù)的技巧,就是i2c通信時如何將一個字節(jié)的數(shù)據(jù)發(fā)送出去。大家注意寫函數(shù)中,我用的那個for循環(huán)的技巧。for(mask=0x80;mask!=0;mask>>=1),由于i2c通信是從高位開始發(fā)送數(shù)據(jù),所以我們先從最高位開始,0x80和dat進(jìn)行按位與運算,從而得知dat第7位是0還是1,然后右移一位,也就是變成了用0x40和dat按位與運算,得到第6位是0還是1,一直到第0位結(jié)束,最終通過if語句,把dat的8位數(shù)據(jù)依次發(fā)送了出去。其他的邏輯大家對照前邊講到的理論知識,認(rèn)真研究明白就可以了。
上一個:合肥計算機(jī)培訓(xùn)機(jī)構(gòu)有哪些,合肥北大青鳥計算機(jī)培訓(xùn)機(jī)構(gòu)
下一個:一文了解超濾膜設(shè)備油水分離過程!

探尋寺院僧侶茶葉的來源
便攜式手持氣象站價格是多少?
gmn軸承質(zhì)量可靠(價格優(yōu)惠歡迎訂購)
氙燈老化試驗箱作業(yè)指導(dǎo)書
華米手表二維碼在哪里找(華米手表二維碼在哪里掃)
藍(lán)花楹的花期
AC0201FR-07330KL,國巨車規(guī)電阻0201 330KΩ ±1% 1/20W
HRY5護(hù)套式電加熱器價格
房價評估價是按什么標(biāo)準(zhǔn)
蕉柑落果和裂果原因與對策
十八禁 网站在线观看免费视频_2020av天堂网_一 级 黄 色 片免费网站_绝顶高潮合集Videos