0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于單片機的I2C總線與AT24C01實驗 I2C總線通信原理講解

CHANBAEK ? 來源:RYMCU ? 作者:RYMCU ? 2023-07-27 16:51 ? 次閱讀

概述

單片機應(yīng)用系統(tǒng)中,串行通信總線技術(shù)是非常重要的通信手段。常用的串行總線通信方式包括異步串行通信 UART、I2C(Inter IC BUS)、單總線(One WIRE BUS)以及 SPI 總線(Serial Peripheral Interface BUS)等。單片機的串口通信為 UART 的一種,DS18B20 的通信方式為單總線。采用 I2C 總線通信方式的常用器件包括 E2PROM 存儲器件 AT24C01 以及 AD/DA 器件 PCF8951,這章內(nèi)容主要講解 I2C 總線通信工作原理并結(jié)合 AT24C01 進行應(yīng)用試驗。

13.1 I2C 總線通信原理

I2C(Inter-Integrated Circuit)總線是由 PHILIP S 公司開發(fā)的兩線式串行通信總線,由于連接主機以及外圍設(shè)備。兩根數(shù)據(jù)線一個為時鐘線 SCL,另一根為數(shù)據(jù)線 SDA,可實現(xiàn)數(shù)據(jù)的發(fā)送或接收。通常將 I2C 通信速率分為:低速模式 100Kbit/s、快速模式 400Kbit/s 以及高速模式 3.4Mbit/s,I2C 器件為向下兼容模式,一般所用 I2C 器件均支持低速模式。I2C 通信器件典型電路如下圖所示:
圖片

如上圖所示,在 I2C 總線上掛載多個外圍器件,總線與電源之間配置了上拉電阻,使所有器件之間形成了“線與”的邏輯關(guān)系,任何一個器件將總線拉低,總線將保持低電平,因此任意一個器件都可以當(dāng)成主設(shè)備或者從設(shè)備。

I2C 通信最底層的時序操作包含四種類型的信號,所用基于 I2C 總線的外圍器件都是在這五種底層信號的基礎(chǔ)上進行數(shù)據(jù)的讀寫,這五種信號分別是:

1) 起始信號;
2. 停止信號;
3. 寫字節(jié)信號;
4. 讀字節(jié)并發(fā)送應(yīng)答信號;
5. 讀字節(jié)并發(fā)送非應(yīng)答信號。

13.1.1 I2C 通信起始、停止信號

起始信號,功能為通知 I2C 器件可以開始進行數(shù)據(jù)操作,操作時序為:當(dāng) SCL 為高電平時,SDA 由高電平向低電平跳變。停止信號,功能為通知 I2C 器件數(shù)據(jù)操作已結(jié)束,操作時序為:當(dāng) SCL 為高電平時,SDA 由低電平向高電平跳變。時序如下圖所示:
圖片
I2C 起始信號、停止信號時序 C 語言函數(shù)如下所示:

void Delay_I2C(void) 
{//延時函數(shù),設(shè)置傳輸速率
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//總線起始信號
void Start_I2C(void)
{
	//SCL高電平期間,拉低SDA
	SCL_I2C = 0;
	SDA_I2C = 1;//在SCL低電平期間先將SDA拉高,為起始信號做準備
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 0;//拉低SDA,發(fā)送起始信號
	Delay_I2C();	
	SCL_I2C = 0;
}
//總線停止信號
void Stop_I2C(void)
{
	//SCL高電平期間,拉高SDA
	SCL_I2C = 0;
	SDA_I2C = 0;//在SCL低電平期間先將SDA拉低,為停止信號做準備
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 1;//拉高SDA,發(fā)送停止信號
	Delay_I2C();	
	SCL_I2C = 0;	
}

13.1.2 I2C 寫字節(jié)信號

I2C 寫字節(jié)信號,功能為向總線寫入 1 字節(jié)的數(shù)據(jù),操作時序下圖所示:
圖片

在寫入數(shù)據(jù)的過程中,數(shù)據(jù)順序為從高位到低位,最先寫入的數(shù)據(jù)為 bit7,依次到 bit0 共 8 位數(shù)據(jù)。如果接收器件收到了上述 1 字節(jié)的數(shù)據(jù),會在 SCL 的第 9 個周期的高電平期間將 SDA 拉低為“0”,這個第 9 位數(shù)據(jù)稱為應(yīng)答位 ACK,作用為通知主機已經(jīng)收到了 1 字節(jié)的數(shù)據(jù)。因此,在主機程序中通過 ACK 位判斷 1 字節(jié)數(shù)據(jù)是否寫入成功。在寫數(shù)據(jù)的過程中要求,數(shù)據(jù)在 SCL 高電平期間要保持 SDA 數(shù)據(jù)穩(wěn)定,在 SCL 低電平期間,SDA 可由高電平變?yōu)榈碗娖交蛘叩碗娖阶優(yōu)楦唠娖?,如下圖所示。
圖片
I2C 寫字節(jié)信號 C 語言函數(shù)代碼如下圖所示:

//I2C寫入字節(jié)dat,返回應(yīng)答信號
bit Wr_I2C(unsigned char dat)
{
	bit ack; //存儲應(yīng)答位
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量

	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		if((mask & dat)==0) SDA_I2C=0;
		else                SDA_I2C=1;

		Delay_I2C();
		SCL_I2C = 1;
		Delay_I2C();		
		SCL_I2C = 0;  //完成一位的傳送
	}

	SDA_I2C=1;	//主機釋放總線
	Delay_I2C();
	SCL_I2C = 1;
	ack = SDA_I2C;//獲取應(yīng)答位
	Delay_I2C();
	SCL_I2C = 0;

	return ack; //返回0寫入成功,返回1寫入失敗
}

13.1.3 I2C 讀字節(jié)并發(fā)送應(yīng)答信號

I2C 讀字節(jié)并發(fā)送應(yīng)答信號時序與圖 13-4 基本相同,只不過 bit7-bit0 由 I2C 從器件給出,在 SCL 高電平期間主機將數(shù)據(jù)讀取,第 9 位應(yīng)答信號 ACK 由主機給出,ACK 為“0”表示主機后續(xù)還要繼續(xù)讀取數(shù)據(jù),為“1”時主機不再讀取后續(xù)數(shù)據(jù),可以結(jié)束通信。C 語言函數(shù)如下圖所示:

//I2C讀操作,并發(fā)送應(yīng)答信號
unsigned char RdACK_I2C(void)
{
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量
	unsigned char dat;

	 SDA_I2C=1;//確保主機釋放SDA
	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//為0時,dat對應(yīng)位清零
		else           dat |=  mask;//否則置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=0;	 //8位數(shù)據(jù)傳送完后,拉低SDA發(fā)送應(yīng)答信號
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

13.1.4 I2C 讀字節(jié)并發(fā)送非應(yīng)答信號

與讀字節(jié)并發(fā)送應(yīng)答信號相同,唯一的區(qū)別為主機發(fā)出非應(yīng)答信號,即 ACK=1,主機不再讀取后續(xù)數(shù)據(jù),可以結(jié)束通信。C 語言函數(shù)如下圖所示:

//I2C讀操作,并發(fā)送非應(yīng)答信號
unsigned char RdNAK_I2C(void)
{
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量
	unsigned char dat;

	 SDA_I2C=1;//確保主機釋放SDA
	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//為0時,dat對應(yīng)位清零
		else           dat |=  mask;//否則置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=1;	  //8位數(shù)據(jù)傳送完后,拉高SDA發(fā)送非應(yīng)答信號
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

將上述 5 個底層 I2C 總線操作函數(shù)放到文件“Drive_I2C.c”以及“Drive_I2C.h”。

“Drive_I2C.h”完整代碼如下:

#ifndef __I2C_H__
#define __I2C_H__

extern void Start_I2C(void); //起始信號
extern void Stop_I2C(void);	 //停止信號
extern unsigned char RdACK_I2C(void); //讀字節(jié)并發(fā)送應(yīng)答信號
extern unsigned char RdNAK_I2C(void); //讀字節(jié)并發(fā)送非應(yīng)答信號
extern bit Wr_I2C(unsigned char dat); //讀字節(jié)信號

#endif

“Drive_I2C.c”完整代碼如下:

#include< reg52.h >
#include< intrins.h >


sbit SCL_I2C = P2^0;//總線管腳定義
sbit SDA_I2C = P2^1;

void Delay_I2C(void) 
{//延時函數(shù),設(shè)置傳輸速率
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//總線起始信號
void Start_I2C(void)
{
	//SCL高電平期間,拉低SDA
	SCL_I2C = 0;
	SDA_I2C = 1;//在SCL低電平期間先將SDA拉高,為起始信號做準備
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 0;//拉低SDA,發(fā)送起始信號
	Delay_I2C();	
	SCL_I2C = 0;
}
//總線停止信號
void Stop_I2C(void)
{
	//SCL高電平期間,拉高SDA
	SCL_I2C = 0;
	SDA_I2C = 0;//在SCL低電平期間先將SDA拉低,為停止信號做準備
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 1;//拉高SDA,發(fā)送停止信號
	Delay_I2C();	
	SCL_I2C = 0;	
}
//I2C寫入字節(jié)dat,返回應(yīng)答信號
bit Wr_I2C(unsigned char dat)
{
	bit ack; //存儲應(yīng)答位
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量

	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		if((mask & dat)==0) SDA_I2C=0;
		else                SDA_I2C=1;

		Delay_I2C();
		SCL_I2C = 1;
		Delay_I2C();		
		SCL_I2C = 0;  //完成一位的傳送
	}

	SDA_I2C=1;	//主機釋放總線
	Delay_I2C();
	SCL_I2C = 1;
	ack = SDA_I2C;//獲取應(yīng)答位
	Delay_I2C();
	SCL_I2C = 0;

	return ack; //返回0寫入成功,返回1寫入失敗
}

//I2C讀操作,并發(fā)送非應(yīng)答信號
unsigned char RdNAK_I2C(void)
{
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量
	unsigned char dat;

	 SDA_I2C=1;//確保主機釋放SDA
	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//為0時,dat對應(yīng)位清零
		else           dat |=  mask;//否則置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=1;	  //8位數(shù)據(jù)傳送完后,拉高SDA發(fā)送非應(yīng)答信號
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

//I2C讀操作,并發(fā)送應(yīng)答信號
unsigned char RdACK_I2C(void)
{
	unsigned char mask;	//探測字節(jié)內(nèi)某一位值的掩碼變量
	unsigned char dat;

	 SDA_I2C=1;//確保主機釋放SDA
	for(mask=0x80;mask!=0;mask >>=1)//從高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//為0時,dat對應(yīng)位清零
		else           dat |=  mask;//否則置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=0;	 //8位數(shù)據(jù)傳送完后,拉低SDA發(fā)送應(yīng)答信號
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

13.1.5 I2C 一次通信時序

所有基于 I2C 總線通信設(shè)備都是以上面 5 條最底層操作為基礎(chǔ)的,完成一次完整的 I2C 通信時序如下圖所示:
圖片

如圖所示,一次完整的 I2C 總線通信至少包含起始信號 、一次字節(jié)讀或?qū)懀蛘叨啻巫x或?qū)懀约巴V剐盘?。在起始信號與停止信號之間讀或?qū)懙木唧w內(nèi)容與 I2C 器件本身的上層通信協(xié)議有關(guān)。接下來我們將講解基于 I2C 總線通信技術(shù)的 E2PROM 存儲器 AT24C01 的上層通信協(xié)議以及具體使用實例。

13.2 E2PROM 存儲器 AT24C01 應(yīng)用

AT24C01 是 Atmel 公司生產(chǎn)的一款 E2PROM 數(shù)據(jù)存儲器,容量為 128 字節(jié),具有掉電不丟失的功能。在單片機中也有存儲器,一種為數(shù)據(jù)存儲器 RAM,一種為程序存儲器,在掉電的情況下 RAM 內(nèi)的數(shù)據(jù)會丟失,而程序存儲器一般不支持在線編程。然而在很多應(yīng)用場合我們希望把運行過程中重要的數(shù)據(jù)存儲下來,而在掉電的情況下數(shù)據(jù)不丟失。AT24C01 能滿足這樣的要求,它與單片之間通過 I2C 總線通信實現(xiàn)信息的交換。
圖片

下面介紹一下芯片管腳,A0~A2 為地址輸入引腳,SDA、SCL 為 I2C 總線接口,VCC,GND 分別為電源和地,WP 為寫保護管腳,當(dāng) WP 接高電平時,禁止外部對它進行寫數(shù)據(jù),只能讀取它的數(shù)據(jù)。如上圖所示,將 WP 接地,單片機即可對它進行讀也可以寫。

13.2.1 AT24C01 單字節(jié)寫通信

AT24C01 寫字節(jié)如下圖所示:
圖片

如上圖所示,AT24C01 的寫字節(jié)時序步驟如下:

1)起始信號;
2. 寫器件地址;
3. 寫存儲地址;
4. 寫存儲數(shù)據(jù);
5. 停止信號。

當(dāng) I2C 總線上掛載多個從器件時,單片機通過器件地址來區(qū)別器件,那在我們開發(fā)板上的 AT24C01 的器件地址是多少呢?AT24C01 器件地址如下所示:
圖片
如上圖所示,地址的高 4 位為“1010”固定值,即 0x5,后四位分別由 A2-A0、R/W 決定,在 RY-51 開發(fā)板上將器件的 A2-A0 都接地,如原理圖所示,因此為“000”,最后一位為讀寫方向位,當(dāng) R/W=0 時,表示我們接下來寫數(shù)據(jù),當(dāng) R/W=1 時,表示我們接下來要讀數(shù)據(jù)。很顯然我們這里是要寫數(shù)據(jù),因此 R/W=0。合并起來,要寫的地址為 0x50。第 3 步為寫存儲器地址,AT24C01 總共有 128 字節(jié)的存儲器,它的地址分別為 0x00~0x80,因此可以選擇任一地址存入數(shù)據(jù)。第 4 步為寫存儲數(shù)據(jù),即為你寫存儲的 8 位數(shù)據(jù)??偨Y(jié)上述,往 AT24C01 寫入一個字節(jié)數(shù)據(jù)函數(shù)如下所示:

//往AT24C01地址addr寫入單字節(jié)數(shù)據(jù)dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
	Start_I2C();
	Wr_I2C(0x50< 1);//通知地址50的器件,接下來寫操作
	Wr_I2C(addr);  	//寫入要操作的地址addr
	Wr_I2C(dat);		//向addr寫入數(shù)據(jù)dat
	Stop_I2C();
}

13.2.2 AT24C01 單字節(jié)讀通信

隨機讀取 AT24C01 單字節(jié)通信如下圖所示:
圖片
隨機讀取單字節(jié)數(shù)據(jù)通信協(xié)議如上圖所示,步驟如下:

1) 起始信號
2. 寫器件地址,方向為寫;
3. 寫存儲器地址 addr;
4. 起始信號;
5. 寫器件地址,方向為讀;
6. 讀單字節(jié)數(shù)據(jù),并發(fā)送非應(yīng)答信號
7. 停止信號。

第 1 步至第 3 步為告訴 AT24C01 我將從地址 addr 處讀取數(shù)據(jù),第 4 步到第 6 步為讀取存儲器地址 addr 處的數(shù)據(jù),并告訴 AT24C01 后面不再繼續(xù)讀數(shù)據(jù)了,第 7 步結(jié)束本次通信。具體函數(shù)代碼如下圖所示:

//讀取AT24C01存儲地址addr處的數(shù)據(jù)
unsigned char RdByte_AT24C01(unsigned char addr)
{
	unsigned char dat;	

	Start_I2C();
	Wr_I2C(0x50< 1);//通知地址50的器件,接下來寫操作
	Wr_I2C(addr); //寫入要操作的地址addr
	Start_I2C();
	Wr_I2C((0x50< 1)|0x01);//通知地址50的器件,接下來讀操作
	dat = RdNAK_I2C();//從地址addr讀出數(shù)據(jù),讀出數(shù)據(jù)后不應(yīng)答E2Prom
	Stop_I2C();

	return dat;
}

到這里我們便完成了對 AT24C01 單字節(jié)的讀、寫通信函數(shù),按照慣例我們將函數(shù)封裝到“Drive_AT 24C01.c”、“Drive_AT 24C01.h”,如下。

Drive_AT 24C01.h 代碼:

#ifndef __AT24C01_H__
#define __AT24C02_H__

extern void WrByte_AT24C01(unsigned char addr,unsigned char dat);//寫單字節(jié)
extern unsigned char RdByte_AT24C01(unsigned char addr); //讀單字節(jié)
extern void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//寫多字節(jié)
extern void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//讀多字節(jié)

#endif

Drive_AT 24C01.c 代碼:

#include< reg52.h >
#include"Drive_I2C.h"

//往AT24C01地址addr寫入單字節(jié)數(shù)據(jù)dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
	Start_I2C();
	Wr_I2C(0x50< 1);//通知地址50的器件,接下來寫操作
	Wr_I2C(addr);  	//寫入要操作的地址addr
	Wr_I2C(dat);		//向addr寫入數(shù)據(jù)dat
	Stop_I2C();
}
//讀取AT24C01存儲地址addr處的數(shù)據(jù)
unsigned char RdByte_AT24C01(unsigned char addr)
{
	unsigned char dat;	

	Start_I2C();
	Wr_I2C(0x50< 1);//通知地址50的器件,接下來寫操作
	Wr_I2C(addr); //寫入要操作的地址addr
	Start_I2C();
	Wr_I2C((0x50< 1)|0x01);//通知地址50的器件,接下來讀操作
	dat = RdNAK_I2C();//從地址addr讀出數(shù)據(jù),讀出數(shù)據(jù)后不應(yīng)答E2Prom
	Stop_I2C();

	return dat;
}
//多字節(jié)寫
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(len > 0)//檢測上一次是否完成所以數(shù)據(jù)寫操作
	{	
		while(1)
		{//循環(huán)檢測器件應(yīng)答信號
			Start_I2C();
			if(0 == Wr_I2C(0x50< 1)) break;//收到應(yīng)答,跳出循環(huán)
			Stop_I2C();//沒收到應(yīng)答,發(fā)送停止信號,繼續(xù)循環(huán)檢測
		}
		Wr_I2C(addr); //寫入要操作的初始地址addr
		
		while(len > 0)
		{
			Wr_I2C(*str++);//寫入一個字節(jié),并將字符串指針指向下一個字符
			len--;//字符數(shù)減1
			addr++;//存儲地址加1	
			if(0 == (addr & 0x07))//檢測是否到達了下一頁的起始地址,
				break;			//即上一個字節(jié)已經(jīng)寫到頁的最后邊界了
				           		//跳出停止繼續(xù)寫,每頁的起始地址后3位為0
								//因此判斷addr后3為是否為0即可	   
		}
		Stop_I2C();	
	}			
}
//多字節(jié)讀
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(1)
	{//循環(huán)檢測器件應(yīng)答信號
		Start_I2C();
		if(0 == Wr_I2C(0x50< 1)) break;//收到應(yīng)答,跳出循環(huán)
		Stop_I2C();//沒收到應(yīng)答,發(fā)送停止信號,繼續(xù)循環(huán)檢測
	}
	Wr_I2C(addr); //寫入要操作的初始地址addr
    Start_I2C();//再次發(fā)送起始信號
	Wr_I2C((0x50< 1)|0x01);//通知地址50的器件,接下來讀操作
	while(len > 1)
	{
		*str++ = RdACK_I2C();//讀字節(jié)并應(yīng)答
		len--;
	}
	*str = RdNAK_I2C();//最后一個字節(jié),讀字節(jié)并非應(yīng)答
	Stop_I2C();		
}
//尋址AT24C01
bit Addressing_AT24C01(unsigned char addr)
{
	bit ack;

	Start_I2C();
	ack =  Wr_I2C(addr< 1);
	Stop_I2C();

	return ack;
}

如上所示,所有的代碼都是以 I2C 通信的 5 個底層函數(shù)為基礎(chǔ)的,因此我們需要將“Drive_I2C.h”文件包含到代碼中。

13.2.3 AT24C01 單字節(jié)讀寫應(yīng)用

下面我們建立一個工程,寫一個實例來展示單片機對 AT24C01 的讀寫應(yīng)用。應(yīng)用的功能為首先往 AT24C01 存儲器地址 0x08 處寫入數(shù)據(jù) 110,然后從該處把數(shù)據(jù)讀出來顯示在 1602 液晶上,以此來驗證讀寫操作的正確性,主文件“MainAT24C01.c”代碼如下所示:

#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01頭文件
#include"Drive_1602.h"

#define uchar unsigned char
#define  uint unsigned int

sbit DU = P2^7;//數(shù)碼管段選、位選引腳定義
sbit WE = P2^6;	
uchar str[10]=0;
void main()
{
	uchar dat=0;
	Init_1602();
	P0 = 0xff;//關(guān)閉所有數(shù)碼
	WE = 1;
	WE = 0;

	//往AT24C01存儲器地址0x08處寫入數(shù)字110
	WrByte_AT24C01(0x08,110);
	Disp_1602_str(1,2,"AT24C02 test!");
	//讀取AT24C01存儲器地址0x08處的數(shù)據(jù)
	dat = RdByte_AT24C01(0x08);

	str[0]=dat/100+'0';
	str[1]=dat%100/10+'0';
	str[2]=dat%10+'0';
	//將數(shù)據(jù)顯示在1602的第2行第6列處
	Disp_1602_str(2,6,str);

	while(1);
}

將工程編譯后,下載到開發(fā)板驗證功能的正確性。

AT24C01 對寫時序有一個特殊的要求,當(dāng)完成一次數(shù)據(jù)通信后,需要延遲 tWR 才能開始下一次起始信號如下圖所示,tWR 為 AT24C01 內(nèi)部處理數(shù)據(jù)時間,查詢 AT24C01 數(shù)據(jù)手冊可知為 10ms。因此,我們在主程序代碼第 19 行與第 22 行之間插入了第 20 行代碼達到延時的目的,我們可以將第 20 行代碼挪到后面測試一下效果。
圖片

13.2.4 AT24C01 多字節(jié)寫通信

根據(jù)上面的介紹,大家很容易發(fā)現(xiàn)每隔 10ms 才能進行一次正常的寫數(shù)據(jù)操作是非常浪費時間的,尤其是在進行多個字節(jié)寫操作的時候。AT24C01 提供了另外一種多字節(jié)的寫模式,為頁操作模式。首先介紹一下 AT24C01 的內(nèi)部存儲器的分頁結(jié)構(gòu)。AT24C01 總共有 128 個字節(jié)的存儲空間,總共分為 16 頁,每一頁總有 8 各字節(jié)。第一頁的地址范圍為 0x00~0x07,依次往下均分為 16 頁。頁操作模式通信時序如下圖所示:

圖片

如上如所示,首先發(fā)送起始信號、寫器件地址、寫起始地址,緊接著寫入多個字節(jié),寫完一個字節(jié),器件內(nèi)部會將地址自動加 1,最后結(jié)束信號。這里需要注意的是,連續(xù)寫的多個字節(jié)必須在同一頁內(nèi),不能進行跨頁連續(xù)讀寫,因此一次通信周期內(nèi)最多可以寫入 8 個字節(jié)的數(shù)據(jù)。如果我們需要寫的數(shù)據(jù)很多,而一頁寫不下怎么辦?首先,啟動頁寫通信,在寫的過程中判斷是否要寫到頁的邊界了,當(dāng)?shù)竭_頁邊界后停止該次頁寫通信,再重新發(fā)起頁寫通信將剩余的數(shù)據(jù)寫入,這樣便實現(xiàn)了任意個字節(jié)的寫入,具體函數(shù)如下圖所示。其中,str 為需要寫入的字符串,addr 為寫入 AT24C01 的起始地址,len 為寫入字符個數(shù)。多字節(jié)寫數(shù)據(jù)代碼如下:

//多字節(jié)寫
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(len > 0)//檢測上一次是否完成所以數(shù)據(jù)寫操作
	{	
		while(1)
		{//循環(huán)檢測器件應(yīng)答信號
			Start_I2C();
			if(0 == Wr_I2C(0x50< 1)) break;//收到應(yīng)答,跳出循環(huán)
			Stop_I2C();//沒收到應(yīng)答,發(fā)送停止信號,繼續(xù)循環(huán)檢測
		}
		Wr_I2C(addr); //寫入要操作的初始地址addr
		
		while(len > 0)
		{
			Wr_I2C(*str++);//寫入一個字節(jié),并將字符串指針指向下一個字符
			len--;//字符數(shù)減1
			addr++;//存儲地址加1	
			if(0 == (addr & 0x07))//檢測是否到達了下一頁的起始地址,
				break;			//即上一個字節(jié)已經(jīng)寫到頁的最后邊界了
				           		//跳出停止繼續(xù)寫,每頁的起始地址后3位為0
								//因此判斷addr后3為是否為0即可	   
		}
		Stop_I2C();	
	}			
}

13.2.5 AT24C01 多字節(jié)讀通信

多字節(jié)讀時序如下圖所示,與單字節(jié)類似,只是在第一個數(shù)據(jù)緊接著著讀取多個數(shù)據(jù),這里要注意的是只有最后一個字節(jié)的數(shù)據(jù)發(fā)送非應(yīng)答信號,前面的數(shù)據(jù)均發(fā)送應(yīng)答信號,這個也很好理解,應(yīng)為我們都到最后一個字節(jié)時要告訴 AT24C01 我們不再繼續(xù)讀數(shù)據(jù)了,因此發(fā)送非應(yīng)答信號。

圖片
多字節(jié)讀取函數(shù)代碼如下圖所示:

//多字節(jié)讀
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(1)
	{//循環(huán)檢測器件應(yīng)答信號
		Start_I2C();
		if(0 == Wr_I2C(0x50< 1)) break;//收到應(yīng)答,跳出循環(huán)
		Stop_I2C();//沒收到應(yīng)答,發(fā)送停止信號,繼續(xù)循環(huán)檢測
	}
	Wr_I2C(addr); //寫入要操作的初始地址addr
    Start_I2C();//再次發(fā)送起始信號
	Wr_I2C((0x50< 1)|0x01);//通知地址50的器件,接下來讀操作
	while(len > 1)
	{
		*str++ = RdACK_I2C();//讀字節(jié)并應(yīng)答
		len--;
	}
	*str = RdNAK_I2C();//最后一個字節(jié),讀字節(jié)并非應(yīng)答
	Stop_I2C();		
}

將上述的多字節(jié)寫、多字節(jié)讀取函數(shù)添加到驅(qū)動文件“Drive_AT 24C01.c”、“Drive_AT 24C01.h”中,后續(xù)應(yīng)用中只需要將文件添加到項目中,調(diào)用相關(guān)的函數(shù)就可以了。

13.2.6 AT24C01 多字節(jié)讀寫應(yīng)用

本小節(jié)實現(xiàn) AT24C01 多字節(jié)的讀寫應(yīng)用,先向 AT24C01 連續(xù)寫入多字節(jié)數(shù)據(jù),然后將數(shù)據(jù)讀出顯示在 1602 液晶顯示模塊上,驗證讀寫的正確性。主程序代碼如下所示:

#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01頭文件
#include"Drive_1602.h"

#define uchar unsigned char
#define  uint unsigned int

sbit DU = P2^7;//數(shù)碼管段選、位選引腳定義
sbit WE = P2^6;	

uchar str1[]="AT24c01 Wr Str!";
uchar str2[20];

void main()
{
	uchar dat=0;
	P0 = 0;//關(guān)閉所有數(shù)碼管
	WE = 1;
	WE = 0;

	Init_1602();//1602初始化

	WrStr_AT24C01(str1,0x05,16);//寫入16個字節(jié)
	RdStr_AT24C01(str2,0x05,16);//讀取16個字節(jié)

	Disp_1602_str(1,1,str2); //將數(shù)據(jù)從第一行第一列開始顯示

	while(1);
}

如代碼所示,從地址 0x05 開始寫入 16 個字節(jié)的數(shù)據(jù),由于 AT24C01 為 8 個字節(jié)為一頁,因此本次為橫跨了 3 頁的寫操作,有效的驗證的多字節(jié)的寫操作。

13.3 本章小結(jié)

本章詳細介紹了I2C總線的通信原理,以及驅(qū)動函數(shù)的編寫,AT24C01芯片的使用。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 單片機
    +關(guān)注

    關(guān)注

    6030

    文章

    44489

    瀏覽量

    631975
  • uart
    +關(guān)注

    關(guān)注

    22

    文章

    1219

    瀏覽量

    101118
  • I2C總線
    +關(guān)注

    關(guān)注

    8

    文章

    386

    瀏覽量

    60791
  • 總線通信
    +關(guān)注

    關(guān)注

    0

    文章

    48

    瀏覽量

    11820
  • AT24C01
    +關(guān)注

    關(guān)注

    0

    文章

    3

    瀏覽量

    5242
收藏 人收藏

    評論

    相關(guān)推薦

    i2c總線ppt(I2C總線器件應(yīng)用)

    I2C總線器件應(yīng)用第一節(jié) I2C總線器件應(yīng)用概述I2C總線工作原理
    發(fā)表于 08-13 17:34 ?0次下載

    億恒C166 系列單片機I2C 總線的軟件模擬

    億恒C166 系列單片機I2C 總線的軟件模擬
    發(fā)表于 05-14 16:11 ?6次下載

    基于I2C總線的單主多從單片機之間的通信

    介紹了作者所設(shè)計的基于I2C 總線的測試系統(tǒng)的結(jié)構(gòu)及I2C 總線通信協(xié)議的特點,給出了I2C
    發(fā)表于 12-19 17:06 ?108次下載

    什么是i2c總線

    什么是i2c總線  下載請點擊: i2c總線協(xié)議中文版 
    發(fā)表于 11-05 09:26 ?2903次閱讀

    I2C總線單片機C語言實現(xiàn)及其應(yīng)用

    I2C總線單片機C語言實現(xiàn)及其應(yīng)用 本文介紹了I2C總線的概念、接口特性和傳輸時序,提出了一
    發(fā)表于 10-17 08:36 ?2943次閱讀
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>的<b class='flag-5'>單片機</b><b class='flag-5'>C</b>語言實現(xiàn)及其應(yīng)用

    PIC單片機I2C 24LC02 C讀寫程序

    PIC單片機I2C 24LC02 C讀寫程序 1   I2C總線特點 &
    發(fā)表于 10-30 08:27 ?5380次閱讀

    AVR單片機I2C總線實驗

     AVR單片機I2C總線實驗。 1、用24C02記錄CPU啟動次數(shù),并在PB口上顯示出來。 
    發(fā)表于 06-30 11:27 ?2040次閱讀

    基于CPLD的I2C總線接口設(shè)計

    在電路設(shè)計中,I2C總線是比較常用的兩線式串行通信方式,大多數(shù)的CPU都擅長于并口操作,不具備直接操作I2C總線接口的能力。為了使不具備
    發(fā)表于 02-12 16:11 ?95次下載
    基于CPLD的<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>接口設(shè)計

    i2c

    單片機i2c總線操作;單片機i2c總線操作;單片機
    發(fā)表于 05-17 11:09 ?35次下載

    基于I2C總線的單主多從通信

    基于I2C總線的單主多從單片機之間的通信
    發(fā)表于 08-30 16:03 ?13次下載
    基于<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>的單主多從<b class='flag-5'>通信</b>

    80C51單片機模擬I2C總線的主機程序分享

    I2C總線協(xié)議程序 在使用的過程中一定要注意時序、時間的問題。 i2c.c /* I2C.c 標準80C51
    發(fā)表于 12-05 15:39 ?3365次閱讀
    80<b class='flag-5'>C</b>51<b class='flag-5'>單片機</b>模擬<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>的主機程序分享

    基于MCS-51單片機I2C總線接口電路的設(shè)計

    單片機控制系統(tǒng)中,廣泛使用I2C器件。如果單片機自帶I2C總線接口,則所有I2C器件對應(yīng)連接到
    發(fā)表于 10-15 16:13 ?3351次閱讀
    基于MCS-51<b class='flag-5'>單片機</b><b class='flag-5'>I2C</b><b class='flag-5'>總線</b>接口電路的設(shè)計

    使用單片機實現(xiàn)I2C總線實驗文件免費下載

    本文檔的主要內(nèi)容詳細介紹的是使用單片機實現(xiàn)I2C總線實驗文件免費下載。
    發(fā)表于 03-24 11:11 ?7次下載

    I2C總線的基本通信總結(jié)

    在電子設(shè)計開發(fā)中I2C大家已經(jīng)很熟悉了,每一種電子產(chǎn)品小到電子開關(guān),大到衛(wèi)星通信都會利用的I2C總線。據(jù)統(tǒng)計I2C在電子產(chǎn)品中占據(jù)了93%的
    的頭像 發(fā)表于 08-26 14:08 ?1.8w次閱讀
    <b class='flag-5'>I2C</b><b class='flag-5'>總線</b>的基本<b class='flag-5'>通信</b>總結(jié)

    51單片機——I2C總線

    本文主要介紹I2C總線協(xié)議時序圖,并介紹I2C總線的數(shù)據(jù)傳送格式,最后介紹了I2C總線存儲器
    發(fā)表于 11-23 16:22 ?13次下載
    51<b class='flag-5'>單片機</b>——<b class='flag-5'>I2C</b><b class='flag-5'>總線</b>