3774|10

9798

帖子

24

TA的资源

版主

楼主
 

用SensorTile做一个录音笔,首先要解决SD卡写入慢的问题 [复制链接]

 

要使用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函数

static void SD_IO_SPI_Write(uint8_t Value)

{

  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_t Size, uint32_t Timeout)

{

  uint32_t tickstart = 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;

    goto error;

  }

 

  if((pData == NULL ) || (Size == 0))

  {

    errorcode = HAL_ERROR;

    goto error;

  }

 

  /* 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;

          goto error;

        }

      }

    }

  }

  /* 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

        {

          *((__IO uint8_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;

          goto error;

        }

      }

    }

  }

 

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

  return errorcode;

}

 

 

解决办法是在SensorTile.c里添加2个函数,实现多个字节的写入

void SD_IO_WriteByteNBytes(uint8_t *pData, uint16_t Size)

{

  /* Send the byte */

  SD_IO_SPI_WriteNBytes(pData , Size);

}

static void SD_IO_SPI_WriteNBytes(uint8_t *pData, uint16_t Size)

{

  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_t BSP_SD_WriteBlocks(uint32_t* p32Data, uint64_t Sector, uint16_t BlockSize, uint32_t NumberOfBlocks)

{

  uint32_t counter = 0, offset = 0;

  uint8_t rvalue = 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)

    {

      return MSD_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 */

  return rvalue;

}

 

 

以为这样改问题就可以解决就大错特错了

测试时发现问题依旧

经过无数次调试发现问题出现在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_t BSP_SD_WriteBlocks(uint32_t *pData, uint64_t WriteAddr, uint32_t BlockSize, uint32_t NumOfBlocks)

{

  if(HAL_SD_WriteBlocks(&uSdHandle, pData, WriteAddr, BlockSize, NumOfBlocks) != SD_OK)

  {

    return MSD_ERROR;

  }

  else

  {

    return MSD_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 */

  return response;

 

 

最后尝试可以把延时设置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

  */

void SD_IO_WriteByte(uint8_t Data)

{

  /* 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_t SD_IO_ReadByte(void)

{

  uint8_t data = 0;

 

  /* Get the received data */

  data = LL_SPI_ReceiveData8(SPI_SD_Handle.Instance);

 

  /* Return the shifted data */

  return data;

}

 

 

全部改完后以为这样能成就大错特错了

发现通信根本不正常,原来是LL库在发送和接收前需要对某寄存器进行判断

/**

  * @brief  Writes a byte on the SD.

  * @param  Data: byte to send.

  * @retval None

  */

void SD_IO_WriteByte(uint8_t Data)

{

//  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_t SD_IO_ReadByte(void)

{

  uint8_t data = 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 */

  return data;

}

 

 

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

 

经过3天的不懈努力这个功能终于没有完成!!

最新回复

过程艰辛,虾哥的付出必须要支持一下啊。哈哈  详情 回复 发表于 2017-5-29 09:50
点赞 关注
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 

回复
举报

9798

帖子

24

TA的资源

版主

沙发
 
本来很简单的功能
白天晚上,花了我3整天的时间
真佩服自己干活的效率


这是用汗水和眼泪都没完成的项目文件
STSW-STLKT01 v1.2.0.rar (5.28 MB, 下载次数: 89)
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

2167

帖子

8

TA的资源

五彩晶圆(初级)

板凳
 

看到那么大的一行字的时候,笑哭
个人签名坐而言不如起而行
 
 
 

回复

2721

帖子

0

TA的资源

纯净的硅(中级)

4
 
使用SDIO通信

点评

没有那么多引脚,只能通过SPI和SD卡通信  详情 回复 发表于 2017-5-28 11:02
 
 
 

回复

1万

帖子

25

TA的资源

版主

5
 
你是打算深入研究sensortile吗?各种功能都被你玩遍了。

点评

SensorTile的集成度很高 想用它做一个深度开发 比如多功能手环,功能多到没朋友 不过SD卡如果卡在这里后边的功能就不好做了  详情 回复 发表于 2017-5-28 11:03
 
 
 

回复

855

帖子

5

TA的资源

一粒金砂(高级)

6
 
虾哥牛逼,最后这个功能没有完成是个亮点啊

点评

牛逼就调出来了 一肚子苦水啊  详情 回复 发表于 2017-5-29 09:52
个人签名作为一个菜逼,干货并没有多少。唯一会的就是水,所以回帖水分大。望见谅!
 
 
 

回复

9798

帖子

24

TA的资源

版主

7
 

没有那么多引脚,只能通过SPI和SD卡通信
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

9798

帖子

24

TA的资源

版主

8
 
dcexpert 发表于 2017-5-27 22:38
你是打算深入研究sensortile吗?各种功能都被你玩遍了。

SensorTile的集成度很高
想用它做一个深度开发
比如多功能手环,功能多到没朋友
不过SD卡如果卡在这里后边的功能就不好做了
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

94

帖子

4

TA的资源

一粒金砂(中级)

9
 
过程艰辛,虾哥的付出必须要支持一下啊。哈哈

点评

这个问题先放一下,有空找找相关资料去  详情 回复 发表于 2017-5-29 09:52
 
 
 

回复

9798

帖子

24

TA的资源

版主

10
 
人民币的幻想 发表于 2017-5-28 09:36
虾哥牛逼,最后这个功能没有完成是个亮点啊

牛逼就调出来了
一肚子苦水啊
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复

9798

帖子

24

TA的资源

版主

11
 
w562601331 发表于 2017-5-29 09:50
过程艰辛,虾哥的付出必须要支持一下啊。哈哈

这个问题先放一下,有空找找相关资料去
个人签名虾扯蛋,蛋扯虾,虾扯蛋扯虾
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表