要使用SensorTile实现对SD卡的数据写入
使用过程中发现写入的数据和实际差别很大
测试发现有一部分数据已经丢失
大概的流程是准备要写的数据,数据准备好后会触发中断
中断里设置写入标志,主while中判断写入标志为1时写入数据
后来在中断里加一个计数器,又在写入函数里加一个计数器
操作完以后比较2个计数发现写入计数比中断计数少很多
怀疑是SD写入速率过慢,数据还没写完第二个中断就已经出来了
在配置里把SPI的速率调到最高,已经达到40MHz
测试写入速率还是很慢
用示波器测量一下发现每个字节的间隔非常大
估计是代码里有延时或者其它判断造成的延时
顺着代码找到一处
f_write一次性写入8K字节的数据
在写数据时调用了SensorTile_sd.c文件里的BSP_SD_WriteBlocks函数
数据被拆分成多个512字节的block
每个block的512字节通过for循环将数据循环写入
/* Write the block data to SD : write count data by block */ for(counter=0;counter<BlockSize;counter++) { /* Send the pointed byte */ SD_IO_WriteByte(*pData); /* Point to the next location where the byte read will be saved */ pData++; } |
SensorTile.c的SD_IO_WriteByte函数调用了SD_IO_SPI_Write函数
SD_IO_SPI_Write函数调用了stm32l4xx_hal_spi.c的HAL_SPI_Transmit函数
staticvoidSD_IO_SPI_Write(uint8_tValue) { HAL_StatusTypeDef status=HAL_OK; status=HAL_SPI_Transmit(&SPI_SD_Handle,(uint8_t*)&Value,1,SpixTimeout); /* Check the communication status */ if(status!=HAL_OK) { /* Execute user timeout callback */ SD_IO_SPI_Error(); } } |
HAL_SPI_Transmit函数允许一次写入多个字节数据
而实际SD_IO_SPI_Write函数都是通过HAL_SPI_Transmit写入一个字节
512个字节就需要调用512次HAL_SPI_Transmit函数
而HAL_SPI_Transmit函数里边有很多代码,每个字节都要做这些判断显然很浪费时间
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef*hspi,uint8_t*pData,uint16_tSize,uint32_tTimeout) { uint32_ttickstart=HAL_GetTick(); HAL_StatusTypeDef errorcode=HAL_OK; assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction)); /* Process Locked */ __HAL_LOCK(hspi); if(hspi->State!=HAL_SPI_STATE_READY) { errorcode=HAL_BUSY; gotoerror; } if((pData==NULL)||(Size==0)) { errorcode=HAL_ERROR; gotoerror; } /* Set the transaction information */ hspi->State=HAL_SPI_STATE_BUSY_TX; hspi->ErrorCode=HAL_SPI_ERROR_NONE; hspi->pTxBuffPtr=pData; hspi->TxXferSize=Size; hspi->TxXferCount=Size; hspi->pRxBuffPtr=(uint8_t*)NULL; hspi->RxXferSize=0; hspi->RxXferCount=0; /* Configure communication direction : 1Line */ if(hspi->Init.Direction==SPI_DIRECTION_1LINE) { SPI_1LINE_TX(hspi); } /* Reset CRC Calculation */ if(hspi->Init.CRCCalculation==SPI_CRCCALCULATION_ENABLE) { SPI_RESET_CRC(hspi); } /* Check if the SPI is already enabled */ if((hspi->Instance->CR1&SPI_CR1_SPE)!=SPI_CR1_SPE) { /* Enable SPI peripheral */ __HAL_SPI_ENABLE(hspi); } /* Transmit data in 16 Bit mode */ if(hspi->Init.DataSize>SPI_DATASIZE_8BIT) { /* Transmit data in 16 Bit mode */ while(hspi->TxXferCount>0) { /* Wait until TXE flag is set to send data */ if((hspi->Instance->SR&SPI_FLAG_TXE)==SPI_FLAG_TXE) { hspi->Instance->DR=*((uint16_t*)hspi->pTxBuffPtr); hspi->pTxBuffPtr+=sizeof(uint16_t); hspi->TxXferCount--; } else { /* Timeout management */ if((Timeout==0)||((Timeout!=HAL_MAX_DELAY)&&((HAL_GetTick()-tickstart)>=Timeout))) { errorcode=HAL_TIMEOUT; gotoerror; } } } } /* Transmit data in 8 Bit mode */ else { while(hspi->TxXferCount>0) { /* Wait until TXE flag is set to send data */ if((hspi->Instance->SR&SPI_FLAG_TXE)==SPI_FLAG_TXE) { if(hspi->TxXferCount>1) { /* write on the data register in packing mode */ hspi->Instance->DR=*((uint16_t*)hspi->pTxBuffPtr); hspi->pTxBuffPtr+=sizeof(uint16_t); hspi->TxXferCount-=2; } else { *((__IOuint8_t*)&hspi->Instance->DR)=(*hspi->pTxBuffPtr++); hspi->TxXferCount--; } } else { /* Timeout management */ if((Timeout==0)||((Timeout!=HAL_MAX_DELAY)&&((HAL_GetTick()-tickstart)>=Timeout))) { errorcode=HAL_TIMEOUT; gotoerror; } } } } /* Enable CRC Transmission */ if(hspi->Init.CRCCalculation==SPI_CRCCALCULATION_ENABLE) { hspi->Instance->CR1|=SPI_CR1_CRCNEXT; } /* Check the end of the transaction */ if(SPI_EndRxTxTransaction(hspi,Timeout)!=HAL_OK) { hspi->ErrorCode=HAL_SPI_ERROR_FLAG; } /* Clear overrun flag in 2 Lines communication mode because received is not read */ if(hspi->Init.Direction==SPI_DIRECTION_2LINES) { __HAL_SPI_CLEAR_OVRFLAG(hspi); } if(hspi->ErrorCode!=HAL_SPI_ERROR_NONE) { errorcode=HAL_ERROR; } error: hspi->State=HAL_SPI_STATE_READY; /* Process Unlocked */ __HAL_UNLOCK(hspi); returnerrorcode; } |
解决办法是在SensorTile.c里添加2个函数,实现多个字节的写入
voidSD_IO_WriteByteNBytes(uint8_t*pData,uint16_tSize) { /* Send the byte */ SD_IO_SPI_WriteNBytes(pData,Size); } staticvoidSD_IO_SPI_WriteNBytes(uint8_t*pData,uint16_tSize) { HAL_StatusTypeDef status=HAL_OK; status=HAL_SPI_Transmit(&SPI_SD_Handle,pData,Size,SpixTimeout); /* Check the communication status */ if(status!=HAL_OK) { /* Execute user timeout callback */ SD_IO_SPI_Error(); } } |
然后在BSP_SD_WriteBlocks通过调用一次SD_IO_WriteByteNBytes函数实现512个字节的快速写入
uint8_tBSP_SD_WriteBlocks(uint32_t*p32Data,uint64_tSector,uint16_tBlockSize,uint32_tNumberOfBlocks) { uint32_tcounter=0,offset=0; uint8_trvalue=MSD_ERROR; uint8_t*pData=(uint8_t*)p32Data; if(SD_CardType!=HIGH_CAPACITY_SD_CARD) { Sector*=BlockSize; } /* Data transfer */ while(NumberOfBlocks--) { /* Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write blocks and Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ if(SD_IO_WriteCmd(SD_CMD_WRITE_SINGLE_BLOCK,Sector+offset,0xFF,SD_RESPONSE_NO_ERROR)!=HAL_OK) { returnMSD_ERROR; } /* Send dummy byte */ SD_IO_WriteByte(SD_DUMMY_BYTE); /* Send the data token to signify the start of the data */ SD_IO_WriteByte(SD_START_DATA_SINGLE_BLOCK_WRITE); /* Write the block data to SD : write count data by block */ // for (counter = 0; counter < BlockSize; counter++) // { // /* Send the pointed byte */ // SD_IO_WriteByte(*pData); // // /* Point to the next location where the byte read will be saved */ // pData++; // } SD_IO_WriteByteNBytes(pData,BlockSize); /* Set next write address */ if(SD_CardType!=HIGH_CAPACITY_SD_CARD) { offset+=BlockSize; } else { offset+=1; } /* Put CRC bytes (not really needed by us, but required by SD) */ SD_IO_ReadByte(); SD_IO_ReadByte(); /* Read data response */ if(SD_GetDataResponse()==SD_DATA_OK) { /* Set response value to success */ rvalue=MSD_OK; } else { /* Set response value to failure */ rvalue=MSD_ERROR; } } /* Send dummy byte: 8 Clock pulses of delay */ SD_IO_WriteDummy(); /* Returns the reponse */ returnrvalue; } |
以为这样改问题就可以解决就大错特错了
测试时发现问题依旧
经过无数次调试发现问题出现在SD_GetDataResponse函数上
BSP_SD_WriteBlocks每次写过都要判断一下SD_GetDataResponse的返回值
/* Read data response */ if(SD_GetDataResponse()==SD_DATA_OK) { /* Set response value to success */ rvalue=MSD_OK; } |
SD_GetDataResponse有这样一句话/* Wait null data */ while (SD_IO_ReadByte() == 0)
读SPI数据,如果为0就一直读
SD_IO_ReadByte最后也是执行的HAL_SPI_TransmitReceive函数
我在while中加一个计数器,发现每次都要执行200次以上才能跳出while循环
很多时候都能达到1万多次
函数太复杂了,没法改
看一下别的工程,去STM32L476G_EVAL里边看下
一看不要紧,差点没被气死
这是stm32l476g_eval_sd.c里的BSP_SD_WriteBlocks函数,短短几行就能实现这个功能
uint8_tBSP_SD_WriteBlocks(uint32_t*pData,uint64_tWriteAddr,uint32_tBlockSize,uint32_tNumOfBlocks) { if(HAL_SD_WriteBlocks(&uSdHandle,pData,WriteAddr,BlockSize,NumOfBlocks)!=SD_OK) { returnMSD_ERROR; } else { returnMSD_OK; } } |
赶紧把STM32L476G_EVAL里的SD移植到SensorTile里
在stm32l4xx_hal_conf.h文件中把#define HAL_SD_MODULE_ENABLED注释去掉
添加stm32l4xx_hal_sd.c和stm32l4xx_ll_sdmmc.c到工程的STM32L4xx_HAL_Driver目录
复制stm32l476g_eval_sd.c的内容到SensorTile_sd.c
同样的方法复制.h文件
编译,通过
如果认为这样就把功能完成就大错特错了
在修改SD的I/O口时发现STM32L476G_EVAL使用的是SDMMC模块
使用之根数据线那种,不是SensorTile里的SPI
费半天劲还要改回来
再测试时如果像这样在Wait null data前加一个50ms的延时结果MISO引脚抬起的时候也是在延时完执行了SD_IO_ReadByte()后
HAL_Delay(50); /* Wait null data */ while(SD_IO_ReadByte()==0) { i++; } /* Return response */ returnresponse; |
最后尝试可以把延时设置2ms正好,2ms = 512byte 8k才32ms时间已经非常短了
如果觉得这样问题就解决那就大错特错了,测试时发现问题依旧
费半天劲还要改回来
既然标准库里的函数太复杂那就试试LL库
添加头文件#include "stm32l4xx_ll_spi.h"
把SD_IO_WriteByte和SD_IO_ReadByte里的SPI读写函数改成LL库的
/** * @brief Writes a byte on the SD. * @param Data: byte to send. * @retval None */ voidSD_IO_WriteByte(uint8_tData) { /* Send the byte */ LL_SPI_TransmitData8(SPI_SD_Handle.Instance,Data); } /** * @brief Reads a byte from the SD. * @param None * @retval The received byte. */ uint8_tSD_IO_ReadByte(void) { uint8_tdata=0; /* Get the received data */ data=LL_SPI_ReceiveData8(SPI_SD_Handle.Instance); /* Return the shifted data */ returndata; } |
全部改完后以为这样能成就大错特错了
发现通信根本不正常,原来是LL库在发送和接收前需要对某寄存器进行判断
/** * @brief Writes a byte on the SD. * @param Data: byte to send. * @retval None */ voidSD_IO_WriteByte(uint8_tData) { // SD_IO_SPI_Write(Data); // return; /* Wait until TXE flag is set to send data */ while((SPI_SD_Handle.Instance->SR&SPI_FLAG_TXE)!=SPI_FLAG_TXE); /* Send the byte */ LL_SPI_TransmitData8(SPI_SD_Handle.Instance,Data); } /** * @brief Reads a byte from the SD. * @param None * @retval The received byte. */ uint8_tSD_IO_ReadByte(void) { uint8_tdata=0; // return SD_IO_SPI_Read(); while((SPI_SD_Handle.Instance->SR&SPI_FLAG_TXE)!=SPI_FLAG_TXE); LL_SPI_TransmitData8(SPI_SD_Handle.Instance,0xff); while((SPI_SD_Handle.Instance->SR&SPI_FLAG_RXNE)!=SPI_FLAG_RXNE); /* Get the received data */ data=LL_SPI_ReceiveData8(SPI_SD_Handle.Instance); /* Return the shifted data */ returndata; } |
改完判断后通信正常,试一下速度还没有原来的快