本帖最后由 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标准编程和快速编程测试成功。
|
|