实验目的
学习STM32的IAP(在应用编程功能),可以把没有用到的片上的FLASH用作数据存储。数据手册请参看第2章/
实验简介
不同型号的STM32,其FLASH容量也有所不同,最小的只有16k字节,最大的则达到1024k字节,星光的枭龙STM32开发板选择的是STM32F103VET6的FLASH容量为512K字节,属于大容量产品(另外还有中容量和小容量产品),其闪存结构图如下:
编程和擦除闪存
闪存编程一次可以写入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时,必须预取缓冲区处于开启状态。
- 只有在系统时钟(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"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #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 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页擦除操作 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #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)); //将数据存在读数组中 } } 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++; } 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; // err = HAL_FLASHEx_Erase(&EraseInitStruct,&SECTORError); HAL_FLASH_Lock(); //上锁 return err; } |