N76E003|MS51FB9AE之IIC
I2C 總線(xiàn)提供了一種串行通信方式,用在 MCU 與 EEPROM,LCD模塊,溫度傳感器等等之間控制。
I2C 用兩條線(xiàn) (數(shù)據(jù)線(xiàn)SDA 和時(shí)鐘線(xiàn) SCL) 在設(shè)備間傳輸數(shù)據(jù)。
I2C 總線(xiàn)用作主機(jī)與從機(jī)之間雙向數(shù)據(jù)傳輸?梢杂糜诙嘀鳈C(jī)系統(tǒng),支持無(wú)中央主機(jī)及多主機(jī)系統(tǒng),主機(jī)與主機(jī)之間的總線(xiàn)仲載傳輸,同步時(shí)鐘SCL的存在,允許設(shè)備間使用不同比特率的數(shù)據(jù)傳輸。支持四種傳輸模式:主發(fā),主收,從發(fā),從收。
I2C 總線(xiàn)僅支持 7位地址。支持廣播呼叫,支持標(biāo)準(zhǔn)速率傳輸 (100kbps) 和快速傳輸( 400k bps)
功能描述
對(duì)于雙向傳輸操作,SDA 及SCL 引腳必須配置成開(kāi)漏模式,形成邏輯線(xiàn)與功能:總線(xiàn)上當(dāng)有一個(gè)器件輸出0,總
線(xiàn)上就是0電平,所有器件全輸出1,總線(xiàn)上才是高電平,需要通過(guò)外接上拉電阻把電平拉高。N76E003, 在設(shè)置
I2CEN (I2CON.6)使能I2C功能之前,必須把SCL,SDA的輸出鎖存在邏輯1的狀態(tài)。
I2C 空閑時(shí),兩條線(xiàn)都為高。這時(shí)任一設(shè)備都可以做為主機(jī)發(fā)個(gè)起始位 START 開(kāi)始數(shù)據(jù)傳輸,在停止位 STOP
出現(xiàn)之前,總線(xiàn)被認(rèn)為處于忙狀態(tài)。主機(jī)產(chǎn)生串行時(shí)鐘脈沖以及起始位和停止位。如果總線(xiàn)上沒(méi)有START起始
信號(hào),則所有總線(xiàn)設(shè)備被認(rèn)為未被尋址從機(jī),硬件自動(dòng)匹配自己的從機(jī)地址或廣播呼叫地址,(廣播地址可由 GC
(I2ADDR.0)使能或禁止.)。若地址匹配,就產(chǎn)生中斷。
I2C總線(xiàn)上傳輸?shù)拿總(gè)字節(jié)都包含8個(gè)數(shù)據(jù)位(MSB優(yōu)先)和一個(gè)應(yīng)答位,共9位。但每次傳輸?shù)淖止?jié)個(gè)數(shù)沒(méi)有明確
界定(起始位 START 和停止位 STOP之間的字節(jié)個(gè)數(shù)) ,但每個(gè)字節(jié)都應(yīng)有一個(gè)應(yīng)答位。主機(jī)產(chǎn)生8個(gè)時(shí)鐘脈
沖,以傳輸8位數(shù)據(jù)。在第8個(gè)時(shí)鐘SCL下沿后,由SDA腳輸出數(shù)據(jù)后,SDA轉(zhuǎn)為輸入模式在第9個(gè)時(shí)鐘脈沖以讀取第9位應(yīng)答位。在第9個(gè)時(shí)鐘脈沖后,數(shù)據(jù)接收端若沒(méi)準(zhǔn)備好接收下一個(gè)字節(jié),可以拉住時(shí)鐘線(xiàn)保持低,讓傳輸掛起。接收端釋放時(shí)鐘線(xiàn)SCL以后,傳輸繼續(xù)。
開(kāi)始和停止條件
I2C 總線(xiàn)時(shí)序定義了起始START (S)和結(jié)束STOP (P)的條件。時(shí)鐘SCL為高時(shí),數(shù)據(jù)線(xiàn)SDA由高電平至低電平的跳變被認(rèn)為是起始START標(biāo)志。時(shí)鐘SCL為高時(shí),數(shù)據(jù)線(xiàn)SDA由低電平至高電平的跳變被認(rèn)為是結(jié)束STOP標(biāo)志。起始和結(jié)束都由主機(jī)產(chǎn)生,起始和結(jié)束之間被認(rèn)為是總線(xiàn)忙狀態(tài)。當(dāng)成功判定結(jié)束條件以后,主機(jī)釋放總
線(xiàn),所有設(shè)備都回到監(jiān)聽(tīng)總線(xiàn)起始位狀態(tài),之前被呼叫從機(jī)也轉(zhuǎn)為未尋址從機(jī)。
I2C總線(xiàn)進(jìn)入空閑狀態(tài)等待下一個(gè)起始START信號(hào),開(kāi)始下一次傳輸。
主機(jī)若發(fā)出停止位STOP,傳輸就停止了。然而,主機(jī)也可以不發(fā)停止位,而是再次發(fā)出起始START信號(hào)(Sr)繼續(xù)和上個(gè)地址通信,或者換個(gè)地址繼續(xù)通信。各種讀/寫(xiě)組合格式操作可能存在一個(gè)傳輸中。
7位地址數(shù)據(jù)格式
起始位 START 之后, 第一個(gè)字節(jié)必須是“7位地址SLA+第8位讀寫(xiě)方向位( W)”,用以定義目標(biāo)從機(jī)地址以及從機(jī)寫(xiě)入或讀出數(shù)據(jù)。若第8位是0,即SLA+W,表示下個(gè)字節(jié)開(kāi)始主機(jī)向從機(jī)寫(xiě)數(shù)據(jù);若是1,即SLA+R,就表
示下字節(jié)開(kāi)始,主機(jī)向從機(jī)讀數(shù)據(jù)。所以,一個(gè)典型數(shù)據(jù)包含起始位 START,SLA+W/R,一個(gè)或多個(gè)字節(jié)數(shù)據(jù),最后是停止位 STOP。當(dāng)一個(gè)從機(jī)已被尋址及讀寫(xiě)方向通過(guò)字節(jié)SLA+W/R,隨后的8位數(shù)據(jù)就跟隨之前的設(shè)定進(jìn)行傳輸。
I2C總線(xiàn)還有一種特殊尋址方式,廣播呼叫尋址。
在該模式下,發(fā)送的首字節(jié)數(shù)據(jù)為0。
廣播呼叫模式應(yīng)用于主機(jī)希望向所有從機(jī)傳輸相同數(shù)據(jù)。當(dāng)此尋址方式啟用。收到廣播要不要發(fā)應(yīng)答由軟件決定。若某個(gè)從機(jī)發(fā)了應(yīng)答,這個(gè)從機(jī)就收發(fā)后續(xù)數(shù)據(jù),和標(biāo)準(zhǔn)從機(jī)接收方式相同。注意:地址0x00默認(rèn)用于廣播呼叫方式,不能用于普通從機(jī)地址。因此理論上,總共7位地址I2C 總線(xiàn),共可以連接127個(gè)設(shè)備,地址由1至127。
在數(shù)據(jù)傳輸過(guò)程中,在時(shí)鐘高電平時(shí),SDA需要保持?jǐn)?shù)據(jù)內(nèi)容不能更改。只有在SCL為低時(shí),SDA內(nèi)容可以改變。
應(yīng)答ACK
每字節(jié)傳輸SCL第9個(gè)脈沖用于傳輸應(yīng)答位 (ACK)。通過(guò)將SDA拉低,來(lái)允許接收端(無(wú)論主機(jī)或是從機(jī))回應(yīng)發(fā)送端(無(wú)論主機(jī)或從機(jī))。應(yīng)答位時(shí)鐘由主機(jī)產(chǎn)生。發(fā)送端設(shè)備在應(yīng)答位時(shí)鐘高電平周期內(nèi),需放棄對(duì)SDA的控制。ACK 為一個(gè)低電平信號(hào)。在應(yīng)答位時(shí)鐘周期的高電平時(shí),SDA保持低電平用以表示接收端已成功接收到發(fā)送端的數(shù)據(jù)。通常被尋址的從機(jī)在整個(gè)傳輸過(guò)程中每字節(jié)都需要回復(fù)應(yīng)答位。當(dāng)該從機(jī)接收無(wú)法應(yīng)答(NACK)從機(jī)地址時(shí),從機(jī)將SDA線(xiàn)保持高以便主機(jī)產(chǎn)生停止(STOP)或發(fā)送重復(fù)開(kāi)始(START)信號(hào)。
若從機(jī)接收沒(méi)有應(yīng)答從機(jī)地址后,將自身切換到未尋址從機(jī)模式,從而無(wú)法接收更多數(shù)據(jù)字節(jié)。這時(shí)從機(jī)將SDA線(xiàn)拉高,此時(shí)主機(jī)應(yīng)發(fā)送停止STOP信號(hào)或重復(fù)起始(repeated START)信號(hào)。
若是主機(jī)接收,主機(jī)控制著收發(fā)字節(jié)個(gè)數(shù),主機(jī)在最后一個(gè)字節(jié)收發(fā)結(jié)束后不發(fā)應(yīng)答位信號(hào),從機(jī)端將切換為未尋址從機(jī)模式,并釋放SDA線(xiàn),以便主機(jī)直接發(fā)停止位STOP 或重開(kāi)始位START。
仲裁
主機(jī)僅可在總線(xiàn)空閑時(shí)發(fā)起傳輸。可能有多個(gè)器件同時(shí)發(fā)開(kāi)始位START試圖發(fā)起數(shù)據(jù)傳輸,這時(shí)就會(huì)出現(xiàn)總線(xiàn)
仲裁。在該狀態(tài)下,當(dāng)SCL為高時(shí),SDA上呈現(xiàn)仲裁信號(hào)。在仲裁過(guò)程中,第一個(gè)發(fā)起主機(jī)對(duì)SDA線(xiàn)置1(高電
平)而另一個(gè)主機(jī)發(fā)送0(低電平),發(fā)送后主機(jī)會(huì)對(duì)SDA線(xiàn)上信號(hào)與自己發(fā)出的信號(hào)進(jìn)行比較,由于“線(xiàn)與”
的原因,時(shí)鐘SCL為高時(shí),發(fā)送0的主機(jī)會(huì)成功,而發(fā)送1的主機(jī)會(huì)失敗。發(fā)送失敗的主機(jī)立刻切換自身到未被尋
址的從機(jī)狀態(tài),以確保自身能被仲裁勝利的主機(jī)尋址到。同時(shí)也釋放數(shù)據(jù)線(xiàn),并回到地址偵測(cè)狀態(tài),仲載失敗的
主機(jī),仍會(huì)發(fā)送時(shí)鐘,直到當(dāng)前字節(jié)結(jié)束。
仲裁機(jī)制讓每個(gè)主機(jī)發(fā)送數(shù)據(jù)時(shí),都會(huì)同時(shí)比較總線(xiàn)上的數(shù)據(jù)是否與自己發(fā)送的一致。注:如果其它主機(jī)發(fā)送
0,發(fā)送1的主機(jī)會(huì)在仲裁中失敗,仲裁會(huì)持續(xù)到總線(xiàn)上只有一個(gè)主機(jī)。如兩個(gè)主機(jī)同時(shí)向一個(gè)從機(jī)發(fā)數(shù)據(jù)時(shí),地
址相同,仲裁會(huì)在第二個(gè)字節(jié)持續(xù)。
I2C 總線(xiàn)的這種仲裁機(jī)制,讓總線(xiàn)上的設(shè)備可以有多個(gè)主機(jī),而且沒(méi)有優(yōu)先等級(jí)。從機(jī)不介入仲裁。
I2C控制寄存器
I2C共有五個(gè)控制寄存器: I2CON, I2STAT, I2DAT, I2ADDR, 和 I2CLK. 這些寄存器用以提供協(xié)議控制,狀態(tài)顯示,數(shù)據(jù)傳輸、接收以及時(shí)鐘速率控制。為靈活應(yīng)用,配置I2CPX (I2CON.0)可以交換SDA,SCL引腳功能。
工作模式
I2C 協(xié)議定義了四種模式:主機(jī)發(fā)送,主機(jī)接收,從機(jī)發(fā)送,從機(jī)接收。還有一種特殊模式廣播呼叫模式,其操作方式與從機(jī)接收模式類(lèi)似。
主機(jī)發(fā)送模式
主機(jī)發(fā)送多個(gè)字節(jié)到從機(jī),主機(jī)產(chǎn)生時(shí)鐘,故需要在I2CLK內(nèi)填入設(shè)定值。主機(jī)發(fā)送模式需要將STA (I2CON.5) 置1。此時(shí),一旦檢測(cè)到總線(xiàn)空閑,主機(jī)就會(huì)發(fā)出一個(gè)起始位START,若成功, SI(I2CON.3) 將被置1,狀態(tài)碼I2STAT置為08H。接下來(lái)應(yīng)把從機(jī)地址和寫(xiě)位(SLA+W)寫(xiě)入 I2DAT ,然后清0位SI,總線(xiàn)上發(fā)出SLA+W。
主機(jī)發(fā)出SLA+W 收到從機(jī)應(yīng)答位ACK后, SI被置1,狀態(tài)碼I2STAT = 18H。接下來(lái)將按照用戶(hù)定義格式發(fā)送數(shù)據(jù)。所有的數(shù)據(jù)發(fā)送完以后,位 STO (I2CON.4)置1,并清除SI位以發(fā)出停止信號(hào)STOP,或者也可以發(fā)送重復(fù)起始信號(hào)repeat START,而不發(fā)送STOP,直接開(kāi)始新一輪數(shù)據(jù)傳輸。
主機(jī)接收模式
主機(jī)接收模式,由從機(jī)傳輸數(shù)據(jù)。初始化設(shè)置與主機(jī)發(fā)送模式相同,主機(jī)發(fā)送起始位以后, I2DAT 應(yīng)寫(xiě)入從機(jī)地址和“讀位” (SLA+R)。收到從機(jī)應(yīng)答位ACK后 SI 被置1且狀態(tài)碼 I2STAT= 40H。 SI 清0后開(kāi)始接收從機(jī)數(shù)據(jù),若 AA 位 (I2CON.2) =1,主機(jī)收到數(shù)據(jù)后回應(yīng)答位;若 AA =0主機(jī)收到數(shù)據(jù)后不回應(yīng)答NACK。然后主機(jī)可以發(fā)停止位或重起始開(kāi)始新一輪傳輸。
從機(jī)接收模式
在從機(jī)接收模式下,從機(jī)接收主機(jī)發(fā)來(lái)的數(shù)據(jù)。在傳輸開(kāi)始前,I2ADDR 應(yīng)寫(xiě)入從機(jī)地址,I2CLK內(nèi)容無(wú)效,AA置1用以響應(yīng)主機(jī)的尋址。上述初始化后,從機(jī)進(jìn)入空閑模式,等待“寫(xiě)”信號(hào)(SLA+W)。若主機(jī)仲裁失敗,也會(huì)直接進(jìn)入從機(jī)接收模式。
當(dāng)從機(jī)被“寫(xiě)”信號(hào)SLA+W尋址到后,需要清0 SI位,以便從主機(jī)接收數(shù)據(jù)。如果在傳輸過(guò)程中AA=0,從機(jī)將在下一字節(jié)返回?zé)o應(yīng)答位NACK,從機(jī)也將轉(zhuǎn)為未尋址從機(jī),與主機(jī)聯(lián)系終止,不再接收數(shù)據(jù),且I2DAT保持之前接收到的數(shù)據(jù)。
從機(jī)發(fā)送模式
從機(jī)發(fā)送模式,數(shù)據(jù)由從機(jī)發(fā)送給主機(jī)。當(dāng)初始化I2ADDR及I2CON值后,器件等待直到自身地址被“讀”信號(hào)(SLA+R)尋址。若主機(jī)仲裁失敗,也可進(jìn)入從機(jī)發(fā)送模式。
當(dāng)從機(jī)被“讀”信號(hào)SLA+R尋址,需要將SI信號(hào)清0用以向主機(jī)發(fā)送數(shù)據(jù)。通常主機(jī)接收每字節(jié)數(shù)據(jù)后會(huì)返回應(yīng)答位,如果沒(méi)有接收到應(yīng)答位,接下去的傳輸中,從機(jī)將發(fā)送全1數(shù)據(jù)。并變?yōu)槲磳ぶ窂臋C(jī)。如果傳輸過(guò)程中AA清0 ,從機(jī)將發(fā)送最后一個(gè)字節(jié)數(shù)據(jù),并在接下去的傳輸中發(fā)送全1數(shù)據(jù),并將自身變?yōu)槲磳ぶ窂臋C(jī)。
各狀態(tài)碼表述
I2STAT狀態(tài)寄存器中,有兩種狀態(tài)碼不歸屬25種之前所述狀態(tài):F8H 及 00H。
F8H 用以標(biāo)示在之前的傳輸中沒(méi)有有效信息,意味著,SI標(biāo)志位為0且無(wú)I2C 中斷產(chǎn)生。
00H 標(biāo)示在傳輸過(guò)程中,有總線(xiàn)錯(cuò)誤發(fā)生?偩(xiàn)錯(cuò)誤是指START 或 STOP在傳輸?shù)倪^(guò)程中出現(xiàn)在錯(cuò)誤的位置,例如在地址或數(shù)據(jù)的第二位或應(yīng)答位。當(dāng)總線(xiàn)發(fā)生錯(cuò)誤,SI標(biāo)志馬上會(huì)被置1,工作中的節(jié)點(diǎn)設(shè)備馬上切換到未被尋址從機(jī)模式,釋放SDA 和SCL,并將I2STAT寄存器清0。要恢復(fù)總線(xiàn)狀態(tài),需要置位STO位并清除SI位,然后STO會(huì)由硬件清0,而不需要真正的STOP信號(hào)波形,釋放總線(xiàn)恢復(fù)到正?臻e狀態(tài)。
有一種特殊情況,當(dāng)SDA線(xiàn)被強(qiáng)制拉低導(dǎo)致阻塞,START信號(hào)或重復(fù)起始(repeat START)信號(hào)無(wú)法成功產(chǎn)生時(shí),從機(jī)將失去同步。解決方法是在SCL線(xiàn)上額外多送一個(gè)時(shí)鐘脈沖。通過(guò)將STA位置1,總線(xiàn)上產(chǎn)生額外的時(shí)鐘脈沖,由于SDA始終拉低,SDA線(xiàn)上不會(huì)產(chǎn)生START信號(hào)。一旦SDA線(xiàn)被釋放,正常的START信號(hào)送出,狀態(tài)寄存器上會(huì)顯示08H,串行傳輸繼續(xù)。相同的狀況,如果需要發(fā)送重復(fù)開(kāi)始 (repeated START)信號(hào)受阻,也可以采用上述方式。在上述方式完成傳輸后,狀態(tài)寄存器顯示10H,而不是顯示08H。注:軟件無(wú)法介入上述總線(xiàn)問(wèn)題傳輸。
重要說(shuō)明: 當(dāng)總線(xiàn)上看到的STATUS 為總線(xiàn)錯(cuò)誤(bus error)時(shí), I2C為持續(xù)使能狀態(tài),由于對(duì)I2C模塊來(lái)說(shuō)錯(cuò)誤并沒(méi)有清除,無(wú)法直接用清除 SI 的方式來(lái)停止總線(xiàn)錯(cuò)誤,故SI會(huì)保持在1。所以,如果程序中用SI來(lái)判定當(dāng)下I2C狀態(tài)并繼續(xù)流程時(shí),建議用下述方法來(lái)增加程序的穩(wěn)定性。
解決方法:
– 通過(guò)對(duì)總線(xiàn)送出Stop信號(hào)
– 如果送出Stop仍然無(wú)效,說(shuō)明時(shí)序已被打亂,需要對(duì)I2C總線(xiàn)傳輸進(jìn)行關(guān)閉再重新開(kāi)始傳輸。
I2C 超時(shí)溢出
N76E003帶一組14位超時(shí)計(jì)數(shù)器,用于防止I2C總線(xiàn)故障。一旦使能了超時(shí)定時(shí)器,計(jì)數(shù)器開(kāi)始計(jì)數(shù)直至溢出,
即如果開(kāi)啟中斷,I2TOF位會(huì)被硬件置1。當(dāng)使能計(jì)數(shù)器,SI置1復(fù)位計(jì)數(shù)器, SI清0后重新開(kāi)始計(jì)數(shù)。
若I2C總線(xiàn)出現(xiàn)故障,SI 位長(zhǎng)時(shí)間不能清0,14位的超時(shí)定時(shí)器就會(huì)溢出,并進(jìn)入中斷。
I2C 中斷
I2C的兩個(gè)標(biāo)志位:SI 和 I2TOF。這兩個(gè)標(biāo)志都會(huì)產(chǎn)生I2C事件中斷請(qǐng)求。如果 EI2C (EIE.0) = 1且 EA = 1,當(dāng)兩個(gè)標(biāo)志中的任意一個(gè)發(fā)生時(shí), CPU 就會(huì)去執(zhí)行中斷代碼。用戶(hù)可以讀取這兩個(gè)標(biāo)志位,來(lái)確定中斷產(chǎn)生的原因。這兩個(gè)標(biāo)志需軟件清0。
另外,當(dāng)使用I2C超時(shí)寄存器功能時(shí),必須打開(kāi)I2C中斷用來(lái)配合超時(shí)的時(shí)序,否則I2C超時(shí)計(jì)數(shù)標(biāo)志不會(huì)產(chǎn)生置位。
/*---------------------------------------------------------------------------------------------------------*//**//* Copyright(c) 2017 Nuvoton Technology Corp. All rights reserved. *//**//*---------------------------------------------------------------------------------------------------------*///***********************************************************************************************************// Nuvoton Technoledge Corp. // Website: http://www.nuvoton.com// E-Mail : MicroC-8bit@nuvoton.com// Date : Jun/21/2017//***********************************************************************************************************//***********************************************************************************************************// File Function: N76E003 I2C demo code, Slave Address of 24LC64 = 0xA0//// ____________ ______________ // | | SDA | |// | |<-------->| |// | | | |// |N76E003(M) | | 24LC64(S) |// | | SCL | |// | |--------->| |// |____________| |_____________|//// Microchip I2C EEPROM 24xx64 (64K Bit) is used as the slave device. // The page size are 32Byte. Total are 256 page.// If verification passes, Port3 will show 0x78. If there is any failure// occured during the progress, Port3 will show 0x00.//***********************************************************************************************************#include "N76E003.h"#include "SFR_Macro.h"#include "Function_define.h"#include "Common.h"#include "Delay.h"#define SYS_CLK_EN 0#define SYS_SEL 2#define SYS_DIV_EN 0 //0: Fsys=Fosc, 1: Fsys = Fosc/(2*CKDIV)#define SYS_DIV 1#define I2C_CLOCK 2#define EEPROM_SLA 0xA0#define EEPROM_WR 0#define EEPROM_RD 1#define LED P3#define PAGE_SIZE 32#define PAGE_NUMBER 4#define ERROR_CODE 0x78#define TEST_OK 0x00bit I2C_Reset_Flag;//========================================================================================================void Init_I2C(void){ P13_OpenDrain_Mode; // Modify SCL pin to Open drain mode. don't forget the pull high resister in circuit P14_OpenDrain_Mode; // Modify SDA pin to Open drain mode. don't forget the pull high resister in circuit/* Set I2C clock rate */ I2CLK = I2C_CLOCK; /* Enable I2C */ set_I2CEN; }//========================================================================================================void I2C_SI_Check(void){ if (I2STAT == 0x00) { I2C_Reset_Flag = 1; set_STO; SI = 0; if(SI) { clr_I2CEN; set_I2CEN; clr_SI; clr_I2CEN; } } }void One_Page_Read(UINT8 u8PageNumber,UINT8 u8DAT){ UINT8 u8Count; UINT16 u16Address; u16Address = (UINT16)u8PageNumber*32; /* Step1 */ set_STA; /* Send Start bit to I2C EEPROM */ clr_SI; while (!SI); if (I2STAT != 0x08) /* 0x08: A START condition has been transmitted*/ { I2C_Reset_Flag = 1; printf("\nI2C 'Send STA' error"); goto Read_Error_Stop; } /* Step2 */ I2DAT = (EEPROM_SLA | EEPROM_WR); /* Send (SLA+W) to EEPROM */ clr_STA; /* Clear STA and Keep SI value in I2CON */ clr_SI; while (!SI); if (I2STAT != 0x18) /* 0x18: SLA+W has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send SLA+W' error"); goto Read_Error_Stop; } /* Step3 */ I2DAT = HIBYTE(u16Address); /* Send I2C EEPROM's High Byte Address */ clr_SI; while (!SI); if (I2STAT != 0x28) /* 0x28: Data byte in S1DAT has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send I2C High Byte Address' error"); goto Read_Error_Stop; } /* Step4 */ I2DAT = LOBYTE(u16Address); /* Send I2C EEPROM's Low Byte Address */ clr_SI; while (!SI); if (I2STAT != 0x28) /* 0x28: Data byte in S1DAT has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send I2C Low Byte Address' error"); goto Read_Error_Stop; } /* Step5 */ set_STA; /* Repeated START */ clr_SI; while (!SI); if (I2STAT != 0x10) /* 0x10: A repeated START condition has been transmitted */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send STA' error"); goto Read_Error_Stop; } /* Step6 */ clr_STA; /* Clear STA and Keep SI value in I2CON */ I2DAT = (EEPROM_SLA | EEPROM_RD); /* Send (SLA+R) to EEPROM */ clr_SI; while (!SI); if (I2STAT != 0x40) /* 0x40: SLA+R has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send SLA+R' error"); goto Read_Error_Stop; } /* Step7 *//* Verify I2C EEPROM data */for (u8Count = 0; u8Count <PAGE_SIZE; u8Count++) { set_AA; /* Set Assert Acknowledge Control Bit */ clr_SI; while (!SI); if (I2STAT != 0x50) /* 0x50:Data byte has been received; NOT ACK has been returned */ { I2C_Reset_Flag = 1; printf("\nI2C 'No Ack' error"); goto Read_Error_Stop; } if (I2DAT != u8DAT) /* Send the Data to EEPROM */ { I2C_Reset_Flag = 1; printf("\nI2C 'Read data' error"); goto Read_Error_Stop; } u8DAT = ~u8DAT; } /* Step8 */ clr_AA; /* Send a NACK to disconnect 24xx64 */ clr_SI; while (!SI); if (I2STAT != 0x58) /* 0x58:Data byte has been received; ACK has been returned */ { I2C_Reset_Flag = 1; printf("\nI2C 'Ack' error"); goto Read_Error_Stop; } /* Step9 */ clr_SI; set_STO; while (STO) /* Check STOP signal */ { I2C_SI_Check(); if (I2C_Reset_Flag) goto Read_Error_Stop; } Read_Error_Stop: if (I2C_Reset_Flag) { I2C_SI_Check(); printf("\nI2C Read error, test stop"); I2C_Reset_Flag = 0; }}//========================================================================================================void One_Page_Write(UINT8 u8PageNumber,UINT8 u8DAT){ UINT8 u8Count; UINT16 u16Address; u16Address = (UINT16)u8PageNumber*32; /* Step1 */ set_STA; /* Send Start bit to I2C EEPROM */ clr_SI; while (!SI); if (I2STAT != 0x08) /* 0x08: A START condition has been transmitted*/ { I2C_Reset_Flag = 1; printf("\nI2C 'Send STA' error"); goto Write_Error_Stop; } /* Step2 */ clr_STA; /* Clear STA and Keep SI value in I2CON */ I2DAT = EEPROM_SLA | EEPROM_WR; /* Send (SLA+W) to EEPROM */ clr_SI; while (!SI); if (I2STAT != 0x18) /* 0x18: SLA+W has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send SLA+W' error"); goto Write_Error_Stop; } /* Step3 */ I2DAT = HIBYTE(u16Address); /* Send EEPROM's High Byte Address */ clr_SI; while (!SI); if (I2STAT != 0x28) /* 0x28: Data byte in S1DAT has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send High byte address' error"); goto Write_Error_Stop; } /* Step4 */ I2DAT = LOBYTE(u16Address); /* Send EEPROM's Low Byte Address */ clr_SI; while (!SI); if (I2STAT != 0x28) /* 0x28: Data byte in S1DAT has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Send Low byte address' error"); goto Write_Error_Stop; } /* Step5 *//* Write data to I2C EEPROM */for (u8Count = 0; u8Count < PAGE_SIZE; u8Count++) { I2DAT = u8DAT; /* Send data to EEPROM */ clr_SI; while (!SI); if (I2STAT != 0x28) /* 0x28: Data byte in S1DAT has been transmitted; ACK has been received */ { I2C_Reset_Flag = 1; printf("\nI2C 'Write Data' error"); goto Write_Error_Stop; } u8DAT = ~u8DAT; } //After STOP condition, a inner EEPROM timed-write-cycle //will occure and EEPROM will not response to outside command/* 0x18: SLA+W has been transmitted; ACK has been received *//* Step6 */do { set_STO; /* Set I2C STOP Control Bit */ clr_SI; while (STO) /* Check STOP signal */ { I2C_SI_Check(); if (I2C_Reset_Flag) goto Write_Error_Stop; } set_STA; /* Check if no ACK is returned by EEPROM, it is under timed-write cycle */ clr_SI; while (!SI); if (I2STAT != 0x08) /* 0x08: A START condition has been transmitted*/ { I2C_Reset_Flag = 1; printf("\nI2C 'Wait Ready' error"); goto Write_Error_Stop; } clr_STA; /* Clear STA and Keep SI value in I2CON */ I2DAT = (EEPROM_SLA | EEPROM_WR); /* Send (SLA+W) to EEPROM */ clr_SI; while (!SI); }while(I2STAT != 0x18); /* Step7 */ set_STO; /* Set STOP Bit to I2C EEPROM */ clr_SI; while (STO) /* Check STOP signal */ { I2C_SI_Check(); if (I2C_Reset_Flag) goto Write_Error_Stop; } Write_Error_Stop: if (I2C_Reset_Flag) { I2C_SI_Check(); I2C_Reset_Flag = 0; printf("\nI2C Write error, test stop"); } }//========================================================================================================void main(void){ Set_All_GPIO_Quasi_Mode; InitialUART0_Timer3(115200); /* Initial I2C function */ Init_I2C(); //initial I2C circuit/* page0 R/W */ printf ("\n\n24LC64 Page0 Write (0x55,0xAA...)..."); One_Page_Write(0,0x55); //page0, write 0x55,0xAA,........ printf ("\n\n24LC64 Page0 Read..."); One_Page_Read (0,0x55); //page0, read 0x55,0xAA,......../* page1 R/W */ printf ("\n\n24LC64 Page1 Write (0x00,0xFF...)..."); One_Page_Write(1,0x00); //page1, write 0x00,0xFF,........ printf ("\n\n24LC64 Page1 Read..."); One_Page_Read (1,0x00); //page1, read 0x00,0xFF,......../* page2 R/W */ printf ("\n\n24LC64 Page2 Write (0xAA,0x55...)..."); One_Page_Write(2,0xAA); //page2, write 0xAA,0x55,........ printf ("\n\n24LC64 Page2 Read..."); One_Page_Read (2,0xAA); //page2, read 0xAA,0x55,......../* page3 R/W */ printf ("\n\n24LC64 Page3 Write (0xFF,0x00...)..."); One_Page_Write(3,0xFF); //page3, write 0xFF,0x00,........ printf ("\n\n24LC64 Page3 Read..."); One_Page_Read (3,0xFF); //page3, read 0xFF,0x00,......../* page255 R/W */ printf ("\n\n24LC64 Page255 Write (0x0F,0xF0...)..."); One_Page_Write(255,0x0F); //page255, write 0x0F,0xF0,........ printf ("\n\n24LC64 Page255 Read..."); One_Page_Read (255,0x0F); //page255, read 0x0F,0xF0,......../* ==== Test Pass ==== */ printf ("\n\N76E003 <--> 24LC64, I2C Demo Code test pass..."); while (1);/* =================== */}
編輯:admin 最后修改時(shí)間:2023-03-06