求一个flash存储和读写的方案

2019-10-15 02:17发布

我已经调通了flash。
现在我的需求是这样:
我每隔1分钟存储一次信息,包括:时间,应力,信号幅值。。。。。大概100字节。
这样存最多可以存半个月的信息吧。

会有上位机软件不定期向我获取flash里的信息。它的指令里会给我两个时间参数,我要把这段时间内的历史数据发给上位机。

我的问题是这样:
1,如何把指令里的时间段和我的FLASH存储地址对应起来
2,考虑到停电的情况,flash里存储的信息并不是连续的。也就是说我要知道上位机想要获取的时间段内我自身有没有掉电。
3,半个月后我会用新的信息把最老的信息覆盖掉,这就导致flash最早的有效信息不一定在哪个地址上。


因为以前没做过这样的事,感觉有点思路但还是很乱。
请有经验的前辈指点一下。

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
13条回答
密耳
1楼-- · 2019-10-15 07:01
你的FLASH是指STM32内部FLASH还是外接的?STM32的FLASH写次数咱按10W次来算(10W次是比较高了),能写多少天??写RAM吧,数据量由太大,,我觉得你们的规划还是有欠思考的地方。一般规定固定写多少条记录。记录满后往前覆盖(左移右移的过程),正常暂存在RAM里,加入掉电识别电路,断电时把记录写入FLASH..
XUZJWWSZ
2楼-- · 2019-10-15 07:56
密耳 发表于 2016-11-17 09:24
你的FLASH是指STM32内部FLASH还是外接的?STM32的FLASH写次数咱按10W次来算(10W次是比较高了),能写多少 ...

是外部FLASH。
多谢你的建议,确实硬件设计没经验,根本不知掉电识别电路。
不过掉电只是意外情况,一般是不会掉电的。但是我写程序的时候却不得不考虑这种情况。
我现在是想在现有基础上先把程序写出来。以后PCB再改版。
XUZJWWSZ
3楼-- · 2019-10-15 07:58
我搞错了,3楼的密耳才是最佳答案,我手滑把自己的回复设为最佳了。
super910906
4楼-- · 2019-10-15 10:31
我现在在调W25Q128,刚刚拿到板子,求分享代码。215801622@qq.com
1005934646
5楼-- · 2019-10-15 16:01
 精彩回答 2  元偷偷看……
XUZJWWSZ
6楼-- · 2019-10-15 20:48
因为我用的芯片型号不同,所以对文件做了微小的修改,下面附上开发板源文件:flash.c
/****************************************Copyright (c)**************************************************                        

**------------------------------------------------------------------------------------------------------
** Modified by:                       
** Modified date:       
** Version:
** Descriptions:               
********************************************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include "SST25VF016B.h"

/* 移植本软件包时需要修改以下的函数或宏 */

/*******************************************************************************
* Function Name  : SPI_FLASH_Init
* Description    : 初始化控制SSI的管脚
* Input          : None
* Output         : None
* Return         : None
* Attention                 : None
*******************************************************************************/
void SPI_FLASH_Init(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  //RCC_APB1PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_SPI_FLASH_CS, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
                                                                  
  GPIO_InitStructure.GPIO_Pin = SPI_FALSH_CS_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(SPI_FALSH_CS_PORT, &GPIO_InitStructure);

  SPI_FLASH_CS_HIGH();
  /* SPI2 Config -------------------------------------------------------------*/                                                                   
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_CRCPolynomial = 7;
  SPI_Init(SPI1, &SPI_InitStructure);
  /* Enable SPI2 */
  SPI_Cmd(SPI1, ENABLE);
}


/*******************************************************************************
* Function Name  : Flash_ReadWriteByte
* Description    : 通过硬件SPI发送一个字节到SST25VF016B
* Input          : - data: 发送的数据
* Output         : None
* Return         : SST25VF016B 返回的数据
* Attention                 : None
*******************************************************************************/
uint8_t Flash_ReadWriteByte(uint8_t data)               
{
  /* Loop while DR register in not emplty */
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

  /* Send byte through the SPI2 peripheral */
  SPI_I2S_SendData(SPI1, data);
   
  /* Wait to receive a byte */
  while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

  /* Return the byte read from the SPI bus */
  return SPI_I2S_ReceiveData(SPI1);                                             
}


/* 以下函数在移植时无需修改 */
/*******************************************************************************
* Function Name  : SSTF016B_RD
* Description    : SST25VF016B的读函数,可选择读ID和读数据操作
* Input          : - Dst: 目标地址,范围 0x0 - MAX_ADDR(MAX_ADDR = 0x1FFFFF)
*                  - RcvBufPt: 接收缓存的指针
*                  - NByte: 要读取的数据字节数       
* Output         : 操作成功则返回OK,失败则返回ERROR
* Return         : SST25VF016B 返回的数据
* Attention                 : 若某功能下,某一入口参数无效,可在该入口参数处填Invalid,该参数将被忽略
*******************************************************************************/
uint8_t SSTF016B_RD(uint32_t Dst, uint8_t* RcvBufPt ,uint32_t NByte)
{
        uint32_t i = 0;
        if ((Dst+NByte > MAX_ADDR)||(NByte == 0))        return (ERROR);         /*        检查入口参数 */
       
    SPI_FLASH_CS_LOW();
        Flash_ReadWriteByte(0x0B);                                                 /* 发送读命令 */
        Flash_ReadWriteByte(((Dst & 0xFFFFFF) >> 16));        /* 发送地址信息:该地址由3个字节组成        */
        Flash_ReadWriteByte(((Dst & 0xFFFF) >> 8));
        Flash_ReadWriteByte(Dst & 0xFF);
        Flash_ReadWriteByte(0xFF);                                                /* 发送一个哑字节以读取数据        */
        for (i = 0; i < NByte; i++)               
        {
       RcvBufPt[i] = Flash_ReadWriteByte(0xFF);               
        }
    SPI_FLASH_CS_HIGH();
        return (ENABLE);
}

/*******************************************************************************
* Function Name  : SSTF016B_RdID
* Description    : SST25VF016B的读ID函数,可选择读ID和读数据操作
* Input          : - IDType: ID类型。用户可在Jedec_ID,Dev_ID,Manu_ID三者里选择
* Output         : - RcvbufPt: 存储ID变量的指针
* Return         : 操作成功则返回OK,失败则返回ERROR
* Attention                 : 若填入的参数不符合要求,则返回ERROR
*******************************************************************************/
uint8_t SSTF016B_RdID(idtype IDType,uint32_t* RcvbufPt)
{
        uint32_t temp = 0;
              
        if (IDType == Jedec_ID)
        {
                SPI_FLASH_CS_LOW();       
                               
                Flash_ReadWriteByte(0x9F);                                          /* 发送读JEDEC ID命令(9Fh)        */
                    
        temp = (temp | Flash_ReadWriteByte(0x00)) << 8;  /* 接收数据 */
                temp = (temp | Flash_ReadWriteByte(0x00)) << 8;       
        temp = (temp | Flash_ReadWriteByte(0x00));              /* 在本例中,temp的值应为0xBF2541 */

        SPI_FLASH_CS_HIGH();

                *RcvbufPt = temp;
                return (ENABLE);
        }
       
        if ((IDType == Manu_ID) || (IDType == Dev_ID) )
        {
            SPI_FLASH_CS_LOW();       
               
                Flash_ReadWriteByte(0x90);                                /* 发送读ID命令 (90h or ABh) */
            Flash_ReadWriteByte(0x00);                                /* 发送地址        */
                Flash_ReadWriteByte(0x00);                                /* 发送地址        */
                Flash_ReadWriteByte(IDType);                    /* 发送地址 - 不是00H就是01H */
                temp = Flash_ReadWriteByte(0x00);            /* 接收获取的数据字节 */

        SPI_FLASH_CS_HIGH();

                *RcvbufPt = temp;
                return (ENABLE);
        }
        else
        {
                return (ERROR);       
        }
}


/*******************************************************************************
* Function Name  : SSTF016B_WR
* Description    : SST25VF016B的写函数,可写1个和多个数据到指定地址
* Input          : - Dst: 目标地址,范围 0x0 - MAX_ADDR(MAX_ADDR = 0x1FFFFF)
*                  - SndbufPt: 发送缓存区指针
*                  - NByte: 要写的数据字节数
* Output         : None
* Return         : 操作成功则返回OK,失败则返回ERROR
* Attention                 : 若某功能下,某一入口参数无效,可在该入口参数处填Invalid,该参数将被忽略
*******************************************************************************/
uint8_t SSTF016B_WR(uint32_t Dst, uint8_t* SndbufPt,uint32_t NByte)
{
        uint8_t temp = 0,StatRgVal = 0;
        uint32_t i = 0;
        if (( (Dst+NByte-1 > MAX_ADDR)||(NByte == 0) ))
        {
                return (ERROR);         /*        检查入口参数 */
        }

       
        SPI_FLASH_CS_LOW();                                 
        Flash_ReadWriteByte(0x05);                                                            /* 发送读状态寄存器命令        */
        temp = Flash_ReadWriteByte(0xFF);                                            /* 保存读得的状态寄存器值 */
        SPI_FLASH_CS_HIGH();                                                               

        SPI_FLASH_CS_LOW();                               
        Flash_ReadWriteByte(0x50);                                                            /* 使状态寄存器可写        */
        SPI_FLASH_CS_HIGH();       
               
        SPI_FLASH_CS_LOW();                               
        Flash_ReadWriteByte(0x01);                                                            /* 发送写状态寄存器指令 */
        Flash_ReadWriteByte(0);                                                                    /* 清0BPx位,使Flash芯片全区可写 */
        SPI_FLASH_CS_HIGH();                       
       
        for(i = 0; i < NByte; i++)
        {
                SPI_FLASH_CS_LOW();                               
                Flash_ReadWriteByte(0x06);                                                    /* 发送写使能命令 */
                SPI_FLASH_CS_HIGH();                       

                SPI_FLASH_CS_LOW();                                       
                Flash_ReadWriteByte(0x02);                                                     /* 发送字节数据烧写命令        */
                Flash_ReadWriteByte((((Dst+i) & 0xFFFFFF) >> 16));  /* 发送3个字节的地址信息 */
                Flash_ReadWriteByte((((Dst+i) & 0xFFFF) >> 8));
                Flash_ReadWriteByte((Dst+i) & 0xFF);
                Flash_ReadWriteByte(SndbufPt[i]);                                        /* 发送被烧写的数据        */
                SPI_FLASH_CS_HIGH();                       

                do
                {
                          SPI_FLASH_CS_LOW();                                         
                        Flash_ReadWriteByte(0x05);                                            /* 发送读状态寄存器命令 */
                        StatRgVal = Flash_ReadWriteByte(0xFF);                        /* 保存读得的状态寄存器值 */
                        SPI_FLASH_CS_HIGH();                                                       
                  }
                while (StatRgVal == 0x03 );                                                  /* 一直等待,直到芯片空闲        */

        }

        SPI_FLASH_CS_LOW();                                       
        Flash_ReadWriteByte(0x06);                                                            /* 发送写使能命令 */
        SPI_FLASH_CS_HIGH();                       

        SPI_FLASH_CS_LOW();                                       
        Flash_ReadWriteByte(0x50);                                                            /* 使状态寄存器可写        */
        SPI_FLASH_CS_HIGH();
                       
        SPI_FLASH_CS_LOW();                               
        Flash_ReadWriteByte(0x01);                                                            /* 发送写状态寄存器指令 */
        Flash_ReadWriteByte(temp);                                                             /* 恢复状态寄存器设置信息 */
        SPI_FLASH_CS_HIGH();

        return (ENABLE);               
}


/*******************************************************************************
* Function Name  : SSTF016B_Erase
* Description    : 根据指定的扇区号选取最高效的算法擦除
* Input          : - sec1: 起始扇区号,范围(0~511)
*                  - sec2: 终止扇区号,范围(0~511)
* Output         : None
* Return         : 操作成功则返回OK,失败则返回ERROR
* Attention                 : None
*******************************************************************************/
uint8_t SSTF016B_Erase(uint32_t sec1, uint32_t sec2)
{
        uint8_t  temp1 = 0,temp2 = 0,StatRgVal = 0;
    uint32_t SecnHdAddr = 0;                                 
        uint32_t no_SecsToEr = 0;                                                               /* 要擦除的扇区数目 */
        uint32_t CurSecToEr = 0;                                                              /* 当前要擦除的扇区号 */
       
        /*  检查入口参数 */
        if ((sec1 > SEC_MAX)||(sec2 > SEC_MAX))       
        {
                return (ERROR);       
        }
          
           SPI_FLASH_CS_LOW();                         
        Flash_ReadWriteByte(0x05);                                                                /* 发送读状态寄存器命令        */
        temp1 = Flash_ReadWriteByte(0x00);                                                /* 保存读得的状态寄存器值 */
        SPI_FLASH_CS_HIGH();                                                               

        SPI_FLASH_CS_LOW();                       
        Flash_ReadWriteByte(0x50);                                                                /* 使状态寄存器可写        */
        SPI_FLASH_CS_HIGH();                       

        SPI_FLASH_CS_LOW();                                                                         
        Flash_ReadWriteByte(0x01);                                                                /* 发送写状态寄存器指令        */
        Flash_ReadWriteByte(0);                                                                        /* 清0BPx位,使Flash芯片全区可写 */
        SPI_FLASH_CS_HIGH();
       
        SPI_FLASH_CS_LOW();                       
        Flash_ReadWriteByte(0x06);                                                                /* 发送写使能命令 */
        SPI_FLASH_CS_HIGH();                       

        /* 如果用户输入的起始扇区号大于终止扇区号,则在内部作出调整 */
        if (sec1 > sec2)
        {
           temp2 = sec1;
           sec1  = sec2;
           sec2  = temp2;
        }
        /* 若起止扇区号相等则擦除单个扇区 */
        if (sec1 == sec2)       
        {
                SPI_FLASH_CS_LOW();                               
                Flash_ReadWriteByte(0x06);                                                    /* 发送写使能命令 */
                SPI_FLASH_CS_HIGH();                       

            SecnHdAddr = SEC_SIZE * sec1;                                          /* 计算扇区的起始地址        */
            SPI_FLASH_CS_LOW();       
            Flash_ReadWriteByte(0x20);                                                          /* 发送扇区擦除指令 */
            Flash_ReadWriteByte(((SecnHdAddr & 0xFFFFFF) >> 16)); /* 发送3个字节的地址信息 */
                   Flash_ReadWriteByte(((SecnHdAddr & 0xFFFF) >> 8));
                   Flash_ReadWriteByte(SecnHdAddr & 0xFF);
                  SPI_FLASH_CS_HIGH();                       
                do
                {
                          SPI_FLASH_CS_LOW();                         
                        Flash_ReadWriteByte(0x05);                                                  /* 发送读状态寄存器命令 */
                        StatRgVal = Flash_ReadWriteByte(0x00);                          /* 保存读得的状态寄存器值        */
                        SPI_FLASH_CS_HIGH();                                                               
                  }
                while (StatRgVal == 0x03);                                              /* 一直等待,直到芯片空闲        */
                return (ENABLE);                       
        }
       
    /* 根据起始扇区和终止扇区间距调用最快速的擦除功能 */       
       
        if (sec2 - sec1 == SEC_MAX)       
        {
                SPI_FLASH_CS_LOW();                       
                Flash_ReadWriteByte(0x60);                                                          /* 发送芯片擦除指令(60h or C7h) */
                SPI_FLASH_CS_HIGH();                       
                do
                {
                          SPI_FLASH_CS_LOW();                         
                        Flash_ReadWriteByte(0x05);                                                  /* 发送读状态寄存器命令 */
                        StatRgVal = Flash_ReadWriteByte(0x00);                          /* 保存读得的状态寄存器值        */
                        SPI_FLASH_CS_HIGH();                                                               
                  }
                while (StatRgVal == 0x03);                                                  /* 一直等待,直到芯片空闲        */
                   return (ENABLE);
        }
       
        no_SecsToEr = sec2 - sec1 +1;                                                  /* 获取要擦除的扇区数目 */
        CurSecToEr  = sec1;                                                                          /* 从起始扇区开始擦除        */
       
        /* 若两个扇区之间的间隔够大,则采取8扇区擦除算法 */
        while (no_SecsToEr >= 8)
        {
                SPI_FLASH_CS_LOW();                               
                Flash_ReadWriteByte(0x06);                                                    /* 发送写使能命令 */
                SPI_FLASH_CS_HIGH();                       

            SecnHdAddr = SEC_SIZE * CurSecToEr;                                  /* 计算扇区的起始地址 */
            SPI_FLASH_CS_LOW();       
            Flash_ReadWriteByte(0x52);                                                          /* 发送32KB擦除指令 */
            Flash_ReadWriteByte(((SecnHdAddr & 0xFFFFFF) >> 16)); /* 发送3个字节的地址信息 */
                   Flash_ReadWriteByte(((SecnHdAddr & 0xFFFF) >> 8));
                   Flash_ReadWriteByte(SecnHdAddr & 0xFF);
                  SPI_FLASH_CS_HIGH();                       
                do
                {
                          SPI_FLASH_CS_LOW();                         
                        Flash_ReadWriteByte(0x05);                                                  /* 发送读状态寄存器命令 */
                        StatRgVal = Flash_ReadWriteByte(0x00);                          /* 保存读得的状态寄存器值        */
                        SPI_FLASH_CS_HIGH();                                                               
                  }
                while (StatRgVal == 0x03);                                                  /* 一直等待,直到芯片空闲        */
                CurSecToEr  += 8;
                no_SecsToEr -=  8;
        }
        /* 采用扇区擦除算法擦除剩余的扇区 */
        while (no_SecsToEr >= 1)
        {
                SPI_FLASH_CS_LOW();                               
                Flash_ReadWriteByte(0x06);                                                    /* 发送写使能命令 */
                SPI_FLASH_CS_HIGH();                       

            SecnHdAddr = SEC_SIZE * CurSecToEr;                                  /* 计算扇区的起始地址 */
            SPI_FLASH_CS_LOW();       
            Flash_ReadWriteByte(0x20);                                                          /* 发送扇区擦除指令 */
            Flash_ReadWriteByte(((SecnHdAddr & 0xFFFFFF) >> 16)); /* 发送3个字节的地址信息 */
                   Flash_ReadWriteByte(((SecnHdAddr & 0xFFFF) >> 8));
                   Flash_ReadWriteByte(SecnHdAddr & 0xFF);
                  SPI_FLASH_CS_HIGH();                       
                do
                {
                          SPI_FLASH_CS_LOW();                         
                        Flash_ReadWriteByte(0x05);                                                  /* 发送读状态寄存器命令 */
                        StatRgVal = Flash_ReadWriteByte(0x00);                          /* 保存读得的状态寄存器值        */
                        SPI_FLASH_CS_HIGH();                                                               
                  }
                while (StatRgVal == 0x03);                                                  /* 一直等待,直到芯片空闲 */
                CurSecToEr  += 1;
                no_SecsToEr -=  1;
        }
    /* 擦除结束,恢复状态寄存器信息 */
        SPI_FLASH_CS_LOW();                       
        Flash_ReadWriteByte(0x06);                                                                  /* 发送写使能命令 */
        SPI_FLASH_CS_HIGH();                       

        SPI_FLASH_CS_LOW();                       
        Flash_ReadWriteByte(0x50);                                                                  /* 使状态寄存器可写 */
        SPI_FLASH_CS_HIGH();                       
        SPI_FLASH_CS_LOW();                       
        Flash_ReadWriteByte(0x01);                                                                  /* 发送写状态寄存器指令 */
        Flash_ReadWriteByte(temp1);                                                                  /* 恢复状态寄存器设置信息 */
        SPI_FLASH_CS_HIGH();   
        return (ENABLE);
}


/*******************************************************************************
* Function Name  : SPI_FLASH_Test
* Description    : 读取SST25VF016B ID
* Input          : None
* Output         : None
* Return         : None
* Attention                 : None
0x00BF258E 查看ChipID的值是否0xBF258E
*******************************************************************************/
void SPI_FLASH_Test(void)
{
    uint32_t  ChipID = 0;
          SSTF016B_RdID(Jedec_ID, &ChipID);                                   /*  单步运行到此处时, 在Watch里*/

        ChipID &= ~0xff000000;                                                                        /*  仅保留低24位数据            */
        if (ChipID != 0x00BF2541)
         {                                                                                        /*  ID不正确进入死循环          */
           while(1);
    }
}

/*********************************************************************************************************
      END FILE
*********************************************************************************************************/

一周热门 更多>