說到IIC(一般也叫I2C,其實都是同樣的)通信,是一種最簡單的通信協議。在學習STM32時第一個接觸的就是串口USART通信協議,接下來就是IIC通信協議了還有的就是SPI協議,SPI咱們下一章再說,這一章就說說IIC吧。不少模塊都用到過IIC通信,最多見的就是4針的0.96寸OLED顯示屏,固然啦在學習STM32是咱們通常最早接觸到就是經過IIC來與EEPROM進行通信,可是本章咱們只講協議自己。編程
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 的工做,且使軟件設計更加簡單。緩存
I2C通訊的時候,通訊雙方地位是不對等的,而是分主設備和從設備。通訊由主設備發起,由主設備主導,從設備只是按照I2C協議被動的接受主設備的通訊,並及時響應。markdown
誰是主設備、誰是從設備是由通訊雙方來定的(I2C協議並沒有規定),通常來講一個芯片能夠只能作主設備、也能夠只能作從設備、也能夠既能當主設備又能當從設備(軟件配置)。函數
有不少人認爲在通訊時單片機是主設備,器件是從設備,這是不嚴謹的。STM32單片機也能夠當從設備,只是你沒見到過罷了。學習
如何牢記IIC通訊的起始信號和結束信號的時序? 咱們把IIC通訊看作一條遊蕩在水中小船,把船面當作SDA數據線,水面波瀾起伏當作IIC通訊的時鐘SCK,沒有水船就不能走,同理沒有時鐘線就沒有通訊。由於SCL 維持高電平,SDA 線發生一個從高到低的降低沿起始信號就開始了,因此咱們能夠把船頭當作起始信號(想象一下,在湖面上一條彎彎的小船在順水而行)。同時SCL 維持高電平,SDA 線發生一個從低到高的上升沿就是中止信號,故咱們能夠把船尾看作中止信號(小船的船尾是否是與水面夾角爲45°)。所以若是你記不住IIC通訊的時序,請想象一下,你坐在一條小船上,順水而下坐在船上拿着電腦寫着IIC驅動程序就能夠了。 ui
功能:配置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的數據和時鐘引腳。設置爲推輓輸出便可。設計
功能: 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
功能: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控制。
功能: 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 線設置爲高電平以釋放總線的控制權,方便後續的通信。
功能: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協議的過程原理就好了,應爲通常來講咱們用的都是別人寫好的代碼,咱們只須要會用就能夠了,若是你的代碼和我這些有出入也沒有關係,只要能正常通信便可,固然若是你的設計在過程當中出現了一些問題,或者顯示不正常,咱們首先考慮的也不是底層協議的問題,而是你代碼的其餘問題。
功能: 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總線,等待接收方的應答信號。
功能: 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 把它做爲函數返回值返回;
相對來講,硬件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 規格書》,以它爲準。
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通信不陌生了吧!