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下载

J-Link固件烧录以及使用J-Flash向arm硬件板下载固件程序

STM8程序烧录时怎么加密

如何实现J-Link的Flash烧录算法?