i2c協議解讀(附讀寫過程c代碼)

原文鏈接:https://blog.csdn.net/qq_39759656/article/details/81232909

1.what is I2C?

簡單講就是用來傳輸數據的兩根線:一根數據線(SDA)一根時鐘線(SCL)

2.I2C怎麼傳輸的?

(1)基本過程:
1.主機發出開始信號
2.主機接着發出一字節的從機地址信息,其中最低位爲讀寫控制碼(1爲讀、0爲寫), 高七位爲從機器件地址
3.從機發出認可信號
4.主機開始發送信號,每發完一字節後,從機發出認可信號給主機
5.主機發出停止信號
在這裏插入圖片描述

(2)對以上信號的具體說明:
開始信號:在時鐘線爲高電平期間,數據線由高變低,將產生一個開始信號
停止信號:在時鐘線爲高電平期間,數據線由低變高,將產生一個停止信號
應答信號:即認可信號,主機寫從機時,每寫完一個字節,如果正確從機將在下一個時鐘週期將數據線拉低,以告訴主機操作有效。在主機讀從機的時候,正確讀完一個字節後,主機在下一個時鐘週期同樣也要將數據線拉低,發出認可信號,告訴從機所發數據已經收妥(注:讀從機時主機在最後一個字節數據接收完後,不發應答,直接發停止信號)

注意:任何在時鐘線爲高電平期間的數據線上的電平改變都被認爲是起始和停止信號,所以數據改變必須要在時鐘爲低電平時改變。

(3)數據格式:

在這裏插入圖片描述

I2C支持兩種數據格式:

7bit/10bit尋址數據格式

7bit/10bit尋址和重複開始信號的格式

從設備地址:

總線上每個設備都有自己的一個addr,共7個bit,廣播地址全0.
系統中可能有多個同種芯片,爲此addr分爲固定部分和可編程部份,細節視芯片而定,看datasheet。
在這裏插入圖片描述

硬件結構:

每一個I2C總線器件內部的SDA、SCL引腳電路結構都是一樣的,引腳的輸出驅動與輸入緩衝連在一起。其中輸出爲漏極開路的場效應管、輸入緩衝爲一隻高輸入阻抗的同相器。這種電路具有兩個特點:

(1)由於 SDA、SCL 爲漏極開路結構,藉助於外部的上拉電阻實現了信號的“線與”邏輯;
(2)引腳在輸出信號的同時還將引腳上的電平進行檢測,檢測是否與剛纔輸出一致。爲 “時鐘同步”和“總線仲裁”提供硬件基礎。

3.幾個I2C的疑問

(1)模擬I2C與硬件I2C有什麼區別

原理上:硬件I2C(提供專門的SDA,SCL口)的時鐘是系統產生的,一般由晶振分頻產生。模擬I2C通過編程模擬時鐘線和數據線。

控制上:硬件I2C通過硬件中斷實現各種操作。模擬I2C沒有中斷的概念,通過IO口電平置高置低來實現寫入和讀取。

性能上:硬件模式更高效更穩定。

(2)MPU6050和MPU9250的I2C

之前對於這兩個芯片的通過I2C的操作地址有點亂,如要獲取到加速度計的原始值,首先要發送開始信號,然後是發送從機地址(I2C的設備地址),然後發送寄存器的地址(這裏很多人會搞亂了,因爲要發兩次地址,第一次是I2C設備地址,第二次是加速度計的地址),接下來才能獲取到原始數據。

(3)slave address
i2c通訊是master下可能掛多個slave,每個slave擁有不同的slave address,master通過不同的address設置來遍歷下掛的slave,每個i2c接口的芯片會有不同的slave addres配置。芯片廠商在設計i2c接口時會設計多個address配置,以避免多slave訪問時地址重複,如下舉例tmp513溫控芯片slave address的設置:
在這裏插入圖片描述

4.怎麼實現I2C?

之前的所有鋪墊都是爲了實現I2C通訊,所以怎麼用代碼實現也是尤爲關鍵的一個問題。

下面是是I2C的讀寫流程:
在這裏插入圖片描述

寫寄存器的標準流程爲:

  1. Master發起START
  2. Master發送I2C addr(7bit)和w操作0(1bit),等待ACK
  3. Slave發送ACK
  4. Master發送reg addr(8bit),等待ACK
  5. Slave發送ACK
  6. Master發送data(8bit),即要寫入寄存器中的數據,等待ACK
  7. Slave發送ACK
  8. 第6步和第7步可以重複多次,即順序寫多個寄存器
  9. Master發起STOP
void Tmp513_Reg_Write(unsigned char regaddr, unsigned short regdata) 
{ 
	unsigned int i;
	unsigned int j;
	unsigned char opcode;
	unsigned char low_regdata;
	unsigned char high_regdata;
	
	low_regdata = regdata & 0xff;
	high_regdata = (regdata >> 8) & 0xff;

	TMP513_DATA_OUT;

	// START
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	opcode = TMP513_WRITE_CODE;

	// Operation code
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Reg address
	TMP513_DATA_OUT;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = (regaddr >> (7-i));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = regaddr;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Write high byte
	TMP513_DATA_OUT;
	for(j = 0; j < 7; j++){
		(TMP513_SDA->DATA) = (high_regdata >> (7 - j));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = high_regdata;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	
	// Write low byte
	TMP513_DATA_OUT;
	for(j = 0; j < 7; j++){
		(TMP513_SDA->DATA) = (low_regdata >> (7 - j));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = low_regdata;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Stop
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;

	TMP513_DATA_IN;
}

讀寄存器的標準流程爲:

  1. Master發送I2C addr(7bit)和w操作1(1bit),等待ACK
  2. Slave發送ACK
  3. Master發送reg addr(8bit),等待ACK
  4. Slave發送ACK
  5. Master發起START
  6. Master發送I2C addr(7bit)和r操作1(1bit),等待ACK
  7. Slave發送ACK
  8. Slave發送data(8bit),即寄存器裏的值
  9. Master發送ACK
  10. 第8步和第9步可以重複多次,即順序讀多個寄存器

以上通訊流程模擬I2C跟硬件I2C都類似,不同的地方是讀寫:

附i2c讀寫過程驅動c代碼:

unsigned short Tmp513_Reg_Read(unsigned char regaddr) 
{ 
	unsigned int i;
	unsigned int j;
	unsigned char opcode;
	unsigned short readdata;

	// Register pointer set
	TMP513_DATA_OUT;
	
	// START
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Operation code
	opcode = TMP513_WRITE_CODE;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Reg address
	TMP513_DATA_OUT;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = (regaddr >> (7-i));
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}
	(TMP513_SDA->DATA) = regaddr;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	
	// Stop
	Delay(TMP513_DELAY);
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;
	TMP513_DATA_IN;

	// Read register
	// Start
	TMP513_DATA_OUT;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Operation code
	opcode = TMP513_READ_CODE;
	for(i = 0; i < 7; i++){
		(TMP513_SDA->DATA) = opcode;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
		opcode = opcode >> 1;
	}
	(TMP513_SDA->DATA) = opcode;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Slave ACK
	TMP513_DATA_IN;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Read reg's high byte
	readdata = 0;
	for(j = 0; j < 8; j++){
		Delay(TMP513_DELAY);
		readdata = 	readdata << 1;
		readdata = ((TMP513_SDA->DATA) & 0x00000001) | readdata;
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}

	// Master ACK
	Delay(TMP513_DELAY);
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;
	TMP513_DATA_IN;

	// Read reg's low byte
	for(j = 0; j < 8; j++){
		Delay(TMP513_DELAY);
		readdata = 	readdata << 1;
		readdata = ((TMP513_SDA->DATA) & 0x00000001) | readdata;
		(TMP513_SCL->DATA) = 1;
		Delay(TMP513_DELAY);
		(TMP513_SCL->DATA) = 0;
	}

	// Master NO ACK
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 0;

	// Stop
	TMP513_DATA_OUT;
	(TMP513_SDA->DATA) = 0;
	Delay(TMP513_DELAY);
	(TMP513_SCL->DATA) = 1;
	Delay(TMP513_DELAY);
	(TMP513_SDA->DATA) = 1;

	TMP513_DATA_IN;
	return readdata;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章