最近做个项目用到STM32H750这款芯片,其片内flash只有128KB大小(虽网上说实际上和H743一样有2MB,但保险起见还是没有采用此方法),由于项目使用了RTOS、LWIP、TLS等,生成程序比较大、所以加了片W25Q32(4MB大小)作为片外FLASH使用,在需求有串口升级程序的功能、加上启动时需要先初始化QSPI然后设置内存映射到片外FLASH、则采用了Bootloader(片内) + App(片外)的方式,那问题就来了,Bootloader在片内,可以直接STLink直接下载,而App却没法直接下载,总不能一直采用串口升级调试吧,就算下到SAM运行也不是长久之计,思考再三还是得使用下载算法直接下到片外FLASH运行。
如果你电脑已经安装Keil,那么可以在以下目录找到官方的工程模板:
{Keil安装目录}\ARM\PACK\ARM\CMSIS\5.3.0\Device\_Template_Flash
将这些全部复制到新文件夹,再将相关的芯片驱动库复制进去,之后打开工程,如果打开之后提示:
需要将工程模板的文件选择、然后右键进属性选项,把只读选项去掉,再重新打开即可。
重新打开之后,点击小魔术棒,选择对应的芯片,之后再将要用到的驱动文件逐一添加。
这边添加的都是Bootloader程序的文件(使用STM32CubeMX生成),这边几个文件需要修改一下:
将里面用到中断和判断超时的部分屏蔽掉,因为没有系统中断故hal库的计时器并不工作。
之后点编译如果遇到以下问题:
\xxx.axf: Error: L6265E: Non-PI Section xxxxxxxxx.o(.data) cannot be assigned to PI Exec region PrgData.
可以参考这篇博文解决:STM32用STLINK烧写外置FLASH遇到的问题,编译结果:
先根据片外FLASH芯片调整参数,在FlashDev.c文件里头的结构体:
struct FlashDevice const FlashDevice = { FLASH_DRV_VERS, // 驱动版本 不需要修改! "STM32H750 QSPI(W25Q32) 4MB Flash", // 下载算法名称(Keil里显示) EXTSPI, // 设备类型:片外FLASH 0x90000000, // 片外FLASH地址 4 * 1024 *1024, // 片外FLASH大小(4MB) 1024, // 页大小(一次写入的数据大小,这边W25Q32页大小是256,为了写入快点这边设置1024) 0, // 保留位,必须是0 0xFF, // 擦除后数据值 1000, // 页写入超时时间(1S) 6000, // 扇区擦除超时时间(6S) 4 * 1024, 0x000000, // 扇区大小:4KB 地址:0x000000 SECTOR_END };
之后实现FlashPrg.c里的相关函数:
/**************************************************************************//** * @file FlashPrg.c * @brief Flash Programming Functions adapted for New Device Flash * @version V1.0.0 * @date 10. January 2018 ******************************************************************************/ /* * Copyright (c) 2010-2018 Arm Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FlashOS.h" // FlashOS Structures #include "led.h" #include "main.h" #include "w25q32_qspi.h" /* Mandatory Flash Programming Functions (Called by FlashOS): int Init (unsigned long adr, // Initialize Flash unsigned long clk, unsigned long fnc); int UnInit (unsigned long fnc); // De-initialize Flash int EraseSector (unsigned long adr); // Erase Sector Function int ProgramPage (unsigned long adr, // Program Page Function unsigned long sz, unsigned char *buf); Optional Flash Programming Functions (Called by FlashOS): int BlankCheck (unsigned long adr, // Blank Check unsigned long sz, unsigned char pat); int EraseChip (void); // Erase complete Device unsigned long Verify (unsigned long adr, // Verify Function unsigned long sz, unsigned char *buf); - BlanckCheck is necessary if Flash space is not mapped into CPU memory space - Verify is necessary if Flash space is not mapped into CPU memory space - if EraseChip is not provided than EraseSector for all sectors is called */ /* * Initialize Flash Programming Functions * Parameter: adr: Device Base Address * clk: Clock Frequency (Hz) * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) * Return Value: 0 - OK, 1 - Failed */ int Init (unsigned long adr, unsigned long clk, unsigned long fnc) { userSystemInit(); LED1_ON(); if (W25_Qspi_Get_ID() != W25X_MF_ID) return (1); // Finished without Errors return (0); // Finished without OK } /* * De-Initialize Flash Programming Functions * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) * Return Value: 0 - OK, 1 - Failed */ int UnInit (unsigned long fnc) { LED1_OFF(); return (0); // Finished without OK } // Blank Check int BlankCheck(unsigned long adr, unsigned long sz, unsigned char pat) { return (0); // Memory is blank 默认全部为空 在写入程序的时候会自己判断 } /* * Erase complete Flash Memory * Return Value: 0 - OK, 1 - Failed */ int EraseChip (void) { LED1_FLASH(); if (W25_Qspi_WriteEnable()) { if (W25_Qspi_EraseDatas(0, 3)) return (0); // Finished without OK } return (1); // Finished without Errors } /* * Erase Sector in Flash Memory * Parameter: adr: Sector Address * Return Value: 0 - OK, 1 - Failed */ int EraseSector (unsigned long adr) { adr -= QSPI_FLASH_MEM_ADDR; LED1_FLASH(); if (W25_Qspi_WriteEnable()) { if (W25_Qspi_EraseDatas(adr, 0)) return (0); // Finished without OK } return (1); // Finished without Errors } /* * Program Page in Flash Memory * Parameter: adr: Page Start Address * sz: Page Size * buf: Page Data * Return Value: 0 - OK, 1 - Failed */ int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) { adr -= QSPI_FLASH_MEM_ADDR; LED1_FLASH(); if (W25_Qspi_WriteDatas_API(adr, buf, sz)) return (0); // Finished without OK return (1); // Finished without Errors } // Verify Function extern uint8_t checkBuf[W25X_SECTOR_SIZE]; unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) { unsigned long i; adr -= QSPI_FLASH_MEM_ADDR; LED1_FLASH(); if (W25_Qspi_ReadDatas(adr, checkBuf, sz) == 0) return (1); // Finished without Errors for (i=0; i<sz; i++) if (buf[i] != checkBuf[i]) break; return (adr + i + QSPI_FLASH_MEM_ADDR); }
之后编译生成下载算法文件
将生成的XXXX.FLM文件复制到keil下载算法目录:
{Keil安装目录}\ARM\Flash
之后就可以在配置界面看到了
编译APP程序、使用该算法下载校验、运行