STM32第九章-IIC通信應用

  說到IIC(一般也叫I2C,其實都是同樣的)通信,是一種最簡單的通信協議。在學習STM32時第一個接觸的就是串口USART通信協議,接下來就是IIC通信協議了還有的就是SPI協議,SPI咱們下一章再說,這一章就說說IIC吧。不少模塊都用到過IIC通信,最多見的就是4針的0.96寸OLED顯示屏,固然啦在學習STM32是咱們通常最早接觸到就是經過IIC來與EEPROM進行通信,可是本章咱們只講協議自己。編程

1、 IIC 簡介

  IIC(Inter-Integrated Circuit)總線是一種由 PHILIPS 公司開發的兩線式串行總線,用於鏈接微控制器及其外圍設備。它是由數據線 SDA 和時鐘 SCL 構成的串行總線,可發送和接收數據。在 CPU 與被控 IC 之間、IC 與 IC 之間進行雙向傳送,高速 IIC 總線通常可達 400kbps 以上。   I2C 總線在傳送數據過程當中共有三種類型信號, 它們分別是:開始信號、結束信號和應答信號。   開始信號:SCL 爲高電平時,SDA 由高電平向低電平跳變,開始傳送數據。   結束信號:SCL 爲高電平時,SDA 由低電平向高電平跳變,結束傳送數據。   應答信號:接收數據的 IC 在接收到 8bit 數據後,向發送數據的 IC 發出特定的低電平脈衝,表示已收到數據。CPU 向受控單元發出一個信號後,等待受控單元發出一個應答信號,CPU 接收到應答信號後,根據實際狀況做出是否繼續傳遞信號的判斷。若未收到應答信號,由判斷爲受控單元出現故障。這些信號中,起始信號是必需的,結束信號和應答信號均可以不要。     IIC使用 SDA信號線來傳輸數據,使用 SCL信號線進行數據同步。SDA數據線在 SCL的每一個時鐘週期傳輸一位數據。傳輸時,SCL爲高電平的時候 SDA 表示的數據有效,即此時的 SDA 爲高電平時表示數據「1」,爲低電平時表示數據「0」。當 SCL爲低電平時,SDA的數據無效,通常在這個時候SDA進行電平切換,爲下一次表示數據作好準備。每次數據傳輸都以字節爲單位,每次傳輸的字節數不受限制。   若是咱們直接控制STM32的兩個GPIO 引腳,分別用做 SCL和SDA,按照上述信號的時序要求,直接像控制 LED 燈那樣控制引腳的輸出(如果接收數據時則讀取 SDA電平),就能夠實現 IIC通信。一樣假如咱們按照 USART的要求去控制引腳,也能實現 USART通信。因此只要遵照協議,就是標準的通信,無論您如何實現它,無論是ST生產的控制器仍是ATMEL生產的存儲器, 都能按通信標準交互。   因爲直接控制 GPIO 引腳電平產生通信時序時,須要由 CPU 控制每一個時刻的引腳狀態,因此稱之爲「軟件模擬協議」方式。相對地,還有「硬件協議」方式,STM32 的 IIC片上外設專門負責實現IIC通信協議,只要配置好該外設,它就會自動根據協議要求產生通信信號,收發數據並緩存起來,CPU只要檢測該外設的狀態和訪問數據寄存器,就能完成數據收發。這種由硬件外設處理IIC協議的方式減輕了 CPU 的工做,且使軟件設計更加簡單。緩存

2、通訊特徵:串行、同步、非差分、低速率

  • I2C屬於串行通訊,全部的數據以位爲單位在SDA線上串行傳輸。
  • 同步通訊就是通訊雙方工做在同一個時鐘下,通常是通訊的A方經過一根CLK信號線傳輸A本身的時鐘給B,B工做在A傳輸的時鐘下。因此同步通訊的顯著特徵就是:通訊線中有CLK。
  • 非差分。由於I2C通訊速率不高,並且通訊雙方距離很近,因此使用電平信號通訊。
  • 低速率。I2C通常是用在同一個板子上的2個IC之間的通訊,並且用來傳輸的數據量不大,因此自己通訊速率很低(通常幾百KHz,不一樣的I2C芯片的通訊速率可能不一樣,具體在編程的時候要看本身所使用的設備容許的I2C通訊最高速率,不能超過這個速率)

突出特徵 1:主設備+從設備(必須明確)

  • I2C通訊的時候,通訊雙方地位是不對等的,而是分主設備和從設備。通訊由主設備發起,由主設備主導,從設備只是按照I2C協議被動的接受主設備的通訊,並及時響應。markdown

  • 誰是主設備、誰是從設備是由通訊雙方來定的(I2C協議並沒有規定),通常來講一個芯片能夠只能作主設備、也能夠只能作從設備、也能夠既能當主設備又能當從設備(軟件配置)。函數

  • 有不少人認爲在通訊時單片機是主設備,器件是從設備,這是不嚴謹的。STM32單片機也能夠當從設備,只是你沒見到過罷了。學習

突出特徵 2:能夠多個設備掛在一條總線上(從設備地址)

  • I2C 通訊能夠一對一(1個主設備對1個從設備),也能夠一對多(1個主設備對多個從設備)。
  • 主設備來負責調度總線,決定某一時間和哪一個從設備通訊。注意:同一時間內,I2C 的總線上只能傳輸一對設備的通訊信息,因此同一時間只能有一個從設備和主設備通訊,其餘從設備處於「冬眠」狀態,不能出來搗亂,不然通訊就亂套了。
  • 每個 I2C 從設備在通訊中都有一個 I2C 從設備地址,這個設備地址是從設備自己固有的屬性,而後通訊時主設備須要知道本身將要通訊的那個從設備的地址,而後在通訊中經過地址來甄別是否是本身要找的那個從設備。(這個地址是一個電路板上惟一的,不是全球惟一的)

I2C 的總線空閒狀態、起始位、結束位

  • I2C 總線上有 1 個主設備,n(n>=1)個從設備。I2C 總線上有 2 種狀態;空閒態(全部從設備都未和主設備通訊,此時總線空閒)和忙態(其中一個從設備在和主設備通訊,此時總線被這一對佔用,其餘從設備必須歇着)。
  • 整個通訊分爲一個週期一個週期的,兩個相鄰的通訊週期是空閒態。每個通訊週期由一個起始位開始,一個結束位結束,中間是本週期的通訊數據。
  • 起始位並非一個時間點,起始位是一個時間段,在這段時間內總線狀態變化狀況是:SCL 線維持高電平,同時 SDA 線發生一個從高到低的降低沿。
  • 與起始位類似,結束位也是一個時間段。在這段時間內總線狀態變化狀況是:SCL 線維持高電平,同時 SDA 線發生一個從低到高的上升沿。

如何牢記IIC通訊的起始信號和結束信號的時序?   咱們把IIC通訊看作一條遊蕩在水中小船,把船面當作SDA數據線,水面波瀾起伏當作IIC通訊的時鐘SCK,沒有水船就不能走,同理沒有時鐘線就沒有通訊。由於SCL 維持高電平,SDA 線發生一個從高到低的降低沿起始信號就開始了,因此咱們能夠把船頭當作起始信號(想象一下,在湖面上一條彎彎的小船在順水而行)。同時SCL 維持高電平,SDA 線發生一個從低到高的上升沿就是中止信號,故咱們能夠把船尾看作中止信號(小船的船尾是否是與水面夾角爲45°)。所以若是你記不住IIC通訊的時序,請想象一下,你坐在一條小船上,順水而下坐在船上拿着電腦寫着IIC驅動程序就能夠了 ui

I2C 數據傳輸格式(數據位&ACK)

  • 每個通訊週期的發起和結束都是由主設備來作的,從設備只有被動的響應主設備,無法本身自發的去作任何事情。
  • 主設備在每一個通訊週期會先發8位的從設備地址(其實8位中只有7位是從設備地址,還有1位表示主設備下面要寫入仍是讀出)到總線(主設備是以廣播的形式發送的,只要是總線上的全部從設備其實都能收到這個信息)。而後總線上的每一個從設備都能收到這個地址,而且收到地址後和本身的設備地址比較看是否相等。若是相等說明主設備本次通訊就是給我說話,若是不想等說明此次通訊與我無關,不用聽了無論了。
  • 發送方發送一段數據後,接收方須要迴應一個 ACK。這個響應自己只有1個 bit 位,不能攜帶有效信息,只能表示 2 個意思(要麼表示收到數據,即有效響應;要麼表示未收到數據,無效響應)。應答信號ACK只能是必須是接收方發送,由於只有發送方發送數據後接收方纔能應答。
  • 在某一個通訊時刻,主設備和從設備只能有一個在發(佔用總線,也就是向總線寫),另在每一個時鐘的上升沿,把要發送的數據準備好,發送的纔有效應答信號ACK只能是必須是接收方發送,由於只有發送方發送數據後接收方纔能應答一個在收(從總線讀)。若是在某個時間主設備和從設備都試圖向總線寫那就完蛋了,通訊就亂套了。

數據在總線上的傳輸協議

  • I2C通訊時的基本數據單位也是以字節爲單位的,每次傳輸的有效數據都是1個字節(8位)。
  • 起始位及其後的8個CLK中都是主設備在發送(主設備掌控總線),此時從設備只能讀取總線,經過讀總線來得知主設備發給從設備的信息;而後到了第9週期,按照協議規定從設備須要發送ACK給主設備,因此此時主設備必須釋放總線(主設備把總線置爲高電平而後不要動,其實就相似於總線空閒狀態),同時從設備試圖拉低總線後發出ACK。若是從設備拉低總線失敗,或者從設備根本就沒有拉低總線,則主設備看到的現象就是總線在第9週期仍然一直保持高,這對主設備來講,意味着我沒收到ACK,主設備就認爲剛纔給從設備發送的8字節不對(接收失敗)

2、 軟件模擬協議

1.IIC初始化函數

功能:配置IIC的時鐘線和數據線spa

void IIC_Init(void) {					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOC, ENABLE);		   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推輓輸出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure); 
	IIC_SCL=1;
	IIC_SDA=1;
}
複製代碼

   由於是軟件模擬IIC那麼咱們選擇IIC通信的引腳就相對來講說比較隨意,具體使用的引腳可查閱《STM32F1xx 規格書》,以它爲準。這裏咱們就選擇PC十一、PC12做爲IIC的數據和時鐘引腳。設置爲推輓輸出便可。設計

2.起始信號

功能: CPU發起I2C總線起始信號code

void IIC_Start(void) {
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:當 CLK 爲高電平時,DATA 從高到低改變
	delay_us(4);	
	IIC_SCL=0;//鉗住I2C總線,準備發送或接收數據 
	delay_us(4);
}
複製代碼

  起始信號產生後,全部從機設備就開始等待STM32緊接下來的從機地址信號。在IIC總線上,每一個設備的地址都是惟一的,當主機廣播的地址與某個設備地址相同時,這個設備就被選中了,沒被選中的設備將會忽略以後的數據信號。根據IIC協議,這個從機地址能夠是 7位或10位。在地址位以後,是傳輸方向的選擇位,該位爲 0時,表示後面的數據傳輸方向是由主機傳輸至從機,即主機向從機寫數據。該位爲 1時,則相反,即主機由從機讀數據。orm

3.等待應答信號

功能:CPU 產生一個時鐘,並讀取器件的 ACK 應答信號

//返回值:1,接收應答失敗 0,接收應答成功
u8 IIC_Wait_Ack(void) {
	u8 re;
	IIC_SDA=1;delay_us(1);//CPU釋放SDA總線 
	IIC_SCL=1;delay_us(1);//CPU驅動SCL=1,此時器件會返回ACK應答
	if(READ_SDA){//CPU讀取SDA口線狀態
	   re=1;
	}else{
	  re=0;
	}	
	IIC_SCL=0;//時鐘輸出0 
	return re;  
} 
複製代碼

  該函數用於 STM32 做爲發送方時,等待及處理接收方傳來的響應或非響應信號, 即通常調用前面的 IIC_SendByte 函數後,再調用本函數檢測響應。   STM32控制 SDA 信號線輸出高阻態,釋放它對 SDA的控制權,由接收方控制;控制 SCL 信號線切換高低電平,產生一個時鐘信號,根據IIC協議,此時接收方若把 SDA 設置爲低電平,就表示返回一個「應答」信號,若 SDA 保持爲高電平,則表示返回一個「非應答 」信號;在 SCL 切換高低電平之間,有個延時確保給予了足夠的時間讓接收方返回應答信號,延時後使用宏SDA_READ 讀取 SDA 線的電平,根據電平值賦予 re 變量的值; 函數的最後返回 re的值,接收到響應時返回 0,未接收到響應時返回 1。當 STM32 做爲數據接收端,調用 IIC_ReadByte 函數後,須要給發送端返回應答或非應答信號,此時可以使用 IIC_Ack及 IIC_Nack 函數處理,該處理與 IIC_Wait_Ack函數相反,此時 SDA線也由 STM32控制。

4.應答信號

功能: CPU 產生一個 ACK 信號

//CPU產生一個ACK信號
void IIC_Ack(void) {
	IIC_SDA=0;//CPU驅動SDA=0
	delay_us(2);
	IIC_SCL=1;//CPU產生一個時鐘
	delay_us(2);
	IIC_SCL=0;
	delay_us(2);
	IIC_SDA=1;//CPU釋放SDA總線
}
//CPU產生1個NACK信號
void IIC_Nack (void) {
	IIC_SDA=1();//CPU驅動SDA=1
	delay_us(2);
	IIC_SDA=1;//CPU產生1個時鐘
	delay_us(2);
	IIC_SCL=0;
	delay_us(2);	
}
複製代碼

  I2C 的數據和地址傳輸都帶響應。響應包括「應答(ACK)」和「非應答(NACK)」兩種信號。做爲數據接收端時,當設備接收到 I2C 傳輸的一個字節數據或地址後,若但願對方繼續發送數據,則須要向對方發送「應答(ACK)」信號,發送方會繼續發送下一個數據;若接收端但願結束數據傳輸,則向對方發送「非應答(NACK)」信號,發送方接收到該信號後會產生一箇中止信號,結束信號傳輸。   代碼的具體流程就是:根據要返回「應答」仍是「非應答」信號,先準備好 SDA 線的電平,IIC_Ack函數中把 SDA 線設置爲低電平,表示「應答」信號,IIC_Nack 函數中把 SDA 線設置爲高電平,表示「非應答」信號;控制 SCL 線進行高低電平切換,產生一個時鐘信號,在 SCL 線的高低電平之間加入一個延時,確保有足夠的時間讓通信的另外一方接收到 SDA信號線的電平;在 IIC_Ack 函數的末尾,響應信號發送結束後,從新把 SDA 線設置爲高電平以釋放總線的控制權,方便後續的通信。

5.中止信號

功能:CPU 發起 I2C 總線中止信號

{	
	IIC_SDA=0;//STOP:當 CLK 爲高電平時候, SDA出現一個上調錶示IIC總線中止信號
	IIC_SCL=1;
 	delay_us(4);
	IIC_SDA=1;//發送I2C總線結束信號 
}
複製代碼

  中止信號直接看是時序圖就能夠搞定了,在SCL和SDA都爲低電平的狀況下,首先把時鐘線SCL拉高,再把數據線SDA拉高,IIC就會結束傳輸了。   以上就是軟件模擬IIC協議了,在平時的應用中咱們實際上不須要掌握這些具體的代碼,只要知道IIC協議的過程原理就好了,應爲通常來講咱們用的都是別人寫好的代碼,咱們只須要會用就能夠了,若是你的代碼和我這些有出入也沒有關係,只要能正常通信便可,固然若是你的設計在過程當中出現了一些問題,或者顯示不正常,咱們首先考慮的也不是底層協議的問題,而是你代碼的其餘問題。

6.IIC發送字節

功能: CPU向I2C總線設備發送8bit數據

void IIC_SendByte(u8 Byte) {
	u8 i;
	/* 先發送字節的高位bit7 */
	for (i = 0; i < 8; i++)
	{		
		if (Byte & 0x80)
		{
		  IIC_SDA=1;
		}
		else
		{
			IIC_SDA=0;
		}
		i2c_Delay();
		IIC_SCL=1;
		delay_us(2);
		IIC_SCL=0;
		if (i == 7)
		{
			 IIC_SDA=1;// 釋放總線
		}
		Byte <<= 1;	/* 左移一個bit */
		delay_us(2);
	}
}
複製代碼

  該函數以其輸入參數做爲要使用 I2C 協議輸出的數據,該數據大小爲一字節。函數的主體是一個 8 次的 for 循環,循環體執行一次將會對外發送一個數據位,循環結束時恰好發送完該字節數據。步驟分解以下:   首先程序對輸入參數Byte 和 0x80「與」運算,判斷其最高位的邏輯值,爲 1 時控制 SDA輸出高電平,爲 0則控制 SDA輸出低電平;接下來 延時,以此保證 SDA 線輸出的電平已穩定,再進行後續操做;以後控制 SCL線產生高低電平跳變,也就是產生 I2C協議中 SCL線的通信時鐘; 在 SCL線高低電平之間有個延時,該延時期間 SCL線維持高電平,根據 I2C協議,此時數據有效,通信的另外一方會在此時讀取 SDA 線的電平邏輯,高電平時接收到該位爲數據 1,不然爲 0;就這樣一次循環體執行結束,Byte 左移一位以便下次循環發送下一位的數據;如次循環 8 次,把Byte 中的 8 位個數據位發送完畢,在最後一位發送完成後(此時循環計數器 i=7),控制 SDA 線輸出 1(即高阻態),也就是說發送方釋放 SDA總線,等待接收方的應答信號。

7.IIC讀取字節

功能: CPU從IIC總線設備讀取8bit數據

u8_t IIC_ReadByte(void) {
	u8 i;
	u8 value;
	//讀到第1個bit爲數據的bit7
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		IIC_SCL=1;
		delay_us(2);
		if (DA_READ)
		{
			value++;
		}
		IIC_SCL=0;
		delay_us(2);
	}
	return value;
}
複製代碼

  IIC_ReadByte 函數也是以 for 循環爲主體,循環體會被執行 8次,執行完畢後將會接收到一個字節的數據,循環體接收數據的流程以下:   首先使用一個變量 value 暫存要接收的數據,每次循環開始前先對 value 的值左移 1 位,以給 value 變量的 bit0 騰出空間,bit0 將用於緩存最新接收到的數據位,一位一位地接收並移位,最後拼出完整的 8位數據;而後控制 SCL線進行高低電平切換,輸出 I2C 協議通信用的時鐘; 在 SCL 線高低電平切換之間,有個延時,該延時確保給予了足夠的時間讓數據發送方進行處理,即發送方在 SCL 時鐘驅動下經過 SDA 信號線發出電平邏輯信號,而這個延時以後,做爲數據接收端的 STM32 使用宏 SDA_READ讀取 SDA信號線的電平,若信號線爲 1,則 value++,即把它的 bit0置 1,不然不 操做(這樣該位將保持爲 0),這樣就讀取到了一位的數據;接下來SCL線切換成低電平後,加入延時,以便接收端根據須要切換 SDA 線輸出數據;直到循環結束後,value 變量中包含有 1 個字節數據,使用 return 把它做爲函數返回值返回;

3、 硬件協議

  相對來講,硬件IIC直接使用外設來控制引腳,能夠減輕 CPU 的負擔。不過使用硬件IIC 時必須使用某些固定的引腳做爲 SCL 和 SDA,軟件模擬IIC則可使用任意 GPIO 引腳,相對比較靈活。   STM32的IIC外設可用做通信的主機或從機,支持 100Kbit/s 和 400Kbit/s 的速率,支持 7位、10位設備地址,支持 DMA數據傳輸,並具備數據校驗功能。它的IIC外設還支持 SMBus2.0協,SMBus 協議與IIC相似,主要應用於筆記本電腦的電池管理中。   STM32 芯片有多個IIC外設,它們的IIC通信信號引出到不一樣的 GPIO 引腳上,使用時必須配置到這些指定的引腳,GPIO引腳的複用功能,可查閱《STM32F1xx 規格書》,以它爲準。 在這裏插入圖片描述

IIC初始化函數

void IIC_init(void) {
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;// 開漏輸出 
	GPIO_Init(GPIOA, &GPIO_InitStructure);	
	IIC_SCL=1;
	IIC_SDA=1;//給一箇中止信號, 復位I2C總線上的全部設備到待機模式
}
複製代碼

  由於是硬件IIC直接使用外設來控制引腳,那麼咱們選擇IIC通信的引腳就比較固定,具體使用的引腳可查閱《STM32F1xx 規格書》,以它爲準。能夠看到PB6和PB7兩個引腳能夠做爲IIC的通信引腳,並且PB6爲SCL時鐘線,而PB7則爲SDA數據線,並設置爲開漏輸出。   這裏爲啥設置爲開漏輸出的方式呢?   這是因爲使用的是軟件模擬IIC方式,而IIC協議的 GPIO 必須的開漏輸出模式,開漏輸出模式在輸出高電平時實際輸出高阻態,當IIC該總線上全部設備都輸出高阻態時,由外部的上拉電阻上拉爲高電平。另外當 STM32 的 GPIO 配置成開漏輸出模式時,它仍然能夠經過讀取GPIO 的輸入數據寄存器獲取外部對引腳的輸入電平,也就是說它同時具備浮空輸入模式的功能,所以在後面控制 SDA線對外輸出電平或讀取 SDA線的電平信號時不須要切換 GPIO的模式。   另外在應交IIC協議之下,它的起始信號、等待應答信號、應答信號、中止信號都與軟件模擬IIC協議之下的函數相同,在這裏我就不重複說明了。 總結:IIC通信協議很簡單,在實際項目中咱們不須要掌握具體的IIC協議代碼,只要會用便可,做爲最多見且經常使用的協議,咱們最好可以背下來或者有所瞭解。如今IIC通信不陌生了吧!

相關文章
相關標籤/搜索