STM32内部FLASH打包读写 STM32f030f4p6 内部flash 打包读写

STM32f030f4p6 内部flash 打包读写

 

最近做到的项目在运行需要把一组uint8_t(unsigned char)的数据进行掉电储存,想到单片机STM32f030f4p6内部flash可以直接由程序操作,写了以下代码用于uint8_t数据打包保存和读取。



1、程序清单 与 测试结果

本程序包含5个文件,分别是:

1、Flash.c:内部flash读取储存相关函数

2、Flash.h:flash头文件

3、USART1.c:STM32F030F4P6的串口驱动,串口仅用于打印数据观察。

4、USART1.h:串口头文件

5、main.c:防止程序主入口

1、Flash.c

  View Code

2、Flash.h

  View Code

3、USART1.c

  View Code

4、USART1.h

  View Code

5、main.c

  View Code




 测试结果:

第54-199省略..





2、程序详解

2.1、内存结构:

STM32F0xx的flash结构如下:最多具有64页,每页1KByte大小。

我使用的STM32F030F4P6 flash区域有16K,所以实际上只有0-15页,本程序中把需要保存的数据数据存放在最后一页(第15页)中。

2.2、定义数据包结构

为了保证储存和读出flash的数据是正确的,本程序将写入flash数据分为3个区域

  1. 报头区:写入数据的时候,将第15页的第1、2字节写入0xaa55,在读出的时候如果此位置不是0xaa55,则表示这段数据数据无效,不是由自己存入的数据或者程序出现了异常;
  2. 长度:写入数据的时候,第15页的第3、4字节写入有效数据的长度,读出这个字段,就知道上次自己一共存入了多少数据;
  3. 数据段:从第15页的第5字节开始,全部字节(1020byte)用于储存uint8_t类型的数据;

2.3、写操作

写操作有如下步骤:

  • 保护:由于我们只有1Kbyte空间,出去4个字节的报头和长度,实际只能存储1020个u8类型,写太多返回失败。
  • 解flash锁
  • 页擦除
  • 写入数据包
  • 上锁
复制代码
/*******************************************************************************
* Function Name  : Flash_eeprom_WriteWithPacked
* Description    : Write a group of datas to flash.
* Input          : buff:pointer of first data, length: write length
* Output         : 
* Return         : true/false
*******************************************************************************/
bool writeMessageToFlash( uint8_t *buff , uint16_t length)
{
    uint16_t temp;
    int i;
    
    /*Protection*/
    if( (length+4) > STM32F0xx_PAGE_SIZE )
    {
        return false;
    }
    
    FLASH_Unlock(  );    

    /*Clear all flags*/
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR );
    
    /*Erase first . Do not rember.*/
    if(FLASH_COMPLETE != FLASH_ErasePage(STM32F0xx_FLASH_PAGE15_STARTADDR))//°üº¬Á˵ȴýbusy
    {
        return false;
    }
    
    /*Write head*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR, EEPPROM_PACKAGEHEAD );
    /*Write length*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+2 , length );
    
    
    /*Write datas*/
    for(i=0 ;i<length/2 ;i++)
    {
        temp = buff[2*i]|(uint16_t)buff[2*i+1]<<8;
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+2*i , temp);
    }  
    if( isItOddNumber(length) )//Write one more if length is odd number.
    {        
        temp = (uint16_t)buff[length-1];
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+(length-1) , temp);
    }

    
    /*Read out and check*/
    for(i=0 ;i<length ;i++)
    {
        if( *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i) != buff[i] )
        {
            FLASH_Lock();
            return false;
        }
    }    
    
    FLASH_Lock();
    return true;    
}
复制代码

2.3.1、解锁操作:

用户手册描述如下:

在(芯片)重置过后,为了防止多余的擦写操作flash会被保护。除了OBL_LAUNCH位用于重载option bit,FLASH_CR寄存器的其他部分都不可以访问。

需要把以下两个解锁序列写入FLASH_KEY寄存器,才能访问FLASH_CR:

  • 0x45670123
  • 0xCDEF89AB

检索官方库定位FLASH_Unlock()这个函数,它的底层实现与文档描述相符,另外增加了防止重复解锁的操作,直接使用FLASH_Unlock()这个函数即可以完成解锁。

2.3.2、页擦除

用户手册描述如下:

1、通过检查FLASH_SR寄存器的BSY位来确认flash没有使用

2、把FLASH_CR寄存器的PER位置SET

3、通过编写FLASH_AR寄存器来确认要擦除的页

4、将FLASH_CR寄存器的STRT位置SET

5、等待BSY位rst

6、检查FLASH_SR寄存器中的EOP标记(成功会置SET)(流程图没有、库函数没检查)

7、清除EOP标记(流程图没有、库函数没清除)

检索官方库定位到FLASH_ErasePage(uint32_t Page_Address)这个函数,它的底层实现与文档流程图描述相符,值得注意的是,手册描述中6、7步并没有出现在手册的流程图和库函数中(库版本V1.2.0)。但在使用中没有出现问题,这里先记录看以后使用中会不会出现问题

 

2.3.3、往flash中写入数据

用户手册描述如下

  1、通过检查FLASH_SR寄存器的BSY位来确认flash没有使用

  2、设置FLASH_CR寄存器的PG位

  3、在目标地址上写入半个word的数据

  4、等BSY位reset

  5、检查FLASH_SR寄存器的EOP标记(SET表示成功)(流程图没有出现、库函数中没检查)

 检索官方库定位到FLASH_ErasePage(uint32_t Page_Address)这个函数,它的底层实现与文档流程图描述相符,同样地,库函数没有检查第五步的EOP标记

 

2.3.4、上锁操作

根据手册提示,定位到库函数void FLASH_Lock(void)。只要置位LOCK位就上锁了。

 







2.4、读操作  

程序如下

复制代码
/*******************************************************************************
* Function Name  : readPackedMessageFromFlash
* Description    : Read packed message form flash
* Input          : buff:point to first location of received buffer.length:Maxmum length of reception
* Output         : 
* Return         : reception length
*******************************************************************************/
uint16_t readPackedMessageFromFlash( uint8_t *buff , uint16_t length)
{
    int i;
    uint16_t getLength;
    
    if( !doseFlashHasPackedMessage() )
        return 0;
    
    /*Get valuable length*/
    getLength = getValuablePackedMessageLengthofFlash();
    
    /*Read out message*/
    for(i=0;i<MIN(getLength,length);i++)
    {
        buff[i]= *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i);
    }     
    
    return MIN(getLength,length);
}
复制代码

根据用户手册提示,读操作是可以直接取址的,所以读操作实际只有如下一句只要用uint8_t类型取出目标地址再取值就可以了。

但是,我们的数据是打包的(详见2.2),所以还需要:

1、根据报头判断是不是有效数据

2、根据长度判断要读取多少数据

3、最后才是读出数据

2.4.1、判断数据有效性:

我们通过报头来判断数据是不是自己写入的。

也就是判断flash第15页的第1、2个字节是不是0xaa55,如果不是,那这段数据是无效的。

另外再判断一下长度字段,如果长度等于0,也就是后面没有数据,那这段数据也是无效的。

复制代码
/*******************************************************************************
* Function Name  : doseFlashHasPackedMessage
* Description    : Does flash has packed messages   
* Input          : None
* Output         : 
* Return         : ture/false
*******************************************************************************/
bool doseFlashHasPackedMessage(void)
{
    uint16_t length;
    uint16_t getHead;    

    /*Is head matched*/ 
    getHead = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR ));      
    if( EEPPROM_PACKAGEHEAD != getHead )
    {
        return false;
    }
    
    /*Is length zero*/
    length = (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));
    if( 0 == length)
    {
        return false;
    }
    
    return true;
}
复制代码

 

2.4.2、根据长度读出数据:

在读Flash程序中,getLength是从flash中读出的长度,length是我们指定的最大读取长度。如果getLength大于我们指定读取长度,很可能会造成溢出,所以两者取小的一个防止溢出。





 
 

最近做到的项目在运行需要把一组uint8_t(unsigned char)的数据进行掉电储存,想到单片机STM32f030f4p6内部flash可以直接由程序操作,写了以下代码用于uint8_t数据打包保存和读取。



1、程序清单 与 测试结果

本程序包含5个文件,分别是:

1、Flash.c:内部flash读取储存相关函数

2、Flash.h:flash头文件

3、USART1.c:STM32F030F4P6的串口驱动,串口仅用于打印数据观察。

4、USART1.h:串口头文件

5、main.c:防止程序主入口

1、Flash.c

  View Code

2、Flash.h

  View Code

3、USART1.c

  View Code

4、USART1.h

  View Code

5、main.c

  View Code




 测试结果:

第54-199省略..





2、程序详解

2.1、内存结构:

STM32F0xx的flash结构如下:最多具有64页,每页1KByte大小。

我使用的STM32F030F4P6 flash区域有16K,所以实际上只有0-15页,本程序中把需要保存的数据数据存放在最后一页(第15页)中。

2.2、定义数据包结构

为了保证储存和读出flash的数据是正确的,本程序将写入flash数据分为3个区域

  1. 报头区:写入数据的时候,将第15页的第1、2字节写入0xaa55,在读出的时候如果此位置不是0xaa55,则表示这段数据数据无效,不是由自己存入的数据或者程序出现了异常;
  2. 长度:写入数据的时候,第15页的第3、4字节写入有效数据的长度,读出这个字段,就知道上次自己一共存入了多少数据;
  3. 数据段:从第15页的第5字节开始,全部字节(1020byte)用于储存uint8_t类型的数据;

2.3、写操作

写操作有如下步骤:

  • 保护:由于我们只有1Kbyte空间,出去4个字节的报头和长度,实际只能存储1020个u8类型,写太多返回失败。
  • 解flash锁
  • 页擦除
  • 写入数据包
  • 上锁
复制代码
/*******************************************************************************
* Function Name  : Flash_eeprom_WriteWithPacked
* Description    : Write a group of datas to flash.
* Input          : buff:pointer of first data, length: write length
* Output         : 
* Return         : true/false
*******************************************************************************/
bool writeMessageToFlash( uint8_t *buff , uint16_t length)
{
    uint16_t temp;
    int i;
    
    /*Protection*/
    if( (length+4) > STM32F0xx_PAGE_SIZE )
    {
        return false;
    }
    
    FLASH_Unlock(  );    

    /*Clear all flags*/
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR );
    
    /*Erase first . Do not rember.*/
    if(FLASH_COMPLETE != FLASH_ErasePage(STM32F0xx_FLASH_PAGE15_STARTADDR))//°üº¬Á˵ȴýbusy
    {
        return false;
    }
    
    /*Write head*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR, EEPPROM_PACKAGEHEAD );
    /*Write length*/
    FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+2 , length );
    
    
    /*Write datas*/
    for(i=0 ;i<length/2 ;i++)
    {
        temp = buff[2*i]|(uint16_t)buff[2*i+1]<<8;
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+2*i , temp);
    }  
    if( isItOddNumber(length) )//Write one more if length is odd number.
    {        
        temp = (uint16_t)buff[length-1];
        FLASH_ProgramHalfWord( STM32F0xx_FLASH_PAGE15_STARTADDR+4+(length-1) , temp);
    }

    
    /*Read out and check*/
    for(i=0 ;i<length ;i++)
    {
        if( *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i) != buff[i] )
        {
            FLASH_Lock();
            return false;
        }
    }    
    
    FLASH_Lock();
    return true;    
}
复制代码

2.3.1、解锁操作:

用户手册描述如下:

在(芯片)重置过后,为了防止多余的擦写操作flash会被保护。除了OBL_LAUNCH位用于重载option bit,FLASH_CR寄存器的其他部分都不可以访问。

需要把以下两个解锁序列写入FLASH_KEY寄存器,才能访问FLASH_CR:

  • 0x45670123
  • 0xCDEF89AB

检索官方库定位FLASH_Unlock()这个函数,它的底层实现与文档描述相符,另外增加了防止重复解锁的操作,直接使用FLASH_Unlock()这个函数即可以完成解锁。

2.3.2、页擦除

用户手册描述如下:

1、通过检查FLASH_SR寄存器的BSY位来确认flash没有使用

2、把FLASH_CR寄存器的PER位置SET

3、通过编写FLASH_AR寄存器来确认要擦除的页

4、将FLASH_CR寄存器的STRT位置SET

5、等待BSY位rst

6、检查FLASH_SR寄存器中的EOP标记(成功会置SET)(流程图没有、库函数没检查)

7、清除EOP标记(流程图没有、库函数没清除)

检索官方库定位到FLASH_ErasePage(uint32_t Page_Address)这个函数,它的底层实现与文档流程图描述相符,值得注意的是,手册描述中6、7步并没有出现在手册的流程图和库函数中(库版本V1.2.0)。但在使用中没有出现问题,这里先记录看以后使用中会不会出现问题

 

2.3.3、往flash中写入数据

用户手册描述如下

  1、通过检查FLASH_SR寄存器的BSY位来确认flash没有使用

  2、设置FLASH_CR寄存器的PG位

  3、在目标地址上写入半个word的数据

  4、等BSY位reset

  5、检查FLASH_SR寄存器的EOP标记(SET表示成功)(流程图没有出现、库函数中没检查)

 检索官方库定位到FLASH_ErasePage(uint32_t Page_Address)这个函数,它的底层实现与文档流程图描述相符,同样地,库函数没有检查第五步的EOP标记

 

2.3.4、上锁操作

根据手册提示,定位到库函数void FLASH_Lock(void)。只要置位LOCK位就上锁了。

 







2.4、读操作  

程序如下

复制代码
/*******************************************************************************
* Function Name  : readPackedMessageFromFlash
* Description    : Read packed message form flash
* Input          : buff:point to first location of received buffer.length:Maxmum length of reception
* Output         : 
* Return         : reception length
*******************************************************************************/
uint16_t readPackedMessageFromFlash( uint8_t *buff , uint16_t length)
{
    int i;
    uint16_t getLength;
    
    if( !doseFlashHasPackedMessage() )
        return 0;
    
    /*Get valuable length*/
    getLength = getValuablePackedMessageLengthofFlash();
    
    /*Read out message*/
    for(i=0;i<MIN(getLength,length);i++)
    {
        buff[i]= *(uint8_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+4+i);
    }     
    
    return MIN(getLength,length);
}
复制代码

根据用户手册提示,读操作是可以直接取址的,所以读操作实际只有如下一句只要用uint8_t类型取出目标地址再取值就可以了。

但是,我们的数据是打包的(详见2.2),所以还需要:

1、根据报头判断是不是有效数据

2、根据长度判断要读取多少数据

3、最后才是读出数据

2.4.1、判断数据有效性:

我们通过报头来判断数据是不是自己写入的。

也就是判断flash第15页的第1、2个字节是不是0xaa55,如果不是,那这段数据是无效的。

另外再判断一下长度字段,如果长度等于0,也就是后面没有数据,那这段数据也是无效的。

复制代码
/*******************************************************************************
* Function Name  : doseFlashHasPackedMessage
* Description    : Does flash has packed messages   
* Input          : None
* Output         : 
* Return         : ture/false
*******************************************************************************/
bool doseFlashHasPackedMessage(void)
{
    uint16_t length;
    uint16_t getHead;    

    /*Is head matched*/ 
    getHead = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR ));      
    if( EEPPROM_PACKAGEHEAD != getHead )
    {
        return false;
    }
    
    /*Is length zero*/
    length = (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));
    if( 0 == length)
    {
        return false;
    }
    
    return true;
}
复制代码

 

2.4.2、根据长度读出数据:

在读Flash程序中,getLength是从flash中读出的长度,length是我们指定的最大读取长度。如果getLength大于我们指定读取长度,很可能会造成溢出,所以两者取小的一个防止溢出。





猜你喜欢

转载自www.cnblogs.com/lj-hero/p/10659165.html