I2C簡(jiǎn)介
I2C總線是由Philips公司開(kāi)發(fā)的一種簡(jiǎn)單、雙向二線制同步串行總線。它只需要兩根線即可在連接于總線上的器件之間傳送信息。
主器件用于啟動(dòng)總線傳送數(shù)據(jù),并產(chǎn)生時(shí)鐘以開(kāi)放傳送的器件,此時(shí)任何被尋址的器件均被認(rèn)為是從器件.在總線上主和從、發(fā)和收的關(guān)系不是恒定的,而取決于此時(shí)數(shù)據(jù)傳送方向。如果主機(jī)要發(fā)送數(shù)據(jù)給從器件,則主機(jī)首先尋址從器件,然后主動(dòng)發(fā)送數(shù)據(jù)至從器件,最后由主機(jī)終止數(shù)據(jù)傳送;如果主機(jī)要接收從器件的數(shù)據(jù),首先由主器件尋址從器件.然后主機(jī)接收從器件發(fā)送的數(shù)據(jù),最后由主機(jī)終止接收過(guò)程。在這種情況下.主機(jī)負(fù)責(zé)產(chǎn)生定時(shí)時(shí)鐘和終止數(shù)據(jù)傳送。
I2C:就好像上下級(jí)對(duì)話。一個(gè)領(lǐng)導(dǎo)面對(duì)一個(gè)或者多個(gè)員工。只有領(lǐng)導(dǎo)主動(dòng)說(shuō)話的份兒,下面的員工不能主動(dòng)說(shuō)話。只有領(lǐng)導(dǎo)問(wèn)了,員工才能答。
I2C通信只需要兩個(gè)引腳 一個(gè)數(shù)據(jù)線,一個(gè)時(shí)鐘線。 數(shù)據(jù)線顧名思義就是用來(lái)傳遞數(shù)據(jù)的。時(shí)鐘線是來(lái)決定數(shù)據(jù)傳輸?shù)乃俣取.?dāng)時(shí)鐘線為高電平時(shí),數(shù)據(jù)線上的數(shù)據(jù)才會(huì)被認(rèn)為是有效的。數(shù)據(jù)線的 數(shù)據(jù)有四種狀態(tài) : 高電平,低電平,下降沿(高電平變低電平),上升沿(低電平變高電平)。當(dāng)時(shí)鐘線為高電平時(shí)候這四種狀態(tài)分別代表:1,0,起始位,停止位。
I2C的從模式與主模式的區(qū)別
宏觀上來(lái)講,主模式:就是主CPU作為主機(jī),向從機(jī)(掛載器件)發(fā)送接收數(shù)據(jù)。
從模式:就是主CPU作為從機(jī),接收和發(fā)送主機(jī)(掛載器件)數(shù)據(jù)。而主從機(jī)的分別其實(shí)是一個(gè)觸發(fā)的作用,主機(jī)主動(dòng)觸發(fā),從機(jī)只能被動(dòng)響應(yīng)觸發(fā)。
I2C(Inter-Integrated Circuit)總線是由PHILIPS公司開(kāi)發(fā)的兩線式串行總線,用于連接微控制器及其外圍設(shè)備。是微電子通信控制領(lǐng)域廣泛采用的一種總線標(biāo)準(zhǔn)。它是同步通信的一種特殊形式,具有接口線少,控制方式簡(jiǎn)單,器件封裝形式小,通信速率較高等優(yōu)點(diǎn)。
I2C 總線支持任何IC 生產(chǎn)過(guò)程(CMOS、雙極性)。通過(guò)串行數(shù)據(jù)(SDA)線和串行時(shí)鐘 (SCL)線在連接到總線的器件間傳遞信息。每個(gè)器件都有一個(gè)唯一的地址識(shí)別(無(wú)論是微控制器——MCU、LCD 驅(qū)動(dòng)器、存儲(chǔ)器或鍵盤(pán)接口),而且都可以作為一個(gè)發(fā)送器或接收器(由器件的功能決定)。LCD 驅(qū)動(dòng)器只能作為接收器,而存儲(chǔ)器則既可以接收又可以發(fā)送數(shù)據(jù)。除了發(fā)送器和接收器外,器件在執(zhí)行數(shù)據(jù)傳輸時(shí)也可以被看作是主機(jī)或從機(jī)。主機(jī)是初始化總線的數(shù)據(jù)傳輸并產(chǎn)生允許傳輸?shù)臅r(shí)鐘信號(hào)的器件。此時(shí),任何被尋址的器件都被認(rèn)為是從機(jī)。
PIC單片機(jī)之I2C(從模式)
介紹完了我們就來(lái)看看PIC單片機(jī)使用MSSP模塊實(shí)現(xiàn)I2C從模式。
模式單片機(jī)的數(shù)據(jù)。
下面為AT24C02的隨機(jī)地址讀取的協(xié)議。
第一個(gè)字節(jié) :輸入7位地址和一位的寫(xiě)狀態(tài)位,
第二個(gè)字節(jié):然后寫(xiě)入EEPROM數(shù)據(jù)地址,
第三個(gè)字節(jié):輸入7位地址和一位的讀狀態(tài)位,
第四~N個(gè)字節(jié):讀出的EEPROM的數(shù)據(jù)。
我們來(lái)講解下程序的基本思路:我們使能了MSSP中斷,即是I2C接收中斷,當(dāng)PIC單片機(jī)接收到一個(gè)數(shù)據(jù)后就會(huì)產(chǎn)生中斷。那是接收到設(shè)備地址,還是接收到數(shù)據(jù),由SSP1STAT寄存器的狀態(tài)位來(lái)判斷。
需要判斷的狀態(tài)位分別是 :
數(shù)據(jù)和地址: 用來(lái)判斷接收到是地址還是數(shù)據(jù)
啟動(dòng)位: 用來(lái)判斷是否接收到啟動(dòng)位
讀寫(xiě): 用來(lái)判斷是寫(xiě)狀態(tài)還是讀狀態(tài)。
緩存滿: 用來(lái)判斷緩沖區(qū)是否滿
我們以隨機(jī)地址讀取為例:講講程序執(zhí)行的過(guò)程
1,從單片機(jī)接收到啟示位和設(shè)備地址中斷:我們判斷SSP1STAT的狀態(tài)位為(寫(xiě)狀態(tài),地址,緩存滿,接收到啟示位) 然后讀取緩存中的設(shè)備地址, 接著在讀取 需要讀/寫(xiě)的數(shù)據(jù)地址。
2,單片機(jī)再次接收到設(shè)備地址:我們判斷是SSP1STAT的狀態(tài)為(讀狀態(tài))然后從設(shè)備就輸出數(shù)據(jù)
我們以寫(xiě)字節(jié)數(shù)據(jù)為例:
1,從單片機(jī)接收到啟示位和設(shè)備地址中斷:我們判斷SSP1STAT的狀態(tài)位為(寫(xiě)狀態(tài),地址,緩存滿,接收到啟示位) 然后讀取緩存中的設(shè)備地址, 接著在讀取 需要讀/寫(xiě)的數(shù)據(jù)地址。
2,單片機(jī)判斷SSP1STAT的狀態(tài)位為(寫(xiě)狀態(tài),數(shù)據(jù),緩存滿)那么單片機(jī)就接收輸入的數(shù)據(jù)。
初始化設(shè)置:
1,設(shè)置I2C通信的兩引腳為CLK SCL為輸入,
TRISB6 = input;
TRISB4 = input;
2,將MSSP設(shè)置為I2C從模式,七位從地址
SSP1CONbits.SSPM0 = 0;
SSP1CONbits.SSPM1 = 1;
SSP1CONbits.SSPM2 = 1;
SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address
3,使能CLK時(shí)鐘
SSP1CONbits.CKP = 1; // enable clock
4,設(shè)置從設(shè)備地址為 0xA0
SSP1ADD =0xA0; //slave address is 0xa0
5,開(kāi)啟I2C
SSP1CONbits.SSPEN=1;//enable I2c
6,清楚狀態(tài)標(biāo)志
SSPSTAT=0;
7,使能I2C中斷
PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
如果你要使用PIC單片機(jī)I2C從模式只要使用下面的代碼:
將void i2c_salve_interrupt_tx();void i2c_salve_interrupt_rx();放到中斷程序中,如下:
void interrupt isr(void)
{
if(SSP1IE && SSP1IF)
{
i2c_salve_interrupt_tx();
i2c_salve_interrupt_rx();
SSP1IF=0;
}
}
將初始化函數(shù)init_i2c_slave();放到主函數(shù)中
void main()
{
init_i2c_slave();
}
頭文件 :i2c_salve.h
#ifndef _I2C_SALVE_H
#define _I2C_SALVE_H
void init_i2c_slave();
void i2c_salve_interrupt_tx();
void i2c_salve_interrupt_rx();
#endif
代碼:i2c_salve.c
#include ;
#define input 1
#define RX_BUF_LEN 29
#define while_delay 6000
unsigned char i2c_address,word_address,Register[29];
unsigned char RANDOM_READ,i2c_counter;
extern unsigned char A_readflag;
/*I2C SALVE */
void init_i2c_slave()
{
TRISB6 = input;
TRISB4 = input;
SSP1CONbits.SSPM0 = 0;
SSP1CONbits.SSPM1 = 1;
SSP1CONbits.SSPM2 = 1;
SSP1CONbits.SSPM3 = 0;// I2C slave mode ,7bit address
SSP1CONbits.CKP = 1; // enable clock
SSP1ADD =0xA0; //slave address is 0xa0
SSP1CONbits.SSPEN=1;//enable I2c
SSPSTAT=0;
PIE1bits.SSP1IE = 1;//Enabe interrupt MSSP
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
}
/*I2C salve mode interrupt */
void i2c_salve_interrupt_tx()//master read
{
unsigned char Temp;
unsigned int timercounter;
Temp=SSP1STAT;
Temp &= 0x2D;
if(SSP1STATbits.R_nW ==1)//Read operation.
{
A_readflag=0;
SSP1IF = 0;
i2c_address = SSP1BUF;
i2c_counter = word_address;
while(i2c_counter 《 RX_BUF_LEN)
{
SSP1BUF=Register[i2c_counter];//send data
SSP1CONbits.CKP=1;// enable colck
timercounter=while_delay;
while(PIR1bits.SSP1IF == 0)
{
timercounter--;
if(timercounter==0)
{
return;
}
SSP1IF = 0;
if(SSP1CON2bits.ACKSTAT == 1)
{
return ; //NOACK
}
else
{
i2c_counter++;//ACK
}
}
SSP1IF = 0;
}
}
void i2c_salve_interrupt_rx()//master writer
{
unsigned char rx_status;
unsigned char Temp;
unsigned int timercounter;
rx_status=false;
Temp=SSP1STAT;
Temp &= 0x2D;
if(Temp==0x09)//Write operation,last byte was an address,buffer is full
{
SSP1IF = 0;
i2c_address = SSP1BUF;
timercounter=while_delay;
while(PIR1bits.SSP1IF == 0)
{
timercounter--;
if(timercounter==0)
{
return ;
}
}//waiting for send ~ACK
SSP1IF = 0;
word_address = SSP1BUF;
return ;
}
if(Temp==0x29)//Write operation,last byte was data,buffer is full
{
SSP1IF=0;
Register[word_address]=SSP1BUF;
word_address++;
if(word_address》=RX_BUF_LEN)
{
word_address=0;
}
}
}
評(píng)論
查看更多