大家好,通過前一期的學(xué)習(xí),我們已經(jīng)對ICD2 仿真燒寫器和增強(qiáng)型PIC 實(shí)驗(yàn)板的使用方法及學(xué)習(xí)方式有所了解與熟悉,學(xué)會(huì)了如何用單片機(jī)來控制發(fā)光管、繼電器、蜂鳴器、按鍵、數(shù)碼管、RS232 串口、步進(jìn)電機(jī)、溫度傳感器等資源,體會(huì)到了學(xué)習(xí)板的易用性與易學(xué)性,看了前幾期實(shí)例,當(dāng)你實(shí)驗(yàn)成功后一定很興奮,很有成就感吧!現(xiàn)在我們就趁熱打鐵,再向上跨一步,一起來學(xué)習(xí)一下I2C 總線的工作原理及使用方法,這樣我們可以將一些我們要保存的數(shù)據(jù)存儲(chǔ)到I2C總線的非易失存儲(chǔ)器中,實(shí)現(xiàn)斷電保持的功能,比如:你設(shè)置了一個(gè)密碼,但不至于這個(gè)設(shè)備斷過電以后就要重新設(shè)置過,我們可以將密碼數(shù)據(jù)寫在非易失存儲(chǔ)器里面,還有如汽車的量程表的讀數(shù)是不斷累計(jì)的,可以通過不斷訪問I2C 存儲(chǔ)器實(shí)現(xiàn)。 一、I2C總線特點(diǎn) I2C 總線是主從結(jié)構(gòu),單片機(jī)是主器件,存儲(chǔ)器是從器件。一條總線可以帶多個(gè)從器件( 也可以有多主結(jié)構(gòu)),I2C 總線的SDA 和SCL 是雙向的,開路門結(jié)構(gòu),通過上拉電阻接正電源。進(jìn)行數(shù)據(jù)傳輸時(shí),SDA 線上的數(shù)據(jù)必須在時(shí)鐘的高電平周期保持穩(wěn)定。數(shù)據(jù)線的高或低電平狀態(tài)只有在SCL 線的時(shí)鐘信號是低電平時(shí)才能改變,如圖1 所示。 圖1 數(shù)據(jù)位的有效性規(guī)定 在SCL 線是高電平時(shí),SDA 線從高電平向低電平切換表示起始條件;當(dāng)SCL 是高電平時(shí)SDA 線由低電平向高電平切換表示停止條件如圖2 所示。 圖2 起始和停止信號 發(fā)送到SDA 線上的每個(gè)字節(jié)必須為8 位。 可以由高位到低位傳輸多個(gè)字節(jié)。每個(gè)字節(jié)后必須跟一個(gè)響應(yīng)位(ACK)。響應(yīng)時(shí)鐘脈沖由主機(jī)產(chǎn)生。主機(jī)釋放SDA 線從機(jī)將SDA 線拉低,并在時(shí)鐘脈沖的高電平期間保持穩(wěn)定。如圖3 示。當(dāng)主機(jī)接受數(shù)據(jù)時(shí),它收到最后一個(gè)數(shù)據(jù)字節(jié)后,必須向從機(jī)發(fā)出一個(gè)結(jié)束傳送的信號。這個(gè)信號是由主機(jī)對從機(jī)的“非應(yīng)答”來實(shí)現(xiàn)的。然后,從機(jī)釋放SDA 線,以允許主機(jī)產(chǎn)生終止或重復(fù)起始信號。 圖3 字節(jié)格式與應(yīng)答 二、數(shù)據(jù)幀格式 (1)主機(jī)向從機(jī)發(fā)送數(shù)據(jù),數(shù)據(jù)的傳送方向在傳輸過程中不改變,如圖4 所示。 圖4 主機(jī)向從機(jī)發(fā)送數(shù)據(jù) 注:陰影部分:表示主機(jī)向從機(jī)發(fā)送數(shù)據(jù);無陰影部分:表示主機(jī)向從機(jī)讀取數(shù)據(jù)。 A:表示應(yīng)答; (2)主機(jī)在第一個(gè)字節(jié)后,立即向從機(jī)讀取數(shù)據(jù),如圖5 所示。 圖5 主機(jī)在第一個(gè)字節(jié)后立即讀從機(jī) (3)復(fù)合格式,如圖6 所示。傳輸改變方向的時(shí)候,起始條件和從機(jī)地址都會(huì)被重復(fù),但R/ W-位取反。如果主機(jī)接收器發(fā)送一個(gè)停止或重復(fù)起始信號,它之前應(yīng)該發(fā)送了一個(gè)不響應(yīng)信號( 圖6 復(fù)合格式 由以上格式可見,無論哪種傳輸方式,起始信號、終止信號和地址均由主機(jī)發(fā)出(圖中陰影部分),數(shù)據(jù)字節(jié)的傳送方向則由尋址字節(jié)中的方向位規(guī)定,每個(gè)字節(jié)的傳送都必須有應(yīng)答位(A 或 下面通過24C02 實(shí)例在增強(qiáng)型PIC 實(shí)驗(yàn)板上編程,其硬件原理圖如圖7 所示,U7 為實(shí)驗(yàn)板上24C02 芯片,SDA 與單片機(jī)的RB5 口相連,SCL 與單片機(jī)RB4 相連,七段數(shù)碼管D5、D7、D8 組成了顯示單元,字形碼的數(shù)據(jù)通過RC 口送入,各數(shù)碼管的顯示片選信號分別不同的RA 口進(jìn)行控制。 圖7 讀/ 寫AT24C 系列存儲(chǔ)器原理圖 在MPLab IDE 軟件中新建工程,加入源程序代碼,同時(shí)進(jìn)行芯片型號的選擇和配置位的設(shè)置,我們實(shí)驗(yàn)所用的芯片型號為PIC16F877A。 編寫的程序代碼如下,其中程序流程圖如圖8 所示。 三、軟件流程圖 圖8 I2C 總線讀/ 寫數(shù)據(jù)流程圖 四、軟件代碼 /**********/ /* 目標(biāo)器件:PIC16F877A */ /* 晶振:4.0MHZ */ /* 編譯環(huán)境:MPLAB V7.51 */ /**********/ /********** 包含頭文件 **********/ #include /********** 數(shù)據(jù)定義 **********/ #define address 0xa #define nop() asm("nop") #define OP_READ 0xa1 // 器件地址以及讀取操作 #define OP_WRITE 0xa0 // 器件地址以及寫入操作 /********** 端口定義 **********/ #define SCL RB4 #define SDA RB5 #define SCLIO TRISB4 #define SDAIO TRISB5 /********** 共陰LED 段碼表 **********/ const char table[]={0xC0,0xF9,0xA4,0x B0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x 83,0xC6,0xA1,0x86,0x8E}; /********** 函數(shù)功能: 延時(shí)子程序 **********/ void delay() { int i; for(i=0;i<100;i++) {;} } /********** 函數(shù)功能: 開始信號 **********/ void start() { SDA=1; nop(); SCL=1; nop();nop();nop();nop();nop(); SDA=0; nop();nop();nop();nop();nop(); SCL=0; nop();nop(); } /********** 函數(shù)功能: 停止信號 **********/ void stop() { SDA=0; nop(); SCL=1; nop();nop();nop();nop();nop(); SDA=1; nop();nop();nop();nop(); } /********** 函數(shù)功能: 讀取數(shù)據(jù) 出口參數(shù):read_data **********/ unsigned char shin() { unsigned char i,read_data; for(i=0;i<8;i++) { nop();nop();nop(); SCL=1; nop();nop(); read_data《=1; if(SDA == 1) read_data=read_data+1; nop(); SCL=0; } return(read_data); } /********** 函數(shù)功能: 向EEPROM 寫數(shù)據(jù) 入口參數(shù):write_data 出口參數(shù):ack_bit **********/ bit shout(unsigned char write_data) { unsigned char i; unsigned char ack_bit; for(i = 0; i < 8; i++) { if(write_data&0x80) SDA=1; else SDA=0; nop(); SCL = 1; nop();nop();nop();nop();nop(); SCL = 0; nop(); write_data 《= 1; } nop();nop(); SDA = 1; nop();nop(); SCL = 1; nop();nop();nop(); ack_bit = SDA; // 讀取應(yīng)答 SCL = 0; nop();nop(); return ack_bit; // 返回AT24Cxx 應(yīng)答位 } /********** 函數(shù)功能: 向指定地址寫數(shù)據(jù) 入口參數(shù):addr,write_data **********/ void write_byte(unsigned char addr, unsigned char write_data) { start(); shout(OP_WRITE); shout(addr); SDAIO = 0; // 在寫入數(shù)據(jù)前SDA 應(yīng)設(shè)置為輸出 shout(write_data); stop(); delay(); } /********** 函數(shù)功能: 向指定地址讀數(shù)據(jù) 入口參數(shù):random_addr 出口參數(shù):read_data **********/ unsigned char read_random(unsigned char random_addr) { unsigned char read_data; start(); shout(OP_WRITE); shout(random_addr); start(); shout(OP_READ); SDAIO = 1; // 讀取數(shù)據(jù)前SDA 應(yīng)設(shè)置為輸入 read_data = shin(); stop(); return(read_data); } /********** 函數(shù)功能: 顯示子程序 入口參數(shù):k **********/ void display(unsigned char k) { TRISA=0X00; // 設(shè)置A 口全為輸出 PORTC=table[k/1000]; // 顯示千位 PORTA=0xEF; delay(); PORTC=table[k/100%10]; // 顯示百位 PORTA=0xDF; delay(); PORTC = table [k/ 10%10] ; // 顯示十位 PORTA=0xFB; delay(); PORTC=table[k%10]; // 顯示個(gè)位 PORTA=0xF7; delay(); } /********** 函數(shù)功能: 主程序 **********/ void main() { unsigned char eepromdata; TRISB=0X00; OPTION&=~(1《7); // 設(shè)置RB 口內(nèi)部上拉電阻有效 TRISC=0X00; PORTB=0X00; PORTC=0xff; TRISA=0X00; eepromdata=0; write_byte(0x01,0x55); // 向0x01 地址寫入0x55(85) 的數(shù)據(jù) delay(); write_byte(0x02,0xaa); // 向0x02 地址寫入0xAA(170) 的數(shù)據(jù) delay(); eepromdata=read_random(0x02); // 讀取其中一個(gè)地址內(nèi)的數(shù)據(jù)來驗(yàn)證 while(1) { display(eepromdata); } } 編好程序后將編譯好的HEX 碼通過ICD2仿真燒寫器燒入單片機(jī)芯片,上電運(yùn)行,主程序中在0x01 地址寫入了“0x55”, 在0x02 地址寫入了“0xaa”,然后在while 循環(huán)中讀出0x02地址的值,也就是我們之前寫入的“0x55”,讀出后顯示在數(shù)碼管上,我們可以看到數(shù)碼管顯示“170”,即“0xaa”相應(yīng)的十進(jìn)制數(shù)。 作為初學(xué)者的讀者一定對有些語句會(huì)有點(diǎn)疑問,可以看程序中的注釋部份,24c 系列IC 數(shù)據(jù)手冊和源程序相結(jié)合來進(jìn)行分析。 關(guān)鍵字:PIC單片機(jī) I2C總線 主從結(jié)構(gòu) 編輯:什么魚 引用地址:http://news./mcu/ic487155.html 本網(wǎng)站轉(zhuǎn)載的所有的文章、圖片、音頻視頻文件等資料的版權(quán)歸版權(quán)所有人所有,本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如果本網(wǎng)所選內(nèi)容的文章作者及編輯認(rèn)為其作品不宜公開自由傳播,或不應(yīng)無償使用,請及時(shí)通過電子郵件或電話通知我們,以迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失。 |
|