你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的内部Flash应用之模拟EEPROM

[复制链接]
STMCU小助手 发布时间:2021-11-2 23:56
71.1 初学者重要提示
  学习本章节前,务必优先学习第70章。
  使用内部Flash模拟EEPROM,务必告诉编译要使用的存储空间,防止这个空间存入了程序。
  STM32H7的Flash编程时,务必保证要编程的地址是32字节对齐的,即此地址对32求余为0。并且编程的数据必须32字节整数倍。
  STM32H743XI有两个独立的BANK,一个BANK的编程和擦除操作对另一个BANK没有任何影响。但是用户应用程序和要擦写的Flash扇区在同一个BANK,在执行擦写操作时,应用应用程序将停止运行,包括中断服务程序。
  使用内部Flash模拟EEPROM要做到先擦除后使用。
71.2 模拟EEPROM驱动设计
这里重点把内部Flash的读取,编程和擦除做个说明。

71.2.1 内部Flash擦除的实现
内部Flash的擦除思路如下:

  第1步,获取擦除地址所处的扇区。
  第2步,调用函数HAL_FLASH_Unlock解锁。
  第3步,调用函数HAL_FLASHEx_Erase擦除一个扇区。
  第4步,调用函数HAL_FLASH_Lock上锁。
按照这个思路,程序实现如下:

  1. 1.    /*
  2. 2.    ******************************************************************************************************
  3. 3.    *    函 数 名: bsp_EraseCpuFlash
  4. 4.    *    功能说明: 擦除CPU FLASH一个扇区 (128KB)
  5. 5.    *    形    参: _ulFlashAddr : Flash地址
  6. 6.    *    返 回 值: 0 成功, 1 失败
  7. 7.    *              HAL_OK       = 0x00,
  8. 8.    *              HAL_ERROR    = 0x01,
  9. 9.    *              HAL_BUSY     = 0x02,
  10. 10.    *              HAL_TIMEOUT  = 0x03
  11. 11.    *
  12. 12.    ******************************************************************************************************
  13. 13.    */
  14. 14.    uint8_t bsp_EraseCpuFlash(uint32_t _ulFlashAddr)
  15. 15.    {
  16. 16.        uint32_t FirstSector = 0, NbOfSectors = 0;
  17. 17.        FLASH_EraseInitTypeDef EraseInitStruct;
  18. 18.        uint32_t SECTORError = 0;
  19. 19.        uint8_t re;
  20. 20.   
  21. 21.        /* 解锁 */
  22. 22.        HAL_FLASH_Unlock();
  23. 23.        
  24. 24.        /* 获取此地址所在的扇区 */
  25. 25.        FirstSector = bsp_GetSector(_ulFlashAddr);
  26. 26.        
  27. 27.        /* 固定1个扇区 */
  28. 28.        NbOfSectors = 1;   
  29. 29.   
  30. 30.        /* 擦除扇区配置 */
  31. 31.        EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
  32. 32.        EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
  33. 33.        
  34. 34.        if (_ulFlashAddr >= ADDR_FLASH_SECTOR_0_BANK2)
  35. 35.        {
  36. 36.            EraseInitStruct.Banks         = FLASH_BANK_2;
  37. 37.        }
  38. 38.        else
  39. 39.        {
  40. 40.            EraseInitStruct.Banks         = FLASH_BANK_1;
  41. 41.        }
  42. 42.        
  43. 43.        EraseInitStruct.Sector        = FirstSector;
  44. 44.        EraseInitStruct.NbSectors     = NbOfSectors;
  45. 45.        
  46. 46.        /* 扇区擦除 */   
  47. 47.        re = HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
  48. 48.        
  49. 49.        /* 擦除完毕后,上锁 */
  50. 50.        HAL_FLASH_Lock();   
  51. 51.        
  52. 52.        return re;
  53. 53.    }
复制代码



这里将此程序设计的关键点为大家做个说明:

  第25行函数是通过函数bsp_GetSector获取要擦除地址所处的扇区。这个函数的实现比较简单,代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_GetSector
  4. *    功能说明: 根据地址计算扇区首地址
  5. *    形    参: 无
  6. *    返 回 值: 扇区号(0-7)
  7. *********************************************************************************************************
  8. */
  9. uint32_t bsp_GetSector(uint32_t Address)
  10. {
  11.     uint32_t sector = 0;

  12.     if (((Address < ADDR_FLASH_SECTOR_1_BANK1) && (Address >= ADDR_FLASH_SECTOR_0_BANK1)) || \
  13.         ((Address < ADDR_FLASH_SECTOR_1_BANK2) && (Address >= ADDR_FLASH_SECTOR_0_BANK2)))   
  14.     {
  15.         sector = FLASH_SECTOR_0;  
  16.     }
  17.     else if (((Address < ADDR_FLASH_SECTOR_2_BANK1) && (Address >= ADDR_FLASH_SECTOR_1_BANK1)) || \
  18.       ((Address < ADDR_FLASH_SECTOR_2_BANK2) && (Address >= ADDR_FLASH_SECTOR_1_BANK2)))   
  19.     {
  20.         sector = FLASH_SECTOR_1;  
  21.     }
  22.     else if (((Address < ADDR_FLASH_SECTOR_3_BANK1) && (Address >= ADDR_FLASH_SECTOR_2_BANK1)) || \
  23.       ((Address < ADDR_FLASH_SECTOR_3_BANK2) && (Address >= ADDR_FLASH_SECTOR_2_BANK2)))   
  24.     {
  25.         sector = FLASH_SECTOR_2;  
  26.     }
  27.     else if (((Address < ADDR_FLASH_SECTOR_4_BANK1) && (Address >= ADDR_FLASH_SECTOR_3_BANK1)) || \
  28.       ((Address < ADDR_FLASH_SECTOR_4_BANK2) && (Address >= ADDR_FLASH_SECTOR_3_BANK2)))   
  29.     {
  30.         sector = FLASH_SECTOR_3;  
  31.     }
  32.     else if (((Address < ADDR_FLASH_SECTOR_5_BANK1) && (Address >= ADDR_FLASH_SECTOR_4_BANK1)) || \
  33.       ((Address < ADDR_FLASH_SECTOR_5_BANK2) && (Address >= ADDR_FLASH_SECTOR_4_BANK2)))   
  34.     {
  35.         sector = FLASH_SECTOR_4;  
  36.     }
  37.     else if (((Address < ADDR_FLASH_SECTOR_6_BANK1) && (Address >= ADDR_FLASH_SECTOR_5_BANK1)) || \
  38.       ((Address < ADDR_FLASH_SECTOR_6_BANK2) && (Address >= ADDR_FLASH_SECTOR_5_BANK2)))   
  39.     {
  40.         sector = FLASH_SECTOR_5;  
  41.     }
  42.     else if (((Address < ADDR_FLASH_SECTOR_7_BANK1) && (Address >= ADDR_FLASH_SECTOR_6_BANK1)) || \
  43.       ((Address < ADDR_FLASH_SECTOR_7_BANK2) && (Address >= ADDR_FLASH_SECTOR_6_BANK2)))   
  44.     {
  45.         sector = FLASH_SECTOR_6;  
  46.     }
  47.     else if (((Address < ADDR_FLASH_SECTOR_0_BANK2) && (Address >= ADDR_FLASH_SECTOR_7_BANK1)) || \
  48.       ((Address < CPU_FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_7_BANK2)))
  49.     {
  50.         sector = FLASH_SECTOR_7;  
  51.     }
  52.     else
  53.     {
  54.         sector = FLASH_SECTOR_7;
  55.     }

  56.     return sector;
  57. }
复制代码


由于STM32H7的BANK1和BANK2是独立的,都有8个扇区,所以程序里面只需返回要擦除地址所处的扇区号即可。

  第47行的擦除函数HAL_FLASHEx_Erase在第70章的4.4小节有说明。
71.2.2 内部Flash编程的实现
内部Flash的编程思路如下:

  第1步,判断是否要编写数据进去,如果数据已经在内部Flash里面。
  第2步,调用函数HAL_FLASH_Unlock解锁。
  第3步,调用函数HAL_FLASH_Program对内部Flash编程数据。
  第4步,调用函数HAL_FLASH_Lock上锁。
按照这个思路,程序实现如下:

  1. 1.    /*
  2. 2.    ******************************************************************************************************
  3. 3.    *    函 数 名: bsp_WriteCpuFlash
  4. 4.    *    功能说明: 写数据到CPU 内部Flash。 必须按32字节整数倍写。不支持跨扇区。扇区大小128KB. \
  5. 5.    *              写之前需要擦除扇区. 长度不是32字节整数倍时,最后几个字节末尾补0写入.
  6. 6.    *    形    参: _ulFlashAddr : Flash地址
  7. 7.    *             _ucpSrc : 数据缓冲区
  8. 8.    *             _ulSize : 数据大小(单位是字节, 必须是32字节整数倍)
  9. 9.    *    返 回 值: 0-成功,1-数据长度或地址溢出,2-写Flash出错(估计Flash寿命到)
  10. 10.    ******************************************************************************************************
  11. 11.    */
  12. 12.    uint8_t bsp_WriteCpuFlash(uint32_t _ulFlashAddr, uint8_t *_ucpSrc, uint32_t _ulSize)
  13. 13.    {
  14. 14.        uint32_t i;
  15. 15.        uint8_t ucRet;
  16. 16.   
  17. 17.        /* 如果偏移地址超过芯片容量,则不改写输出缓冲区 */
  18. 18.        if (_ulFlashAddr + _ulSize > CPU_FLASH_BASE_ADDR + CPU_FLASH_SIZE)
  19. 19.        {
  20. 20.            return 1;
  21. 21.        }
  22. 22.   
  23. 23.        /* 长度为0时不继续操作  */
  24. 24.        if (_ulSize == 0)
  25. 25.        {
  26. 26.            return 0;
  27. 27.        }
  28. 28.   
  29. 29.        ucRet = bsp_CmpCpuFlash(_ulFlashAddr, _ucpSrc, _ulSize);
  30. 30.   
  31. 31.        if (ucRet == FLASH_IS_EQU)
  32. 32.        {
  33. 33.            return 0;
  34. 34.        }
  35. 35.   
  36. 36.        __set_PRIMASK(1);          /* 关中断 */
  37. 37.   
  38. 38.        /* FLASH 解锁 */
  39. 39.        HAL_FLASH_Unlock();
  40. 40.   
  41. 41.        for (i = 0; i < _ulSize / 32; i++)   
  42. 42.        {
  43. 43.            uint64_t FlashWord[4];
  44. 44.            
  45. 45.            memcpy((char *)FlashWord, _ucpSrc, 32);
  46. 46.            _ucpSrc += 32;
  47. 47.            
  48. 48.            if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, _ulFlashAddr,
  49. 49.                                      (uint64_t)((uint32_t)FlashWord)) == HAL_OK)
  50. 50.            {
  51. 51.                _ulFlashAddr = _ulFlashAddr + 32; /* 递增,操作下一个256bit */               
  52. 52.            }        
  53. 53.            else
  54. 54.            {
  55. 55.                goto err;
  56. 56.            }
  57. 57.        }
  58. 58.        
  59. 59.        /* 长度不是32字节整数倍 */
  60. 60.        if (_ulSize % 32)
  61. 61.        {
  62. 62.            uint64_t FlashWord[4];
  63. 63.            
  64. 64.            FlashWord[0] = 0;
  65. 65.            FlashWord[1] = 0;
  66. 66.            FlashWord[2] = 0;
  67. 67.            FlashWord[3] = 0;
  68. 68.            memcpy((char *)FlashWord, _ucpSrc, _ulSize % 32);
  69. 69.            if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, _ulFlashAddr,
  70. 70.                                               (uint64_t)((uint32_t)FlashWord)) == HAL_OK)
  71. 71.            {
  72. 72.                ; // _ulFlashAddr = _ulFlashAddr + 32;
  73. 73.               
  74. 74.            }        
  75. 75.            else
  76. 76.            {
  77. 77.                goto err;
  78. 78.            }
  79. 79.        }
  80. 80.        
  81. 81.          /* Flash 加锁,禁止写Flash控制寄存器 */
  82. 82.          HAL_FLASH_Lock();
  83. 83.   
  84. 84.          __set_PRIMASK(0);          /* 开中断 */
  85. 85.   
  86. 86.        return 0;
  87. 87.        
  88. 88.    err:
  89. 89.          /* Flash 加锁,禁止写Flash控制寄存器 */
  90. 90.          HAL_FLASH_Lock();
  91. 91.   
  92. 92.          __set_PRIMASK(0);          /* 开中断 */
  93. 93.   
  94. 94.        return 1;
  95. 95.    }
复制代码

关于此函数有几个要点:

  第1个参数必须32字节对齐,即要编程的Flash地址对32求余为0。
  第3个参数必须是32字节的整数倍,长度不是32字节整数倍时,最后几个字节补0写入。
  第29行,函数bsp_CmpCpuFlash放在这里只有一个作用,判断将要写入的数据是否已经在内部Flash存在,如果已经存在,无需重复写入,直接返回。
  第36行,做了一个关中断操作,这里有个知识点要给大家普及下,由于H7的BANK1和BANK2是独立的,当前正在擦除的扇区所处的BANK会停止所有在此BANK执行的程序,包含中断也会停止执行。比如当前的应用程序都在BANK1,如果要擦除或者编程BANK2,是不会不影响BANK1里面执行的程序。这里安全起见加上了开关中断。
  第41到57行,先将32字节整数倍的数据通过函数HAL_FLASH_Program编程,此函数每次可以固定编程32字节数据。
  第60到79行,将剩余不足32字节的数据补0,凑齐32字节编程。
71.2.3 内部Flash读取的实现
内部Flash数据读取比较简单,采用总线方式读取,跟访问内部RAM是一样的。比如要读取地址

0x08100000里面的一个32bit变量,我们就可以:

变量 = *(uint32_t *)(0x08100000)

为了方便起见,也专门准备了一个函数:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_ReadCpuFlash
  4. *    功能说明: 读取CPU Flash的内容
  5. *    形    参:  _ucpDst : 目标缓冲区
  6. *             _ulFlashAddr : 起始地址
  7. *             _ulSize : 数据大小(单位是字节)
  8. *    返 回 值: 0=成功,1=失败
  9. *********************************************************************************************************
  10. */
  11. uint8_t bsp_ReadCpuFlash(uint32_t _ulFlashAddr, uint8_t *_ucpDst, uint32_t _ulSize)
  12. {
  13.     uint32_t i;

  14.     if (_ulFlashAddr + _ulSize > CPU_FLASH_BASE_ADDR + CPU_FLASH_SIZE)
  15.     {
  16.         return 1;
  17.     }

  18.     /* 长度为0时不继续操作,否则起始地址为奇地址会出错 */
  19.     if (_ulSize == 0)
  20.     {
  21.         return 1;
  22.     }

  23.     for (i = 0; i < _ulSize; i++)
  24.     {
  25.         *_ucpDst++ = *(uint8_t *)_ulFlashAddr++;
  26.     }

  27.     return 0;
  28. }
复制代码


71.2.4 告诉编译器使用的扇区(重要)
使用内部Flash模拟EEPROM切不可随意定义一个扇区使用。因为编译器并不知道用户使用了这个扇区,导致应用程序也会编程到此扇区里面,所以就需要告诉编译器。

告诉MDK的方法如下(0x0810 0000是H7的BANK2首地址):

  1. const uint8_t para_flash_area[128*1024] __attribute__((at(0x08100000)));
复制代码

告诉IAR的方法如下:

  1. #pragma location=0x08100000
  2. const uint8_t para_flash_area[128*1024];
复制代码

这里有两点特别注意:

  模拟EEPROM的扇区可以定义到从第2个扇区开始的任何扇区,但不可以定义到首扇区,因为这个扇区是默认的boot启动地址。
  如果应用程序不大的话,不推荐定义到末尾扇区,以MDK为例,定义到末尾扇区后,会导致整个Flash空间都被使用,从而让程序下载下载时间变长。
71.3 模拟EEPROM板级支持包(bsp_cpu_flash.c)
模拟EEPROM的驱动文件bsp_cpu_flash.c主要实现了如下几个API供用户调用:

  bsp_GetSector
  bsp_ReadCpuFlash
  bsp_CmpCpuFlash
  bsp_EraseCpuFlash
  bsp_WriteCpuFlash
71.3.1 函数bsp_GetSector
函数原型:

  1. uint32_t bsp_GetSector(uint32_t Address)
复制代码

函数描述:

此函数主要用于获取给定地址所处的扇区。

函数参数:

  第1个参数是用户给定的地址。
  返回值,范围FLASH_SECTOR_0到FLASH_SECTOR_7。
71.3.2 函数bsp_ReadCpuFlash
函数原型:

  1. uint8_t bsp_ReadCpuFlash(uint32_t _ulFlashAddr, uint8_t *_ucpDst, uint32_t _ulSize)
复制代码

函数描述:

此函数用于从内部Flash读取数据

函数参数:

  第1个参数读取的起始地址。
  第2个参数是读取数据的存储地址
  第3个参数是读取数据的大小,单位字节。
  返回值,0表示成功,1表示失败。
71.3.3 函数bsp_CmpCpuFlash
函数原型:

uint8_t bsp_CmpCpuFlash(uint32_t _ulFlashAddr, uint8_t *_ucpBuf, uint32_t _ulSize)

函数描述:

要编程的数据是否在内部Flash已经存在。

函数参数:

  第1个参数是内部Flash地址。
  第2个参数是缓冲区地址。
  第3个参数是数据大小,单位字节。
  返回值:
FLASH_IS_EQU               0   Flash内容和待写入的数据相等,不需要擦除和写操作。

FLASH_REQ_WRITE          1     Flash不需要擦除,直接写。

FLASH_REQ_ERASE          2     Flash需要先擦除,再写。

FLASH_PARAM_ERR         3     函数参数错误。

71.3.4 函数bsp_EraseCpuFlash
函数原型:

uint8_t bsp_EraseCpuFlash(uint32_t _ulFlashAddr)

函数描述:

此函数用于擦除一个扇区,大小128KB

函数参数:

  第1个参数要擦除的扇区地址,可以是此扇区范围内的任意值,一般填扇区首地址即可。
  返回值:
HAL_OK        = 0x00

HAL_ERROR    = 0x01

HAL_BUSY      = 0x02

HAL_TIMEOUT  = 0x03

71.3.5 函数bsp_WriteCpuFlash
函数原型:

uint8_t bsp_WriteCpuFlash(uint32_t _ulFlashAddr, uint8_t *_ucpSrc, uint32_t _ulSize)

函数描述:

此函数用于编程数据到内部Flash。

函数参数:

  第1个参数是要编程的内部Flash地址。
  第2个参数是数据缓冲区地址。
  第3个参数是数据大小,单位字节。
  返回值,0-成功,1-数据长度或地址溢出,2-写Flash出错(估计Flash寿命到)。
注意事项:

  第1个参数必须32字节对齐,即要编程的Flash地址对32求余为0。
  第3个参数必须是32字节的整数倍,长度不是32字节整数倍时,此函数会将几个字节补0写入
71.4 模拟EEPROM驱动移植和使用
模拟EEPROM移植步骤如下:

  第1步:复制bsp_cpu_flash.c和bsp_cpu_flash.h到自己的工程目录,并添加到工程里面。
  第2步:Flash驱动文件主要用到HAL库的Flash驱动文件,简单省事些可以添加所有HAL库C源文件进来。
  第3步,应用方法看本章节配套例子即可。
71.5 实验例程设计框架
通过程序设计框架,让大家先对配套例程有一个全面的认识,然后再理解细节,本次实验例程的设计框架如下:

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


  第1阶段,上电启动阶段:

这部分在第14章进行了详细说明。
  第2阶段,进入main函数:

  第1部分,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器和LED。
  第2部分,应用程序设计部分,实现内部Flash模拟EEPROM。
71.6 实验例程说明(MDK)
配套例子:

V7-049_内部Flash模拟EEPROM

实验目的:

学习内部Flash模拟EEPROM。
实验内容:

使用内部Flash模拟EEPROM,务必告诉编译要使用的存储空间,防止这个空间存入了程序。
对于同一个地址空间,仅支持一次编程(不推荐二次编程,即使是将相应bit由数值1编程0)。
只能对已经擦除的空间做编程,擦除1个扇区是128KB。
H7的Flash编程时,务必保证要编程的地址是32字节对齐的,即此地址对32求余为0。
并且编程的数据必须32字节整数倍,函数bsp_WriteCpuFlash对字节数不够32字节整数倍的情况自动补0。

实验操作:

K1键按下,将8bit,16bit和32bit数据写入到内部Flash。
K2键按下,将结构体数据写入到内部Flash。
上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


程序设计:

  系统栈大小分配:

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


  RAM空间用的DTCM:

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.     /* 配置MPU */
  12.     MPU_Config();

  13.     /* 使能L1 Cache */
  14.     CPU_CACHE_Enable();

  15.     /*
  16.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
  17.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
  18.        - 设置NVIV优先级分组为4。
  19.      */
  20.     HAL_Init();

  21.     /*
  22.        配置系统时钟到400MHz
  23.        - 切换使用HSE。
  24.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
  25.     */
  26.     SystemClock_Config();

  27.     /*
  28.        Event Recorder:
  29.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
  30.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
  31.     */   
  32. #if Enable_EventRecorder == 1  
  33.     /* 初始化EventRecorder并开启 */
  34.     EventRecorderInitialize(EventRecordAll, 1U);
  35.     EventRecorderStart();
  36. #endif

  37. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
  38.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
  39.     bsp_InitTimer();      /* 初始化滴答定时器 */
  40.     bsp_InitLPUart();    /* 初始化串口 */
  41.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
  42.     bsp_InitLed();        /* 初始化LED */   
  43.     bsp_InitExtSDRAM(); /* 初始化SDRAM */
  44. }
复制代码


  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: MPU_Config
  4. *    功能说明: 配置MPU
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void MPU_Config( void )
  10. {
  11.     MPU_Region_InitTypeDef MPU_InitStruct;

  12.     /* 禁止 MPU */
  13.     HAL_MPU_Disable();

  14.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
  15.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  16.     MPU_InitStruct.BaseAddress      = 0x24000000;
  17.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  18.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  19.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  20.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
  21.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  22.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  23.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  24.     MPU_InitStruct.SubRegionDisable = 0x00;
  25.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  26.     HAL_MPU_ConfigRegion(&MPU_InitStruct);


  27.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
  28.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  29.     MPU_InitStruct.BaseAddress      = 0x60000000;
  30.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
  31.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  32.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  33.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
  34.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  35.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
  36.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
  37.     MPU_InitStruct.SubRegionDisable = 0x00;
  38.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  39.     HAL_MPU_ConfigRegion(&MPU_InitStruct);

  40.     /*使能 MPU */
  41.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
  42. }

  43. /*
  44. *********************************************************************************************************
  45. *    函 数 名: CPU_CACHE_Enable
  46. *    功能说明: 使能L1 Cache
  47. *    形    参: 无
  48. *    返 回 值: 无
  49. *********************************************************************************************************
  50. */
  51. static void CPU_CACHE_Enable(void)
  52. {
  53.     /* 使能 I-Cache */
  54.     SCB_EnableICache();

  55.     /* 使能 D-Cache */
  56.     SCB_EnableDCache();
  57. }
复制代码



  每10ms调用一次蜂鸣器处理:

蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_RunPer10ms
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_RunPer10ms(void)
  11. {
  12.     bsp_KeyScan10ms();
  13. }
复制代码


  主功能:

主程序实现如下操作:

  启动一个自动重装软件定时器,每100ms翻转一次LED2。
  K1键按下,将8bit,16bit和32bit数据写入到内部Flash。
  K2键按下,将结构体数据写入到内部Flash。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: c程序入口
  5. *    形    参: 无
  6. *    返 回 值: 错误代码(无需处理)
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.     uint8_t ucKeyCode;    /* 按键代码 */
  12.     uint8_t  ucTest, *ptr8;
  13.     uint16_t uiTest, *ptr16;
  14.     uint32_t ulTest, *ptr32;
  15.     PARAM_T tPara, *paraptr;


  16.     /* 初始化数据 */
  17.     tPara.Baud485 = 0x5555AAAA;
  18.     tPara.ParamVer = 0x99;
  19.     tPara.ucBackLight = 0x7788;
  20.     tPara.ucRadioMode = 99.99f;


  21.     bsp_Init();        /* 硬件初始化 */
  22.     PrintfLogo();    /* 打印例程名称和版本等信息 */
  23.     PrintfHelp();    /* 打印操作提示 */

  24.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
  25.     while (1)
  26.     {
  27.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

  28.         /* 判断定时器超时时间 */
  29.         if (bsp_CheckTimer(0))   
  30.         {
  31.             /* 每隔100ms 进来一次 */  
  32.             bsp_LedToggle(2);
  33.         }

  34.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
  35.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  36.         if (ucKeyCode != KEY_NONE)
  37.         {
  38.             switch (ucKeyCode)
  39.             {
  40.                 case KEY_DOWN_K1:            /* K1键按下,将8bit,16bit和32bit数据写入到内部Flash */

  41.                 /*
  42.                  1、对于同一个地址空间,仅支持一次编程(不推荐二次编程,即使是将相应bit由数值1编程0)。
  43.                  2、只能对已经擦除的空间做编程,擦除1个扇区是128KB。
  44.                  3、H7的Flash编程时,务必保证要编程的地址是32字节对齐的,即此地址对32求余为0。并且编
  45. 程的数据必须32字节整数倍。函数bsp_WriteCpuFlash对字节数不够32字节整数倍的情况自动补
  46. 0。
  47.                 */
  48.                      /* 擦除扇区 */
  49.                     bsp_EraseCpuFlash((uint32_t)para_flash_area);

  50.                     ucTest = 0xAA;
  51.                     uiTest = 0x55AA;
  52.                     ulTest = 0x11223344;

  53.                     /* 扇区写入数据 */
  54.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*0,  (uint8_t *)&ucTest,
  55. sizeof(ucTest));
  56.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*1,  (uint8_t *)&uiTest,
  57. sizeof(uiTest));
  58.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*2,  (uint8_t *)&ulTest,
  59. sizeof(ulTest));               

  60.                     /* 读出数据并打印 */
  61.                     ptr8  = (uint8_t  *)(para_flash_area + 32*0);
  62.                     ptr16 = (uint16_t *)(para_flash_area + 32*1);
  63.                     ptr32 = (uint32_t *)(para_flash_area + 32*2);

  64.                     printf("写入数据:ucTest = %x, uiTest = %x, ulTest = %x\r\n", ucTest, uiTest, ulTest);
  65.                     printf("读取数据:ptr8 = %x, ptr16 = %x, ptr32 = %x\r\n", *ptr8, *ptr16, *ptr32);

  66.                     break;

  67.                 case KEY_DOWN_K2:            /* K2键按下, 将结构体数据写入到内部Flash */
  68.                     /* 擦除扇区 */
  69.                     bsp_EraseCpuFlash((uint32_t)para_flash_area);

  70.                     /* 扇区写入数据 */
  71.                     bsp_WriteCpuFlash((uint32_t)para_flash_area,  (uint8_t *)&tPara, sizeof(tPara));            

  72.                     /* 读出数据并打印 */
  73.                     paraptr  = (PARAM_T  *)((uint32_t)para_flash_area);


  74.                     printf("写入数据:Baud485=%x, ParamVer=%x, ucBackLight=%x, ucRadioMode=%f\r\n",
  75.                                                                        tPara.Baud485,
  76.                                                                         tPara.ParamVer,
  77.                                                                         tPara.ucBackLight,
  78.                                                                    paraptr->ucRadioMode);

  79.                     printf("读取数据:Baud485=%x, ParamVer=%x, ucBackLight=%x, ucRadioMode=%f\r\n",
  80.                                                                         paraptr->Baud485,
  81.                                                                             paraptr->ParamVer,
  82.                                                                            paraptr->ucBackLight,
  83.                                                                           paraptr->ucRadioMode);
  84.                     break;               
  85.                 default:
  86.                     /* 其它的键值不处理 */
  87.                     break;
  88.             }
  89.         }
  90.     }
  91. }
复制代码


71.7 实验例程说明(IAR)
配套例子:

V7-049_内部Flash模拟EEPROM

实验目的:

学习内部Flash模拟EEPROM。
实验内容:

使用内部Flash模拟EEPROM,务必告诉编译要使用的存储空间,防止这个空间存入了程序。
对于同一个地址空间,仅支持一次编程(不推荐二次编程,即使是将相应bit由数值1编程0)。
只能对已经擦除的空间做编程,擦除1个扇区是128KB。
H7的Flash编程时,务必保证要编程的地址是32字节对齐的,即此地址对32求余为0。
并且编程的数据必须32字节整数倍,函数bsp_WriteCpuFlash对字节数不够32字节整数倍的情况自动补0。

实验操作:

K1键按下,将8bit,16bit和32bit数据写入到内部Flash。
K2键按下,将结构体数据写入到内部Flash。
上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1。



程序设计:

  系统栈大小分配:

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


  RAM空间用的DTCM:

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png


  硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.     /* 配置MPU */
  12.     MPU_Config();

  13.     /* 使能L1 Cache */
  14.     CPU_CACHE_Enable();

  15.     /*
  16.        STM32H7xx HAL 库初始化,此时系统用的还是H7自带的64MHz,HSI时钟:
  17.        - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
  18.        - 设置NVIV优先级分组为4。
  19.      */
  20.     HAL_Init();

  21.     /*
  22.        配置系统时钟到400MHz
  23.        - 切换使用HSE。
  24.        - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
  25.     */
  26.     SystemClock_Config();

  27.     /*
  28.        Event Recorder:
  29.        - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
  30.        - 默认不开启,如果要使能此选项,务必看V7开发板用户手册第xx章
  31.     */   
  32. #if Enable_EventRecorder == 1  
  33.     /* 初始化EventRecorder并开启 */
  34.     EventRecorderInitialize(EventRecordAll, 1U);
  35.     EventRecorderStart();
  36. #endif

  37. bsp_InitDWT();      /* 初始化DWT时钟周期计数器 */      
  38.     bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
  39.     bsp_InitTimer();      /* 初始化滴答定时器 */
  40.     bsp_InitLPUart();    /* 初始化串口 */
  41.     bsp_InitExtIO();    /* 初始化FMC总线74HC574扩展IO. 必须在 bsp_InitLed()前执行 */   
  42.     bsp_InitLed();        /* 初始化LED */   
  43.     bsp_InitExtSDRAM(); /* 初始化SDRAM */
  44. }
复制代码


  MPU配置和Cache配置:

数据Cache和指令Cache都开启。配置了AXI SRAM区(本例子未用到AXI SRAM)和FMC的扩展IO区。

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: MPU_Config
  4. *    功能说明: 配置MPU
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void MPU_Config( void )
  10. {
  11.     MPU_Region_InitTypeDef MPU_InitStruct;

  12.     /* 禁止 MPU */
  13.     HAL_MPU_Disable();

  14.     /* 配置AXI SRAM的MPU属性为Write back, Read allocate,Write allocate */
  15.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  16.     MPU_InitStruct.BaseAddress      = 0x24000000;
  17.     MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
  18.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  19.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  20.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
  21.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  22.     MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
  23.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
  24.     MPU_InitStruct.SubRegionDisable = 0x00;
  25.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  26.     HAL_MPU_ConfigRegion(&MPU_InitStruct);


  27.     /* 配置FMC扩展IO的MPU属性为Device或者Strongly Ordered */
  28.     MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
  29.     MPU_InitStruct.BaseAddress      = 0x60000000;
  30.     MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;   
  31.     MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  32.     MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
  33.     MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;   
  34.     MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
  35.     MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
  36.     MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
  37.     MPU_InitStruct.SubRegionDisable = 0x00;
  38.     MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

  39.     HAL_MPU_ConfigRegion(&MPU_InitStruct);

  40.     /*使能 MPU */
  41.     HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
  42. }

  43. /*
  44. *********************************************************************************************************
  45. *    函 数 名: CPU_CACHE_Enable
  46. *    功能说明: 使能L1 Cache
  47. *    形    参: 无
  48. *    返 回 值: 无
  49. *********************************************************************************************************
  50. */
  51. static void CPU_CACHE_Enable(void)
  52. {
  53.     /* 使能 I-Cache */
  54.     SCB_EnableICache();

  55.     /* 使能 D-Cache */
  56.     SCB_EnableDCache();
  57. }
复制代码



  每10ms调用一次蜂鸣器处理:

蜂鸣器处理是在滴答定时器中断里面实现,每10ms执行一次检测。

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_RunPer10ms
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
  5. *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
  6. *    形    参: 无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_RunPer10ms(void)
  11. {
  12.     bsp_KeyScan10ms();
  13. }
复制代码


  主功能:

主程序实现如下操作:

启动一个自动重装软件定时器,每100ms翻转一次LED2。
K1键按下,将8bit,16bit和32bit数据写入到内部Flash。
K2键按下,将结构体数据写入到内部Flash。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: c程序入口
  5. *    形    参: 无
  6. *    返 回 值: 错误代码(无需处理)
  7. *********************************************************************************************************
  8. */
  9. int main(void)
  10. {
  11.     uint8_t ucKeyCode;    /* 按键代码 */
  12.     uint8_t  ucTest, *ptr8;
  13.     uint16_t uiTest, *ptr16;
  14.     uint32_t ulTest, *ptr32;
  15.     PARAM_T tPara, *paraptr;


  16.     /* 初始化数据 */
  17.     tPara.Baud485 = 0x5555AAAA;
  18.     tPara.ParamVer = 0x99;
  19.     tPara.ucBackLight = 0x7788;
  20.     tPara.ucRadioMode = 99.99f;


  21.     bsp_Init();        /* 硬件初始化 */
  22.     PrintfLogo();    /* 打印例程名称和版本等信息 */
  23.     PrintfHelp();    /* 打印操作提示 */

  24.     bsp_StartAutoTimer(0, 100);    /* 启动1个100ms的自动重装的定时器 */
  25.     while (1)
  26.     {
  27.         bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */

  28.         /* 判断定时器超时时间 */
  29.         if (bsp_CheckTimer(0))   
  30.         {
  31.             /* 每隔100ms 进来一次 */  
  32.             bsp_LedToggle(2);
  33.         }

  34.         /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
  35.         ucKeyCode = bsp_GetKey();    /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
  36.         if (ucKeyCode != KEY_NONE)
  37.         {
  38.             switch (ucKeyCode)
  39.             {
  40.                 case KEY_DOWN_K1:            /* K1键按下,将8bit,16bit和32bit数据写入到内部Flash */

  41.                 /*
  42.                  1、对于同一个地址空间,仅支持一次编程(不推荐二次编程,即使是将相应bit由数值1编程0)。
  43.                  2、只能对已经擦除的空间做编程,擦除1个扇区是128KB。
  44.                  3、H7的Flash编程时,务必保证要编程的地址是32字节对齐的,即此地址对32求余为0。并且编
  45. 程的数据必须32字节整数倍。函数bsp_WriteCpuFlash对字节数不够32字节整数倍的情况自动补
  46. 0。
  47.                 */
  48.                      /* 擦除扇区 */
  49.                     bsp_EraseCpuFlash((uint32_t)para_flash_area);

  50.                     ucTest = 0xAA;
  51.                     uiTest = 0x55AA;
  52.                     ulTest = 0x11223344;

  53.                     /* 扇区写入数据 */
  54.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*0,  (uint8_t *)&ucTest,
  55. sizeof(ucTest));
  56.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*1,  (uint8_t *)&uiTest,
  57. sizeof(uiTest));
  58.                     bsp_WriteCpuFlash((uint32_t)para_flash_area + 32*2,  (uint8_t *)&ulTest,
  59. sizeof(ulTest));               

  60.                     /* 读出数据并打印 */
  61.                     ptr8  = (uint8_t  *)(para_flash_area + 32*0);
  62.                     ptr16 = (uint16_t *)(para_flash_area + 32*1);
  63.                     ptr32 = (uint32_t *)(para_flash_area + 32*2);

  64.                     printf("写入数据:ucTest = %x, uiTest = %x, ulTest = %x\r\n", ucTest, uiTest, ulTest);
  65.                     printf("读取数据:ptr8 = %x, ptr16 = %x, ptr32 = %x\r\n", *ptr8, *ptr16, *ptr32);

  66.                     break;

  67.                 case KEY_DOWN_K2:            /* K2键按下, 将结构体数据写入到内部Flash */
  68.                     /* 擦除扇区 */
  69.                     bsp_EraseCpuFlash((uint32_t)para_flash_area);

  70.                     /* 扇区写入数据 */
  71.                     bsp_WriteCpuFlash((uint32_t)para_flash_area,  (uint8_t *)&tPara, sizeof(tPara));            

  72.                     /* 读出数据并打印 */
  73.                     paraptr  = (PARAM_T  *)((uint32_t)para_flash_area);


  74.                     printf("写入数据:Baud485=%x, ParamVer=%x, ucBackLight=%x, ucRadioMode=%f\r\n",
  75.                                                                        tPara.Baud485,
  76.                                                                         tPara.ParamVer,
  77.                                                                         tPara.ucBackLight,
  78.                                                                    paraptr->ucRadioMode);

  79.                     printf("读取数据:Baud485=%x, ParamVer=%x, ucBackLight=%x, ucRadioMode=%f\r\n",
  80.                                                                         paraptr->Baud485,
  81.                                                                             paraptr->ParamVer,
  82.                                                                            paraptr->ucBackLight,
  83.                                                                           paraptr->ucRadioMode);
  84.                     break;               
  85.                 default:
  86.                     /* 其它的键值不处理 */
  87.                     break;
  88.             }
  89.         }
  90.     }
  91. }
复制代码



71.8 总结
本章节就为大家讲解这么多, 实际应用中的注意事项比较多,应用到项目之前务必实际测试熟悉下。

aHR0cHM6Ly9pbWcyMDIwLmNuYmxvZ3MuY29tL2Jsb2cvMTM3OTEwNy8yMDIwMDMvMTM3OTEwNy0yMDIw.png
收藏 1 评论0 发布时间:2021-11-2 23:56

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版