打印
[RISC-V MCU 应用开发]

二十三、CH32V103应用教程——读写内部FLASH

[复制链接]
2220|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 RISCVLAR 于 2020-12-5 15:54 编辑

CH32V103应用教程——读写内部FLASH

本章教程将通过程序代码进行内部FLASH读写操作。

1、CH32V103内部FLASH简介及相关函数介绍
CH32V103芯片含有一个内部FLASH,其存储数据在掉电后不会丢失,主要用于存储程序代码。芯片在重新上电并复位后,可通过加载读取内部FLASH中程序代码运行。

通常,我们可通过两种方式对内部FLASH进行读写:一是通过下载器等外部工具读写内部FLASH,二是通过芯片运行程序代码读取自身内部FLASH。本章即通过第二种方法进行内部FLASH读写。此外,就读写速度而言,读写内部FLASH比外部FLASH快的多,且由于内部FLASH掉电后数据不会丢失,因此内部FLASH剩余空间可用于存储重要数据和关键记录。

由于内部FLASH可被通过外部工具或程序代码读写,为了防止内部FLASH中存储数据被获取,某些应用会禁止读写内部FLASH内容,或在第一次运行时计算加密信息并记录到某些区域,然后删除自身的部分加密代码,这些应用都涉及到内部FLASH的操作。

CH32V103内部FLASH主要包含主存储器和信息块两块区域,其中信息块又可被分为两块系统引导代码存储区域、用户选择字和厂商配置字区域。其中,主存储器区域主要用于用户的应用程序存储,以4K字节(32 页)单位进行写保护划分;除了“厂商配置字”区域出厂锁定,用户不可访问,其他区域在一定条件下用户可操作。

CH32V103内部FLASH具有2种编程/擦除方式,具体如下:
  • 标准编程:此方式是默认编程方式(兼容方式)。这种模式下CPU以单次2字节方式执行编程,单次1K字节执行擦除及整片擦除操作。
  • 快速编程:此方式采用页操作方式(推荐)。经过特定序列解锁后,执行单次128字节的编程及128字节擦除。

关于CH32V103内部FLASH具体信息,可参考CH32V103应用手册。CH32V103内部FLASH标准库函数具体内容如下:
/*Functions used for all CH32V10x devices*/
void FLASH_SetLatency(uint32_t FLASH_Latency);
void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess);
void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);
void FLASH_Unlock(void);
void FLASH_Lock(void);
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
FLASH_Status FLASH_EraseAllPages(void);
FLASH_Status FLASH_EraseOptionBytes(void);
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);
FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);
FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);
uint32_t FLASH_GetUserOptionByte(void);
uint32_t FLASH_GetWriteProtectionOptionByte(void);
FlagStatus FLASH_GetReadOutProtectionStatus(void);
FlagStatus FLASH_GetPrefetchBufferStatus(void);
void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);
FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);
void FLASH_ClearFlag(uint32_t FLASH_FLAG);
FLASH_Status FLASH_GetStatus(void);
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
void FLASH_Unlock_Fast(void);
void FLASH_Lock_Fast(void);
void FLASH_BufReset(void);
void FLASH_BufLoad(uint32_t Address, uint32_t Data0, uint32_t Data1, uint32_t Data2, uint32_t Data3);
void FLASH_ErasePage_Fast(uint32_t Page_Address);
void FLASH_ProgramPage_Fast(uint32_t Page_Address);

/* New function used for all CH32V10x devices */
void FLASH_UnlockBank1(void);
void FLASH_LockBank1(void);
FLASH_Status FLASH_EraseAllBank1Pages(void);
FLASH_Status FLASH_GetBank1Status(void);
FLASH_Status FLASH_WaitForLastBank1Operation(uint32_t Timeout);
1.1、void FLASH_SetLatency(uint32_t FLASH_Latency)
功  能:设置代码延迟值。
输  入:FLASH_Latency:指定FLASH延迟值。

1.2、void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess)
功  能:启用或禁用半周期闪存访问。
输  入:FLASH_HalfCycleAccess:指定闪存半周期访问模式。

1.3、void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer)
功  能:启用或禁用预取缓冲区。
输  入:FLASH_PrefetchBuffer:指定预取缓冲区的状态。

1.4、void FLASH_Unlock(void)
功  能:解锁闪存程序擦除控制器。
输  入:无

1.5、void FLASH_UnlockBank1(void)
功  能:解锁闪存组1程序擦除控制器。
输  入:无

1.6、void FLASH_Lock(void)
功  能:锁定闪存程序擦除控制器。
输  入:无

1.7、void FLASH_LockBank1(void)
功  能:锁定闪存组1程序擦除控制器。
输  入:无

1.8、FLASH_Status FLASH_ErasePage(uint32_t Page_Address)
功  能:擦除指定的闪存页。
输  入:Page_Address:要删除的页面地址。

1.9、FLASH_Status FLASH_EraseAllPages(void)
功  能:清除所有闪存页。
输  入:无

1.10、FLASH_Status FLASH_EraseAllBank1Pages(void)
功  能:清除所有Bank1闪存页。
输  入:无

1.11、FLASH_Status FLASH_EraseOptionBytes(void)
功  能:擦除闪存选项字节。
输  入:无

1.12、FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data)
功  能:在指定的地址编程一个字。
输  入:Address:指定要编程的地址。Data:指定要编程的数据。

1.13、FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
功  能:在指定地址编程半个字。
输  入:Address:指定要编程的地址。Data:指定要编程的数据。

1.14、FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data)
功  能:在指定的选项字节数据地址编程半个字。
输  入:Address:指定要编程的地址。Data:指定要编程的数据。

1.15、FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages)
功  能:所需页面写保护。
输  入:FLASH_Pages:指定要写保护的页的地址。

1.16、FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)
功  能:启用或禁用读取保护。
输  入:Newstate:读出保护的新状态(启用或禁用)。

1.17、FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY)
功  能:编程闪存用户选项字节:IWDG_SW/RST_STOP/RST_STDBY。
输  入:OB_IWDG:选择IWDG模式。OB_IWDG_SW:选择软件IWDG;OB_IWDG_HW:已选择硬件IWDG。
OB_STOP:进入停止模式时重置事件。OB_STOP_NoRST:进入STOP时不生成重置;OB_u STOP_RST:进入STOP时生成重置。
OB _STDBY:进入待机状态时重置事件。OB_STDBY_NoRST:进入待机状态时不生成复位;OB STDBY_RST:进入停机位时生成复位。

1.18、uint32_t FLASH_GetUserOptionByte(void)
功  能:返回闪存用户选项字节值。
输  入:无

1.19、uint32_t FLASH_GetWriteProtectionOptionByte(void)
功  能:返回闪存写入保护选项字节寄存器值。
输  入:无

1.20、FlagStatus FLASH_GetReadOutProtectionStatus(void)
功  能:检查闪存读取保护状态是否设置。
输  入:无

1.21、FlagStatus FLASH_GetPrefetchBufferStatus(void)
功  能:检查是否设置了闪存预取缓冲区状态。
输  入:无

1.22、void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState)
功  能:启用或禁用指定的闪存中断。
输  入:FLASH_IT:指定要启用或禁用的闪存中断源。NewState:指定的Flash中断的新状态(启用或禁用)。

1.23、FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG)
功  能:检查是否设置了指定的闪存标志。
输  入:FLASH_FLAG:指定要检查的FLASH标志。

1.24、void FLASH_ClearFlag(uint32_t FLASH_FLAG)
功  能:清除闪存的挂起标志。
输  入:FLASH_FLAG:指定要清除的FLASH标志。

1.25、FLASH_Status FLASH_GetStatus(void)
功  能:返回闪存状态。
输  入:无

1.26、FLASH_Status FLASH_GetBank1Status(void)
功  能:返回闪存组1状态。
输  入:无

1.27、FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout)
功  能:等待闪存操作完成或超时。
输  入:Timeout:闪存编程超时

1.28、FLASH_Status FLASH_WaitForLastBank1Operation(uint32_t Timeout)
功  能:等待Bank1上的闪存操作完成或超时。
输  入:Timeout:闪存编程超时

1.29、void FLASH_Unlock_Fast(void)
功  能:解锁快速程序擦除模式。
输  入:无

1.30、void FLASH_Lock_Fast(void)
功  能:锁定快速程序擦除模式。
输  入:无

1.31、void FLASH_BufReset(void)
功  能:闪存缓冲复位。
输  入:无

1.32、void FLASH_BufLoad(uint32_t Address, uint32_t Data0, uint32_t Data1, uint32_t Data2, uint32_t Data3)
功  能:闪存缓冲加载(128位)。
输  入:无

1.33、void FLASH_ErasePage_Fast(uint32_t Page_Address)
功  能:擦除指定的闪存页(1页=128字节)。
输  入:Page_Address:要删除的页面地址。

1.34、void FLASH_ProgramPage_Fast(uint32_t Page_Address)
功  能:编程指定的闪存页(1页=128字节)。
输  入:Page_Address:要编程的页面地址。

使用以上函数时,直接在程序中进行调用即可。

2、硬件设计
本章教程主要通过程序代码进行内部FLASH读写操作,使用CH32V103内部资源,无需进行硬件连接。

3、软件设计
CH32V103内部FLASH编程/擦除方式有两种,一种是标准编程,一种是快速编程,本章即使用上述这两种方式分别进行内部FLASH读写操作,程序编程主要分3个步骤:
1、对内部FLASH进行解锁;
2、对内部FLASH进行页擦除;
3、对内部FLASH进行读写操作。
根据上述操作步骤,编写具体程序,具体程序如下:
flash.h文件
#ifndef __FLASH_H
#define __FLASH_H

#include "ch32v10x_conf.h"

typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
#define PAGE_WRITE_START_ADDR  ((uint32_t)0x0800F000) /* Start from 60K */
#define PAGE_WRITE_END_ADDR    ((uint32_t)0x08010000) /* End at 63K */
#define FLASH_PAGE_SIZE         1024

int Flash_Test(void);
int Flash_Test_Fast(void);

#endif
flash.h文件主要包含相关定义和函数声明;
flash.c文件
#include "flash.h"

/* Global Variable */
uint32_t EraseCounter = 0x00; //记录要擦除多少页
uint32_t Address = 0x00;      //记录写入的地址
uint16_t Data = 0xAAAA;       //记录写入的数据
uint32_t NbrOfPage;           //记录写入多少页

volatile FLASH_Status FLASHStatus = FLASH_COMPLETE; //记录每次擦除的结果
volatile TestStatus MemoryProgramStatus = PASSED;   //记录整个测试结果

/*******************************************************************************
* Function Name  : Flash_Test
* Description    : Flash Program Test.
* Input          : None
* Return         : None
*******************************************************************************/
int Flash_Test(void)
{
    FLASH_Unlock(); //解锁

    NbrOfPage = (PAGE_WRITE_END_ADDR - PAGE_WRITE_START_ADDR) / FLASH_PAGE_SIZE; //计算要擦除多少页

    //FLASH_FLAG_BSY:指示忙状态标志;FLASH_FLAG_EOP:指示操作结束标志;FLASH_FLAG_PGERR:闪存程序错误标志;FLASH_FLAG_WRPRTERR:指示写保护错误标志
    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP|FLASH_FLAG_PGERR |FLASH_FLAG_WRPRTERR); //清空所有标志位

    for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++) //按页擦除
    {
      FLASHStatus = FLASH_ErasePage(PAGE_WRITE_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter));
    }

    Address = PAGE_WRITE_START_ADDR; //向内部FLASH写入数据起始地址

    while((Address < PAGE_WRITE_END_ADDR) && (FLASHStatus == FLASH_COMPLETE))
    {
      FLASHStatus = FLASH_ProgramHalfWord(Address, Data); //向指定地址写入半字
      Address = Address + 2;
    }

    FLASH_Lock(); //上锁

    Address = PAGE_WRITE_START_ADDR;

    while((Address < PAGE_WRITE_END_ADDR) && (MemoryProgramStatus != FAILED))
    {
      if((*(__IO uint16_t*) Address) != Data)
      {
        MemoryProgramStatus = FAILED;
      }
        Address += 2;
    }
    return MemoryProgramStatus;

}

/*******************************************************************************
* Function Name  : Flash_Test_Fast
* Description    : Flash Fast Program Test.(128Byte)
* Input          : None
* Return         : None
*******************************************************************************/
int Flash_Test_Fast(void)
{
    u8 i, Verity_Flag=0;
    u32 buf[32];

    for(i=0; i<32; i++)
    {
        buf[i] = i;
    }

    FLASH_Unlock_Fast(); //快速编程模式解锁

    FLASH_ErasePage_Fast(0x0800E000); //擦除指定闪存页,此处擦除0x0800E000这个地址所指定页

    printf("128Byte Page Erase Sucess\r\n");

    FLASH_BufReset(); //复位闪存缓冲区,即执行清除内部128字节缓存区操作

    FLASH_BufLoad(0x0800E000, buf[0], buf[1], buf[2], buf[3]); //向指定地址开始连续写入16字节数据(4字节/次操作,写的地址每次偏移量为4),然后执行加载到缓冲区
    FLASH_BufLoad(0x0800E000 + 0x10, buf[4], buf[5], buf[6], buf[7]);
    FLASH_BufLoad(0x0800E000 + 0x20, buf[8], buf[9], buf[10], buf[11]);
    FLASH_BufLoad(0x0800E000 + 0x30, buf[12], buf[13], buf[14], buf[15]);
    FLASH_BufLoad(0x0800E000 + 0x40, buf[16], buf[17], buf[18], buf[19]);
    FLASH_BufLoad(0x0800E000 + 0x50, buf[20], buf[21], buf[22], buf[23]);
    FLASH_BufLoad(0x0800E000 + 0x60, buf[24], buf[25], buf[26], buf[27]);
    FLASH_BufLoad(0x0800E000 + 0x70, buf[28], buf[29], buf[30], buf[31]);

    FLASH_ProgramPage_Fast(0x0800E000); //启动一次快速页编程动作,编程指定的闪存页

    printf("128Byte Page Program Sucess\r\n");

    FLASH_Lock_Fast();

    for(i=0; i<32; i++) //读编程地址数据校验
    {
        if(buf[i] == *(u32*)(0x0800E000 + 4*i))
        {
            Verity_Flag = 0;
        }
        else
        {
            Verity_Flag = 1;
            break;
        }
    }
    return Verity_Flag;
}
flash.c文件主要包含两个函数,一个是内部FLASH标准编程函数Flash_Test,一个是内部FLASH快速编程函数Flash_Test_Fast,这两个函数具体执行过程如下:
内部FLASH标准编程函数Flash_Test执行过程:
(1)调用FLASH_Unlock函数进行解锁;
(2) 根据起始地址及结束地址计算需要擦除页数;
(3) 调用FLASH_ClearFlag函数清除各种标志位;
(4) 使用for循环以及调用FLASH_ErasePage函数擦除页数,每次擦除一页;
(5) 使用while循环并调用FLASH_ProgramWord函数向起始地址至结束地址的存储区域都写入变量“Data”存储的数值数值;
(6) 调用FLASH_Lock函数进行上锁;
(7) 使用指针读取写入的数据内容并校验。

内部FLASH快速编程函数Flash_Test_Fast执行过程与标准编程执行过程类似,具体如下:
(1)调用FLASH_Unlock_Fast函数进行解锁;
(2) 调用FLASH_ErasePage_Fast函数擦除指定闪存页;
(3) 调用FLASH_BufReset函数复位闪存缓冲区,执行清除内部128字节缓存区操作;
(4) 调用FLASH_BufLoad函数向指定地址开始连续写入16字节数据(4字节/次操作,写的地址每次偏移量为4),然后执行加载到缓冲区;
(5) 调用FLASH_ProgramPage_Fast函数启动一次快速页编程动作,编程指定的闪存页;
(6) 调用FLASH_Lock_Fast函数进行上锁;
(7) 使用for循环读取编程地址进行数据校验并返回校验值。
以上两个函数执行过程可对照CH32V103应用手册第24章闪存操作流程进行程序编写,更有助于理解编程。

main.c函数
int main(void)
{
        Delay_Init();
        USART_Printf_Init(115200);

        printf("SystemClk:%d\r\n",SystemCoreClock);
        printf("This is flash example\r\n");

        if(Flash_Test()== PASSED)
         {
           printf("读写内部 FLASH 标准编程测试成功\r\n");
         }
        else
         {
           printf("读写内部 FLASH 标准编程测试失败\r\n");
         }

    if(Flash_Test_Fast())
    {
     printf("128Byte Page Verity Fail,快速编程失败\r\n");
    }
    else
    {
     printf("128Byte Page Verity Sucess,快速编程成功\r\n");
    }

      while(1)
      {
      }
}
main.c函数主要进行函数初始化以及根据flash.c文件两个函数返回值输出相应信息。

4、下载验证
将编译好的程序下载到开发板并复位,串口打印情况具体如下:

根据串口打印信息可知,内部FLASH标准编程和快速编程测试成功。





22、读写内部FLASH.rar

474.98 KB

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

132

主题

293

帖子

41

粉丝