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
首先看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; /*返回*/
}