STM32flash读写程序LED的按键控制

在 STM32 微控制器中,闪存是一种用来存储程序代码和非易失性数据的存储器,这意味着即使在断电后数据也不会丢失。

目录

闪存操作基础

下面具体使用按键控制LED的程序来展示flash的基础用法

完整工程如下:

文件束

main.c

key.h

key.c

led.h

led.c

flash.h

flash.c


闪存操作基础

  • 解锁闪存 (FLASH_Unlock):

    • 首先调用 FLASH_Unlock 函数解锁闪存控制器,以允许写操作。
    • 如果闪存控制寄存器被锁定,这一步是必须的,否则无法进行写操作。
  • 清除标志 (FLASH_ClearFlag):

    • 清除闪存操作的各种标志位(包括忙标志、操作结束标志、编程错误标志和写保护错误标志),确保没有残留的错误标志影响后续操作。
  • 擦除闪存页 (FLASH_ErasePage):

    • 擦除包含目标地址的闪存页。
    • 在 STM32 微控制器中,闪存是以页为单位进行擦除的,而不是按字节或字进行擦除。这一步是为了确保目标地址上的旧数据被清除。
  • 写入数据 (FLASH_ProgramHalfWord):

    • 将 16 位数据(半字)写入指定地址。
    • 这个操作在目标地址写入新的数据。
  • 再次清除标志 (FLASH_ClearFlag):

    • 再次清除所有闪存操作标志,确保操作完成后没有残留的错误标志。
  • 锁定闪存 (FLASH_Lock):

    • 最后调用 FLASH_Lock 函数重新锁定闪存控制器,以防止意外的写操作。

下面具体使用按键控制LED的程序来展示flash的基础用法

完整工程如下:

文件束


flash.c

  • FLASH_W 函数的作用是将 16 位的数据写入到指定的闪存地址,通过解锁闪存、清除标志位、擦除页、写入数据、再次清除标志位和锁定闪存的步骤来确保数据写入的正确性和安全性。
  • FLASH_R 函数的作用是从指定的闪存地址读取 16 位的数据,并返回读取的数据。

#include "flash.h"
//闪存写入函数
void FLASH_W(u32 add, u16 dat) { 
    FLASH_Unlock(); // 解锁闪存 
    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除闪存标志位
    FLASH_ErasePage(add); // 擦除闪存页
    FLASH_ProgramHalfWord(add, dat); // 写入数据到闪存
    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 再次清除闪存标志位
    FLASH_Lock(); // 锁定闪存  
}
//闪存读取函数
u16 FLASH_R(u32 add) { 
    u16 a;
    a = *(u16*)(add); // 从指定地址读取16位数据
    return a; // 返回读取的数据
}

main.c

这段代码的核心逻辑是通过按键输入来增加一个计数值,并通过 LED 显示该计数值。同时,将计数值存储在闪存中,以便在下次上电时可以读取并继续使用。具体操作步骤包括系统初始化、读取闪存数据、检测按键、更新计数值、显示在 LED 上以及将新值写入闪存。这种方式确保了计数值在断电后不会丢失。

#include "stm32f10x.h" 
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "key.h" 
#include "flash.h" 

#define FLASH_START_ADDR  0x0801f000 // 闪存起始地址

int main (void) {
    u16 a; // 定义变量,用于存储从闪存读取的值
    
    RCC_Configuration(); // 配置系统时钟
    LED_Init(); // 初始化LED
    KEY_Init(); // 初始化按键

    a = FLASH_R(FLASH_START_ADDR); // 从闪存读取数据

    GPIO_Write(LEDPORT, a); // 将读取的数据写入LED端口,点亮对应的LED
    while (1) { // 主循环
        if (!GPIO_ReadInputDataBit(KEYPORT, KEY1)) { // 检测按键是否按下
            delay_ms(20); // 延时去抖
            if (!GPIO_ReadInputDataBit(KEYPORT, KEY1)) { // 再次检测按键,确保按键稳定按下
                a++; // 增加计数值
                if (a > 3) { // 如果计数值超过3,重置为0
                    a = 0; 
                }
                GPIO_Write(LEDPORT, a); // 更新LED显示
                FLASH_W(FLASH_START_ADDR, a); // 将新的计数值写入闪存

                while (!GPIO_ReadInputDataBit(KEYPORT, KEY1)); // 等待按键松开
            }
        }
    }
}

key.h

#ifndef __KEY_H
#define __KEY_H
#include "sys.h"

//#define KEY1 PAin(0)// PA0
//#define KEY2 PAin(1)// PA1

#define KEYPORT    GPIOA    // 按键端口
#define KEY1    GPIO_Pin_0    // 按键1对应的引脚
#define KEY2    GPIO_Pin_1    // 按键2对应的引脚

void KEY_Init(void); // 按键初始化函数声明

#endif

key.c

#include "key.h"

void KEY_Init(void) { 
    GPIO_InitTypeDef GPIO_InitStructure;     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟       
    GPIO_InitStructure.GPIO_Pin = KEY1 | KEY2; // 配置KEY1和KEY2引脚               
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 设置为上拉输入模式     
    GPIO_Init(KEYPORT, &GPIO_InitStructure); // 初始化引脚     
}

led.h

#ifndef __LED_H
#define __LED_H
#include "sys.h"

//#define LED1 PBout(0)// PB0
//#define LED2 PBout(1)// PB1    
#define LEDPORT    GPIOB    // LED端口
#define LED1    GPIO_Pin_0    // LED1对应的引脚
#define LED2    GPIO_Pin_1    // LED2对应的引脚

void LED_Init(void); // LED初始化函数声明

#endif

led.c

#include "led.h"

void LED_Init(void) { 
    GPIO_InitTypeDef GPIO_InitStructure;     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE); // 使能GPIOA、GPIOB和GPIOC时钟       
    GPIO_InitStructure.GPIO_Pin = LED1 | LED2; // 配置LED1和LED2引脚              
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置为推挽输出模式     
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速度为50MHz 
    GPIO_Init(LEDPORT, &GPIO_InitStructure); // 初始化引脚     
}

flash.h

#ifndef __FLASH_H
#define __FLASH_H
#include "sys.h"

void FLASH_W(u32 add, u16 dat); // 闪存写入函数声明
u16 FLASH_R(u32 add); // 闪存读取函数声明

#endif

总结:

如此看来,Flash 的操作方法很简单,只要在程序中想存代者数据的地方加入一行 Flash 写操作,给出地址和数据即可。注意:要写入的数据必须是 16 位的,程序中为写入数据还特别定义了 16 位的变量 a。下一步是读出,在程序中想读出的地方调用 FLASH R 函数:给出读取地址就能从中读出数据。

关于 Flash 操作需要注意几个细节。第一是写入地址不能和用户程序相冲突。刚才说过,在 Flash中有地址分布规则,下载的用户程序(HEX文件)是要在第 0开始写入的。根据程序大小,会占用不同大小的 Flash 空间。如果你想在运行时保存临时数据,你的数据就不能和下载程序空间重叠,否则会破坏程序内容。所以建议把临时数据放到比较靠后的地址中。在今后的项目开发中,如果要存储临时数据,你就需要考虑用户程序所占用的空间大小,然后在用户程序没有5用的空白区域存放临时数据.

引用\[1\]中的代码是一个用于在STM32F103C8T6最小系统板上进行Flash读写的示例程序。该程序的目的是将32KB的数据写入Flash,并验证写入数据的正确性和读写速率。程序首先配置了Cubemx工程,然后定义了Flash的起始地址为0x0801f000。在主程序中,通过读取按键的状态来触发Flash的写入操作,并将写入的数据通过LED显示出来。程序中还提到了一些关于Flash操作的注意事项,如擦除操作以页为单位,写操作以16位宽度为单位,擦除一页需要10ms等。 根据你的问题,如果你想编写一个用于STM32Flash读写程序,你可以参考这个示例程序。你需要包含相应的头文件,并根据需要配置Flash的起始地址。然后,你可以通过读取按键的状态来触发Flash的写入操作,并通过LED或其他方式来显示写入的数据。在编写程序时,还需要注意一些关于Flash操作的细节,如擦除操作以页为单位,写操作以16位宽度为单位,避免死循环擦写等。希望这个回答对你有帮助! #### 引用[.reference_title] - *1* [STM32Flash读写操作](https://blog.csdn.net/weixin_52288941/article/details/122147152)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [STM32FLASH读写程序](https://blog.csdn.net/weixin_44681745/article/details/115523985)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值