FLash存储

FLash

  • 在嵌入式系统中,Flash 通常指的是微控制器中的闪存,它是一种非易失性存储器,用于存储程序代码(.bin和.hex)和一些常驻数据(静态数据),即便在掉电后数据也不会丢失。
  • Flash的操作通常通过HAL_FLASH系列函数进行,包括编程、擦除以及保护等功能。
  • Flash存储器可以用来实现Bootloader(引导加载程序),用于在系统启动时进行初步的硬件初始化,并可以选择性地更新应用程序代码。
  • Bootloader可以通过串口、USB或其他接口接收新的固件,并将其写入Flash中,实现远程升级。
  • 常用的一些函数
向指定FLash地址写入数据
HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
参数:
	FLASH_TypeProgram:指定编程类型(字节、半字或双字)。
	uint32_t Address:要编程的起始地址。
	uint32_t Data:要写入的数据。
	
功能:擦除指定的Flash扇区或全部扇区。	
HAL_FLASH_Erase()
参数:
	FLASH_EraseInitTypeDef *pFlashEraseInit:指向包含擦除初始化参数的结构体指针。
	uint32_t *PagesError:指向一个数组,用于返回擦除失败的页号。

功能:解锁Flash访问。
HAL_FLASH_Unlock()

功能:锁定Flash访问。
HAL_FLASH_Lock()


-----------自己编写的FLash的接口函数-------------------
#include "stm32f1xx_hal.h"  // 包含 STM32 HAL 库头文件
#include "flash-ops.h"      // 包含自定义的 flash 操作头文件

/*
    每次写入 都是 4B,要求 data是4B对齐的,即len是4的整数倍
*/
int flash_ops_write(int addr, int *data, int len)  // 定义 flash 写操作函数
{
    int i;  // 循环变量
    int ret;  // 返回值
    HAL_StatusTypeDef status;  // HAL 库操作的状态值
    
    if ((len % 4) != 0) {  // 判断 len 是否是 4 的倍数
        int mode = len % 4;  // 计算多余的字节数
        len -= mode;  // 减去多余的字节数
        len += 4;  // 向上调整为 4 字节对齐
    }
    
    /*1 解锁*/
    HAL_FLASH_Unlock();  // 解锁 Flash,使能写操作
    
    /*2 擦除*/
    FLASH_EraseInitTypeDef f;  // 初始化 FLASH_EraseInitTypeDef 结构体
    f.TypeErase = FLASH_TYPEERASE_PAGES;  // 设置擦除类型为页面擦除
    f.PageAddress = addr;  // 设置要擦除的页面的起始地址
    f.NbPages = 1;  // 设置擦除的页数为 1
    uint32_t PageError = 0;  // 定义变量用于存储擦除时的错误码
    status = HAL_FLASHEx_Erase(&f, &PageError);  // 调用擦除函数,传入擦除配置
    if (status != HAL_OK) {  // 如果擦除失败
        ret = -12;  // 设置错误返回值为 -12
        goto end;  // 跳转到结束部分,跳过写入步骤
    }
    
    /*3 编程 烧写 */
    for (i = 0; i < len / 4; i += 1) {  // 循环按 4 字节写入数据
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i * 4, data[i]);  // 调用 HAL 库写入 4 字节的数据
    }
    
    ret = 0;  // 如果成功,设置返回值为 0
    
end:    
    /*4 锁住*/
    HAL_FLASH_Lock();  // 锁住 Flash,禁止写操作
    
    return ret;  // 返回结果
}

int flash_ops_read(int addr, void *data, int len)  // 定义 flash 读操作函数
{
    int i;  // 循环变量
    
    volatile char *ptaddr = (__IO char *)addr;  // 定义指向 flash 读地址的指针
    char *ptdata = (char *)data;  // 定义指向目标数据缓冲区的指针
    
    for (i = 0; i < len; i++) {  // 循环读取数据
        ptdata[i] = ptaddr[i];  // 将 flash 中的数据逐字节复制到输出缓冲区
    }
    
    return 0;  // 返回成功
}
-------------------.h文件-------------------
#ifndef __flash_ops_HH_H  // 防止重复包含头文件的预处理指令
#define __flash_ops_HH_H  // 定义宏以确保头文件只被包含一次

/*
    STM32F103RCT6 有 256KB 的 Flash 空间
    地址范围从 0x08000000 到 0x0803FFFF
    每页的大小为 2KB
    我们预留了 6 个页供用户使用,每个页可保存 2KB 数据
    
    STM32F103RBT6 有 128KB 的 Flash 空间
    地址范围从 0x08000000 到 0x0801FFFF
    每页大小为 1KB
    
    下面是倒数几页的地址:
    
              0x0801F000  // 倒数第三页开始
    .....    0x0801F400  // 倒数第二页开始
    倒数第二页: 0x0801F800  // 倒数第二页开始
    倒数第一页: 0x0801FC00  // 倒数第一页开始
*/

// 定义用于 STM32F103RBT6 的 Flash 页的起始地址(1KB 每页)
#define PAGE0_RBT6_ADDR  0x0801F000  // 倒数第三页地址
#define PAGE1_RBT6_ADDR  0x0801F400  // 倒数第二页地址
#define PAGE2_RBT6_ADDR  0x0801F800  // 倒数第一页地址
#define PAGE3_RBT6_ADDR  0x0801FC00  // 最后一页地址

// 定义通用 Flash 页的地址
#define PAGET_ADDR  0x08007000  // 通用的 Flash 页地址

// 定义用于 STM32F103RCT6 的 Flash 页的起始地址(2KB 每页)
#define PAGE0_RCT6_ADDR  0x0803D000  // 倒数第六页地址
#define PAGE1_RCT6_ADDR  0x0803D800  // 倒数第五页地址
#define PAGE2_RCT6_ADDR  0x0803E000  // 倒数第四页地址
#define PAGE3_RCT6_ADDR  0x0803E800  // 倒数第三页地址
#define PAGE4_RCT6_ADDR  0x0803F000  // 倒数第二页地址
#define PAGE5_RCT6_ADDR  0x0803F800  // 倒数第一页地址

// 函数声明:用于将数据写入指定 Flash 地址
int flash_ops_write(int addr, int *data, int len);

// 函数声明:用于从指定 Flash 地址读取数据
int flash_ops_read(int addr, void *data, int len);

#endif  // 结束头文件包含保护

  • 代码示例
#include "main.h"  // 包含主头文件
#include "flash-ops.h"  // 包含flash操作头文件

// 定义一个结构体usrinfo,用于存储用户的WiFi信息和用户凭证
struct usrinfo{
	char wifiname[32];  // WiFi名称,最大长度32字节
	char wifipasswd[16];  // WiFi密码,最大长度16字节
	char usrname[16];  // 用户名,最大长度16字节
	char usrpasswd[8];  // 用户密码,最大长度8字节
	long flags;  // 一个标志位,可以用于存储状态信息
};

ADC_HandleTypeDef hadc1;  // ADC1的句柄,用于配置和管理ADC的操作
RTC_HandleTypeDef hrtc;  // RTC的句柄,用于配置和管理RTC的操作
UART_HandleTypeDef huart1;  // UART1的句柄,用于配置和管理UART1的操作

// 函数原型声明
void SystemClock_Config(void);  // 系统时钟配置函数
static void MX_GPIO_Init(void);  // GPIO初始化函数
static void MX_ADC1_Init(void);  // ADC1初始化函数
static void MX_USART1_UART_Init(void);  // USART1 UART初始化函数
static void MX_RTC_Init(void);  // RTC初始化函数

// 重定向printf函数,通过串口发送数据
int __io_putchar(int ch)  
{
    HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);  // 通过UART1发送一个字符
    return ch;  // 返回发送的字符
}

// 主函数
int main(void)
{
  HAL_Init();  // 初始化HAL库,复位所有外设,配置Flash接口和Systick定时器

  SystemClock_Config();  // 配置系统时钟
  MX_GPIO_Init();  // 初始化GPIO
  MX_ADC1_Init();  // 初始化ADC1
  MX_USART1_UART_Init();  // 初始化UART1
  MX_RTC_Init();  // 初始化RTC

  // 初始化结构体usrinfo,并赋初始值
  struct usrinfo info;
  info.flags = 10086;  // 设置flags值为10086
  strcpy(info.wifiname, "haiyangxing");  // 将WiFi名称设为 "haiyangxing"
  strcpy(info.wifipasswd, "12345678");  // 将WiFi密码设为 "12345678"
  strcpy(info.usrname, "xiaowang");  // 将用户名设为 "xiaowang"
  strcpy(info.usrpasswd, "123456");  // 将用户密码设为 "123456"

  flash_ops_write(PAGE4_RCT6_ADDR, &info, sizeof(info));  // 将info结构体数据写入Flash的Page4中

  memset(&info, 0, sizeof(info));  // 清空info结构体的数据
  flash_ops_read(PAGE4_RCT6_ADDR, &info, sizeof(info));  // 从Flash的Page4中读取数据到info结构体中

  // 无限循环
  while (1)
  {
    // 通过UART1输出WiFi和用户信息,每秒输出一次
	printf("wifi=%s pass=%s username=%s usrpasswd=%s flags=%d\r\n",
	        info.wifiname, info.wifipasswd, info.usrname, info.usrpasswd, info.flags);
	HAL_Delay(1000);  // 延迟1秒
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值