zoukankan      html  css  js  c++  java
  • 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

    #include "Flash.h"
    
    #include "USART1.h"
    /*******************************************************************************
    * 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;
    }
    /*******************************************************************************
    * Function Name  : getValuablePackedMessageLengthofFlash
    * Description    : Get valuable packed message length of flash 
    * Input          : None
    * Output         : 
    * Return         : valuable length
    *******************************************************************************/
    uint16_t getValuablePackedMessageLengthofFlash( void )
    {
        uint16_t length;
             
        /*Is head matched*/       
        if( EEPPROM_PACKAGEHEAD != (*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR )) )
        {
            return 0;
        }
        
        /*Get length*/
        length = (uint16_t)(*(uint16_t*)(STM32F0xx_FLASH_PAGE15_STARTADDR+2));   
        
        return length;
    }
    /*******************************************************************************
    * 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);
    }
    /*******************************************************************************
    * Function Name  : isItOddNumber
    * Description    : is input data an odd number?
    * Input          : number:input data
    * Output         : 
    * Return         : true/false
    *******************************************************************************/
    bool isItOddNumber(uint16_t number)
    {
        if(0 != number%2)
        {
            return true;
        }
        return false;
    }
    /*******************************************************************************
    * 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;    
    }
    
    
    /*******************************************************************************
    * Function Name  : flashReadWriteTest
    * Description    : Flash read write test.
    * Input          : None
    * Output         : None
    * Return         : None
    *******************************************************************************/
    void flashReadWriteTest( void ) 
    {
        #define testReadWriteNumber  200
        uint8_t buff_write[testReadWriteNumber]={0};
        uint8_t buff_read[testReadWriteNumber]={0};
        uint16_t length;
        int i;
        
        for( i=0;i<testReadWriteNumber;i++)
        {
            buff_write[i]=i;
        }
        
        writeMessageToFlash( buff_write , testReadWriteNumber);
        length = readPackedMessageFromFlash( buff_read , testReadWriteNumber);   
        printf("length=%d
    ",length);
        for(i=0;i<length;i++)
        {
            printf("read[%d]=%d
    ",i,buff_read[i]);
        }
        
        while(1);    
    }
    View Code

    2、Flash.h

    #ifndef __FLASH_H
    #define __FLASH_H
    
    #include "stm32f0xx.h"
    #include <stdbool.h>
    
    //Message head
    #define EEPPROM_PACKAGEHEAD 0xAA55//
    
    //Flash page head
    #define STM32F0xx_PAGE_SIZE 0x400
    #define STM32F0xx_FLASH_PAGE0_STARTADDR 0x8000000
    #define STM32F0xx_FLASH_PAGE1_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE2_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+2*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE3_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+3*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE4_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+4*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE5_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+5*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE6_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+6*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE7_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+7*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE8_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+8*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE9_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+9*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE10_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+10*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE11_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+11*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE12_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+12*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE13_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+13*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE14_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+14*STM32F0xx_PAGE_SIZE)
    #define STM32F0xx_FLASH_PAGE15_STARTADDR (STM32F0xx_FLASH_PAGE0_STARTADDR+15*STM32F0xx_PAGE_SIZE)
    
    
    #define MIN(A,B) (A<B?A:B)
    
    
    void flashReadWriteTest( void ) ;
    
    #endif
    View Code

    3、USART1.c

    #include "USART1.h"
    
    
    void USART1_Init(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
    
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_1);
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);
    
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    
        USART_InitStructure.USART_BaudRate = 115200;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode =  USART_Mode_Tx;
    
        USART_Init(USART1, &USART_InitStructure);
        USART_Cmd(USART1, ENABLE);
    
    
    
    }
    
    
    void USART1_IRQHandler(void)
    {
    
    
    }
    
    
    /**
      * @brief  Retargets the C library printf function to the USART.
      * @param  None
      * @retval None
      */
    PUTCHAR_PROTOTYPE
    {
        /* Place your implementation of fputc here */
        /* e.g. write a character to the USART */
        USART_SendData(USART1, (uint8_t) ch);
    
        /* Loop until the end of transmission */
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {}
    
        return ch;
    }
    View Code

    4、USART1.h

    #ifndef __BSP_USART1_H
    #define __BSP_USART1_H
    
    #include "stm32f0xx.h"
    #include <stdio.h>
    
    #ifdef __GNUC__
    /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
       set to 'Yes') calls __io_putchar() */
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif /* __GNUC__ */
    
    void USART1_Init(void);
    
    #endif
    View Code

    5、main.c

    #include "stm32f0xx.h"
    #include "USART1.h"
    #include "Flash.h"
    int main(void)
    {
        USART1_Init();    
        flashReadWriteTest();
        while(1)
        {
        }
    }
    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大于我们指定读取长度,很可能会造成溢出,所以两者取小的一个防止溢出。





    到此为止程序写完了,要是有什么错误或不足的地方还望各位大佬指出,要是使用中出现什么BUG也希望大家回来反馈一下,十分感谢

     

  • 相关阅读:
    Leetcode练习(Python):链表类:第206题:反转链表:反转一个单链表。
    Leetcode练习(Python):链表类:第203题:移除链表元素:删除链表中等于给定值 val 的所有节点。
    Leetcode练习(Python):链表类:第160题:相交链表:编写一个程序,找到两个单链表相交的起始节点。
    Leetcode练习(Python):链表类:第141题:环形链表:给定一个链表,判断链表中是否有环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
    Leetcode练习(Python):链表类:第83题:删除排序链表中的重复元素:给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
    【Java基础总结】数据库编程
    【Java基础总结】多线程
    特迷茫的大三时期
    解决忘记了开机密码,无法进入系统的方法
    一开机未通过输入密码登录,就出现用户名或密码错误??
  • 原文地址:https://www.cnblogs.com/HongYi-Liang/p/9047255.html
Copyright © 2011-2022 走看看