1. 程式人生 > >mini2440----keil for AMR之IIC讀寫EEPROM(AT24C08)

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;
}