標(biāo)題: 第12章 I2C總線與EEPROM 12.2 [打印本頁]
作者: 卓然塵世間 時間: 2026-4-28 14:42
標(biāo)題: 第12章 I2C總線與EEPROM 12.2
12.2.I2C尋址模式上一節(jié)介紹的是I2C每一位信號的時序流程,而I2C通信在字節(jié)級的傳輸中,也有固定的時序要求。I2C通信的起始信號(Start)后,首先要發(fā)送一個從機(jī)的地址,這個地址一共有7位,緊跟著的第8位是數(shù)據(jù)方向位(R/W),“0”表示接下來要發(fā)送數(shù)據(jù)(寫),‘“1”表示接下來是請求數(shù)據(jù)(讀)。
打電話的時候,當(dāng)撥通電話,接聽方撿起電話肯定要回一個“喂”,這就是告訴撥電話的人,這邊有人了。同理,這個第九位ACK實(shí)際上起到的就是這樣一個作用。當(dāng)發(fā)送完了這7位地址和1位方向后,如果發(fā)送的這個地址確實(shí)存在,那么這個地址的器件應(yīng)該回應(yīng)一個ACK(拉低SDA即輸出“0”),如果不存在,就沒“人”回應(yīng)NACK(SDA將保持高電平即“1”)。
寫一個簡單的程序,訪問一下Kingst51開發(fā)板上的EEPROM的地址,另外再寫一個不存在的地址,看看它們是否能回一個ACK,來了解和確認(rèn)一下這個問題。
Kingst51開發(fā)板上的EEPROM器件型號是24C02,在24C02的數(shù)據(jù)手冊3.6節(jié)中可查到,24C02的7位地址中,其中高4位是0b1010,低3位的地址取決于具體電路的設(shè)計(jì),由芯片的A2、A1、A0這3個引腳的實(shí)際電平?jīng)Q定,來看一下電路圖,如圖12-4所示。
12-4.png (32.25 KB, 下載次數(shù): 0)
下載附件
2026-4-28 14:41 上傳
圖12-4 24C02原理圖
從圖12-4可以看出,A2、A1、A0都是接的GND,也就是說都是0,因此24C02的7位地址實(shí)際上是二進(jìn)制的0b1010000,也就是0x50。用I2C的協(xié)議來尋址0x50,另外再尋址一個不存在的地址0x62,尋址完畢后,通過邏輯分析儀觀察一下兩個地址是否回復(fù)ACK。
/*****************************main.c文件程序源代碼******************************/
#include <reg52.h>
#include <intrins.h>
#define I2CDelay() {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;
bit I2CAddressing(unsigned char addr);
void main()
{
I2CAddressing(0x50); //查詢地址為0x50的器件
I2CAddressing(0x62); //查詢地址為0x62的器件
while (1);
}
/* 產(chǎn)生總線起始信號 */
void I2CStart()
{
I2C_SDA = 1; //首先確保SDA、SCL都是高電平
I2C_SCL = 1;
I2CDelay();
I2C_SDA = 0; //先拉低SDA
I2CDelay();
I2C_SCL = 0; //再拉低SCL
}
/* 產(chǎn)生總線停止信號 */
void I2CStop()
{
I2C_SCL = 0; //首先確保SDA、SCL都是低電平
I2C_SDA = 0;
I2CDelay();
I2C_SCL = 1; //先拉高SCL
I2CDelay();
I2C_SDA = 1; //再拉高SDA
I2CDelay();
}
/* I2C總線寫操作,dat-待寫入字節(jié),返回值-從機(jī)應(yīng)答位的值 */
bit I2CWrite(unsigned char dat)
{
bit ack; //用于暫存應(yīng)答位的值
unsigned char mask; //用于探測字節(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
ack = I2C_SDA; //讀取此時的SDA值,即為從機(jī)的應(yīng)答值
I2CDelay();
I2C_SCL = 0; //再拉低SCL完成應(yīng)答位,并保持住總線
return ack; //返回從機(jī)應(yīng)答值
}
/* I2C尋址函數(shù),即檢查地址為addr的器件是否存在,返回值-從器件應(yīng)答值 */
bit I2CAddressing(unsigned char addr)
{
bit ack;
I2CStart(); //產(chǎn)生起始位,即啟動一次總線操作
ack = I2CWrite(addr<<1); //器件地址需左移一位,因?qū)ぶ访畹淖畹臀?/font>
//為讀寫位,用于表示之后的操作是讀或?qū)?/font>
I2CStop(); //不需進(jìn)行后續(xù)讀寫,而直接停止本次總線操作
return ack;
}
前面的章節(jié)中已經(jīng)提到利用庫函數(shù)_nop_()可以進(jìn)行精確延時,一個_nop_()的時間就是一個機(jī)器周期,這個庫函數(shù)包含在intrins.h這個文件中,如果要使用這個庫函數(shù),只需要在程序最開始,和包含reg52.h一樣,include<intrins.h>之后,程序中就可以使用這個庫函數(shù)了。
還有一點(diǎn)要提一下,I2C通信分為低速模式100kbit/s、快速模式400kbit/s和高速模式3.4Mbit/s。因?yàn)樗械?/font>I2C器件都支持低速,但卻未必支持另外兩種速度,所以作為通用的I2C程序選擇100k這個速率,也就是說實(shí)際程序產(chǎn)生的時序必須小于等于100k的時序參數(shù),很明顯也就是要求SCL的高低電平持續(xù)時間都不短于5us,因此在時序函數(shù)中通過插入I2CDelay()這個總線延時函數(shù)(它實(shí)際上就是4個NOP指令,用define在文件開頭做了定義),加上改變SCL值語句本身占用的至少一個周期,來達(dá)到這個速度限制。如果以后需要提高速度,那么只需要減小這里的總線延時時間即可。
此外學(xué)習(xí)一個發(fā)送數(shù)據(jù)的技巧,就是I2C通信時如何將一個字節(jié)的數(shù)據(jù)發(fā)送出去。注意函數(shù)I2CWrite中,用的for循環(huán)的技巧。for (mask=0x80; mask!=0; mask>>=1),由于I2C通信是從高位開始發(fā)送數(shù)據(jù),所以先從最高位開始,0x80和dat進(jìn)行按位與運(yùn)算,從而得知dat第7位是0還是1,然后右移一位,也就是變成了用0x40和dat按位與運(yùn)算,得到第6位是0還是1,一直到第0位結(jié)束,最終通過if語句,把dat的8位數(shù)據(jù)依次發(fā)送了出去。
使用Kingst LA5016邏輯分析儀將抓到的波形顯示出來,并且用過I2C的協(xié)議解碼器將協(xié)議解析出來,如圖12-5所示。從圖上可以看出,第一個字節(jié)發(fā)的是0x50,回復(fù)了一個ACK;第二個字節(jié)發(fā)了一個0x62,但是出現(xiàn)的是NAK,說明這個地址沒有產(chǎn)生應(yīng)答。
12-5.png (13.44 KB, 下載次數(shù): 0)
下載附件
2026-4-28 14:41 上傳
圖12-5 邏輯分析儀抓取I2C地址
在邏輯分析儀的I2C協(xié)議設(shè)置中,有三種地址格式顯示方式,也就是目前市面上各種資料對I2C協(xié)議地址定義的方式。如圖12-6所示。
12-6.png (21.5 KB, 下載次數(shù): 0)
下載附件
2026-4-28 14:41 上傳
圖12-6 I2C地址顯示格式
前邊講I2C發(fā)送的第一個字節(jié)是7位地址加一位讀寫位,但是有些資料直接將讀寫位歸結(jié)到I2C的地址,也有的資料將7位地址位認(rèn)為是高7位,以開發(fā)板的0x50地址和0x62地址為例,即地址二進(jìn)制0b1010 000,寫的時候是0b1010 0000;讀的時候是0b1010 0001。
方式1:8-bit,包含讀/寫位,0x50地址對應(yīng)這種方式寫地址為0xA0;讀地址為0xA1。
方式2:8-bit,讀/寫位顯示為0,即寫地址和讀地址都是0xA0。
方式3:7-bit,本教材采用的方式,寫地址和讀地址都是0x50。
| 歡迎光臨 (http://www.denmoz.com/bbs/) |
Powered by Discuz! X3.1 |