这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » 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函数

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_tTimeout)

{

  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_tNumberOfBlocks)

{

  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;

}

 

 

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




关键词: SensorTile     录音笔    

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

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

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


共2条 1/1 1 跳转至

回复

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