ARM的CF卡驅動分析
CF卡是一種包含了控制和大容量flash存儲器的標準器件,具有容量大、體積小、高性能、較高的抗震性和較好的兼容性等特點。
CF卡內集成了控制器、Flash Memory陣列和讀寫緩衝區,其設計符合PCMCIA(Personal Computer Memory Card International Association)和ATA(Advanced Technology Attachment)接口規範。CF卡支持3種接口訪問模式,分別爲符合PCMCIA規範的Memory Mapped模式、I/O Mapped模式和符合ATA規範的True IDE模式。上電時,OE(9腳)爲低電平,CF卡進入True IDE;上電時,OE(9腳)爲高電平,CF卡進入Memory Mapped模式或I/O Mapped模式;此時通過修改CF卡的配置選項寄存器進入相應的模式。配置選項寄存器格式如表1所示:
SRESET |
LevelREQ |
conf5 |
conf4 |
conf3 |
conf2 |
conf1 |
conf0 |
工作模式 |
軟復位信號 |
中斷模式選擇 |
0 |
0 |
0 |
0 |
0 |
0 |
Memory Mapped模式 |
0 |
0 |
0 |
0 |
0 |
1 |
I/O Mapped對應於16位系統 |
||
0 |
0 |
0 |
0 |
1 |
0 |
I/O Mapped 對應於1F0h-1F7h 3F6h-3F7h |
||
0 |
0 |
0 |
0 |
1 |
1 |
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)
/*讀輔助狀態寄存器*/
顯然他們與硬件電路連接的A0,A1,A2,CS0,CS1有關
#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
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。
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; /*返回*/
}