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