COFILOS SD 卡驱动程序
COFILOS SD 卡驱动程序
在 COFILOS 上开发 SD 卡驱动程序
去年,我完成了 COFILOS 和 Perseus 板的一个重要里程碑。我完成了一个完全使用 TDD 编写的 SD 卡驱动程序。
您可以查看以下视频 COFILOS SD Card。
即使我从互联网上获得了指导代码和足够的相关信息,编写起来仍然很困难。结果是一个非常易读和有组织的的代码。
初始化阶段非常复杂(占用了实现时间的大部分),如下所示
/*!
* \brief Device Start (Stub)
* @param pstDriver_ : Pointer to Device structure
* @return 0 (SUCCESS), 1: Fail (Could not open SPI driver)
*/
BOOL f_DriverSD_Start(void *pstDriver_ )
{
type_stDRIVERSD *pst_SD;
INT8U v_error;
INT8U v_PhysDevID;
BOOL v_retval;
pst_SD = (type_stDRIVERSD *) pstDriver_;
if (pst_SD->stDriver.eState != DRIVER_CLOSE) return 1;
if (pst_SD->f_HAL_HookCardPresent == NULL) return 1;
if (pst_SD->f_HAL_HookCardPresent() == 0) return 255;
v_retval = 1; /* default is failure */
f_DriverSDHelper_InitSPI(pst_SD);
f_DriverCoFILOS_Open((PINT8) "SPI.Driver",
0xFF, (DRIVER_STRUCT *) &pst_SD->st_SPI);
Driver_Start((DRIVER_STRUCT *) &pst_SD->st_SPI);
/* Acquire Mutex, we need uninterrupted SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_ON, NULL);
f_DriverSDHelper_ResetSD(pst_SD);
/* set default value for Unknown Card Type */
/* Assuming initially that all is good */
v_PhysDevID = pst_SD->v_PhyVolumeID;
st_SDPhysical[v_PhysDevID].v_UnknownCard = eSDUn_None;
pst_SD->v_CardType = eSDCardUnknown;
/* set CS, clocked */
f_DriverSDHelper_SelectDevice(pst_SD);
v_error = f_DriverSDHelper_SendCMD0(pst_SD);
if (v_error == 0xFE)
{
/* Unknown Card */
/* TODO: Unknown Card */
f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD0);
}
else
{
v_error = f_DriverSDHelper_SendCMD8(pst_SD);
if (v_error == 0) /* SDV2 Byte or Block */
{
v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x40000000);
v_error = f_DriverSDHelper_SendCMD58(pst_SD);
if (pst_SD->v_CardType == eSDCardSDv2Byte)
{
v_error = f_DriverSDHelper_SendCMD16(pst_SD);
st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Byte;
}
else
{
st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv2Block;
}
}
else if (v_error == 0xFE) /* SDv1 or MMCv3 or Unknown ? */
{
st_SDPhysical[v_PhysDevID].v_CardType = eSDCardSDv1;
v_error = f_DriverSDHelper_SendACMD41(pst_SD, 0x00000000);
if (v_error != 0)
{
/* MMCv3 ? */
v_error = f_DriverSDHelper_SendCMD1(pst_SD);
if (v_error == 0)
{
st_SDPhysical[v_PhysDevID].v_CardType = eSDCardMMCv3;
}
else
{
f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD1);
}
}
if (v_error == 0)
{
v_error = f_DriverSDHelper_SendCMD16(pst_SD);
}
}
else
{
/* Unknown card */
f_DriverSDHelper_UnknownDevice(pst_SD->v_PhyVolumeID, eSDUn_CMD8);
}
}
/* if Card is SDv1 or better then acquire CSD/CID */
switch(st_SDPhysical[v_PhysDevID].v_CardType)
{
case eSDCardSDv1:
case eSDCardSDv2Block:
case eSDCardSDv2Byte:
f_DriverSDHelper_SendCMD9(pst_SD);
f_DriverSDHelper_SendCMD10(pst_SD);
break;
default:;
}
/* Deassert CS, clocked */
f_DriverSDHelper_DeSelectDevice(pst_SD);
/* Release Mutex, finished exclusive SPI access */
Driver_Control((DRIVER_STRUCT *) &pst_SD->st_SPI, CMD_SET_SPIMUTEX_OFF, NULL);
if (st_SDPhysical[v_PhysDevID].v_CardType == eSDCardUnknown)
{
v_retval = 1;
}
else
{
st_SDPhysical[v_PhysDevID].v_DriverState = eSD_Started;
v_retval = 0;
pst_SD->st_SPI.v_ConfigReg = 0xA002; /* increase clock to 24MHz */
}
st_SDPhysical[v_PhysDevID].v_OpenCnt = 0;
return v_retval;
}
在主机(PC)上完成 TDD 阶段后,我需要在实际目标(ColdFire)微控制器上运行我的测试。
在下图中,您可以看到硬件设置以及我的程序员。我使用 Kingston 的 8GB micro-SD 卡进行测试。
我已经捕获了 SD 卡的扇区(和镜像)并将它们保存在我的硬盘上。我希望能够比较微控制器读取的内容与实际存储的数据。
在目标运行代码后,我打开了一个 PuTTY 终端来连接我的目标上的虚拟 COM 端口。然后通过 CLI(命令行解释器)我挂载了 SD 卡。我发出了 SDInfo
命令,提供的数据是 SD 卡的“几何结构
”。
然后我读取了扇区零(或 FAT 文件系统的 MBR)。我将数据与捕获的镜像进行比较,以确认读取了相同的信息。
第一个 FAT 扇区 (0x2000) 也被读取并验证。我必须检查它,因为我需要在稍后阶段运行一个 FAT 文件系统处理程序来读取 SD 卡(虽然不是很快)。
然后,开始写入测试。我 CLI 中的 write
命令目前支持写入单个 32 位值。这足以验证扇区写入操作。所以我使用了未使用的扇区 1 来写入值 0x1234567
。
接下来,对扇区 1 进行读取以检查扇区是否已写入。瞧!请注意大端写入(字节 0 处为 0x78,而不是 0x12)。
同时在调试过程中,我使用了 Tektronix TDS3012B 数字示波器和一个 Python 脚本来捕获和解码 SD SPI 数据。
该脚本还输出一个 VCD 格式的文件,适用于 GTKWave。因此,我可以看到并分析数据,包括模拟和数字数据。
由于使用了 TDD 方法,目标上的调试阶段非常快。通过几次迭代以及示波器和 Python 脚本工具的帮助,SD 卡驱动程序运行良好。