STM32 SPI-DMA读写 SPI Flash (只需改动两个函数) +cube配置 多器件

最近在项目中遇到了 通过SPI DMA方式读写W25Q256;
网上许多例程都是直接读取flash,读写会占用比较长的时间 ,如果这个SPI上还挂载了其他SPI 器件,如SPI显示屏,就需要通过开启DMA来提升速度了。
在这里记录一下我的调试过程:
一.详细CUBE 配置
1.基础设置,我用的是SPI6.STM32 SPI-DMA读写 SPI Flash (只需改动两个函数) +cube配置 多器件_第1张图片
2.DMA设置,并设置中断优先级STM32 SPI-DMA读写 SPI Flash (只需改动两个函数) +cube配置 多器件_第2张图片
3.SPI global interrupt不用开启
然后生成代码;
二,代码。
基础代码是正点原子的SPI flash 函数
基本读写函数没有开启DMA,因为只有单字节读写,所以不用开启DMA,后面两个就是DMA读写函数(hal库操作还是贼简单)


static inline void W25Q_SpiDmaStop()
{
    while(__HAL_DMA_GET_FLAG(&hdma_spi2_tx,DMA_FLAG_TCIF3_7)) { asm(""); }

   while (hspiflash.State != HAL_SPI_STATE_READY);
  // W25Q_SpiLcdCsDisable();
}

uint8_t W25Q_SpiReadWriteByte(uint8_t TxData)
{
    uint8_t Rxdata;

    HAL_SPI_TransmitReceive(&hspiflash,&TxData,&Rxdata,1, 1000);

    return Rxdata;                      //返回收到的数据
}
uint16_t W25Q_SpiWriteDMA(uint8_t *pTxData, uint16_t Size)
{
    W25Q_SpiDmaStop();
  //  uint16_t retval =  HAL_SPI_Transmit_DMA(&hspiflash,pTxData,Size);
    uint16_t retval = 	HAL_SPI_TransmitReceive_DMA(&hspiflash,pTxData,pTxData,Size);


    return retval;
}


/* 开始读数据,因为底层DMA缓冲区有限,必须分包读 */
uint16_t W25Q_SpiReadDMA( uint8_t *pRxData, uint32_t Size)
{
    uint16_t rem;
    uint32_t g_spiLen;
    uint8_t g_spiRxBuf[SPI_BUFFER_SIZE];
    uint8_t g_spiTxBuf[SPI_BUFFER_SIZE];
    uint8_t retval = 0;


   for (uint16_t i = 0; i <( Size / SPI_BUFFER_SIZE); i++)
       {
           g_spiLen = SPI_BUFFER_SIZE;

           HAL_SPI_TransmitReceive_DMA(&hspiflash,g_spiTxBuf,g_spiRxBuf,g_spiLen);

           memcpy(pRxData, g_spiRxBuf, SPI_BUFFER_SIZE);
           pRxData += SPI_BUFFER_SIZE;
       }

   rem = Size % SPI_BUFFER_SIZE;    /* 剩余字节 */
   if (rem > 0)
      {
          g_spiLen = rem;

          HAL_SPI_TransmitReceive_DMA(&hspiflash,g_spiTxBuf,g_spiRxBuf,g_spiLen);

          memcpy(pRxData, g_spiRxBuf, rem);
      }





   return retval;
}

基本读写函数写好了,就只需要去改flash的块读写操作函数即可,中间有一个很容易忽略的问题在后面提出

//读取SPI FLASH  
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u32 NumByteToRead)   
{ 
 	u16 i;   										    
	W25QXX_CS=0;                            //使能器件   
    SPI6_ReadWriteByte(W25X_ReadData);      //发送读取命令
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI6_ReadWriteByte((u8)((ReadAddr)>>24));
    }
    SPI6_ReadWriteByte((u8)((ReadAddr)>>16));   //发送24bit地址
    SPI6_ReadWriteByte((u8)((ReadAddr)>>8));
    SPI6_ReadWriteByte((u8)ReadAddr);
#ifdef SPI_USE_DMA
     W25Q_SpiReadDMA(pBuffer,NumByteToRead);
     W25Q_SpiCsDisable();
     W25Q_SpiDmaStop(); //等待写入完成
#else
    for(i=0;i<NumByteToRead;i++)
	{ 
        pBuffer[i]=SPI6_ReadWriteByte(0XFF);    //循环读数
    }
   W25Q_SpiCsDisable();
#endif

}  
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!	 
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
 	u16 i;  
 	u8* rxBuffer;
    W25QXX_Write_Enable();                  //SET WEL 
	W25Q_SpiCsEnable();                            //使能器件
    SPI6_ReadWriteByte(W25X_PageProgram);   //发送写页命令
    if(W25QXX_TYPE==W25Q256)                //如果是W25Q256的话地址为4字节的,要发送最高8位
    {
        SPI6_ReadWriteByte((u8)((WriteAddr)>>24));
    }
    SPI6_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址
    SPI6_ReadWriteByte((u8)((WriteAddr)>>8));
    SPI6_ReadWriteByte((u8)WriteAddr);
#ifdef SPI_USE_DMA
    W25Q_SpiWriteDMA(pBuffer,NumByteToWrite);
    W25Q_SpiDmaStop();
    W25Q_SpiCsDisable();                    //取消片选
#else
    for(i=0;i<NumByteToWrite;i++)SPI6_ReadWriteByte(pBuffer[i]);//循环写数
    W25Q_SpiCsDisable();                    //取消片选
#endif

	W25QXX_Wait_Busy();					   //等待写入结束
} 

上面通过宏定义区分是否使用DMA,其中需要注意的就是W25Q64
的片选信号在DMA方式时不能马上取消片选;因为SPI6_WriteDMA函数实际只是打开了DMA,数据并没有发送完成。
当然在这里等待写入完成也是个坑,暂时没想到怎么填,如果SPI上只挂载了flash,就不用等了,可以去干其他的事。所以只能在回调函数中去取消片选。
W25QXX_Wait_Busy(); //等待写入结束
STM32 SPI-DMA读写 SPI Flash (只需改动两个函数) +cube配置 多器件_第3张图片

添加回调函数

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
   if(hspi== &hspi6)
   {
       W25QXX_CS=1;
   }

}

void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{

    if(hspi== &hspi6)
      {
          W25QXX_CS=1;
      }

}

到这里;DMA方式读写flash才算完成配置了。
总结如下:
1. 配置SPI 及对应Tx RX DMA;接收DMA要分包读取,一次性读取大量数据DMA缓存不够
2.改写flash块读写函数;
3.添加回调函数,取消片选; ->3.直接等待DMA传输完成就取消片选
速度测试:
写100K数据用时2s-3s,因为100K数据需要擦除32个扇区,
W25QXX_Erase_Sector扇区擦除命令就占了90ms,所以spi-flash最好不要实时存取数据,在开关机阶段操作存取保存的数据比较好

你可能感兴趣的:(#,stm32cube,嵌入式)