基于STM32的SDIO用4位总线24MHZ DMA模式操作SDHC卡

更新时间:2023-11-25 10:54:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

基于STM32的SDIO用4位总线24MHZDMA模式操作SHDC卡

很蛋疼 的发现网上很多所谓的SDIO操作SHDC无意例外都是官方的那个烂玩意,完全没有修改过,所以很多时候根本无法初始化SHDC,我也在网上看到很多人关 于这部分的疑问,虽然STM32的SDIO的确是可以这样操作。但是很佩服那群人,什么都没改就发上来,把哥我害惨了。。。。

经过查资料,追踪, 最后运气可佳。我发现自己的金士顿4GSD卡(class4)不能初始化跟用4位总线dma操作的原因。。各位也可以上网去找别人的试试,很多人都说不能 用4位总线操作,而且用1位总线也只能是在低速率以及开启流控的情况下。而且经常出错。而4位总线总是提示没有检测到起始位。 但是他们都只会问,都没有去想象为什么,我也是。。但是后来发现。STM32的SDIO是完全没问题的,可以读写SHDC,用4位总线24MHZ工作在DMA模式,大家看我修改出来的例程就知道了。看我改过的地方,对比下官方的。 首先:

在配置的时候, 一开始的时候sd卡需要有至少发74个时钟使它自己初始话,这是2.0规范要求的,但是你们自己看看官方的,完全没有这个,我一直追踪,发先在电源初始化那里就已经卡住了- -|||。 于是我在那里面加入了一个发送74个时钟的小代码。

SD_Error SD_PowerON(void) {

SD_Error errorstatus = SD_OK;

uint32_t response = 0, count = 0; bool validvoltage = FALSE;

uint32_t SDType = SD_STD_CAPACITY; int16_t i = 0;

/* Power ON Sequence

-------------------------------------------------------*/ /* Configure the SDIO peripheral */ SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV; /* HCLK = 72MHz, SDIOCLK = 72MHz, SDIO_CK = HCLK/(178 + 2) = 400 KHz */

SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable; SDIO_Init(&SDIO_InitStructure);

/* Set Power State to ON */

SDIO_SetPowerState(SDIO_PowerState_ON); /* Enable SDIO Clock */ SDIO_ClockCmd(ENABLE);

/* CMD0: GO_IDLE_STATE

-------------------------------------------------------*/ /* No CMD response required */

SDIO_CmdInitStructure.SDIO_Argument = 0x0;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_GO_IDLE_STATE; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; for(;i < 74; i++) { SDIO_SendCommand(&SDIO_CmdInitStructure); CmdError(); } 看到没有,就是画线的那个,这个用37也可以了。但是规范点用74、

接下来,自己初始化可以通过了,因为上电正确一般是可以初始化正常的,这个没什么问题,到了这个函数就开始有问题了。

status = SD_SelectDeselect((u32) (SDCardInfo.RCA << 16)); if (status != SD_OK) {

rt_kprintf(\ goto __return; }

rt_kprintf(\ status = SD_EnableWideBusOperation(SDIO_BusWide_4b); 就是这个函数,使能四位总线模式的函数。事实上,在经过上一次的操作后,配置总线的时候我是这么配置的:

SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV +5 ; SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Enable; SDIO_Init(&SDIO_InitStructure);

我发现加入流控是必须 的,避免出现数据出错,因为这个模式很不稳定,很多时候会出现overrun的错误,或者crc错误。这时候我们可以先不要调用SD_EnableWideBusOperation(SDIO_BusWide_4b); 这样的话,其实我们是可以直接操作读写SHDC的,但是会发现不仅速度低,而且经常出错。这就是我为什么拼命要搞出四位总线的原因。

所以首先要SD_SelectDeselect((u32) (SDCardInfo.RCA << 16));命令选择SD为传送模式后,要再调用SD_EnableWideBusOperation(SDIO_BusWide_4b)配置总线为四位总线模式;这是为了能够以更快的速度读取SHDC卡。事实上这个函数是有问题的。传送是命令是没有问题的,但是判断依据问题不小。 大家看我的代码 SD_Error SD_EnableWideBusOperation(u32 WideMode) {

SD_Error errorstatus = SD_OK;

/* MMC Card doesn't support this feature */ if (SDIO_MULTIMEDIA_CARD == CardType) {

errorstatus = SD_UNSUPPORTED_FEATURE; rt_kprintf(\ return(errorstatus); }

else if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||

(SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType)) {

if (SDIO_BusWide_8b == WideMode) {

errorstatus = SD_UNSUPPORTED_FEATURE; rt_kprintf(\ return(errorstatus); }

else if (SDIO_BusWide_4b == WideMode) {

errorstatus = SDEnWideBus(ENABLE);

if (SD_OK == errorstatus) {

/* Configure the SDIO peripheral */

SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_4b; SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;

SDIO_Init(&SDIO_InitStructure); } else

rt_kprintf(\ } else {

errorstatus = SDEnWideBus(DISABLE);

if (SD_OK == errorstatus) {

/* Configure the SDIO peripheral */

SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV; SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b; SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;

SDIO_Init(&SDIO_InitStructure); } else

rt_kprintf(\ } }

return(errorstatus); }

注意到这个函数SDEnWideBus(ENABLE);

static SD_Error SDEnWideBus(FunctionalState NewState) {

SD_Error errorstatus = SD_OK; uint32_t scr[2] = {0, 0};

if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED) {

errorstatus = SD_LOCK_UNLOCK_FAILED;

rt_kprintf(\ return(errorstatus); }

/* Get SCR Register */

errorstatus = FindSCR(RCA, scr); /*首先注意到这个函数这个函数会卡到。*/

if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* If wide bus operation to be enabled */ if (NewState == ENABLE) {

/* If requested card supports wide bus operation */ // if ((scr[1] & SD_WIDE_BUS_SUPPORT) != SD_ALLZERO) // {

/* Send CMD55 APP_CMD with argument as card's RCA.*/

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16; SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp1Error(SDIO_APP_CMD); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* Send ACMD6 APP_CMD with argument as 2 for wide bus mode */ SDIO_CmdInitStructure.SDIO_Argument = 0x2;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_SD_SET_BUSWIDTH; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SDIO_APP_SD_SET_BUSWIDTH); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus);

}

return(errorstatus); // } // else

// {

// errorstatus = SD_REQUEST_NOT_APPLICABLE;

// rt_kprintf(\// return(errorstatus); // }

} /* If wide bus operation to be disabled */ else {

/* If requested card supports 1 bit mode operation */ if ((scr[1] & SD_SINGLE_BUS_SUPPORT) != SD_ALLZERO) {

/* Send CMD55 APP_CMD with argument as card's RCA.*/

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16; SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SDIO_APP_CMD);

if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* Send ACMD6 APP_CMD with argument as 2 for wide bus mode */ SDIO_CmdInitStructure.SDIO_Argument = 0x00;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_SD_SET_BUSWIDTH; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SDIO_APP_SD_SET_BUSWIDTH); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

return(errorstatus); } else {

errorstatus = SD_REQUEST_NOT_APPLICABLE;

rt_kprintf(\ return(errorstatus); } } }

static SD_Error FindSCR(uint16_t rca, uint32_t *pscr) {

uint32_t index = 0;

SD_Error errorstatus = SD_OK; uint32_t tempscr[2] = {0, 0}; uint16_t delay_time;

/* Set Block Size To 8 Bytes */

/* Send CMD55 APP_CMD with argument as card's RCA */ SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)8;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SET_BLOCKLEN; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp1Error(SDIO_SET_BLOCKLEN); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* Send CMD55 APP_CMD with argument as card's RCA */

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16; SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp1Error(SDIO_APP_CMD);

if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT; SDIO_DataInitStructure.SDIO_DataLength = 8;

SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_8b; SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO; SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block; SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable; SDIO_DataConfig(&SDIO_DataInitStructure);

/*经过测试,发现原来没有反应或者出错是因为缺少必要的处理时间,我这里是慢慢调试出来的比较理想的延时。 大家可以试试不加,有的卡运气好的可能通过了,但是不好的- -肯定没有数据。。。可能是看卡的质量吧,我只试过我的SD卡,不敢定论*/

for(delay_time = 0; delay_time < 20; delay_time++) __nop();

//SDIO_ClearFlag(SDIO_FLAG_STBITERR);

/* Send ACMD51 SD_APP_SEND_SCR with argument as 0 */ SDIO_CmdInitStructure.SDIO_Argument = 0x0;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SD_APP_SEND_SCR; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SDIO_SD_APP_SEND_SCR); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

while (!(SDIO->STA & (SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR ))) {

if (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET) {

*(tempscr + index) = SDIO_ReadData(); index++; if (index == 2) break; } }

if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET) {

SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT); errorstatus = SD_DATA_TIMEOUT;

rt_kprintf(\ return(errorstatus); }

else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET) {

SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL); errorstatus = SD_DATA_CRC_FAIL;

rt_kprintf(\ return(errorstatus); }

else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET) {

SDIO_ClearFlag(SDIO_FLAG_RXOVERR); errorstatus = SD_RX_OVERRUN;

rt_kprintf(\ return(errorstatus);

}

else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET) {

SDIO_ClearFlag(SDIO_FLAG_STBITERR); errorstatus = SD_START_BIT_ERR;

rt_kprintf(\ return(errorstatus); }

/* Clear all the static flags */ SDIO_ClearFlag(SDIO_STATIC_FLAGS);

*(pscr + 1) = ((tempscr[0] & SD_0TO7BITS) << 24) | ((tempscr[0] & SD_8TO15BITS) << 8) | ((tempscr[0] & SD_16TO23BITS) >> 8) | ((tempscr[0] & SD_24TO31BITS) >> 24);

*(pscr) = ((tempscr[1] & SD_0TO7BITS) << 24) | ((tempscr[1] & SD_8TO15BITS) << 8) | ((tempscr[1] & SD_16TO23BITS) >> 8) | ((tempscr[1] & SD_24TO31BITS) >> 24); return(errorstatus); }

看 上面这个函数的里面的彩色代码。那个是我自己添加的,正如我所说的,官方的SDcard文件忽视了sd卡自己处理所需要的延时问题。我在这里面发现,如果 不加入我这个延时,基本上,如果不先对SDIO_FLAG_STBITER置位的话,就会报错(一开始初始化成四位总线的,),要嘛没有反应(1位总 线),进入死循环。。基本上能退出这个函数就是因为出错- -。官方库忽略掉了这个延时,才导致这样的结果(所以BS那些只拿官方的东西直接忽悠我们骗点数的人)。 再注意下下一个彩色代码 if (index == 2) break;

知 道为什么要等于2就退出吗?因为定义index的时候初始值是0,但是你们自己追踪下,事实上发送这条命令只会返回两个数据, 我的返回的是两个0.而返回两个数据后基本总线就没反映了,成了死循环- -(至少我的卡是这样, 别人的卡我就不知道了,不过市面的SHDC卡大多买的都是金士顿的吧?不知道我的金士顿卡有没有代表性。)

基本上这样是能正常退出的,如果你们的卡不行,试着再降低下频率,我发现我的金士顿4G(class4)只有在小于12MHZ的一位总线下才有反应。当然, 是在没有配置为4位总线前。基本上这个函数就到这里,接着再返回SDEnWideBus(FunctionalState NewState)函数看下里面的以下代码

static SD_Error SDEnWideBus(FunctionalState NewState) {

SD_Error errorstatus = SD_OK; uint32_t scr[2] = {0, 0};

if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED) {

errorstatus = SD_LOCK_UNLOCK_FAILED;

rt_kprintf(\ return(errorstatus); }

/* Get SCR Register */

errorstatus = FindSCR(RCA, scr); /*首先注意到这个函数这个函数会卡到。*/

if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* If wide bus operation to be enabled */ if (NewState == ENABLE) {

/* If requested card supports wide bus operation */ // if ((scr[1] & SD_WIDE_BUS_SUPPORT) != SD_ALLZERO) // {

/* Send CMD55 APP_CMD with argument as card's RCA.*/

SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16; SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_CMD;

SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure); errorstatus = CmdResp1Error(SDIO_APP_CMD); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus); }

/* Send ACMD6 APP_CMD with argument as 2 for wide bus mode */ SDIO_CmdInitStructure.SDIO_Argument = 0x2;

SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_APP_SD_SET_BUSWIDTH; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;

SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure);

errorstatus = CmdResp1Error(SDIO_APP_SD_SET_BUSWIDTH); if (errorstatus != SD_OK) {

rt_kprintf(\ return(errorstatus);

}

return(errorstatus); // } // else // {

// errorstatus = SD_REQUEST_NOT_APPLICABLE;

// rt_kprintf(\

// return(errorstatus); // }

} /* If wide bus operation to be disabled */

彩 色的这些都是注释掉的呵,因为,事实上, findsrc函数返回的结果本身我就不知道是否是正确的,而且在sd2.0规范里面并没有仔细说明怎么切换总线模式,z只是要求先判断是否是传输模式, 接着是否是上锁,之后就可以直接用CMD6修改总线操作模式了,所以至今没发现findsrc这个函数的作用。另外在规范里面我也没有找到提及 findsrc这个函数的。于是直接忽略,将判断去掉,直接就设置成四位总线24MHZ,一开始不行, 我就降低频率,出现跟原来一样的反应,再降到12MHZ,突然有数据了, 于是又慢慢调试, 升到24MHZ,可以正常读写了。基本上改的就是这两个地方。其它地方因为目前没有研究的更深入没有发现问题,暂时就不改了。

至此,我已经可以用STM32的SDIO4位总线DMA模式读写SHDC卡了。经过测试,可以用文件系统读写文件, 创建文件夹,但是不能替换文件名- -,肯能是文件系统有问题。

附上我用的例程是rt_thread的文件系统, 算是为它小小宣传下,可能大家会用到, 基于MDK的rt_thread.工程在目录的bsp/stm32/里面。

文件我上传到了csdn- -由于是先上传的。后来才打算写这篇博客,见谅。。。。 PS:这是本人的有生以来第一篇博客。。。

http://download.csdn.net/source/2405254 这是例程下载地址- -我真的不是骗分的。。。我不缺。。。。

还记得上次我用RT_thread系统里面的文件系统调试了下STM32的SDIO读取SHDC。自然, 我是写的不好的呵,但是仍然希转载的人注意个人劳动成果,注明转载以及出处。 这次我由于个人兴趣,需要把SIDO驱动移植到到ASP系统里面去。

按理说,上次改好了, 这次应该可以正常编译就可以直接用的了。但是事情总没有你想象中的那么顺利,我发现在读跟写的时候,会出现一个相应超时的错误返回“RSP_TIMEOUT”,然后退出,一般是出现在这个位置

SD_Error SD_WriteBlock(uint32_t addr, uint32_t *writebuff, uint16_t BlockSize) {

SD_Error errorstatus = SD_OK;

uint8_t power = 0, cardstate = 0;

uint32_t timeout = 0, bytestransferred = 0;

uint32_t cardstatus = 0, count = 0, restwords = 0; uint32_t *tempbuff = writebuff; // uint32_t temp; //SYSTIM tim1,tim2; if (writebuff == NULL) {

errorstatus = SD_INVALID_PARAMETER;

return(errorstatus); }

TransferError = SD_OK; TransferEnd = 0;

TotalNumberOfBytes = 0;

SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT; SDIO_DataInitStructure.SDIO_DataLength = 0;

SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_1b; SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard; SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block; SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Disable; SDIO_DataConfig(&SDIO_DataInitStructure); SDIO_DMACmd(DISABLE);

if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED) {

errorstatus = SD_LOCK_UNLOCK_FAILED; return(errorstatus); }

if (CardType == SDIO_HIGH_CAPACITY_SD_CARD) {

BlockSize = 512; addr /= 512; }

/* Set the block size, both on controller and card */

if ((BlockSize > 0) && (BlockSize <= 2048) && ((BlockSize & (BlockSize - 1)) == 0)) {

power = convert_from_bytes_to_power_of_two(BlockSize); SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize; SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SET_BLOCKLEN; SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short; SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No; SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable; SDIO_SendCommand(&SDIO_CmdInitStructure); temp = SDIO->STA;

errorstatus = CmdResp1Error(SDIO_SET_BLOCKLEN);

不管读写都是在这个地方命令出错,而且有时候即使不出现RSP_TIMEOUT提示,也会出现ILLEGAL_CMD错误。

但是有时候你写出错后,读就有可以。都很随机,写是肯定失败了。 这个问题很多人都可能遇到吧。

但是其实你有用调试输出的话,就会发现,在你一调用命令发送函数的时候,读取SDIO->STA里面的内容,你会发现已经出错了呵。

这就是未发先错。但是你读取了SDIO->STA会自动清除自己的状态的, 于是 ,一发送后马上读取SDIO->STA的内容,再调用命令回应函数,这个命令有可以通过了。这样一般是可以读正常了。(如上绿色代码)。 上面那个也没花我多少时间, 我更多时间却是浪费在了写shdc上。

写函数也会报这个错误,但是如果你先读再写,有时候又不会报 这个错误。呵呵, 是不是很奇怪。还有基本上写用SD_WriteBlock()函数的话,一般会返回CRC_FAILED错误,也就是校验失败了,于是自然而然的退出了 写,返回错误。但是我在rt_thread系统上是可以正常写的,但是,写不会退出写函数,但是内容早就写完了,这个bug也是我调试两个系统代码的时候 才发现的。而在asp里面,写如果不报错,也是没写进去的。

我自然而然 的想到可能是系统编译器的问题,但是后来我又排除了这个可能, 至于怎么排除,属于个人技巧问题呵。

再接着我就查看了整个读写驱动,每个错误都调试。结果还是没有找出个所以然来,依旧的CRC_FAILED.T_T 难道是读写顺序也有关。。。试了下,一样不行。相反的,更多时候连读也有问题了。。。。 在5个连续通宵到早上7点的惨痛代价后,我想起了一个事情。 那就是关于STM32传输数据的SDIO。

首先,我采用的是4位DMA模式,所以我的读写是自动的,asp系统不可以,但是rt_thread却可以读写,即使是编译器不同,但是时钟晶振总是一样的,不可能一个错误,一个正常的。除非系统中断影响,还有一个致命伤,晶振!!!!!!!!

没错了,就是晶振,在asp里面我的stm32晶振是调到最高72Mhz的,但是rt_thread我没注意过,能够读,但是写会数据出错,就说明是数据太快,SHDC卡跟不上节奏,所以crc校验出错了。 当然,推想还是要靠实践证明的,于是我就把4位总线SDIO的时钟调低了,如下:

SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV+1;

SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;

SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;

SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable; SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_4b;

SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Enable; SDIO_Init(&SDIO_InitStructure);

直接加1就是了,这样的话分频出来实际上是72M/(2+2) = 18M

然后试了下, 竟然可以写,而且更重要的是,这个写是没有bug的,rt_thread系统的写会是死循环,回不来了,但是这个写回正常返回。

再接着,我自然也会想那么先前的SDIO->STAs错误是不是也是这个原因造成的。 于是我也把temp = SDIO->STA注释掉,结果: 一切正常。

呵呵,对不起了,各位, 忽悠了你们这么久,其实前面都可以不要的,我直接跟你们说是由于STM32的晶振频率影响了SHDC的写就可以了的。

实际上改慢下SIDO的读写速度,大家应该意见很大把, 那如果你们不写的话, 可以直接用我个temp=SDIO->STA;这样读取速度一样,但是不能写,而且- -读的也很不稳定。

顺便说下把,看到有个论坛一个老大哥用sdio读取shdc跟sd时候不一样, 说用fat的f_open函数失败,

然后说是自己没仔细看, 忽略了SHDC跟SD卡的系统引导区不同,要去查手册的。

我其实很想说的是,“大哥,你的读函数出错了才是。SHDC的系统引导区跟普通SD卡一样的。只是FAt16格式化的SD卡盘格式符跟fat32的SD卡格式符存放地址不一样而已。你读取的数据错误了。” 其它也没啥了,文件已经上传CSDN。

感觉自己好白痴啊, 这门简单的地方现在才发现,白白浪费了5天的光阴的。 http://download.csdn.net/source/2600382 另外,本人不善于文笔,写的不好。不要喷我 还有,请不要张冠李戴,把作者的东西说成自己的。 纯粹的原创,转载的请注明出处。

以前也弄过zigbee的,等有空了就顺便把zigbee协议栈2006的串口弄出个教程把, 发现这个不难,但是纠结在这上面的人很多啊。

本文来源:https://www.bwwdw.com/article/m2st.html

Top