ecos-SD卡驱动程序设计

********************
* 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);
     }
}

 

 原文地址 http://www.devbone.com/LeadBBS/Announce/Announce.asp?BoardID=100&ID=8355
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值