打印

ecos-SD卡驱动程序设计

[复制链接]
1584|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiao6666|  楼主 | 2012-10-15 15:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
********************
* SD卡驱动程序设计 *
********************
                 ------《ecos增值包》之SD卡驱动
    2006/09/17   asdjf@163.com  www.armecos.com

    《ecos增值包》提供了SD/MMC卡驱动程序。SD/MMC卡是体积小(24mm * 32mm * 1.4mm),重量轻(<2克)的非易失性大容量存储设备。典型的卡容量为16MB、128MB、256MB、512MB直至4GB。


    硬件级别有两种方式访问SD卡:专用SD总线或者SPI总线。在使用时软件会选择使用其中一种接口。SD总线在性能上优于SPI总线,但需要增加额外硬件。


    SD卡一般被格式化为PC兼容的格式,第一块保存分区表,其余部分存放一个单独的FAT文件系统。


    SD卡可以在任何时间插入和拔出,设备驱动程序在下一次I/O操作时将检测到拔出事件并通过发送错误码ENODEV向更高层软件汇报,不过,高层代码不保证系统能从这个错误恢复。期望的正确做法是应用代码在尝试访问文件I/O前显式地调用mount挂装SD卡,在卡移除前调用unmount。在mount 和unmount之间,系统倾向于在缓存中保存数据块,以便提升性能。如果在unmount前移除SD卡,就会破坏文件系统正确性,导致文件系统处于不稳定状态。定期使用同步sync将减少文件系统被破坏的风险。


    SD卡驱动的主要内容是:硬件初始化、SPI时钟速率设置/恢复、寄存器读写、数据块读写擦、电源开关控制、卡在位检测、卡变动识别。


    最重要的代码之一是mmc_spi_send_command_start(...),完成命令的组装发送应答。如代码所示,首先将命令、参数和CRC校验组装成6字节命令封装,第7字节的FF是为了获得紧接着的卡应答目的设置的。前面MMC_SPI_BACKGROUND_WRITES条件编译里的代码是为后台模式写操作准备的,用于继续等待前一次写操作命令完成后的数据写入扇区延时,这么做可以在写命令完成后立即做别的工作,不必等待写扇区完成,在下一次写操作前会判断上一次写是否完成,若没有完成则继续等待。


    接下来就是SPI总线上的操作了,如《SPI驱动程序设计》一节所述,首先要调用cyg_spi_transaction_begin开始SPI传输过程,这个函数完成总线锁定,避免多个线程同时访问同一个SPI总线。然后调用cyg_spi_transaction_transfer发送命令和接收应答。由于《ecos增值包》的驱动程序可以同时驱动多个SPI总线,所以第一个参数必须提供设备识别dev,以便区分是哪个SPI总线设备。 cyg_mmc_spi_polled参数用于选择工作模式(查询/中断),此处应该选择中断模式,以便提高CPU工作效率。SMARTARM2200的 SPI是全双工的,收发同时进行,给出发送缓冲、接收缓冲和传输数量,此函数就能自动完成命令发送和应答接收。可能响应不是立即的,此时需要不断查询有效的卡响应。当然应该提供超时退出机制。


    数据块读写与此类似,主要是调用SPI总线驱动,按照SD卡访问流程操作,配套书上已经讲得非常详细了,在此不再赘述,请读者举一反三。


    CID和CSD是两个比较重要的寄存器,解码程序见后面示例。


    SD卡驱动程序总流程:


    硬件初始化(切记一定要断开JP7的ATA_INT跳线,因为SSEL需要设置为主机模式,需要上拉。如果不断开ATA_INT,会干扰上拉,导致SPI主机工作不正常。)
    至少延时74个时钟周期
    复位SD卡命令
    激活初始化处理命令
    读取并解析CID
    读取并解析CSD
    设置SPI时钟为最大值
    设置读写块长度
    ---数据块读写擦---


static cyg_uint32
mmc_spi_send_command_start(cyg_mmc_spi_disk_info_t* disk, cyg_uint32 command, cyg_uint32 arg)
{
    cyg_spi_device* dev = disk->mmc_spi_dev;
    cyg_uint8       request[7];
    cyg_uint8       response[7];
    cyg_uint8       reply;
    int             i;

#ifdef MMC_SPI_BACKGROUND_WRITES   
    if (disk->mmc_writing) {
        DEBUG2("mmc_spi_send_command_start(): polling for completion of previous write/n");
        disk->mmc_writing   = 0;
        response[0]    = 0x00;
        for (i = 0; (i < MMC_SPI_WRITE_BUSY_RETRIES) && (0x00FF != response[0]); i++) {
            cyg_spi_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response);
        }
    }
#endif   


    request[0]  = command | 0x0040;
    request[1]  = (arg >> 24) & 0x00FF;
    request[2]  = (arg >> 16) & 0x00FF;
    request[3]  = (arg >>  8) & 0x00FF;
    request[4]  = arg         & 0x00FF;
    request[5]  = (command == 0x00) ? 0x0095 : 0x00ff;
    request[6]  = 0x00ff;

    cyg_spi_transaction_begin(dev);


    cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 7, request, response, 0);
    DEBUG2("Sent command %02x %d: reply bytes %02x %02x %02x %02x %02x %02x %02x/n", command, arg, /
                response[0], response[1], response[2], response[3], response[4], response[5], response[6]);


    reply       = response[6];
    for (i = 0; (i < MMC_SPI_COMMAND_RETRIES) && (0 != (reply & 0x0080)); i++) {
        cyg_spi_transaction_transfer(dev, cyg_mmc_spi_polled, 1, mmc_spi_ff_data, response, 0);
        reply = response[0];
        DEBUG2("  loop %d, additional reply %02x/n", i, reply);
    }

    return (cyg_uint32) reply;
}

#define UNSTUFF_BITS(resp,start,size)                              /
     ({                                                /
           const int __size = size;                        /
           const cyg_uint32 __mask = (__size < 32 ? 1 << __size : 0) - 1;      /
           const int __off = 3 - ((start) / 32);                  /
           const int __shft = (start) & 31;                  /
           cyg_uint32 __res;                                    /
                                                     /
           __res = resp[__off] >> __shft;                        /
           if (__size + __shft > 32)                        /
                 __res |= resp[__off-1] << ((32 - __shft) % 32);      /
           __res & __mask;                                    /
     })

static void mmc_decode_cid(cyg_uint32 *resp, struct mmc_cid *mmc_cid)
{
     memset(mmc_cid, 0, sizeof(struct mmc_cid));

     /*
      * SD doesn't currently have a version field so we will
      * have to assume we can parse this.
      */
     mmc_cid->manfid              = UNSTUFF_BITS(resp, 120, 8);
     mmc_cid->oemid                  = UNSTUFF_BITS(resp, 104, 16);
     mmc_cid->prod_name[0]            = UNSTUFF_BITS(resp, 96, 8);
     mmc_cid->prod_name[1]            = UNSTUFF_BITS(resp, 88, 8);
     mmc_cid->prod_name[2]            = UNSTUFF_BITS(resp, 80, 8);
     mmc_cid->prod_name[3]            = UNSTUFF_BITS(resp, 72, 8);
     mmc_cid->prod_name[4]            = UNSTUFF_BITS(resp, 64, 8);
     mmc_cid->hwrev                  = UNSTUFF_BITS(resp, 60, 4);
     mmc_cid->fwrev                  = UNSTUFF_BITS(resp, 56, 4);
     mmc_cid->serial              = UNSTUFF_BITS(resp, 24, 32);
     mmc_cid->year                    = UNSTUFF_BITS(resp, 12, 8);
     mmc_cid->month                  = UNSTUFF_BITS(resp, 8, 4);

     mmc_cid->year += 2000; /* SD cards year offset */
}

static void mmc_decode_csd(cyg_uint32 *resp, struct mmc_csd *mmc_csd)
{
     unsigned int e, m, csd_struct;


     csd_struct = UNSTUFF_BITS(resp, 126, 2);

     switch (csd_struct) {
     case 0:
           m = UNSTUFF_BITS(resp, 115, 4);
           e = UNSTUFF_BITS(resp, 112, 3);
           mmc_csd->tacc_ns       = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
           mmc_csd->tacc_clks       = UNSTUFF_BITS(resp, 104, 8) * 100;

           m = UNSTUFF_BITS(resp, 99, 4);
           e = UNSTUFF_BITS(resp, 96, 3);
           mmc_csd->max_dtr        = tran_exp[e] * tran_mant[m];
           mmc_csd->cmdclass        = UNSTUFF_BITS(resp, 84, 12);

           e = UNSTUFF_BITS(resp, 47, 3);
           m = UNSTUFF_BITS(resp, 62, 12);
           mmc_csd->capacity        = (1 + m) << (e + 2);

           mmc_csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
           mmc_csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
           mmc_csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
           mmc_csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
           mmc_csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
           mmc_csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
           mmc_csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
           break;
     case 1:
           mmc_csd->tacc_ns       = 0; /* Unused */
           mmc_csd->tacc_clks       = 0; /* Unused */

           m = UNSTUFF_BITS(resp, 99, 4);
           e = UNSTUFF_BITS(resp, 96, 3);
           mmc_csd->max_dtr        = tran_exp[e] * tran_mant[m];
           mmc_csd->cmdclass        = UNSTUFF_BITS(resp, 84, 12);

           m = UNSTUFF_BITS(resp, 48, 22);
           mmc_csd->capacity     = (1 + m) << 10;

           mmc_csd->read_blkbits = 9;
           mmc_csd->read_partial = 0;
           mmc_csd->write_misalign = 0;
           mmc_csd->read_misalign = 0;
           mmc_csd->r2w_factor = 4; /* Unused */
           mmc_csd->write_blkbits = 9;
           mmc_csd->write_partial = 0;
           break;
     default:
           D("unrecognised CSD structure version %d/n", csd_struct);
     }
}

相关帖子

沙发
yy668yy2011| | 2012-10-26 14:55 | 只看该作者
1# xiao6666 初学者,求指导!

使用特权

评论回复
板凳
GoldSunMonkey| | 2012-10-26 15:20 | 只看该作者
:handshake

使用特权

评论回复
地板
wmsk| | 2012-10-26 22:48 | 只看该作者
:handshake和猴哥

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

48

主题

453

帖子

1

粉丝