STM32F103 FLASH数据存储实验

实验目的

学习STM32的IAP(在应用编程功能),可以把没有用到的片上的FLASH用作数据存储。数据手册请参看第2章/

实验简介

不同型号的STM32,其FLASH容量也有所不同,最小的只有16k字节,最大的则达到1024k字节,星光的枭龙STM32开发板选择的是STM32F103VET6的FLASH容量为512K字节,属于大容量产品(另外还有中容量和小容量产品),其闪存结构图如下:

STM32F103 FLASH数据存储实验_第1张图片
编程和擦除闪存
闪存编程一次可以写入16位(半字)。
闪存擦除操作可以按页面擦除或完全擦除(全擦除)。全擦除不影响信息块
为了确保不发生过度编程,闪存编程和擦除控制器块是由一个固定的时钟控制的。
写操作(编程或擦除)结束时可以触发中断。仅当闪存控制器接口时钟开启时,此中断可以用来从WFI(等待中断)模式退出。

闪存读取
闪存的指令和数据访问是通过AHB总线完成的。预取模块是用于通过ICode(访问指令)总线读取指令的。仲裁是作用在闪存接口,并且DCode(访问数据)总线上的数据访问优先。

读访问可以有以下配置选项:
等待时间:可以随时更改的用于读取操作的等待状态的数量。

预取缓冲区(2个64位):在每一次复位以后被自动打开,由于每个缓冲区的额大小(64位)与闪存的带宽相同,因此只通过需一次读闪存的操作即可更新整个缓冲区的内容。由于预取缓冲区的存在,CPU可以工作在更高的主频CPU
每次取指最多为32位的字,取一条指令时,下一条指令已经在缓冲区中等待。

半周期:用于功耗优化

注意:
1.这些选项应与闪存存储器的访问时间一起使用。等待周期体现了系统时钟(SYSCLK)频率与闪存访问时间的关系:

0等待周期,当 0 < SYSCLK < 24MHZ
1等待周期,当24MHz < SYSCLK < = 48MHz
2等待周期,当48MHz < SYSCLK <= 72MHz

2.半周期配置不能与使用了预分频的AHB(高级高性能总线)一起使用,时钟系统应该等于HCLK时钟。该特性只能用在时钟频率为8MHz或低于8MHz时,可以直接使用的内部RC振荡器(HSI),或者是主振荡器(HSE),但不能用PLL(锁相环倍频输出)

3.当AHB预分频系数不为1时,必须预取缓冲区处于开启状态。

  1. 只有在系统时钟(SYSCLK)小于24MHz并且没有打开AHB的预分频器(即HCLK必须等于SYSHCLK)时,才能执行预取缓冲器的打开和关闭操作,一般而言,在初始化过程中执行预取缓冲器的打开和关闭操作,这时微控制器的时钟由8MHz的内部RC振荡器(HSI)提供。

5.使用DMA:DMA在DCode总线上访问闪存存储器,它的优先级比ICode上的取指高。DMA在每次传送完成后具有一个空余的周期。有些指令可以和DMA传输一起执行。

具体寄存器内容,请参看数据手册第2章节的内容。

代码

main.c
下载后打开串口调试助手,波特率为115200,程序功能是向STM32F103VET6的FLASH页200,即地址0x08064000开始处写入250字节数据,然后再读出来,比较读出和写入是否一致,正确串口打印“STM32 FLASH ReadWrite OK”,错误串口打印"STM32 FLASH ReadWrite ERROR"

#include "MyIncludes.h"

#define FLASH_PAGE 200
//页号 200页
#define PAGE_NUM 1
//擦除页数 1
#define PAGE_ADDR (STM32_FLASH_BASE + FLASH_PAGE * STM32_PAGE_SIZE)
//编程地址   0x08000000(FLASH的起始地址)+  200 * 2048
//16进位制转换为0x08064000;
u16 sys_cnt = 0;
void systick_isr(void)
{
  if(sys_cnt <100 )
  sys_cnt++;
  else
  {
    sys_cnt = 0;
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
  }
}

u8 res;
u8 wr_buff[250];
//写数组
u8 rd_buff[250];
//读数组
int main(void)
{
 u8 i;
 System_Init();
 LED_Init();
 SysTick_Init(systick_isr);
 USART1_Init(115200,NULL,NULL);
 
 for( i = 0; i<250 ; i++)
 wr_buff[i] = i+1;
 //写数组 1-250
 
 res = StmFlash_Erase(FLASH_PAGE,PAGE_NUM);
 //STM32F1的FLASH页擦除操作  200页   要擦除1页

 res = StmFlash_Program(PAGE_ADDR,(u16*)wr_buff,250);
 //STM32F1的FLASH页编程,每页2K字节   编程地址  编程字节
 
 StmFalsh_Read(PAGE_ADDR,rd_buff,250);
 //STM32F1的FLASH读取
 
 if(strncmp((char *)wr_buff,(char *)rd_buff,250) != 0)
 {
   printf("STM32 FLASH ReadWrite ERROR!!!\r\n");
   while(1);
 }
 printf("STM32 FLASH ReadWrite OK!!!\r\n");
 while(1)
 {
 }
}


stm32_flash.h

#ifndef __STM32_FLASH_H_
#define __STM32_FLASH_H_

#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"

#define STM32_FLASH_BASE 0x08000000
//STM32 FLASH的起始地址
#define STM32_PAGE_SIZE 2048
//页大小2KB

enum{FLASH_SUCESS = 0,FLASH_ERROR};


void StmFalsh_Read(uint32_t ReadAddr,uint8_t *rd_buff,uint32_t Len);
//Stm32F1FlASH读取函数
uint32_t StmFlash_Program(uint32_t WriteAddr,uint16_t *wr_buf,uint32_t Len);
//STM32F1的FLASH编程函数
uint32_t StmFlash_Erase(uint32_t Page_ID,uint32_t NbOfPages);
//STM32F1的FLASH页擦除操作函数

#endif

stm32_flash.c

#include "stm32_flash.h"

FLASH_EraseInitTypeDef EraseInitStruct;
//结构体成员:类型擦除(大容量擦除和页面擦除)
//列祖:当启用大容量擦除时,选择要擦除的列组。
//页地址:禁用大容量擦除时要擦除的初始闪存页地址
//删除页数:要删除的页数
void StmFalsh_Read(uint32_t ReadAddr,uint8_t *rd_buff,uint32_t Len)
//STM32F1的FLASH读取(读地址,读取数据存储空间,读取长度)
{
  uint32_t i;
  for(i = 0; i<Len; i++)
  {
   *(rd_buff++) = *((uint8_t *)(ReadAddr + i));
   //*(uint8_t *)0x8000000;//就是读取FLASH中地址0x8000000处的数据
   //将数据存在读数组中
  }
}

uint32_t StmFlash_Program(uint32_t WriteAddr,uint16_t *wr_buff,uint32_t Len)
//STM32F1的FLASH页编程(编程起始地址,待编程的数据 , 编程长度)
{
  uint8_t err = FLASH_SUCESS;
  //err = 0;
  uint32_t Address,EndAddress;
 //开始地址  结束地址
  Address = WriteAddr;
  //Address = 0x08000000(FLASH的起始地址)+  200 * 2048
  EndAddress = WriteAddr + Len;
  //EndAddress =0x08000000(FLASH的起始地址)+  200 * 2048 +250;
  HAL_FLASH_Unlock();
  //解锁
  while(Address < EndAddress)
  {
    if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD ,Address,*wr_buff) == HAL_OK )
    //第一个参数是指定地址编写半个字
   {
      Address += 2;
       wr_buff++;
      //按半字编程,一次写2个字节
     //*在这里可能有疑问,为什么读250次
      //(看void StmFalsh_Read()),而写的话,写 250/2次,
    //  这是因为读是读一个字节,而写的话是半个字。 一个字等于4个字节 ,半个字就是2个字节
     // *(uint32_t *)0x8000000;//读一个字
  //  *(uint8_t *)0x8000000;//读一个字节;
  //  *(uint16_t *)0x8000000;//读半字;  */
     
   }
   else
   {
     err = FLASH_ERROR;
     break;
   } 
  }
  HAL_FLASH_Lock();
  //上锁
  return err;
}

uint32_t StmFlash_Erase(uint32_t Page_ID,uint32_t NbOfPages)
{
  uint8_t err;
  uint32_t FirstPage = 0;
  uint32_t SECTORError = 0;
  
  HAL_FLASH_Unlock();
  //解锁
  FirstPage = STM32_FLASH_BASE + Page_ID *   STM32_PAGE_SIZE;
  // FirstPage = 0x08000000  +  200 * 2048
  EraseInitStruct.TypeErase =  FLASH_TYPEERASE_PAGES;
  //擦除类型:页面擦除
  EraseInitStruct.PageAddress = FirstPage;
  //擦除页面地址:第一页
  EraseInitStruct.NbPages = NbOfPages;
  //要删除的页数
  EraseInitStruct.Banks = FLASH_BANK_1;
  //选择要擦除的列组   1   1个bank最大为512KB
 
  err = HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError);
  HAL_FLASH_Lock();
  //上锁
  return err;
}

你可能感兴趣的:(星光STM32)