找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 6909|回復(fù): 20
打印 上一主題 下一主題
收起左側(cè)

關(guān)于I2C總線讀寫應(yīng)答機(jī)制

  [復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:266429 發(fā)表于 2021-10-26 10:06 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
I2C總線中的應(yīng)答機(jī)制,屬于該總線規(guī)格中的關(guān)鍵內(nèi)容,本來(lái)并不復(fù)雜,也很容易理解,但問(wèn)題在于現(xiàn)有的教材基本都是含糊不清,有的甚至?xí)鹫`解。因此,有必要詳細(xì)說(shuō)明。
先說(shuō)明兩個(gè)I2C總線中很重要的規(guī)則:
1. 總線在正常工作期間,從器件對(duì)時(shí)鐘線無(wú)控制權(quán),即從器件的時(shí)鐘端口輸出應(yīng)始終保持在高電位,時(shí)鐘線的狀態(tài),只能由主器件進(jìn)行操作;至于數(shù)據(jù)線,則主器件與從器件都有權(quán)參與操作。
2. 總線正常工作時(shí),時(shí)鐘線處于高電位期間,不允許改變數(shù)據(jù)線的狀態(tài),即此時(shí)無(wú)論主器件還是從器件,都不能對(duì)其數(shù)據(jù)端口進(jìn)行狀態(tài)改變,除非是起始和終止操作。
記住這兩個(gè)規(guī)則,很重要。
以下均通過(guò)AT24C的驗(yàn)證。

一、應(yīng)答原理
I2C總線,有點(diǎn)像我們用的對(duì)講機(jī),盡管可以雙向通訊,但雙方不能同時(shí)說(shuō)話,于是,就必須設(shè)有應(yīng)答機(jī)制,而且,這個(gè)應(yīng)答機(jī)制,必須是雙方互動(dòng)的,一方的話說(shuō)完了,就要問(wèn)對(duì)方收到?jīng)]有,對(duì)方如果收到了這個(gè)問(wèn)話,還得要再回個(gè)話。

二、向從器件寫入時(shí)的應(yīng)答機(jī)制
I2C與這種對(duì)講機(jī)的應(yīng)答的具體方式還是有些不一樣的,規(guī)定是這樣:在主機(jī)向從機(jī)發(fā)送數(shù)據(jù)時(shí),規(guī)定每次發(fā)送一字節(jié)后就進(jìn)行應(yīng)答,主機(jī)每發(fā)送一字節(jié)數(shù)據(jù)后就停一下等待從機(jī)的反應(yīng),從機(jī)在收到一字節(jié)后的反應(yīng)就是將其自身的數(shù)據(jù)端輸出低電位,也就是向主機(jī)發(fā)出信號(hào)說(shuō)自己剛收到一字節(jié),并等待主機(jī)的回應(yīng);主機(jī)接下來(lái)的操作就是拉高自身的數(shù)據(jù)端口電位,再檢測(cè)端口電位是否為低,如果檢測(cè)到為低,則認(rèn)為從機(jī)已經(jīng)收到了一個(gè)字節(jié),然后要告訴從機(jī)我知道了也就是給從機(jī)一個(gè)回應(yīng),回應(yīng)的方法是將時(shí)鐘線操作一個(gè)完整的總線時(shí)鐘周期即先拉高再拉低;從器件在收到主器件的這個(gè)回應(yīng)后,就將其自身數(shù)據(jù)端口的電位拉高即交出對(duì)數(shù)據(jù)線的控制權(quán),這樣就完成了一次完整的應(yīng)答。
以上的時(shí)序是不能弄錯(cuò)的,比如主器件在發(fā)送完成一字節(jié)數(shù)據(jù)后,拉高數(shù)據(jù)線端以檢測(cè)從器件的應(yīng)答信號(hào)的操作,按I2C總線的規(guī)則必須先將時(shí)鐘線拉低才能再改變數(shù)據(jù)線,而一字節(jié)從發(fā)送到完成應(yīng)答,時(shí)鐘線的操作只能是9個(gè)總線周期,這9個(gè)總線周期中減除最后一個(gè)完整的應(yīng)答總線時(shí)鐘周期,那這個(gè)拉低的時(shí)間只能是安排在第8?jìng)(gè)周期。
另外,我們可以做一個(gè)實(shí)驗(yàn),就是監(jiān)測(cè)數(shù)據(jù)線的電位高低變化,從實(shí)驗(yàn)結(jié)果來(lái)看,在寫數(shù)據(jù)時(shí),如果發(fā)送的數(shù)據(jù)的第0位為1的話,在時(shí)鐘線第8?jìng)(gè)總線周期的高電位時(shí),數(shù)據(jù)線的電位也為高電位;而接下來(lái)時(shí)鐘線電位一旦變低,則數(shù)據(jù)線的電位也同時(shí)變?yōu)榈碗娖,也就是說(shuō),這時(shí)從器件將對(duì)其自身的數(shù)據(jù)端口輸出低電位,這個(gè)就是從器件在寫入數(shù)據(jù)時(shí)的應(yīng)答信號(hào),這個(gè)信號(hào)是在第8?jìng)(gè)總線時(shí)鐘周期的低電位時(shí)發(fā)出的,而非有些教材說(shuō)的在第9個(gè)時(shí)鐘周期時(shí)發(fā)出。

三、從發(fā)送方與接收方的角度來(lái)說(shuō)明
對(duì)于i2c總線來(lái)說(shuō),在數(shù)據(jù)傳輸時(shí),無(wú)論是主器件還是從器件,無(wú)論是讀還是寫,接收方在收到一字節(jié)數(shù)據(jù)后,規(guī)定是在時(shí)鐘線處于低電位時(shí),立即在其數(shù)據(jù)端輸出低電平,而發(fā)送方則立即在其數(shù)據(jù)端口輸出高電平,然后發(fā)送方對(duì)數(shù)據(jù)線的電位進(jìn)行檢測(cè),所以,我們?cè)诰帉懗绦驎r(shí),對(duì)于讀和寫的應(yīng)答,程序是不一樣的,寫入時(shí),程序是將數(shù)據(jù)線拉高;讀取時(shí),是將數(shù)據(jù)線拉低。如果你在讀取時(shí)仍編寫成拉高數(shù)據(jù)線的電位,那就沒(méi)法連續(xù)讀取數(shù)據(jù)了。
以上操作之后,時(shí)鐘線發(fā)出第9個(gè)周期來(lái)完成雙方的互動(dòng)式應(yīng)答并復(fù)位主從器件的數(shù)據(jù)端口輸出值,即接收方釋放對(duì)數(shù)據(jù)線的控制權(quán),將這個(gè)控制權(quán)交還給發(fā)送方,以便發(fā)送方向數(shù)據(jù)線輸出數(shù)據(jù)。根據(jù)總線正常工作期間,時(shí)鐘線處于高電位時(shí)不得改變數(shù)據(jù)線狀態(tài)的規(guī)則,接收方只能在第9個(gè)周期的低電位期間向其自身的數(shù)據(jù)端口輸出高電位。

四、從接收方應(yīng)答的必須動(dòng)作的角度來(lái)說(shuō)明
我們?cè)贀Q一個(gè)角度來(lái)作說(shuō)明,數(shù)據(jù)接收器件的應(yīng)答有兩個(gè)動(dòng)作,一個(gè)是發(fā)出表示自己收到了一字節(jié)的信號(hào),就是向其數(shù)據(jù)端口輸出低電平,第二個(gè)動(dòng)作是釋放數(shù)據(jù)端口。這兩個(gè)動(dòng)作的發(fā)生時(shí)間均需由主器件的時(shí)鐘線發(fā)出的信號(hào)來(lái)控制,其發(fā)生的時(shí)序只能分配在第8、第9這最后兩個(gè)時(shí)鐘周期中執(zhí)行,具體是第一個(gè)動(dòng)作在第8?jìng)(gè)時(shí)鐘周期時(shí)執(zhí)行,第二個(gè)動(dòng)作是在第9個(gè)時(shí)鐘周期時(shí)執(zhí)行。按照總線正常工作期間,時(shí)鐘線處于高電位時(shí)不得改變數(shù)據(jù)線的狀態(tài)的原則,這兩個(gè)動(dòng)作只能在時(shí)鐘線處于低電位時(shí)執(zhí)行。
所以,讀數(shù)據(jù)操作時(shí),從器件在發(fā)送完成一字節(jié)數(shù)據(jù)后,在第8?jìng)(gè)總線時(shí)鐘周期中時(shí)鐘線處于低電位時(shí),會(huì)立即釋放數(shù)據(jù)線的控制權(quán),然后等待主器件的繼續(xù)讀取或停止指令。

五、讀指令發(fā)出后的應(yīng)答機(jī)制
按照讀取數(shù)據(jù)時(shí),先在時(shí)鐘線低電位時(shí)期放數(shù)據(jù),再拉高時(shí)鐘線進(jìn)行讀取的規(guī)定,則我們可以知道,在主器件讀取第一個(gè)字節(jié)的操作中,一旦從器件收到一字節(jié)的讀取指令,從器件就會(huì)與主器件互動(dòng)進(jìn)行應(yīng)答,這個(gè)應(yīng)答自然是按寫狀態(tài)進(jìn)行操作,然后在第9個(gè)總線時(shí)鐘周期的低電平時(shí)期,從器件就會(huì)把所要讀取的這一字節(jié)數(shù)據(jù)的第7位數(shù)據(jù)放在數(shù)據(jù)線上,然后主器件就會(huì)拉高時(shí)鐘線電位后進(jìn)行讀取,然后拉低時(shí)鐘線以讓從器件放上第6位數(shù)據(jù),依此順推。
讀數(shù)據(jù)時(shí),第一個(gè)應(yīng)答信號(hào)中各動(dòng)作后的電位變化如下:
i2cstart();//再次啟動(dòng)總線,開(kāi)始讀
I2cSendByte(0xa1); //向從器件寫入讀指令,讀取某一存儲(chǔ)單元中為10010110的數(shù)據(jù)。此動(dòng)作后,數(shù)據(jù)線為低
   I2CSDA=1;delayms(1);//此動(dòng)作后,數(shù)據(jù)線仍為低
I2CSCL=1;delayms(1);//此動(dòng)作后,數(shù)據(jù)線仍為低
I2CSCL=0;delayms(1); //此動(dòng)作后,數(shù)據(jù)線變?yōu)楦,說(shuō)明應(yīng)答一完成,從器件即將第7位數(shù)據(jù)(為1)放在了數(shù)據(jù)線上
說(shuō)明一下,以上試驗(yàn),是本人在數(shù)據(jù)端口和時(shí)鐘端口分別接了一個(gè)燈(串了限流電阻)進(jìn)行觀察所得。

六、關(guān)于非應(yīng)答信號(hào)的質(zhì)疑
前面說(shuō)過(guò)了,數(shù)據(jù)發(fā)送方在發(fā)送完成一字節(jié)后,會(huì)立即釋放對(duì)數(shù)據(jù)線的控制權(quán),所以,有些教材中“當(dāng)主器件接收數(shù)據(jù)時(shí),在最后一個(gè)數(shù)據(jù)字節(jié),必須發(fā)送一個(gè)非應(yīng)答位,使受控器件釋放數(shù)據(jù)線,以便主器件產(chǎn)生一個(gè)停止信號(hào)來(lái)終止總線數(shù)據(jù)傳輸”的說(shuō)法是有問(wèn)題的,因?yàn)樽詈笠粋(gè)字節(jié)發(fā)出去之后,受控器件已經(jīng)主動(dòng)釋放了數(shù)據(jù)線。當(dāng)然了,你也可以這樣做,并不影響程序的運(yùn)行,但我們不能這樣理解。
我們知道,每一次的數(shù)據(jù)傳輸之后,從器件的存儲(chǔ)器單元地址指針值都會(huì)加1,這個(gè)從器件存儲(chǔ)單元地址值加1的時(shí)序時(shí)間,是在從器件發(fā)送或接收到一個(gè)完整的字節(jié)后就立即進(jìn)行的,與有無(wú)應(yīng)答操作無(wú)關(guān),所以,讀取最后一個(gè)數(shù)據(jù)后,我們根本無(wú)需進(jìn)行所謂的非應(yīng)答操作,而是可以直接發(fā)出停止信號(hào)退出總線。關(guān)于這一點(diǎn),你可以進(jìn)行試驗(yàn),在直接退出后,緊跟著輸入一個(gè)讀指令進(jìn)行讀一個(gè)從器件單元中的數(shù)據(jù)的操作,一是看看這個(gè)退出是否是正常退出,二是看看讀出的結(jié)果是上一次操作中的最后一個(gè)單元的數(shù)據(jù)還是下一個(gè)單元的數(shù)據(jù)。

七、中斷應(yīng)答
對(duì)于從器件這個(gè)情況比較少見(jiàn),但也不是不可能發(fā)生,而主器件則不奇怪。
主器件對(duì)于自身中斷的處理比較容易,沒(méi)什么特殊的。
從器件在收到一字節(jié)數(shù)據(jù)后,如果暫時(shí)不想繼續(xù)接收數(shù)據(jù)比如其要先處理中斷,則可以將其時(shí)鐘線置低電位以獲取對(duì)時(shí)鐘的控制權(quán)。因此,嚴(yán)格來(lái)說(shuō),主器件每向從器件發(fā)送完成一字節(jié)后,主器件都應(yīng)該檢測(cè)時(shí)鐘線及數(shù)據(jù)線的電位,以判斷從器件的狀態(tài)并確定其下一步的動(dòng)作。

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏4 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:266429 發(fā)表于 2021-10-26 21:15 | 只看該作者
讀應(yīng)答有以下四個(gè)動(dòng)作:
i2csda=0;delayus(4);
   i2cscl=1;delayus(4);
   i2cscl=0;delayus(4);
   i2csda=1;delayus(4);
最后一個(gè)動(dòng)作是主器件釋放對(duì)數(shù)據(jù)線的控制權(quán),千萬(wàn)別忘了。
回復(fù)

使用道具 舉報(bào)

板凳
ID:262 發(fā)表于 2021-10-27 17:26 | 只看該作者
好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

地板
ID:266429 發(fā)表于 2021-10-27 19:45 | 只看該作者
本篇有些內(nèi)容,顯然與教材是相沖突的,反正,要么教材錯(cuò)了,要么我錯(cuò)了,到底誰(shuí)對(duì)誰(shuí)錯(cuò)呢?希望大家吱個(gè)聲。
回復(fù)

使用道具 舉報(bào)

5#
ID:123289 發(fā)表于 2021-10-28 10:40 | 只看該作者
總結(jié)的很優(yōu)秀,值得對(duì)I2C通訊認(rèn)識(shí)模糊的人認(rèn)真讀一下。
給個(gè)
回復(fù)

使用道具 舉報(bào)

6#
ID:401564 發(fā)表于 2021-10-28 11:26 | 只看該作者
二、向從器件寫入時(shí)的應(yīng)答機(jī)制
這個(gè)就是在第9個(gè)時(shí)鐘周期的時(shí)候,輸出應(yīng)答位的,說(shuō)破天了也是第9個(gè)
24Cxx是上升沿寫入的,在寫入完8個(gè)位的數(shù)據(jù)之后,SCL是低電平
然后把CSL拉高,這就是一個(gè)上升沿,就是第9個(gè)時(shí)鐘周期,如果再把時(shí)鐘拉低,那就是時(shí)鐘周期已經(jīng)完成,從機(jī)就不再響應(yīng)了

至于釋放數(shù)據(jù)線主要權(quán)什么,SDA=1;之類的,這僅僅只是8051端口的操作而已
有的別的單片機(jī)不是這樣操作,不存在把數(shù)據(jù)線拉高的說(shuō)話
先學(xué)會(huì)看時(shí)序圖,配合時(shí)序看別人的代碼,這就行了,IIC很簡(jiǎn)單
回復(fù)

使用道具 舉報(bào)

7#
ID:123289 發(fā)表于 2021-10-28 15:00 | 只看該作者
【七、中斷應(yīng)答】這一點(diǎn)能考慮到的人不多。
在I2C通訊過(guò)程序中,無(wú)論是哪一方,發(fā)生了中斷,中斷服務(wù)返回后(可能執(zhí)行了一段時(shí)間),如何確保雙方的通訊接續(xù)上去而不受影響。是需要事先做預(yù)案的。
回復(fù)

使用道具 舉報(bào)

8#
ID:266429 發(fā)表于 2021-10-29 11:49 | 只看該作者
Y_G_G 發(fā)表于 2021-10-28 11:26
二、向從器件寫入時(shí)的應(yīng)答機(jī)制
這個(gè)就是在第9個(gè)時(shí)鐘周期的時(shí)候,輸出應(yīng)答位的,說(shuō)破天了也是第9個(gè)
24Cxx是 ...

第9個(gè)周期是發(fā)送器件的回應(yīng)周期,目的是通知接收器件釋放對(duì)數(shù)據(jù)線的控制權(quán);第8?jìng)(gè)周期才是接收器件的回應(yīng)周期,告訴對(duì)方我收到一個(gè)字節(jié)了。
我都是站在這個(gè)總線協(xié)議設(shè)計(jì)者的角度來(lái)思考驗(yàn)證的。很疑惑教材的說(shuō)法,反正我在讀應(yīng)答編程時(shí),如果與寫應(yīng)答的程序相同的話,在AT24C上是沒(méi)法連續(xù)讀數(shù)據(jù)的。
回復(fù)

使用道具 舉報(bào)

9#
ID:401564 發(fā)表于 2021-10-29 14:01 | 只看該作者
慢慢思考 發(fā)表于 2021-10-29 11:49
第9個(gè)周期是發(fā)送器件的回應(yīng)周期,目的是通知接收器件釋放對(duì)數(shù)據(jù)線的控制權(quán);第8?jìng)(gè)周期才是接收器件的回 ...

你數(shù)一下是不是第9個(gè)

寫入的時(shí)候一樣的是第九個(gè)時(shí)鐘教材是有人審核的,不可能幾乎所有的教材都是錯(cuò)的
隨便找個(gè)時(shí)序圖來(lái)看,它也是第9個(gè)
是最后一個(gè)字節(jié)發(fā)送完之后,時(shí)鐘是低電平,然后,時(shí)鐘線拉高,之后是等待SDA低電平
24Cxx是上升沿寫入,所以,這已經(jīng)是第9個(gè)時(shí)鐘了,你隨便找一個(gè)IIC的寫函數(shù)看一下是不是這樣的
  • for(i = 0;i < 8;i++)
  • //        {
  • //                if(dat & 0x80)        IIC_SDA = 1;        //判斷發(fā)送位,先發(fā)送高位
  • //                else        IIC_SDA = 0;
  • //                IIC_Delay();
  • //                IIC_SCL = 1;        //為SCL下降做準(zhǔn)備
  • //                IIC_Delay();
  • //                IIC_SCL = 0;        //時(shí)鐘是低電平
  • //                dat<<=1;
  • //        }
  •         IIC_SDA = 1;        //釋放數(shù)據(jù)線
  •         IIC_Delay();
  •         IIC_SCL = 1;        //這是不是一個(gè)時(shí)鐘的上升沿?,這就是第8個(gè)時(shí)鐘完成之后的第9個(gè)時(shí)鐘!!!
  •         IIC_Delay();
  •         iic_ack |= IIC_SDA;        //讀入應(yīng)答位
  •         IIC_SCL = 0;
  •         return iic_ack;        //返回應(yīng)答信號(hào)


回復(fù)

使用道具 舉報(bào)

10#
ID:624769 發(fā)表于 2021-10-29 17:17 | 只看該作者
Y_G_G 發(fā)表于 2021-10-29 14:01
你數(shù)一下是不是第9個(gè)

寫入的時(shí)候一樣的是第九個(gè)時(shí)鐘教材是有人審核的,不可能幾乎所有的教材都是錯(cuò)的

16 和17 應(yīng)該交換一下吧?

雖然有個(gè) Delay 在前面, 即便SCL高電平也已經(jīng)能讀到ACK了,但是從規(guī)范上來(lái)講,應(yīng)該先拉低時(shí)鐘,然后再讀ACK才可以吧?
回復(fù)

使用道具 舉報(bào)

11#
ID:401564 發(fā)表于 2021-10-29 20:41 | 只看該作者
188610329 發(fā)表于 2021-10-29 17:17
16 和17 應(yīng)該交換一下吧?

雖然有個(gè) Delay 在前面, 即便SCL高電平也已經(jīng)能讀到ACK了,但是從規(guī)范上來(lái) ...

低電平等于時(shí)鐘完成了,讀取不到電平狀態(tài),協(xié)議上也是高電平的時(shí)候讀取的


回復(fù)

使用道具 舉報(bào)

12#
ID:266429 發(fā)表于 2021-10-31 22:30 | 只看該作者
Y_G_G 發(fā)表于 2021-10-29 14:01
你數(shù)一下是不是第9個(gè)

寫入的時(shí)候一樣的是第九個(gè)時(shí)鐘教材是有人審核的,不可能幾乎所有的教材都是錯(cuò)的

我先問(wèn)一句:按教材的做法,能連續(xù)讀取數(shù)據(jù)么?這個(gè)實(shí)驗(yàn)本來(lái)極其簡(jiǎn)單,分分鐘的事。
回復(fù)

使用道具 舉報(bào)

13#
ID:266429 發(fā)表于 2021-11-1 10:17 | 只看該作者
Y_G_G 發(fā)表于 2021-10-29 14:01
你數(shù)一下是不是第9個(gè)

寫入的時(shí)候一樣的是第九個(gè)時(shí)鐘教材是有人審核的,不可能幾乎所有的教材都是錯(cuò)的

假設(shè)教材上寫的方案行得通,如果讓你來(lái)設(shè)計(jì)這個(gè)總線協(xié)議,你覺(jué)得哪種方案更合理?
總線協(xié)議,說(shuō)起來(lái)總歸是純?nèi)藶樵O(shè)計(jì)的東西,對(duì)于人為設(shè)計(jì)的東西,我們應(yīng)該都可以質(zhì)疑并找出更合理的方案,然后你自己就可以申請(qǐng)專利了,當(dāng)然,I2C總線這么簡(jiǎn)單的東西,就不要想了,當(dāng)初弄出這東西的人,都是一群高智商的人。
I2C總線的這個(gè)問(wèn)題,本來(lái)只是個(gè)非常簡(jiǎn)單的問(wèn)題,而且是個(gè)非常有意義的問(wèn)題,沒(méi)想到參與討論的人這么少,只能嘆口氣了。
回復(fù)

使用道具 舉報(bào)

14#
ID:401564 發(fā)表于 2021-11-1 10:49 | 只看該作者
慢慢思考 發(fā)表于 2021-11-1 10:17
假設(shè)教材上寫的方案行得通,如果讓你來(lái)設(shè)計(jì)這個(gè)總線協(xié)議,你覺(jué)得哪種方案更合理?
總線協(xié)議,說(shuō)起來(lái)總歸 ...

你看了我的代碼圖片沒(méi)有,你怎么數(shù),它都是第9個(gè)時(shí)鐘
就算是你自己寫的,只要是能連續(xù)讀取的,它也是第9個(gè)時(shí)鐘,你把你的代碼文件上傳,C也行,匯編也行
我?guī)湍愀淖⑨?我讓你找出第9個(gè)出來(lái)
協(xié)議這種東西,你怎么設(shè)計(jì)才是合理的?
你搞低作為應(yīng)答,就有人就會(huì)問(wèn)"你為什么不把高電平作為應(yīng)答呢?"
那我高電平作為應(yīng)答,那還是有人會(huì)問(wèn)"你為什么不把低電平作為應(yīng)答呢?
回復(fù)

使用道具 舉報(bào)

15#
ID:266429 發(fā)表于 2021-11-1 16:36 | 只看該作者
Y_G_G 發(fā)表于 2021-11-1 10:49
你看了我的代碼圖片沒(méi)有,你怎么數(shù),它都是第9個(gè)時(shí)鐘
就算是你自己寫的,只要是能連續(xù)讀取的,它也是第9個(gè)時(shí) ...

當(dāng)然看過(guò)了,類似的代碼另外也看了不少。我手上提到I2C的紙質(zhì)書(shū)就有四本,其中兩本的有關(guān)應(yīng)答的時(shí)序圖中,在第8?jìng)(gè)時(shí)鐘周期時(shí)鐘線的低電位時(shí),已有明確的應(yīng)答低電位信號(hào);一本則是第8周期時(shí)鐘線下降沿末端,數(shù)據(jù)線電位開(kāi)始下降,在第9周期前達(dá)到低電位;最后一本則是含混不清,似乎是在第9周期時(shí)鐘線上升沿中達(dá)到低電位,真夠亂套的。
至于應(yīng)答是用高電平還是低電平,這個(gè)I2C總線規(guī)格設(shè)計(jì)是這樣考慮的:器件在其端口輸出高電平,就意味著其交出對(duì)線路的控制權(quán),所以,用低電平作應(yīng)答信號(hào)。
至于我編的程序,二樓有讀出操作的應(yīng)答程序。
下面是程序的主要部分,請(qǐng)指教:
void i2cstart()//開(kāi)始程序,其實(shí)應(yīng)分為開(kāi)機(jī)初始化部分與總線開(kāi)始部分這兩個(gè)子函數(shù)
{
   i2cscl=1;delayus(4);
   i2csda=1;delayus(4);
   i2csda=0;delayus(4);
   i2cscl=0;delayus(4);
}
//////
void i2cend()//結(jié)束
{
    i2csda=0;delayus(4);
        i2cscl=1;delayus(4);
        i2csda=1;delayus(4);
}
//////
void i2cwack()//寫入應(yīng)答
{
           i2csda=1; delayus(4);//執(zhí)行后數(shù)據(jù)線狀態(tài)為低,說(shuō)明從器件的應(yīng)答信號(hào)已發(fā)出
        i2cscl=1;delayus(4);//數(shù)據(jù)線狀態(tài)為低
           i2cscl=0;delayus(4);//數(shù)據(jù)線狀態(tài)為高
}
//////
void i2crack()//讀出應(yīng)答
{
   i2csda=0;delayus(4);
   i2cscl=1;delayus(4);
   i2cscl=0;delayus(4);
   i2csda=1;delayus(4);//主器件釋放對(duì)數(shù)據(jù)線的控制權(quán)
}
///////
void i2cwritebyte(unsigned char wdat)//寫入一字節(jié),加入應(yīng)答
{          
    unsigned char i;
        P2=wdat;
        for(i=0;i<8;i++)
        {
           i2csda=0x01&wdat>>7;
           wdat=wdat<<1;
          
           i2cscl=1;delayus(4);
           i2cscl=0;delayus(4);
        }//完成后數(shù)據(jù)線狀態(tài)為低,與被輸入該字節(jié)數(shù)據(jù)0位的值無(wú)關(guān)
         i2cwack();
}
///////
unsigned char i2creadbyte()//讀出一字節(jié),加入應(yīng)答
{
      
          unsigned char rdat=0,i;
          for(i=0;i<8;i++)
          {
          i2cscl=1;delayus(4);
          rdat=rdat<<1|i2csda;
          i2cscl=0;delayus(4);
          }                   //讀出來(lái)的這一字節(jié)數(shù)據(jù)最后一位無(wú)論是0還是1,此時(shí)數(shù)據(jù)線狀態(tài)均為1
           i2crack(); //應(yīng)答完成后數(shù)據(jù)線狀態(tài)與下一待讀字節(jié)的最高位一致
           return(rdat);
}

//////
void main()//連續(xù)讀兩個(gè)字節(jié)并送至P2口
{       
         i2cstart();
         i2cwritebyte(0xa0);
         i2cwritebyte(0x01);
         i2cend();

    i2cstart();
        i2cwritebyte(0xa1);
        P2=i2creadbyte();
        delayms(2000);
        P2=i2creadbyte();
        i2cend();

        while(1);
}
注:讀出來(lái)的數(shù)據(jù)直接送到P2口,其上接有8只燈;數(shù)據(jù)線與時(shí)鐘線上各接有一只燈用以觀察實(shí)驗(yàn)過(guò)程中端口電位。
寫入應(yīng)答有三個(gè)動(dòng)作,其三個(gè)動(dòng)作分別執(zhí)行后各自對(duì)應(yīng)的數(shù)據(jù)線的狀態(tài)實(shí)測(cè)值已在程序中標(biāo)注,由此可知寫入時(shí)從器件應(yīng)答信號(hào)具體發(fā)出的時(shí)序時(shí)間。
讀數(shù)據(jù)時(shí),最后一字節(jié)數(shù)據(jù)沒(méi)有用非應(yīng)答,用的依然是應(yīng)答,然后結(jié)束。
回復(fù)

使用道具 舉報(bào)

16#
ID:975504 發(fā)表于 2021-11-1 16:49 | 只看該作者
51黑有你更精彩,感謝!
回復(fù)

使用道具 舉報(bào)

17#
ID:401564 發(fā)表于 2021-11-1 18:23 | 只看該作者
慢慢思考 發(fā)表于 2021-11-1 16:36
當(dāng)然看過(guò)了,類似的代碼另外也看了不少。我手上提到I2C的紙質(zhì)書(shū)就有四本,其中兩本的有關(guān)應(yīng)答的時(shí)序圖 ...

以你的代碼為例
void i2cwritebyte(unsigned char wdat)//寫入一字節(jié),加入應(yīng)答
{         
    unsigned char i;
        P2=wdat;
        for(i=0;i<8;i++)
        {
           i2csda=0x01&wdat>>7;
           wdat=wdat<<1;
         
           i2cscl=1;delayus(4);
           i2cscl=0;delayus(4);//最后時(shí)鐘線是低電平,第8個(gè)時(shí)鐘已經(jīng)結(jié)束
        }
         i2cwack();//應(yīng)答中時(shí)鐘線是高電平,這就是第9 個(gè)時(shí)鐘
}
但是,你的應(yīng)答并不對(duì)
應(yīng)答是要等待SDA出現(xiàn)低電平,而不是簡(jiǎn)單延時(shí)一下
這個(gè)SDA的低電平是24C02給出的
理論上應(yīng)該是:
        SCL=1;//時(shí)鐘高電平,保持從機(jī)在第9個(gè)時(shí)鐘                          
        Delay();//延時(shí)
        SDA=1;//釋放SDA
        while(SDA) ;等待從機(jī)出現(xiàn)應(yīng)答,重點(diǎn)在這里,延時(shí)是不行的,必需得是等待,這是協(xié)議規(guī)定的


但是,在實(shí)際情況中,考慮從機(jī)有故障或者什么的,可能不會(huì)應(yīng)答,while(SDA) ;會(huì)卡死
所以,可以使用:
while((SDA==1)&(k<1000))         //超時(shí)就不再等待應(yīng)答
                {
                        k++;
                        Delay();
                }




而你的程序,本身就是錯(cuò)誤的:
void i2cwack()//寫入應(yīng)答
{
         i2csda=1; delayus(4);//執(zhí)行后數(shù)據(jù)線狀態(tài)為低,說(shuō)明從器件的應(yīng)答信號(hào)已發(fā)出
        i2cscl=1;delayus(4);//數(shù)據(jù)線狀態(tài)為低
        i2cscl=0;delayus(4);//數(shù)據(jù)線狀態(tài)為高
}
應(yīng)該是:
void i2cwack()//寫入應(yīng)答
{
        i2csda=1; delayus(4);
        i2cscl=1;delayus(4);        while(i2csda);         //這里要等待,不是延時(shí),重點(diǎn)!重點(diǎn)!重點(diǎn)!可以加入超時(shí)檢測(cè)退出,防止卡死
        i2cscl=0;delayus(4);
}
而且,IIC停止讀取之前要加一定不應(yīng)答信號(hào),這個(gè)信號(hào)要由單片機(jī)給出,告訴從機(jī),不要再發(fā)送數(shù)據(jù)了
這個(gè)信號(hào)不是絕對(duì)需要,有的器件你直接停止就可以了,但有的不行,你要給出不應(yīng)答才能正確讀取下一次的數(shù)據(jù)
像你的代碼,能正常就是運(yùn)氣好,因?yàn)橛械腎IC器件硬件電氣性能很好,它的反應(yīng)比單片機(jī)還快,它可能壓根就不需要應(yīng)答
回復(fù)

使用道具 舉報(bào)

18#
ID:266429 發(fā)表于 2021-11-1 19:41 | 只看該作者
Y_G_G 發(fā)表于 2021-11-1 18:23
以你的代碼為例
void i2cwritebyte(unsigned char wdat)//寫入一字節(jié),加入應(yīng)答
{         

void i2cwritebyte(unsigned char wdat)//寫入一字節(jié),加入應(yīng)答
{         
    unsigned char i;
        P2=wdat;
        for(i=0;i<8;i++)
        {
           i2csda=0x01&wdat>>7;
           wdat=wdat<<1;
         
           i2cscl=1;delayus(4);
           i2cscl=0;delayus(4);//最后時(shí)鐘線是低電平,第8個(gè)時(shí)鐘已經(jīng)結(jié)束,但結(jié)束時(shí)數(shù)據(jù)線已經(jīng)是低電平
        }
         i2cwack();//應(yīng)答中時(shí)鐘線是高電平,這就是第9 個(gè)時(shí)鐘。是一高一低,低時(shí)數(shù)據(jù)線恢復(fù)高電平
}


SCL=1;//時(shí)鐘高電平,保持從機(jī)在第9個(gè)時(shí)鐘                          
        Delay();//延時(shí)
        SDA=1;//釋放SDA
這里的延時(shí),并不是用來(lái)等待從器件給出應(yīng)答信號(hào),而是為了讓時(shí)鐘線的高電位穩(wěn)定一下。至于從器件的拉低數(shù)據(jù)線的應(yīng)答信號(hào),我在前面的實(shí)驗(yàn)已經(jīng)明確指出,它在這個(gè)SCL=1執(zhí)行之前,就已經(jīng)發(fā)出了。原則上,在時(shí)鐘線處于高電位時(shí),是不允許改變數(shù)據(jù)線狀態(tài)的,所以,在SCL=1執(zhí)行之后,無(wú)論是從器件還是主器件,都不能對(duì)數(shù)據(jù)端口進(jìn)行操作。
對(duì)應(yīng)的,在讀取數(shù)據(jù)的程序編寫中,主器件的應(yīng)答信號(hào)也就是拉低數(shù)據(jù)線的操作,也是在應(yīng)答期SCL=1的操作之前進(jìn)行的,也就是一個(gè)字節(jié)數(shù)據(jù)讀完、SCL=0之后就立即執(zhí)行SDA=0這個(gè)操作,只能這樣安排順序。

至于我的程序中的“錯(cuò)誤”,是因?yàn)閮H是一個(gè)試驗(yàn)應(yīng)答規(guī)格的程序,所以嘛,沒(méi)有設(shè)計(jì)專門的程序來(lái)檢測(cè),而是用人眼觀察數(shù)據(jù)線上燈的亮滅。
回復(fù)

使用道具 舉報(bào)

19#
ID:401564 發(fā)表于 2021-11-1 23:32 | 只看該作者
慢慢思考 發(fā)表于 2021-11-1 19:41
void i2cwritebyte(unsigned char wdat)//寫入一字節(jié),加入應(yīng)答
{         
    unsigned char i;

太神奇了
錯(cuò)誤就是錯(cuò)誤,
1,到底是不是我說(shuō)的第9個(gè)時(shí)鐘?還是像你說(shuō)的第8個(gè)?

2,我沒(méi)有看到你代碼中有等待應(yīng)答的指令,一個(gè)沒(méi)有等待應(yīng)答的IIC,它能叫IIC嗎?錯(cuò)誤還加引號(hào),敢情你還覺(jué)得這沒(méi)有等待應(yīng)答的IIC代碼是對(duì)的?
你要說(shuō)其它事,可能在每個(gè)人心里都有不一樣的看法,我覺(jué)得是對(duì)的,你也可以覺(jué)得是錯(cuò)誤的,我有我的標(biāo)準(zhǔn),你有你的標(biāo)準(zhǔn)

但這不一樣,這是單純的技術(shù)問(wèn)題,它是有對(duì)錯(cuò)的,有標(biāo)準(zhǔn)的
我可以肯定的告訴你:你這個(gè)IIC代碼是錯(cuò)誤的,放哪都是錯(cuò)誤的,別加引號(hào)你告訴我一下,你等待從機(jī)應(yīng)答的代碼在哪里??????等待!等待!等待!
回復(fù)

使用道具 舉報(bào)

20#
ID:266429 發(fā)表于 2021-11-2 08:41 | 只看該作者
Y_G_G 發(fā)表于 2021-11-1 23:32
太神奇了
錯(cuò)誤就是錯(cuò)誤,
1,到底是不是我說(shuō)的第9個(gè)時(shí)鐘?還是像你說(shuō)的第8個(gè)?

其實(shí)第一個(gè)問(wèn)題,是第8?jìng)(gè)還是第9個(gè),這個(gè)就是個(gè)怎么計(jì)數(shù)的問(wèn)題,各人看法不一樣,無(wú)所謂,可以確定的是,在寫入操作中,最后一位數(shù)放在數(shù)據(jù)線上,時(shí)鐘線然后拉高讀取數(shù)據(jù),然后再拉低后,從器件就將數(shù)據(jù)線拉成低電位了。
第二個(gè)問(wèn)題嘛,正常編程當(dāng)然不能這樣,我這本是根據(jù)我這個(gè)實(shí)驗(yàn)的具體情況偷了個(gè)懶省了這一步,加個(gè)引號(hào)并不是認(rèn)為這不是錯(cuò)誤。實(shí)際上這種做法也是可以通過(guò)的,它在功能單一且對(duì)運(yùn)行速度無(wú)太多要求的場(chǎng)合中,你的每一步之間的延時(shí)足夠長(zhǎng)比如10ms,基本上出錯(cuò)的可能性也不大,但這樣絕對(duì)不規(guī)范。
回復(fù)

使用道具 舉報(bào)

21#
ID:976973 發(fā)表于 2021-11-2 11:57 | 只看該作者
好資料,51黑有你更精彩!!!
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表