[原创] lm3s8962 FatFs文件系统移植步骤总结~~

xielijuan   2010-11-28 22:29 楼主

学习完了8962文件系统的移植,现在对移植的步骤做一个详细的总结。

由于学习的SD文件系统移植的过程中,SD卡的通信模式是SPI模式,8962SSI模块是兼容SPI模式的,所以,还需要先学习一下SSI模块的配置和操作。

 

一.Stellaris SSI模块具有以下特性:

主机或从机操作

时钟位速率和预分频可编程

独立的发送和接收FIFO16位宽,8个单元深。

Freescale SPIMICROWIRE、或德州仪器同步串行接口的操作可编程

数据帧大小可编程,范围为416

内部回送测试(loopback test)模式,可进行诊断/调试测试

 

二. FIFO操作

1发送FIFO

通用发送FIFO是一个16位宽、8单元深、先进先出的存储缓冲区。CPU通过写SSI数据 (SSIDR)寄存器来将数据写入发送FIFO,数据在由发送逻辑读出之前一直保存在发送FIFO中。

SSI配置为主机或从机时,并行数据在进行串行转换并通过SSITx管脚分别发送到相关的从机或主机之前先写入发送FIFO

 

2 .接收 FIFO

通用接收FIFO是一个16位宽、8单元深、先进先出的存储缓冲区。从串行接口接收到的数据在由CPU读出之前一直保存在缓冲区中,CPU通过读SSIDR寄存器来访问读FIFO

SSI配置为主机或从机时,从SSIRx管脚接收到的串行数据在分别并行加载到相关的从机或主机接收FIFO之前先进行记录(registered)。

回复评论 (29)

三. 中断
SSI可在出现下列情况时产生中断:
■ 发送FIFO服务
■ 接收FIFO服务
■ 接收FIFO超时
■ 接收FIFO溢出
所有中断事件在发送到中断控制器之前要先执行“或”操作,因此,在任何给定的时刻SSI只能向控制器发送一个中断请求。在4个可单独屏蔽的中断中,每个都可以通过置位SSI 中断屏蔽 (SSIIM)寄存器中适当的位来屏蔽。将适当的屏蔽位置1可使能中断。
SSI提供单独的输出和组合的中断输出,这样,允许使用全局中断服务程序或组合的器件驱动程序来处理中断。发送和接收动态数据流的中断与状态中断是分开的,因此,可以根据FIFO的触发深度trigger level)对数据执行读和写操作。各个中断源的状态可从SSI 原始中断状态(SSIRIS)和SSI屏蔽后的中断状态(SSIMIS)寄存器中读取。

四.初始化和配置
在使用SSI时,必须通过置位RCGC1寄存器的SSI位来使能SSI外设时钟。
针对不同的帧格式,SSI可通过以下步骤进行配置:
1. 确保在对任何配置进行更改之前先将SSICR1寄存器中的SSE位禁止。
2. 确定SSI为主机还是从机:
a. 作为主机时,将 SSICR1 寄存器的值设为0x0000.0000。
b. 作为从机时 (输出使能),将 SSICR1 寄存器的值设为0x0000.0004。
c. 作为从机时 (输出禁止),将 SSICR1寄存器的值设为 0x0000.000C。
3. 通过写SSICPSR寄存器来配置时钟预分频除数。
4. 写SSICR0寄存器,实现以下配置:
■ 串行时钟率 (SCR)
■ 如果使用Freescale SPI模式,则配置所需的时钟相位/极性(SPH和SPO)
■ 协议模式:Freescale SPI, TI SSF, MICROWIRE (FRF)
■ 数据长度 (DSS)
5. 通过置位SSICR1寄存器的SSE位来使能SSI。
举例:假定SSI的配置如下:
■ 主机操作
■ Freescale SPI模式 (SPO=1, SPH=1)
■ 1 Mbps位速率
■ 8个数据位
如果系统时钟为20MHz,则位速率的计算如下:
FSSIClk = FSysClk / (CPSDVSR * (1 + SCR))
1x106 = 20x106 / (CPSDVSR * (1 + SCR))
在此情况下,如果CPSDVSR=2,,SCR必须为9。
具体的配置序列如下:
1. 确保SSICR1寄存器的SSE位禁止。
2. 向 SSICR1 寄存器写入0x0000.0000。
3. 向 SSICPSR 寄存器写入0x0000.0002。
4. 向 SSICR0 寄存器写入0x0000.09C7。
5. 将SSICR1寄存器的SSE位置1来使能SSI。
点赞  2010-11-28 22:29
好,开始操作系统了~~~
一.        移植效果
1.        可以通过“超级终端”等工具呈现命令界面,并传递命令。
2.        在命令界面中实现以下文件系统操作命令:
help : 显示所有命令
h    :请求帮助
?    : 请求帮助
ls   : 显示文件列表
chdir: 更改目录
cd   : 更改目录
pwd  : 显示当前工作目录
cat  : 显示一个文本文件的内容
fwrite:新建文件并写入
mkdir:在当前目录下创建目录
二.        移植条件
1.        可以识别参数的串口命令界面
2.        Lm3s8962评估板
3.        FatFs文件系统
点赞  2010-11-28 22:30
三,对命令界面所实现功能的描述。
1.        SSCOM3.2(作者:聂小猛(丁丁))终端工具设置。
首先打开串口终端,设置波特率115200,8位数据,无奇偶校验,无流控。串口号设置成板子在你电脑上的端口号(可以在设备管理器中查看,我的是COM10).
届时,开发板复位后,终端显示:

SD Card Example Program
Type 'help' for help.

/>
然后可以输入命令执行,比如输入help命令,则会显示命令界面当前支持的所有命令,并显示输入的参数。
2.        串口终端命令输入过程。
在串口终端输入一个字符时,其工作过程为:
void
UARTStdioIntHandler(void)
{
    unsigned long ulInts;
    char cChar;
    long lChar;
static tBoolean bLastWasCR = false;

//获得并清除中断源
    ulInts = MAP_UARTIntStatus(g_ulBase, true);
    MAP_UARTIntClear(g_ulBase, ulInts);
//判断被中断的TX FIFO是否有可用空间。
    if(ulInts & UART_INT_TX)
    {
        //尽可能多的把字符装入到发送FIFO中
        UARTPrimeTransmit(g_ulBase);

        //输出缓冲区为空,关闭发送中断
        if(TX_BUFFER_EMPTY)
        {
            MAP_UARTIntDisable(g_ulBase, UART_INT_TX);
        }
    }

    //是否被收到的字符中断?
    if(ulInts & (UART_INT_RX | UART_INT_RT))
    {
        //从UART中获取所有有效字符
        while(MAP_UARTCharsAvail(g_ulBase))
        {
            //抽口接收一个字符一个字符
            lChar = MAP_UARTCharGetNonBlocking(g_ulBase);
            cChar = (unsigned char)(lChar & 0xFF);
                }
           部分代码省略
    }
}
串口接收到字符引起中断,然后调用函数MAP_UARTCharGetNonBlocking(g_ulBase);获取字符并放入串口消息队列。

四,移植前的准备
FatFs Module一开始就是为了能在不同的单片机上使用而设计的,所以具有良好的层次结构,如图1所示。最顶层是应用层,使用者无需理会FatFs Module的内部结构和复杂的FAT协议,只需要调用FatFs Module提供给用户的一系列应用接口函数,如f_open,f_read,f_write、f_close等,就可以像在PC上读/写文件那样简单。
点赞  2010-11-28 22:30

 

  111.jpg

中间层FatFs Module实现了FAT文件读/写协议。FatFs Module的完全版提供的是ffcffh,除非有必要,使用者一般不用修改,使用时将需要版本的头文件直接包含进去即可。

 

需要使用者编写移植代码的是FatFs Module提供的底层接口,它包括存储媒介读/写接口DiskIO和供给文件创建修改时间的实时时钟。在这里,编写的移植代码在文件mmc-ek-lm3s8962.c中。

 

[ 本帖最后由 xielijuan 于 2010-11-28 22:38 编辑 ]
点赞  2010-11-28 22:31
五,移植步骤。
第一步:SPI和相关I/O的初始化
8962中的SD卡的通信模式是SPI。这就需要8962提供SPI读/写接口代码,主要包括初始化、读和写。SPI初始化包括SPI相关寄存器的初始化和相关I/O口的初始化。例程中的代码代码包含在文件mmc-ek-lm3s8962.c里。SSI模块的配置在下面这个函数里:

这个函数并不是真的打开电源,而是初始化SSI端口和SD卡需要的引脚
static
void power_on (void)
{

//打开用于在SSI上驱动SD卡的外设,以及CS引脚
    SysCtlPeripheralEnable(SDC_SSI_SYSCTL_PERIPH);    //打开SSI端口
SysCtlPeripheralEnable(SDC_GPIO_SYSCTL_PERIPH);    //打开GPIO  A端口
// #define SDC_GPIO_SYSCTL_PERIPH  SYSCTL_PERIPH_GPIOA
SysCtlPeripheralEnable(SDC_CS_GPIO_SYSCTL_PERIPH);    //打开芯片片选择引脚CS
(GPIOG 0)

    /* 对SSI进行一系列配置*/
    GPIOPinTypeSSI(SDC_GPIO_PORT_BASE, SDC_SSI_PINS);
    GPIOPadConfigSet(SDC_GPIO_PORT_BASE, SDC_SSI_PINS, GPIO_STRENGTH_4MA,
                     GPIO_PIN_TYPE_STD_WPU);      //4毫安,上拉
    GPIOPinTypeGPIOOutput(SDC_CS_GPIO_PORT_BASE, SDC_CS);  //把PG0设置为输出
    GPIOPadConfigSet(SDC_CS_GPIO_PORT_BASE, SDC_CS, GPIO_STRENGTH_4MA,
                     GPIO_PIN_TYPE_STD_WPU);

DESELECT();   //在mmc-ek-lm3s8962.c文件开头的时候定义了,这个函数的作用是
//取消对对SD卡的选中。操作是对PG0赋值1

    /* 配置SSI0端口 */
    SSIConfigSetExpClk(SDC_SSI_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                       SSI_MODE_MASTER, 400000, 8);
    SSIEnable(SDC_SSI_BASE);

    /* Set DI and CS high and apply more than 74 pulses to SCLK for the card to be able to accept a native command. */
    send_initial_clock_train();//  这个函数也是文件中定义的,大概是对时钟进行设置,没太看懂

    PowerFlag = 1;
}

上面用到的自定义的函数,这里做一下解析,库函数的作用大家可以参照库函数表:
static
void SELECT(void)
{
    GPIOPinWrite(SDC_CS_GPIO_PORT_BASE, SDC_CS, 0);
}
//宏定义里有这样的定义:
//#define SDC_CS_GPIO_PORT_BASE      GPIO_PORTG_BASE       //PG端口
//#define SDC_CS_GPIO_SYSCTL_PERIPH  SYSCTL_PERIPH_GPIOG
//#define SDC_CS                     GPIO_PIN_0             //0号引脚
//而在原理图的第19页的表格,我们可以看到
//Pin 19 PG0 SD card chip select
//Pin 19 PG0 OLED display chip select
//这个Pin 19 PG0就是控制SD工作的开关,它但是他同时也OLED的使能引脚,所以,这两个模块不能同时工作。原理图如下:
点赞  2010-11-28 22:40

22.jpg

 

static

void DESELECT(void)

{

    GPIOPinWrite(SDC_CS_GPIO_PORT_BASE, SDC_CS, SDC_CS);

}

//这个函数的功能刚好上一个相反,是关闭的作用。

 

 

第二步.现在就可以进行读/写了。

 

 

//下面是一些变量的定义

static volatile

DSTATUS Stat = STA_NOINIT;    /* 磁盘状态 */

 

static volatile

BYTE Timer1, Timer2;    /* 100Hz 的递减计时器*/

 

static

BYTE CardType;            /* b0: MMC(MultiMediaCard,多媒体存储卡), b1:SD卡控制, b2:块寻址,这个变量是存储卡的类型的变量 */

 

static

BYTE PowerFlag = 0;     /*显示电源是否打开 */

 

 

通过sPI发送一个字节到存储卡

static

void xmit_spi(BYTE dat)

{

    DWORD rcvdat;

 

    SSIDataPut(SDC_SSI_BASE, dat); /* 将数据写入发送队列tx fifo */

 

    SSIDataGet(SDC_SSI_BASE, &rcvdat); /* 写的时候刷新读取的数据 */

}

 

 

通过sPI从存储卡读取一个字节

static

BYTE rcvr_spi (void)

{

    DWORD rcvdat;

 

    SSIDataPut(SDC_SSI_BASE, 0xFF); /* 写入虚拟数据*/

 

    SSIDataGet(SDC_SSI_BASE, &rcvdat); /*从接收队列 rx fifo读取数据 */

 

    return (BYTE)rcvdat;

}

static

void rcvr_spi_m (BYTE *dst)

{

    *dst = rcvr_spi();

}

[ 本帖最后由 xielijuan 于 2010-11-28 22:42 编辑 ]
点赞  2010-11-28 22:40
第三步.基本接口函数
在具备SPI读/写接口的基础上编写SD卡接口代码,需要编写3个基本接口函数:
①        向SD卡发送1条命令:
Static BYTE send_cmd (BYTE cmd, DWORD arg)②向SD卡发送1个数据包:

②向SD卡发送1个数据包:
Static BOOL xmit_datablock (const BYTE *buff, BYTE token)

③从SD卡接收1个数据包:
  Static BOOL rcvr_datablock (BYTE *buff, UINT btr)

这三个函数,也是在这个文件中,具体怎么实现的这里简要说一下:

(1)        向SD卡发送1条命令:
static
BYTE send_cmd (
    BYTE cmd,        /* 命令字节 */
    DWORD arg        /* 参数*/
)
{
    BYTE n, res;


    if (wait_ready() != 0xFF) return 0xFF;

    /* 发送命令包*/
    xmit_spi(cmd);                        /* Command */
    xmit_spi((BYTE)(arg >> 24));        /* Argument[31..24] */
    xmit_spi((BYTE)(arg >> 16));        /* Argument[23..16] */
    xmit_spi((BYTE)(arg >> 8));            /* Argument[15..8] */
    xmit_spi((BYTE)arg);                /* Argument[7..0] */
    n = 0;
    if (cmd == CMD0) n = 0x95;            /* CRC for CMD0(0) */
    if (cmd == CMD8) n = 0x87;            /* CRC for CMD8(0x1AA) */
    xmit_spi(n);

    /* 接收命令响应*/
    if (cmd == CMD12) rcvr_spi();        /* 如果停止读写,跳过一个字节*/
    n = 10;                                /*10次超时等待一个有效应答 */
    do
        res = rcvr_spi();
    while ((res & 0x80) && --n);

    return res;            /*返回一个响应值 */
}

②向SD卡发送1个数据包:

static
BOOL xmit_datablock (
    const BYTE *buff,    /*被传送的512块数据块 */
    BYTE token            /* 数据/停止标记*/
)
{
    BYTE resp, wc;


    if (wait_ready() != 0xFF) return FALSE;

    xmit_spi(token);                    /*传送数据标记 */
    if (token != 0xFD) {            /* 数据传送*/
        wc = 0;
        do {                            /* 传送512字节数据块给存储卡*/
            xmit_spi(*buff++);
            xmit_spi(*buff++);
        } while (--wc);
        xmit_spi(0xFF);                    /* 循环*/
        xmit_spi(0xFF);
        resp = rcvr_spi();                /* 接收一个响应数据 */
        if ((resp & 0x1F) != 0x05)        /*如果不接收,返回错误 */
            return FALSE;
    }

    return TRUE;
}

③从SD卡接收1个数据包:

static
BOOL rcvr_datablock (
    BYTE *buff,            /* 数据缓冲器,用来存数接收到的数据*/
    UINT btr            /* 字节计数器*/
)
{
    BYTE token;


    Timer1 = 10;
    do {                            /* 等待数据包*/
        token = rcvr_spi();
    } while ((token == 0xFF) && Timer1);
    if(token != 0xFE) return FALSE;    /* 如果不是有效的数据标记,返回错误*/

    do {                            /* 接收数据块到缓冲器*/
        rcvr_spi_m(buff++);
        rcvr_spi_m(buff++);
    } while (btr -= 2);
    rcvr_spi();                        /* 停止循环*/
    rcvr_spi();

    return TRUE;                    /* 返回成功 */
}


//下面是一个等待函数,等待卡准备好
static
BYTE wait_ready (void)
{
    BYTE res;
    Timer2 = 50;    /*超时等待500ms*/
    rcvr_spi();
    do
        res = rcvr_spi();
    while ((res != 0xFF) && Timer2);

    return res;
}
点赞  2010-11-28 22:42

第四步. 编写DiskIO

编写好存储媒介的接口代码后,就可以编写DiskIO了,DiskIO结构如图所示。

FatFs的移植实际上需要编写7个接口函数,分别是:

33.jpg  

1DSTATUS disk_initialize(BYTE drv)

  存储媒介初始化函数。由于存储媒介是SD卡,所以实际上是对SD卡的初始化。drv是物理磁盘号码,由于FatFs只支持一个物理磁盘,所以drv应恒为O。执行无误返回0,错误返回非O。程序如下

DSTATUS disk_initialize (

    BYTE drv        /* 物理磁盘号*/

)

{

    BYTE n, ty, ocr[4];

    if (drv) return STA_NOINIT;            /* 只支持一个磁盘*/

    if (Stat & STA_NODISK) return Stat;    /*插槽里没有磁盘 */

 

    power_on();                            /* 打开插槽电源*/

    send_initial_clock_train();

 

[ 本帖最后由 xielijuan 于 2010-11-28 22:48 编辑 ]
点赞  2010-11-28 22:43
SELECT();                /* CS为低电平,代表选中了SD卡*/
    ty = 0;
    if (send_cmd(CMD0, 0) == 1) {            /* 进入空闲状态 */
        Timer1 = 100;                        /* 初始化超时 1000 msec */
        if (send_cmd(CMD8, 0x1AA) == 1) {    /* SDC Ver2+ */
            for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
            if (ocr[2] == 0x01 && ocr[3] == 0xAA) {    /*这个卡可以工作在2.7-3.6范围                do {
                    if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 1UL << 30) == 0)    break;    /* ACMD41 with HCS bit */
                } while (Timer1);
                if (Timer1 && send_cmd(CMD58, 0) == 0) {    /* 检查CS位*/
                    for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
                    ty = (ocr[0] & 0x40) ? 6 : 2;
                }
            }
        } else {                            /* SDC Ver1 or MMC */
            ty = (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) <= 1) ? 2 : 1;    /* SDC : MMC */
            do {
                if (ty == 2) {
                    if (send_cmd(CMD55, 0) <= 1 && send_cmd(CMD41, 0) == 0) break;    /* ACMD41 */
                } else {
                    if (send_cmd(CMD1, 0) == 0) break;                                /* CMD1 */
                }
            } while (Timer1);
            if (!Timer1 || send_cmd(CMD16, 512) != 0)    /* 选择读写块长度 */
                ty = 0;
        }
    }
    CardType = ty;
    DESELECT();            /* CS = H */
    rcvr_spi();            /* Idle (Release DO) */

    if (ty) {            /*初始化成功*/
        Stat &= ~STA_NOINIT;        /* 清零STA_NOINIT */
        set_max_speed();
    } else {            /*初始化失败,关闭电源 */
        power_off();
    }

    return Stat;
}

(2)DSTATUS disk_status(BYTE drV);
  状态检测函数。检测是否支持当前的存储媒介,对FatFs来说,只要drv为0,就认为支持,然后返回O。程序如下:
DSTATUS disk_status (
    BYTE drv        /* 物理磁盘号*/
)
{
    if (drv) return STA_NOINIT;        /* 只支持一个物理磁盘 */
    return Stat;
}

(3)DRESULT disk_read(BYTE drv,BYTE*buff,DWORD sector,BYTE.count);

读扇区函数。在SD卡读接口函数的基础上编写,*buff存储已经读取的数据,sector是开始读的起始扇区,count是需要读的扇区数。1个扇区512个字节。执行无误返回O,错误返回非0。程序如下:

DRESULT disk_read (
    BYTE drv,            /* 物理磁盘号 */
    BYTE *buff,            /* 指向数据缓存的指针*/
    DWORD sector,        /* 起始扇区号(LBA) */
    BYTE count            /* 扇区计数器 (1..255) */
)
{
    if (drv || !count) return RES_PARERR;
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    if (!(CardType & 4)) sector *= 512;    /* 如果需要,转换为地址字节*/

    SELECT();            /* CS 为低电平,选择SD卡*/

    if (count == 1) {    /* 单块读取*/
        if ((send_cmd(CMD17, sector) == 0)  
            && rcvr_datablock(buff, 512))
            count = 0;
    }
    else {                /* 多块读取 */
        if (send_cmd(CMD18, sector) == 0) {  
            do {
                if (!rcvr_datablock(buff, 512)) break;
                buff += 512;
            } while (--count);
            send_cmd(CMD12, 0);                /* 停止传送*/
        }
    }

    DESELECT();            /* CS 为高电平,没有选中SD卡 */
    rcvr_spi();            /* Idle (Release DO) */

    return count ? RES_ERROR : RES_OK;
}
点赞  2010-11-28 22:48
(4)DRESULT disk_write(BYTE drv,const BYTE*buff,DWORD sector,BYTE count);

  写扇区函数。在SD卡写接口函数的基础上编写,*buff存储要写入的数据,sector是开始写的起始扇区count是需要写的扇区数。1个扇区512个字节。执行无误返回O,错误返回非0。
DRESULT disk_write (
    BYTE drv,            /* 物理磁盘号 */
    const BYTE *buff,             /* 指向数据缓存的指针*/
    DWORD sector,        /* 起始扇区号(LBA) */
    BYTE count            /* 扇区计数器 (1..255) */
)
{
    if (drv || !count) return RES_PARERR;
    if (Stat & STA_NOINIT) return RES_NOTRDY;
    if (Stat & STA_PROTECT) return RES_WRPRT;

    if (!(CardType & 4)) sector *= 512;   /* 如果需要,转换为地址字节*/

    SELECT();            /*CS为低,选中 */

    if (count == 1) {    /* 单块写*/
        if ((send_cmd(CMD24, sector) == 0)   
            && xmit_datablock(buff, 0xFE))
            count = 0;
    }
    else {                /* 多快写 */
        if (CardType & 2) {
            send_cmd(CMD55, 0); send_cmd(CMD23, count);    /* ACMD23 */
        }
        if (send_cmd(CMD25, sector) == 0) {   
            do {
                if (!xmit_datablock(buff, 0xFC)) break;
                buff += 512;
            } while (--count);
            if (!xmit_datablock(0, 0xFD))    /*停止传送标记 */
                count = 1;
        }
    }

    DESELECT();            /* CS置位 */
    rcvr_spi();            /* Idle (Release DO) */

    return count ? RES_ERROR : RES_OK;
}

(5)DRESULT disk_ioctl(BYTE drv,BYTE ctrl,VoiI*buff);
  存储媒介控制函数。ctrl是控制代码,*buff存储或接收控制数据。可以在此函数里编写自己需要的功能代码,比如获得存储媒介的大小、检测存储媒介的上电与否存储媒介的扇区数等。如果是简单的应用,也可以不用编写,返回O即可。
DRESULT disk_ioctl (
    BYTE drv,        /* 物理磁盘号*/
    BYTE ctrl,        /*控制代码 */
    void *buff        /* 发送接收的控制数据缓冲区*/
)
{
    DRESULT res;
    BYTE n, csd[16], *ptr = buff;
    WORD csize;


    if (drv) return RES_PARERR;

    res = RES_ERROR;

    if (ctrl == CTRL_POWER) {
        switch (*ptr) {
        case 0:        /*子控制代码=0 (关闭) */
            if (chk_power())
                power_off();        /* Power off */
            res = RES_OK;
            break;
        case 1:        /*子控制代码=1 (开启)*/
            power_on();                /* Power on */
            res = RES_OK;
            break;
        case 2:        /*子控制代码=2 (power get)*/
            *(ptr+1) = (BYTE)chk_power();
            res = RES_OK;
            break;
        default :
            res = RES_PARERR;
        }
    }
    else {
        if (Stat & STA_NOINIT) return RES_NOTRDY;

        SELECT();        /* CS = L */

        switch (ctrl) {
        case GET_SECTOR_COUNT :    /* 获取磁盘扇区数*/
            if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
                if ((csd[0] >> 6) == 1) {    /* SDC ver 2.00 */
                    csize = csd[9] + ((WORD)csd[8] << 8) + 1;
                    *(DWORD*)buff = (DWORD)csize << 10;
                } else {                    /* MMC or SDC ver 1.XX */
                    n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
                    csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
                    *(DWORD*)buff = (DWORD)csize << (n - 9);
                }
                res = RES_OK;
            }
            break;

        case GET_SECTOR_SIZE :    /*获取磁盘扇区大小 */
            *(WORD*)buff = 512;
            res = RES_OK;
            break;

        case CTRL_SYNC :    /*确定数据被写入 */
            if (wait_ready() == 0xFF)
                res = RES_OK;
            break;

        case MMC_GET_CSD :    /* 接收 CSD 作为数据块(16 bytes) */
            if (send_cmd(CMD9, 0) == 0        /* READ_CSD */
                && rcvr_datablock(ptr, 16))
                res = RES_OK;
            break;

        case MMC_GET_CID :    /*接收CID 作为数据块 (16 bytes) */
            if (send_cmd(CMD10, 0) == 0        /* READ_CID */
                && rcvr_datablock(ptr, 16))
                res = RES_OK;
            break;

        case MMC_GET_OCR :    /* Receive OCR as an R3 resp (4 bytes) */
            if (send_cmd(CMD58, 0) == 0) {    /* READ_OCR */
                for (n = 0; n < 4; n++)
                    *ptr++ = rcvr_spi();
                res = RES_OK;
            }

//        case MMC_GET_TYPE :    /*获取卡类型的标记 (1 byte) */
//            *ptr = CardType;
//            res = RES_OK;
//            break;

        default:
            res = RES_PARERR;
        }

        DESELECT();            /* CS = H */
        rcvr_spi();            /* Idle (Release DO) */
    }

    return res;
}
点赞  2010-11-28 22:48
(6) disk_timerproc (void)
设备时钟中断程序。
void disk_timerproc (void)
{
//    BYTE n, s;
    BYTE n;


    n = Timer1;                        /* 100Hz递减计时器 */
    if (n) Timer1 = --n;
    n = Timer2;
    if (n) Timer2 = --n;
}
(7)DWORD get_fattime(Void);

  实时时钟函数。返回一个32位无符号整数,时钟信息包含在这32位中,如下所示:

  bit31:25 年(O..127)从1980年到现在的年数

  bit24:21 月(1…12)

  bit20:16 日(1..31)

  bitl5.1] 时(O..23)

  bitl0:5 分(O..59)

  bit4:0 秒/2(0..29)

  如果用不到实时时钟,也可以简单地返回一个数。正确编写完DiskIO,移植工作也就基本完成了。
DWORD get_fattime (void)
{

    return    ((2007UL-1980) << 25)    // Year = 2007
            | (6UL << 21)            // Month = June
            | (5UL << 16)            // Day = 5
            | (11U << 11)            // Hour = 11
            | (38U << 5)            // Min = 38
            | (0U >> 1)                // Sec = 0
            ;
}


第五步,接下来的工作就是对FatFs进行配置。
  atFs是一款可配置可裁减的文件系统,使用者可以选择自己需要的功能。FatFs总共有5个文件,分别是ff.c、ff.h、diskio.c、diskio.h和integer.h。ff_c和integer.h一般不用改动,前面的移植工作主要更改的是diskio.c,而配置FatFs则主要修改ff.h和diskio.h。

  在diskio.h中,使用者可以根据需要使能disk—write或disk_ioetl。例如diskio.h中以下代码使能disk_write

#define _READONLY        0        /* 1: Read-only mode */

  在ff.h中,使用者可以根据需要对整个文件系统进行全面的配置:

  ①  #define_MCU_ENDIAN。有1和2两个值可设,默认情况下设1,以获得较好的系统性能。如果单片机是大端模式或者设为1时系统运行不正常,则必须设为2。

  ② #define_FS_READONLY。设为1时将使能只读操作,程序编译时将文件系统中涉及写的操作全部去掉,以节省空间。

  ③#define_FS_MINIMIZE。有0、1、2、3四个选项可设。设0表示可以使用全部Tiny-FatFs提供的用户函数;设1将禁用f_stat、f_getfree、f_unlink、f_mkdir、f_chmod和f_rename;设2将在1的基础上禁用f_opendir和f_readdir;设3将在1和2的基础上再禁用f_lseek。使用者可以根据需要进行裁减,以节省空间。
其关于ff.h的解读,我在学习文件系统的时候已经做过了解,也写成了心得。

到这里,文件系统的移植就基本完成了~然后就可以编写自己的程序了,关于SD应用程序的编写,之前已经总结过了~~接下来,就是学习VS1003了~~
点赞  2010-11-28 22:49
顶。。。mark....
   我也用的8962进行的这个文件系统的移植 。。不知你是否已经进行了JPEG的解码和EBOOK功能的扩展。。。我正在进行JPEG/JPG图片的解码。希望多多交流 。。。。。。
只有想不到,没有做不到。
点赞  2010-11-29 10:37
这篇文章很不错啊,支持,我也正在使用,希望能多交流!
点赞  2010-11-29 18:48
楼主写的不错,一定要顶
点赞  2010-11-29 19:38
高手要再写成文档就更好了
点赞  2010-11-29 23:00
经典之作啊,顶楼主!!!!!!!!!1
点赞  2010-11-30 17:26

回复 13楼 shilaike 的帖子

呵呵~谢谢~~还没有呢;P 因为我申请的任务事做一个MP3,所以,下一步得先学习扩展芯片解码音频文件,共同学习~~~
点赞  2010-11-30 21:46

回复 14楼 zhstars 的帖子

谢谢~~ ,新手新手,多多交流~~~
点赞  2010-11-30 21:47

回复 15楼 0212009623 的帖子

;P 谢~~~顶~~~
点赞  2010-11-30 21:48
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复