本帖最后由 老叶子 于 2025-9-26 14:33 编辑
背景导入 需求很容易理解,制作固件时,并不是程序写完就完事了,程序运行还需要外部资源,比如字库、图片、音频数据等。这些资源通常是只读的,而且很大,所以它们一个合适的归宿就是spiflash。 我想了三种方法烧录这些资源 - 先烧录程序,再通过ymodem、http之类的协议将资源下载到spiflash中
- 通过jflash将程序与资源一起烧录到板子内
- 通过JFlashSPI将资源烧录到板子上的spiflash中
JFlashSPI也是segger提供的一个小工具,将板子上spi引脚与jlink连接后,可以直接识别、烧录spiflash 方法2最好,一步到位。方法2也是最麻烦的,需要为板子写下载算法(flashload)程序 下载算法的知识有点冷门,但也有教程,搜了搜发现内容与安富莱的教程类似,所以略过中间商,直接看安富莱的就行《STM32H7 的 SPI 总线应用之 SPI Flash的 MDK 下载算法制作》、《STM32H7 的 SPI 总线应用之 SPI Flash的STM32CubeProg下载算法制作》。通过本文标题也知道,这两篇文章都与我的需求不太相符,但众多教程里又说了,MDK的下载算法与segger的下载算法通用,这句话确实没错,甚至segger也承认了 device support kit:segger提供的数据烧录方案(付费),segger flashload是其中的一部分 segger flashload:segger最新推出的下载算法,替代open flashload open flashload(OFL):segger以前的下载算法,但segger为了推广sfl,将ofl的资料全删了 cmsis loaders:arm推出的下载算法,即MDK支持的下载算法。 经过测试,直接将MDK中的FLM文件应用于jflash是可以的,但将安富莱例程应用于jflash时,又报错了,将安富莱例程应用于MDK时,又可以使用,那么,肯定有地方出了差错。 将MDK下载算法应用于jflash的方法请参考以下几个链接: 以7.62版本为界,jflash添加下载算法的方式有了改变。7.62版本后C:\Users\<USER>\AppData\Roaming\SEGGER目录下默认没有JLinkDevices目录,用户新建一个即可。除JLinkDevices目录外,JLinkDevices目录内文件夹、xml、下载文件的命名没有要求。关于xml文件的语法,除上述链接外,还可以参考上述的segger DSK文档 OFL制作

最后解决问题是依靠这篇文章。 问题的根源在于MDK要求下载算法是位置无关的,即安富莱教程中“84.3.7第 7 步, 保证生成的算法文件中 RO 和 RW 段的独立性,即与地址无关”章节。当代码地址无关后,生成的FLM文件中PrgData 域中.bss段的执行地址域DSCR域中.constdata的执行地址(exec addr)重合,即下图中红框内容。我猜测,MDK会处理地址冲突,然后将下载算法传递给jlink,但通过jflash传递给jlink的下载算法则不会处理这种地址冲突
虽然segger删除了OFL的资料,但互联网是有记忆的,从我找到的一些OFL工程来看,并没有出现“-fPIC”编译选项,也印证了OFL代码不需要地址无关 OFL中模板有两个库文件SEGGER_OFL_Lib_CortexM_LE.a、SEGGER_OFL_Lib_CortexM_BE.a。分别供SEGGER_OPEN_CalcCRC函数、SEGGER_OPEN_Start函数使用。具体的使用方法看模板就清楚了。虽然segger说SEGGER_OPEN_CalcCRC比Verify校验快,但我感觉还不如Verify。SEGGER_OPEN_Start开启flash trubo模式,但我不知道这个模式的功能,希望有大神补充一下 MCU:STM32F407VET6
norFLash:W25Q64 外设:usart1、spi2 工程基于STM32CubeMx生成,CMAKE_BUILD_TYPE为Debug时,生成测试程序,可直接烧录在内部flash上运行。CMAKE_BUILD_TYPE为Release时,生成下载算法 ofl/gcc-arm-none-eabi.cmake为Release时的编译选项,除了下述修改外,与cubemx生成的gcc-arm-none-eabi.cmake相同。 [C] 纯文本查看 复制代码 # 模板内也用的软浮点
set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=soft ")
# 更改链接脚本
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -T ${CMAKE_SOURCE_DIR}/ofl/stm32f4xx_flashloader.ld")
工程根目录下的CMakePresets.json若存在toolchainFile属性,一定要删除,否则Realse时不会使用ofl/gcc-arm-none-eabi.cmake "toolchainFile": "${sourceDir}/cmake/gcc-arm-none-eabi.cmake" ofl/stm32cubemx/CMakeLists.txt在cmake/stm32cubemx/CMakeLists.txt的基础上进行了精简,因为代码直接在RAM上执行,不需要启动文件。代码全部使用轮询操作,也不需要中断函数 [C] 纯文本查看 复制代码 # 新增两个宏定义,分别表示spiflash在工程中的逻辑起始地址与扇区尺寸,会传递给FlashPrg.c
target_compile_definitions(stm32cubemx INTERFACE
USE_HAL_DRIVER
STM32F407xx
$<$<CONFIG:Debug>:DEBUG>
EXFLASH_BASE_ADDR=0xC0000000
EXFLASH_SECTORS_SIZE=4096
) 链接文件本来是用于STM32H743IITx,但修改为STM32F407VE的内存映射一样使用。注意,OFL不能把RAM全占用,毕竟RAM中还有jflash的下载缓存
[C] 纯文本查看 复制代码 MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
如何使用 代码中所有的字符串、字库、图片、音频,归根结底,都是数组。将外部资源转化为数组,再融入到程序中,就可以通过jflash烧录了。最终实现的效果与安富莱教程中《STM32H7 的内部 Flash 和 SPI Flash 同时使用 MDK 一键下载》相同 作者给出了一个测试例子,基于rt-thread env环境,通过gcc编译,没有这个环境也没关系,通过修改链接脚本,都可实现相同效果。注意,请预留4G空间为测试工程生成的bin文件(最后并不使用这个bin文件) 在链接脚本中开辟spiflash的内存映射。在下载算法中,将W25Q64的逻辑起始地址指定为0xC0000000(EXFLASH_BASE_ADDR)。W25Q64共8M,按1M、2M、5M分为3个区域,其中0xC0100000-0xC0300000区域供程序运行中使用,烧录时不能污染,所以没在连接脚本中体现 [C] 纯文本查看 复制代码 MEMORY
{
ROM (rx) : ORIGIN = 0x08000000, LENGTH = 256k /* 512KB flash */
RAM1 (rw) : ORIGIN = 0x20000000, LENGTH = 128k /* 128K sram */
RAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 64k /* 64K sram */
NorFlash1 (rw) : ORIGIN =0xC0000000, LENGTH = 1M
NorFlash2 (rw) : ORIGIN =0xC0300000, LENGTH = 5M
}
SECTIONS
{
.NorFlash1 : ALIGN(4)
{
. = ALIGN(4);
KEEP(*(.NorFlash1))
*(.NorFlash1.*)
. = ALIGN(4);
__NorFlash1_free__ = .;
} > NorFlash1
.NorFlash2 : ALIGN(4)
{
. = ALIGN(4);
KEEP(*(.NorFlash2))
*(.NorFlash2.*)
. = ALIGN(4);
__NorFlash2_free__ = .;
} > NorFlash2
} 通过__attribute__,指定数组的section属性。__attribute__也可以指定数组所在的具体地址、对齐属性等 [C] 纯文本查看 复制代码 __attribute__((section(".NorFlash1"))) char spi_store_1[] = "aaa,This is stored in extenal norflash1. \n\r";
__attribute__((section(".NorFlash1"))) char spi_store_2[] = "bbb This is stored in extenal norflash1. \n\r";
__attribute__((section(".NorFlash2"))) char spi_store_3[] = "ccc This is stored in extenal norflash2. \n\r";
__attribute__((section(".NorFlash2"))) char spi_store_4[] = "ddd This is stored in extenal norflash2. \n\r"; 程序中可直接使用数组的地址(也就是字符串指针),但MCU若没有XIP功能,不能直接读取存放在spiflash地址的数组内容
[C] 纯文本查看 复制代码 extern char spi_store_1[];
extern char spi_store_2[];
extern char spi_store_3[];
extern char spi_store_4[];
int main(void)
{
LOG_I("addr.spi_store_1=0x%x,spi_store_2=0x%x", spi_store_1,spi_store_2);
LOG_I("addr.spi_store_3=0x%x,spi_store_4=0x%x", spi_store_3,spi_store_4);
// 这种用法会报错
// LOG_I("content.spi_store_3[0]=%d", spi_store_1[0]);
}
之前讲解了如何使MDK下载算法应用于jflash,将自己的OFL应用于jflash的步骤类似,按照ld中的内存映射修改xml即可。值得注意的是,一次烧录可以使用多种OFL [XML] 纯文本查看 复制代码 <Device>
<ChipInfo Vendor="ST"
Name="Stm32F407VE_VS"
WorkRAMAddr="0x20000000"
WorkRAMSize="0x00020000"
Core="JLINK_CORE_CORTEX_M4"/>
<FlashBankInfo Name="MCU Flash"
BaseAddr="0x08000000"
MaxSize="0x00080000"
Loader="STM32F4xx_512.FLM"
LoaderType="FLASH_ALGO_TYPE_OPEN" />
<FlashBankInfo Name="SPI1 Flash"
BaseAddr="0xC0000000"
MaxSize="0x00100000"
Loader="f407_ofl.elf"
LoaderType="FLASH_ALGO_TYPE_OPEN" />
<FlashBankInfo Name="SPI2 Flash"
BaseAddr="0xC0300000"
MaxSize="0x00500000"
Loader="f407_ofl.elf"
LoaderType="FLASH_ALGO_TYPE_OPEN" />
</Device> jflash中创建新工程,即可看到添加的内容 烧录时直接使用elf文件,若地址不在范围内还会报错
|