使用GPIO模擬I2C總線進行通信


I2C總線的通信過程(見圖4-8)主要包含三個主要階段:起始階段、數據傳輸階段和終止階段。

1. 起始階段

在I2C總線不工作的情況下,SDA(數據線)和SCL(時鍾線)上的信號均為高電平。如果此時主機需要發起新的通信請求,那么需要首先通過SDA和SCL發出起始標志。當SCL為高電平時,SDA電平從高變低,這一變化表示完成了通信的起始條件。

在起始條件和數據通信之間,通常會有延時要求,具體的指標會在設備廠商的規格說明書中給出。

2. 數據傳輸階段

I2C總線的數據通信是以字節(8位)作為基本單位在SDA上進行串行傳輸的。一個字節的傳輸需要9個時鍾周期。其中,字節中每一位的傳輸都需要一個時鍾周期,當新的SCL到來時,SCL為低電平,此時數據發送方根據當前傳輸的數據位控制SDA的電平信號。如果傳輸的數據位為"1",就將SDA電平拉高;如果傳輸的數據位為"0",就將SDA的電平拉低。當SDA上的數據准備好之后,SCL由低變高,此時數據接收方將會在下一次SCL信號變低之前完成數據的接收。當8位數據發送完成后,數據接收方需要一個時鍾周期以使用SDA發送ACK信號,表明數據是否接收成功。當ACK信號為"0"時,說明接收成功;為"1"時,說明接收失敗。每個字節的傳輸都是由高位(MSB)到低位(LSB)依次進行傳輸。

I2C總線協議中規定,數據通信的第一個字節必須由主機發出,內容為此次通信的目標設備地址和數據通信的方向(讀/寫)。在這個字節中,第1~7位為目標設備地址,第0位為通信方向,當第0位為"1"時表示讀,即后續的數據由目標設備發出主機進行接收;當第0位為"0"時表示寫,即后續的數據由主機發出目標設備進行接收。在數據通信過程中,總是由數據接收方發出ACK信號。

3. 終止階段

當主機完成數據通信,並終止本次傳輸時會發出終止信號。當SCL 是高電平時,SDA電平由低變高,這個變化意味着傳輸終止。

下面給出了模擬I2C總線進行讀寫的偽代碼,用以說明如何使用GPIO實現I2C通信:

#define SDA 254                         //定義SDA所對應的GPIO接口編號  
#define SCL 255                         //定義SCL所對應的GPIO接口編號  
#define OUTP 1                          //表示GPIO接口方向為輸出  
#define INP 0                           //表示GPIO接口方向為輸入  
/* I2C起始條件 */  
int i2c_start()  
{  
//初始化GPIO口  
set_gpio_direction(SDA, OUTP);          //設置SDA方向為輸出  
set_gpio_direction (SCL, OUTP);         //設置SCL方向為輸出  
         set_gpio_value(SDA, 1);                //設置SDA為高電平  
set_gpio_value(SCL, 1);                 //設置SCL為高電平  
delay();                            //延時  
//起始條件  
set_gpio_value(SDA, 0);                 //SCL為高電平時,SDA由高變低  
delay();  
}  
/* I2C終止條件 */  
void i2c_stop()  
{  
set_gpio_value(SCL, 1);  
set_gpio_direction(SDA, OUTP);  
set_gpio_value(SDA, 0);  
delay();  
set_gpio_value(SDA, 1);             //SCL高電平時,SDA由低變高  
}  
/*   
I2C讀取ACK信號(寫數據時使用)  
返回值 :0表示ACK信號有效;非0表示ACK信號無效  
*/  
unsigned char i2c_read_ack()  
{  
unsigned char r;  
set_gpio_direction(SDA, INP);           //設置SDA方向為輸入  
set_gpio_value(SCL,0);              // SCL變低  
r = get_gpio_value(SDA);                //讀取ACK信號  
delay();  
set_gpio_value(SCL,1);              // SCL變高  
delay();  
return r;  
}  
/* I2C發出ACK信號(讀數據時使用) */  
int i2c_send_ack()  
{  
set_gpio_direction(SDA, OUTP);          //設置SDA方向為輸出  
set_gpio_value(SCL,0);              // SCL變低  
set_gpio_value(SDA, 0);             //發出ACK信號  
delay();  
set_gpio_value(SCL,1);              // SCL變高  
delay();  
}  
/* I2C字節寫 */  
void i2c_write_byte(unsigned char b)  
{  
int i;  
set_gpio_direction(SDA, OUTP);          //設置SDA方向為輸出  
for (i=7; i>=0; i--) {  
set_gpio_value(SCL, 0);             // SCL變低  
delay();  
set_gpio_value(SDA, b & (1<<i));        //從高位到低位依次准備數據進行發送  
set_gpio_value(SCL, 1);             // SCL變高  
delay();  
}  
i2c_read_ack();                 //檢查目標設備的ACK信號  
}  
/* I2C字節讀 */  
unsigned char i2c_read_byte()  
{  
int i;  
unsigned char r = 0;  
set_gpio_direction(SDA, INP);           //設置SDA方向為輸入  
for (i=7; i>=0; i--) {  
set_gpio_value(SCL, 0);         // SCL變低  
delay();  
r = (r <<1) | get_gpio_value(SDA);      //從高位到低位依次准備數據進行讀取  
set_gpio_value(SCL, 1);         // SCL變高  
delay();  
}  
i2c_send_ack();                 //向目標設備發送ACK信號  
return r;  
}  
/*  
I2C讀操作  
addr:目標設備地址  
buf:讀緩沖區  
len:讀入字節的長度  
*/  
void i2c_read(unsigned char addr, unsigned char* buf, int len)  
{  
int i;  
unsigned char t;  
i2c_start();                        //起始條件,開始數據通信  
//發送地址和數據讀寫方向  
t = (addr << 1) | 1;                    //低位為1,表示讀數據  
i2c_write_byte(t);  
//讀入數據  
for (i=0; i<len; i++)  
buf[i] = i2c_read_byte();  
i2c_stop();                     //終止條件,結束數據通信  
}  
/*  
I2C寫操作  
addr:目標設備地址  
buf:寫緩沖區  
len:寫入字節的長度  
*/  
void i2c_write (unsigned char addr, unsigned char* buf, int len)  
{  
int i;  
unsigned char t;  
i2c_start();                        //起始條件,開始數據通信  
//發送地址和數據讀寫方向  
t = (addr << 1) | 0;                    //低位為0,表示寫數據  
i2c_write_byte(t);  
//寫入數據  
for (i=0; i<len; i++)  
i2c_write_byte(buf[i]);  
i2c_stop();                     //終止條件,結束數據通信  
}  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM