9.1 用I/O口模擬I2C總線實現AT24C16的讀寫 9.1.1、實例功能 I2C總線(Inter Integrate Circuit BUS)全稱為芯片間總線,是Philips公司推出的一種雙向二進制總線。它在芯片間以兩根連線實現全雙工同步數據傳送,一條數據線(SDA)和一條串行時鐘線(SCL),可以很方便的構成外圍器件擴展系統。 I2C總線協議允許總線介入多個期間,總線上的器件既可以作為主控制器也可以作為被控制器,既可以是發送器,也可以是接收器。I2C總線在進行數據交換時,作為主控制器的器件需要通過總線競爭獲得主控權,然后才可以啟動數據傳輸。系統中每個器件都具有唯一的芯片地址,數據傳輸時通過尋址可以確定數據接收方。 I2C總線自從出現以后,得到了廣泛應用。I2C總線結構簡單,可靠性和抗干擾性好,可構成各種通用的硬件和軟件模塊。方便重復利用,大大簡化了系統的設計過程。 I2C總線的實現有兩種方法:一、軟件模擬I2C通信協議實現數據傳輸,二、利用硬件I2C接口實現數據傳輸。 ATmega16單片機集成了硬件I2C模塊,稱為TWI接口,TWI電路結構簡單,只占用兩個I/O口,可以實現多個器件共享一條總線,使用比較方便,系統也很簡潔。AVR單片機用硬件實現了這種總線的時序,省去了很多編程工作。只要控制相關的寄存器,就可以實現通過TWI總線傳輸數據。 但是使用硬件I2C接口的缺點是接口固定,在特定的系統里面,會增加硬件和軟件設計的復雜程度。在本例中我們采用模擬I2C總線時序的方法實現I2C通信。軟件模擬I2C時序的方法增加了軟件的復雜程度,但是方便了硬件設計,模擬I2C接口可以使用單片機的如何普通I/O口。 本節首先介紹I2C總線的的一些基本知識:特點、結構、原理、控制時序、與單片機的接口方法等。最后通過一個實例實現模擬I2C接口。 本實例分為三個功能模塊,分別描述如下: ● 單片機系統:利用ATmega16單片機與AT24C16實現數據傳輸,利用模擬I2C總線接口的方法讀寫AT24C16。 ● 外圍電路:外圍電路分兩部分:LED顯示部分(用于指示從AT24C16中讀取的數值正確與否)、AT24C16接口電路電路(實現模擬I2C總線功能)。 ● 軟件程序:編寫軟件,實現對AT24C16的數據讀寫。 通過本實例的學習,掌握以下內容: ● 理解AT24C16的特點、結構和原理和接口設計方法。 ● 掌握AT24C16的控制時序和控制方法流程。 ● 掌握模擬I2C總線的設計方法。 9.1.2 器件和原理 1、I2C總線介紹 AT24C16的外形級封裝和引腳說明如圖9.1.1。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg 圖9.1.1 AT24C16的外形封裝和引腳說明 I2C總線協議規定,任何將數據傳送到總線的器件作為發送器。任何從總線接收數據的器件為接收器。主器件控制串行時鐘和起始、停止信號的發生。主器件何從期間都可以發送或接收數據,但是主器件控制數據傳送模式(發送或者接收)。 通過器件地址輸入端A0、A1、A2可以實現講最多8個at24c01器件和a424c02器件、4個at24c04器件、2個at24c08器件、1個at24c16器件連接到總線上。當總線上只有一個器件時,A0、A1、A2可以連接到地或者懸空。 WP寫保護引腳:當該引腳連接到VCC,I2C器件內的內容被寫保護(只能讀)。如果允許對器件進行正常的讀寫,那么WP引腳需連接到地或者懸空。 2、I2C總線接口 I2C總線的信號線有兩種: ●時鐘線SCL。 ●數據線SDA。 SCL和SDA都是雙向總線,I2C總線為同步傳輸串行總線結構,及總線上的數據信號完全與時鐘同步。數據傳輸采用主從方式:主器件尋址從器件,啟動總線數據傳輸,并產生時鐘脈沖。總線傳輸中的所有狀態及操作都有相應的編碼,主器件依照這些協議編碼自動地進行總線控制與管理。從器件接收主器件的請求并應答。數據傳輸結束后,主器件將總線釋放。 當總線空閑時,SCL和SDA均為高電平。連接到總線上的器件的輸出端口必須是漏極開路,任一器件輸出低電平時,總線信號變低。即總線SCL和SDA上的信號都是線“與”的關系。 由于SDA和SCL的端口輸出都是漏極開路,因此總線上必須連接上拉電阻。上拉電阻的大小與電源電壓、傳輸速率等有關系。當傳輸速率為100KHz時,上拉電阻一般采用10K,對于400KHz的傳輸速率,上拉電阻可以采用2K歐姆。 9.1.3、I2C總線的尋址方式 I2C總線上的器件都是共用總線的,因此,主器件在進行數據傳輸前必須選擇需要通信的從器件。即進行總線尋址。 I2C總線上所有外圍器件都有唯一的地址,這個地址由器件地址和引腳地址兩部分組成。共7位。器件地址是I2C器件固有的地址編碼,器件出廠時已經給定,不可更改。引腳地址由I2C總線外圍器件的地址引腳A0、A1、A2決定,根據其在電路中接電源正極、接地或者懸空的不同,形成不同的地址代碼。引腳地址數也決定了同一器件可接入總線的最大數目。 地址位與一個方向位共同構成I2C總線器件尋址字節。尋址字節的格式如圖所示;方向位(R/W)規定了總線上主器件與外圍器件(從器件)的數據傳輸方向。當方向位R/W=1時,表示主器件讀取從器件的數據;R/W=0時,表示主器件向從器件發送數據。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image004.jpg 9.1.4、I2C總線的數據傳輸協議 I2C總線的數據傳輸遵循嚴格的時序格式,下面分別介紹數據傳輸過程中的格式。 1、起始信號、終止信號 在時鐘線SCL為高電平期間,數據線SDA上出現高電平向低電平變化的下降沿時,被認為是起始信號。起始信號出現以后,后面才可以進行尋址或數據傳輸等。 在時鐘信號SCL高電平期間,數據線SDA上出現由低電平到高電平變化的上升沿時,被認為是終止信號。終止信號一出現,所有總線操作都結束,主器件釋放總線控制權。 起始信號、終止信號的時序如下圖所示。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg 2、數據讀寫 當SCL為高電平期間,SDA上的數據必須保持不變,如果此時SDA上的電平發生變化,則會被認為是起始或者終止信號。只有在SCL為低電平期間,SDA上的數據才能發生變化。所以在進行讀取SDA上的數據時,必須使SCL處于低電平。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image008.jpg 3、總線數據位 每次發送到I2C總線SDA上的數據必須是一個字節,傳輸的數據字節按照由高位到低位的順序發送。在I2C總先啟動后或應答信號后的第1-8個時鐘脈沖,對應于要傳送字節的8位數據。 I2C總線上的數據是伴隨著時鐘脈沖,一位一位的傳送的,每位數據占一個時鐘脈沖。在時鐘線SCL高電平期間,數據線SDA的狀態就表示要傳送的數據,高電平為數據1,低電平為數據0.在數據傳送時,數據線上數據的變化在時鐘線為低電平時完成;而時鐘線為高電平時,數據線必須保持穩定,否則數據線上的任何變化都被當成起始或者終止信號,從而導致數據傳輸停止。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image010.jpg 4、應答信號、非應答信號 I2C總線數據傳送時,每傳送一個字節數據后都必須有應答信號。應答信號由主器件產生。主器件在第9個時鐘位上釋放數據總線,使其處于高電平狀態,此時從器件輸出低電平拉低數據線產生應答信號。 在傳送完一個字節數據后,在第9個時鐘位上,從器件輸出高電平為非應答信號。非應答信號的產生有兩種情況: ● 當從器件正在進行其他處理而無法接收總線上的數據時,從器件不產生應答,此時從器件釋放總線,將數據線置為高電平。這樣,主器件可產生一個停止信號來終止總線傳輸數據。 ● 當主器件接收來自從器件的數據時,接收到最后一個字節數據后,必須給從器件發送一個非應答信號,使從器件釋放數據總線。這樣,主器件才可以發送終止信號,從而終止數據傳送。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg 5、數據傳送格式 I2C總線協議規定了完整的數據傳送格式。按照協議規定,數據的傳輸以主器件發送起始信號開始,然后發送尋址從器件的尋址字節。尋址字節共8位,前7位為被尋址的從器件(對于AT24C16來說,由于總線上只能連接1個AT24C16,所以對AT24C16的尋址字節,高4位固定為1010,1-3位則是其內部頁地址的高3位),第0位是方向位。在尋址字節后面跟著操作地址,操作地址指明了對被尋址器件內部的某一位或某幾位進行操作。操作地址之后跟的就是要傳輸的數據了,數據傳輸結束后,主器件發送一個終止信號以釋放總線控制權。 數據可以單字節傳輸,也可以多字節傳輸,如果主器件希望繼續占用總線,則可以不發送停止信號,馬上再次發送起始信號,便可以進行新的操作。 9.1.5 AT24C16的內部數據存儲結構 AT24C16內部有2048*8位的存儲容量,即可以存儲2K字節的數據。這2K字節被放在128個頁內,每頁存放16個字節。所以對AT24C16內部的訪問需要11位地址(0-7ff)。對AT24C16訪問時,按照頁地址和頁偏移量的方式進行訪問。比如要訪問第100頁的第3個字節,則在發送尋址的時候,就要發送0X0643,其中頁地址的高三位放在器件地址中。所以在編寫程序對AT24C16第100頁的第3個字節進行寫數據的時候,步驟如下:1)發送起始信號;2)發送器件地址0XA6(1010 0110,1010是固定地址,011是頁地址的高三位,0表示寫操作);3)發送操作地址0X43(0100 0011,0100是頁地址的低四位,0011是頁地址偏移量,即第100頁內的第三個字節,4)發送要寫的數據,5)發送終止信號。 9.1.6、電路和連接 LED發光二極管電路已經在第一個實例中介紹過,本例中不再重復。本例中AT24C16與單片機的連接如圖9.1.2所示,由于AT24C16的數據線、時鐘線要求空閑狀態為高電平,所以我在AT24C16的數據線和時鐘線上分別加了4.7K的上拉電阻,如果不想接上拉電阻的話,可以使能PA2口的內部上拉功能,但是程序設計會有些不同。AT24C16的數據線SDA、時鐘線SCL分別連到單片機的PC1、PC0口。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg 圖9.1.2 AT24C16電路 7.1.4、程序設計 1、程序功能 程序的功能是使用單片機的PC1、PC0口的模擬I2C總線時序實現對AT24C16的數據讀寫操作,然后用LED的亮滅指示讀取數據的正確性。 2函數說明 本程序多個功能函數,分別是: ● AT24C16操作相關函數: void I2C_Init(void); //I2C端口初始化 unsigned char I2C_Start(void); //發送起始信號 void I2C_Stop(void); //發送結束信號 unsigned char I2C_WriteByte(unsigned char dat); //寫一個字節 unsigned char I2C_ReadByte(unsigned char ack); //讀一個字節 unsigned char EEPROM_ReadByte(unsigned intadd); //從固定地址讀一字節 void EEPROM_WriteByte(unsigned int add,unsigned char data); //向固定地址寫一字節● 延時相關函數: void Delayus(unsigned intlus); //us延時函數 void Delayms(unsigned intlms); //ms延時函數 由于WINAVR自帶函數庫中的延時函數使用起來很不方便,并且晶振頻率不同,延時時間也有區別,所以本實例中自己寫了兩個延時函數。 3、使用WINAVR開發環境,使用的是外部12M的晶振,所以需要將makefile文件中的時鐘頻率修改為12M。另外在程序燒錄到單片機的時候,熔絲位也要選擇為外部12M晶振(注意是晶振,不是外部振蕩器,一定不要選擇錯了,否則會導致單片機不能再燒寫程序)。 4、程序說明。在本實例中我們首先了解了I2C總線的原理和特點,用模擬I2C總線接口的方式實現了對AT24C16的讀寫操作。 5、程序代碼 #include <avr/io.h> //io端口寄存器配置文件,必須包含 #include <util/delay.h> //端口聲明 /*注: AVR單片機I/O口模擬I2C總線時建議在外部連接上拉電阻 ,這樣可通過改變I/O口輸入輸出方向的方式 來設置高低電平, 輸出口保持不變(0) ,此時如DDRX寄存器為1則變成輸出0,若DDRX為0,則I/O口 呈現高阻狀態,但因外部的上拉電阻,總線相當于設置高電平,即通過設置DDRX的方式控制總線的高低 */ #define SCL_INPUT (DDRC &=~(1 << PC0)) //SCL設置為輸入口 #define SCL_OUTPUT (DDRC |= (1<< PC0)) //SCL設置為輸出口 #define SCL_LOW (PORTC &= ~(1<< PC0)) //SCL設置為輸出低電平 #define SCL_HIGH (PORTC |= (1<< PC0)) //SCL設置為輸出高電平 #define SCL_INDATA (PINC & (1<< PC0)) //讀取SCL的端口狀態 #define SDA_INPUT (DDRC &=~(1 << PC1)) //SDA設置為輸入口 #define SDA_OUTPUT (DDRC |= (1<< PC1)) //SDA設置為輸出口 #define SDA_LOW (PORTC &= ~(1<< PC1)) //SDA設置為輸出低電平 #define SDA_HIGH (PORTC |= (1<< PC1)) //SDA設置為輸出高電平 #define SDA_INDATA (PINC & (1<< PC1)) //讀取SDA的端口狀態 //變量聲明 #define EEPROM_BUS_ADDRESS0xa0 //器件地址 //函數聲明 void Delayus(unsigned intlus); //us延時函數 void Delayms(unsigned intlms); //ms延時函數 void I2C_Init(void); //I2C端口初始化 unsigned char I2C_Start(void); //發送起始信號 void I2C_Stop(void); //發送結束信號 unsigned char I2C_WriteByte(unsigned char dat); //寫一個字節 unsigned char I2C_ReadByte(unsigned char ack); //讀一個字節 unsigned charEEPROM_ReadByte(unsigned int add); //從固定地址讀一字節 void EEPROM_WriteByte(unsignedint add,unsigned char data); //向固定地址寫一字節 int main(void) //GCC中main文件必須為返回整形值的函數,沒有參數 { unsignedchar i; PORTB= 0x00; DDRB= 0xFF; //端口PortB設為輸出口,通過相應位LED的變化指示程序運行結果 I2C_Init(); //I2C端口初始化 EEPROM_WriteByte(0x01aa,0x5a); //向固定地址寫一字節,向第26頁的第十個字節寫入數據0x5a i= EEPROM_ReadByte(0x01aa); //從固定地址讀一字節 if(i== 0x5a) { PORTB|= 0x01; //讀出的數據正確,則LED0點亮 } else { PORTB|= 0x02; //讀出的數據不正確,則LED1點亮 } while(1) { } } //I2C初始化函數 void I2C_Init(void) { SCL_LOW; //SCL的PORT狀態鎖定為0,以后不再改變 SCL_INPUT; //SCL設置為輸入口 SDA_LOW; //SDA的PORT狀態鎖定為0,以后不再改變 SDA_INPUT; //SDA設置為輸入口 Delayus(10); } //I2C起始條件 unsigned char I2C_Start(void) { Delayus(10); SDA_INPUT; //SDA高電平 Delayus(10); //延時一段時間,使單片機時鐘頻率符合I2C時鐘 SCL_INPUT; //SCL高電平 Delayus(10); SDA_OUTPUT; //SDA變低,產生由高到低的變化 Delayus(10); SCL_OUTPUT; //SCL變低,占用總線 Delayus(10); return1; } //I2C結束條件 void I2C_Stop(void) { Delayus(10); SDA_OUTPUT; //SDA低電平 Delayus(10); SCL_INPUT; //SCL高電平 Delayus(10); SDA_INPUT; //SDA變為高電平,產生由低到高的變化 Delayus(10); } //向I2C寫一個字節 unsigned char I2C_WriteByte(unsigned char dat) { unsignedchar i,ack; //ack為應答信號 for(i= 0;i < 8; i++) //寫8位(1個字節)數據 { if(dat& 0x80) //寫入數據,左移,從最高位寫入 { SDA_INPUT; //如果該位為1,SDA拉高電平 } else { SDA_OUTPUT; // 如果該位為0,SDA拉低電平 } SCL_INPUT; //SCL高電平,保持數據 Delayus(10); SCL_OUTPUT; //SCL低電平,數據被送入I2C dat<<= 1; //需要寫入的數據左移一位,送最高位 Delayus(10); // } Delayus(10); SDA_INPUT; //SDA拉高,同時變為輸入口 Delayus(10); SCL_INPUT; //SCL拉高,準備讀取應答信號 Delayus(10); if(SDA_INDATA) { ack= 0; //如果此時SDA為高,說明沒有應答信號 PORTB|= 0x04; //沒有應答信號,點亮LED2 } else { ack= 1; //如果此時SDA為低,說明有應答信號 PORTB|= 0x08; //有應答信號,點亮LED3 } SCL_OUTPUT; //SCL拉低 Delayus(10); returnack; //返回應答信號 } //從I2C讀一個字節 unsigned char I2C_ReadByte(unsigned char ack) { unsignedchar i,dat = 0; //dat為讀出的數據 SDA_INPUT; //SDA變為輸入口 for(i= 0;i < 8;i++) //讀出8位(1個字節)數據 { Delayus(10); SCL_OUTPUT; //SCL低,這時允許從I2C發送數據到SDA上 Delayus(10); SCL_INPUT; //SCL高,準備讀取SDA上的數據 Delayus(10); dat<<= 1; //讀取的數據左移一位,從最高位讀起 if(SDA_INDATA) { dat++; //如果DSA為高,則讀取的數據加1 } Delayus(10); } SCL_OUTPUT; //SCL拉低,準備下一個數據變化 Delayus(10); if(!ack) // { SDA_INPUT; //發送NACK } else { SDA_OUTPUT; //發送ACK } Delayus(10); SCL_INPUT; //SCL高 Delayus(10); SCL_OUTPUT; //SCL低 Delayus(10); return(dat); //返回讀到的數據 } //從固定地址讀一字節 unsigned charEEPROM_ReadByte(unsigned int add) { unsignedchar data; I2C_Start(); //發送起始信號 I2C_WriteByte(EEPROM_BUS_ADDRESS | ((add>> 8) << 1)); //寫器件地址和頁地址的高3位 I2C_WriteByte(add); //寫頁地址的低4位和頁地址內部偏移量 I2C_Start(); //發送起始信號 I2C_WriteByte(EEPROM_BUS_ADDRESS | 0x01); //發送讀命令 data= I2C_ReadByte(0); //讀一個字節 I2C_Stop(); //發送結束信號 returndata; } //向固定地址寫一字節 void EEPROM_WriteByte(unsignedint add,unsigned char data) { I2C_Start(); //發送起始信號 I2C_WriteByte(EEPROM_BUS_ADDRESS | ((add>> 8) << 1)); //寫器件地址和頁地址的高3位 I2C_WriteByte(add); //寫頁地址的低4位和頁地址內部偏移量 I2C_WriteByte(data); //寫一個字節數據 I2C_Stop(); //發送結束信號 Delayms(10); } //us級別的延時函數 void Delayus(unsigned int lus) { while(lus--) { _delay_loop_2(3); //_delay_loop_2(1)是延時4個時鐘周期,參數為3則延時12 //個時鐘周期,本實驗用12M晶體,則12個時鐘周期為12/12=1us } } //ms級別的延時函數 void Delayms(unsigned int lms) { while(lms--) { Delayus(1000); //延時1ms } } 9.2 基于硬件接口的AT24C02的讀寫實驗 9.2.1、實例功能 AVR單片機提供了實現標準2先串行總線通信TWI(兼容I2C總線)硬件接口。其主要性能和特點有: ● 簡單,但是強大而靈活的串行通信接口,只需要兩根線; ● 支持主機和從機操作; ● 器件可以作為發送器,也可以作為接收器; ● 7位地址空間,最大允許有128個從機; ● 支持多主機模式; ● 最高可達400K的數據傳輸率; ● 全可編程的從機地址 ● 地址監聽中斷可以是AVR單片機從休眠狀態喚醒。 AVR的TWI是支持I2C通信的硬件接口,使用硬件接口的優點是可以直接使用硬件接口完成I2C通信,而不必使用I/O口模擬I2C的時序。比軟件模擬要簡單,代碼短,效率高。 本實例包括三個功能模塊,分別介紹如下: ● 單片機系統:利用ATmega16單片機與AT24C16通信,實現數據的讀寫,并使用LED指示讀寫數據是否正確。 ● 外圍電路:外圍電路分兩部分:LED顯示電路(實現指示讀寫數據是否正確)、AT24C16通信電路(實現對AT24C16的讀寫操作)。 ● 軟件程序:編寫軟件,實現利用AVR的硬件I2C接口對AT24C16進行數據讀寫數據功能。 通過本實例的學習,掌握以下內容: ● 理解AVR單片機硬件I2C接口的特點、原理。 ● 掌握AVR單片機硬件I2C接口的控制時序和控制方法流程。 ● 掌握使用查詢方法對AT24C16進行操作的軟件編程。 9.2.2 器件和原理 上一示例中我們已經對I2C總線的特點、原理進行了了解,并且通過利用I/O口模擬I2C總線時序的方法實現對AT24C16的讀寫。 在本例中我們學習使用AVR提供的硬件I2C接口。 1、TWI模塊描述 AVR的TWI模塊由總線接口單元、比特率發生器、地址匹配單元和控制單元等模塊構成。 ● SDA和SCL引腳 SDA和SCL是AVR單片機TWI接口的引腳。引腳的輸出驅動器包含一個波形斜率限制器,以滿足TWI規范;引腳的輸入部分包含尖峰抑制但愿,以去除小于50ns的毛刺。 ●波特率發生器 TWI工作在主控器模式時,又該控制單元產生TWI時鐘信號,并驅動時鐘線SCL, ●總線接口單元 這個單元包括:數據和地址移位寄存器、起始、停止信號控制和總線仲裁判定的硬件電路。 ● 地址匹配單元 地址匹配單元將檢測總線上接收到的地址是否與TWAR寄存器中的7位地址相匹配。如果匹配成功,將通知控制單元轉入適當的操作狀態。TWI可以響應,也可以不響應主控器對其的尋址訪問。 ● 控制單元 控制單元監聽TWI總線,并根據TWI控制寄存器的設置作出相應的響應。 當在TWI總線上產生需要應用程序干預處理的事件時,先對TWI的中斷標志位TWINT進行相應設置,在下一個時鐘周期時,將表示這個事件的狀態字寫入TWI狀態寄存器TWSR中。在其他情況下,TWSR中的內容為一個表示無事件發生的狀態字。一旦TWINT標志位置1,就會將時鐘線SCL拉低,暫停TWI總線上的傳送,讓用戶程序處理事件。 在下列事件出現時,TWINT標志位設為“1”: ● 在TWI傳送完一個起始或再次起始(START/RESTART)信號后; ● 在TWI傳送完一個主控器尋址讀/寫(SLA+R/W)數據后; ● 在TWI傳送完一個地址字節后; ● 在TWI丟失總線控制權后; ● 在TWI被主控器尋址(地址匹配成功)后; ● 在TWI接收到一個數據字節后; ● 在作為被控器時,TWI接收到停止或再次起始信號后; ● 由于非法的起始或停止信號造成總線上的沖突出錯時。 2、TWI寄存器 1)、波特率寄存器 TWBR file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image016.jpg TWBR用于設置波特率發生器的分頻因子,波特率發生器是一個頻率分頻器,當工作在主控器模式下,它產生和提供SCL引腳上的時鐘信號。 在主機模式下,TWBR的值應大于10,否則可能會產生不正確的輸出。尤其在中斷模式下,TWBR的值應大于10。 2)、控制寄存器 TWCR file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image018.jpg TWCR用來控制TWI操作。例如使能TWI接口;在總線上加起始和終止信號;產生ACK應答等。 ● 位7–TWINT:TWI中斷標志位。當TWI接口完成當前工作并期待應用程序響應時,該位被置位。如果全局中斷控制位和TWCR寄存器中的TWIE位都置位,則MCU將跳到TWI中斷向量處。一旦TWINT標志位被置位,,時鐘線SCL將被拉位低。在執行中斷服務程序時,TWINT標志位不會由硬件自動清零,必須通過由軟件將該位寫為“1”來清零,清零TWINT標志位將開始TWI接口的操作,因此對TWI寄存器TWAR、TWI狀態寄存器TWSR、TWI數據寄存器TWDR的訪問,必須在清零TWINT標志位前完成。 ● 位6—TWEA:TWI應答(ACK)允許位。TWEA位控制應答ACK信號的發生。如果TWEA位置1,則在器件作為主控器接收器時,接收到一個數據字節時, ACK脈沖將在TWI總線上發生。如果清零TWEA位,將使器件暫時虛擬地脫離TWI總線。 ● 位5—TWSTA:TWI起始(START)信號狀態位。當要將器件設置為串行總線上的主控器時,須設置TWSTA位為1.TWI借口硬件將檢查總線是否空閑。如果總線空閑,將在總線上發送一個起始信號;如果總線不空閑,則TWI將等待總線上一個停止信號被檢測后,再發出一個新的起始信號,以獲得總線的控制權而成為主控器。當起始信號發出后,硬件將自動清零TWSTA位。 ● 位4—TWSTO:TWI停止(STOP——信號狀態位。當芯片工作在主控模式時,設置TWSTO位為1,將在總線上產生一個終止信號。當終止信號發出后,TWSTO位將被自動清零。 ● 位3—TWWC:TWI寫沖突標志位。當TWINT為0時,試圖向TWI寫數據,TWWC位將被置1;當TWINT位為1時,寫數據時,TWWC由硬件自動清零。 ● 位2—TWEN:TWI允許位。TWEN用于使能TWI接口操作和激活TWI接口。該位置1,則TWI接口模塊將I/O引腳PC0、PC1轉換為SCL和SDA引腳。如果該位清零,則TWI模塊將被關閉,所有TWI傳輸將被終止,PC0、PC1轉換為普通I/O引腳。 ● 位1—保留位。讀出總為0; ● 位0—TWIE:TWI中斷使能位。當該位為1,并且全局終端也使能時,只要TWINT標志為1,TWI中斷請求就使能。 4)狀態寄存器 TWSR file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image020.jpg ● 位【7:3】--TWS:TWI狀態位。這5位反映了TWI邏輯狀態和TWI總線的狀態。不同的狀態碼表示不同的操作狀態,具體可查詢數據手冊獲得。注意,從TWSR寄存器中讀取的值包括了5位狀態值和2位預分頻值。因此,在檢查狀態位時,應該將預分頻器位屏蔽,使狀態檢驗與預分頻器無關。 ● 位2—保留。讀出始終為0; ● 位【1:0】--TWPS:TWI預分頻器位。這些位能讀能寫,用于設置波特率的預分頻率。如下表所示。 file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image022.jpg 5)數據寄存器 TWDR file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image024.jpg 在發送模式下,TWDR寄存器的內容為下一個要傳送的字節;在接受模式下,TWDR寄存器的內容為最后接受的字節。當TWI不處在字節移位操作過程時,該寄存器可以被讀寫即當TWI中斷標志位置位時,該寄存器可以被寫入。在第一次TWI中斷發生前,數據寄存器不能由用戶初始化。 6)(被控器)地址寄存器TWAR file:///C:/Users/m/AppData/Local/Temp/msohtmlclip1/01/clip_image026.jpg TWAR寄存器高7位的內容為被控器的7位地址字。當TWI設置為被控接收器或被控發送器時,在TWAR中應設置被控器尋址地址。而在主控器模式下,不需要設置TWAR。 9.2.3、電路連接 本例中的電路連接與上一實例完全相同,在此不再說明。 需要提醒的是,在AT24C16的接口電路中,我們在電路板上增加了兩個上拉電阻,這兩個上拉電阻分別接在SCL和SDA引腳上。 9.2.4 利用硬件TWI接口實現I2C總線的讀寫 1、程序功能 程序的功能是使用單片機的硬件TWI接口實現I2C總線的讀寫操作,然后將讀出數據通過LED指示正確與否。硬件TWI接口可以使用查詢和中斷兩種方式實現I2C總線的讀寫,在本實例中我們使用查詢法實現,下一實例中我們再討論使用中斷方式的操作。 2函數說明 本程序多個功能函數,分別是: ● TWI硬件i2c總線操作相關函數: void I2C_Init(void); //I2C端口初始化 unsigned char I2C_Start(void); //發送起始信號 void I2C_Stop(void); //發送結束信號 unsigned char I2C_WriteByte(unsigned char dat); //寫一個字節 unsigned char I2C_ReadByte(unsigned char ack); //讀一個字節 unsigned char EEPROM_ReadByte(unsigned intadd); //從固定地址讀一字節 void EEPROM_WriteByte(unsigned intadd,unsigned char data); //向固定地址寫一字節 void EEPROM_ReadPage(unsigned intadd,unsigned char n,unsigned char *data); //多字節讀操作 void EEPROM_WritePage(unsigned intadd,unsigned char n,unsigned char *data); //多字節寫操作 ● 延時相關函數: void Delayus(unsigned intlus); //us延時函數 void Delayms(unsigned int lms); //ms延時函數 由于WINAVR自帶函數庫中的延時函數使用起來很不方便,并且晶振頻率不同,延時時間也有區別,所以本實例中自己寫了兩個延時函數。 3、使用WINAVR開發環境,使用的是外部12M的晶振,所以需要將makefile文件中的時鐘頻率修改為12M。另外在程序燒錄到單片機的時候,熔絲位也要選擇為外部12M晶振(注意是晶振,不是外部振蕩器,一定不要選擇錯了,否則會導致單片機不能再燒寫程序)。 4、程序說明 在本實例中我們不但實現了使用AVR單片機的硬件TWI接口,通過查詢方法實現了對AT24C16的讀寫,并且通過控制LED的亮滅來指示讀出的數據是否正確。 在程序中我們使用查詢法實現對AT24C16的讀寫,寫操作的具體步驟是: 1)TWI寄存器配置; 2)發送START信號; 3)輪詢等待TWINT置位,TWINT置位表示START信號已發出; 4)發送寫器件地址,到TWDR寄存器,清零TWINT標志位,等待TWINT再次置位,再次置位表示數據已發送完成; 5)讀取總線狀態是否是器件地址發送完成并收到ACK; 6)發送數據地址,并清零TWINT標志位,然后等待TWINT再次置位,再次置位表示數據已發送完成; 7)讀取總線狀態是否是器件地址發送完成并收到ACK; 8)發送數據字節,并清零TWINT標志位,然后等待TWINT再次置位,再次置位表示數據已發送完成; 9)讀取總線狀態是否是器件地址發送完成并收到ACK; 10)數據寫操作結束,發送終止信號。 讀操作的具體步驟是: 1)TWI寄存器配置; 2)發送START信號; 3)輪詢等待TWINT置位,TWINT置位表示START信號已發出; 4)發送寫器件地址,到TWDR寄存器,清零TWINT標志位,等待TWINT再次置位,再次置位表示數據已發送完成; 5)讀取總線狀態是否是器件地址發送完成并收到ACK; 6)發送數據地址,并清零TWINT標志位,然后等待TWINT再次置位,再次置位表示數據已發送完成; 7)讀取總線狀態是否是器件地址發送完成并收到ACK; 8)發送重復開始(RESTART)信號,并清零TWINT標志位,然后等待TWINT再次置位,再次置位表示發送重復開始信號完成; 9)發送讀數據信號,并等待發送完成ACK,判斷總線狀態是否正確; 10)讀取TWDR的值,并根據是否讀取完最后一個字節,發送ACK或NACK; 11)數據讀取完畢,發送終止信號。 5、程序代碼 由于本實例的程序量比較大,這里就不再列出了,直接放在附件里面。 9.3 使用中斷方法實現TWI總線的讀寫 9.3.1、實例功能 在前面的實例中我們分別學習了使用模擬I2C總線和硬件TWI接口實現對AT24C16的讀寫,這兩種方法都存在編程比較復雜,代碼執行效率低等缺點。在本實例中我們將學習使用中斷方法實現對AT24C16的讀寫;中斷方法的優點是執行效率高,不占用CPU過多時間,從而使CPU能有更多的時間處理別的任務,非常適合實時多任務系統中使用。 本實例包括三個功能模塊,分別介紹如下: ● 單片機系統:利用ATmega16單片機的硬件TWI接口實現對AT24C16的讀寫,并將讀取出來的數據通過串口發送到計算機,同時利用LED指示讀出的數據是否正確。 ● 外圍電路:外圍電路分三部分:串口電路部分(實現將讀取的數值送到計算機的功能)、LED顯示電路(指示讀取的數據是否正確)、AT24C16接口電路(實現對AT24C16的讀寫)。 ● 軟件程序:編寫軟件,實現使用中斷方法對AT24C16進行數據的讀寫。 通過本實例的學習,掌握以下內容: ● 理解使用中斷方法實現TWI總線的接口設計方法和程序設計。 9.3.2、器件和原理 前面的實例中我們已經了解了TWI總線的使用方法以及I2C總線的通信協議和操作時序,在此我們不再重復。 9.3.3、電路連接 本實例的電路由三部分:串口轉換電路、LED顯示電路、AT24C16接口電路。這些電路我們在前面已經介紹過,在此不再說明。 需要提醒的是,在AT24C16的接口電路中,我們在電路板上增加了兩個上拉電阻,這兩個上拉電阻分別接在SCL和SDA引腳上。 9.3.4 程序設計 1、程序功能 程序的功能是使用單片機的TWI硬件接口,實現在中斷方式下對AT24C16的讀寫,然后將讀出數據通過串口發送到計算機,并且在電路板上通過LED的亮滅指示讀取的數據是否正確。 2函數說明 本程序多個功能函數,分別是: ● 端口初始化函數,設置各端口的初始工作狀態。 ● 串口通信相關函數: void Usart_Init(void); //USART寄存器設置 void Usart_PutChar(unsigned charcTXData); //字節發送函數 這些函數已經在前面的實例中做過介紹,在此不再重復。 ● AT24C16操作相關函數: void I2C_Init(void); //I2C端口初始化 unsigned char EEPROM_ReadWrite(unsigned charI2C_Add,unsigned int add, unsignedchar n,unsigned char *data); //讀寫數據操作,參數分別是:器件地址,數據地址,數據長度,讀寫的數據 ● 延時相關函數: void Delayus(unsigned intlus); //us延時函數 void Delayms(unsigned intlms); //ms延時函數 由于WINAVR自帶函數庫中的延時函數使用起來很不方便,并且晶振頻率不同,延時時間也有區別,所以本實例中自己寫了兩個延時函數。 3、使用WINAVR開發環境,使用的是外部12M的晶振,所以需要將makefile文件中的時鐘頻率修改為12M。另外在程序燒錄到單片機的時候,熔絲位也要選擇為外部12M晶振(注意是晶振,不是外部振蕩器,一定不要選擇錯了,否則會導致單片機不能再燒寫程序)。 4、程序說明 在本實例中我們不但實現了使用AVR單片機的硬件TWI接口,通過中斷方法實現了對AT24C16的讀寫,并且將讀出的數據通過串口發送到計算機進行顯示,同時通過控制LED的亮滅來指示讀出的數據是否正確。 使用中斷方法的處理步驟和查詢法的步驟基本一致,不同之處在于在中斷方式下不用使程序在等待中斷標志位置位時一直處于等待狀態,而是在中斷標志置位后自動進入中斷處理函數。
|