OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 568|回复: 0

《M144Z-M3最小系统板使用指南——STM32F103版》第四十三章 FLASH模拟EEPROM实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4667
金钱
4667
注册时间
2019-5-8
在线时间
1224 小时
发表于 2024-5-13 11:03:01 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2024-5-11 17:00 编辑

第四十三章 FLASH模拟EEPROM实验
1)实验平台:正点原子 M144Z-M3 STM32F103最小系统板

2) 章节摘自【正点原子】M144Z-M3最小系统板使用指南——STM32F103版


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boar ... _mini_sysboard.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32技术交流QQ群:725095144

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章将介绍使用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控制寄存器,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_FLASH_Unlock(void);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240511165708.png
表43.2.1.1 函数HAL_FLASH_Unlock()形参描述
该函数的返回值描述,如下表所示:

QQ截图20240511165737.png
表43.2.1.2 函数HAL_FLASH_Unlock()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 解锁访问Flash控制寄存器 */
  5.     HAL_FLASH_Unlock();
  6. }
复制代码
②:擦除Flash
该函数用于擦除Flash,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_FLASHEx_Erase(   FLASH_EraseInitTypeDef *pEraseInit,
  2.                                          uint32_t *SectorError);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240511165746.png
表43.2.1.3 函数HAL_FLASHEx_Erase()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240511165837.png
表43.2.1.4 函数HAL_FLASHEx_Erase()返回值描述
该函数使用FLASH_EraseInitTypeDef类型结构体指针传入了Flash的擦除信息,该结构体的定义如下所示:
  1. typedef struct
  2. {
  3.     uint32_t TypeErase;     /* 擦除类型 */
  4.     uint32_t Banks;         /* 块编号 */
  5.     uint32_t Sector;        /* 起始扇区 */
  6.     uint32_t NbSectors;     /* 扇区数量 */
  7.     uint32_t VoltageRange;  /* 电压范围 */
  8. }FLASH_EraseInitTypeDef;
复制代码
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.    FLASH_EraseInitTypeDef erase = {0};
  5.     uint32_t sector
  6.    
  7.     /* 擦除Flash */
  8.    HAL_FLASHEx_Erase(&erase, §or);
  9. }
复制代码
③:编程Flash
该函数用于编程Flash,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_FLASH_Program(   uint32_t TypeProgram,
  2.                                          uint32_t Address,
  3.                                          uint64_t Data);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240511165852.png
表43.2.1.5 函数HAL_FLASH_Program()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240511165900.png
表43.2.1.6 函数HAL_FLASH_Program()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     uint8_t buf;
  5.    
  6.     /* 编程Flash */
  7.    HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, 0, &buf);
  8. }
复制代码
④:上锁访问Flash控制寄存器
该函数用于上锁访问Flash控制寄存器,其函数原型如下所示:
  1. HAL_StatusTypeDef HAL_FLASH_Lock(void);
复制代码
该函数的形参描述,如下表所示:
QQ截图20240511165909.png
表43.2.1.7 函数HAL_FLASH_Lock()形参描述
该函数的返回值描述,如下表所示:
QQ截图20240511165916.png
表43.2.1.8 函数HAL_FLASH_Lock()返回值描述
该函数的使用示例,如下所示:
  1. #include "stm32f1xx_hal.h"
  2. void example_fun(void)
  3. {
  4.     /* 上锁访问Flash控制寄存器 */
  5.     HAL_FLASH_Lock();
  6. }
复制代码

43.2.2Flash驱动
本章实验的Flash驱动主要负责向应用层提供Flash的读写操作函数。本章实验中,Flash的驱动代码包括stmflash.c和stmflash.h两个文件。
Flash驱动中,读取Flash数据的函数,如下所示:
  1. /**
  2. *@brief   从指定地址读取一字的数据
  3. *@param   addr: 指定读取数据的地址
  4. *@retval  读取到的一字数据
  5. */
  6. uint32_t stmflash_read_word(uint32_t addr)
  7. {
  8.     return (*(volatile uint32_t *)addr);
  9. }
  10. /**
  11. *@brief   从指定地址读取指定字的数据
  12. *@param   addr: 指定读取数据的地址
  13. *@param   buf: 存储读取数据的起始地址
  14. *@param   length: 指定读取数据的长度,单位:字
  15. *@retval  无
  16. */
  17. voidstmflash_read(uint32_t addr, uint32_t *buf, uint32_t length)
  18. {
  19.     uint32_t index;
  20.    
  21.     for (index=0; index<length; index++)
  22.     {
  23.          buf[index] = stmflash_read_word(addr);
  24.          addr += sizeof(uint32_t);
  25.     }
  26. }
复制代码
STM32F103片上Flash的读取十分简单,仅需读取对应地址的数据即可。
Flash驱动中,往Flash写入数据的函数,如下所示:
  1. /**
  2. *@brief   往指定地址不检查地写入指定字的数据
  3. *@param   addr: 指定写入数据的地址
  4. *@param   buf: 存储写入数据的起始地址
  5. *@param   length: 指定写入数据的长度,单位:字
  6. *@retval  无
  7. */
  8. voidstmflash_write_nocheck(uint32_t addr, uint32_t *buf, uint16_t length)
  9. {
  10.     uint16_t index;
  11.    
  12.    HAL_FLASH_Unlock();
  13.     for (index=0; index<length; index++)
  14.     {
  15.          HAL_FLASH_Program( FLASH_TYPEPROGRAM_WORD,
  16.                              addr + (index << 2),
  17.                              buf[index]);
  18.     }
  19.    HAL_FLASH_Lock();
  20. }
  21. /**
  22. *@brief   往指定地址写入指定字的数据
  23. *@param   addr: 指定写入数据的地址
  24. *@param   buf: 存储写入数据的起始地址
  25. *@param   length: 指定写入数据的长度,单位:字
  26. *@retval  无
  27. */
  28. static uint32_t g_flash_buf[FLASH_PAGE_SIZE / sizeof(uint32_t)];
  29. voidstmflash_write(uint32_t addr, uint32_t *buf, uint16_t length)
  30. {
  31.     uint32_t offaddr;
  32.     uint16_t pagepos;
  33.     uint16_t pageoff;
  34.     uint16_t pageremain;
  35.     uint16_t data_index;
  36.    FLASH_EraseInitTypeDef flash_erase_init_struct = {0};
  37.     uint32_t pageerr;
  38.    
  39.     /* 检查写入地址范围的合法性 */
  40.     if ( (!IS_FLASH_PROGRAM_ADDRESS(addr)) ||
  41.          (!IS_FLASH_PROGRAM_ADDRESS(addr + (length * sizeof(uint32_t)) - 1)))
  42.     {
  43.          return;
  44.     }
  45.    
  46.    offaddr = addr - FLASH_BASE;
  47.    pagepos = offaddr / FLASH_PAGE_SIZE;
  48.    pageoff = (offaddr % FLASH_PAGE_SIZE) / sizeof(uint32_t);
  49.    pageremain = (FLASH_PAGE_SIZE/ sizeof(uint32_t)) - pageoff;
  50.     if (length <= pageremain)
  51.     {
  52.          pageremain = length;
  53.     }
  54.    
  55.     while (1)
  56.     {
  57.          stmflash_read(FLASH_BASE + pagepos * FLASH_PAGE_SIZE,
  58.          g_flash_buf,
  59.          sizeof(g_flash_buf) / sizeof(uint32_t));
  60.          for (data_index=0; data_index<pageremain; data_index++)
  61.          {
  62.              /* 判断是否需要擦除 */
  63.              if (g_flash_buf[data_index + pageoff] != 0xFFFFFFFF)
  64.              {
  65.                  break;
  66.              }
  67.          }
  68.          if (data_index < pageremain)
  69.          {
  70.              /* 擦除页 */
  71.              flash_erase_init_struct.TypeErase =FLASH_TYPEERASE_PAGES;
  72.              flash_erase_init_struct.Banks = FLASH_BANK_1;
  73.              flash_erase_init_struct.PageAddress =
  74.                                          FLASH_BASE+ pagepos * FLASH_PAGE_SIZE;
  75.              flash_erase_init_struct.NbPages = 1;
  76.              HAL_FLASH_Unlock();
  77.              HAL_FLASHEx_Erase(&flash_erase_init_struct, &pageerr);
  78.              HAL_FLASH_Lock();
  79.             
  80.              for (data_index=0; data_index<pageremain; data_index++)
  81.              {
  82.                  g_flash_buf[pageoff + data_index] = buf[data_index];
  83.              }
  84.              stmflash_write_nocheck( FLASH_BASE + pagepos * FLASH_PAGE_SIZE,
  85.                                      g_flash_buf,
  86.                                      sizeof(g_flash_buf) / sizeof(uint32_t));
  87.          }
  88.          else
  89.          {
  90.              stmflash_write_nocheck(addr, buf, pageremain);
  91.          }
  92.          
  93.          /* 判断写入是否完成 */
  94.          if (pageremain == length)
  95.          {
  96.              break;
  97.          }
  98.          else
  99.          {
  100.              pagepos++;
  101.              pageoff = 0;
  102.              buf += pageremain;
  103.              addr += (pageremain * sizeof(uint32_t));
  104.              length -= pageremain;
  105.             
  106.              if (length > (FLASH_PAGE_SIZE / sizeof(uint32_t)))
  107.              {
  108.                  pageremain = FLASH_PAGE_SIZE / sizeof(uint32_t);
  109.              }
  110.              else
  111.              {
  112.                  pageremain = length;
  113.              }
  114.          }
  115.     }
  116. }
复制代码
在写Flash前需要先判断待写入的比特位是否为1,若不为1则需要先进行擦除操作,否则将写入失败,保证待写入位置的比特位全部为0后,方可调用函数HAL_FLASH_Program()对Flash进行编程。

43.2.3 实验应用代码
本章实验的应用代码,如下所示:
  1. static const uint8_t g_text_buf[] = {"STM32 FLASHTEST"};
  2. #define TEXT_SIZE (((sizeof(g_text_buf)>> 2) << 2) + 4)
  3. int main(void)
  4. {
  5.     uint8_t t = 0;
  6.     uint8_t key;
  7.     uint8_t data[TEXT_SIZE];
  8.     uint8_t wdata[TEXT_SIZE] = {0};
  9.     uint8_t index;
  10.    
  11.    HAL_Init();                         /* 初始化HAL库 */
  12.    sys_stm32_clock_init(RCC_PLL_MUL9); /* 配置时钟,72MHz */
  13.    delay_init(72);                     /* 初始化延时 */
  14.    usart_init(115200);                 /* 初始化串口 */
  15.    led_init();                         /* 初始化LED */
  16.    key_init();                         /* 初始化按键 */
  17.    lcd_init();                         /* 初始化LCD */
  18.    
  19.    lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  20.    lcd_show_string(30, 70, 200, 16, 16, "FLASHTEST", RED);
  21.    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  22.    
  23.    lcd_show_string(30, 110, 200, 16, 16, "WK_UP:WriteKEY0:Read", RED);
  24.    
  25.     for (index=0; index<sizeof(g_text_buf); index++)
  26.     {
  27.          wdata[index] = g_text_buf[index];
  28.     }
  29.    
  30.     while (1)
  31.     {
  32.          key = key_scan(0);
  33.          if (key == WKUP_PRES)
  34.          {
  35.              /* 往Flash写入数据 */
  36.              lcd_fill(0, 130, 239, 319, WHITE);
  37.              lcd_show_string(30, 130, 200, 16, 16, "Start WriteFlash....", BLUE);
  38.              stmflash_write( FLASH_END + 1 - TEXT_SIZE,
  39.                              (uint32_t *)wdata,
  40.                              TEXT_SIZE / sizeof(uint32_t));
  41.              lcd_show_string(30, 130, 200, 16, 16, "Flash WriteFinished!", BLUE);
  42.          }
  43.          else if (key == KEY0_PRES)
  44.          {
  45.              /* 从Flash读取数据 */
  46.              lcd_show_string(30, 130, 200, 16, 16, "Start ReadFlash.... ", BLUE);
  47.              stmflash_read(  FLASH_END + 1 - TEXT_SIZE,
  48.                              (uint32_t *)data,
  49.                              TEXT_SIZE / sizeof(uint32_t));
  50.              lcd_show_string(30, 130, 200, 16, 16, "The DataReaded Is:  ", BLUE);
  51.              lcd_show_string(30, 150, 200, 16, 16, (char *)data, BLUE);
  52.          }
  53.          
  54.          if (++t == 20)
  55.          {
  56.              t = 0;
  57.              LED0_TOGGLE();
  58.          }
  59.          
  60.          delay_ms(10);
  61.     }
  62. }
复制代码
从本章实验的应用代码中可以看到,在完成相关的初始化工作后,便会不断地等待按键输入,若检测到WKUP按键被按下,则会往Flash的指定地址中写入指定的数据,若检测到KEY_0按键被按下,则会从Flash的指定地址中读取数据,并在LCD上进行显示。

43.3 下载验证
在完成编译和烧录操作后,可以看到LCD上显示了本实验相关的信息,此时便可按下WKUP按键往Flash的指定地址写入指定数据,然后再按下KEY_0按键从Flash的指定地址将写入的数据读回来在LCD上进行显示,此时便可以看到在LCD上显示了“STM32 FLASH TEST”的提示信息,该提示信息就是从Flash中读回的数据。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 23:57

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表