I2C通信協(xié)議在嵌入式IC中應(yīng)用的特別廣泛,所以今天給大家詳細(xì)的講解一下,有解釋的不正確或不合理的地方歡迎大家提出意見(jiàn)。
IIC是一種半雙工串行同步通信協(xié)議,由數(shù)據(jù)線SDA和時(shí)鐘線SCL構(gòu)成串行總線,可用于發(fā)送和接收數(shù)據(jù),通常是由主設(shè)備發(fā)起,從設(shè)備被動(dòng)響應(yīng),實(shí)現(xiàn)數(shù)據(jù)的傳輸。
02 I2C硬件原理圖
SDA: 數(shù)據(jù)線(雙向)
SCL: 時(shí)鐘線(主機(jī)控制)
因?yàn)镮2C總線接口是開(kāi)漏輸出(見(jiàn)下面的電氣特性圖),所以SDA和SCL必須接上拉電阻!(一般選用4.7K~10K的電阻)。
I2C總線上可以掛載多個(gè)主設(shè)備,以及多個(gè)從設(shè)備,在從機(jī)沒(méi)有收到主機(jī)的地址訪問(wèn)信息前從機(jī)不會(huì)主動(dòng)向主機(jī)發(fā)送數(shù)據(jù)。
03 I2C接口電氣特性
04 I2C總線數(shù)據(jù)傳輸起始和停止條件
起始條件: 在SCL為高電平期間,SDA產(chǎn)生一個(gè)下降沿信號(hào)
停止條件: 在SCL為高電平期間,SDA產(chǎn)生一個(gè)上升沿信號(hào)
//程序中的宏定義
#define HIGH 1
#define LOW 0
/* IO方向設(shè)置 */
#define SDA_IN() {GPIOA- >CRH&=0XFFFF0FFF;GPIOA- >CRH|=(uint32_t)8< 12;}
#define SDA_OUT() {GPIOA- >CRH&=0XFFFF0FFF;GPIOA- >CRH|=(uint32_t)3< 12;}
/* IO操作 */
#define IIC_SCL(n) (n?HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET)) //SCL
#define IIC_SDA(n) (n?HAL_GPIO_WritePin(GPIOA,GPIO_PIN_11,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOA,GPIO_PIN_11,GPIO_PIN_RESET)) //SDA
#define READ_SDA HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) //輸入SDA
//產(chǎn)生IIC起始信號(hào)
void IIC_Start(void)
{
SDA_OUT();//sda線輸出
IIC_SDA(HIGH);
IIC_SCL(HIGH);
delay_us(4);
IIC_SDA(LOW);//START:when CLK is high,DATA change form High to low
delay_us(4);
IIC_SCL(LOW);//鉗住I2C總線,準(zhǔn)備發(fā)送或接收數(shù)據(jù)
}
//產(chǎn)生IIC停止信號(hào)
void IIC_Stop(void)
{
SDA_OUT();//sda線輸出
IIC_SCL(LOW);
IIC_SDA(LOW);//STOP:when CLK is high DATA change form low to High
delay_us(4);
IIC_SCL(HIGH);
IIC_SDA(HIGH);//發(fā)送I2C總線結(jié)束信號(hào)
delay_us(4);
}
05 數(shù)據(jù)傳輸格式
- 在SCL的每個(gè)時(shí)鐘脈沖期間傳輸 1 個(gè)數(shù)據(jù)位;
- 地址由 7 bit 構(gòu)成,最低位為讀寫命令,0:寫,1:讀
- SDA數(shù)據(jù)線上的 1 個(gè)字節(jié)由 8 個(gè)數(shù)據(jù)位組成,字節(jié)可以是設(shè)備地址、寄存器地址,也可以是寫入或從從機(jī)讀取的數(shù)據(jù);
- 首先傳輸數(shù)據(jù)的是最高有效位(MSB);
- 在啟動(dòng)和停止條件之間,可以將任意數(shù)量字節(jié)的數(shù)據(jù)從主設(shè)備傳輸?shù)綇脑O(shè)備;
- 在時(shí)鐘周期的高相位期間,SDA線上的數(shù)據(jù)必須保持穩(wěn)定,因?yàn)镾CL高時(shí)數(shù)據(jù)線上的變化被解釋為控制命令(啟動(dòng)或停止);
應(yīng)答信號(hào)
Acknowledge (ACK) and Not Acknowledge (NACK);
數(shù)據(jù)的每個(gè)字節(jié)(包括地址字節(jié))后面跟著來(lái)自接收器的一個(gè)ACK位,ACK位允許接收機(jī)與發(fā)射機(jī)進(jìn)行通信,告知該字節(jié)已成功接收,并可發(fā)送另一個(gè)字節(jié),這其實(shí)是I2C總線的一種數(shù)據(jù)校驗(yàn)方式。
在接收機(jī)發(fā)送ACK之前,發(fā)射機(jī)必須釋放SDA線路。為了發(fā)送ACK位,接收器應(yīng)在ACK/NACK相關(guān)時(shí)鐘周期(第9個(gè)周期)的低相位期間拉低SDA線,以便在ACK/NACK相關(guān)時(shí)鐘周期的高相位期間SDA線穩(wěn)定在低電平,同時(shí)必須考慮設(shè)置和保持的時(shí)間。
當(dāng)然,主機(jī)也有可能會(huì)接收到費(fèi)應(yīng)答信號(hào)NACK:
接收到非應(yīng)答信號(hào)NACK可能有以下原因:
1.接收器無(wú)法接收或發(fā)送,因?yàn)樗趫?zhí)行某些實(shí)時(shí)功能,并且尚未準(zhǔn)備好開(kāi)始與主機(jī)通信;
2.在傳輸過(guò)程中,接收器獲取其不理解的數(shù)據(jù)或命令;
3.在傳輸過(guò)程中,接收器無(wú)法再接收任何數(shù)據(jù)字節(jié);
4.主接收器數(shù)據(jù)已經(jīng)讀取完畢,默認(rèn)NACK信號(hào)的從機(jī)發(fā)過(guò)來(lái)的;
等待應(yīng)答信號(hào)和產(chǎn)生應(yīng)答信號(hào)程序:
//主機(jī)等待從機(jī)應(yīng)答信號(hào)
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN();//主機(jī)SDA設(shè)置為讀取模式
IIC_SDA(HIGH);//主機(jī)釋放SDA信號(hào)線
delay_us(1);
IIC_SCL(HIGH);
delay_us(1);
while(READ_SDA)//等待并讀取SDA狀態(tài)
{
ucErrTime++;
if(ucErrTime >250)//等待超時(shí)后結(jié)束本次數(shù)據(jù)傳輸
{
IIC_Stop();
return 1;
}
}
IIC_SCL(LOW);//時(shí)鐘輸出0
return 0;
}
//產(chǎn)生ACK應(yīng)答信號(hào)
void IIC_Ack(void)
{
IIC_SCL(LOW);
SDA_OUT();
IIC_SDA(LOW);
delay_us(2);
IIC_SCL(HIGH);
delay_us(2);
IIC_SCL(LOW);
}
//產(chǎn)生NACK非應(yīng)答信號(hào)
void IIC_NAck(void)
{
IIC_SCL(LOW);
SDA_OUT();
IIC_SDA(HIGH);
delay_us(2);
IIC_SCL(HIGH);
delay_us(2);
IIC_SCL(LOW);
}
06 主機(jī)通過(guò)I2C總線向從機(jī)設(shè)備寫數(shù)據(jù)
I2C總線按照如下示意圖向指定設(shè)備指定寄存器發(fā)送數(shù)據(jù):
I2C總線發(fā)送數(shù)據(jù)的過(guò)程
0.主機(jī)發(fā)送一個(gè)起始信號(hào);
1.主機(jī)發(fā)送從機(jī)設(shè)備地址,最低位為0,表示寫命令,R/W=0;
2.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
3.主機(jī)發(fā)送設(shè)備寄存器地址;
4.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
5.主機(jī)發(fā)送一個(gè)字節(jié)數(shù)據(jù);
6.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
8.主機(jī)接收從機(jī)上傳的一個(gè)字節(jié)數(shù)據(jù)(一般情況下只發(fā)送一個(gè)字節(jié)數(shù)據(jù));
9.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
10.主機(jī)繼續(xù)發(fā)送數(shù)據(jù),等待從機(jī)應(yīng)答信號(hào),重復(fù)步驟8和9;
11.主機(jī)發(fā)送一個(gè)停止信號(hào);
主機(jī)模擬I2C發(fā)送數(shù)據(jù)代碼實(shí)現(xiàn)(基于STM32)
//發(fā)送一個(gè)字節(jié)數(shù)據(jù)
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL(LOW);
for(t=0;t< 8;t++)
{
IIC_SDA((txd&0x80) >>7);
txd< <=1;
delay_us(2);
IIC_SCL(HIGH);
delay_us(2);
IIC_SCL(LOW);
delay_us(2);
}
}
//I2C總線向設(shè)指定設(shè)備指定寄存器寫一個(gè)字節(jié)數(shù)據(jù)
//devaddr:設(shè)備地址
//addr:寄存器地址
//data:待發(fā)送數(shù)據(jù)
void iicDevWriteByte(uint8_t devaddr,uint8_t addr,uint8_t data)
{
IIC_Start();//起始信號(hào)
IIC_Send_Byte(devaddr);//發(fā)送從機(jī)設(shè)備地址
IIC_Wait_Ack();
IIC_Send_Byte(addr);//發(fā)送寄存器地址
IIC_Wait_Ack();
IIC_Send_Byte(data);//發(fā)送數(shù)據(jù)
IIC_Wait_Ack();
IIC_Stop();//停止信號(hào)
}
//I2C總線向指定設(shè)備指定地址連續(xù)寫多個(gè)字節(jié)數(shù)據(jù)
//devaddr:設(shè)備地址
//addr:寄存器地址
//len:發(fā)送數(shù)據(jù)的長(zhǎng)度
//wbuf:待發(fā)送數(shù)據(jù)緩存
void iicDevWrite(uint8_t devaddr,uint8_t addr,uint8_t len,uint8_t *wbuf)
{
int i=0;
IIC_Start();//起始信號(hào)
IIC_Send_Byte(devaddr);//發(fā)送從機(jī)設(shè)備地址,發(fā)送寫命令,R/W=0
IIC_Wait_Ack();
IIC_Send_Byte(addr);//寄存器地址
IIC_Wait_Ack();
for(i=0; i< len; i++)
{
IIC_Send_Byte(wbuf[i]);
IIC_Wait_Ack();//等待ACK信號(hào)
}
IIC_Stop( );//停止信號(hào)
}
07 主機(jī)通過(guò)I2C總線讀取從機(jī)發(fā)送數(shù)據(jù)
I2C總線按照如下示意圖從指定從機(jī)的指定寄存器讀取數(shù)據(jù):
I2C總線讀取數(shù)據(jù)的過(guò)程
0.主機(jī)發(fā)送一個(gè)起始信號(hào);
1.主機(jī)發(fā)送從機(jī)設(shè)備地址,最低位為0,表示寫命令,R/W=0;
2.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
3.主機(jī)發(fā)送設(shè)備寄存器地址;
4.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
5.主機(jī)重復(fù)發(fā)送一個(gè)起始信號(hào);
6.主機(jī)發(fā)送從機(jī)設(shè)備地址,最低位為1,表示讀命令,R/W=1;
7.主機(jī)等待接收從機(jī)的應(yīng)答信號(hào);
8.主機(jī)接收從機(jī)上傳的一個(gè)字節(jié)數(shù)據(jù);
9.若主機(jī)發(fā)送ACK應(yīng)答信號(hào),繼續(xù)接收從機(jī)數(shù)據(jù);若主機(jī)發(fā)送NACK非應(yīng)答信號(hào),停止接收數(shù)據(jù);
10.主機(jī)發(fā)送一個(gè)停止信號(hào);
主機(jī)模擬I2C接收數(shù)據(jù)代碼實(shí)現(xiàn)(基于STM32)
//讀取一個(gè)字節(jié)數(shù)據(jù)
//ack=1時(shí),發(fā)送ACK,表示還有數(shù)據(jù)待繼續(xù)讀取
//ack=0時(shí),發(fā)送NACK,表示停止讀取數(shù)據(jù)
uint8_t IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA設(shè)置為輸入
for(i=0;i< 8;i++ )
{
IIC_SCL(LOW);
delay_us(2);
IIC_SCL(HIGH);
receive< <=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//發(fā)送nACK
else
IIC_Ack();//發(fā)送ACK
return receive;
}
//從指定設(shè)備指定寄存器地址讀取一個(gè)字節(jié)數(shù)據(jù)
//ReadAddr:開(kāi)始讀數(shù)的地址
//temp:讀到的數(shù)據(jù)
uint8_t iicDevReadByte(uint8_t devaddr,uint8_t addr)
{
uint8_t temp=0;
IIC_Start();//起始信號(hào)
IIC_Send_Byte(devaddr);//發(fā)送從機(jī)設(shè)備地址,發(fā)送寫命令,R/W=0
IIC_Wait_Ack();
IIC_Send_Byte(addr);//發(fā)送寄存器地址
IIC_Wait_Ack();
IIC_Start();//Repeated START
IIC_Send_Byte(devaddr|1);//發(fā)送從機(jī)設(shè)備地址,發(fā)送讀命令,R/W=1
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//停止信號(hào)
return temp;
}
//從指定設(shè)備指定寄存器地址連續(xù)讀取多個(gè)字節(jié)數(shù)據(jù)
//devaddr:從機(jī)設(shè)備地址
//addr:寄存器地址
//len:字節(jié)總長(zhǎng)度
//rbuf:讀取數(shù)據(jù)緩存區(qū)
void iicDevRead(uint8_t devaddr,uint8_t addr,uint8_t len,uint8_t *rbuf)
{
int i=0;
IIC_Start();//起始信號(hào)
IIC_Send_Byte(devaddr);//發(fā)送從機(jī)設(shè)備地址,發(fā)送寫命令,R/W=0
IIC_Wait_Ack();
IIC_Send_Byte(addr);//發(fā)送寄存器地址
IIC_Wait_Ack();
IIC_Start();//Repeated START
IIC_Send_Byte(devaddr|1);//發(fā)送從機(jī)設(shè)備地址,發(fā)送寫命令,R/W=1
IIC_Wait_Ack();
for(i=0; i< len; i++)
{
if(i==len-1)
{
rbuf[i]=IIC_Read_Byte(0);//全部數(shù)據(jù)接收完畢,主機(jī)發(fā)送NACK信號(hào),產(chǎn)生停止信號(hào)
}
else
rbuf[i]=IIC_Read_Byte(1);//數(shù)據(jù)未全部接收完,主機(jī)發(fā)送ACK信號(hào),繼續(xù)接收數(shù)據(jù)
}
IIC_Stop( );//停止信號(hào)
}
-
上拉電阻
+關(guān)注
關(guān)注
5文章
366瀏覽量
31058 -
寄存器
+關(guān)注
關(guān)注
31文章
5421瀏覽量
123400 -
I2C總線
+關(guān)注
關(guān)注
8文章
408瀏覽量
61937 -
SDA
+關(guān)注
關(guān)注
0文章
125瀏覽量
28706 -
開(kāi)漏輸出
+關(guān)注
關(guān)注
0文章
59瀏覽量
7477
發(fā)布評(píng)論請(qǐng)先 登錄
i2c總線的特點(diǎn)
I2C總線規(guī)范與I2C器件C51讀寫程序
并口模擬I2C總線的設(shè)計(jì)
I2C總線的工作原理與應(yīng)用

I2C總線詳解
基于CPLD的I2C總線接口設(shè)計(jì)

i2c總線用來(lái)做什么_i2c總線數(shù)據(jù)傳輸過(guò)程
I2C總線的基本通信總結(jié)

評(píng)論