一. IIC的基本介紹
1.1IIC的簡介
IIC是雙線(不算地線)半雙工的一種通訊方式(可以雙向通訊,但不可以在同一時間雙向數據傳輸).
IIC串行總線一般有兩根信號線,一根是雙向的數據線SDA,另一根是時鐘線SCL,其時鐘信號是由主控器件產生。所有接到IIC總線設備上的串行數據SDA都接到總線的SDA上,各設備的時鐘線SCL接到總線的SCL上。對于并聯在一條總線上的每個IC都有唯一的地址。
?圖1 IIC拓撲網絡圖2 IIC時序圖
1.2地址問題
IIC設備地址
協議格式中第一個字節(為slave address)由7位地址和一位R/W讀寫位組成的,這字節是個器件地址。
常用IIC接口通用器件的器件地址是由種類型號,及尋址碼組成的,共7位。
如格式如下:
D7 D6 D5 D4 D3 D2 D1 D0
(1)、器件類型:D7-D4共4位決定的。這是由半導公司生產時就已固定此類型的了,也就是說這4位已是固定的。
(2)、用戶自定義地址碼:D3-D1共3位。這是由用戶自己設置的,通常的作法如EEPROM這些器件是由外部IC的3個引腳所組合電平決定的(用常用的名字如A0,A1,A2)。這也就是尋址碼。所以為什么同一IIC總線上同一型號的IC只能最多共掛8片同種類芯片的原因了。
(3)、最低一位就是R/W位,,“0”表示寫,“1”表示讀(通常讀寫信號中寫上面有一橫線,表示低電平)。所以IIC設備通常有兩個地址,即讀地址和寫地址。
IIC 設備的7 位地址是就當前IIC總線而言的,是“相對地址”。不同的IIC總線上的設備可以使用相同的7 位地址,但是它們所在的i2c 總線不同。所以在系統中一個IIC設備的“絕對地址”由二元組(IIC適配器的ID 和設備在該總線上的7 位地址)表示。”,所以這個函數的作用主要是排除同一IIC總線上出現多個地址相同的設備。
1.3 IIC協議
IIC總線在傳輸數據的過程中一共有三種類型信號,分別為:開始信號、結束信號和應答信號。這些信號中,起始信號是必需的,結束信號和應答信號,都可以不要。同時我們還要介紹其空閑狀態、數據的有效性、數據傳輸。
圖2 IIC時序圖
(1)空閑狀態
當IIC總線的數據線SDA和時鐘線SCL兩條信號線同時處于高電平時,規定為總線的空閑狀態。此時各個器件的輸出級場效應管均處在截止狀態,即釋放總線,由兩條信號線各自的上拉電阻把電平拉高。
(2)起始信號與停止信號
起始信號:當時鐘線SCL為高期間,數據線SDA由高到低的跳變;啟動信號是一種電平跳變時序信號,而不是一個電平信號;
停止信號:當時鐘線SCL為高期間,數據線SDA由低到高的跳變;停止信號也是一種電平跳變時序信號,而不是一個電平信號。
(3) 應答信號
發送器每發送一個字節(8個bit),就在時鐘脈沖9期間釋放數據線,由接收器反饋一個應答信號。
應答信號為低電平時,規定為有效應答位(ACK,簡稱應答位),表示接收器已經成功地接收了該字節;
應答信號為高電平時,規定為非應答位(NACK),一般表示接收器接收該字節沒有成功。
對于反饋有效應答位ACK的要求是:接收器在第9個時鐘脈沖之前的低電平期間將數據線SDA拉低,并且確保在該時鐘的高電平期間為穩定的低電平。 如果接收器是主控器,則在它收到最后一個字節后,發送一個NACK信號,以通知被控發送器結束數據發送,并釋放數據線SDA,以便主控接收器發送一個停止信號P。
(4)數據有效性
IIC總線進行數據傳送時,時鐘信號為高電平期間,數據線上的數據必須保持穩定;只有在時鐘線上的信號為低電平期間,數據線上的高電平或低電平狀態才允許變化。即:數據在時鐘線SCL的上升沿到來之前就需準備好。并在在下降沿到來之前必須穩定。
(5)數據的傳達
在IIC總線上傳送的每一位數據都有一個時鐘脈沖相對應(或同步控制),即在SCL串行時鐘的配合下,在SDA上逐位地串行傳送每一位數據。數據位的傳輸是邊沿觸發。
二.代碼
關于IIC代碼部分的內容這里主要講解HAL塊部分的內容。這里HAL初始化的三個部分包括:引腳配置初始化,寫數據函數,讀數據函數。
在引腳配置的 IICInit 中對IIC的兩個 引腳(SCL,SDA)進行引腳的配置,并對IIC相關的庫函數進行配置,其中引腳的配置這里不再進行講解,需要注意的是GPIO的模式要配置成GPIO_MODE_AF_OD 復用開漏輸出或者GPIO_Mode_Out_PP數據寄存器輸出。
關于IIC的配置,IIC的模式,自身地址,
typedef struct
{
uint32_t I2C_ClockSpeed; /*設置SCL時鐘頻,此值不低于40000*/
uint16_t I2C_Mode; /* 指定工作模式,可選I2C模式和SMBUS模式*/
uint16_t I2C_DutyCycle; /*指定時鐘占空比,可選 low/high=2:1以及16:9模式*/
uint16_t I2C_OwnAddress1; /*指定自身的I2C設備地址 */
uint16_t I2C_Ack; /*使能或者關閉響應 (一般都是使能) */
uint16_t I2C_AcknowledgedAddress; /*指定地址的長度,可以位7位及10位 */
}I2C_InitTypeDef;
? I2C_ClockSpeed 設置I2C的傳輸速率,在調用初始化函數時,函數會根據我們輸入的數值經過運算后把時鐘因子寫入到I2C的時鐘控制寄存器CCR。而我們寫入的這個參數值不得高于400KHz。實際上由于CCR寄存器不能寫入小數類型的時鐘因子,影響到 SCL的實際頻率可能會低于本成員設置的參數值,這時除了通訊稍慢一點以外,不會對I2C的標準通訊造成其它影響。
? I2C_Mode 選擇I2C的使用方式,有I2C模式(I2C_Mode_I2C )和SMBus主、從模式(I2C_Mode_SMBusHost、 I2C_Mode_SMBusDevice ) 。 I2C不需要在此處區分主從模式,直接設置I2C_Mode_I2C即可。
? I2C_DutyCycle 設置I 2C的SCL線時鐘的占空比。該配置有兩個選擇,分別為低電平時間比高電平時間為2:1 ( I2C_DutyCycle_2)和16:9 (I2C_DutyCycle_16_9)。其實這兩個模式的比例差別并不大,一般要求都不會如此嚴格,這里隨便選就可以了。
? I2C_OwnAddress1 配置STM32的I2C設備自己的地址,每個連接到I2C總線上的設備都要有一個自己的地址,作為主機也不例外。地址可設置為7位或10位(受下面 I2C_AcknowledgeAddress成員決定),只要該地址是I2C總線上唯一的即可。 STM32的I2C外設可同時使用兩個地址,即同時對兩個地址作出響應,這個結構成員I2C_OwnAddress1配置的是默認的、OAR1寄存器存儲的地址,若需要設置第二個地址寄存器OAR2,可使用 I2C_OwnAddress2Config函數來配置,OAR2不支持10位地址。
? I2C_Ack_Enable 配置I 2C應答是否使能,設置為使能則可以發送響應信號。一般配置為允許應答(I2C_Ack_Enable),這是絕大多數遵循I 2C標準的設備的通訊要求,改為禁止應答(I2C_Ack_Disable)往往會導致通訊錯誤。
? I2C_AcknowledgeAddress 選擇I2C的尋址模式是7位還是10位地址。這需要根據實際連接到I2C總線上設備的地址進行選擇,這個成員的配置也影響到 I2C_OwnAddress1成員,只有這里設置成10位模式時, I2C_OwnAddress1才支持10位地址。
利用庫函數版本編寫HAL代碼部分如下:
void IICInit(void)
{
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
/* 配置硬件IIC需要的變量 */
I2C_InitTypeDef I2C_InitStructure;
/* 使能與 I2C1 有關的時鐘 */
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB,ENABLE );
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
/* PB6-I2C1_SCL、PB7-I2C1_SDA*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*IIC外設初始化*/
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ; //配置為普通IIC模式
//I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
//I2C_InitStructure.I2C_OwnAddress1 = SlaveAddress;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //使能自動應答
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 50000; //5K的速度?
/* I2C1 初始化 */
I2C_Init(I2C1, &I2C_InitStructure);
/* 使能 I2C1 */
I2C_Cmd (I2C1,ENABLE);
/*允許應答模式*/
I2C_AcknowledgeConfig(I2C1, ENABLE);
}
void ByteWrite(u8 addr,u8 dataValue)
{
I2C_GenerateSTART(I2C1,ENABLE);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1,addr);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1,dataValue);
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1,ENABLE);
}
u8 ByteRead(u8 addr)
{
u8 dataValue;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));
I2C_GenerateSTART(I2C1,ENABLE);//起始信號
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Transmitter);//發送設備地址+寫信號
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//
I2C_Cmd(I2C1,ENABLE);
I2C_SendData(I2C1,addr);//發送存儲單元地址,從0開始
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTART(I2C1,ENABLE);//起始信號
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1,HMC_ADDR,I2C_Direction_Receiver);//發送設備地址+讀信號
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
I2C_AcknowledgeConfig(I2C1,DISABLE);
I2C_GenerateSTOP(I2C1,ENABLE);
while(!(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)));
dataValue=I2C_ReceiveData(I2C1);//讀出寄存器數據
return dataValue;
}
審核編輯:湯梓紅
-
IIC
+關注
關注
11文章
300瀏覽量
38311 -
串行總線
+關注
關注
1文章
182瀏覽量
30615 -
代碼
+關注
關注
30文章
4780瀏覽量
68527
發布評論請先 登錄
相關推薦
評論