[RISC-V MCU 应用开发] 二十三、CH32V103应用教程——读写内部FLASH

[复制链接]
 楼主| RISCVLAR 发表于 2020-12-5 15:49 | 显示全部楼层 |阅读模式
本帖最后由 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标准库函数具体内容如下:
  1. /*Functions used for all CH32V10x devices*/
  2. void FLASH_SetLatency(uint32_t FLASH_Latency);
  3. void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess);
  4. void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);
  5. void FLASH_Unlock(void);
  6. void FLASH_Lock(void);
  7. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
  8. FLASH_Status FLASH_EraseAllPages(void);
  9. FLASH_Status FLASH_EraseOptionBytes(void);
  10. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
  11. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
  12. FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
  13. FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);
  14. FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);
  15. FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);
  16. uint32_t FLASH_GetUserOptionByte(void);
  17. uint32_t FLASH_GetWriteProtectionOptionByte(void);
  18. FlagStatus FLASH_GetReadOutProtectionStatus(void);
  19. FlagStatus FLASH_GetPrefetchBufferStatus(void);
  20. void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);
  21. FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);
  22. void FLASH_ClearFlag(uint32_t FLASH_FLAG);
  23. FLASH_Status FLASH_GetStatus(void);
  24. FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);
  25. void FLASH_Unlock_Fast(void);
  26. void FLASH_Lock_Fast(void);
  27. void FLASH_BufReset(void);
  28. void FLASH_BufLoad(uint32_t Address, uint32_t Data0, uint32_t Data1, uint32_t Data2, uint32_t Data3);
  29. void FLASH_ErasePage_Fast(uint32_t Page_Address);
  30. void FLASH_ProgramPage_Fast(uint32_t Page_Address);

  31. /* New function used for all CH32V10x devices */
  32. void FLASH_UnlockBank1(void);
  33. void FLASH_LockBank1(void);
  34. FLASH_Status FLASH_EraseAllBank1Pages(void);
  35. FLASH_Status FLASH_GetBank1Status(void);
  36. 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文件
  1. #ifndef __FLASH_H
  2. #define __FLASH_H

  3. #include "ch32v10x_conf.h"

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

  8. int Flash_Test(void);
  9. int Flash_Test_Fast(void);

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

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

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

  9. /*******************************************************************************
  10. * Function Name  : Flash_Test
  11. * Description    : Flash Program Test.
  12. * Input          : None
  13. * Return         : None
  14. *******************************************************************************/
  15. int Flash_Test(void)
  16. {
  17.     FLASH_Unlock(); //解锁

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

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

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

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

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

  31.     FLASH_Lock(); //上锁

  32.     Address = PAGE_WRITE_START_ADDR;

  33.     while((Address < PAGE_WRITE_END_ADDR) && (MemoryProgramStatus != FAILED))
  34.     {
  35.       if((*(__IO uint16_t*) Address) != Data)
  36.       {
  37.         MemoryProgramStatus = FAILED;
  38.       }
  39.         Address += 2;
  40.     }
  41.     return MemoryProgramStatus;

  42. }

  43. /*******************************************************************************
  44. * Function Name  : Flash_Test_Fast
  45. * Description    : Flash Fast Program Test.(128Byte)
  46. * Input          : None
  47. * Return         : None
  48. *******************************************************************************/
  49. int Flash_Test_Fast(void)
  50. {
  51.     u8 i, Verity_Flag=0;
  52.     u32 buf[32];

  53.     for(i=0; i<32; i++)
  54.     {
  55.         buf[i] = i;
  56.     }

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

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

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

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

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

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

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

  71.     FLASH_Lock_Fast();

  72.     for(i=0; i<32; i++) //读编程地址数据校验
  73.     {
  74.         if(buf[i] == *(u32*)(0x0800E000 + 4*i))
  75.         {
  76.             Verity_Flag = 0;
  77.         }
  78.         else
  79.         {
  80.             Verity_Flag = 1;
  81.             break;
  82.         }
  83.     }
  84.     return Verity_Flag;
  85. }
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函数
  1. int main(void)
  2. {
  3.         Delay_Init();
  4.         USART_Printf_Init(115200);

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

  7.         if(Flash_Test()== PASSED)
  8.          {
  9.            printf("读写内部 FLASH 标准编程测试成功\r\n");
  10.          }
  11.         else
  12.          {
  13.            printf("读写内部 FLASH 标准编程测试失败\r\n");
  14.          }

  15.     if(Flash_Test_Fast())
  16.     {
  17.      printf("128Byte Page Verity Fail,快速编程失败\r\n");
  18.     }
  19.     else
  20.     {
  21.      printf("128Byte Page Verity Sucess,快速编程成功\r\n");
  22.     }

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

4、下载验证
将编译好的程序下载到开发板并复位,串口打印情况具体如下:
图片1.png
根据串口打印信息可知,内部FLASH标准编程和快速编程测试成功。





22、读写内部FLASH.rar

474.98 KB, 下载次数: 112

您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部

133

主题

296

帖子

44

粉丝
快速回复 在线客服 返回列表 返回顶部