本帖最后由 正点原子运营 于 2024-5-11 17:00 编辑
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板
2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版
6)正点原子STM32技术交流QQ群:725095144
本章将介绍使用STM32F103的片上Flash模拟EEPROM,并对齐进行读写操作。通过本章的学习,读者将学习到Flash的使用。 本章分为如下几个小节: 43.1 硬件设计 43.2 程序设计 43.3 下载验证
43.1 硬件设计 43.1.1 例程功能 1. 按下WKUP按键可往Flash写入“STM32 FLASH TEST” 2. 按下KEY0按键可从Flash读取到“STM32 FLASH TEST”,并显示至LCD 3. LED0闪烁,提示程序正在运行
43.1.2 硬件资源 1. LED LED0 - PB5 2. 正点原子2.8/3.5/4.3/7/10寸TFTLCD模块 3. 按键 WKUP - PA0 KEY0 - PE4
43.1.3 原理图 本章实验使用的Flash为STM32F103的片上资源,因此没有对应的连接原理图。
43.2 程序设计 43.2.1 HAL库的Flash驱动 STM32F103的片上Flash是可以直接读取的,但Flash无法直接写入,写入Flash前,需要先对其进行擦除操作,其具体的操作步骤如下: ①:解锁访问Flash控制寄存器 ②:擦除Flash ③:编程Flash ④:上锁访问Flash控制寄存器 在HAL库中对应的驱动函数如下: ①:解锁访问Flash控制寄存器 该函数用于解锁访问Flash控制寄存器,其函数原型如下所示: - HAL_StatusTypeDef HAL_FLASH_Unlock(void);
复制代码该函数的形参描述,如下表所示: 表43.2.1.1 函数HAL_FLASH_Unlock()形参描述 该函数的返回值描述,如下表所示:
表43.2.1.2 函数HAL_FLASH_Unlock()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* 解锁访问Flash控制寄存器 */
- HAL_FLASH_Unlock();
- }
复制代码②:擦除Flash 该函数用于擦除Flash,其函数原型如下所示: - HAL_StatusTypeDef HAL_FLASHEx_Erase( FLASH_EraseInitTypeDef *pEraseInit,
- uint32_t *SectorError);
复制代码该函数的形参描述,如下表所示: 表43.2.1.3 函数HAL_FLASHEx_Erase()形参描述 该函数的返回值描述,如下表所示: 表43.2.1.4 函数HAL_FLASHEx_Erase()返回值描述 该函数使用FLASH_EraseInitTypeDef类型结构体指针传入了Flash的擦除信息,该结构体的定义如下所示: - typedef struct
- {
- uint32_t TypeErase; /* 擦除类型 */
- uint32_t Banks; /* 块编号 */
- uint32_t Sector; /* 起始扇区 */
- uint32_t NbSectors; /* 扇区数量 */
- uint32_t VoltageRange; /* 电压范围 */
- }FLASH_EraseInitTypeDef;
复制代码该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- FLASH_EraseInitTypeDef erase = {0};
- uint32_t sector
-
- /* 擦除Flash */
- HAL_FLASHEx_Erase(&erase, §or);
- }
复制代码③:编程Flash 该函数用于编程Flash,其函数原型如下所示: - HAL_StatusTypeDef HAL_FLASH_Program( uint32_t TypeProgram,
- uint32_t Address,
- uint64_t Data);
复制代码该函数的形参描述,如下表所示: 表43.2.1.5 函数HAL_FLASH_Program()形参描述 该函数的返回值描述,如下表所示: 表43.2.1.6 函数HAL_FLASH_Program()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- uint8_t buf;
-
- /* 编程Flash */
- HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, 0, &buf);
- }
复制代码④:上锁访问Flash控制寄存器 该函数用于上锁访问Flash控制寄存器,其函数原型如下所示: - HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码该函数的形参描述,如下表所示: 表43.2.1.7 函数HAL_FLASH_Lock()形参描述 该函数的返回值描述,如下表所示: 表43.2.1.8 函数HAL_FLASH_Lock()返回值描述 该函数的使用示例,如下所示: - #include "stm32f1xx_hal.h"
- void example_fun(void)
- {
- /* 上锁访问Flash控制寄存器 */
- HAL_FLASH_Lock();
- }
复制代码
43.2.2Flash驱动 本章实验的Flash驱动主要负责向应用层提供Flash的读写操作函数。本章实验中,Flash的驱动代码包括stmflash.c和stmflash.h两个文件。 Flash驱动中,读取Flash数据的函数,如下所示: - /**
- *@brief 从指定地址读取一字的数据
- *@param addr: 指定读取数据的地址
- *@retval 读取到的一字数据
- */
- uint32_t stmflash_read_word(uint32_t addr)
- {
- return (*(volatile uint32_t *)addr);
- }
- /**
- *@brief 从指定地址读取指定字的数据
- *@param addr: 指定读取数据的地址
- *@param buf: 存储读取数据的起始地址
- *@param length: 指定读取数据的长度,单位:字
- *@retval 无
- */
- voidstmflash_read(uint32_t addr, uint32_t *buf, uint32_t length)
- {
- uint32_t index;
-
- for (index=0; index<length; index++)
- {
- buf[index] = stmflash_read_word(addr);
- addr += sizeof(uint32_t);
- }
- }
复制代码STM32F103片上Flash的读取十分简单,仅需读取对应地址的数据即可。 Flash驱动中,往Flash写入数据的函数,如下所示: - /**
- *@brief 往指定地址不检查地写入指定字的数据
- *@param addr: 指定写入数据的地址
- *@param buf: 存储写入数据的起始地址
- *@param length: 指定写入数据的长度,单位:字
- *@retval 无
- */
- voidstmflash_write_nocheck(uint32_t addr, uint32_t *buf, uint16_t length)
- {
- uint16_t index;
-
- HAL_FLASH_Unlock();
- for (index=0; index<length; index++)
- {
- HAL_FLASH_Program( FLASH_TYPEPROGRAM_WORD,
- addr + (index << 2),
- buf[index]);
- }
- HAL_FLASH_Lock();
- }
- /**
- *@brief 往指定地址写入指定字的数据
- *@param addr: 指定写入数据的地址
- *@param buf: 存储写入数据的起始地址
- *@param length: 指定写入数据的长度,单位:字
- *@retval 无
- */
- static uint32_t g_flash_buf[FLASH_PAGE_SIZE / sizeof(uint32_t)];
- voidstmflash_write(uint32_t addr, uint32_t *buf, uint16_t length)
- {
- uint32_t offaddr;
- uint16_t pagepos;
- uint16_t pageoff;
- uint16_t pageremain;
- uint16_t data_index;
- FLASH_EraseInitTypeDef flash_erase_init_struct = {0};
- uint32_t pageerr;
-
- /* 检查写入地址范围的合法性 */
- if ( (!IS_FLASH_PROGRAM_ADDRESS(addr)) ||
- (!IS_FLASH_PROGRAM_ADDRESS(addr + (length * sizeof(uint32_t)) - 1)))
- {
- return;
- }
-
- offaddr = addr - FLASH_BASE;
- pagepos = offaddr / FLASH_PAGE_SIZE;
- pageoff = (offaddr % FLASH_PAGE_SIZE) / sizeof(uint32_t);
- pageremain = (FLASH_PAGE_SIZE/ sizeof(uint32_t)) - pageoff;
- if (length <= pageremain)
- {
- pageremain = length;
- }
-
- while (1)
- {
- stmflash_read(FLASH_BASE + pagepos * FLASH_PAGE_SIZE,
- g_flash_buf,
- sizeof(g_flash_buf) / sizeof(uint32_t));
- for (data_index=0; data_index<pageremain; data_index++)
- {
- /* 判断是否需要擦除 */
- if (g_flash_buf[data_index + pageoff] != 0xFFFFFFFF)
- {
- break;
- }
- }
- if (data_index < pageremain)
- {
- /* 擦除页 */
- flash_erase_init_struct.TypeErase =FLASH_TYPEERASE_PAGES;
- flash_erase_init_struct.Banks = FLASH_BANK_1;
- flash_erase_init_struct.PageAddress =
- FLASH_BASE+ pagepos * FLASH_PAGE_SIZE;
- flash_erase_init_struct.NbPages = 1;
- HAL_FLASH_Unlock();
- HAL_FLASHEx_Erase(&flash_erase_init_struct, &pageerr);
- HAL_FLASH_Lock();
-
- for (data_index=0; data_index<pageremain; data_index++)
- {
- g_flash_buf[pageoff + data_index] = buf[data_index];
- }
- stmflash_write_nocheck( FLASH_BASE + pagepos * FLASH_PAGE_SIZE,
- g_flash_buf,
- sizeof(g_flash_buf) / sizeof(uint32_t));
- }
- else
- {
- stmflash_write_nocheck(addr, buf, pageremain);
- }
-
- /* 判断写入是否完成 */
- if (pageremain == length)
- {
- break;
- }
- else
- {
- pagepos++;
- pageoff = 0;
- buf += pageremain;
- addr += (pageremain * sizeof(uint32_t));
- length -= pageremain;
-
- if (length > (FLASH_PAGE_SIZE / sizeof(uint32_t)))
- {
- pageremain = FLASH_PAGE_SIZE / sizeof(uint32_t);
- }
- else
- {
- pageremain = length;
- }
- }
- }
- }
复制代码在写Flash前需要先判断待写入的比特位是否为1,若不为1则需要先进行擦除操作,否则将写入失败,保证待写入位置的比特位全部为0后,方可调用函数HAL_FLASH_Program()对Flash进行编程。
43.2.3 实验应用代码 本章实验的应用代码,如下所示: - static const uint8_t g_text_buf[] = {"STM32 FLASHTEST"};
- #define TEXT_SIZE (((sizeof(g_text_buf)>> 2) << 2) + 4)
- int main(void)
- {
- uint8_t t = 0;
- uint8_t key;
- uint8_t data[TEXT_SIZE];
- uint8_t wdata[TEXT_SIZE] = {0};
- uint8_t index;
-
- HAL_Init(); /* 初始化HAL库 */
- sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
- delay_init(72); /* 初始化延时 */
- usart_init(115200); /* 初始化串口 */
- led_init(); /* 初始化LED */
- key_init(); /* 初始化按键 */
- lcd_init(); /* 初始化LCD */
-
- lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
- lcd_show_string(30, 70, 200, 16, 16, "FLASHTEST", RED);
- lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
-
- lcd_show_string(30, 110, 200, 16, 16, "WK_UP:WriteKEY0:Read", RED);
-
- for (index=0; index<sizeof(g_text_buf); index++)
- {
- wdata[index] = g_text_buf[index];
- }
-
- while (1)
- {
- key = key_scan(0);
- if (key == WKUP_PRES)
- {
- /* 往Flash写入数据 */
- lcd_fill(0, 130, 239, 319, WHITE);
- lcd_show_string(30, 130, 200, 16, 16, "Start WriteFlash....", BLUE);
- stmflash_write( FLASH_END + 1 - TEXT_SIZE,
- (uint32_t *)wdata,
- TEXT_SIZE / sizeof(uint32_t));
- lcd_show_string(30, 130, 200, 16, 16, "Flash WriteFinished!", BLUE);
- }
- else if (key == KEY0_PRES)
- {
- /* 从Flash读取数据 */
- lcd_show_string(30, 130, 200, 16, 16, "Start ReadFlash.... ", BLUE);
- stmflash_read( FLASH_END + 1 - TEXT_SIZE,
- (uint32_t *)data,
- TEXT_SIZE / sizeof(uint32_t));
- lcd_show_string(30, 130, 200, 16, 16, "The DataReaded Is: ", BLUE);
- lcd_show_string(30, 150, 200, 16, 16, (char *)data, BLUE);
- }
-
- if (++t == 20)
- {
- t = 0;
- LED0_TOGGLE();
- }
-
- delay_ms(10);
- }
- }
复制代码从本章实验的应用代码中可以看到,在完成相关的初始化工作后,便会不断地等待按键输入,若检测到WKUP按键被按下,则会往Flash的指定地址中写入指定的数据,若检测到KEY_0按键被按下,则会从Flash的指定地址中读取数据,并在LCD上进行显示。
43.3 下载验证 在完成编译和烧录操作后,可以看到LCD上显示了本实验相关的信息,此时便可按下WKUP按键往Flash的指定地址写入指定数据,然后再按下KEY_0按键从Flash的指定地址将写入的数据读回来在LCD上进行显示,此时便可以看到在LCD上显示了“STM32 FLASH TEST”的提示信息,该提示信息就是从Flash中读回的数据。 |