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