回首頁   上一頁   通用輸出入控制    異常向量與外部中斷控制   計時器控制

       串列埠UART控制    串列埠SPI控制    串列埠I2C控制    RTC控制控制    FLASH記憶體控制    uCOS-II系統程式

I2C串列EEPROM存取實習  I2C串列EEPROM中斷模式存取實習

          . I2C 串列EEPROM實習電路

/****************I2C串列EEPROM存取範例**********************
*檔名︰I2C1.C
*功能︰將列表資料寫入EEPROM,再讀取EEPROM資料由LED輸出
*模擬:開啟I2C及Watch視窗,單步執行,觀察暫存器的變化
********************************************************************/
#include "LPC2106.h"
#define device 0xa0 // I2C元件僕位址0xA0(寫入時)或0xA1(讀取時)
#define addr_I2C 0x00 // 記憶體開始位址
uint8 TABLE[]={0,1,2,3,4,5,6,7,8,9};
/************************************************************************
* 名稱︰Start()
* 功能︰送出I2C開始位元
*************************************************************************/
void Start(void)
{
I2CONCLR=0xff; // 清除所有旗標
I2CONSET=I2EN+STA; // I2C致能及送出啟始位元
while((I2CONSET & SI)==0); // 等待I2C中斷旗標
}
/************************************************************************
* 名稱︰Stop()
* 功能︰送出I2C停止位元
*************************************************************************/
void Stop(void)
{
I2CONCLR=STAC+SIC+AAC; // 清除STA、SI及AA旗標
I2CONSET=I2EN+STO; // I2C致能及送出停止位元
}
/************************************************************************
* 名稱︰SendByte(uint8 c)
* 功能︰寫入EEPROM字元。
* 進入參數︰寫入字元。
*************************************************************************/
void SendByte(uint8 c)
{
I2DAT=c; // SDA送出資料
I2CONCLR=STAC+SIC; // 清除STA及SI旗標
while((I2CONSET & SI)==0); // 等待I2C中斷旗標
}
/************************************************************************
* 名稱︰WrByte(uint8 addr, uint8 c)
* 功能︰寫入1-Byte資料。
* 進入參數︰元件位址、EEPROM位址、資料。
*************************************************************************/
void WrByte(uint8 addr, uint8 c)
{
Start(); // 送出開始位元
SendByte(device); // 送出元件寫入位址
SendByte(addr); // 送出記憶體位址
SendByte(c); // 送出記憶體資料
Stop(); // 送出停止位元
}
/************************************************************************
* 名稱︰uint8 IRcvByte()
* 功能︰接收EEPROM字元。
* 退出參數︰已接收的字元。
************************************************************************/
uint8 RcvByte(void)
{
I2CONCLR=SIC+AAC; // 清除SI及AA旗標
while((I2CONSET & SI)==0); // 等待停止旗標
return(I2DAT); // SDA接收資料
}
/************************************************************************
* 名稱︰uint8 RdByte()
* 功能︰讀取EEPROM資料。
* 退出參數︰已讀取的資料。
************************************************************************/
uint8 RdByte(void)
{
uint8 i;
Start(); // 送出開始位元
SendByte(device+1); // 送出元件讀取位址
i=RcvByte(); // 讀取記憶體資料
Stop(); // 送出停止位元
return(i); // 送回已讀取的資料
}
/********************************************************************
* 名稱︰I2C_Init()
* 功能︰I2C初始化,包括接腳及時脈設定
**********************************************************************/
void I2C_Init(void)
{
PINSEL0 = SCL+SDA; // 設定使用I2C接腳
IOSET =IODIR= LED1+LED2+LED3+LED4; // 設定LED為輸出1
I2SCLH = I2SCLL = 18; // Fpclk=13.824MHz,Fscl=13.824M/(18+18)=384K
}
/*******************************************************************
* 名稱︰main()
* 功能︰向EEPROM寫入10-byte資料,然後讀出判斷是否正確。
********************************************************************/
int main(void)
{
uint8 i;
I2C_Init(); // I2C初始化
for(i=0; i<10; i++) // 寫入10筆列表資料到EEPROM
{
WrByte(addr_I2C+i, TABLE[i]);// 寫入列表資料到EEPROM
Delay_ms(2); // 延時,等待寫入
}
while(1) // 重覆輸出所讀取的資料
{
Start(); // 送出啟始位元
SendByte(device); // 送出元件位址
SendByte(addr_I2C); // 送出記憶體位址
for(i=0; i<10; i++) // 讀取10筆EEPROM資料
{
IOCLR=LED1+LED2+LED3+LED4; // 清除LED=0
IOSET=RdByte()<<10; // 讀取EEPROM資料由LED輸出
Delay_ms(500);
}
}
return(0);
}

/****************I2C串列EEPROM中斷模式存取範例**********************
*檔名︰I2C2.C
*功能︰將data_buf_w內資料寫入EEPROM,再將EEPROM資料讀取到data_buf_r,
* 兩者相比較,若符合則LED亮,若不符合則LED閃爍
*模擬:開啟I2C及Watch視窗,單步執行,觀察暫存器的變化
*硬體操作:開啟Watch視窗,以十進制觀察data_buf_w及,data_buf_r的內容
***************************************************************************/
#include "LPC2106.h"

#define CSI24WC02 0xA0 // I2C元件僕位址0xA0(寫入時)或0xA1(讀取時)
volatile uint8 I2C_sla; // 僕(I2C元件)位址
volatile uint8 I2C_suba; // 記憶體位址(EEPROM內的位址)
volatile uint8 *I2C_buf_w; // 資料寫入緩衝區指標
volatile uint8 *I2C_buf_r; // 資料讀取緩衝區指標
volatile uint8 I2C_num; // 操作資料個數
volatile uint8 I2C_end; // 操作結束旗標,0=未完成,1=操作結束,0xFF=操作失敗
volatile uint8 I2C_suba_en; // 記憶體位址致能,0=位址禁能,1=讀取,2=寫人
/***************************************************************************
* 名稱︰IRQ_I2C()
* 功能︰I2C中斷,透過判斷I2C狀態進行相應的操作。
****************************************************************************/
void IRQ_I2C(void) __irq
{
uint8 sta;
sta = I2STAT; // 讀出EEPROM工作狀態
switch(sta)
{
case 0x08: // 己送出啟始(ATART)信號
if(1==I2C_suba_en) I2DAT = I2C_sla & 0xFE; //若I2C_suba_en=1為讀取,送出讀取僕位址
else I2DAT = I2C_sla; //若I2C_suba_en=2為寫入,送出寫入僕位址
I2CONCLR = 0x28; /* 0010 1000 匯流排進入不可尋址僕模式
bit3: SIC=1,清除I2C中斷旗標SI=0
bit5: STAC=1,清除啟始旗標STA=0*/
break;

case 0x10: // 己送出連續啟始(ATART)信號
I2DAT = I2C_sla; // 送出僕位址
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
break;

case 0x18: // 已送出僕位址及寫入(SLA+W),並已接收回應(ACK)
if(0==I2C_suba_en) // 若I2C_suba_en=0 記憶體位址禁能,直接送出資料
{
if(I2C_num>0) //若資料數>0,再繼續送出資料
{
I2DAT = *I2C_buf_w++; // 寫入資料到EEPROM,換下一筆資料
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
I2C_num--; // 資料數遞減
}
else //若資料數=0,結束匯流排
{ I2CONSET = 0x10; // 0001 0000,bit4:STO=1,結束匯流排
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
I2C_end = 1; // 設定操作結束旗標
}
break;
}
if(1==I2C_suba_en) // 若 I2C_suba_en=1表示讀取操作,
{ I2DAT = I2C_suba; // 送出記憶體位址
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
}
if(2==I2C_suba_en) // 若 I2C_suba_en=2表示寫入操作
{ I2DAT = I2C_suba; // 送出記憶體位址
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
I2C_suba_en = 0; // 記憶體己寫入,I2C_suba_en=0 記憶體位址禁能
}
break;

case 0x28: // 已送出資料,並接收到回應(ACK)
if(0==I2C_suba_en) // 若I2C_suba_en=0 記憶體位址禁能,則直接送出資料
{
if(I2C_num>0) // 若資料數>0,再繼續送出資料
{ I2DAT = *I2C_buf_w++; // 寫入資料到EEPROM。換下一筆資料
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
I2C_num--; // 資料數遞減
}
else // 若資料數=0,結束匯流排
{ I2CONSET = 0x10; // 0001 0000,bit4:STO=1,結束匯流排
I2CONCLR = 0x28; // 中斷旗標SI=0,啟始旗標STA=0
I2C_end = 1; // 設定操作結束旗標
}
break;
}
if(1==I2C_suba_en) // 若I2C_suba_en=1表示讀取操作
{
I2CONSET = 0x20; // 0010 0000,bit5:STA=1重新啟動匯流排
I2CONCLR = 0x08; // 中斷旗標SI=0,
I2C_suba_en = 0; // 0=記憶體位址禁能,記憶體位址己處理
}
break;

case 0x20: // 已送出SLA+W,並已接收回應非確認(NOT ACK)結束信號
case 0x30: // S1DAT內的資料已送出,並已接收回應非確認(NOT ACK)結束信號
case 0x38: // 有遺失SLA+R/W或資料
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
I2C_end = 0xFF; // 設定旗標=FF,I2C存取失敗
break;

case 0x40: // 己送出SLA+R,並已接收到回應確認(ACK)
if(1==I2C_num) // 若是最後一位元組,接收資料後送出非回應信號
I2CONCLR = 0x2C; // 0010 1100,回應非確認AA=0,SI=0,STA=0
else // 接收資料並送出回應信號
{
I2CONSET = 0x04; // AA=1,接收到資料後,產生回應確認(ACK)
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
}
break;

case 0x50: //已接收資料,並已送出回應確認(ACK)
*I2C_buf_r++ = I2DAT; // 讀取EEPROM資料,換下一筆資料
I2C_num--; //資料計數遞減
if(1==I2C_num) //若是最後一筆資料,則回應非確認(NO ACK)
I2CONCLR = 0x2C; // 0010 1100,回應非確認AA=0,SI=0,STA=0
else
{ I2CONSET = 0x04; // 接收到資料後,AA=1產生回應確認(ACK)
I2CONCLR = 0x28; // 中斷旗標SI=0, 啟始旗標STA=0
}
break;

case 0x58: //已接收資料,並已送出回應非確認(NO ACK)
*I2C_buf_r++ = I2DAT; // 讀取EEPROM最後byte資料
I2CONSET = 0x10; // bit4:STO=1,結束匯流排
I2CONCLR = 0x28;// 中斷旗標SI=0, 啟始旗標STA=0
I2C_end = 1; // 操作結束旗標,1=操作結束
break;

case 0x48: //已送出SLA+R,並已接收回應非確認(NO ACK)
I2CONCLR = 0x28;// 中斷旗標SI=0, 啟始旗標STA=0
I2C_end = 0xFF; // 操作結束旗標,0xFF=操作失敗
break;
}
VICVectAddr = 0x00; // 中斷處理結束
}
/************************************************************************
* 名稱︰ISendStr()
* 功能︰寫入EEPROM資料。
* 退出參數︰返回值為0時表示出錯,為1時表示操作正確。
* 說明︰使用前設定好參數
*************************************************************************/
uint8 ISendStr(void)
{
I2C_end = 0; //操作結束旗標=0,表示未完成
I2CONCLR = 0x2C; // 0010 1100,回應AA=0,中斷旗標SI=0,啟始旗標STA=0
I2CONSET = 0x40; // 0100 0000 bit6:I2EN=1,I2C致能
I2CONSET = 0x64; /* 0110 0100 設定為主機,並啟動匯流排
bit2:AA=1,回應確認
bit5:STA=1,啟始旗標
bit6:I2EN=1,I2C致能*/
while(0==I2C_end); // 若I2C_end=0,表示操作未完成,等待之
if(1==I2C_end) return(1); // 若I2C_end=1,表示操作結束,返回值=1
else return(0); // 若I2C_end=0xFF,表示操作失敗,返回值=0
}
/************************************************************************
* 名稱︰IRcvStr()
* 功能︰讀取EEPROM資料。
* 退出參數︰返回值為0時表示出錯,為1時表示操作正確。
* 說明︰使用前設定好參數
************************************************************************/
uint8 IRcvStr(void)
{
if(0==I2C_num) return(0); //若資料計數=0,返回值0
I2C_end = 0; //操作結束旗標=0,表示未完成
I2CONCLR = 0x2C; // 0010 1100,回應AA=0,中斷旗標SI=0,啟始旗標STA=0
I2CONSET = 0x40; // 0100 0000 bit6:I2EN=1,I2C致能
I2CONSET = 0x64; /* 0110 0100 設定為主機,並啟動匯流排
bit2:AA=1,回應確認
bit5:STA=1,啟始旗標
bit6:I2EN=1,I2C致能*/
while(0==I2C_end); // I2C_end=0表示操作未完成,等待之
if(1==I2C_end) return(1); // I2C_end=1表示操作結束,返回值=1
else return(0); // I2C_end=0xFF表示操作失敗,返回值=0
}
/********************************************************************
* 名稱︰I2C_Init()
* 功能︰I2C初始化,包括時脈設定及向量IRQ中斷。
**********************************************************************/
void I2C_Init(void)
{
I2SCLH = I2SCLL = 18; // Fpclk=13.824MHz,Fscl=13.824M/(18+18)=384K
PINSEL0 = SCL+SDA; // 設定使用I2C接腳
IOSET = IODIR = LED1; // 設定LED1為輸出=1,LED1息
/* 設定I2C中斷允許 */
VICIntSelect = 0x00000000; // 設定所有通道為IRQ中斷
VICVectCntl0 = CntlI2C; // I2C通道分發到IRQ slot 0,即優先級最高
VICVectAddr0 = (int)IRQ_I2C; // 設定I2C中斷向量位址
VICIntEnable = IntI2C; // 致能I2C中斷
}
/************************************************************************
* 名稱︰WrEepromErr()
* 功能︰讀寫EEPRM錯誤報警,令LED1閃爍。
************************************************************************/
void WrEepromErr(void)
{ while(1)
{ IOSET = LED1; //LED1息
Delay_ms(100);
IOCLR = LED1; //LED1亮
Delay_ms(100);
}
}
/************************************************************************
* 名稱︰main()
* 功能︰向EEPROM寫入10-byte資料,然後讀出判斷是否正確。
************************************************************************/
int main(void)
{ uint8 i;
uint8 data_buf_w[10]; // 寫入資料緩衝器
uint8 data_buf_r[10]; // 讀取資料緩衝器

I2C_Init(); // I2C初始化
for(i=0; i<10; i++) data_buf_w[i] = i; // 寫入0-9到data_buf內

I2C_sla = CSI24WC02; // 僕寫入位址
I2C_suba = 0x00; // 記憶體位址由0開始
I2C_suba_en = 2; // 記憶體位址致能控制,2=寫人操作
I2C_buf_w = data_buf_w; // 資料寫入緩衝區指標
I2C_num = 10; // 資料byte數
ISendStr(); // 在0x00位址開始連續寫入10-byte資料
Delay_ms(1); // 等待寫入週期結束

I2C_sla = CSI24WC02+1; // 僕讀取位址
I2C_suba = 0x00; // 記憶體位址由0開始
I2C_suba_en = 1; // 記憶體位址致能控制,1=讀取操作
I2C_buf_r = data_buf_r; // 資料讀取緩衝區指標
I2C_num = 10; // 資料byte數
IRcvStr(); // 在0x00位址開始連續讀取10-byte的EEPROM資料

/* 校驗讀出的資料,若不正確則閃爍LED報警 */
for(i=0; i<9; i++) //連續比較10-byte資料
{
if(data_buf_w[i]!=data_buf_r[i]) WrEepromErr(); //資料比較,若其中有誤則LED1閃爍
else IOCLR = LED1; // 若無誤,LED1亮
}
while(1); //空轉,停止執行
}