STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

前言

        最近做个项目用到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

 STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 将这些全部复制到新文件夹,再将相关的芯片驱动库复制进去,之后打开工程,如果打开之后提示:

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

需要将工程模板的文件选择、然后右键进属性选项,把只读选项去掉,再重新打开即可。

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 重新打开之后,点击小魔术棒,选择对应的芯片,之后再将要用到的驱动文件逐一添加。

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 这边添加的都是Bootloader程序的文件(使用STM32CubeMX生成),这边几个文件需要修改一下:

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 将里面用到中断和判断超时的部分屏蔽掉,因为没有系统中断故hal库的计时器并不工作。

之后点编译如果遇到以下问题:

\xxx.axf: Error: L6265E: Non-PI Section xxxxxxxxx.o(.data) cannot be assigned to PI Exec region PrgData.

 可以参考这篇博文解决:STM32用STLINK烧写外置FLASH遇到的问题,编译结果:

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 算法实现

先根据片外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);
}

之后编译生成下载算法文件

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

添加到Keil使用 

将生成的XXXX.FLM文件复制到keil下载算法目录:

{Keil安装目录}\ARM\Flash

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 之后就可以在配置界面看到了

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

编译APP程序、使用该算法下载校验、运行

STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

 STM32 MDK片外FLASH下载算法制作 —— 基于QSPI(W25Q32)

上一篇:C++函数实现计算一个数的阶乘


下一篇:C 语言是一门抽象的、面向过程的语言,C 语言广泛应用于底层开发