【WCH蓝牙系列芯片】-基于CH32V208开发板—FLASH读写、擦除
-------------------------------------------------------------------------------------------------------------------------------------
在CH32V208系列芯片中,各个型号的参数如下图:
FLASH的大小包含零等待区和非零等待区,一共是480K。
1、内部实际存储代码的flash为慢速flash;
2、芯片复位后由硬件根据配置把慢速flash的代码拷贝到 用ram工艺做的快速flash区域,然后从快速flash里面运行;
3、芯片内部的慢速flash通常都比较大,并且支持运行代码,只是速度慢了点;
4、WCH官网的宣传FLASH大小实际上是针对快速FLASH.
在手册中查到,可以将快速FLASH/RAM配置为以下三种;需要在WCHISPTOOL工具中配置,也需要修改LD文件与之对应。
在 Link.ld中,也需要修改对应位置的参数
通过V208的EVT中,使用FLASH的例程对flash进行读写、擦除操作以及标准编程和快速编程的过程。
操作flash时是不能超过60M,但操作flash时芯片内部有二分频,所以程序不超过120的话是不需要软件配置分频的,但我们一般建议的话是不超过100M。当主频率超过100MHz时,在操作FLASH时需要注意:将HCLK除以2会导致与HCLK相关的外设时钟也被除以2。在使用过程中需要注意这一点。
编程过程中,只有半字和字编程,没有字节编程,都可用于标准编程和快速编程
//编程半个字(half-word)的函数
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
//用于在Flash存储器上编程单个字(32位数据)的函数
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
在程序中,先进行标准编程flash测试,
#include "debug.h" /* Global define */ typedef enum { FAILED = 0, PASSED = !FAILED } TestStatus; #define PAGE_WRITE_START_ADDR ((uint32_t)0x08008000) /* Start from 32K */ //Flash存储器的起始地址,这里表示从地址0x08008000(32K)开始写入数据 #define PAGE_WRITE_END_ADDR ((uint32_t)0x08009000) /* End at 36K */ //Flash存储器的结束地址,这里表示写入数据的结束地址为0x08009000(36K)。 #define FLASH_PAGE_SIZE 4096 //Flash存储器的页大小,这里表示每个页的大小为4096字节(4KB)。 #define FLASH_PAGES_TO_BE_PROTECTED FLASH_WRProt_Pages60to63 //需要保护的Flash页范围,这里表示保护页为从60到63页。 /* Fast Mode define */ #define FAST_FLASH_PROGRAM_START_ADDR ((uint32_t)0x08008000) //快速模式下编程的起始地址,这里表示快速编程模式的起始地址为0x08008000。(32K) #define FAST_FLASH_PROGRAM_END_ADDR ((uint32_t)0x08010000) //快速模式下编程的结束地址,这里表示快速编程模式的结束地址为0x08010000。(64K) #define FAST_FLASH_SIZE (64*1024) //快速模式下编程的Flash大小,这里表示快速编程模式的Flash大小为64KB。 /* Global Variable */ uint32_t EraseCounter = 0x0; //擦除计数器,用于记录擦除的次数。初始值为0。 uint32_t Address = 0x0; //Flash操作的地址变量,用于指定Flash中的地址。初始值为0。 uint16_t Data = 0x1234; //编程数据,用于存储要编程到Flash的16位数据。初始值为0xAAAA。 uint32_t WRPR_Value = 0xFFFFFFFF; ////写保护寄存器的值,用于配置Flash的写保护。初始值为0xFFFFFFFF uint32_t ProtectedPages = 0x0; //受保护的页数,用于记录被写保护的Flash页的数量。初始值为0。 uint32_t NbrOfPage; //页数,用于记录需要擦除的Flash页的数量。该值由根据计算得出。 volatile FLASH_Status FLASHStatus = FLASH_COMPLETE; //页数,用于记录需要擦除的Flash页的数量。该值由根据计算得出。 volatile TestStatus MemoryProgramStatus = PASSED; //存储程序操作状态的变量,用于跟踪编程操作的结果。初始值为PASSED。 volatile TestStatus MemoryEraseStatus = PASSED; //存储擦除操作状态的变量,用于跟踪擦除操作的结果。初始值为PASSED。 u32 buf[64]; uint16_t flashdata; //标准编程 void Flash_Test() { printf("FLASH Test\n"); /*When the main frequency exceeds 100MHz, attention should be paid when *operating FLASH: dividing HCLK by two will result in the related peripheral *clock of HCLK being divided by two. Attention should be paid when using. //在执行FLASH操作时,如果主频率超过100MHz,需要注意将主频分为二并将与HCLK相关的外设时钟也除以二的情况 */ RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2; //将主频分为二 __disable_irq(); USART_Printf_Init(115200); FLASH_Unlock(); //解锁FLASH //页面的起始地址 PAGE_WRITE_START_ADDR 和结束地址 PAGE_WRITE_END_ADDR //页面的大小 FLASH_PAGE_SIZE NbrOfPage = (PAGE_WRITE_END_ADDR - PAGE_WRITE_START_ADDR) / FLASH_PAGE_SIZE; //计算需要擦除的页面数量, //忙标志位 FLASH_FLAG_BSY、 //结束标志位 FLASH_FLAG_EOP 、 //写保护错误标志位 FLASH_FLAG_WRPRTERR。 FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP |FLASH_FLAG_WRPRTERR); //清除FLASH的状态标志位 //擦除Flash每一页数据 for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++) { FLASHStatus = FLASH_ErasePage(PAGE_WRITE_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter)); //Erase 4KB 执行擦除操作 if(FLASHStatus != FLASH_COMPLETE) //判断是否擦除成功 { printf("FLASH Erase Fail\r\n"); //失败 } printf("FLASH Erase Suc\r\n"); } Address = PAGE_WRITE_START_ADDR; //初始化地址变量 Address 为页面的起始地址 printf("Programing...\r\n"); //表示开始编程 while((Address < PAGE_WRITE_END_ADDR) && (FLASHStatus == FLASH_COMPLETE)) //编程Flash { FLASHStatus = FLASH_ProgramHalfWord(Address, Data); //使用FLASH_ProgramHalfWord()函数逐个编程半字(16位)数据到Flash中 // 打印地址和数据 printf("Addr: %08X, Data: %04X", Address, Data); Address = Address + 2; } Address = PAGE_WRITE_START_ADDR; //写入数据起始地址 printf("Program Cheking...\r\n"); //检查Flash 中的数据是否与原始数据相同 while((Address < PAGE_WRITE_END_ADDR) && (MemoryProgramStatus != FAILED)) { if((*(__IO uint16_t*) Address) != Data) // Flash 内容转换为 uint16_t 类型的数据,并与 Data 进行比较 { MemoryProgramStatus = FAILED; } Address += 2; } if(MemoryProgramStatus == FAILED) { printf("Memory Program FAIL!\r\n"); } else { printf("Memory Program PASS!\r\n"); } FLASH_Lock(); //锁定Flash:调用FLASH_Lock()函数锁定Flash,确保保护已编程的数据。 RCC->CFGR0 &= ~(uint32_t)RCC_HPRE_DIV2; //清除 RCC_CFGR0 寄存器中的 RCC_HPRE_DIV2 位,恢复主频为原来的频率。 __enable_irq(); //使能中断 USART_Printf_Init(115200); }
通过从串口打印,查看地址下将数据写入
在程序中,快速编程flash测试,
//快速擦除和编程 Flash 的函数 void Flash_Test_Fast(void) { u16 i,j,flag; for(i=0; i<64; i++) { buf[i] = i; } printf("FLASH Fast Mode Test\n"); RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2; //配置时钟分频,将时钟频率分为原来的一半 __disable_irq(); //禁用中断并初始化串口以便进行打印输出 USART_Printf_Init(115200); FLASH_Unlock_Fast(); //快速解锁 Flash 存储器, FLASH_EraseBlock_32K_Fast(FAST_FLASH_PROGRAM_START_ADDR); //快速擦除一个32K字节的块 printf("Program 32KByte start\n"); for(i=0; i<128; i++) { //128*256=32768 = 32K FLASH_ProgramPage_Fast(FAST_FLASH_PROGRAM_START_ADDR + 256*i, buf); //将数据缓冲区 buf 中的数据以256字节的页方式快速编程到Flash存储器中,每次编程一个256字节的页,循环执行128次,总共编程32K字节。 } //检查已编程的数据是否正确 for(i=0; i<128; i++) { for(j=0; j<64; j++) { if(*(u32*)(FAST_FLASH_PROGRAM_START_ADDR+256*i+4*j) != j) //它会将内存中的值与循环变量 j 的值进行比较 { flag = 0; break; } else { flag = 1; //相同,标志位为1 } } } if(flag) { printf("Program 32KByte suc\n"); } else printf("Program fail\n"); printf("Erase 256Byte...\n"); FLASH_ErasePage_Fast(FAST_FLASH_PROGRAM_START_ADDR); //快速擦除一个256字节的页 printf("Read 4KByte...\n"); for(i=0;i<1024; i++) { printf("%08x ",*(u32*)(FAST_FLASH_PROGRAM_START_ADDR+4*i)); //通过循环逐个读取4K字节页的内容并打印 } printf("\n"); printf("Erase 4KByte...\n"); FLASH_ErasePage(FAST_FLASH_PROGRAM_START_ADDR); //擦除一个4K字节的页 printf("Read 8KByte...\n"); for(i=0;i<2048; i++) { printf("%08x ",*(u32*)(FAST_FLASH_PROGRAM_START_ADDR+4*i)); //通过循环逐个读取8K字节页的内容并打印。 } printf("\n"); printf("Erase 32KByte...\n"); FLASH_EraseBlock_32K_Fast(FAST_FLASH_PROGRAM_START_ADDR); //快速擦除一个32K字节的块 printf("Read 32KByte...\n"); for(i=0;i<(1024*9); i++) { printf("%08x ",*(u32*)(FAST_FLASH_PROGRAM_START_ADDR+i*4)); //逐个读取32K字节块的内容并打印。 } printf("\n"); FLASH_Lock_Fast(); RCC->CFGR0 &= ~(uint32_t)RCC_HPRE_DIV2; __enable_irq(); USART_Printf_Init(115200); }
通过串口打印出数据,
在擦除过程中,擦除后在硬件层面是全F没问题;由于代码中读写codeflash时会经由内核加解密,得出的结果都是相对应的,因此通过芯片手册可以看到擦除成功后对应的数据。
注意:
FLASH中的0x08077C00是蓝牙的配对绑定的程序地址,上电之后,蓝牙会初始化,这个地址后面的数据就会擦掉。
针对这个地址,
1、 可以不用配对绑定,将
#ifndef BLE_SNV
#define BLE_SNV TRUE
改为FALSE,就可以用这个地址,但是这个方法不太建议,这样会让蓝牙就用不了配对绑定。
2、在使用flash时,尽量不要写到这个0x08077C00地址后面的数据,就往前面写