手把手系列--编写Keil MDK 外部FLASH下载算法

声明:博文中涉及到的版权软件只用于教学使用

一、目的

        完整工程下载地址:

        链接:https://pan.baidu.com/s/1xtr8m_KGsyx64wN6rcnZyg 
        提取码:re4i

        很多同学都是从STM32F103开始了STM32的开发之旅,在使用Keil进行下载的时候需要设置下载算法,此时我们直接选择Keil提供的下载算法完成了代码的下载。

        最近淘了一块STM32H750XBH6 ArtPi开发板,属于M7内核,内部Flash的大小为128KBytes,同时板子上还有一块W25QJV64的华邦的Flash芯片(8MBytes),连接在QUADSPI硬件接口上。

        本篇博文主要介绍如何实现下载算法,以便后续将代码或者数据存储在外部Flash中,所有的操作都是基于此文档进行的。

链接:https://pan.baidu.com/s/1x0ljEoBC7k1pa_kOrW24Dw 
提取码:w2ox

 Flash Programming (open-cmsis-pack.github.io)icon-default.png?t=M4ADhttps://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/flashAlgorithm.html

二、准备

        Keil MDK V5.34

        STM32CubeMX V6.3.0

        MDK5 Software Packs (keil.com)https://www.keil.com/dd2/pack/#        

        下载此PACK

                STMicroelectronics STM32H7 Series Device Support and Examples

        

        关于STM32CubeMX的使用我会在另外的博文中介绍。

         关于Keil的安装以及科学使用,请自行百度。我的Keil安装目录在H:\Keil_v5

三、实战

        下面我们开始构建Keil工程。

        (1)首先从拷贝目录H:\Keil_v5\ARM\Flash\_Template到H:\STM32目录下

        修改工程文件夹以及功能名称STM32H750XBH6_ArtPi_QSPI_W25Q64JV;

        删除STM32H750XBH6_ArtPi_QSPI_W25Q64JV目录下的文件

        删除 Test文件夹

        修改NewDevice.uvprojx为STM32H750XBH6_ArtPi_QSPI_W25Q64JV.uvprojx

        修改NewDevice.uvproj为STM32H750XBH6_ArtPi_QSPI_W25Q64JV.uvproj

        这些操作以后目录如下

        

      (2)打开工程

        修改Device选项

                

         修改编译器选项

        

         修改Name of Executable为STM32H750XBH6_ArtPi_QSPI_W25Q64JV

         确认C/C++(AC6)以及Asm选项中都选择Read-Only Position Independent、Read-Write Position Independent

        

                 在Manage Run-Time Environment选项中选中如图

        

         在上图中STM32CubeMX行点击播放按钮运行STM32CubeMX程序。

         

         在GPIO选项中设置对应的IO配置,主要是QUADSP的配置,另外我额外设置PC15/PI8这两个IO用于控制板载的两个LED

        具体如下

     

   尤其要注意要设置IO速度为Very High

        设置RCC参数

        

         设置QUADSPI相关参数

         其中分频系数为1,即二分频;Flash Size为22,即8MBytes;

        设置系统时钟

        

        系统主时钟480MHz

         QUADSPI选用HCLK3作为时钟输入

        

         这样设置下来QUAPSPI的时钟为240MHz / 2 = 120MHz;而W25Q64JV Flash的最大时钟频率133MHz。

        设置工程属性

        

 

 

        点击Close关闭工程并且关闭STM32CubeMX程序。

        点击Manage Run-Time Environment选项中OK按钮。

        修改文件FlashDev.c

        

/**************************************************************************//**
 * @file     FlashDev.c
 * @brief    Flash Device Description 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


struct FlashDevice const FlashDevice  =  {
   FLASH_DRV_VERS,             // Driver Version, do not modify!
   "STM32H750XBH6_ArtPi_QSPI_W25Q64JV",   // Device Name 
   EXTSPI,                     // Device Type
   0x90000000,                 // Device Start Address
   0x000800000,                 // Device Size in Bytes (8MB)
   1024,                       // Programming Page Size
   0,                          // Reserved, must be 0
   0xFF,                       // Initial Content of Erased Memory
   1000,                        // Program Page Timeout 1000 mSec
   3000,                       // Erase Sector Timeout 3000 mSec

// Specify Size and Address of Sectors
   0x001000, 0x000000,         // Sector Size  4kB
   SECTOR_END
};

                移除Device里面的Startup内容

        

        添加System File组并添加文件 ./RTE/Device/STM32F769NIHx/system_stm32h7xx.c”

         在工程目录下添加W25Q64JV文件夹

        H:\STM32\STM32H750XBH6_ArtPi_QSPI_W25Q64JV\W25Q64JV

       添加组QUADSPI Memory组,并添加quadspi.c文件

        并将文件quadspi.c、quadspi.h拷贝进去

        

        

                        文件内容为quadspi.h

                

#ifndef QUADSPI_H
#define QUADSPI_H
#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"
#include <stdint.h>


#define W25Q64JV_WRITE_ENABLE (0x06)
/*
 * The Quad Enable (QE) bit is set to 1 by default in the factory, therefore the device supports Standard/Dual
SPI as well as Quad SPI after power on. This bit cannot be reset to 1.
 */
#define W25Q64JV_INPUT_FAST_READ (0xeb)
#define W25Q64JV_PAGE_PROGRAM (0x02)
#define W25Q64JV_STATUS_REG1 (0x05)
#define W25Q64JV_ENABLE_RESET (0x66)
#define W25Q64JV_RESET_DEVICE (0x99)
#define W25Q64JV_DEVICE_ID (0x90)
#define W25Q64JV_ID_NUMBER (0x4b)
#define W25Q64JV_ERASE_SECTOR (0x20)
#define W25Q64JV_ERASE_CHIP (0xc7)

#define MEMORY_FLASH_SIZE 0x800000 /* 64MBits => 8MBytes */
#define MEMORY_BLOCK_SIZE 0x10000 /* 64KBytes */
#define MEMORY_SECTOR_SIZE 0x1000 /* 4KBytes */
#define MEMORY_PAGE_SIZE 0x100 /* 32768 pages of 256Bytes */

uint8_t CSP_QUADSPI_Init(void);
uint8_t CSP_QSPI_EraseSector(uint32_t EraseStartAddress, uint32_t EraseEndAddress);
uint8_t CSP_QSPI_WriteMemory(uint8_t *buffer, uint32_t address, uint32_t buffer_size);
uint8_t CSP_QSPI_EnableMemoryMappedMode(void);
uint8_t CSP_QSPI_Erase_Chip(void);

#ifdef __cplusplus
}
#endif
#endif

         quadspi.c文件

        

#include "quadspi.h"

extern QSPI_HandleTypeDef hqspi;

static uint8_t QSPI_WriteEnable(void);
static uint8_t QSPI_AutoPollingMemReady(void);
static uint8_t QSPI_ResetChip(void);


uint8_t QSPI_ResetChip(void) {

    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_ENABLE_RESET,
    };

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }

    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction = W25Q64JV_RESET_DEVICE;
    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }

    if (QSPI_AutoPollingMemReady() != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

uint8_t QSPI_AutoPollingMemReady(void) {

    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_STATUS_REG1,
        .DataMode = QSPI_DATA_1_LINE,
    };

    QSPI_AutoPollingTypeDef conf = {
        .Match = 0x00,
        .Mask = 0x01,
        .MatchMode = QSPI_MATCH_MODE_AND,
        .StatusBytesSize = 1,
        .Interval = 0x10,
        .AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE,
    };

    if (HAL_QSPI_AutoPolling(&hqspi, &cmd, &conf, HAL_MAX_DELAY) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

uint8_t QSPI_WriteEnable(void) {

    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_WRITE_ENABLE,
    };

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }

    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction = W25Q64JV_STATUS_REG1;

    cmd.DataMode = QSPI_DATA_1_LINE;
    cmd.DummyCycles = 0;
    cmd.NbData = 0;

    QSPI_AutoPollingTypeDef conf = {
        .Match = 0x02,
        .Mask = 0x02,
        .MatchMode = QSPI_MATCH_MODE_AND,
        .StatusBytesSize = 1,
        .Interval = 0x10,
        .AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE,
    };

    if (HAL_QSPI_AutoPolling(&hqspi, &cmd, &conf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

uint8_t CSP_QUADSPI_Init(void) {
    hqspi.Instance = QUADSPI;
    if (HAL_QSPI_DeInit(&hqspi) != HAL_OK) {
        return HAL_ERROR;
    }
    MX_QUADSPI_Init();
    if (QSPI_ResetChip() != HAL_OK) {
        return HAL_ERROR;
    }
    HAL_Delay(1);
    
    if (QSPI_AutoPollingMemReady() != HAL_OK) {
        return HAL_ERROR;
    }
    return HAL_OK;
}

uint8_t CSP_QSPI_EraseSector(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
    EraseStartAddress = EraseStartAddress - EraseStartAddress % MEMORY_SECTOR_SIZE;
    
    if (QSPI_WriteEnable() != HAL_OK) {
        return HAL_ERROR;
    }

    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_ERASE_SECTOR,
        .AddressMode = QSPI_ADDRESS_1_LINE,
        .AddressSize = QSPI_ADDRESS_24_BITS,
    };
    while (EraseEndAddress >= EraseStartAddress) {
        cmd.Address = (EraseStartAddress & 0x0FFFFFFF);
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }

        if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
            return HAL_ERROR;
        }
        EraseStartAddress += MEMORY_SECTOR_SIZE;
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
    }

    return HAL_OK;
}

uint8_t CSP_QSPI_WriteMemory(uint8_t *buffer, uint32_t address, uint32_t buffer_size) {
    uint32_t end_addr, current_size, current_addr;
    current_addr = 0;
    
    while (current_addr <= address) {
        current_addr += MEMORY_PAGE_SIZE;
    }
    current_size = current_addr - address;
    if (current_size > buffer_size) {
        current_size = buffer_size;
    }
    current_addr = address;
    end_addr = address + buffer_size;
    
    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_PAGE_PROGRAM,
        .AddressMode = QSPI_ADDRESS_1_LINE,
        .AddressSize = QSPI_ADDRESS_24_BITS,
        .DataMode = QSPI_DATA_1_LINE,
        .DummyCycles = 0,
    };
    do {
        cmd.Address = current_addr;
        cmd.NbData = current_size;
        
        if (current_size == 0) {
            return HAL_OK;
        }
        
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }
        if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
            return HAL_ERROR;
        }
        
        if (HAL_QSPI_Transmit(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
            return HAL_ERROR;
        }
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
        current_addr += current_size;
        buffer += current_size;
        current_size = ((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
                                (end_addr - current_addr) : MEMORY_PAGE_SIZE;
    } while (current_addr <= end_addr);
    return HAL_OK;
}

uint8_t CSP_QSPI_EnableMemoryMappedMode(void) {
    QSPI_MemoryMappedTypeDef mem_mapped_cfg = {
        .TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE,
    };

    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_INPUT_FAST_READ,
        .AddressMode = QSPI_ADDRESS_4_LINES,
        .Address = 0,
        .AddressSize = QSPI_ADDRESS_24_BITS,
        .AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES,
        .AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS,
        .AlternateBytes    = 0xf0, //datasheet p22
        .DataMode = QSPI_DATA_4_LINES,
        .DummyCycles = 4,
        .NbData = 0,
    };
    if (HAL_QSPI_MemoryMapped(&hqspi, &cmd, &mem_mapped_cfg) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

uint8_t CSP_QSPI_Erase_Chip(void) {
    if (QSPI_WriteEnable() != HAL_OK) {
        return HAL_ERROR;
    }
    QSPI_CommandTypeDef cmd = {
        .InstructionMode = QSPI_INSTRUCTION_1_LINE,
        .Instruction = W25Q64JV_ERASE_CHIP,
    };

    if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }
    if (QSPI_AutoPollingMemReady() != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

拷贝文件FlashOS.h到工程根目录

        

/**************************************************************************//**
 * @file     FlashOS.h
 * @brief    Data structures and entries Functions
 * @version  V1.0.0
 * @date     10. January 2018
 ******************************************************************************/
/*
 * Copyright (c) 2010-2020 Arm Limited (or its affiliates). 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.
 */
 
#define VERS       1           // Interface Version 1.01

#define UNKNOWN    0           // Unknown
#define ONCHIP     1           // On-chip Flash Memory
#define EXT8BIT    2           // External Flash Device on 8-bit  Bus
#define EXT16BIT   3           // External Flash Device on 16-bit Bus
#define EXT32BIT   4           // External Flash Device on 32-bit Bus
#define EXTSPI     5           // External Flash Device on SPI

#define SECTOR_NUM 512         // Max Number of Sector Items
#define PAGE_MAX   65536       // Max Page Size for Programming

struct FlashSectors  {
  unsigned long   szSector;    // Sector Size in Bytes
  unsigned long AddrSector;    // Address of Sector
};

#define SECTOR_END 0xFFFFFFFF, 0xFFFFFFFF

struct FlashDevice  {
   unsigned short     Vers;    // Version Number and Architecture
   char       DevName[128];    // Device Name and Description
   unsigned short  DevType;    // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...
   unsigned long    DevAdr;    // Default Device Start Address
   unsigned long     szDev;    // Total Size of Device
   unsigned long    szPage;    // Programming Page Size
   unsigned long       Res;    // Reserved for future Extension
   unsigned char  valEmpty;    // Content of Erased Memory

   unsigned long    toProg;    // Time Out of Program Page Function
   unsigned long   toErase;    // Time Out of Erase Sector Function

   struct FlashSectors sectors[SECTOR_NUM];
};

#define FLASH_DRV_VERS (0x0100+VERS)   // Driver Version, do not modify!

// Flash Programming Functions (Called by FlashOS)
extern          int  Init        (unsigned long adr,   // Initialize Flash
                                  unsigned long clk,
                                  unsigned long fnc);
extern          int  UnInit      (unsigned long fnc);  // De-initialize Flash
extern          int  BlankCheck  (unsigned long adr,   // Blank Check
                                  unsigned long sz,
                                  unsigned char pat);
extern          int  EraseChip   (void);               // Erase complete Device
extern          int  EraseSector (unsigned long adr);  // Erase Sector Function
extern          int  ProgramPage (unsigned long adr,   // Program Page Function
                                  unsigned long sz,
                                  unsigned char *buf);
extern unsigned long Verify      (unsigned long adr,   // Verify Function
                                  unsigned long sz,
                                  unsigned char *buf);

拷贝文件FlashProg.c

       注意:我在对应的函数里面调用了一些LED控制的操作,当然这个操作只是为了在操作flash时有一些UI效果,非必须可以删除掉。

/**************************************************************************//**
 * @file     FlashPrg.c
 * @brief    Flash Programming Functions adapted for New Device Flash
 * @version  V1.0.0
 * @date     28. October 2020
 ******************************************************************************/
/*
 * 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

/* 
   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
*/

#include "main.h"
#include "quadspi.h"

extern QSPI_HandleTypeDef hqspi;
unsigned int DevAddr;

/*
 *  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) {
  int ret = 0;
  volatile int i;
  volatile unsigned char * ptr = (volatile unsigned char * )&hqspi;

  for (i = 0; i < sizeof(hqspi); i++) {
    *ptr++ = 0U;
  }

  DevAddr = adr;

  __disable_irq();

  HAL_Init();
  SystemInit();

  SystemClock_Config();
  SystemCoreClockUpdate();
    MX_GPIO_Init();
  if (CSP_QUADSPI_Init() != HAL_OK) {
    ret = 1;
  }
        //HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
    //HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_RESET);
    
    //HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
    //HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
  return (ret);
}


/*
 *  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) {
  int ret = 0;
   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);
   HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET);
  if (hqspi.State != HAL_QSPI_STATE_BUSY_MEM_MAPPED) {
    if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
      ret = 1;
    }
  }

  return (ret);
}


/*
 *  Erase complete Flash Memory
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseChip (void) {
  int ret = 0;
    HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
  if (CSP_QSPI_Erase_Chip () != HAL_OK) {
    ret = 1;
  }
  HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
  return (ret);
}


/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseSector (unsigned long adr) {
  int ret = 1;
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
  if (adr >= DevAddr) {
    adr -= DevAddr;

    if (CSP_QSPI_EraseSector (adr, adr + MEMORY_BLOCK_SIZE) == HAL_OK) {
      ret = 0;
    }
  }

  return (ret);
}


/*
 *  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) {
  int ret = 1;
    HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
  if (adr >= DevAddr) {
    adr -= DevAddr;

    if (CSP_QSPI_WriteMemory (buf, adr, sz) == HAL_OK) {
      ret = 0;
    }
  }

  return (ret);
}

unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf){
  volatile unsigned long ret = adr + sz;
  volatile int i;


  if (hqspi.State != HAL_QSPI_STATE_BUSY_MEM_MAPPED) {
    if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
      ret = 0U;
    }
  }
  
  if (ret != 0U) {
    for (i = 0; i < sz; i++) {
      if ((*((volatile unsigned char  *) (adr+i))) != buf[i]) {
        // Verification Failed (return address)
        ret = adr + i;
        break;
      }
      HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
       HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
    }
  }

  return (ret);
}

int  BlankCheck  (unsigned long adr, unsigned long sz, unsigned char pat) {
  int ret = 0;
  volatile int i;

  if (hqspi.State != HAL_QSPI_STATE_BUSY_MEM_MAPPED) {
    if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
      ret = 1;
    }
  }
  
  if (ret == 0) {
    for (i = 0; i < sz; i++) {
      if ((*((volatile unsigned char  *) (adr+i))) != pat) {
        ret = 1;
        break;
      }
    }
  }

  if (CSP_QUADSPI_Init() != HAL_OK) {
    HAL_Delay (100);
    if (CSP_QUADSPI_Init() != HAL_OK) {
      ret = 1;
    }
  }

  return (ret);
}

        添加头文件路径以及宏定义

        

       替换main.c文件

                

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

QSPI_HandleTypeDef hqspi;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
    return HAL_OK;
}

uint32_t HAL_GetTick(void) {
    static uint32_t ticks = 0U;
    uint32_t i;
    for (i = (SystemCoreClock >> 14U); i > 0U; i--) {
        __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
        __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
    }
    return ++ticks;
}

void HAL_Delay(uint32_t Delay) {
    uint32_t tickstart = HAL_GetTick();
    uint32_t wait = Delay;
    if (wait < HAL_MAX_DELAY) {
        wait += (uint32_t)(HAL_TICK_FREQ_DEFAULT);
    }
    while ((HAL_GetTick() - tickstart) < wait) {
        __NOP();
    }
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Supply configuration update enable
  */
  HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
  /** Configure the main internal regulator output voltage
  */
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);

  while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 5;
  RCC_OscInitStruct.PLL.PLLN = 192;
  RCC_OscInitStruct.PLL.PLLP = 2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
  RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  RCC_OscInitStruct.PLL.PLLFRACN = 0;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief QUADSPI Initialization Function
  * @param None
  * @retval None
  */
void MX_QUADSPI_Init(void)
{

  /* USER CODE BEGIN QUADSPI_Init 0 */

  /* USER CODE END QUADSPI_Init 0 */

  /* USER CODE BEGIN QUADSPI_Init 1 */

  /* USER CODE END QUADSPI_Init 1 */
  /* QUADSPI parameter configuration*/
  hqspi.Instance = QUADSPI;
  hqspi.Init.ClockPrescaler = 1;
  hqspi.Init.FifoThreshold = 4;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 22;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_5_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI_Init 2 */

  /* USER CODE END QUADSPI_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOF_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET);

  /*Configure GPIO pin : PC15 */
  GPIO_InitStruct.Pin = GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : PI8 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

        替换main.h文件

        

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32h7xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */
void SystemClock_Config(void);
void MX_QUADSPI_Init(void);
/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
void   MX_GPIO_Init(void);
void   MX_QUADSPI_Init(void);
/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

        编译成功后输出

        

               

        完整工程下载地址:

        链接:https://pan.baidu.com/s/1xtr8m_KGsyx64wN6rcnZyg 
        提取码:re4i

至此,我们就完成了 STM32H750XBH6_ArtPi_QSPI_W25Q64JV下载算法的工程。

后续

        本篇中主要介绍了如何实现特定平台STM32H750XBH6 ART RTHREAD开发板W25Q64JV的下载算法。在下一篇博文中会介绍如何验证我们实现的下载算法是否可用。

  • 14
    点赞
  • 133
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值