mini2440----keil for AMR之IIC讀寫EEPROM(AT24C08)
文章大綱:
一: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型別,因為在中斷中改變了值,不然值被儲存在快取中了,最後檢測時,不能真正讀到其值。詳細見
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;
}