ARM的CF卡驱动分析

    技术2022-05-19  29

    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;                                            /*返回*/

    }


    最新回复(0)