如何将spi闪存添加到STM32CubeProgrammer的外部加载器中第2部分
如何将SPI闪存添加到STM32CubeProgram的外部加载程序中?欢迎回到我们系列文章的第2部分,该部分解释了如何在STM32CubeProgrammer的ExternalLoader部分和STM32C中添加自定义SPI闪存。。。
如何将SPI闪存添加到STM32CubeProgram的外部加载程序中?
欢迎回到我们系列文章的第2部分,该部分解释了如何在STM32CubeProgram的ExternalLoader部分和STM32CubeIDE中添加自定义SPI闪存。
在第一部分我们介绍了代码编辑所需的先决条件和链接器脚本-编辑的完整文件在本文末尾介绍。接下来的步骤将显示如何创建外部加载器的必要文件。
首先,创建一个文件,该文件将接收所有必要的ExternalLoader函数。在Core->Src中,用鼠标右键单击,转到New->Source File并创建一个名为“Loader_Src.c”的文件。


该文件需要接收以下特定格式的函数(相同的名称、类型、参数和返回值)。STM32CubeProgram将使用这些功能来管理外部内存,您应该根据功能用途将内存驱动程序放置在其中。以下是需要填充的函数:
int写入(uint32_t地址,uint32_t大小,uint8_t*缓冲区);int Read(uint32_t地址,uint32_t大小,uint8_t*缓冲区);int扇区擦除(uint32_t擦除起始地址,uint32_t擦除结束地址);int MassErase(无效);您也可以添加以下功能,但它们是可选的:
uint32-t校验和(uint32_t起始地址,uint32_t大小,uint22_t初始化值);int验证(uint32_t内存地址,uint32_t内存缓冲区地址,uint32_t大小);函数的返回值需要为“0”以表示操作失败,或为“1”表示操作成功,您可以创建这些定义来帮助您管理:
#定义LOADER_OK 0x1#定义LOADER_FAIL 0x0我们需要包括HAL驱动程序和一些初始化功能来管理外围设备,因此在代码中添加以下行:
#include“main.h”#include“gpio.h”extern void SystemClock_Config(void);外部无效MX_SPI2_Init(无效);外部无效MX_GPIO_Init(无效);我们需要在此文件中创建的剩余函数是初始化链接器脚本中提到的入口点将逐步调用的函数:
int Init(void){SystemInit();SCB->VTOR=0x20000000|0x200;HAL_DInit(我们需要创建的另外两个文件是“Dev_Info.c”和“Dev_Iinfo.h”。这些文件将管理STM32CubeProgrammer用于读取、编程和擦除设备的标头内存信息——STM32CuboProgrammer安装文件夹中提供了一些示例,UM2237中也有这些示例。所以,让我们从头开始创建这些文件。在Core->Inc文件夹中创建“Dev_Inf.h”,在Core->Src中创建“Dev_Inf.c”。

#define MCU_FLASH 1#define NAND_FLASH 2#define NOR_FLASH 3#define SRAM 4#define PSRAM 5#define PC_CARD 6#define SPI_FLASH 7#define I2C_FLASH 8#define SDRAM 9#define I2C_EEPROM 10#define SECTOR_NUM 10//最大扇区类型数struct DeviceSectors{unsigned long SectorNum;//无符号长扇区数SectorSize;//扇区大小以字节为单位};struct StorageInfo{char DeviceName[100];//设备名称和描述unsigned short DeviceType;//设备类型:ONCHIP、EXT8BIT、EXT16BIT…unsigned long DeviceStartAddress;//默认设备起始地址unsigned long DeviceSize;//设备总大小unsigned leng PageSize;//编程页面大小unsignedchar EraseValue;//擦除内存struct DeviceSectors扇区的内容[SECTOR_NUM];};在“Dev_Inf.c”中,我们只需要声明和初始化该结构,并将其存储在RAM的.Dev_info部分(为此,在结构声明中放置__attribute__(部分(“.Dev_info”))。结构的变量数据应更改为与内存兼容。
/**Dev_Inf.c**/#include“Dev_Inf.h”/*此结构包含ST-LINK实用程序用于编程和擦除设备的信息*/#如果已定义(__ICCARM__)__root struct StorageInfo const StorageInfo={#else struct StorageInfo__attribute__((section(“.Dev_info”))/*const*/StorageInfo={#endif“W25Q64_STM32G070R”,//设备名称+版本号SPI_FLASH,//设备类型0x00000000,//设备起始地址MEMORY_FLASH_SIZE,//以字节为单位的设备大小MEMORY_PAGE_SIZE,//编程页面大小0xFF,//擦除内存的初始内容//指定扇区的大小和地址(查看下面的示例){0x80,//扇区号,0x1000},//扇区大小{0x00000000、0x00000000}}};当外部加载器存在于结构数据上时,该结构数据由多维数据集程序员显示。

在构建项目时,函数(包括内存驱动程序函数)应位于RAM_D1内存区域中的.text节内,并创建了一个名为.Dev_Info的带有StorageInfo结构的节。



在设置中,转到C/C++构建->构建步骤,并在构建后步骤的命令文本框中添加以下文本:
cp“${BuildArtifactFileBaseName}.elf”“../STM32G070_W25Q64.stldr”


现在,驱动程序已经准备好使用了,我们需要将其导入STM32CubeProgrammer中。将STM32G070_W25Q64.stldr文件(位于根项目文件夹中)复制到STM32CubeProgrammer的External Loader文件夹中。找到这个文件的一个快速方法是用鼠标右键点击它,然后进入“显示在->系统资源管理器”,这样文件资源管理器应该在文件所在的文件夹中打开。

然后,将*.stldr文件复制并粘贴到STM32CubeProgramer的External Loader文件夹中,该文件夹位于:
“C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgramer\bin\ExternalLoader”(此路径可能会根据您的安装路径而更改,另一种访问方法是用鼠标右键单击STM32CubeProgramer桌面快捷方式,然后转到“打开文件位置”,在打开的位置搜索ExternalLoader文件夹)。


回到“内存和文件版本”,搜索外部内存的地址,如果过程顺利,你会看到存储在上面的内容。在这种情况下,内存中存储了一个从0x00000000地址开始的序列。

如果所有步骤都做得正确,则外部加载器运行正常,现在您可以直接读取、编程和擦除外部存储器。祝贺

参考文献:
- NUCLEO-G007RB-STM32Nucleo-64开发板,带STM32G007RB MCU,支持Arduino和ST morp。。。
- STM32 Nucleo-64板(MB1360)-用户手册
- Arm®Cortex®-M0+32位MCU、128 KB闪存、36 KB RAM、4个USART、定时器、ADC、DAC、通信I/F、1.7-3。。。。
- STM32G0x0基于Arm®的高级32位MCU-参考手册
- STM32CubeProg-适用于所有STM32的STM32Cube编程器软件
- STM32CubeProgrammer软件说明-用户手册
链接器文件代码:
/*入口点*/Entry(Init)/*为Loader代码和设备信息生成2段*/PHDRS{Loader PT_LOAD;SgInfo PT_LOAD;}/*用户模式堆栈的最高地址*/_estack=ORIGIN(RAM_D1)+LENGTH(RAM _D1);/*“RAM”RAM型内存结束*/_Min_Heap_Size=0x200;/*所需堆数量*/_Min_Stack_Size=0x400;/*所需堆栈数量*/*内存定义*/MEMORY{RAM_D1(xrw):ORIGIN=0x20000004,LENGTH=36K-4}/*节*/Sections{.irs_vector:{.=.+0x1FC;/*isr矢量偏移量*/.=ALIGN(4);KEEP(*(.irs_vector))/*启动代码*/.=ALIGN(4)}>RAM_D1:加载器.ARM.extab:{*(.ARM.extab*.gnu.linkonce.armextab.*)}>RAM_D1.ARM:{__exidx_start=.;*(.ARM.exidx*)__exidx_end=.;}>RAM_D1:加载程序.preit_array:{PROVIDE_HDDEN(__prenit_array_start=.);KEEP(*(.preit_arry*))PROVIDE_HIDDEN(__ prenit_aarray_end=.));}>RAM_D1:加载程序.init_array:{PROVIDE_HDDEN(__init_array_start=._HIDDEN(__fini_array_start=。);KEEP(*(排序(.fini_array.*))KEEP((*(.fini_array*))PROVID_HIDDEN(__fini_array_end=.);}>RAM_D1:Loader/*启动时用于初始化数据*/_sidat=LOADADDR(.data);/*已将数据段初始化为“RAM”RAM类型内存*/.data:{.=ALIGN(4);_sdata=.;/*在数据开始处创建全局符号*/*(.data)/*.data节*/*(_data*)/*.data*节*/.=ALIGN(4),_edata=.;/*在数据结束处定义全局符号*/}>RAM_D1:加载器/*未初始化的数据节*/对齐(4)。bss:{/*启动时使用此项初始化.bss部分*/_sbss=.;/*在bss开始处定义全局符号*/__bss_start__=_sbss;*(.bss)*(.bs*)*(COMMON).=ALIGN(4);_ebss=..;/*定义bss结束处的全局符号*/___bss_end__=_ebss;}>RAM_D1:加载程序/*将程序代码和其他数据加载到RAM类型内存*/.text:{.=ALIGN(4),*(.text)/*.text部分(代码)*/*(.text*)/*.text*节(代码对齐(4)_etext=.;/*在代码末尾定义全局符号*/}>RAM_D1:Loader.Dev_Info:{__Dev_Info_START=.;*(.Dev_Info*)KEEP(*(.DDev_Info))__Dev_Info _end=.;}>RAM _D1:SgInfo/*将常量数据放入“FLASH”Rom型内存*/.rodata:{.=ALIGN(4);*(.rodata)/*.rodata段(常量、字符串等)*/*(.roduta*RAM_D1:装载机/*User_heap_back部分,用于检查是否有足够的“RAM”RAM类型内存剩余*/_user_heap_stack:{.=ALIGN(8);PROVIDE(end=.);PRO维德(_end=