【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地址后面的数据,就往前面写

 

posted on 2023-10-18 11:14  凡仕  阅读(572)  评论(0编辑  收藏  举报