文章大綱:
一:EEPROM芯片介紹(包括各種讀寫的時序與管腳定義)
二:S3C2440中對於IIC需要配置的寄存器
三:IIC成功讀寫EEPROM的程序(光盤的那個IIC讀寫程序真心對初學者不好理解)
一:EEPROM芯片介紹
在這裏分析AT24C02A/AT24C04A/AT24C08A,對於其他不同型號的EEPROM芯片要根據具體手冊進行分析。他們的大小分別是2K(256*8)/4K(512*8)/8K(1024*8)因此可以看出實際大小是256/512/1024byte,。對於AT24C02A的三位地址線都是寫死的,因爲在進行讀寫操作時使用8位地址已經足夠,所以三位地址線寫死作爲片選,對於AT24C08A的三位地址線第一位必須寫死,後兩位可以作爲內部頁地址。因爲AT24C08A的大小超過了256byte,8爲尋址,已經沒法使用到芯片內所有的空間。因此對於後面兩位也就可以由程序決定了。
寫EEPROM有兩種,(在寫數據的時候,AMR9作爲主設備,EEPROM是從設備)
第一種寫byte方式:
寫一個byte實際上需要發送三次數據。在這個過程中,主設備爲發送狀態。第一個數據——設備地址。第二數據——ARM9想寫的EEPROM中的地址。第三個數據——想寫入到EEPROM中的具體數據。最後停止。
第二種寫頁方式:
自我感覺其實寫頁與寫byte應該是一致的,第一個數據——設備地址。第二數據——ARM9想寫的EEPROM中的地址(但是這個地址是首地址。AT24C02一頁是8byte,AT24C04/08一頁是16byte。所以在寫頁的時候最多寫一頁的大小,如果寫太多就會重新又從首地址開始,以前寫的會被覆蓋掉。)。第三、四、······數據——就是你想寫入到EEPROM中的數據。最後停止
讀EEPROM中的數據
第一種讀當前地址數據
主設備仍然是ARM9,從設備是EEPROM,但是要注意主設備的狀態,有時候會是發送狀態,有時候會是接收狀態
第一個數據——(主設備現在處於發生狀態)發送從設備地址,並且把主設備配置爲接收狀態。
第二個數據——(主設備處於接收狀態)ARM9接收數據,注意此時是NO ACK。再停止。(要在產生NO ACK後在讀取數據這時數據會是穩定的。網上有問爲什麼在讀IIC最後需要讀兩次,我自己實驗了,只需要最後一次就行,)
第二種隨機讀數據方式
第一個數據——(主設備處於發生狀態),發送一個從設備地址。第一個設備地址是用來從設備匹配的,也在文檔中被稱爲a “dummy” byte write sequence
第二個數據——(主設備處於發生狀態),發送一個想讀取數據在EEPROM中的地址。
第三個數據——(主設備處於發生狀態),發送一個從設備地址。這是特定要求這樣發送的。。(在這裏主設備會被配置爲接收狀態),這此發送設備地址是用來同時調整主設備狀態的。
第四個數據——(主設備處於接收狀態)需要讀的數據。也是一個NO ACK,與讀當前地址類似。最後再停止。
第三種讀序列地址
與讀當前數據有些類似。
第一個數據——(主設備處於發送狀態),發出設備地址,並配置主設備爲接收狀態。爲後面接收數據準備
第二、三···個數據——(主設備處於接收狀態),前面每個數據都會發送ACK,最後一個數據是一個NO ACK。
再停止。
以上這些,主要要注意主設備狀態的調整,以及爲NO ACK時的處理,後面有事例程序,能夠比較清楚的看到怎麼進行處理的。
二:S3C2440中對於IIC需要配置的寄存器
GPECON,主要是把這個GPIO配置爲IIC模式。
IICCON:其中[0]---[3]與[6]共同決定IIC總線的時鐘頻率。
[4]是一箇中斷標誌位,我們如果沒有用中斷方式的話,應該可以通過查詢這一位進行。(我用的中斷,沒有具體自己實踐)
[5]IIC中斷使能。[7]是否發送ACK。這一位在後面讀數據的時候,要注意進行改變。
IICSTAT:這個寄存器主要是一些標誌爲,不需要配置,主要要配置的是這幾位。
[4]使能IIC數據線的,使其能夠發送數據。
[5]啓動和停止IIC,1啓動。0停止。
[6-7]是配置AMR9的狀態的,一般CPU是一個主設備的角色。只有在兩塊CPU進行相互通信的時候,可能把他配置成爲一個從設備的狀態。所以在我們實驗中,ARM9全部都是處於主設備的角色。
IICADD是CPU做從設備的時候,給他配置的從設備地址,這裏可以不用配置。
IICDS:數據移位寄存器。發送數據就是把數據發到這個寄存器。接收數據就是從這個寄存器中去取數據。
如果使用中斷當然還得配置INTMSK,打開IIC中斷。
三:IIC成功讀寫EEPROM的程序
首先要對程序有幾點說明:
1:f_GetACK必須是volatile類型,因爲在中斷中改變了值,不然值被保存在緩存中了,最後檢測時,不能真正讀到其值。詳細見
在C編程中使用到的幾個重要關鍵字之一volatile
2:IIC的中斷總是在ACK週期內,產生的,我沒有貼出操作流程圖,ARM9文檔中IIC這章已經清楚給出。所以在有ACK的那些數據發送與接收都可以用中斷操作,但是從讀數據的後接收數據來看,由於是NO ACK,所以就沒有用中斷操作了,而且自己進行了一個延時。在讀數據。
static U8 _iicData[IICBUFSIZE];
static volatile int f_GetACK;
void Test_Iic(void)
{
unsigned int i,j,save_E,save_PE;
static U8 data[256];
Uart_Printf("\nIIC Test(Interrupt) using AT24C02\n");
save_E = rGPECON;
save_PE = rGPEUP;
IIC_Init(); //初始化IIC必須的一些寄存器
Uart_Printf("Write test data into AT24C02\n");
for(i=0;i<48;i++)
Wr24C080(0xa0,i,i); //slvaddr, addr, data
for(i=0;i<48;i++)
data[i] = 0;
Uart_Printf("Read test data from AT24C02\n");
for(i=0;i<48;i++)
Rd24C080(0xa0,i,&(data[i]));
//Line changed 0 ~ f
for(i=0;i<3;i++)
{
for(j=0;j<16;j++)
Uart_Printf("%2x ",data[i*16+j]);
Uart_Printf("\n");
}
rINTMSK |= BIT_IIC;
rGPEUP = save_PE;
rGPECON = save_E;
}
void IIC_Init(void)
{
//配置GPE端口爲IIC功能
rGPECON &=~(0xF<<28);
rGPECON |=(1<<31)|(1<<29);
//產生ACK,IIC中斷使能,頻率200KHz
rIICCON = 0;
rIICCON |=(7)|(1<<5)|(1<<7);
//模式爲主發送,使能Rx/Tx (不管是讀還是寫初始化都爲主發送)
rIICSTAT |=(3<<6)|(1<<4);
rIICADD = 0x10;//從地址 表示2440作爲從設備的時候的地址,
//在這裏2440是作爲一個主設備存在的,所以沒有作用。
//EEPROM的標識符爲1010
//控制字節,其中高四位爲器件類型標識符,後三位作爲片選
//最後一位決定讀寫,0是讀,1是寫。
//IIC傳輸中斷開啓
rINTMOD=0x0;
rINTMSK &=~BIT_IIC;
pISR_IIC = (unsigned)IicInt;
}
//*************************[ Wr24C080 ]****************************
void Wr24C080(U32 slvAddr,U32 addr,U8 data)
{
f_GetACK = 0;
rIICDS = slvAddr; //發送第一個數據
rIICSTAT = 0xf0;
while(f_GetACK == 0); //等待發送結束
f_GetACK = 0;
rIICDS = addr; //發送第二個數據
rIICCON = 0xaf;
while(f_GetACK == 0); //等待發送結束
f_GetACK = 0;
rIICDS = data; //發送第三個數據
rIICCON = 0xaf;
while(f_GetACK ==0); //等待發送結束
rIICSTAT = 0xd0; //停止IIC
rIICCON = 0xaf;
Delay(3);
}
void Rd24C080(U32 slvAddr,U32 addr,U8 *data)
{
char cRecvByte;
f_GetACK = 0;
rIICDS = slvAddr; //發送第一個數據
rIICSTAT = 0xf0;
while(f_GetACK==0); //等待結束
f_GetACK = 0;
rIICDS = addr; //發送第二個數據
rIICCON = 0xAF;
while(f_GetACK==0); //等待結束
f_GetACK = 0;
rIICDS = slvAddr; //發送第三個數據
rIICSTAT = 0xb0; //配置主設備狀態爲接收
rIICCON = 0xaf;
while(f_GetACK==0); //等待結束
f_GetACK = 0;
rIICCON = 0x2f; //NO ACK配置
Delay(2); //等待其穩定,延時不要求精確
cRecvByte = rIICDS; //接收第四個數據
rIICSTAT = 0x90; //停止IIC
rIICCON = 0xaf;
Delay(3);
*data = cRecvByte;
}
void __irq IicInt(void)
{
ClearPending(BIT_IIC);
f_GetACK = 1;
}