前言
最近做个项目用到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);
}
之后编译生成下载算法文件
添加到Keil使用
将生成的XXXX.FLM文件复制到keil下载算法目录:
{Keil安装目录}\ARM\Flash
之后就可以在配置界面看到了
编译APP程序、使用该算法下载校验、运行