单片机的 ROM 容量虽然不大,PY32F003 有 64K 字节的 ROM,但实际应用中会在 MCU 中存储持久化的数据,例如:在物联网应用中,需要把物模型持久化,作为非易失性数据,掉电了也要保存。这就要用到在 FLASH 保存这些数据。
PY32F003 支持 FLASH 读写。
PY32F003 的 FLASH 写入支持“按页写入”、“按扇区写入”和“全部写入”三种方式,实用中常会用到前两种方式。PY32F003 的 FLASH 页(Page)是一块 128 字节的 ROM 区域,可以直接寻址;扇区(Sector)是一块 4096 字节的 ROM 区域,可以直接寻址。
数据手册上列出了 FLASH 的可寻址空间和访问限制(后面有数据手册的截图)。
好啦,干货奉上 ;)
老样子,以下几个步骤,就能搞好。
/** ----------------------------------------------------------------------------
* @name : uint32_t* Flash_Read(void);
* @brief : 从预设的FLASH地址读取1页(128 Byte)的数据,保存在 data 中
* @param : None.
* @retval : NULL 或者数据的首地址
* @remark :
*** ----------------------------------------------------------------------------
*/
uint32_t* Flash_Read(uint16_t* buf_size);
/** ----------------------------------------------------------------------------
* @name : HAL_StatusTypeDef Flash_Write(uint32_t *data);
* @brief : 从预设的FLASH地址写入1页(128Byte)的数据,data是要写入的数据
* @param : [in] data: 要写入的数据指针.
* @param : [in] data_size: 要写入的数据长度
* @retval : HAL_OK: 写入成功; 其它: 错误
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才是有效数据
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef Flash_Write(uint32_t* data, const uint16_t data_size);
/**
******************************************************************************
* @file app_flash.c
* @brief Application level Flash read/write/erase codes.
******************************************************************************
* @attention
*
* Copyright (c) 2023 CuteModem Intelligence.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "main.h"
/* 定义用户数据在 FLASH 中存储的首地址, 这里指定了扇区号8, 页号256的首地址 */
#define FLASH_USER_START_ADDR 0x08007000
#define FLASH_USER_BUFF_SIZE 32
/* 定义用户数据: 32*4=128 Bytes, 1 page */
uint32_t WD_BUF[FLASH_USER_BUFF_SIZE] = { 0 };
uint32_t RD_BUF[FLASH_USER_BUFF_SIZE] = { 0 };
/** ----------------------------------------------------------------------------
* @name : HAL_StatusTypeDef app_Flash_Erase(void);
* @brief : 擦除从预设的FLASH地址的1页(128Byte)FLASH
* @param : None.
* @retval : HAL_StatusTypeDef. 操作成功返回 HAL_OK, 错误返回错误码
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才能继续操作
*** ----------------------------------------------------------------------------
*/
static HAL_StatusTypeDef app_Flash_Erase(void)
{
uint32_t PAGEError = 0;
FLASH_EraseInitTypeDef EraseInitStruct;
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGEERASE; // 擦写类型: 按页擦
EraseInitStruct.PageAddress = FLASH_USER_START_ADDR; // 擦写起始地址
EraseInitStruct.NbPages = sizeof(WD_BUF) / FLASH_PAGE_SIZE; // 需要擦写的页数量
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) // 执行page擦除,PAGEError返回擦写错误的page,
return HAL_ERROR; // 返回0xFFFFFFFF, 表示擦写成功
return HAL_OK;
}
/** ----------------------------------------------------------------------------
* @name : HAL_StatusTypeDef Local_Flash_Check_Blank(void);
* @brief : 查空 FLASH 中已被擦除的地址
* @param : None.
* @retval : HAL_StatusTypeDef. 操作成功返回 HAL_OK, 错误返回错误码
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才能继续操作
*** ----------------------------------------------------------------------------
*/
static HAL_StatusTypeDef Local_Flash_Check_Blank(void)
{
uint32_t addr = 0;
while (addr < sizeof(WD_BUF))
{
if (0xFFFFFFFF != HW32_REG(FLASH_USER_START_ADDR + addr))
return HAL_ERROR;
addr += 4;
}
return HAL_OK;
}
/** ----------------------------------------------------------------------------
* @name : HAL_StatusTypeDef Local_Flash_Program(void);
* @brief : 写预设地址的 FLASH
* @param : None.
* @retval : HAL_StatusTypeDef. 操作成功返回 HAL_OK, 错误返回错误码
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才能继续操作
*** ----------------------------------------------------------------------------
*/
static HAL_StatusTypeDef Local_Flash_Program(void)
{
uint32_t flash_program_start = FLASH_USER_START_ADDR ; // flash写起始地址
uint32_t flash_program_end = (FLASH_USER_START_ADDR + sizeof(WD_BUF)); // flash写结束地址
uint32_t *src = (uint32_t *)WD_BUF; // 数组指针
while (flash_program_start < flash_program_end)
{
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_PAGE,
flash_program_start,
src) != HAL_OK)
{
return HAL_ERROR;
}
flash_program_start += FLASH_PAGE_SIZE; //flash 起始指针指向下一个 page
src += FLASH_PAGE_SIZE / 4; //更新数据指针
}
return HAL_OK;
}
/** ----------------------------------------------------------------------------
* @name : HAL_StatusTypeDef Flash_Read(void);
* @brief : 校验预设的FLASH地址开始的1页(128Byte)的数据
* @param : None.
* @retval : HAL_StatusTypeDef. 操作成功返回 HAL_OK, 错误返回错误码。
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才是有效数据
*** ----------------------------------------------------------------------------
*/
static HAL_StatusTypeDef Local_Flash_Verify(void)
{
uint32_t addr = 0;
while (addr < sizeof(WD_BUF))
{
if (WD_BUF[addr / 4] != HW32_REG(FLASH_USER_START_ADDR + addr))
{
return HAL_ERROR;
}
addr += 4;
}
return HAL_OK;
}
/** ----------------------------------------------------------------------------
* @name : Local_Flash_Print(const uint32_t* data, const uint16_t data_size);
* @brief : 打印 data_size 个 data
* @param : None.
* @retval : NULL 或者数据的首地址
* @remark :
*** ----------------------------------------------------------------------------
*/
static void Local_Flash_Print(const uint32_t* data, const uint16_t data_size)
{
printf("%d bytes of data in FLASH:\r\n", data_size*sizeof(uint32_t));
for(int i = 0; i < data_size; i++)
{
printf("0x%08X ", data[i]);
if((i + 1) % 4 == 0) printf("\r\n");
}
printf("\r\n");
}
/** ----------------------------------------------------------------------------
* @name : uint32_t* Flash_Read(void);
* @brief : 从预设的FLASH地址读取1页(128Byte)的数据,保存在 data 中
* @param : None.
* @retval : NULL 或者数据的首地址
* @remark :
*** ----------------------------------------------------------------------------
*/
uint32_t* Flash_Read(uint16_t* buf_size)
{
uint32_t addr = 0;
while (addr < sizeof(RD_BUF))
{
RD_BUF[addr / 4] = HW32_REG(FLASH_USER_START_ADDR + addr);
addr += 4;
}
*buf_size = FLASH_USER_BUFF_SIZE;
Local_Flash_Print((uint32_t*)(&RD_BUF[0]), *buf_size);
return (uint32_t*)(&RD_BUF[0]);
}
/** ----------------------------------------------------------------------------
* @name : Flash_Write(uint32_t* data, const uint16_t data_size);
* @brief : 从预设的FLASH地址写入1页(128Byte)的数据,data是要写入的数据
* @param : [in] data, 一个 uint32_t 的数据缓冲区的首地址, 128字节
* @retval : HAL_HandleTypeDef. 操作成功返回 HAL_OK, 错误则返回错误码。
* @remark : 上级函数必须检查操作返回值, 只有 HAL_OK 时才是正确的操作
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef Flash_Write(uint32_t* data, const uint16_t data_size)
{
HAL_StatusTypeDef res = HAL_BUSY;
int i = 0;
uint16_t w_size = data_size;
if(data_size > FLASH_USER_BUFF_SIZE) w_size = FLASH_USER_BUFF_SIZE;
for(i = 0; i < w_size; i++) WD_BUF[i] = data[i];
for(i = w_size; i< FLASH_USER_BUFF_SIZE; i++) WD_BUF[i] = 0U;
res = HAL_FLASH_Unlock(); // 解锁 FLASH
if(res != HAL_OK ) return res;
res = app_Flash_Erase();
if(res != HAL_OK ) return res;
res = Local_Flash_Check_Blank();
if(res != HAL_OK ) return res;
res = Local_Flash_Program();
if(res != HAL_OK ) return res;
res = Local_Flash_Verify();
if(res != HAL_OK ) return res;
res = HAL_FLASH_Lock(); // 锁定 FLASH
if(res != HAL_OK ) return res;
return HAL_OK;
}
说明:
/**
******************************************************************************
* @file main.c
* @brief Main program entry.
******************************************************************************
* @copyright
*
* Copyright (c) 2023 CuteModem Intelligence.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Private includes*/
#include "main.h"
#include
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
struct Student_t {
uint32_t pri_id;
char stu_id[13];
char name[25];
uint8_t grade;
uint8_t class;
char performance[17];
};
/* Private function prototypes -----------------------------------------------*/
/* Private user code ---------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/**
* -------------------------------------------------------------------------
* @file : int main(void)
* @brief : main函数
* @param : 无
* @retval : 无限循环,无返回值
* @remark :
* -------------------------------------------------------------------------
*/
int main(void)
{
HAL_Init(); // systick初始化
SystemClock_Config(); // 配置系统时钟
GPIO_Config();
if(USART_Config() != HAL_OK) Error_Handler();
printf("[SYS_INIT] Debug port initilaized.\r\n");
printf("\r\n+---------------------------------------+"
"\r\n| PY32F003 MCU is ready. |"
"\r\n+---------------------------------------+"
"\r\n 10 digits sent to you! "
"\r\n+---------------------------------------+"
"\r\n");
HAL_Delay(0);
if (DBG_UART_Start() != HAL_OK) Error_Handler();
HAL_Delay(0);
struct Student_t stu1, stu2;
uint16_t stu_rec_size = sizeof(stu1);
uint16_t data_size = 0;
uint32_t* flash_data = Flash_Read(&data_size);
stu2 = *((struct Student_t *)flash_data);
printf("Previous saved student profile: %d bytes \r\n"
" Primary ID = %d\r\n"
" Student ID = '%s'\r\n"
" Name = '%s'\r\n"
" Grade = %d\r\n"
" Class = %d\r\n"
" Performance = '%s'\r\n",
data_size * 4,
stu2.pri_id,
stu2.stu_id,
stu2.name,
stu2.grade,
stu2.class,
stu2.performance);
memset(&stu1, 0, sizeof(stu1));
stu1.pri_id = 24506;
strcpy(stu1.stu_id, "CSTU-2020-02");
strcpy(stu1.name, "Zhang Lao 4");
stu1.grade = 2;
stu1.class = 11;
strcpy(stu1.performance,"A+A-B+B++");
printf("Current student profile: %d bytes.\r\n"
" Primary ID = %d\r\n"
" Student ID = '%s'\r\n"
" Name = '%s'\r\n"
" Grade = %d\r\n"
" Class = %d\r\n"
" Performance = '%s'\r\n",
stu_rec_size,
stu1.pri_id,
stu1.stu_id,
stu1.name,
stu1.grade,
stu1.class,
stu1.performance);
if(Flash_Write((uint32_t*)(&stu1), stu_rec_size) != HAL_OK )
{
printf("Data updating error.\r\n");
Error_Handler();
}
while (1)
{
BSP_LED_Toggle(LED3);
HAL_Delay(500);
}
}
/**
* -------------------------------------------------------------------------
* @brief : void Error_Handler(void)
* @detail : 错误陷阱函数,提示错误,然后死循环
* @param : 无
* @retval : 无
* @remark :
* -------------------------------------------------------------------------
*/
void Error_Handler(void)
{
Debug_Info("[__ERROR_] System halt.");
while (1) {}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
}
#endif /* USE_FULL_ASSERT */
说明:
利用主程序的顺序,用硬代码方式修改第3步的数据,观察打印结果。如果修改了数据,在下一次编译运行时可以看到打印信息的相应变化。连续两次运行,然后 RESET 一次,运行结果如下所示:第二次编译前,将该学生的评分从“A+A-B+B+”修改成了“A+A-B+B++”(多了一个+号),可以看出数据已经更新。
[SYS_INIT] Debug port initilaized.
+---------------------------------------+
| PY32F003 MCU is ready. |
+---------------------------------------+
10 digits sent to you!
+---------------------------------------+
1234567890
128 bytes of data in FLASH:
0x00005FBA 0x55545343 0x3230322D 0x32302D30
0x61685A00 0x4C20676E 0x34206F61 0x00000000
0x00000000 0x00000000 0x0B020000 0x2D412B41
0x2B422B42 0x00000000 0x00000000 0x00000000
0x0800331C 0x00000001 0x0800331C 0x08000469
0x08002852 0x21000000 0x2000032C 0x2000032C
0x00000040 0x00000000 0x0000015C 0x2000032C
0x200007F0 0x200007F1 0x00000001 0x08001FE5
Previous saved student profile: 128 bytes
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B+'
Current student profile: 64 bytes.
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B+'
[SYS_INIT] Debug port initilaized.
+---------------------------------------+
| PY32F003 MCU is ready. |
+---------------------------------------+
10 digits sent to you!
+---------------------------------------+
1234567890
128 bytes of data in FLASH:
0x00005FBA 0x55545343 0x3230322D 0x32302D30
0x61685A00 0x4C20676E 0x34206F61 0x00000000
0x00000000 0x00000000 0x0B020000 0x2D412B41
0x2B422B42 0x00000000 0x00000000 0x00000000
0x0800331C 0x00000001 0x0800331C 0x08000469
0x08002852 0x21000000 0x2000032C 0x2000032C
0x00000040 0x00000000 0x0000015C 0x2000032C
0x200007F0 0x200007F1 0x00000001 0x08001FE5
Previous saved student profile: 128 bytes
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B+'
Current student profile: 64 bytes.
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B++'
[SYS_INIT] Debug port initilaized.
+---------------------------------------+
| PY32F003 MCU is ready. |
+---------------------------------------+
10 digits sent to you!
+---------------------------------------+
1234567890
128 bytes of data in FLASH:
0x00005FBA 0x55545343 0x3230322D 0x32302D30
0x61685A00 0x4C20676E 0x34206F61 0x00000000
0x00000000 0x00000000 0x0B020000 0x2D412B41
0x2B422B42 0x0000002B 0x00000000 0x00000000
0x0800331C 0x00000001 0x0800331C 0x08000469
0x08002852 0x21000000 0x2000032C 0x2000032C
0x00000040 0x00000000 0x0000015C 0x2000032C
0x200007F0 0x200007F1 0x00000001 0x08001FE5
Previous saved student profile: 128 bytes
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B++'
Current student profile: 64 bytes.
Primary ID = 24506
Student ID = 'CSTU-2020-02'
Name = 'Zhang Lao 4'
Grade = 2
Class = 11
Performance = 'A+A-B+B++'
// Defined in py32f003x8.h
#define FLASH_KEY1_Msk (0x45670123UL << FLASH_KEY1_Pos) /*!< 0x45670123 */
#define FLASH_KEY1 FLASH_KEY1_Msk /*!< Flash program erase key1 */
#define FLASH_KEY2_Pos (0U)
#define FLASH_KEY2_Msk (0xCDEF89ABUL << FLASH_KEY2_Pos) /*!< 0xCDEF89AB */
#define FLASH_KEY2 FLASH_KEY2_Msk /*!< Flash program erase key2: used with FLASH_PEKEY1
// Defined in py32f0xx_hal_flash.c
/**
* @brief Unlock the FLASH control register access.
* @retval HAL Status
*/
HAL_StatusTypeDef HAL_FLASH_Unlock(void)
{
HAL_StatusTypeDef status = HAL_OK;
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00U)
{
/* Authorize the FLASH Registers access */
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
/* verify Flash is unlock */
if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0x00U)
{
status = HAL_ERROR;
}
}
return status;
}
#define HW32_REG(ADDRESS) (*((volatile unsigned int *)(ADDRESS)))
探索性的开发,差错难免。谬误之处,欢迎在评论区批评指正。