I2C通信總結

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章