这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界» 论坛首页» 嵌入式开发» STM32» 用SensorTile做一个录音笔,首先要解决SD卡写入慢的问题

共2条 1/1 1 跳转至

用SensorTile做一个录音笔,首先要解决SD卡写入慢的问题

菜鸟
2017-06-13 15:00:40 打赏

要使用SensorTile实现对SD卡的数据写入

使用过程中发现写入的数据和实际差别很大

测试发现有一部分数据已经丢失

大概的流程是准备要写的数据,数据准备好后会触发中断

中断里设置写入标志,主while中判断写入标志为1时写入数据

后来在中断里加一个计数器,又在写入函数里加一个计数器

操作完以后比较2个计数发现写入计数比中断计数少很多

怀疑是SD写入速率过慢,数据还没写完第二个中断就已经出来了

在配置里把SPI的速率调到最高,已经达到40MHz

测试写入速率还是很慢

用示波器测量一下发现每个字节的间隔非常大

估计是代码里有延时或者其它判断造成的延时

顺着代码找到一处

f_write一次性写入8K字节的数据

在写数据时调用了SensorTile_sd.c文件里的BSP_SD_WriteBlocks函数

数据被拆分成多个512字节的block

每个block512字节通过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.cSD_IO_WriteByte函数调用了SD_IO_SPI_Write函数

SD_IO_SPI_Write函数调用了stm32l4xx_hal_spi.cHAL_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个字节就需要调用512HAL_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.cstm32l4xx_ll_sdmmc.c到工程的STM32L4xx_HAL_Driver目录

复制stm32l476g_eval_sd.c的内容到SensorTile_sd.c

同样的方法复制.h文件

编译,通过

如果认为这样就把功能完成就大错特错了

在修改SDI/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 8k32ms时间已经非常短了

如果觉得这样问题就解决那就大错特错了,测试时发现问题依旧

费半天劲还要改回来

既然标准库里的函数太复杂那就试试LL

添加头文件#include "stm32l4xx_ll_spi.h"

SD_IO_WriteByteSD_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;

}

改完判断后通信正常,试一下速度还没有原来的快




关键词: SensorTile 录音笔

专家
2017-06-15 09:35:14 打赏
2楼

楼主想要多块的速度?其实只要开机稳定在3-5s完成初始化,我感觉可以接受。

至于函数影响识别速度,我研究下。


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册]