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ī)的CPU、RAM、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
-
微控制器
+關(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
+關(guān)注
關(guān)注
28文章
1468瀏覽量
122773
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論