IIC概述與軟件模擬IIC
1 IIC總線概述
1.1 基本概念
內(nèi)部集成電路(Inter Integrated circuit )的簡(jiǎn)稱叫做IIC,是一種簡(jiǎn)單的、半雙工同步通信的串行通信接口,IIC總線是上世紀(jì)80年代(1982年)由飛利浦公司設(shè)計(jì)出來,當(dāng)時(shí)的目的是為了給MCU和外圍芯片提供更簡(jiǎn)單的交互方式。
1.2 引腳說明
IIC總線只需要兩根引腳就可以實(shí)現(xiàn)通信,一根是數(shù)據(jù)線SDA,另一根是時(shí)鐘線SCL,所有通過IIC接口通信的外圍器件都掛載在IIC總線上,通過這種機(jī)制就可以實(shí)現(xiàn)多機(jī)通信。
可以看到,外圍器件的時(shí)鐘線和數(shù)據(jù)線都是掛載在IIC總線(由主控芯片提供),并且在空閑狀態(tài)下所有器件的時(shí)鐘線SCL和數(shù)據(jù)線SDA都被總線的上拉電阻拉高,這樣就可以把SDA引腳和SCL引腳設(shè)置為開漏模式即可,好處是防止短路。
每個(gè)掛載在IIC總線上的外圍器件都有獨(dú)立的器件地址,主機(jī)發(fā)送開始信號(hào)后,只需要發(fā)送想要通信的設(shè)備的地址,如果設(shè)備收到地址并且匹配正確,則開始進(jìn)行單獨(dú)通信。
1.3 通信速率
IIC總線支持不同的通信速率,但是一般常用的標(biāo)準(zhǔn)速率100KHZ,但是有的外圍器件可以支持高達(dá)400KHZ的通信速率,而由于IIC總線是半雙工通信,所以同一時(shí)刻只能接收或者發(fā)送,也就是說,IIC總線一般是為了控制,不適合作為大量數(shù)據(jù)傳輸?shù)?a target="_blank">接口。
1.4 通信過程
接口可以下述4種模式中的一種運(yùn)行:
- 從發(fā)送器模式
- 從接收器模式
- 主發(fā)送器模式
- 主接收器模式
默認(rèn)狀態(tài)下工作于從模式 。 接口在生成起始條件后自動(dòng)地從從模式切換到主模式 (誰先發(fā)送開始信號(hào),誰就作為主機(jī))。當(dāng)仲裁丟失或產(chǎn)生停止信號(hào)時(shí),則從主模式切換到從模式,從而實(shí)現(xiàn)多主模式功能。
通信流
- 主模式時(shí),I2C接口啟動(dòng)數(shù)據(jù)傳輸并產(chǎn)生時(shí)鐘信號(hào)。串行數(shù)據(jù)傳輸總是以起始條件開始并以停止條件結(jié)束。起始條件和停止條件都是在主模式下由軟件控制產(chǎn)生。
- 從模式時(shí),I2C接口能識(shí)別它自己的地址(7位或10位)和廣播呼叫地址。軟件能夠控制開啟或禁止廣播呼叫地址的識(shí)別。
- 數(shù)據(jù)和地址按8位/字節(jié)進(jìn)行傳輸,高位在前。跟在起始條件后的1或2個(gè)字節(jié)是地址(7位模式為1個(gè)字節(jié),10位模式為2個(gè)字節(jié))。地址只在主模式發(fā)送。
- 在一個(gè)字節(jié)傳輸?shù)?個(gè)時(shí)鐘后的第9個(gè)時(shí)鐘期間,接收器必須回送一個(gè)應(yīng)答位(ACK)給發(fā)送器。參考下圖。
可以看到,在建立通信的時(shí)候主機(jī)需要發(fā)送 開始信號(hào) ,緊接著主機(jī)需要發(fā)出從器件的 設(shè)備地址 (7bit+1bit),從設(shè)備的物理地址是7bit,但是由于只有一根數(shù)據(jù)線,就需要說清楚數(shù)據(jù)的傳輸方向,數(shù)據(jù)的傳輸方向通過從設(shè)備的地址最低位進(jìn)行表示(最低位是0,表示寫操作,最低位是1,表示讀操作),IIC總線提供了 應(yīng)答機(jī)制 ,也就是說從機(jī)收到了1個(gè)字節(jié)的數(shù)據(jù)之后,會(huì)在第九個(gè)脈沖發(fā)送給主機(jī)一個(gè)應(yīng)答信號(hào)(1bit),如果主機(jī)收到從機(jī)的應(yīng)答信號(hào),則主機(jī)可以繼續(xù)發(fā)送數(shù)據(jù),反之,如果主機(jī)沒有收到從機(jī)發(fā)送的應(yīng)答信號(hào),那主機(jī)就不應(yīng)該繼續(xù)發(fā)送數(shù)據(jù),而是應(yīng)該主動(dòng)發(fā)出一個(gè) 停止信號(hào) ,表示停止通信。
2 軟件模擬IIC的實(shí)現(xiàn)
2.1 IIC初始化
// ---------- software_iic.h ----------
#ifndef __SOFTWARE_IIC_H__
#define __SOFTWARE_IIC_H__
#include "main.h"
#include "tim.h"
#include "gpio.h"
#define DLY_TIM_Handle (&htim1)
// SCL: PB10, SDA: PB11
#define IIC_SCL_PORT GPIOB
#define IIC_SCL_PIN GPIO_PIN_10
#define IIC_SDA_PORT GPIOB
#define IIC_SDA_PIN GPIO_PIN_11
#define IIC_SDA_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define IIC_SCL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define IIC_SCL_WRITE_UP() HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_SET)
#define IIC_SCL_WRITE_DOWN() HAL_GPIO_WritePin(IIC_SCL_PORT, IIC_SCL_PIN, GPIO_PIN_RESET)
#define IIC_SDA_WRITE_UP() HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_SET)
#define IIC_SDA_WRITE_DOWN() HAL_GPIO_WritePin(IIC_SDA_PORT, IIC_SDA_PIN, GPIO_PIN_RESET)
#define IIC_SDA_READ() HAL_GPIO_ReadPin(IIC_SDA_PORT, IIC_SDA_PIN)
void delay_us(uint16_t nus);
void IIC_Init(void);
void IIC_SDA_OutputMode(void);
void IIC_SDA_InputMode(void);
void IIC_StartSignal(void);
void IIC_StopSignal(void);
void IIC_SendBytes(uint8_t data);
uint8_t IIC_ReadBytes(void);
uint8_t IIC_WaitACK(void);
void IIC_MasterACK(uint8_t ack);
#endif
// ---------- software_iic.c ----------
void IIC_Init(void)
{
// 初始化SCL和SDA為開漏輸出
GPIO_InitTypeDef GPIO_InitStruct = {0};
IIC_SDA_GPIO_CLK_ENABLE();
IIC_SCL_GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = IIC_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(IIC_SCL_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = IIC_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
// 初始化SCL和SDA為高電平
IIC_SCL_WRITE_UP();
IIC_SDA_WRITE_UP();
}
2.2 IIC模式
// SDA輸出模式
void IIC_SDA_OutputMode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
IIC_SDA_GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = IIC_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
}
// SDA輸入模式
void IIC_SDA_InputMode(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
IIC_SDA_GPIO_CLK_ENABLE();
GPIO_InitStruct.Pin = IIC_SDA_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(IIC_SDA_PORT, &GPIO_InitStruct);
}
2.3 開始信號(hào)
開始信號(hào)由主機(jī)發(fā)出,表示打算和所有的從器件進(jìn)行通信,IIC總線規(guī)定在SCL時(shí)鐘線保持高電平期間,把SDA數(shù)據(jù)線拉低,表示開始信號(hào)。
// IIC開始信號(hào)
void IIC_StartSignal(void)
{
IIC_SDA_OutputMode(); // 設(shè)置SDA為輸出模式
// 確保SCL和SDA都是高電平
IIC_SCL_WRITE_UP();
IIC_SDA_WRITE_UP();
// 拉低SDA,產(chǎn)生一個(gè)下降沿
// 一般常用的IIC總線標(biāo)準(zhǔn)速率為100kHz,即每個(gè)時(shí)鐘周期為10us,故SDA低電平應(yīng)持續(xù)5us
IIC_SDA_WRITE_DOWN(); // SDA拉低
delay_us(6); // 為了保證兼容性,這里延時(shí)6us
// 拉低SCL,表示準(zhǔn)備通信
IIC_SCL_WRITE_DOWN(); // SCL拉低
}
如何實(shí)現(xiàn)微秒級(jí)的延時(shí)可以參考下文
STM32基于HAL庫實(shí)現(xiàn)微秒延時(shí)
2.4 停止信號(hào)
停止信號(hào)由主機(jī)發(fā)出,表示不打算和從器件繼續(xù)通信,IIC總線規(guī)定在SCL時(shí)鐘線保持高電平期間,把SDA數(shù)據(jù)線拉高,表示停止信號(hào)。
// IIC停止信號(hào)
void IIC_StopSignal(void)
{
IIC_SDA_OutputMode(); // 設(shè)置SDA為輸出模式
// 確保SCL和SDA都是低電平
IIC_SCL_WRITE_DOWN();
IIC_SDA_WRITE_DOWN();
// 拉高SCL,產(chǎn)生一個(gè)上升沿
// 一般常用的IIC總線標(biāo)準(zhǔn)速率為100kHz,即每個(gè)時(shí)鐘周期為10us,故SCL高電平應(yīng)持續(xù)5us
IIC_SCL_WRITE_UP();
delay_us(5);
IIC_SDA_WRITE_UP(); // 拉高SDA,表示通信結(jié)束
delay_us(5); // 確保SDA的電平可以被其他器件檢測(cè)到
}
2.5 數(shù)據(jù)發(fā)送
在主機(jī)發(fā)送開始信號(hào)后,就可以發(fā)送數(shù)據(jù)或者地址,IIC總線規(guī)定數(shù)據(jù)的收發(fā)都是 MSB (高位先出),由于只有一個(gè)數(shù)據(jù)線,所以IIC采用串行方式把數(shù)據(jù)的每個(gè)bit位發(fā)出去。
由于SCL提供的脈沖周期是有規(guī)律的,所以IIC總線規(guī)定只能在SCL脈沖周期的高電平期間進(jìn)行數(shù)據(jù)的讀取或者寫入,在SCL脈沖周期的低電平期間可以進(jìn)行數(shù)據(jù)的修改。
// 主機(jī)發(fā)送數(shù)據(jù)
void IIC_SendBytes(uint8_t Data)
{
uint8_t i = 0;
IIC_SDA_OutputMode(); // 設(shè)置SDA為輸出模式
// 確保SCL和SDA都是低電平
IIC_SCL_WRITE_DOWN();
IIC_SDA_WRITE_DOWN();
// 開始發(fā)送8位數(shù)據(jù)
for (i = 0; i < 8; i++)
{
// SCL低電平期間主機(jī)準(zhǔn)備數(shù)據(jù)
if (Data & (1 < < (7 - i))) // 判斷數(shù)據(jù)的第7-i位是否為1
{
IIC_SDA_WRITE_UP(); // 如果為1,SDA拉高
}
else
{
IIC_SDA_WRITE_DOWN();// 如果為0,SDA拉低
}
delay_us(5); // 至此,數(shù)據(jù)準(zhǔn)備完畢
// 拉高SCL,主機(jī)發(fā)送數(shù)據(jù)
IIC_SCL_WRITE_UP();
delay_us(5); // 至此,數(shù)據(jù)發(fā)送完畢
// 拉低SCL,準(zhǔn)備發(fā)送下一個(gè)數(shù)據(jù)
IIC_SCL_WRITE_DOWN();
delay_us(5);
}
}
2.6 數(shù)據(jù)接收
在主機(jī)發(fā)送開始信號(hào)后,就可以發(fā)送數(shù)據(jù)或者地址,IIC總線規(guī)定數(shù)據(jù)的收發(fā)都是MSB(高位先出),由于只有一個(gè)數(shù)據(jù)線,所以IIC采用串行方式把數(shù)據(jù)的每個(gè)bit位發(fā)出去。
由于SCL提供的脈沖周期是有規(guī)律的,所以IIC總線規(guī)定只能在SCL脈沖周期的高電平期間進(jìn)行數(shù)據(jù)的讀取或者寫入,在SCL脈沖周期的低電平期間可以進(jìn)行數(shù)據(jù)的修改。
// 主機(jī)接收數(shù)據(jù)
uint8_t IIC_ReadBytes(void)
{
uint8_t i = 0;
uint8_t Data = 0; // 用于存儲(chǔ)接收到的數(shù)據(jù)
IIC_SDA_InputMode(); // 設(shè)置SDA為輸入模式
IIC_SCL_WRITE_DOWN(); // 確保SCL為低電平
// 開始接收8位數(shù)據(jù)
for (i = 0; i < 8; i++)
{
// 拉高SCL,主機(jī)準(zhǔn)備接收數(shù)據(jù)
IIC_SCL_WRITE_UP();
delay_us(5); // 至此,從機(jī)數(shù)據(jù)準(zhǔn)備完畢,主機(jī)開始接收
if (IIC_SDA_READ() == 1) // 主機(jī)收到1
{
Data |= (1 < < (7 - i)); // 將收到的1存儲(chǔ)到Data的第7-i位
}
/* 由于Data初始化為0000 0000,所以不需要else語句
else // 收到0
{
Data &= ~(1 < < (7 - i)); // 將收到的0存儲(chǔ)到Data的第7-i位
}
*/
// 拉低SCL,主機(jī)準(zhǔn)備接收下一個(gè)數(shù)據(jù)
IIC_SCL_WRITE_DOWN();
delay_us(5);
}
return Data; // 返回接收到的數(shù)據(jù)
}
2.7 應(yīng)答信號(hào)
IIC總線增加了應(yīng)答機(jī)制,在主機(jī)發(fā)送一個(gè)字節(jié)數(shù)據(jù)之后,從機(jī)在第9個(gè)脈沖周期進(jìn)行應(yīng)答,如果SDA為0,則表示應(yīng)答,如果SDA=1,則表示無應(yīng)答,如果從機(jī)沒有應(yīng)答,則主機(jī)應(yīng)該發(fā)送停止信號(hào),表示停止通信。這里分為兩種情況:
第一種:主機(jī)發(fā)送數(shù)據(jù),從機(jī)進(jìn)行應(yīng)答
// 主機(jī)發(fā)送數(shù)據(jù),從機(jī)進(jìn)行應(yīng)答
uint8_t IIC_WaitACK(void)
{
uint8_t ack;
IIC_SDA_InputMode(); // 設(shè)置SDA為輸入模式
IIC_SCL_WRITE_DOWN(); // 確保SCL是低電平
delay_us(5);
IIC_SCL_WRITE_UP(); // 拉高SCL,主機(jī)準(zhǔn)備接收從機(jī)的應(yīng)答信號(hào)
delay_us(5); // 至此,從機(jī)應(yīng)答信號(hào)準(zhǔn)備完畢,主機(jī)開始接收
// 如果從機(jī)應(yīng)答信號(hào)為0,表示從機(jī)接收到數(shù)據(jù)
if (IIC_SDA_READ() == 0)
{
ack = 0;
}
else // 如果從機(jī)應(yīng)答信號(hào)為1,表示從機(jī)沒有接收到數(shù)據(jù)
{
ack = 1;
}
IIC_SCL_WRITE_DOWN(); // 拉低SCL,主機(jī)忽略數(shù)據(jù)
delay_us(5);
return ack; // 返回從機(jī)的應(yīng)答信號(hào)
}
第二種:從機(jī)發(fā)送數(shù)據(jù),主機(jī)進(jìn)行應(yīng)答
// 從機(jī)發(fā)送數(shù)據(jù),主機(jī)進(jìn)行應(yīng)答,0表示應(yīng)答,1表示不應(yīng)答
void IIC_MasterACK(uint8_t ack)
{
IIC_SDA_OutputMode(); // 設(shè)置SDA為輸出模式
// 確保SCL和SDA都是低電平
IIC_SCL_WRITE_DOWN();
IIC_SDA_WRITE_DOWN();
if (ack == 0) // 如果ack為0,表示主機(jī)應(yīng)答
{
IIC_SDA_WRITE_DOWN(); // SDA拉低
}
else // 如果ack為1,表示主機(jī)不應(yīng)答
{
IIC_SDA_WRITE_UP(); // SDA拉高
}
delay_us(5); // 至此,應(yīng)答信號(hào)準(zhǔn)備完畢
// 拉高SCL,主機(jī)發(fā)出應(yīng)答信號(hào)
IIC_SCL_WRITE_UP();
delay_us(5); // 至此,應(yīng)答信號(hào)發(fā)送完畢
// 拉低SCL,從機(jī)忽略數(shù)據(jù)
IIC_SCL_WRITE_DOWN();
delay_us(5);
}
-
集成電路
+關(guān)注
關(guān)注
5424文章
12054瀏覽量
368391 -
mcu
+關(guān)注
關(guān)注
146文章
17978瀏覽量
366656 -
STM32
+關(guān)注
關(guān)注
2293文章
11032瀏覽量
364767 -
總線
+關(guān)注
關(guān)注
10文章
2959瀏覽量
89738 -
IIC
+關(guān)注
關(guān)注
11文章
306瀏覽量
39491
發(fā)布評(píng)論請(qǐng)先 登錄
在FreeRTOS操作系統(tǒng)下的使用軟件模擬IIC的驅(qū)動(dòng)例程
STM32 IIC和USART接口的基礎(chǔ)知識(shí)點(diǎn)匯總,絕對(duì)實(shí)用
IIC的通信原理及分析IIC的真實(shí)波形
介紹IIC總線特點(diǎn)與STM32 IIC:為ARM初學(xué)者導(dǎo)航(11)
實(shí)現(xiàn)模擬IIC與EEPROM(24c02)通信(基于STM32F103ZET6)

STC——IIC外設(shè)使用及模擬IIC

從IIC實(shí)測(cè)波形入手,搞懂IIC通信

[轉(zhuǎn)]從IIC實(shí)測(cè)波形入手,搞懂IIC通信
![[轉(zhuǎn)]從<b class='flag-5'>IIC</b>實(shí)測(cè)波形入手,搞懂<b class='flag-5'>IIC</b>通信](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
評(píng)論