什么是IIC(I2C)?
IIC 即Inter-Integrated Circuit(集成電路總線),這種總線類型是由飛利浦半導體公司設計出來的一種簡單、雙向、二線制、同步串行總線。它是一種多向控制總線,也就是說多個芯片可以連接到同一總線結構下,同時每個芯片都可以作為實時數據傳輸的控制源。這種方式簡化了信號傳輸總線接口。
那么也就是說,只要收發雙方同時接入SDA(雙向數據線)、SCL(同步時鍾線)便可以進行通信。
I2C總線的工作速度分為 3 種模式(實際上,IIC的通信速率由SCL決定):
S(標准模式),測量與控制場合;
F(快速模式),速率為 400kb/s;(默認)
Hs(高速模式),速率為 3.4Mb/s。
IIC接線框圖
一般情況下,SCL與SDA默認由上拉電阻拉高。這也是為了方便通信協議。
多機連接時,為了區分不同的從機,我們會使用自定義的地址碼進行區分。

IIC的通信狀態
IIC的通信要注意以下6個知識點:
1.空閑狀態
2.開始信號
3.停止信號
4.應答信號
5.數據的有效性
6.數據傳輸
空閑狀態:
在IIC中規定,當SDA、SCL同時為高電平時,視為空閑狀態。
注意,這個規定是通信設備通信前的判斷條件。
開始信號 & 停止信號:
在IIC中規定,當SCL為高電平,且SDA從高到低的跳變時,視為數據開始傳輸;
在IIC中規定,當SCL為高電平,且SDA從低到高的跳變時,視為數據停止傳輸;

數據有效性 & 數據傳送 & 應答信號(ACK)
數據有效性:
在傳輸數據時,應保證數據在SCL的上升沿到來之前准備好,並在下降沿到來之前必須穩定。
(由於在電路中,電平的跳變往往伴隨着毛刺。)
數據傳送:
在I2C總線上傳送的每一位數據都有一個時鍾脈沖相對應(或同步控制),即在SCL串行時鍾的配合下,在SDA上逐位地串行傳送每一位數據。
在一般情況下,傳輸數據時,從數據的最高有效位開始發送。
應答信號:
在IIC中規定,發送方每發送1個字節(8位)后需要接收接收方發送的應答信號。
ACK為0時,視為有效應答;ACK為1時,視為無效響應。
總結:誰發了數據,誰就要接收一個應答信號。
STM32 模擬IIC完整代碼
/* https://blog.csdn.net/return_oops/article/details/80965437 */ //使用IIC1 掛載M24C02,OLED,LM75AD,HT1382 PB6,PB7 #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} //IO操作函數 #define IIC_SCL PBout(6) //SCL #define IIC_SDA PBout(7) //SDA #define READ_SDA PBin(7) //輸入SDA //IIC所有操作函數 void IIC_Init(void); //初始化IIC的IO口 void IIC_Start(void); //發送IIC開始信號 void IIC_Stop(void); //發送IIC停止信號 void IIC_Send_Byte(u8 txd); //IIC發送一個字節 u8 IIC_Read_Byte(unsigned char ack);//IIC讀取一個字節 u8 IIC_Wait_Ack(void); //IIC等待ACK信號 void IIC_Ack(void); //IIC發送ACK信號 void IIC_NAck(void); //IIC不發送ACK信號 void I2C_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr); uint16_t I2C_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead);//寄存器地址,器件地址,要讀的字節數 void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽輸出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); IIC_SCL=1; IIC_SDA=1; } //產生IIC起始信號 void IIC_Start(void) { SDA_OUT(); //sda線輸出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//鉗住I2C總線,准備發送或接收數據 } //產生IIC停止信號 void IIC_Stop(void) { SDA_OUT();//sda線輸出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1;//發送I2C總線結束信號 delay_us(4); } //等待應答信號到來 //返回值:1,接收應答失敗 // 0,接收應答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA設置為輸入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//時鍾輸出0 return 0; } //產生ACK應答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不產生ACK應答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC發送一個字節 //返回從機有無應答 //1,有應答 //0,無應答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低時鍾開始數據傳輸 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //對TEA5767這三個延時都是必須的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //讀1個字節,ack=1時,發送ACK,ack=0,發送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA設置為輸入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//發送nACK else IIC_Ack(); //發送ACK return receive; } void I2C_WriteByte(uint16_t addr,uint8_t data,uint8_t device_addr) { IIC_Start(); if(device_addr==0xA0) //eeprom地址大於1字節 IIC_Send_Byte(0xA0 + ((addr/256)<<1));//發送高地址 else IIC_Send_Byte(device_addr); //發器件地址 IIC_Wait_Ack(); IIC_Send_Byte(addr&0xFF); //發送低地址 IIC_Wait_Ack(); IIC_Send_Byte(data); //發送字節 IIC_Wait_Ack(); IIC_Stop();//產生一個停止條件 if(device_addr==0xA0) // delay_ms(10); else delay_us(2); } uint16_t I2C_ReadByte(uint16_t addr,uint8_t device_addr,uint8_t ByteNumToRead) //讀寄存器或讀數據 { uint16_t data; IIC_Start(); if(device_addr==0xA0) IIC_Send_Byte(0xA0 + ((addr/256)<<1)); else IIC_Send_Byte(device_addr); IIC_Wait_Ack(); IIC_Send_Byte(addr&0xFF); //發送低地址 IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(device_addr+1); //發器件地址 IIC_Wait_Ack(); if(ByteNumToRead == 1)//LM75溫度數據為11bit { data=IIC_Read_Byte(0); } else { data=IIC_Read_Byte(1); data=(data<<8)+IIC_Read_Byte(0); } IIC_Stop();//產生一個停止條件 return data; }
