0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于51單片機(jī)的I2C-EEPROM實(shí)驗(yàn)

科技綠洲 ? 來源:云龍派 ? 作者:云龍派 ? 2023-09-11 10:37 ? 次閱讀

1. I2C介紹

I2C ( Inter—Integrated Circuit)總線是由PHILIPS公司開發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備。是微電子通信控制領(lǐng)域廣泛采用的一種總線標(biāo)準(zhǔn)。它是同步通信的一種特殊形式,具有接口線少,控制方式簡(jiǎn)單,器件封裝形式小,通信速率較高等優(yōu)點(diǎn)。I2C總線只有兩根雙向信號(hào)線。一根是數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL。 由于其管腳少,硬件實(shí)現(xiàn)簡(jiǎn)單,可擴(kuò)展性強(qiáng)等特點(diǎn),因此被廣泛的使用在各大集成芯片內(nèi)。下面從I2C的物理層與協(xié)議層來了解 I2C。

1.1 I2C物理層

I2C通信設(shè)備常用的連接方式如下圖所示:

圖片

它的物理層有如下特點(diǎn):

(1)它是一個(gè)支持多設(shè)備的總線。“總線”指多個(gè)設(shè)備共用的信號(hào)線。在一個(gè)I2C通訊總線中,可連接多個(gè)I2C通訊設(shè)備,支持多個(gè)通訊主機(jī)及多個(gè)通訊從機(jī)。

(2)一個(gè)I2C總線只使用兩條總線線路,一條雙向串行數(shù)據(jù)線(SDA),一條串行時(shí)鐘線(SCL)。數(shù)據(jù)線即用來表示數(shù)據(jù),時(shí)鐘線用于數(shù)據(jù)收發(fā)同步。

(3)每個(gè)連接到總線的設(shè)備都有一個(gè)獨(dú)立的地址,主機(jī)可以利用這個(gè)地址進(jìn)行不同設(shè)備之間的訪問。

(4)總線通過上拉電阻接到電源。當(dāng)I2C設(shè)備空閑時(shí),會(huì)輸出高阻態(tài),而當(dāng)所有設(shè)備都空閑,都輸出高阻態(tài)時(shí),由上拉電阻把總線拉成高電平。

(5)多個(gè)主機(jī)同時(shí)使用總線時(shí),為了防止數(shù)據(jù)沖突,會(huì)利用仲裁方式?jīng)Q定由哪個(gè)設(shè)備占用總線。

(6)具有三種傳輸模式:標(biāo)準(zhǔn)模式傳輸速率為100kbit/s,快速模式為400kbit/s,高速模式下可達(dá)3.4Mbit/s,但目前大多 I2C設(shè)備尚不支持高速模式。

(7)連接到相同總線的IC數(shù)量受到總線的最大電容400pF限制。

高阻態(tài)這是一個(gè)數(shù)字電路里常見的術(shù)語(yǔ),指的是電路的一種輸出狀態(tài),既不是高電平也不是低電平, 如果高阻態(tài)再輸入下一級(jí)電路的話,對(duì)下級(jí)電路無任何影響,和沒接一樣,如果用萬(wàn)用表測(cè)的話有可能是高電平也有可能是低電平,隨它后面接的東西定。

MCU(Micro Control Unit),中文為微控制單元,又稱單片微型計(jì)算機(jī)(Single Chip Microcomputer)或者單片機(jī) ,是指將計(jì)算機(jī)的CPURAM、ROM、定時(shí)計(jì)數(shù)器和多種I/O接口集成在一片芯片上,形成芯片級(jí)的計(jì)算機(jī)。

I2C總線常涉及的術(shù)語(yǔ)

主機(jī):啟動(dòng)數(shù)據(jù)傳送并產(chǎn)生時(shí)鐘信號(hào)的設(shè)備;
從機(jī):被主機(jī)尋址的器件;
多主機(jī):同時(shí)有多于一個(gè)主機(jī)嘗試控制總線但不破壞傳輸;
主模式:用I2CNDAT支持自動(dòng)字節(jié)計(jì)數(shù)的模式;位 I2CRM,I2CSTT,I2CSTP控制數(shù)據(jù)的接收和發(fā)送;
從模式:發(fā)送和接收操作都是由I2C模塊自動(dòng)控制的;
仲裁:是一個(gè)在有多個(gè)主機(jī)同時(shí)嘗試控制總線但只允許其中一個(gè)控制總線并使傳輸不被破壞的過程;
同步:兩個(gè)或多個(gè)器件同步時(shí)鐘信號(hào)的過程;
發(fā)送器:發(fā)送數(shù)據(jù)到總線的器件;
接收器:從總線接收數(shù)據(jù)的器件。

1.2 I2C協(xié)議層

I2C的協(xié)議定義了通信的起始和停止信號(hào)、數(shù)據(jù)有效性、響應(yīng)、仲裁、時(shí)鐘同步和地址廣播等環(huán)節(jié)。

(1)數(shù)據(jù)有效性規(guī)定

I2C總線進(jìn)行數(shù)據(jù)傳送時(shí),時(shí)鐘信號(hào)為高電平期間,數(shù)據(jù)線上的數(shù)據(jù)必須保持穩(wěn)定,只有在時(shí)鐘線上的信號(hào)為低電平期間,數(shù)據(jù)線上的高電平或低電平狀態(tài)才允許變化。 如下圖:

圖片

每次數(shù)據(jù)傳輸都以字節(jié)為單位,每次傳輸?shù)淖止?jié)數(shù)不受限制。

(2)起始和停止信號(hào)

SCL線為高電平期間,SDA 線由高電平向低電平的變化表示起始信號(hào);SCL線為高電平期間,SDA線由低電平向高電平的變化表示終止信號(hào)。 如下圖:

圖片

起始和終止信號(hào)都是由主機(jī)發(fā)出的,在起始信號(hào)產(chǎn)生后,總線就處于被占用的狀態(tài);在終止信號(hào)產(chǎn)生后,總線就處于空閑狀態(tài)。

(3)應(yīng)答響應(yīng)

每當(dāng)發(fā)送器件傳輸完一個(gè)字節(jié)的數(shù)據(jù)后,后面必須緊跟一個(gè)校驗(yàn)位,這個(gè)校驗(yàn)位是接收端通過控制SDA(數(shù)據(jù)線)來實(shí)現(xiàn)的,以提醒發(fā)送端數(shù)據(jù)我這邊已經(jīng)接收完成,數(shù)據(jù)傳送可以繼續(xù)進(jìn)行。這個(gè)校驗(yàn)位其實(shí)就是數(shù)據(jù)或地址傳輸過程中的響應(yīng)。響應(yīng)包括“應(yīng)答(ACK)”和“非應(yīng)答(NACK)”兩種信號(hào)。作為數(shù)據(jù)接收端時(shí),當(dāng)設(shè)備(無論主從機(jī))接收到I2C傳輸?shù)囊粋€(gè)字節(jié)數(shù)據(jù)或地址后,若希望對(duì)方繼續(xù)發(fā)送數(shù)據(jù),則需要向?qū)Ψ桨l(fā)送“應(yīng)答(ACK)”信號(hào)即特定的低電平脈沖,發(fā)送方會(huì)繼續(xù)發(fā)送下一個(gè)數(shù)據(jù);若接收端希望結(jié)束數(shù)據(jù)傳輸,則向?qū)Ψ桨l(fā)送“非應(yīng)答(NACK)”信號(hào)即特定的高電平脈沖,發(fā)送方接收到該信號(hào)后會(huì)產(chǎn)生一個(gè)停止信號(hào),結(jié)束信號(hào)傳輸。應(yīng)答響應(yīng)時(shí)序圖如下:

圖片

每一個(gè)字節(jié)必須保證是8位長(zhǎng)度。數(shù)據(jù)傳送時(shí),先傳送最高位(MSB),每一個(gè)被傳送的字節(jié)后面都必須跟隨一位應(yīng)答位(即一幀共有9位)。

由于某種原因從機(jī)不對(duì)主機(jī)尋址信號(hào)應(yīng)答時(shí)(如從機(jī)正在進(jìn)行實(shí)時(shí)性的處理工作而無法接收總線上的數(shù)據(jù)),它必須將數(shù)據(jù)線置于高電平,而由主機(jī)產(chǎn)生一個(gè)終止信號(hào)以結(jié)束總線的數(shù)據(jù)傳送。

如果從機(jī)對(duì)主機(jī)進(jìn)行了應(yīng)答,但在數(shù)據(jù)傳送一段時(shí)間后無法繼續(xù)接收更多的數(shù)據(jù)時(shí),從機(jī)可以通過對(duì)無法接收的第一個(gè)數(shù)據(jù)字節(jié)的“非應(yīng)答”通知主機(jī),主機(jī)則應(yīng)發(fā)出終止信號(hào)以結(jié)束數(shù)據(jù)的繼續(xù)傳送。

當(dāng)主機(jī)接收數(shù)據(jù)時(shí),它收到最后一個(gè)數(shù)據(jù)字節(jié)后,必須向從機(jī)發(fā)出一個(gè)結(jié)束傳送的信號(hào)。這個(gè)信號(hào)是由對(duì)從機(jī)的“非應(yīng)答”來實(shí)現(xiàn)的。然后,從機(jī)釋放SDA線,以允許主機(jī)產(chǎn)生終止信號(hào)。

這些信號(hào)中,起始信號(hào)是必需的,結(jié)束信號(hào)和應(yīng)答信號(hào)都可以不要。

也就是說一個(gè)主機(jī)一個(gè)從機(jī),主機(jī)向從機(jī)發(fā)送數(shù)據(jù),首先主機(jī)產(chǎn)生一個(gè)起始信號(hào),主機(jī)開始發(fā)送數(shù)據(jù)給從機(jī),如果從機(jī)接收數(shù)據(jù)之后,還想主機(jī)繼續(xù)發(fā)送數(shù)據(jù),就發(fā)送一個(gè)應(yīng)答給主機(jī),主機(jī)收到應(yīng)答信號(hào)后,繼續(xù)向從機(jī)發(fā)送數(shù)據(jù)。如果從機(jī)接收數(shù)據(jù)之后,不想主機(jī)繼續(xù)發(fā)送數(shù)據(jù),終止數(shù)據(jù)傳輸,可以向主機(jī)發(fā)送一個(gè)非應(yīng)答,主機(jī)就會(huì)產(chǎn)生一個(gè)停止信號(hào),結(jié)束I2C的通信。

主機(jī)從從機(jī)讀取數(shù)據(jù)時(shí),主機(jī)發(fā)送一個(gè)起始信號(hào),主機(jī)接收數(shù)據(jù)之后,也要產(chǎn)生一個(gè)應(yīng)答或非應(yīng)答,如果主機(jī)產(chǎn)生一個(gè)應(yīng)答,表示想繼續(xù)讀取從機(jī)數(shù)據(jù),從機(jī)接收到應(yīng)答信號(hào),從機(jī)繼續(xù)發(fā)送數(shù)據(jù)給主機(jī)。主機(jī)如果不想讀取從機(jī)數(shù)據(jù),就會(huì)產(chǎn)生一個(gè)非應(yīng)答后,會(huì)產(chǎn)生一個(gè)停止信號(hào),從機(jī)接收到非應(yīng)答信號(hào),停止數(shù)據(jù)發(fā)送。

(4)總線的尋址方式

I2C總線尋址按照從機(jī)地址位數(shù)可分為兩種,一種是7位,另一種是10位。采用7位的尋址字節(jié)(尋址字節(jié)是起始信號(hào)后的第一個(gè)字節(jié))的位定義如下:

圖片

D7~D1 位組成從機(jī)的地址。D0位是數(shù)據(jù)傳送方向位,為“0”時(shí)表示主機(jī)向從機(jī)寫數(shù)據(jù),為“1”時(shí)表示主機(jī)由從機(jī)讀數(shù)據(jù)。

10位尋址和7位尋址兼容,而且可以結(jié)合使用。10位尋址不會(huì)影響已有的7位尋址,有7位和10位地址的器件可以連接到相同的I2C總線。我們就以7位尋址為例進(jìn)行介紹。

當(dāng)主機(jī)發(fā)送了一個(gè)地址后,總線上的每個(gè)器件都將頭7位與它自己的地址比較,如果一樣,器件會(huì)判定它被主機(jī)尋址,其他地址不同的器件將被忽略后面的數(shù)據(jù)信號(hào)。 至于是從機(jī)接收器還是從機(jī)發(fā)送器,都由R/W位決定的。從機(jī)的地址由固定部分和可編程部分組成。在一個(gè)系統(tǒng)中可能希望接入多個(gè)相同的從機(jī),從機(jī)地址中可編程部分決定了可接入總線該類器件的最大數(shù)目。如一個(gè)從機(jī)的7位尋址位有4位是固定位,3位是可編程位,2^3=8這時(shí)僅能尋址8個(gè)同樣的器件,即可以有8個(gè)同樣的器件接入到該I2C總線系統(tǒng)中。

(5)數(shù)據(jù)傳輸

I2C總線上傳送的數(shù)據(jù)信號(hào)是廣義的,既包括地址信號(hào),又包括真正的數(shù)據(jù)信號(hào)。在起始信號(hào)后必須傳送一個(gè)從機(jī)的地址(7位),第8位是數(shù)據(jù)的傳送方向位(R/W),用“0”表示主機(jī)發(fā)送(寫)數(shù)據(jù)(W), “1”表示主機(jī)接收數(shù)據(jù)(R)。每次數(shù)據(jù)傳送總是由主機(jī)產(chǎn)生的終止信號(hào)結(jié)束。但是,若主機(jī)希望繼續(xù)占用總線進(jìn)行新的數(shù)據(jù)傳送,則可以不產(chǎn)生終止信號(hào),馬上再次發(fā)出起始信號(hào)對(duì)另一從機(jī)進(jìn)行尋址。

在總線的一次數(shù)據(jù)傳送過程中,可以有以下幾種組合方式:

圖片

注意:有陰影部分表示數(shù)據(jù)由主機(jī)向從機(jī)傳送,無陰影部分則表示數(shù)據(jù)由從機(jī)向主機(jī)傳送。A表示應(yīng)答,A非表示非應(yīng)答(高電平)。S表示起始信號(hào),Р表示終止信號(hào)。一個(gè)數(shù)據(jù)幀是9位。

圖片

圖片

由于51單片機(jī)沒有硬件IIC接口,即使有硬件接口我們通常還是采用軟件模擬I2C。 主要原因是硬件 IIC設(shè)計(jì)的比較復(fù)雜,而且穩(wěn)定性不怎么好,程序移植比較麻煩,而用軟件模擬IIC,最大的好處就是移植方便,同一個(gè)代碼兼容所有單片機(jī),任何一個(gè)單片機(jī)只要有IO口(不需要特定IO),都可以很快的移植過去。

EEPROM (Electrically Erasable Programmable read only memory)是指帶電可擦可編程只讀存儲(chǔ)器。是一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片。 EEPROM 可以在電腦上或?qū)S迷O(shè)備上擦除已有信息,重新編程。一般用在即插即用。

2.AT24C02芯片介紹

AT24C02芯片也是EEPROM存儲(chǔ)芯片,是一種掉電后數(shù)據(jù)不丟失的存儲(chǔ)芯片。AT24C0、01/02/04/08/16...是一個(gè)1K/2K/4K/8K/16k位串行CMOS,內(nèi)部含有128/256/512/1024/2048個(gè)8位字節(jié),AT24C01有一個(gè)8字節(jié)頁(yè)寫緩沖器,AT24CO2/04/08/16有一個(gè)16 字節(jié)頁(yè)寫緩沖器。該器件通過I2C總線接口進(jìn)行操作,它有一個(gè)專門的寫保護(hù)功能。開發(fā)板上使用的是 AT24C02 (EEPROM) 芯片,此芯片具有I2C通信接口,芯片內(nèi)保存的數(shù)據(jù)在掉電情況下都不丟失,所以通常用于存放一些比較重要的數(shù)據(jù)等。AT24CO2芯片管腳及外觀圖如下圖所示:

圖片

圖片

芯片管腳說明如下圖所示:

圖片

AT24C02器件地址為7位,高4位固定為1010,低3位由A0/A1/A2信號(hào)線的電平?jīng)Q定。因?yàn)閭鬏數(shù)刂坊驍?shù)據(jù)是以字節(jié)為單位傳送的,當(dāng)傳送地址時(shí),器件地址占7位,還有最后一位(最低位R/W)用來選擇讀寫方向,它與地址無關(guān)。其格式如下:

圖片

開發(fā)板已經(jīng)將芯片的 A0/A1/A2連接到GND,所以器件地址為1010000,即 0x50(未計(jì)算最低位)。如果要對(duì)芯片進(jìn)行寫操作時(shí),R/W即為0,寫器件地址即為0XA0;如果要對(duì)芯片進(jìn)行讀操作時(shí),R/W即為1,此時(shí)讀器件地址為0XA1。開發(fā)板上也將 WP引腳直接接在GND 上,此時(shí)芯片允許數(shù)據(jù)正常讀寫。

I2C總線時(shí)序如下圖所示:

圖片

圖片

開發(fā)板硬件連接

圖片

從圖中可以看出,芯片的SCL和SDA管腳是連接在單片機(jī)的P2.1和P2.0上,為了讓IIC總線默認(rèn)為高電平,通常會(huì)在IIC總線上接上拉電阻,在圖中并沒有看到SCL和SDA管腳有上拉電阻,這是因?yàn)殚_發(fā)板單片機(jī)IO都外接了10K上拉電阻,當(dāng)單片機(jī)IO口連接到芯片的SCL和SDA腳時(shí)即相當(dāng)于它們外接上拉電阻,所以此處可以省去。

**3.多文件工程創(chuàng)建
**

實(shí)現(xiàn)的功能是:系統(tǒng)運(yùn)行時(shí),數(shù)碼管右 3 位顯示 0,按矩陣按鍵 S1 鍵將數(shù)據(jù)寫入到 EEPROM 內(nèi)保存,按 S2 鍵讀取 EEPROM 內(nèi)保存的數(shù)據(jù),按 S3 鍵顯示數(shù)據(jù)加 1,按 S4 鍵顯示數(shù)據(jù)清零,最大能寫入的數(shù)據(jù)是 255。

圖片

App文件夾:用于存放外設(shè)驅(qū)動(dòng)文件,如LED、數(shù)碼管、定時(shí)器等。
Obj文件夾:用于存放編譯產(chǎn)生的 c/匯編/鏈接的列表清單、調(diào)試信息、hex文件、預(yù)覽信息、封裝庫(kù)等文件。
Public文件夾:用于存放51單片機(jī)公共的文件,如延時(shí)、51頭文件、變量類型重定義等。
User文件夾:用于存放用戶主函數(shù)文件,如 main.c。

1.新建一個(gè)工程

圖片

選擇對(duì)應(yīng)的芯片型號(hào)

圖片

圖片

2.向工程添加文件

按照需要給工程分組并添加對(duì)應(yīng)文件,在工程中分3組,User、App、Public,至于前面創(chuàng)建的Obj文件夾是在工程中無需體現(xiàn),因?yàn)橹皇蔷幾g器生成的一些中間文件和.hex執(zhí)行文件。通常在工程組的命名與創(chuàng)建的文件夾名保持一致,方便查找到源文件位置。如下所示:

圖片

新建分組,雙擊可以修改名字,用英文分組,完成后,點(diǎn)擊OK

圖片

分組后,在工程中就會(huì)出現(xiàn)剛才的分組列表,如下所示:

圖片

對(duì)分組添加相應(yīng)的文件,點(diǎn)擊小方框,選擇要添加文件的分組,添加文件。

圖片

同樣的方法,將App、Public工程組中文件也添加進(jìn)去。一般除了main.c沒有對(duì)應(yīng)的main.h的頭文件,其他文件通常會(huì)有對(duì)應(yīng)的.h頭文件,例如public.h和public.c。

圖片

頭文件的書寫

#ifndef _public_H  //public是與文件名相同  ifndef是c語(yǔ)言中的條件編譯,意思是如果沒有定義對(duì)應(yīng)的頭文件,就會(huì)定義該頭文件
#define _public_H




#endif

主要目的是防止頭文件的重復(fù)包含和編譯。

配置魔術(shù)棒選項(xiàng)

這一步的配置工作非常重要,很多人編寫完程序編譯后發(fā)現(xiàn)找不到HEX文件,還有的人直接編譯前面添加好文件的工程出現(xiàn)報(bào)錯(cuò),這些問題都是在這個(gè)地方?jīng)]有配置好導(dǎo)致的。

(1)C51選項(xiàng)卡配置,此處目的是將我們前面添加到工程組中的文件路徑包括進(jìn)來,否則程序中調(diào)用其他文件夾的頭文件則會(huì)報(bào)錯(cuò)找不到頭文件路徑,具體步驟如下:

點(diǎn)擊魔術(shù)棒

圖片

圖片

圖片

頭文件的作用一般是(1)包含其他的頭文件;(2)聲明全局變量;(3)聲明自定義函數(shù)(4)自定義的變量類型(u8 u16),當(dāng)其他程序中加載了該頭文件就可以使用該函數(shù)和全局變量。

public.h文件

#ifndef _public_H//public是與文件名相同  
//ifndef是c語(yǔ)言中的條件編譯,意思是如果沒有定義對(duì)應(yīng)的頭文件,就會(huì)定義該頭文件
//主要目的是防止頭文件的重復(fù)包含和編譯。
#define _public_H


#include "reg52.h" //加載reg52.h頭文件
//定義類型別名
typedef unsigned int u16;
typedef unsigned char u8;


//聲明兩個(gè)延時(shí)函數(shù)
void delay_10us(u16 time);
void delay_ms(u16 time);


#endif

public.c文件

#include "public.h"


void delay_10us(u16 time)  //延時(shí)函數(shù) time=1大概延時(shí)10us
{
  while(time--)
  ;
}


void delay_ms(u16 time)       //延時(shí)函數(shù) time=1大概延時(shí)1ms
{
  u16 i,j;
  for(i=time;i >0;i--)
    for(j=110;j >0;j--)
    ;
}

(2)Output選項(xiàng)卡中把輸出文件夾定位到我們實(shí)驗(yàn)?zāi)夸浵碌?Obj文件夾,如果想在編譯的過程中生成hex文件,那么、Create HEX File 選項(xiàng)勾上。配置如下:

圖片

(3) Listing 選項(xiàng)卡中把輸出文件夾也定位到我們實(shí)驗(yàn)?zāi)夸浵碌腛bj文件夾。其它設(shè)置默認(rèn),配置如下:

圖片

同樣的方式把其他文件夾的文件添加到對(duì)應(yīng)的分組,并將頭文件的路徑添加上。

圖片

4.實(shí)驗(yàn)內(nèi)容和程序

實(shí)現(xiàn)的功能是:系統(tǒng)運(yùn)行時(shí),數(shù)碼管右 3 位顯示 0,按矩陣按鍵 S1 鍵將數(shù)據(jù)寫入到 EEPROM 內(nèi)保存,按 S2 鍵讀取 EEPROM 內(nèi)保存的數(shù)據(jù),按 S3 鍵顯示數(shù)據(jù)加 1,按 S4 鍵顯示數(shù)據(jù)清零,最大能寫入的數(shù)據(jù)是 255。

實(shí)驗(yàn)結(jié)果

圖片

,時(shí)長(zhǎng)00:36

[ ]

主函數(shù)main.c

#include "public.h"
#include "key.h"
#include "smg.h"
#include "at24c02.h"
//#include "iic.h"   at24c02函數(shù)內(nèi)容調(diào)用了iic.h
/*
實(shí)現(xiàn)的功能是:系統(tǒng)運(yùn)行時(shí),數(shù)碼管右 3 位顯示 0,
按 S1 鍵將數(shù)據(jù) 寫入到 EEPROM 內(nèi)保存,
按 S2 鍵讀取 EEPROM 內(nèi)保存的數(shù)據(jù),
按 S3 鍵顯示數(shù)據(jù)加 1,
按 S4 鍵顯示數(shù)據(jù)清零,最大能寫入的數(shù)據(jù)是 255。
*/


#define EEPROM_ADDRESS 0 //宏定義數(shù)據(jù)存儲(chǔ)的地址
void main()
{
  u8 key_value=0;  //保存按鍵的鍵值
  u8 save_value =0;//定義變量,存儲(chǔ)數(shù)據(jù)的變量
  u8 save_buf[3];//三位數(shù)
   while(1)
  {
    key_value=key_juzhen_fanzhuan_scan();
    if(key_value==1)  //按 K1 鍵將數(shù)據(jù) 寫入到 EEPROM 內(nèi)保存
    {
      at24c02_write_one_byte(EEPROM_ADDRESS,save_value);


    }
    else if(key_value==2)  //按 K2 鍵讀取 EEPROM 內(nèi)保存的數(shù)據(jù)
    {
      save_value=at24c2_read_one_byte(EEPROM_ADDRESS);
    }
    else if(key_value==3)
    {
       save_value++;
       //不能一直加 8位二進(jìn)制 最大值255
       if(save_value==255)//當(dāng)數(shù)據(jù)大于255后會(huì)溢出 重新從0開始
         save_value=255;
    }
    else if(key_value==4)
    {
           save_value=0;
    }
    //數(shù)碼管顯示
    //對(duì)十進(jìn)制數(shù)據(jù)進(jìn)行取百位 十位 個(gè)位
    /*
    三位數(shù)提取 百位 假設(shè)三位數(shù)是100 100/100=1 取出百位
    十位提取 (100%100)/10 數(shù)據(jù)對(duì)100取余 ,再除以10 取出十位
    個(gè)位提取  (100%100)%10  數(shù)據(jù)對(duì)100取余,再對(duì)10取余 取出個(gè)位 
    */
//    save_buf[0]=save_value/100;//取出百位
//    save_buf[1]= save_value%100/10;// 取出十位
//    save_buf[2]= save_value%100%10;//取出個(gè)位
    save_buf[0]=gsmg[save_value/100];//取出百位
    save_buf[1]= gsmg[save_value%100/10];// 取出十位
    save_buf[2]= gsmg[save_value%100%10];//取出個(gè)位
      smg_display(save_buf,6);//數(shù)組名表示數(shù)組元素首地址
  }
}

Key.c 矩陣按鍵程序

#include "key.h"








u8 key_juzhen_fanzhuan_scan(void) //定義線翻轉(zhuǎn)法函數(shù)
{
   /*
  線翻轉(zhuǎn)法,就是使所有行線為低電平時(shí),檢測(cè)所有列線是否有低電平,
  如果有,就記錄列線值;然后再翻轉(zhuǎn),使所有列線都為低電平,檢測(cè)所有行線的值,
  由于有按鍵按下,行線的值也會(huì)有變化,記錄行線的值。從而就可以檢測(cè)到全部按鍵。
   */
    static u8 key_value=0;   
    //定義靜態(tài)局部變量 key_value的值會(huì)賦完一次初值之后,key_value的值會(huì)保存上一次的結(jié)果值
    /*開發(fā)板 4*4矩陣鍵盤 行控制端口 P1.7 P1.6 P1.5 P1.4    列控制端口: P1.3 P1.2 P1.1 P1.0
     P1口輸出默認(rèn)是高電平,使所有行線為低電平  此時(shí)P1控制端 二進(jìn)制 0000 1111  十六進(jìn)制 0x0f
     找出按鍵的列值
     {
       按鍵在第1列時(shí),此時(shí)P1控制端 二進(jìn)制 0000 0111  十六進(jìn)制 0x07     key_value=1
    按鍵在第2列時(shí),此時(shí)P1控制端 二進(jìn)制 0000 1011  十六進(jìn)制 0x0b     key_value=2
    按鍵在第3列時(shí),此時(shí)P1控制端 二進(jìn)制 0000 1101  十六進(jìn)制 0x0d     key_value=3
    按鍵在第4列時(shí),此時(shí)P1控制端 二進(jìn)制 0000 1110  十六進(jìn)制 0x0e       key_value=4

     }  
     再使所有的列線為低電平  此時(shí)P1控制端 二進(jìn)制 1111 0000  十六進(jìn)制 0xf0
     {
    按鍵在第1行時(shí),此時(shí)P1控制端 二進(jìn)制  0111 0000  十六進(jìn)制 0x70    按鍵的位置 key_value=對(duì)應(yīng)的行數(shù)=key_value
    按鍵在第2行時(shí),此時(shí)P1控制端 二進(jìn)制  1011 0000  十六進(jìn)制 0xb0    按鍵的位置 key_value=對(duì)應(yīng)的行數(shù)+4*1 = key_value+4    第一行有4個(gè)按鍵
    按鍵在第3行時(shí),此時(shí)P1控制端 二進(jìn)制  1101  0000    十六進(jìn)制 0xd0    按鍵的位置 key_value=對(duì)應(yīng)的行數(shù)+4*2  = key_value+8  前兩行有8個(gè)按鍵
    按鍵在第4行時(shí),此時(shí)P1控制端 二進(jìn)制  1110  0000    十六進(jìn)制 0xe0      按鍵的位置 key_value=對(duì)應(yīng)的行數(shù)+4*3  = key_value+12 前兩行有12個(gè)按鍵
     }
     */
    KEY_Port=0x0f;       //使所有行線為低電平 
    if(KEY_Port!=0x0f)
    {
       delay_10us(1000);//延時(shí)10ms實(shí)現(xiàn)按鍵消抖
      //測(cè)試列
      if(KEY_Port!=0x0f) //實(shí)現(xiàn)消抖后再次檢測(cè)
      {
      KEY_Port=0x0f;  //設(shè)置再次賦值 行線為低電平,方便進(jìn)行檢查 
         switch(KEY_Port)
         {
          case 0x07: key_value = 1;break;
          case 0x0b: key_value = 2;break;
          case 0x0d: key_value = 3;break;
          case 0x0e: key_value = 4;break;
         }
         //測(cè)試列
        KEY_Port=0xf0;//使所有的列線為低電平
        switch(KEY_Port)
         {
          case 0x70: key_value = key_value;break;
          case 0xb0: key_value = key_value+4;break;
          case 0xd0: key_value = key_value+8;break;
          case 0xe0: key_value = key_value+12;break;
         }
         while(KEY_Port!=0xf0);//等待按鍵松開 當(dāng)按鍵松開時(shí) KEY_Port = 0xf0 會(huì)跳出死循環(huán)  
      }


    }
     else
    key_value=0;
    return key_value;//返回函數(shù)值


}

Key.h 頭文件

#ifndef _key_H     //條件編譯語(yǔ)句 如果沒有定義頭文件,就在下面重新定義頭文件
#define _key_H
#include "public.h"  //加載公共部分Public中的頭文件的內(nèi)容


#define KEY_Port P1 //宏定義 無分號(hào) 將P1口定義為矩陣鍵盤的端口 
//開發(fā)板的數(shù)碼管段選信號(hào)由P0口(P0.0-P0.7)控制   位選 P2口默認(rèn)是輸出高電平 默認(rèn)選擇LED8 第1個(gè)數(shù)碼管
//定義數(shù)碼管顯示內(nèi)容的數(shù)組


u8 key_juzhen_fanzhuan_scan(void);//定義線翻轉(zhuǎn)法函數(shù)




#endif

Smg.c 數(shù)碼管顯示函數(shù)

#include "smg.h"
u8 gsmg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
      0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
      //共陰極數(shù)碼管顯示數(shù)據(jù)1-F,最后一個(gè)是數(shù)碼管全滅  表示c語(yǔ)言中的續(xù)行符
void smg_display(u8 dat[],u8 pos)   //dat[]是數(shù)碼管段選數(shù)據(jù) 顯示0 1 2 3...
// pos 位選     1 2 3 4 5 6 7 8 第幾個(gè)數(shù)據(jù)管
//更改數(shù)碼管的顯示 提供數(shù)據(jù)和數(shù)碼管使用的位置
{
 u8 i=0;     //定義變量的時(shí)候賦初值,防止一些編譯器識(shí)別未用的變量產(chǎn)生報(bào)錯(cuò)
 u8 pos_temp=pos-1;//
 for(i=pos_temp;i< 8;i++)
 {
    switch(i)  //用switch-case語(yǔ)句進(jìn)行位選
  {
   case 7: W_C =0;W_B = 0;W_A = 0;break; //對(duì)應(yīng)選擇了Y0-LED1   最后一個(gè)數(shù)碼管
   case 6: W_C =0;W_B = 0;W_A = 1;break; //對(duì)應(yīng)選擇了Y0-LED2
   case 5: W_C =0;W_B = 1;W_A = 0;break; //對(duì)應(yīng)選擇了Y0-LED3
   case 4: W_C =0;W_B = 1;W_A = 1;break; //對(duì)應(yīng)選擇了Y0-LED4
   case 3: W_C =1;W_B = 0;W_A = 0;break; //對(duì)應(yīng)選擇了Y0-LED5
   case 2: W_C =1;W_B = 0;W_A = 1;break; //對(duì)應(yīng)選擇了Y0-LED6
   case 1: W_C =1;W_B = 1;W_A = 0;break; //對(duì)應(yīng)選擇了Y0-LED7
   case 0: W_C =1;W_B = 1;W_A = 1;break; //對(duì)應(yīng)選擇了Y0-LED8  第一個(gè)數(shù)碼管


  }


//  SMG_Port = gsmg[dat[i-pos_temp]];//段選信號(hào)  dat[]表示0 1 2 3 4 5 6 7 -F
  SMG_Port = dat[i-pos_temp];//段選信號(hào)  dat[]表示0 1 2 3 4 5 6 7 -F
  delay_10us(100);//大概1ms延時(shí)
  SMG_Port=0x00;//消隱
 }
}

Smg.h 頭文件

#ifndef _smg_H
#define _smg_H
#include "public.h"


#define SMG_Port P0 //宏定義 將P0口定義SMG_Port 
//開發(fā)板的數(shù)碼管段選信號(hào)由P0口(P0.0-P0.7)控制
//定義數(shù)碼管顯示內(nèi)容的數(shù)組
//定義的是全局變量 gsmg 開頭
extern u8 gsmg[17];   //對(duì)數(shù)組聲明


sbit W_C = P2^4;//sbit定義位變量  位控制選擇數(shù)碼管
sbit W_B = P2^3; //sbit定義位變量
sbit W_A = P2^2;//sbit定義位變量
void smg_display(u8 dat[],u8 pos); //聲明數(shù)碼管動(dòng)態(tài)掃描顯示函數(shù)
#endif

Iic.c

#include "iic.h"


//IIC起始和停止信號(hào)和應(yīng)答信號(hào)函數(shù)
/*
SCL線為高電平期間,SDA 線由高電平向低電平的變化表示起始信號(hào);
SCL線為高電平期間,SDA線由低電平向高電平的變化表示終止信號(hào)。
每當(dāng)發(fā)送器件傳輸完一個(gè)字節(jié)的數(shù)據(jù)后,后面必須緊跟一個(gè)校驗(yàn)位,
這個(gè)校驗(yàn)位是接收端通過控制SDA(數(shù)據(jù)線)來實(shí)現(xiàn)的,
以提醒發(fā)送端數(shù)據(jù)我這邊已經(jīng)接收完成,數(shù)據(jù)傳送可以繼續(xù)進(jìn)行。
這個(gè)校驗(yàn)位其實(shí)就是數(shù)據(jù)或地址傳輸過程中的響應(yīng)。
響應(yīng)包括"應(yīng)答(ACK)"和"非應(yīng)答(NACK)"兩種信號(hào)。
作為數(shù)據(jù)接收端時(shí),當(dāng)設(shè)備(無論主從機(jī))接收到I2C傳輸?shù)囊粋€(gè)字節(jié)數(shù)據(jù)或地址后,
若希望對(duì)方繼續(xù)發(fā)送數(shù)據(jù),則需要向?qū)Ψ桨l(fā)送"應(yīng)答(ACK)"信號(hào)即特定的低電平脈沖,
發(fā)送方會(huì)繼續(xù)發(fā)送下一個(gè)數(shù)據(jù);若接收端希望結(jié)束數(shù)據(jù)傳輸,
則向?qū)Ψ桨l(fā)送"非應(yīng)答(NACK)"信號(hào)即特定的高電平脈沖
,發(fā)送方接收到該信號(hào)后會(huì)產(chǎn)生一個(gè)停止信號(hào),結(jié)束信號(hào)傳輸。
每一個(gè)字節(jié)必須保證是8位長(zhǎng)度。數(shù)據(jù)傳送時(shí),先傳送最高位(MSB),
每一個(gè)被傳送的字節(jié)后面都必須跟隨一位應(yīng)答位(即一幀共有9位)。*/


void iic_start(void)  //根據(jù)起始信號(hào)的時(shí)序編寫iic的起始信號(hào)函數(shù)
{

  //根據(jù)數(shù)據(jù)手冊(cè)的時(shí)間參數(shù)級(jí)或者us SDA由高電平編程低電平  
  IIC_SDA=1;//如果把該條語(yǔ)句放在SCL后面,第二次讀寫會(huì)出現(xiàn)問題
  delay_10us(1);
  IIC_SCL=1;  //SCL為高電平
  delay_10us(1);
  IIC_SDA=0;
  delay_10us(1);
   IIC_SCL=0; //SCL為低電平時(shí),表示占用總線,總線處于工作狀態(tài)
  delay_10us(1);
}


void iic_stop(void) //iic的停止信號(hào)
{
  IIC_SDA=0; //如果把該條語(yǔ)句放在SCL后面,第二次讀寫會(huì)出現(xiàn)問題
    //根據(jù)數(shù)據(jù)手冊(cè)的時(shí)間參數(shù)級(jí)或者us SDA由低電平編程高電平
   delay_10us(1);  
  IIC_SCL=1; //SCL為高電平
  delay_10us(1);
  IIC_SDA=1;
  delay_10us(1);


}
/*
 響應(yīng)包括"應(yīng)答(ACK)"和"非應(yīng)答(NACK)"兩種信號(hào)。
 作為數(shù)據(jù)接收端時(shí),當(dāng)設(shè)備(無論主從機(jī))接收到I2C傳輸?shù)囊粋€(gè)字節(jié)數(shù)據(jù)或地址后,
 若希望對(duì)方繼續(xù)發(fā)送數(shù)據(jù),則需要向?qū)Ψ桨l(fā)送"應(yīng)答(ACK)"信號(hào)即特定的低電平脈沖,
 發(fā)送方會(huì)繼續(xù)發(fā)送下一個(gè)數(shù)據(jù);若接收端希望結(jié)束數(shù)據(jù)傳輸,
 則向?qū)Ψ桨l(fā)送"非應(yīng)答(NACK)"信號(hào)即特定的高電平脈沖,
 發(fā)送方接收到該信號(hào)后會(huì)產(chǎn)生一個(gè)停止信號(hào),結(jié)束信號(hào)傳輸。
*/


//主機(jī)發(fā)送數(shù)據(jù)給從機(jī) 的應(yīng)答信號(hào)和非應(yīng)答信號(hào)
void iic_ack(void) //iicACK應(yīng)答信號(hào)函數(shù)
{
   //ACK是特殊的低電平信號(hào)
  //由時(shí)序圖編寫
  IIC_SCL=0;
  IIC_SDA=0;
  delay_10us(1);
  IIC_SCL=1;
  delay_10us(1);
  IIC_SCL=0;


}
void iic_noack(void) //iic非應(yīng)答信號(hào)函數(shù)
{
   //NACK是特殊的高電平信號(hào)
  //由時(shí)序圖編寫
  IIC_SCL=0;
  IIC_SDA=1;
  delay_10us(1);
  IIC_SCL=1;
  delay_10us(1);
  IIC_SCL=0;


}


//判斷應(yīng)答信號(hào)的函數(shù) 就是主機(jī)讀取IIC_SDA的電平
//函數(shù)設(shè)置一個(gè)返回值 1表示非應(yīng)答  0表示應(yīng)答
u8 iic_wait_ack(void)
{
   /*第一步:因?yàn)镾CL為高電平的時(shí)候數(shù)據(jù)才是穩(wěn)定的,將SCL設(shè)置為高電平.
    第二步;延時(shí)一點(diǎn)時(shí)間進(jìn)行讀取SDA的電平.
    第三步:利用while循環(huán)   while(IIC_SDA) 當(dāng)IIC_SDA=0時(shí),響應(yīng)是ACK應(yīng)答信號(hào)會(huì)跳出循環(huán),
    將SCL設(shè)置為低電平,數(shù)據(jù)可以修改,等待下一次數(shù)據(jù)的變化,并且返回函數(shù)值0.
    當(dāng)IIC_SDA=1時(shí)表示響應(yīng)是NACK非應(yīng)答信號(hào)會(huì)執(zhí)行循環(huán)體里面的函數(shù),一直循環(huán),設(shè)置一個(gè)變量,用來計(jì)時(shí)
    的時(shí)間,不能一直等待,當(dāng)計(jì)時(shí)超過一定的時(shí)間,就認(rèn)為SDA是高電平,此時(shí)是非應(yīng)答,要產(chǎn)生停止信號(hào)
  */
  u8 time_temp=0;
  IIC_SCL=1;
  delay_10us(1);
  while(IIC_SDA)
   {
    time_temp++;//等價(jià)與time_step=time_step+1
    if(time_temp >100)
    {
      iic_stop();
      return 1;
    }


   }
     IIC_SCL=0;
  return 0;
}
//IIC的讀寫字節(jié)數(shù)據(jù)函數(shù)
//IIC的寫字節(jié)函數(shù)
void iic_write_byte(u8 dat)
{
 //要寫數(shù)據(jù)或地址 從高位開始寫 SCL低電平數(shù)據(jù)可以變化 SCL高電平的時(shí)候數(shù)據(jù)穩(wěn)定,就可以發(fā)送出去
  //一個(gè)字節(jié)有8位,因此要一位一位的寫用循環(huán)
  u8 i=0;
  IIC_SCL=0;//SCL是低電平 數(shù)據(jù)可以修改 先寫高位
  for(i=0;i< 8;i++)
  {
     /*獲取dat最高位的 dat&0x80   d7 d6 d5 d4 d3 d2 d1 d0  & 1 0 0 0 0 0 0 0
  根據(jù)與運(yùn)算  dat的低7位都為0 當(dāng)d7=1 時(shí)邏輯表達(dá)式的值為1 當(dāng)d7=0時(shí),邏輯表達(dá)式的值為0
  */
  if((dat&0x80) >0) //dat最高位是1
    IIC_SDA=1; //傳輸數(shù)據(jù)是1
  else
    IIC_SDA=0;  //傳輸數(shù)據(jù)是0
/*對(duì)dat的次高位變成最高位 移位運(yùn)算符dat< < 1 表示dat左移一位但是不會(huì)改變dat的值,
 表達(dá)式的值是dat左移一位的結(jié)果  左移移位運(yùn)算符 dat< <=1      表示dat左移一位將結(jié)果賦值給dat*/
   dat< <=1;
  delay_10us(1);//延時(shí)10us
    IIC_SCL=1; //數(shù)據(jù)要進(jìn)行傳輸 SCL為高電平 數(shù)據(jù)穩(wěn)定 可以進(jìn)行數(shù)據(jù)傳輸
  delay_10us(1);//延時(shí)10us
  IIC_SCL=0; //重新SCL設(shè)置為0 為下一位的數(shù)據(jù)修改做準(zhǔn)備 
  delay_10us(1);//延時(shí)10us
  }


}
//IIC的讀字節(jié)函數(shù)
u8 iic_read_byte(u8 ack)  
//IIC的讀字節(jié)函數(shù),要讀取數(shù)據(jù),因此由函數(shù)的返回值,一個(gè)字節(jié)的數(shù)據(jù)u8
//讀取數(shù)據(jù)需要看返回的響應(yīng)信號(hào) 1 ACK 繼續(xù)讀取 0 NACK不讀取數(shù)據(jù)
{
  u8 i=0;
  u8 receive_dat=0;//接收數(shù)據(jù)
  for(i=0;i< 8;i++)
  {
    IIC_SCL=0;//SCL為低電平 可以修改數(shù)據(jù)
    delay_10us(1);
    IIC_SCL=1;  //SCL為高電平 數(shù)據(jù)穩(wěn)定 可以進(jìn)行讀取
    receive_dat< <=1;
    if(IIC_SDA)  //開始讀取數(shù)據(jù) 判斷SDA管腳電平 SDA==1 得到一個(gè)信號(hào)
    //對(duì)最低位進(jìn)行讀取
    receive_dat++;
    delay_10us(1);  
  /*例如  i=0 SDA=1 receive_dat=0+1=1  高位      先讀取高位
      i=1 SDA=1 receive_dat=1+1=2  次高位
    1 二進(jìn)制  0000 0001
    2 二進(jìn)制  0000 0010
    與實(shí)際不符合 實(shí)際應(yīng)該是0000 0011 并且需要進(jìn)行移位最后11移動(dòng)最高位
    即 receive_dat進(jìn)行左移 假設(shè)i=0 SDA=1  0000 0001 
                  i=1 SDA=0  0000 0010  
                  添加  receive_dat< <=1;左移語(yǔ)句
    假設(shè)從機(jī)數(shù)據(jù)是 1001 0001
    i    receive_dat< <=1;    receive_dat  主機(jī)從從機(jī)讀取的數(shù)據(jù)
    i=0  0000 0000      0000 0001     最高位數(shù)據(jù) SDA=1
    i=1  0000 0010      0000 0010     次高位數(shù)據(jù) SDA=0
    i=2  0000 0100      0000 0100     SDA=0
    i=3  0000 1000      0000 1001     SDA=1
    i=4  0001 0010      0001 0010        SDA=0
    i=5  0010 0100      0010 0100         SDA=0
    i=6  0100 1000      0100 1000        SDA=0
    i=7  1001 0000      1001 0001        SDA=1
    數(shù)據(jù)傳輸完成*/
  }
  //對(duì)響應(yīng)信號(hào)進(jìn)行處理 看主機(jī)是否需要繼續(xù)向從機(jī)讀取數(shù)據(jù) ACK=0 繼續(xù)讀取數(shù)據(jù) ACK=1停此讀取數(shù)據(jù)
  if(!ack) // ack=0時(shí)發(fā)送一個(gè)ack
  iic_noack();
  else
     iic_ack();    // ack=1  !ack=0 時(shí)發(fā)送一個(gè)nack


  return receive_dat;//返回讀取的數(shù)據(jù)
}

Iic.h

#ifndef _iic_H
#define _iic_H




#include "public.h"
//利用I/O模擬IIC的時(shí)序 開發(fā)板的P2.1連接SCL時(shí)鐘線 P2.0連接SDA雙向數(shù)據(jù)傳輸線
sbit IIC_SCL=P2^1;
sbit IIC_SDA=P2^0;
//函數(shù)聲明
void iic_start(void);//根據(jù)起始信號(hào)的時(shí)序編寫iic的起始信號(hào)函數(shù)
void iic_stop(void); //iic的停止信號(hào)
void iic_ack(void); //iicACK應(yīng)答信號(hào)函數(shù)
void iic_noack(void); //iic非應(yīng)答信號(hào)函數(shù)
//判斷應(yīng)答信號(hào)的函數(shù) 就是主機(jī)讀取IIC_SDA的電平
//函數(shù)設(shè)置一個(gè)返回值 1表示非應(yīng)答  0表示應(yīng)答
u8 iic_wait_ack(void);
//IIC的寫字節(jié)函數(shù)
void iic_write_byte(u8 dat);
//IIC的讀字節(jié)函數(shù)
u8 iic_read_byte(u8 ack);
#endif

At24c02.c 程序

#include "at24c02.h"
#include "iic.h"
//at24c02的讀寫程序,根據(jù)IIC協(xié)議層數(shù)據(jù)傳輸編寫
void at24c02_write_one_byte(u8 addr,u8 dat) 
//24C02有256個(gè)字節(jié) 要寫到寫入地址  和寫入的數(shù)據(jù)    主機(jī)寫入從機(jī) 主機(jī)向從機(jī)發(fā)送數(shù)據(jù)
{
   /* 第一步:發(fā)送起始信號(hào) 
  AT24C02器件地址為7位,高4位固定為1010,
  低3位由A0/A1/A2信號(hào)線的電平?jīng)Q定。
  因?yàn)閭鬏數(shù)刂坊驍?shù)據(jù)是以字節(jié)為單位傳送的,
  當(dāng)傳送地址時(shí),器件地址占7位,還有最后一位(最低位R/W)用來選擇讀寫方向,它與地址無關(guān)。 
     寫AT24C02引腳二進(jìn)制 1010 0000  十六進(jìn)制:0xA0
     讀AT24C02引腳二進(jìn)制 1010 0001  十六進(jìn)制:0xA1


     */
  iic_start();//發(fā)送起始信號(hào)  后第一個(gè)字節(jié)是從機(jī)地址+讀寫位
  iic_write_byte(0xA0);
  //從機(jī)發(fā)的ACK   主機(jī)需要等待ACK
  iic_wait_ack();   //等待一個(gè)ACK
  iic_write_byte(addr);  //發(fā)送寫地址的傳輸
  //因?yàn)閿?shù)據(jù)傳輸方向沒有改變 因此不需要從新產(chǎn)生起始信號(hào)和
  iic_wait_ack();    //等待一個(gè)ACK
  iic_write_byte(dat);//寫入一個(gè)字節(jié)數(shù)據(jù)
  iic_wait_ack();   //等待一個(gè)ACK
  iic_stop();//傳輸一個(gè)字節(jié)后 主機(jī)發(fā)出停止信號(hào)
  delay_ms(10);//延時(shí)10ms  將數(shù)據(jù)存儲(chǔ)
}


//讀字節(jié)函數(shù)    就是主機(jī)向從機(jī)讀取數(shù)據(jù)  即從機(jī)向主機(jī)發(fā)送數(shù)據(jù) 
//數(shù)據(jù)的傳輸方向改變
u8 at24c2_read_one_byte(u8 addr)   //讀取數(shù)據(jù)的地址
{  //首先寫入讀取數(shù)據(jù)的存放的地址 然后改變數(shù)據(jù)傳輸方向  讀取數(shù)據(jù)傳輸方向改變
   u8 temp=0;//讀取數(shù)據(jù)存儲(chǔ)變量
   iic_start();//主機(jī)發(fā)送起始信號(hào)
  iic_write_byte(0xA0);//  第一個(gè)字節(jié)是從機(jī)地址+讀寫位
  iic_wait_ack();    //等待一個(gè)ACK
  iic_write_byte(addr);  //發(fā)送讀數(shù)據(jù)存放地址的傳輸
  iic_wait_ack();    //等待一個(gè)ACK
   //開始讀取數(shù)據(jù)   數(shù)據(jù)傳輸方向改變
    iic_start();//主機(jī)發(fā)送起始信號(hào)
  iic_write_byte(0xA1);//  第一個(gè)字節(jié)是從機(jī)地址+讀寫位
  iic_wait_ack();    //等待一個(gè)ACK
  temp=iic_read_byte(0);  //1 ACK 繼續(xù)讀取 0 NACK不讀取數(shù)據(jù)
  iic_stop();//產(chǎn)生停止信號(hào)
  return temp;
}

At24c02.h 頭文件

#ifndef _at24c02_H
#define _at24c02_H
#include "public.h"
void at24c02_write_one_byte(u8 addr,u8 dat) ;
u8 at24c2_read_one_byte(u8 addr);   //讀取數(shù)據(jù)的地址
#endif
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 微控制器
    +關(guān)注

    關(guān)注

    48

    文章

    7394

    瀏覽量

    150625
  • 51單片機(jī)
    +關(guān)注

    關(guān)注

    273

    文章

    5696

    瀏覽量

    122745
  • 總線
    +關(guān)注

    關(guān)注

    10

    文章

    2848

    瀏覽量

    87839
  • EEPROM
    +關(guān)注

    關(guān)注

    9

    文章

    1006

    瀏覽量

    81218
  • I2C
    I2C
    +關(guān)注

    關(guān)注

    28

    文章

    1468

    瀏覽量

    122773
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    C51單片機(jī)模擬I2C總線的C語(yǔ)言實(shí)現(xiàn)

    EEPROM為ATMEL公司的AT24C01A。單片機(jī)為ATMEL公司的AT89C51。2. 軟件說明
    發(fā)表于 08-13 13:55 ?88次下載

    51單片機(jī)實(shí)驗(yàn)程序

    一共有9個(gè)51單片機(jī)實(shí)驗(yàn)程序,實(shí)際上還有相關(guān)的匯編程序N例,單片機(jī)嵌入式程序和I2C源程序等等。
    發(fā)表于 09-11 10:47 ?19次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b><b class='flag-5'>實(shí)驗(yàn)</b>程序

    I2C總線的單片機(jī)C語(yǔ)言實(shí)現(xiàn)及其應(yīng)用

    I2C總線的單片機(jī)C語(yǔ)言實(shí)現(xiàn)及其應(yīng)用 本文介紹了I2C總線的概念、接口特性和傳輸時(shí)序,提出了一種用51系列
    發(fā)表于 10-17 08:36 ?2921次閱讀
    <b class='flag-5'>I2C</b>總線的<b class='flag-5'>單片機(jī)</b><b class='flag-5'>C</b>語(yǔ)言實(shí)現(xiàn)及其應(yīng)用

    I2C總線的51單片機(jī)通用驅(qū)動(dòng)程序

    i2c 51單片機(jī)通用驅(qū)動(dòng)程序
    發(fā)表于 05-20 10:26 ?28次下載

    使用C語(yǔ)言模擬51單片機(jī)I2C總線的資料和程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用C語(yǔ)言模擬51單片機(jī)I2C總線的資料和程序免費(fèi)下載。
    發(fā)表于 08-06 17:34 ?11次下載
    使用<b class='flag-5'>C</b>語(yǔ)言模擬<b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b>的<b class='flag-5'>I2C</b>總線的資料和程序免費(fèi)下載

    使用51單片機(jī)IO模擬I2C的程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用51單片機(jī)IO模擬I2C的程序免費(fèi)下載。
    發(fā)表于 08-02 17:34 ?5次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b>IO模擬<b class='flag-5'>I2C</b>的程序免費(fèi)下載

    使用51單片機(jī)模擬I2C的原理和程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是使用51單片機(jī)模擬I2C的原理和程序免費(fèi)下載。
    發(fā)表于 07-12 17:39 ?3次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b>模擬<b class='flag-5'>I2C</b>的原理和程序免費(fèi)下載

    如何使用C語(yǔ)言實(shí)現(xiàn)51單片機(jī)模擬I2C總線

    EEPROM為ATMEL公司的AT24C01A。單片機(jī)為ATMEL公司的AT89C51。
    的頭像 發(fā)表于 05-05 15:32 ?4343次閱讀
    如何使用<b class='flag-5'>C</b>語(yǔ)言實(shí)現(xiàn)<b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b>模擬<b class='flag-5'>I2C</b>總線

    I2C串行EEPROM與PICmicro單片機(jī)的接口設(shè)計(jì)

    I2C串行EEPROM與PICmicro單片機(jī)的接口設(shè)計(jì)說明。
    發(fā)表于 05-11 10:24 ?7次下載

    STM32F4 I2C-EEPROM實(shí)驗(yàn)例程

    STM32F4 I2C-EEPROM實(shí)驗(yàn)例程(嵌入式開發(fā)專業(yè)課程)-STM32F4 I2C-EEPROM實(shí)驗(yàn)例程,有需要的可以參考!
    發(fā)表于 07-30 16:02 ?19次下載
    STM32F4 <b class='flag-5'>I2C-EEPROM</b><b class='flag-5'>實(shí)驗(yàn)</b>例程

    51單片機(jī)實(shí)驗(yàn)12:EEPROM(IIC總線) 應(yīng)用

    51單片機(jī)實(shí)驗(yàn)12:EEPROM(IIC總線) 應(yīng)用
    發(fā)表于 11-23 16:36 ?21次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機(jī)</b><b class='flag-5'>實(shí)驗(yàn)</b>12:<b class='flag-5'>EEPROM</b>(IIC總線) 應(yīng)用

    單片機(jī)I/O口輸出控制實(shí)驗(yàn)

    單片機(jī)實(shí)驗(yàn)I/O口輸出控制實(shí)驗(yàn)一、實(shí)驗(yàn)目的1.熟悉PROTEUS單片機(jī)仿真軟件的使用。
    發(fā)表于 11-23 17:51 ?20次下載
    【<b class='flag-5'>單片機(jī)</b>】<b class='flag-5'>I</b>/O口輸出控制<b class='flag-5'>實(shí)驗(yàn)</b>

    基于51單片機(jī)的iic--24c02EEPROM讀寫程序

    基于51單片機(jī)的iic--24c02EEPROM讀寫例程源代碼
    發(fā)表于 05-12 16:44 ?0次下載

    基于51單片機(jī)的iic--24c02EEPROM讀寫例程源代碼

    基于51單片機(jī)的iic--24c02EEPROM讀寫例程源代碼
    發(fā)表于 05-18 09:55 ?3次下載

    CW32單片機(jī)I2C接口讀寫EEPROM芯片介紹

    CW32單片機(jī)I2C接口讀寫EEPROM芯片介紹
    的頭像 發(fā)表于 11-09 17:42 ?943次閱讀
    CW32<b class='flag-5'>單片機(jī)</b><b class='flag-5'>I2C</b>接口讀寫<b class='flag-5'>EEPROM</b>芯片介紹