在上一篇《手把手系列--编写Keil MDK 外部FLASH下载算法》我们学会了如何给Keil MDK编写下载算法,本篇我们在这基础上编写用于STM32CubeProgrammer的下载算法。
基于官网文档第2.3.2 External Flash memory programming的内容进行操作。
STM32CubeProgrammer V2.8.0
Keil MDK V5.34
刚开始做的时候,我是想着是直接通过STM32CubeMX生成一个Keil工程,然后在工程中进行对应修改,结果整个过程做下来,最后还是失败了,原因未知。
后来我换了一个思路,即先用Keil创建工程再用STM32CubeMX进行配置的方法,成功完成了本博文的目的。
下面我将过程一一描述出来。(小伙子们,操练起来)
首先,我们先准备一个工程文件夹。
如下:
打开Keil MDK新建工程,工程文件放在上面创建的MDK-ARM目录下
选择芯片STM32H750XBH6
在Manage Run-Time Environment选项卡中选中图中相关项
CMSIS-->CORE
Device-->Startup | STM32Cube Framework
注意不要急于点击OK。下图中黄色的意思是说有依赖项未满足,直接点击一下Resolve即可。
点击Resolve之后如下图
点击下图中的播放按钮(黄色标记处)进入STM32CubeMX
设置RCC参数
LSE指外部低速时钟,黄色说明可能存在冲突,我们这边选择旁路时钟源。
选择GPIO PC15/PI8用于配置LED
配置QUADSPI的IO如下图,注意时钟一定要选择Very High。
设置QUADSPI相关参数
其中Clock Prescaler为1,代表二分频;
FIFO Threshold为4,代表硬件FIFO的字节长度
Flash Size为22,代表2的(22 + 1)次幂即8Mbytes
QuadSPI Mode代表硬件上为4线IO SPI,总共6线。
接下来我们设置系统时钟,开发板外部主时钟为25MHz,我们设置主频为480MHz, HCLK3为240MHz,二分频后QUADSPI时钟为120MHz,不超过W25Q64JV Flash最大的时钟频率133MHz。
接下来我们来设置工程参数
设置Code Generator相关参数
设置高级参数
然后点击右上角的Generate Code
然后点击Close即可,并且关闭STM32CubeMX。
下面我们回到Keil MDK上来。
点击OK即可。
注意我们需要在生成的代码main.c中添加部分代码,完整的main.c代码如下
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* © Copyright (c) 2021 STMicroelectronics.
* All rights reserved.
*
* 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****/
修改的部分为
这边解释一下,为什么需要重实现这几个函数,因为我们的下载算法里面会关闭全局中断,这个时候SYSTICK就不能响应中断,但是我们QSPI接口使用时采用的查询接口,故需要这个延时函数能够工作。
我们需要添加几个分组,分别为QUADSPI Memory、Device Description、Program Functions、System File,如下图
下面我们需要添加具体的文件内容
其中涉及到的几个文件如下,关于每个文件里面的具体内容后面我会具体讲解一下。
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_W25Q64JV_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size) {
if (0 == Size) return HAL_ERROR;
if (QSPI_AutoPollingMemReady() != HAL_OK) {
return HAL_ERROR;
}
QSPI_CommandTypeDef cmd = {
.InstructionMode = QSPI_INSTRUCTION_1_LINE,
.Instruction = W25Q64JV_INPUT_FAST_READ,
.AddressMode = QSPI_ADDRESS_4_LINES,
.Address = ReadAddr,
.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 = Size,
};
if (HAL_QSPI_Command(&hqspi, &cmd, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
return HAL_ERROR;
}
if (QSPI_AutoPollingMemReady() != HAL_OK) {
return HAL_OK;
}
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;
}
if (QSPI_WriteEnable() != 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;
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;
}
quadspi.h
#ifndef QUADSPI_H
#define QUADSPI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
#include
#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 0.
*/
#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 QSPI_W25Q64JV_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size);
uint8_t CSP_QSPI_Erase_Chip(void);
#ifdef __cplusplus
}
#endif
#endif
Dev_Inf.c
/*
* Dev_Inf.c
*
*/
#include "Dev_Inf.h"
#include "quadspi.h"
/* This structure contains information used by ST-LINK Utility to program and erase the device */
#if defined (__ICCARM__)
__root struct StorageInfo const StorageInfo = {
#else
struct StorageInfo const StorageInfo = {
#endif
"STM32H750XBH6_ArtPi_QSPI_W25Q64JV_Prog", // Device Name + version number
NOR_FLASH, // Device Type
0x90000000, // Device Start Address
MEMORY_FLASH_SIZE, // Device Size in Bytes
MEMORY_PAGE_SIZE, // Programming Page Size
0xFF, // Initial Content of Erased Memory
// Specify Size and Address of Sectors (view example below)
{ {
(MEMORY_FLASH_SIZE / MEMORY_SECTOR_SIZE), // Sector Numbers,
(uint32_t) MEMORY_SECTOR_SIZE
}, //Sector Size
{ 0x00000000, 0x00000000 }
}
};
Dev_Inf.h
#ifndef DEV_INF_H_
#define DEV_INF_H_
#define MCU_FLASH 1
#define NAND_FLASH 2
#define NOR_FLASH 3
#define SRAM 4
#define PSRAM 5
#define PC_CARD 6
#define SPI_FLASH 7
#define I2C_FLASH 8
#define SDRAM 9
#define I2C_EEPROM 10
#define SECTOR_NUM 10 // Max Number of Sector types
struct DeviceSectors {
unsigned long SectorNum; // Number of Sectors
unsigned long SectorSize; // Sector Size in Bytes
};
struct StorageInfo {
char DeviceName[100]; // Device Name and Description
unsigned short DeviceType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...
unsigned long DeviceStartAddress; // Default Device Start Address
unsigned long DeviceSize; // Total Size of Device
unsigned long PageSize; // Programming Page Size
unsigned char EraseValue; // Content of Erased Memory
struct DeviceSectors sectors[SECTOR_NUM];
};
#endif /* DEV_INF_H_ */
Loader_Src.c
#include "quadspi.h"
#include "main.h"
#define LOADER_OK 0x1
#define LOADER_FAIL 0x0
extern void SystemClock_Config(void);
extern QSPI_HandleTypeDef hqspi;
#define QSPI_BEGIN_ADDRESS 0x90000000
/**
* @brief System initialization.
* @param None
* @retval LOADER_OK = 1 : Operation succeeded
* @retval LOADER_FAIL = 0 : Operation failed
*/
int
Init(void) {
volatile int i;
volatile unsigned char * ptr = (volatile unsigned char * )&hqspi;
for (i = 0; i < sizeof(hqspi); i++) {
*ptr++ = 0U;
}
*(uint32_t *)0xE000EDF0 = 0xA05F0000; //enable interrupts in debug
SystemInit();
SCB->VTOR = 0x24000000 | 0x200;
__disable_irq();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
__HAL_RCC_QSPI_FORCE_RESET(); //completely reset peripheral
__HAL_RCC_QSPI_RELEASE_RESET();
if (CSP_QUADSPI_Init() != HAL_OK) {
return LOADER_FAIL;
}
//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 LOADER_OK;
}
/**
* @brief Program memory.
* @param Address: page address
* @param Size : size of data
* @param buffer : pointer to data buffer
* @retval LOADER_OK = 1 : Operation succeeded
* @retval LOADER_FAIL = 0 : Operation failed
*/
int
Write(uint32_t Address, uint32_t Size, uint8_t* buffer) {
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
if (Address >= QSPI_BEGIN_ADDRESS) {
Address -= QSPI_BEGIN_ADDRESS;
}
if (CSP_QSPI_WriteMemory((uint8_t*) buffer, Address, Size) != HAL_OK) {
return LOADER_FAIL;
}
return LOADER_OK;
}
/**
* @brief Sector erase.
* @param EraseStartAddress : erase start address
* @param EraseEndAddress : erase end address
* @retval LOADER_OK = 1 : Operation succeeded
* @retval LOADER_FAIL = 0 : Operation failed
*/
int
SectorErase(uint32_t EraseStartAddress, uint32_t EraseEndAddress) {
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
if (EraseStartAddress >= QSPI_BEGIN_ADDRESS) {
EraseStartAddress -= QSPI_BEGIN_ADDRESS;
}
if (EraseEndAddress >= QSPI_BEGIN_ADDRESS) {
EraseEndAddress -= QSPI_BEGIN_ADDRESS;
}
if (CSP_QSPI_EraseSector(EraseStartAddress, EraseEndAddress) != HAL_OK) {
return LOADER_FAIL;
}
return LOADER_OK;
}
/**
* Description :
* Mass erase of external flash area
* Optional command - delete in case usage of mass erase is not planed
* Inputs :
* none
* outputs :
* none
* Note: Optional for all types of device
*/
int
MassErase(void) {
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
if (CSP_QSPI_Erase_Chip() != HAL_OK) {
return LOADER_FAIL;
}
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
return LOADER_OK;
}
/**
* Description :
* Calculates checksum value of the memory zone
* Inputs :
* StartAddress : Flash start address
* Size : Size (in WORD)
* InitVal : Initial CRC value
* outputs :
* R0 : Checksum value
* Note: Optional for all types of device
*/
uint32_t
CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal) {
uint8_t missalignementAddress = StartAddress % 4;
uint8_t missalignementSize = Size;
int cnt;
uint32_t Val;
StartAddress -= StartAddress % 4;
Size += (Size % 4 == 0) ? 0 : 4 - (Size % 4);
for (cnt = 0; cnt < Size; cnt += 4) {
Val = *(uint32_t*) StartAddress;
if (missalignementAddress) {
switch (missalignementAddress) {
case 1:
InitVal += (uint8_t) (Val >> 8 & 0xff);
InitVal += (uint8_t) (Val >> 16 & 0xff);
InitVal += (uint8_t) (Val >> 24 & 0xff);
missalignementAddress -= 1;
break;
case 2:
InitVal += (uint8_t) (Val >> 16 & 0xff);
InitVal += (uint8_t) (Val >> 24 & 0xff);
missalignementAddress -= 2;
break;
case 3:
InitVal += (uint8_t) (Val >> 24 & 0xff);
missalignementAddress -= 3;
break;
}
} else if ((Size - missalignementSize) % 4 && (Size - cnt) <= 4) {
switch (Size - missalignementSize) {
case 1:
InitVal += (uint8_t) Val;
InitVal += (uint8_t) (Val >> 8 & 0xff);
InitVal += (uint8_t) (Val >> 16 & 0xff);
missalignementSize -= 1;
break;
case 2:
InitVal += (uint8_t) Val;
InitVal += (uint8_t) (Val >> 8 & 0xff);
missalignementSize -= 2;
break;
case 3:
InitVal += (uint8_t) Val;
missalignementSize -= 3;
break;
}
} else {
InitVal += (uint8_t) Val;
InitVal += (uint8_t) (Val >> 8 & 0xff);
InitVal += (uint8_t) (Val >> 16 & 0xff);
InitVal += (uint8_t) (Val >> 24 & 0xff);
}
StartAddress += 4;
}
return (InitVal);
}
/**
* Description :
* Verify flash memory with RAM buffer and calculates checksum value of
* the programmed memory
* Inputs :
* FlashAddr : Flash address
* RAMBufferAddr : RAM buffer address
* Size : Size (in WORD)
* InitVal : Initial CRC value
* outputs :
* R0 : Operation failed (address of failure)
* R1 : Checksum value
* Note: Optional for all types of device
*/
uint64_t
Verify(uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement) {
uint32_t VerifiedData = 0, InitVal = 0;
uint64_t checksum;
Size *= 4;
if (CSP_QSPI_EnableMemoryMappedMode() != HAL_OK) {
return LOADER_FAIL;
}
checksum = CheckSum((uint32_t) MemoryAddr + (missalignement & 0xf),
Size - ((missalignement >> 16) & 0xF), InitVal);
while (Size > VerifiedData) {
if (*(uint8_t*) MemoryAddr++
!= *((uint8_t*) RAMBufferAddr + VerifiedData)) {
return ((checksum << 32) + (MemoryAddr + VerifiedData));
}
VerifiedData++;
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15);
}
return (checksum << 32);
}
int Read(uint32_t Address, uint32_t Size, uint16_t *buffer) {
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
if (Address >= QSPI_BEGIN_ADDRESS) {
Address -= QSPI_BEGIN_ADDRESS;
}
if (QSPI_W25Q64JV_Read(buffer, Address, Size) != HAL_OK) {
return LOADER_FAIL;
}
HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_8);
return LOADER_OK;
}
Target.lin
; Linker Control File (scatter-loading)
;
PRG 0x24000004 PI ; Programming Functions
{
PrgCode +0 ; Code
{
* (+RO)
}
PrgData +0 ; Data
{
* (+RW,+ZI)
}
}
DSCR +0 ; Device Description
{
DevDscr +0
{
Dev_Inf.o
}
}
我们将上面的文件放到工程文件夹下面,如下图
然后在Keil对应分组中添加对应的文件,如下图
其中注意一下,System File分组的文件在工程文件夹STM32H750XBH6_ArtPi_QSPI_W25Q64JV_Prog\MDK-ARM\RTE\Device\STM32H750XBHx目录下
下面我们来配置一下工程属性
首先选择Device分组然后右击,按下图设置即可(一定要记得点击OK)
点击Keil的魔术棒按钮,依次按图设置
上图中需要添加宏USE_HAL_DRIVER,STM32H750xx以及添加头文件路径,即工程根目录以及跟目录下的W25Q64JV
注意这边分散加载文件选择工程目录下的Target.lin,关于分散加载文件的知识点会在后面的博文中介绍。
以上我们所有的操作都已经完成,点击编译。
如果按照我上面操作一一操作下来,编译已经没有问题。
我们需要的下载算法文件即STM32H750XBH6_ArtPi_QSPI_W25Q64JV_Prog.stldr
我们需要将这个文件放置到STM32CubeProgrammer安装目录下的对应位置
到现在为止,我们需要做的所有工作已经完成,让我们见证奇迹。
我们打开STM32CubeProgrammer程序。
点击左边框的图标,然后我们就可以选择我们自己实现的下载算法了,我们需要验证这个算法是否可用,让我们接上我们的开发板。
点击connect后的结果如下
我们先下载一个bin文件试试,我们随便下载一个bin文件
从图中我们看到下载完成并且验证完成。
我们再人工对比看看
从截图上看,完全一模一样,并且在之前的下载并验证时,如果你自己看设备,你会发现LED在闪烁。
完整工程源码
链接:https://pan.baidu.com/s/1kIW0lH4TDBuqmuy5G7CYfw
提取码:wr6d
至此,我们就完成了下载算法的制作。