【MCU】如何将资源烧写至外部flash,如spi-flash
STM32将资源烧写至外部flash方式大致分为同应用程序一起烧录和单独烧录
烧录关键就是制作对应算法
方式一、使用通用工具如IDE、J-Flash加载烧写算法
烧录应用程序时一并写入,通过修改分散加载链接脚本将部分常量数据移至外部flash
制作烧录算法步骤如下:
主要实现两个文件,接口实现文件 <FLashPrg.c> 和设备描述文件 <FLashPrg.c>
FLashDev.c
1 /**************************************************************************//** 2 * @file FlashDev.c 3 * @brief Flash Device Description for New Device Flash 4 * @version V1.0.0 5 * @date 10. January 2018 6 ******************************************************************************/ 7 /* 8 * Copyright (c) 2010-2018 Arm Limited. All rights reserved. 9 * 10 * SPDX-License-Identifier: Apache-2.0 11 * 12 * Licensed under the Apache License, Version 2.0 (the License); you may 13 * not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an AS IS BASIS, WITHOUT 20 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 */ 24 25 #include "..\FlashOS.H" // FlashOS Structures 26 27 28 struct FlashDevice const FlashDevice = { 29 FLASH_DRV_VERS, // Driver Version, do not modify! 30 "STM32F429_W25Q128", // Device Name 31 EXTSPI, // Device Type 32 0x90000000, // Device Start Address 33 0x01000000, // Device Size in Bytes (256kB) 34 256, // Programming Page Size 35 0, // Reserved, must be 0 36 0xFF, // Initial Content of Erased Memory 37 100, // Program Page Timeout 100 mSec 38 3000, // Erase Sector Timeout 3000 mSec 39 40 // Specify Size and Address of Sectors 41 0x001000, 0x000000, // Sector Size 8kB (8 Sectors) 42 // 0x010000, 0x010000, // Sector Size 64kB (2 Sectors) 43 // 0x002000, 0x030000, // Sector Size 8kB (8 Sectors) 44 SECTOR_END 45 };
FLashPrg.c
1 /**************************************************************************//** 2 * @file FlashPrg.c 3 * @brief Flash Programming Functions adapted for New Device Flash 4 * @version V1.0.0 5 * @date 10. January 2018 6 ******************************************************************************/ 7 /* 8 * Copyright (c) 2010-2018 Arm Limited. All rights reserved. 9 * 10 * SPDX-License-Identifier: Apache-2.0 11 * 12 * Licensed under the Apache License, Version 2.0 (the License); you may 13 * not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an AS IS BASIS, WITHOUT 20 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 */ 24 25 #include "..\FlashOS.H" // FlashOS Structures 26 #include ".\flash\bsp_spi_flash.h" 27 28 29 #define PAGE_SIZE SPI_FLASH_PageSize 30 31 32 uint8_t auxBuf[PAGE_SIZE]; 33 uint32_t baseAddr; 34 35 /* 36 Mandatory Flash Programming Functions (Called by FlashOS): 37 int Init (unsigned long adr, // Initialize Flash 38 unsigned long clk, 39 unsigned long fnc); 40 int UnInit (unsigned long fnc); // De-initialize Flash 41 int EraseSector (unsigned long adr); // Erase Sector Function 42 int ProgramPage (unsigned long adr, // Program Page Function 43 unsigned long sz, 44 unsigned char *buf); 45 46 Optional Flash Programming Functions (Called by FlashOS): 47 int BlankCheck (unsigned long adr, // Blank Check 48 unsigned long sz, 49 unsigned char pat); 50 int EraseChip (void); // Erase complete Device 51 unsigned long Verify (unsigned long adr, // Verify Function 52 unsigned long sz, 53 unsigned char *buf); 54 55 - BlanckCheck is necessary if Flash space is not mapped into CPU memory space 56 - Verify is necessary if Flash space is not mapped into CPU memory space 57 - if EraseChip is not provided than EraseSector for all sectors is called 58 */ 59 60 61 /* 62 * Initialize Flash Programming Functions 63 * Parameter: adr: Device Base Address 64 * clk: Clock Frequency (Hz) 65 * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 66 * Return Value: 0 - OK, 1 - Failed 67 */ 68 69 int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 70 { 71 /* Add your Code */ 72 baseAddr = adr; 73 SPI_FLASH_Init(); 74 return (0); // Finished without Errors 75 } 76 77 78 /* 79 * De-Initialize Flash Programming Functions 80 * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 81 * Return Value: 0 - OK, 1 - Failed 82 */ 83 84 int UnInit (unsigned long fnc) 85 { 86 /* Add your Code */ 87 return (0); // Finished without Errors 88 } 89 90 91 /* 92 * Erase complete Flash Memory 93 * Return Value: 0 - OK, 1 - Failed 94 */ 95 96 int EraseChip (void) 97 { 98 /* Add your Code */ 99 SPI_FLASH_BulkErase(); 100 return (0); // Finished without Errors 101 } 102 103 104 /* 105 * Erase Sector in Flash Memory 106 * Parameter: adr: Sector Address 107 * Return Value: 0 - OK, 1 - Failed 108 */ 109 110 int EraseSector (unsigned long adr) 111 { 112 /* Add your Code */ 113 SPI_FLASH_SectorErase(adr - baseAddr); 114 return (0); // Finished without Errors 115 } 116 117 118 /* 119 * Program Page in Flash Memory 120 * Parameter: adr: Page Start Address 121 * sz: Page Size 122 * buf: Page Data 123 * Return Value: 0 - OK, 1 - Failed 124 */ 125 126 int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) 127 { 128 /* Add your Code */ 129 SPI_FLASH_PageWrite(buf, adr - baseAddr, sz); 130 return (0); // Finished without Errors 131 } 132 133 /* 134 * Verify Flash Contents 135 * Parameter: adr: Start Address 136 * sz: Size (in bytes) 137 * buf: Data 138 * Return Value: (adr+sz) - OK, Failed Address 139 */ 140 141 /* 142 Verify function is obsolete because all other function leave 143 the SPIFI in memory mode so a memory compare could be used. 144 */ 145 unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf) 146 { 147 int i; 148 SPI_FLASH_BufferRead(auxBuf, adr - baseAddr, sz); 149 for (i = 0; i < PAGE_SIZE; i++) { 150 if (auxBuf[i] != buf[i]) { 151 return (adr + i); // Verification Failed (return address) 152 } 153 } 154 return (adr + sz); // Done successfully 155 }
修改好适配自己的硬件接口,编译会生成 .FLM格式的烧写算法文件,实际是通过如下命令生成的
IDE烧录:烧写程序时选择刚才生成的算法文件即可
J-Flash烧录:拷贝刚生成的算法到JLink/Devices目录下,指定算法文件如下图
方式二、使用特定工具如STM32CubeProgrammer,将数据直接烧写至外部flash
烧写算法制作工程如下:
主要实现两个文件,接口实现文件 <Loader_Src.c> 和设备描述文件 <Dev_Inf.c>
Dev_Inf.c
1 #include "Dev_Inf.h" 2 3 /* This structure containes information used by ST-LINK Utility to program and erase the device */ 4 #if defined (__ICCARM__) 5 __root struct StorageInfo const StorageInfo = { 6 #else 7 struct StorageInfo const StorageInfo = { 8 #endif 9 "M25P64_STM3210E-EVAL", // Device Name + version number 10 SPI_FLASH, // Device Type 11 0x00000000, // Device Start Address 12 0x00800000, // Device Size in Bytes (8MBytes/64Mbits) 13 0x00000100, // Programming Page Size 16Bytes 14 0xFF, // Initial Content of Erased Memory 15 // Specify Size and Address of Sectors (view example below) 16 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes 17 0x00000000, 0x00000000, 18 }; 19 20 /* Sector coding example 21 A device with succives 16 Sectors of 1KBytes, 128 Sectors of 16 KBytes, 22 8 Sectors of 2KBytes and 16384 Sectors of 8KBytes 23 24 0x00000010, 0x00000400, // 16 Sectors of 1KBytes 25 0x00000080, 0x00004000, // 128 Sectors of 16 KBytes 26 0x00000008, 0x00000800, // 8 Sectors of 2KBytes 27 0x00004000, 0x00002000, // 16384 Sectors of 8KBytes 28 0x00000000, 0x00000000, // end 29 */
Loader_Src.c
1 #include "stm32f10x.h" 2 #include "stm32_eval_spi_flash.h" 3 #include "stm3210e_eval.h" 4 5 6 /** 7 * Description : 8 * Initilize the MCU Clock, the GPIO Pins corresponding to the 9 * device and initilize the FSMC with the chosen configuration 10 * Inputs : 11 * None 12 * outputs : 13 * R0 : "1" : Operation succeeded 14 * "0" : Operation failure 15 * Note: Mandatory for all types of device 16 */ 17 int Init (void) 18 { 19 SystemInit(); 20 sFLASH_Init(); 21 return 1; 22 } 23 24 25 /** 26 * Description : 27 * Read data from the device 28 * Inputs : 29 * Address : Write location 30 * Size : Length in bytes 31 * buffer : Address where to get the data to write 32 * outputs : 33 * R0 : "1" : Operation succeeded 34 * "0" : Operation failure 35 * Note: Mandatory for all types except SRAM and PSRAM 36 */ 37 int Read (uint32_t Address, uint32_t Size, uint8_t* buffer) 38 { 39 sFLASH_ReadBuffer(buffer, Address, Size); 40 return 1; 41 } 42 43 44 /** 45 * Description : 46 * Write data from the device 47 * Inputs : 48 * Address : Write location 49 * Size : Length in bytes 50 * buffer : Address where to get the data to write 51 * outputs : 52 * R0 : "1" : Operation succeeded 53 * "0" : Operation failure 54 * Note: Mandatory for all types except SRAM and PSRAM 55 */ 56 int Write (uint32_t Address, uint32_t Size, uint8_t* buffer) 57 { 58 sFLASH_WriteBuffer(buffer, Address, Size); 59 return 1; 60 } 61 62 63 /** 64 * Description : 65 * Erase a full sector in the device 66 * Inputs : 67 * None 68 * outputs : 69 * R0 : "1" : Operation succeeded 70 * "0" : Operation failure 71 * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH 72 */ 73 int MassErase (void) 74 { 75 sFLASH_EraseBulk(); 76 return 1; 77 } 78 79 /** 80 * Description : 81 * Erase a full sector in the device 82 * Inputs : 83 * SectrorAddress : Start of sector 84 * Size : Size (in WORD) 85 * InitVal : Initial CRC value 86 * outputs : 87 * R0 : "1" : Operation succeeded 88 * "0" : Operation failure 89 * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH 90 */ 91 int SectorErase (uint32_t EraseStartAddress, uint32_t EraseEndAddress) 92 { 93 EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x10000; 94 95 while (EraseEndAddress >= EraseStartAddress) { 96 sFLASH_EraseSector(EraseStartAddress); 97 EraseStartAddress += 0x10000; 98 } 99 100 return 1; 101 } 102 103 /** 104 * Description : 105 * Calculates checksum value of the memory zone 106 * Inputs : 107 * StartAddress : Flash start address 108 * Size : Size (in WORD) 109 * InitVal : Initial CRC value 110 * outputs : 111 * R0 : Checksum value 112 * Note: Optional for all types of device 113 */ 114 uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal) 115 { 116 uint8_t missalignementAddress = StartAddress % 4; 117 uint8_t missalignementSize = Size ; 118 int cnt; 119 uint32_t Val; 120 uint8_t value; 121 122 StartAddress -= StartAddress % 4; 123 Size += (Size % 4 == 0) ? 0 : 4 - (Size % 4); 124 125 for(cnt = 0; cnt < Size ; cnt += 4) { 126 sFLASH_ReadBuffer(&value, StartAddress, 1); 127 Val = value; 128 sFLASH_ReadBuffer(&value, StartAddress + 1, 1); 129 Val += value << 8; 130 sFLASH_ReadBuffer(&value, StartAddress + 2, 1); 131 Val += value << 16; 132 sFLASH_ReadBuffer(&value, StartAddress + 3, 1); 133 Val += value << 24; 134 135 if(missalignementAddress) { 136 switch (missalignementAddress) { 137 case 1: 138 InitVal += (uint8_t) (Val >> 8 & 0xff); 139 InitVal += (uint8_t) (Val >> 16 & 0xff); 140 InitVal += (uint8_t) (Val >> 24 & 0xff); 141 missalignementAddress -= 1; 142 break; 143 144 case 2: 145 InitVal += (uint8_t) (Val >> 16 & 0xff); 146 InitVal += (uint8_t) (Val >> 24 & 0xff); 147 missalignementAddress -= 2; 148 break; 149 150 case 3: 151 InitVal += (uint8_t) (Val >> 24 & 0xff); 152 missalignementAddress -= 3; 153 break; 154 } 155 } else if((Size - missalignementSize) % 4 && (Size - cnt) <= 4) { 156 switch (Size - missalignementSize) { 157 case 1: 158 InitVal += (uint8_t) Val; 159 InitVal += (uint8_t) (Val >> 8 & 0xff); 160 InitVal += (uint8_t) (Val >> 16 & 0xff); 161 missalignementSize -= 1; 162 break; 163 164 case 2: 165 InitVal += (uint8_t) Val; 166 InitVal += (uint8_t) (Val >> 8 & 0xff); 167 missalignementSize -= 2; 168 break; 169 170 case 3: 171 InitVal += (uint8_t) Val; 172 missalignementSize -= 3; 173 break; 174 } 175 } else { 176 InitVal += (uint8_t) Val; 177 InitVal += (uint8_t) (Val >> 8 & 0xff); 178 InitVal += (uint8_t) (Val >> 16 & 0xff); 179 InitVal += (uint8_t) (Val >> 24 & 0xff); 180 } 181 182 StartAddress += 4; 183 } 184 185 return (InitVal); 186 } 187 188 189 /** 190 * Description : 191 * Verify flash memory with RAM buffer and calculates checksum value of 192 * the programmed memory 193 * Inputs : 194 * FlashAddr : Flash address 195 * RAMBufferAddr : RAM buffer address 196 * Size : Size (in WORD) 197 * InitVal : Initial CRC value 198 * outputs : 199 * R0 : Operation failed (address of failure) 200 * R1 : Checksum value 201 * Note: Optional for all types of device 202 */ 203 uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement) 204 { 205 uint32_t InitVal = 0; 206 uint32_t VerifiedData = 0; 207 uint8_t TmpBuffer = 0x00; 208 uint64_t checksum; 209 Size *= 4; 210 211 checksum = CheckSum((uint32_t)MemoryAddr + (missalignement & 0xf), Size - ((missalignement >> 16) & 0xF), InitVal); 212 213 while (Size > VerifiedData) { 214 sFLASH_ReadBuffer(&TmpBuffer, MemoryAddr + VerifiedData, 1); 215 216 if (TmpBuffer != *((uint8_t*)RAMBufferAddr + VerifiedData)) 217 return ((checksum << 32) + MemoryAddr + VerifiedData); 218 219 VerifiedData++; 220 } 221 222 return (checksum << 32); 223 }
修改好适配自己的硬件接口,编译会生成 .stldr格式的烧写算法文件,实际是通过如下命令生成的
用法:烧写程序时选择刚才生成的算法文件即可
烧录方法总结
IDE烧写方式通常在功能开发验证阶段使用,且在有UI资源如字库图片时并不方便,在项目量产时效率低下
故需要借助专用工具,如J-Flash、ST工具CubeProgrammer,若有多个文件需要烧写时,就需要做文件合并准备
文件合并方法众多
- 可使用J-Flash、CubeProgranmmer工具的文件另存为,将裸文件bin转为带地址信息的hex,然后可徒手撸,即直接将文件复制粘贴合并,亦可通过J-Flash的文件合并功能
- 可借助合并工具
具体可参考:
求助硬汉app和boot合并成一个bin文件问题 - STM32H7 - 硬汉嵌入式论坛 - Powered by Discuz! (armbbs.cn)
二进制文件合并工具1.1 - 开发环境 - 硬汉嵌入式论坛 - Powered by Discuz! (armbbs.cn)
确保合并后文件烧写到各自指定地址,需要修改烧写算法支持
// 地址信息指定,确保地址范围包括个文件烧写地址
struct FlashDevice const FlashDevice = {
0x00000000, // Device Start Address
0x01000000, // Device Size in Bytes
}
// 擦除接口根据各文件地址做对应处理,比如mcu内部flash、外部spi-flash
int erase(unsigned long addr)
{
if (addr < spi_flash_address) {
// 擦除内部flash
} else {
// 擦除外部flash
}
}
// 烧写接口同擦除,地址来源hex文件
int progame(unsigned long addr)
{
if (addr < spi_flash_address) {
// 擦除内部flash
} else {
// 擦除外部flash
}
}
再牛逼的梦想也架不住傻逼似的坚持