ARM的CF卡驅動分析

ARM的CF卡驅動分析

 

 

 

 

 

CF卡是一種包含了控制和大容量flash存儲器的標準器件,具有容量大、體積小、高性能、較高的抗震性和較好的兼容性等特點。

 

CF卡內集成了控制器、Flash Memory陣列和讀寫緩衝區,其設計符合PCMCIAPersonal Computer Memory Card International Association)和ATAAdvanced Technology Attachment)接口規範。CF卡支持3種接口訪問模式,分別爲符合PCMCIA規範的Memory Mapped模式、I/O Mapped模式和符合ATA規範的True IDE模式。上電時,OE9腳)爲低電平,CF卡進入True IDE;上電時,OE9腳)爲高電平,CF卡進入Memory Mapped模式或I/O Mapped模式;此時通過修改CF卡的配置選項寄存器進入相應的模式。配置選項寄存器格式如表1所示

 

 

SRESET

LevelREQ

conf5

conf4

conf3

conf2

conf1

conf0

工作模式

 

軟復位信號

 

中斷模式選擇

Memory Mapped模式

1

I/O Mapped對應於16位系統

I/O Mapped

對應於1F0h-1F7h 3F6h-3F7h

I/O Mapped

對應於170h-177h 370h-377h

1 CF卡工作模式選擇

TRUEIDE模式接口電路:

點擊看大圖

 

 

下面以內存工作模式爲例子介紹cf卡的驅動(最簡單):

 

首先我們知道當把地址線與數據線掛載到MCU後,我們就可以以內存的方式讀寫CF卡的內部寄存器了,CF卡內部寄存器有:

#define        CF_REG_DATA            IDE_CS0                     

 /*數據寄存器*/

#define        CF_REG_ERR             (IDE_CS0 + IDE_A0)  

  /*讀錯誤寄存器*/

#define        CF_REG_FEATURE       (IDE_CS0 + IDE_A0)  

  /*寫功能寄存器*/

#define        CF_REG_SECCNT        (IDE_CS0 + IDE_A1)   

 /*扇區計數器*/

#define        CF_REG_SECTOR        (IDE_CS0 + IDE_A1 + IDE_A0)               

/*扇區號*/

#define        CF_REG_CYLINDER_LOW       (IDE_CS0 + IDE_A2)                               

/*柱面低8*/

#define        CF_REG_CYLINDER_HIGH       (IDE_CS0 + IDE_A2 + IDE_A0)              

  /*柱面高8*/

#define        CF_REG_DEVICE_HEAD         (IDE_CS0 + IDE_A2 + IDE_A1)              

  /*選擇主從,模式,磁頭*/

#define        CF_REG_COMMAND       (IDE_CS0 + IDE_A2 + IDE_A1 + IDE_A0)

/*寫命令寄存器*/

#define        CF_REG_STATUS        (IDE_CS0 + IDE_A2 + IDE_A1 + IDE_A0)

/*讀狀態寄存器*/

#define        CF_REG_CONTROL             (IDE_CS1 + IDE_A2 + IDE_A1)               

 /*寫控制寄存器*/

#define        CF_REG_ASTATUS       (IDE_CS1 + IDE_A2 + IDE_A1)              

/*讀輔助狀態寄存器*/

 

顯然他們與硬件電路連接的A0A1A2CS0CS1有關

#define        IDE_A0             (1<<1)        //地址信號,決定了內部寄存器的偏移

#define        IDE_A1            (1<<2)        //

#define        IDE_A2            (1<<3)        //

#define        IDE_CS0           ( 0x28000000 ) //片選信號,決定了CF卡的尋址範圍

#define        IDE_CS1           ( 0x28800000 )

 

我們可以根據他們如何接入MCU從而確定CF寄存器的地址。

 

有了寄存器地址,我們就可以讀寫他們了,以下爲兩個關鍵的宏outportw和inportw,實現方法與解釋:

MACRO
     MOV_PC_LR
     [ THUMBCODE
       bx lr
     |
       mov pc,lr
     ]
   MEND

 

EXPORT outportw
     outportw strh r0, [r1]
     MOV_PC_LR

 

EXPORT inportw
     inportw ldrh r0, [r0]
     MOV_PC_LR

 

   首先看c與彙編的接口方式。根據nios的二進制接口規則,當編譯器把c函數編譯到彙編代碼時,如果參數不多於4個,那麼就由r0 r1 r2 r3來傳遞參數,函數的返回值將被放到r0中。比如c中調用這樣一個函數outportw(dat,addr),那麼當編譯器將這個函數編譯爲彙編時,dat的值被賦給r0,addr的值被賦給r1。在於這個c文件在同一個文件夾下的s文件,如果其中有這麼一段:
   export outportw
   outportw str r0,[r1]
   MOV_PC_LR

   那麼調用在c函數中調用outportw()這個函數的實際作用就是將dat的值發送到addr的地址上去。這樣就實現了對底層硬件資源的直接訪問。
   如果c中有這樣一個函數rt = inportw(addr)並且在與這個c文件同一個文件夾下的s文件中有這樣一段代碼的話:
   export inportw
   inportw ldr r0,[r0]
   MOV_PC_LR
   那麼這個函數的實際作用就是將addr地址上的值讀出並作爲函數返回值返回。
   上面兩段彙編代碼中遇到的MOV_PC_LR就是一個MACRO。
   MACRO
   MOV_PC_LR
    [THUMBCODE
       bx lr
    |
        mov pc,lr
    ]
    MEND

   其實這種方式實現c與彙編的接口並不是很方便。這樣做需要有比較好的彙編基礎。其實可以通過相對簡單的c宏定義來實現同樣的功能。我之前經常使用這樣的宏定義來訪問固定的地址。#define rDATA (*((volatile unsigned int *)0x********)),這樣data = rDATA; rDATA = data;就可以實現對固定內存地址的讀寫了。其實剛纔的彙編代碼也是對具體的內存地址進行讀寫,只是這個地址是作爲參數傳遞的。只要將c函數中的宏定義改成這個樣子就可以實現同樣的功能。
#define outportw(dat,addr) (*(volatile U16*)(addr) = (dat))
#define inportw(addr) (*(volatile U16 *)(addr))

 

 

下面是CF卡驅動的步驟:

1.       CF卡的初始化,完成了IO的配置,與硬件連接直接相關

void CF_Init(void)

{

       rGPGCON = rGPGCON & (~(0x0f<<14)) ;                                  

        //GPG7,GPG8 is input

rGPBCON = rGPBCON & (~(0x03<<18));                             

 //GPB9 input

       rGPBCON      = rGPBCON &      (~(0x03<<20)) | (1<<20);           

      //GPB10 output

}

 

2.       探測CF卡是否加載上電

#define     CFCard_Dected ( rGPGDAT&(1<<7) ) 

 //這一位,結CF卡的CF_CD1判斷是否上電

 

void CF_Probe(void)

{

      

       rGPBDAT = ( rGPBDAT&(~(1<<10)) ) | ((0&1)<<10);

       if(CFCard_Dected)                      //Probe CF CARD

              CFCard_Flag=FALSE;

       else

              CFCard_Flag=TRUE;

       if(CFCard_Flag)

       {

              rGPBDAT = ( rGPBDAT&(~(1<<10)) ) | ((1&1)<<10);

              Uart_Printf("CF Card Detect OK/n");           //上電了,ok

       }

       else

              Uart_Printf( "CF Card is NOT Pluged!!!/n" ) ;

 

}

 

3.       設置CF卡的尋址方式,以及主從

 

void CF_SetDevice(void)

{

       CF_Dev_Config = CF_DevReg_DEV1 +                           /*選擇設備1*/

                                CF_DevReg_b5 +

                                CF_DevReg_LBA+                                  /*工作在LAB模式*/

                                CF_DevReg_b7;  

       outportw(CF_Dev_Config,CF_REG_DEVICE_HEAD);     

}

 

4.       然後就可以完成CF卡的讀寫了

1) 

int CF_Write_Sector(U16 * Buffer, U32 Sector ,U8 count)   //寫一個簇

{

       U8   Status=FALSE;

       U16 i;

       if(!CFCard_Flag)                                       /*CF卡不可用,立即返回*/

              return      FALSE;

       CF_WriteSetting( Sector, count);         /*寫扇區設置*/

       do

       {     count--;

                  if(CF_IsBusy())            /*等待設備請求數據傳輸*/

                     {

                         for(i = 0; i < 256; i ++)               

/*連續寫256個字(512字節)數據,即寫入一個扇區*/

                          {

                               outportw(*(Buffer++),CF_REG_DATA);                  

/*向數據寄存器寫一個字數據*/       

                          }

                        

                         Buffer +=256;                      /*調要寫入數據緩衝區的指針*/

                     }

                     else

                            break;                                        /*出錯退出*/

       }while(count>0);

       if(CF_IsBusy())                 

  /*等待設備就緒,讀取狀態寄存器同時檢測設備是否出錯*/

              Status = TRUE;                                  /*操作正確*/ 

       return Status;                                            /*返回*/

}

 

其中:調用了read初始化配置函數和總線檢測函數如下

void CF_WriteSetting(U32 Sectors , U8 Count)    //CF卡的寄存器設置

{                                                     

 //outportw函數爲寫字數據到據存起

                             

   outportw(0,CF_REG_FEATURE);                                            

/*寫特徵寄存器,與驅動接口*/

   outportw(Count,CF_REG_SECCNT);                                      

/*寫扇區計數寄存器,與驅動接口*/

   outportw(Sectors,CF_REG_SECTOR);                                    

/*寫扇區寄存器,與驅動接口*/

   outportw(Sectors/0x100,CF_REG_CYLINDER_LOW);                    

/*寫柱面低8位寄存器,與驅動接口*/

   outportw(Sectors/0x10000,CF_REG_DEVICE_HEAD);             

/*寫設備磁頭寄存器,與驅動接口*/

   outportw(((Sectors/0x1000000)&0x0f)|CF_Dev_Config,CF_REG_DEVICE_HEAD);              /*寫設備磁頭寄存器,與驅動接口*/

   outportw(0x30,CF_REG_COMMAND);                                   

/*寫命令寄存器,與驅動接口,0x30      寫扇區命令*/

}

 

U8 CF_IsBusy(void)                       //測試總線是不是忙

{

       int time="0xffff";                                         //沒有精確的值

       U8 status="FALSE";

       while(time--)

       {

              inportw(CF_REG_ASTATUS);     

             

              if(!(inportw(CF_REG_STATUS)&0x80))     //判斷CF卡是否忙

                     status=TRUE;

              else

                     status=FALSE;

       }

       return status;

}

 

2) 

void CF_ReadSetting(U32 Sectors , U8 Count)      //讀的配置

{                    

   outportw(0,CF_REG_FEATURE);                                            

/*寫特徵寄存器,與驅動接口*/

   outportw(Count,CF_REG_SECCNT);                                      

/*寫扇區計數寄存器,與驅動接口*/

   outportw(Sectors,CF_REG_SECTOR);                                    

/*寫扇區寄存器,與驅動接口*/

   outportw(Sectors/0x100,CF_REG_CYLINDER_LOW);                    

/*寫柱面低8位寄存器,與驅動接口*/

   outportw(Sectors/0x10000,CF_REG_DEVICE_HEAD);             

/*寫設備磁頭寄存器,與驅動接口*/

   outportw(((Sectors/0x1000000)&0x0f)|CF_Dev_Config,CF_REG_DEVICE_HEAD);              /*寫設備磁頭寄存器,與驅動接口*/

   outportw(0x20,CF_REG_COMMAND);                                   

/*寫命令寄存器,與驅動接口,0x20      讀扇區命令*/

 

}

 

int CF_Read_Sector(U16 * Buffer, U32 Sector ,U8 count)  //讀一個簇

{

       U8   Status=FALSE;

       U16 i;

       U16 j;

       if(!(CFCard_Flag))               /*CF卡不可用,立即返回*/

              return      FALSE;                                      /*設備無效直接返回*/

       CF_ReadSetting( Sector, count);          /*讀扇區設置*/

       do

       {    

              count--;                               /*扇區數減1*/

              if(CF_IsBusy())                   /* 等待設備請求數據傳輸*/

              {    

                     for(i = 0; i < 256; i ++)                             

/*連續讀256個字(512字節)數據,及一個扇區大小*/

                {

                         //*Buffer=inportw(CF_REG_DATA);

                         //Buffer++;

                         j=inportw(CF_REG_DATA);

                         *(Buffer++)=j;

                         //*(Buffer++)=inportw(CF_REG_DATA);                 

/*從數據寄存器讀一個字數據*/

            }

               Buffer +=256;                           /*調整數據格式*/

              }

              else break;

              //else return 0;                             /*出錯,退出d0...while*/

       }while(count>0);                                /*所有扇區數據傳輸完成*/

                    

       if(CF_IsBusy())                         

/*等待設備就緒,讀取狀態寄存器同時檢測設備是否出錯*/

              Status = TRUE;                                  /*操作正確*/

 

       return Status;                                            /*返回*/

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章