MDK外部Flash烧录算法文件制作
Posted aron566
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MDK外部Flash烧录算法文件制作相关的知识,希望对你有一定的参考价值。
MDK外部Flash烧录算法文件制作
最近在开发LVGL时图片文件占用大量的片上存储空间,有两个外部的SPI Flash没有完全利用起来,所以研究下直接在烧录阶段将这个图片烧录进外部Flash
硬件平台
- STM32F743VIT6
- Flash:GD25Q64C(8MB的Flash)
- IDE:MDK v5.37.0.0
算法制作工程配置
可以先用CubeMX配置(外设驱动Flash)生成一个MDK工程,之后在此基础上修改!
# 拷贝Flash_Algx目录下的*.axf文件到,当前目录下改名为.FLM文件
cmd.exe /C copy "Flash_Algx\\%L" ".\\@L.FLM"
这里需要看看自己的axf文件生成在哪个目录下,命令改成对应的
# 拷贝当前目录下名为xx.FLM文件到C:\\Keil_v5\\ARM\\Flash\\目录下
cmd.exe /C copy ".\\@L.FLM" "C:\\Keil_v5\\ARM\\Flash\\@L.FLM"
调试信息一定要打开,否则即使生成了.FLM文件也识别不到
设置,代码段地址、数据段地址为相对地址,即位置无关
添加如下选项
# 屏蔽L6503类型警告信息
--diag_suppress L6305
Target.lin
文件内容如下:
; Linker Control File (scatter-loading)
;
PRG 0 PI ; Programming Functions
PrgCode +0 ; Code
* (+RO)
PrgData +0 ; Data
* (+RW,+ZI)
DSCR +0 ; Device Description
DevDscr +0
FlashDev.o
Flash算法驱动
修改硬件初始化代码
延时接口、硬件初始化接口
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();
ticks += 1;
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
*/
int Xmain(void)
/* USER CODE BEGIN 1 */
SystemInit();
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
/* Enable I-Cache---------------------------------------------------------*/
/* Enable D-Cache---------------------------------------------------------*/
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_SPI6_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
return 0;
//while (1);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
/**
* @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_SCALE1);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY))
/** Macro to configure the PLL clock source
*/
__HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSE);
/** 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 = 2;
RCC_OscInitStruct.PLL.PLLN = 64;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 10;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
Error_Handler();
修改外部Flash的描述信息
这里定义了,2MB的 Flash,Flash实际总大小8MB,我只用其中的2MB去用于烧录阶段,所以这里定义了0x00200000
大小,Device Start Address字段定义了这段空间在程序中的位置(配合sct文件,注意不要与片内的地址重合),其他就是页大小,扇区大小,按实际填写即可
struct FlashDevice const FlashDevice =
FLASH_DRV_VERS, // Driver Version, do not modify!
"GD25Q64C0 8MB Flash", // Device Name
EXTSPI, // Device Type
0x90000000, // Device Start Address
0x00200000, // Device Size in Bytes (2MB)
256, // Programming Page Size
0, // Reserved, must be 0
0xFF, // Initial Content of Erased Memory
3000, // Program Page Timeout 100 mSec
6000, // Erase Sector Timeout 3000 mSec
// Specify Size and Address of Sectors
0x001000, 0, // Sector Size 4kB (512 Sectors)
SECTOR_END
;
完善Flash的驱动接口
完善FlashPrg.c
文件中的驱动接口,在初始化中加入硬件的初始化代码Xmian()
例如这样
/**************************************************************************//**
* @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
#define ENABLE_FLASH_ALG_DEBUG 1
#define DEBUG_INIT 0
#define DEBUG_ERASE 0
#define DEBUG_ERASE_CHIP 0
#define DEBUG_PROGRAM 0
#define DEBUG_VERIFY 0
#if ENABLE_FLASH_ALG_DEBUG
#include "string.h"
#include "printf.h"
#define PRINTF(...) printf(__VA_ARGS__)
#include "usart.h"
#define DEBUG_UART &huart2
__weak void _putchar(char character)
HAL_UART_Transmit(DEBUG_UART, (uint8_t *)&character, 1, 0xFFFF);
#else
#define PRINTF(...)
__weak void _putchar(char character)
#endif
#define USE_SFUD_LIB 1
#define SPI_FLASH_MEM_ADDR 0x90000000
#if USE_SFUD_LIB
#include "sfud.h"
#include "spi.h"
#define SPI_HANDLE &hspi6
static sfud_flash_t sfud_dev0 = NULL;
uint8_t aux_buf[4096];
#else
#endif
/*
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)
/* Add your Code */
extern int Xmain(void);
/* 硬件初始化 */
Xmain();
#if USE_SFUD_LIB && DEBUG_INIT == 0
HAL_UART_DeInit(DEBUG_UART);
HAL_SPI_DeInit(SPI_HANDLE);
HAL_UART_Init(DEBUG_UART);
HAL_SPI_Init(SPI_HANDLE);
/* 初始化Flash驱动 */
if(sfud_init() != SFUD_SUCCESS)
return 1;
/* 初始化驱动 */
sfud_dev0 = NULL;
sfud_dev0 = sfud_get_device(SFUD_GD25Q64C_DEVICE0_INDEX);
if(NULL == sfud_dev0)
return 1;
#else
#endif
PRINTF("Extern Flash Init %u.\\r\\n", fnc);
return (0); // Finished without Errors
/*
* 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)
/* Add your Code */
PRINTF("UnInit %u.\\r\\n", fnc);
return (0); // Finished without Errors
/*
* Erase complete Flash Memory
* Return Value: 0 - OK, 1 - Failed
*/
int EraseChip(void)
/* Add your Code */
#if USE_SFUD_LIB && DEBUG_ERASE_CHIP == 0
HAL_UART_DeInit(DEBUG_UART);
HAL_SPI_DeInit(SPI_HANDLE);
HAL_UART_Init(DEBUG_UART);
HAL_SPI_Init(SPI_HANDLE);
if(sfud_dev0 == NULL)
return 1;
uint8_t status;
sfud_read_status(sfud_dev0, &status);
if(sfud_erase(sfud_dev0, 512 * 1024 * 3 + 0, 2 * 1024 * 1024) != SFUD_SUCCESS)
return 1;
#else
#endif
PRINTF("EraseChip.\\r\\n");
return (0); // Finished without Errors
/*
* Erase Sector in Flash Memory
* Parameter: adr: Sector Address
* Return Value: 0 - OK, 1 - Failed
*/
int EraseSector(unsigned long adr)
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
#if USE_SFUD_LIB && DEBUG_ERASE == 0
HAL_UART_DeInit(DEBUG_UART);
HAL_SPI_DeInit(SPI_HANDLE);
HAL_UART_Init(DEBUG_UART);
HAL_SPI_Init(SPI_HANDLE);
if(sfud_dev0 == NULL)
return 1;
uint8_t status;
sfud_read_status(sfud_dev0, &status);
if(sfud_erase(sfud_dev0, 512 * 1024 * 3 + adr, 4096) != SFUD_SUCCESS)
return 1;
#else
#endif
PRINTF("EraseSector 0x%08X.\\r\\n", adr);
return (0); // 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)
/* Add your Code */
adr -= SPI_FLASH_MEM_ADDR;
#if USE_SFUD_LIB && DEBUG_PROGRAM == 0
HAL_UART_DeInit(DEBUG_UART);
HAL_SPI_DeInit(SPI_HANDLE);
HAL_UART_Init(DEBUG_UART);
HAL_SPI_Init(SPI_HANDLE);
if(sfud_dev0 == NULL)
return 1;
uint8_t status;
sfud_read_status(sfud_dev0, &status);
if(sfud_write(sfud_dev0, 512 * 1024 * 3 + adr, sz, buf) != SFUD_SUCCESS)
return 1;
#else
#endif
PRINTF("ProgramPage 0x%08X Size %u.\\r\\n", adr, sz);
return (0); // Finished without Errors
/**
* @brief 校验
*
* @param adr 起始地址
* @param sz 数据大小
* @param buf 要校验的数据缓冲地址
* @return unsigned long 尾部数据地址
*/
unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
adr -= SPI_FLASH_MEM_ADDR;
#if USE_SFUD_LIB && DEBUG_VERIFY == 0
HAL_UART_DeInit(DEBUG_UART);
HAL_SPI_DeInit(SPI_HANDLE);
HAL_UART_Init(DEBUG_UART);
HAL_SPI_Init(SPI_HANDLE);
if(sfud_dev0 == NULL)
return 1;
uint8_t status;
sfud_read_status(sfud_dev0, &status);
if(sfud_read(sfud_dev0, 512 * 1024 * 3 + adr, sz, aux_buf) != SFUD_SUCCESS)
return 1;
for(int i = 0; i < sz; i++)
if(aux_buf[i] != buf[i])
return (adr + i); /* 校验失败 */
#else
#endif
PRINTF("Verify 0x%08X Size %u.\\r\\n", adr, sz);
adr += SPI_FLASH_MEM_ADDR;
return (adr + sz); /* 校验成功 */
/**
* @brief Blank Check Checks if Memory is Blank
*
* @param adr Block Start Address
* @param sz Block Size (in bytes)
* @param pat Block Pattern
* @return int 0 - OK, 1 - Failed (需要擦除)
*/
int BlankCheck(unsigned long adr, unsigned long sz, unsigned char pat)
// adr -= SPI_FLASH_MEM_ADDR;
PRINTF("BlankCheck 0x%08X Size %u Pat %hhu.\\r\\n", adr, sz, pat);
/* 强制擦除 */
return 1;
屏蔽无效代码
使用Flash算法
将生成的FLM文件复制到:C:\\Keil_v5\\ARM\\Flash
目录下
1、打开需要使用的工程,进行添加
找到它
测试验证
分散加载文件的修改
这里新增了一段空间LR_EROM1
,这段空间的地址信息,和大小信息依赖Flash算法中定义,接着定义了USE_EXT_FLASH_2MB_BUF_SPACE
段,用于代码中定义某些常量数据烧录下载的时候到外部Flash
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00200000 ; load region size_region
ER_IROM1 0x08000000 0x00200000 ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
RW_RAM1 0x00000000 0x00010000 ; 64KB ITCM Code Data 400MHz
*(USE_ITCM_SPACE)
*(.text.aidis.ro)
*(.text.aidis.math)
.ANY (+RO)
.ANY (+XO)
RW_IRAM1 0x20000000 0x00020000 ; 128KB DTCM Data 400MHz
*(USE_DTCM_SPACE)
.ANY (+RW +ZI)
RW_IRAM2 0x24000000 0x00080000 ; 512KB Main Data 200MHz
.ANY (+RW +ZI)
RW_IRAM3 0x30000000 0x00020000 ; 128KB D2 DMA Data 200MHz
*(USE_LCD_DMA_BUF_SPACE)
RW_IRAM4 0x30020000 0x00020000 ; 128KB D2 DMA Data 200MHz
*(USE_IDEL1_DMA_BUF_SPACE)
RW_IRAM5 0x30040000 0x00008000 以上是关于MDK外部Flash烧录算法文件制作的主要内容,如果未能解决你的问题,请参考以下文章
脱机烧录实战技能任何支持SWD接口的单片机都可以方便移植的SPI Flash烧写算法制作,含视频说明(2022-05-30)
脱机烧录实战技能任何支持SWD接口的单片机都可以方便移植的SPI Flash烧写算法制作,含视频说明(2022-05-30)
STM32F429的内部Flash和SPI Flash都使用MDK下载