1.I2C通信總結
在STM32中for循環一次需要6個時鐘週期。
I2C總線是NXP公司設計。
1.1協議
參考:
https://blog.csdn.net/zhanghuaichao/article/details/48266309
l 物理層
Ø 只要求兩條總線線路,一條數據線,一條時鐘線,半雙工
Ø 每個連接到總線的器件都可以通過唯一的地址和其他器件通信,主機/從機角色和地址可配置。
Ø 傳輸速率在標準模式下可以達到100kb/s,快速模式下可以達到400kb/s。
l 協議層
Ø 數據的有效性:在時鐘高電平週期內,SDA線上的數據必須保持穩定,數據線僅可以在時鐘SCL爲低電平時改變。
Ø 起始和結束條件
起始條件:當SCL爲高電平的時候,SDA線上由高到低的跳變被定義爲起始條件;
結束條件:當SCL爲高電平的時候,SDA線上由低到高的跳變被定義爲停止條件。
Ø 應答
每當主機向從機發送完一個字節的數據,主機總是需要等待從機給出一個應答信號,以確認從機是否成功接收到了數據,從機應答主機所需要的時鐘仍是主機提供的,應答出現在每一次主機完成8個數據位傳輸後緊跟着的時鐘週期,低電平0表示應答,1表示非應答。
Ø 數據幀格式
在起始信號後必須傳送一個從機的地址(7位),第8位是數據的傳送方向位(R/T),用“0”表示主機發送數據(T),“1”表示主機接收數據(R),每次數據傳送總是由主機產生的終止信號結束。
1.2 應用-EEPROM通信
l 引腳功能表
引腳名稱 | 功能描述 | |
---|---|---|
A0,A1,A2 | 芯片地址 | |
SDA | 數據線 | |
SCL | 時鐘線 | |
WP | 寫保護,高電平禁止寫操作,低電平允許寫 |
l AT24CXX芯片地址
芯片型號 | 總大小字節 | 地址 | 總線最多可接入數量 | 備註 | ||
---|---|---|---|---|---|---|
高4位 | 外部引腳 | 讀寫控制位 | ||||
AT24C64 | 8K | 1010 | A2 A1 A0 | R/W,1-讀,0-寫 | 8 | SOIC封裝可計入8,但其他封裝要參考手冊 |
AT24C128 | 16K | 1010 | A2 A1 A0 | R/W,1-讀,0-寫 | 8 | |
AT24C256 | 64K | 1010 | A2 A1 A0 | R/W,1-讀,0-寫 | 8 | |
AT24C512 | 128K | 1010 | A2 A1 A0 | R/W,1-讀,0-寫 | 8 |
l 讀操作實例
/*
*********************************************************************************************************
* 函 數 名: ee_ReadBytes
* 功能說明: 從串行EEPROM指定地址處開始讀取若干數據
* 形 參: _usAddress : 起始地址
* _usSize : 數據長度,單位爲字節
* _pReadBuf : 存放讀到的數據的緩衝區指針
* 返 回 值: 0 表示失敗,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
uint16_t i;
/* 採用串行EEPROM隨即讀取指令序列,連續讀取若干字節 */
/* 第1步:發起I2C總線啓動信號 */
i2c_Start();
/* 第2步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
i2c_SendByte(EE_DEV_ADDR | I2C_WR); /* 此處是寫指令 */
/* 第3步:發送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
/* 第4步:發送字節地址,24C02只有256字節,因此1個字節就夠了,如果是24C04以上,那麼此處需要連發多個地址 */
if (EE_ADDR_BYTES == 1)
{
i2c_SendByte((uint8_t)_usAddress);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
}
else
{
i2c_SendByte(_usAddress >> 8);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
i2c_SendByte(_usAddress);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
}
/* 第6步:重新啓動I2C總線。下面開始讀取數據 */
i2c_Start();
/* 第7步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
i2c_SendByte(EE_DEV_ADDR | I2C_RD); /* 此處是讀指令 */
/* 第8步:發送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
/* 第9步:循環讀取數據 */
for (i = 0; i < _usSize; i++)
{
_pReadBuf[i] = i2c_ReadByte(); /* 讀1個字節 */
/* 每讀完1個字節後,需要發送Ack, 最後一個字節不需要Ack,發Nack */
if (i != _usSize - 1)
{
i2c_Ack(); /* 中間字節讀完後,CPU產生ACK信號(驅動SDA = 0) */
}
else
{
i2c_NAck(); /* 最後1個字節讀完後,CPU產生NACK信號(驅動SDA = 1) */
}
}
/* 發送I2C總線停止信號 */
i2c_Stop();
return 1; /* 執行成功 */
cmd_fail: /* 命令執行失敗後,切記發送停止信號,避免影響I2C總線上其他設備 */
/* 發送I2C總線停止信號 */
i2c_Stop();
return 0;
}
/*
*********************************************************************************************************
* 函 數 名: ee_WriteBytes
* 功能說明: 向串行EEPROM指定地址寫入若干數據,採用頁寫操作提高寫入效率
* 形 參: _usAddress : 起始地址
* _usSize : 數據長度,單位爲字節
* _pWriteBuf : 存放讀到的數據的緩衝區指針
* 返 回 值: 0 表示失敗,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
uint16_t i,m;
uint16_t usAddr;
/*
寫串行EEPROM不像讀操作可以連續讀取很多字節,每次寫操作只能在同一個page。
對於24xx02,page size = 8
簡單的處理方法爲:按字節寫操作模式,每寫1個字節,都發送地址
爲了提高連續寫的效率: 本函數採用page wirte操作。
*/
usAddr = _usAddress;
for (i = 0; i < _usSize; i++)
{
/* 當發送第1個字節或是頁面首地址時,需要重新發起啓動信號和地址 */
if ((i == 0) || (usAddr & (EE_PAGE_SIZE - 1)) == 0)
{
/* 第0步:發停止信號,啓動內部寫操作 */
i2c_Stop();
/* 通過檢查器件應答的方式,判斷內部寫操作是否完成, 一般小於 10ms
CLK頻率爲200KHz時,查詢次數爲30次左右
*/
for (m = 0; m < 1000; m++)
{
/* 第1步:發起I2C總線啓動信號 */
i2c_Start();
/* 第2步:發起控制字節,高7bit是地址,bit0是讀寫控制位,0表示寫,1表示讀 */
i2c_SendByte(EE_DEV_ADDR | I2C_WR); /* 此處是寫指令 */
/* 第3步:發送一個時鐘,判斷器件是否正確應答 */
if (i2c_WaitAck() == 0)
{
break;
}
}
if (m == 1000)
{
goto cmd_fail; /* EEPROM器件寫超時 */
}
/* 第4步:發送字節地址,24C02只有256字節,因此1個字節就夠了,如果是24C04以上,那麼此處需要連發多個地址 */
if (EE_ADDR_BYTES == 1)
{
i2c_SendByte((uint8_t)usAddr);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
}
else
{
i2c_SendByte(usAddr >> 8);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
i2c_SendByte(usAddr);
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
}
}
/* 第6步:開始寫入數據 */
i2c_SendByte(_pWriteBuf[i]);
/* 第7步:發送ACK */
if (i2c_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件無應答 */
}
usAddr++; /* 地址增1 */
}
/* 命令執行成功,發送I2C總線停止信號 */
i2c_Stop();
return 1;
cmd_fail: /* 命令執行失敗後,切記發送停止信號,避免影響I2C總線上其他設備 */
/* 發送I2C總線停止信號 */
i2c_Stop();
return 0;
}