stm32g070 Flash HAL读写操作

您所在的位置:网站首页 硬件的flash读取有寿命吗 stm32g070 Flash HAL读写操作

stm32g070 Flash HAL读写操作

2024-06-01 08:49| 来源: 网络整理| 查看: 265

一、stm32g070 Flash

由于没有外挂Flash,内存又比较小,所以在此使用内置Flash作为缓存, stmG070CBT6整体flash位128K,为flash规划分区,分区表如下。

在这里插入图片描述

从STM32G070寄存器手册可以看到,内部flash是2K对齐总共有64个页,同时写操作flash时要注意地址为4字节对齐。 我这里把flash,分成几个区域,如下图 在这里插入图片描述

二、驱动方法

1、G0系列的驱动需要注意Flash需要进行64位或快速双32位写入,不支持单字节写入,这是和F103系列最大区别。读写时候是一样的! 这里是 stmflash.h。flash的头文件

/** **************************************************************************************************** * @file stmflash.h * @author [email protected] * @version V1.0 * @date 2024-03-12 * @brief STM32G0内部FLASH读写 驱动代码 * @license Copyright (c) **************************************************************************************************** * @attention * * **************************************************************************************************** */ #ifndef __STMFLASH_H #define __STMFLASH_H #include "main.h" #define UPDATE_FLAG 0x123456 //升级标志 #define UPDATE_OVER 0xffffff //升级完成标志 #define CONFIG_ADDR 0X8004800 //升级标志位存储分区 #define APP1_ADDR 0x8005000 //APP1 跳转地址 #define OTA_ADDR 0x8012800 //OTA 升级地址 #define ERASE_LEN 54 //擦除长度,单位kb typedef enum { FLASH_OK =0x00U, FLASH_ERROR, }FLASH_SATAE_T; FLASH_SATAE_T Write_Flash(uint32_t address, uint8_t *buf, uint32_t length); FLASH_SATAE_T Write_Config(uint64_t data); #endif

stmflash.c文件

/** **************************************************************************************************** * @file stmflash.c * @author [email protected] * @version V1.0 * @date 2024-03-12 * @brief STM32G0内部FLASH读写 驱动代码 * @license Copyright (c) **************************************************************************************************** * @attention * * **************************************************************************************************** */ #include "./BSP/STMFLASH/stmflash.h" uint32_t addr = OTA_ADDR; //最新的Flash地址 /** * @brief 获取 地址Addr 在 Flash中式第几页。根据每页大小2k * @param Addr 开头地址 * @retval FLASH页数 */ static uint32_t GetPage(uint32_t Addr) { uint32_t page = 0; page = (Addr-FLASH_BASE) / FLASH_PAGE_SIZE; return page; } /** * @brief 擦除指定位置和大小的Flash,擦除flash之前必须解锁flash。 * @param addr 开头地址 * @param num 内存大小/kb * @retval FLASH_SATAE_T state */ static FLASH_SATAE_T Erase_Flash(uint32_t addr,uint8_t num) { uint32_t SectorError=0; FLASH_EraseInitTypeDef UPDATE_FLASH; UPDATE_FLASH.TypeErase = FLASH_TYPEERASE_PAGES; UPDATE_FLASH.Page = GetPage(addr); UPDATE_FLASH.NbPages = num; if(HAL_FLASHEx_Erase(&UPDATE_FLASH,&SectorError) != HAL_OK) { printf("ERASE ERROR %x\n",SectorError); goto ERROR; } printf("ERASE SUCCESS %x\n",SectorError); return FLASH_OK; ERROR: return FLASH_ERROR; } /** * @brief 写config Flash,升级标志位 * @param data 64位双字节 * @retval FLASH_SATAE_T state */ FLASH_SATAE_T Write_Config(uint64_t data) { HAL_FLASH_Unlock();//解锁flash if(Erase_Flash(CONFIG_ADDR,1) != FLASH_OK) { //擦除flash goto ERROR; } if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD,CONFIG_ADDR,data) != HAL_OK) { printf("Write_Config ERROR\n"); goto ERROR; } HAL_FLASH_Lock();//上锁 printf("Write_Config SUCCESS\n"); return FLASH_OK; ERROR: HAL_FLASH_Lock();//上锁 return FLASH_ERROR; } /** * @brief 写Flash,这里包括擦除flash,每页Flash是2Kb * @param address 开头地址 * @param *buf 写入数据 * @param length 写入字节数据长度 * @retval FLASH_SATAE_T state */ FLASH_SATAE_T Write_Flash(uint32_t start_address, uint8_t *buf, uint32_t length) { uint32_t i,j,len =0; uint64_t data = 0; uint8_t num = 1; if(length == 0) //长度为0时直接返回 { goto ERROR; } if(length % 8) //长度非8字节倍数时则补齐新的8字节 len = length/8+1; else len = length/8; while(length > num * 2048) num++; //得出需要擦除几页Flash HAL_FLASH_Unlock();//解锁flash if(Erase_Flash(start_address , num) != FLASH_OK) //擦除Flash { printf("Erase_Flash ERROR\n"); goto ERROR; } for(i=0;i data|=(uint64_t)buf[j+i*8] data = 0; for(j=0;j goto ERROR; } start_address += 8; } 三、测试结果

在stmflash.c下添加测试程序,然后在main函数下调用

/** * @brief flash 测试 * @param address 开头地址 * @param *buf 写入数据 * @param length 写入字节数据长度 * @retval void */ void Flash_test(uint32_t start_address, uint8_t *buf, uint32_t length) { Write_Flash(OTA_ADDR,buf,length); //写大数据进入 if(Write_Config(UPDATE_FLAG) != FLASH_OK) //写标志位数据 printf("Write_APP ERROR %x\n",addr); }

main.c 函数下面的while循环下添加测试代码,用串口发送数据,看看对应地址数据是否写入。在keil仿真下,看地址数据!

/* USER CODE BEGIN WHILE */ while (1) { if (debug_rxStC.rx_flag) { debug_rxStC.rx_flag = 0; applenth = debug_rxStC.rx_cnt; debug_rxStC.rx_cnt = 0; printf("用户程序接收完成!\r\n"); printf("代码长度:%dBytes\r\n", applenth); printf("用户程序开始升级!\r\n"); Flash_test(debug_rxStC.rx_buf, applenth); LED0_TOGGLE(); HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } 四、测试结果

这里可以看到#define CONFIG_ADDR 0X8004800 //升级标志位存储分区 config标志位写入成功。 在这里插入图片描述 在看大存储部分#define OTA_ADDR 0x8012800 //OTA 升级地址 写入数据,和串口对比数据对比,没有问题,说明代码测试成功!! 在这里插入图片描述

五、读取flash

读取的话就简单了,直接使用地址即可 。 注意:读取可以是1字节,也可以是2字节、4字节或者8字节形式。我这里用的是单字节读取,连续读取是用uint8_t 数组存储!

/** * @brief 从指定地址读取一个字节 (8位数据)stm32是小端模式 * @param faddr : 读取地址 * @retval 读取到的数据 (8位) */ uint8_t stmflash_read_byte(uint32_t faddr) { /* 读取的话就简单了,直接使用地址即可 */ return *(volatile uint8_t *)faddr; } /** * @brief 从指定地址读取一个字 (32位数据)stm32是小端模式 * @param faddr : 读取地址 * @retval 读取到的数据 (32位) */ uint32_t stmflash_read_word(uint32_t faddr) { /* 读取的话就简单了,直接使用地址即可 */ return *(volatile uint32_t *)faddr; } /** * @brief 从指定地址开始读出指定长度的数据 * @param ReadAddr : 起始地址 * @param pBuffer : 数据指针 * @param NumToRead: 要读取的字(8位)数,即1个字节 * @retval FLASH_SATAE_T state */ FLASH_SATAE_T STMFLASH_Read(uint32_t ReadAddr,uint8_t *pBuffer,uint32_t NumToRead) //连续读取 { uint32_t i; if(NumToRead == 0) //数据长度为0时,直接返回 goto ERROR; for(i = 0;i Write_Flash(OTA_ADDR,buf,length); //写大数据进入 if(Write_Config(UPDATE_FLAG) != FLASH_OK) //写标志位数据 printf("Write_APP ERROR %x\n",addr); uint64_t data = UPDATE_FLAG; Write_Flash(CONFIG_ADDR,(uint8_t *)&data,4);//正确,取uint64_t数据的地址,然后强制转成(uint8_t *) HAL_Delay(5000); printf("read config\n"); printf("CONFIG_ADDR = %x\n",stmflash_read_byte(CONFIG_ADDR)); printf("CONFIG_ADDR+1 = %x\n",stmflash_read_byte(CONFIG_ADDR+1)); printf("CONFIG_ADDR+2 = %x\n",stmflash_read_byte(CONFIG_ADDR+2)); printf("CONFIG_ADDR+3 = %x\n",stmflash_read_byte(CONFIG_ADDR+3)); printf("CONFIG_ADDR+4 = %x\n",stmflash_read_byte(CONFIG_ADDR+4)); printf("CONFIG_ADDR+5 = %x\n",stmflash_read_byte(CONFIG_ADDR+5)); }

测试结果,上图: 在这里插入图片描述 单字节测试读取成功,没有用其他人例程的32位读取或者16位读取。读取时候,不用在乎数据大小!!

六、总结

1、G0系列的Flash是需要64位(8字节)写入的,不支持单字(32位)和半字(16位)写入。 2、全系统每页Flash都是2kb大小。 3、擦除flash只有2种方法:1是页擦除;2是全部擦除。 4、flash的擦除和写入 都需要先解除flash锁,操作完就需要上锁。不上锁可能导致flash数据不安全! 5、flash读取不分数据形式的,可以是1字节,也可以是2字节、4字节或者8字节形式。

后续: 后续在写如何进行STM32G070 在线升级IAP的教程!!!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3