├── .github └── workflows │ └── cmake-single-platform.yml ├── LICENSE ├── SConscript ├── docs └── readme.md ├── gen_pack.sh ├── inc └── flash_blob.h ├── kk.flash_blob.pdsc ├── port ├── GD32 │ ├── GD32_FLASH_DEV.c │ └── GD32_FLASH_PRG.c └── STM32 │ ├── STM32_FLASH_DEV.c │ └── STM32_FLASH_DRV.c ├── readme.md └── src ├── SConscript └── flash_blob.c /.github/workflows/cmake-single-platform.yml: -------------------------------------------------------------------------------- 1 | name: Build documentation and pack 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | release: 10 | types: [published] 11 | 12 | jobs: 13 | pack: 14 | name: Generate pack 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Fetch tags 23 | if: ${{ github.event_name == 'release' }} 24 | run: | 25 | git fetch --tags --force 26 | 27 | - name: Make gen_pack.sh executable 28 | run: chmod +x ./gen_pack.sh 29 | 30 | - name: Generate Pack 31 | uses: Open-CMSIS-Pack/gen-pack-action@main 32 | with: 33 | doxygen-version: 1.9.2 34 | packchk-version: 1.3.96 35 | gen-pack-script: ./gen_pack.sh 36 | gen-pack-output: ./output 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | # RT-Thread building script for bridge 2 | 3 | import os 4 | from building import * 5 | 6 | cwd = GetCurrentDir() 7 | objs = [] 8 | list = os.listdir(cwd) 9 | 10 | if GetDepend('PKG_USING_FLASH_BLOB'): 11 | for d in list: 12 | path = os.path.join(cwd, d) 13 | if os.path.isfile(os.path.join(path, 'SConscript')): 14 | objs = objs + SConscript(os.path.join(d, 'SConscript')) 15 | 16 | Return('objs') -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # 前言 4 | 在进行Flash操作时,一般我们需要设计一套Flash抽象层,至少要包括flash的init,read、write、erase这些操作。但每更换一个单片机或者flash器件就要额外去编写flash驱动去适配init,read、write、erase。尽管有会者不难的属性加持,但适配所有的单片机或者flash器件,工作量也可想而知。 5 | 6 | 本文为大家提供一个适配几乎任意单片机型号的flash驱动,之所以说是几乎,是因为我们这次要借东风,而这个东风就是Keil的FLM文件。 7 | 8 | 项目开源地址:[https://gitee.com/Aladdin-Wang/flash_blob](https://gitee.com/Aladdin-Wang/flash_blob) 9 | 10 | --- 11 | 12 | # 一、FLM文件是什么? 13 | 熟悉Keil的朋友们都知道,当我们要下载编译好的镜像到Flash时,首先要做的一步就是选择合适的Flash下载算法,而这个算法本身就是一个FLM文件: 14 | ![](https://img-blog.csdnimg.cn/5a44095e41044d1fb0aa4b2860c7bb36.png) 15 | 所谓Flash下载算法,是负责擦除,下载应用数据到flash的一个软件。而Keil往往会集成不少FLM文件以支持大多数的flash型号。 16 | 当然,这些算法也是根据不同型号的flash所编写的。只不过,前人们已经为我们种好了大树,我们可以直接在树下乘凉了。 17 | 18 | # 二、FLM文件结构 19 | Keil规定了FLM文件的构成,它是一成不变的,我们才可以放心的对文件本身进行解析,并为自己所用。 20 | 21 | 生成FLM文件的程序中,有两个非常重要的文件,分别是 22 | - FlashPrg.c中包含编程算法。 23 | - FlashDev.c中包含设备参数。 24 | ## 1.FlashPrg.c 25 | FlashPrg.c文件包含强制性flash编程函数Init、UnInit、EraseSector和ProgramPage。可选地,根据设备特性,可以实现函数EraseChip、BlankCheck和Verify 。 26 | | Function Name | Indication | Description | 27 | | ------------- | ---------- | ------------------------------------------------------------ | 28 | | BlankCheck | optional | Check and compare patterns. | 29 | | EraseChip | optional | Delete entire Flash memory content. | 30 | | EraseSector | mandatory | Delete Flash memory content of a specific sector. | 31 | | Init | mandatory | Initialize and prepare device for Flash programming. | 32 | | ProgramPage | mandatory | Write the application into the Flash memory. | 33 | | UnInit | mandatory | De-initialize the microcontroller after one of the Flash programming steps. | 34 | | Verify | optional | Compare Flash memory content with the program code. | 35 | 36 | 其中有4个是必须要有的,我们来逐一说明: 37 | 1. Init 38 | 函数Init为 Flash 编程初始化微控制器。每当尝试将程序下载到 Flash 时都会调用它。 39 | ```c 40 | int Init (unsigned long adr, unsigned long clk, unsigned long fnc); 41 | ``` 42 | 43 | 44 | 参数adr指定设备的基址。 45 | 46 | 参数clk指定用于编程设备的时钟频率。 47 | 48 | 参数fnc是一个数字: 49 | 50 | 1 代表擦除。 51 | 2代表程序。 52 | 3代表验证。 53 | 54 | Code Example: 55 | 56 | ```c 57 | int Init (unsigned long adr, unsigned long clk, unsigned long fnc) { 58 | 59 | // Zero Wait State 60 | FLASH->ACR = 0x00000000; 61 | 62 | // Unlock Flash 63 | FLASH->KEYR = FLASH_KEY1; 64 | FLASH->KEYR = FLASH_KEY2; 65 | 66 | // Test if IWDG is running (IWDG in HW mode) 67 | if ((FLASH->OBR & 0x04) == 0x00) { 68 | // Set IWDG time out to ~32.768 second 69 | IWDG->KR = 0x5555; // Enable write access to IWDG_PR and IWDG_RLR 70 | IWDG->PR = 0x06; // Set prescaler to 256 71 | IWDG->RLR = 4095; // Set reload value to 4095 72 | } 73 | 74 | return (0); 75 | } 76 | ``` 77 | 2. ProgramPage 78 | 函数ProgramPage用于将代码写入闪存。它被调用以将程序下载到 Flash。由于flash通常以块或页的形式组织,因此函数ProgramPage的参数不得跨越这些闪存页的对齐边界。页面大小在结构 FlashDevice 中指定,值为Program Page Size。 79 | 80 | ```c 81 | int ProgramPage ( unsigned long adr, unsigned long sz, unsigned char *buf); 82 | ``` 83 | 84 | 参数adr指定要编程的页面的起始地址。它由主机编程系统与flash页面的起始地址对齐。 85 | 86 | 参数sz指定数据缓冲区中的数据大小。主机编程系统确保不跨越页面边界。 87 | 88 | 参数buf指向包含要编程的数据的数据缓冲区。 89 | 90 | Code Example: 91 | 92 | ```c 93 | int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) { 94 | 95 | sz = (sz + 1) & ~1; // Adjust size for Half Words 96 | 97 | while (sz) { 98 | 99 | FLASH->CR |= FLASH_PG; // Programming Enabled 100 | 101 | M16(adr) = *((unsigned short *)buf); // Program Half Word 102 | while (FLASH->SR & FLASH_BSY); 103 | 104 | FLASH->CR &= ~FLASH_PG; // Programming Disabled 105 | 106 | // Check for Errors 107 | if (FLASH->SR & (FLASH_PGERR | FLASH_WRPRTERR)) { 108 | FLASH->SR |= FLASH_PGERR | FLASH_WRPRTERR; 109 | return (1); // Failed 110 | } 111 | 112 | // Go to next Half Word 113 | adr += 2; 114 | buf += 2; 115 | sz -= 2; 116 | } 117 | 118 | return (0); // Done 119 | } 120 | ``` 121 | 3. EraseSector 122 | 函数EraseSector删除从参数adr指定的地址开始的扇区内容。 123 | 124 | ```c 125 | int EraseSector (unsigned long adr); 126 | ``` 127 | 128 | 参数adr扇区地址 129 | 130 | Code Example: 131 | ```c 132 | int EraseSector (unsigned long adr) { 133 | 134 | FLASH->CR |= FLASH_PER; // Page Erase Enabled 135 | FLASH->AR = adr; // Page Address 136 | FLASH->CR |= FLASH_STRT; // Start Erase 137 | 138 | while (FLASH->SR & FLASH_BSY) { 139 | IWDG->KR = 0xAAAA; // Reload IWDG 140 | } 141 | 142 | FLASH->CR &= ~FLASH_PER; // Page Erase Disabled 143 | 144 | return (0); // Done 145 | } 146 | ``` 147 | 4. UnInit 148 | 函数UnInit取消初始化微控制器,并在擦除、编程或验证步骤结束时调用。 149 | ```c 150 | int UnInit (unsigned long fnc); 151 | ``` 152 | 153 | Code Example 154 | 155 | ```c 156 | int UnInit (unsigned long fnc) { 157 | 158 | // Lock Flash 159 | FLASH->CR |= FLASH_LOCK; 160 | 161 | return (0); 162 | } 163 | ``` 164 | ## 2.FlashPrg.c 165 | 文件FlashDev.c包含以下参数定义: 166 | 1. Flash编程功能。 167 | 2. FlashDevice结构: 168 | ```c 169 | struct FlashDevice const FlashDevice = { 170 | FLASH_DRV_VERS, // 驱动版本,请勿修改! 171 | "New Device 256kB Flash" , // 设备名称 172 | ONCHIP, // 设备类型 173 | 0x00000000, // 设备起始地址 174 | 0x00040000, // 以字节为单位的设备大小 (256kB) 175 | 1024, // 编程页面大小 176 | 0, // 保留,必须为0 177 | 0xFF, // 已擦除内存的初始内容 178 | 100, // 程序页面超时 100 毫秒 179 | 3000, // 擦除扇区超时 3000 毫秒 180 | 181 | // 指定扇区的大小和地址 182 | 0x002000, 0x000000, // 扇区大小 8kB(8 个扇区) 183 | 0x010000, 0x010000, // 扇区大小 64kB(2 个扇区) 184 | 0x002000, 0x030000, // 扇区大小 8kB(8 个扇区) 185 | SECTOR_END 186 | }; 187 | ``` 188 | Device Name通常显示在工具中,用于识别 Flash 算法。确保此名称反映设备名称。 189 | 编程页面大小指定使用函数ProgramPage进行编程 的块大小。对于块大小较小的设备,最好指定物理块大小的倍数,因为这可以减少与目标的通信开销。快速编程的最佳块大小为 1024 字节,但系统本身并不限制此大小值。 190 | # 三、解析FLM文件 191 | ## 1.解析flm文件 192 | 下面让我们解析一下现有的FLM文件,以STM32F4xx_1024.FLM为例: 193 | 将ARM:CMSIS Pack文件夹(通常在D:\Users\Administrator\AppData\Local\Arm\Packs\Keil\STM32F4xx_DFP\2.15.0\CMSIS\Flash)中的内容复制到一个新文件夹中。 194 | 195 | 打开命令行工具,输入arm-none-eabi-readelf -a STM32F4xx_1024.FLM: 196 | 197 | ```c 198 | $ arm-none-eabi-readelf -a STM32F4xx_1024.FLM 199 | ELF Header: 200 | Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 201 | Class: ELF32 202 | Data: 2's complement, little endian 203 | Version: 1 (current) 204 | OS/ABI: UNIX - System V 205 | ABI Version: 0 206 | Type: EXEC (Executable file) 207 | Machine: ARM 208 | Version: 0x1 209 | Entry point address: 0x0 210 | Start of program headers: 12172 (bytes into file) 211 | Start of section headers: 12236 (bytes into file) 212 | Flags: 0x5000000, Version5 EABI 213 | Size of this header: 52 (bytes) 214 | Size of program headers: 32 (bytes) 215 | Number of program headers: 2 216 | Size of section headers: 40 (bytes) 217 | Number of section headers: 16 218 | Section header string table index: 15 219 | 220 | Section Headers: 221 | [Nr] Name Type Addr Off Size ES Flg Lk Inf Al 222 | [ 0] NULL 00000000 000000 000000 00 0 0 0 223 | [ 1] PrgCode PROGBITS 00000000 000034 000144 00 AX 0 0 4 224 | [ 2] PrgData PROGBITS 00000144 000178 000004 00 WA 0 0 4 225 | [ 3] DevDscr PROGBITS 00000148 00017c 0010a0 00 A 0 0 4 226 | [ 4] .debug_abbrev PROGBITS 00000000 00121c 0005a4 00 0 0 1 227 | [ 5] .debug_frame PROGBITS 00000000 0017c0 000104 00 0 0 1 228 | [ 6] .debug_info PROGBITS 00000000 0018c4 00064c 00 0 0 1 229 | [ 7] .debug_line PROGBITS 00000000 001f10 000218 00 0 0 1 230 | [ 8] .debug_loc PROGBITS 00000000 002128 0001b8 00 0 0 1 231 | [ 9] .debug_macinfo PROGBITS 00000000 0022e0 000614 00 0 0 1 232 | [10] .debug_pubnames PROGBITS 00000000 0028f4 000096 00 0 0 1 233 | [11] .symtab SYMTAB 00000000 00298c 000110 10 12 9 4 234 | [12] .strtab STRTAB 00000000 002a9c 000100 00 0 0 1 235 | [13] .note NOTE 00000000 002b9c 00001c 00 0 0 4 236 | [14] .comment PROGBITS 00000000 002bb8 000334 00 0 0 1 237 | [15] .shstrtab STRTAB 00000000 002eec 0000a0 00 0 0 1 238 | Key to Flags: 239 | W (write), A (alloc), X (execute), M (merge), S (strings), I (info), 240 | L (link order), O (extra OS processing required), G (group), T (TLS), 241 | C (compressed), x (unknown), o (OS specific), E (exclude), 242 | y (purecode), p (processor specific) 243 | 244 | There are no section groups in this file. 245 | 246 | Program Headers: 247 | Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align 248 | LOAD 0x000034 0x00000000 0x00000000 0x00148 0x00148 RWE 0x4 249 | LOAD 0x00017c 0x00000148 0x00000148 0x010a0 0x010a0 R 0x4 250 | 251 | Section to Segment mapping: 252 | Segment Sections... 253 | 00 PrgCode PrgData 254 | 01 DevDscr 255 | 256 | There is no dynamic section in this file. 257 | 258 | There are no relocations in this file. 259 | 260 | There are no unwind sections in this file. 261 | 262 | Symbol table '.symtab' contains 17 entries: 263 | Num: Value Size Type Bind Vis Ndx Name 264 | 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 265 | 1: 00000000 0 NOTYPE LOCAL DEFAULT 1 $t 266 | 2: 00000122 0 NOTYPE LOCAL DEFAULT 1 $d 267 | 3: 00000144 0 NOTYPE LOCAL DEFAULT 2 $d.realdata 268 | 4: 00000148 0 NOTYPE LOCAL DEFAULT 3 $d.realdata 269 | 5: 00000000 0 FILE LOCAL DEFAULT ABS FlashPrg.c 270 | 6: 00000000 0 SECTION LOCAL DEFAULT 1 .text 271 | 7: 00000000 0 FILE LOCAL DEFAULT ABS FlashDev.c 272 | 8: 00000148 4256 SECTION LOCAL DEFAULT 3 .constdata 273 | 9: 00000000 0 NOTYPE GLOBAL HIDDEN ABS BuildAttributes$$THM_ISAv 274 | 10: 00000001 28 FUNC GLOBAL HIDDEN 1 GetSecNum 275 | 11: 0000001d 46 FUNC GLOBAL HIDDEN 1 Init 276 | 12: 0000004b 14 FUNC GLOBAL HIDDEN 1 UnInit 277 | 13: 00000059 44 FUNC GLOBAL HIDDEN 1 EraseChip 278 | 14: 00000085 76 FUNC GLOBAL HIDDEN 1 EraseSector 279 | 15: 000000d1 82 FUNC GLOBAL HIDDEN 1 ProgramPage 280 | 16: 00000148 4256 OBJECT GLOBAL HIDDEN 3 FlashDevice 281 | 282 | No version information found in this file. 283 | 284 | Displaying notes found at file offset 0x00002b9c with length 0x0000001c: 285 | Owner Data size Description 286 | ARM 0x0000000c Unknown note type: (0x40000000) 287 | ``` 288 | 通过Symbol table信息我们可以找到Init、UnInit、EraseSector和ProgramPage函数所在的位置。 289 | 290 | 我们还需要根据Section Headers所描述的位置提取出Prgcode(代码),PrgData(数据),DevDscr(设备描述)的信息。 291 | 292 | 在命令行中输入arm-none-eabi-objdum -s -d STM32F4xx_1024.FLM:-s参数可以将所有段的内容一十六进制方式打印出来,-d参数可以将所有包含指令的段反汇编。 293 | 294 | ```c 295 | $ arm-none-eabi-objdump -s -d STM32F4xx_1024.FLM 296 | 297 | STM32F4xx_1024.FLM: file format elf32-littlearm 298 | 299 | Contents of section PrgCode: 300 | 0000 0003000e 202802d3 4009001d 70471028 .... (..@...pG.( 301 | 0010 02d30009 c01c7047 80087047 42484149 ......pG..pGBHAI 302 | 0020 41604249 41600021 0160c168 f0221143 A`BIA`.!.`.h.".C 303 | 0030 c1604069 800606d4 3e483d49 01600621 .`@i....>H=I.`.! 304 | 0040 41603d49 81600020 70473748 01694205 A`=I.`. pG7H.iB. 305 | 0050 11430161 00207047 10b53348 01690424 .C.a. pG..3H.i.$ 306 | 0060 21430161 0169a203 11430161 3349314a !C.a.i...C.a3I1J 307 | 0070 00e01160 c368db03 fbd40169 a1430161 ...`.h.....i.C.a 308 | 0080 002010bd 30b5fff7 bbff2749 ca68f023 . ..0.....'I.h.# 309 | 0090 1a43ca60 02240c61 0a690007 400e0243 .C.`.$.a.i..@..C 310 | 00a0 0a610869 e2031043 08612448 214a00e0 .a.i...C.a$H!J.. 311 | 00b0 1060cd68 ed03fbd4 0869a043 0861c868 .`.h.....i.C.a.h 312 | 00c0 0006000f 03d0c868 1843c860 012030bd .......h.C.`. 0. 313 | 00d0 70b5154d c91c8908 eb688900 f0263343 p..M.....h...&3C 314 | 00e0 eb600023 2b61164b 17e02c69 1c432c61 .`.#+a.K..,i.C,a 315 | 00f0 14680460 ec68e403 fcd42c69 64086400 .h.`.h....,id.d. 316 | 0100 2c61ec68 2406240f 04d0e868 3043e860 ,a.h$.$....h0C.` 317 | 0110 012070bd 001d121d 091f0029 e5d10020 . p........)... 318 | 0120 70bd0000 23016745 003c0240 ab89efcd p...#.gE.<.@.... 319 | 0130 55550000 00300040 ff0f0000 aaaa0000 UU...0.@........ 320 | 0140 01020000 .... 321 | Contents of section PrgData: 322 | 0144 00000000 .... 323 | Contents of section DevDscr: 324 | 0148 01015354 4d333246 34787820 466c6173 ..STM32F4xx Flas 325 | 0158 68000000 00000000 00000000 00000000 h............... 326 | 0168 00000000 00000000 00000000 00000000 ................ 327 | 0178 00000000 00000000 00000000 00000000 ................ 328 | 0188 00000000 00000000 00000000 00000000 ................ 329 | 0198 00000000 00000000 00000000 00000000 ................ 330 | 01a8 00000000 00000000 00000000 00000000 ................ 331 | 01b8 00000000 00000000 00000000 00000000 ................ 332 | 01c8 00000100 00000008 00001000 00040000 ................ 333 | 01d8 00000000 ff000000 64000000 70170000 ........d...p... 334 | 01e8 00400000 00000000 00000100 00000100 .@.............. 335 | 01f8 00000200 00000200 ffffffff ffffffff ................ 336 | ``` 337 | 我们所需要的正是以上信息,接下来的任务只需要写一个上位机,将以上文件提取出来即可,这个工具我已经写好,如图: 338 | ![](https://img-blog.csdnimg.cn/e6c2d314ec024033bcf64015220e3f17.png) 339 | 340 | 选择STM32F4xx_1024.FLM,生成STM32F4xx_1024.FLM.c文件,然后直接添加到我们的工程中即可,生成的代码如下: 341 | 342 | ```c 343 | #include "flash_blob.h" 344 | 345 | #define OPS_OFFSET ((uint32_t)&flash_code) 346 | 347 | #define DEV_OFFSET ((uint32_t)&flash_dev) 348 | 349 | #define RAM_OFFSET ((uint32_t)&rw_data) 350 | 351 | static const uint32_t flash_code[] = 352 | { 353 | 0X0E000300,0XD3022820,0X1D000940,0X28104770,0X0900D302,0X47701CC0,0X47700880,0X49414842, 354 | 0X49426041,0X21036041,0X68C16001,0X431122F0,0X694060C1,0XD4060680,0X493D483E,0X21066001, 355 | 0X493D6041,0X20006081,0X48374770,0X05426901,0X61014311,0X47702000,0X4833B510,0X24046901, 356 | 0X61014321,0X03A26901,0X61014311,0X4A314933,0X6011E000,0X03DB68C3,0X6901D4FB,0X610143A1, 357 | 0XBD102000,0XF7FFB530,0X4927FFBB,0X23F068CA,0X60CA431A,0X610C2402,0X0700690A,0X43020E40, 358 | 0X6908610A,0X431003E2,0X48246108,0XE0004A21,0X68CD6010,0XD4FB03ED,0X43A06908,0X68C86108, 359 | 0X0F000600,0X68C8D003,0X60C84318,0XBD302001,0X4D15B570,0X08891CC9,0X008968EB,0X433326F0, 360 | 0X230060EB,0X4B16612B,0X692CE017,0X612C431C,0X60046814,0X03E468EC,0X692CD4FC,0X00640864, 361 | 0X68EC612C,0X0F240624,0X68E8D004,0X60E84330,0XBD702001,0X1D121D00,0X29001F09,0X2000D1E5, 362 | 0X0000BD70,0X45670123,0X40023C00,0XCDEF89AB,0X00005555,0X40003000,0X00000FFF,0X0000AAAA, 363 | 0X00000201,0X00000000, 364 | }; 365 | 366 | static const uint32_t flash_dev[] = 367 | { 368 | 0X54530101,0X4632334D,0X20787834,0X73616C46,0X00000068,0X00000000,0X00000000,0X00000000, 369 | 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 370 | 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 371 | 0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000,0X00000000, 372 | 0X00010000,0X08000000,0X00100000,0X00000400,0X00000000,0X000000FF,0X00000064,0X00001770, 373 | 0X00004000,0X00000000,0X00010000,0X00010000,0X00020000,0X00020000,0XFFFFFFFF,0XFFFFFFFF, 374 | }; 375 | 376 | static uint32_t rw_data[] = 377 | { 378 | 0X00000000, 379 | }; 380 | 381 | static flash_blob_device_t flash_device = 382 | { 383 | (void*)(OPS_OFFSET + 0X001D), // Init 384 | (void*)(OPS_OFFSET + 0X004B), // UnInit 385 | (void*)(OPS_OFFSET + 0X0059), // EraseChip 386 | (void*)(OPS_OFFSET + 0X0085), // EraseSector 387 | (void*)(OPS_OFFSET + 0X00D1), // ProgramPage 388 | (void*)(DEV_OFFSET), 389 | (void*)(RAM_OFFSET), 390 | }; 391 | static int flash_blob_device_register(void) 392 | { 393 | flash_dev_register(&flash_device); 394 | return 0 ; 395 | } 396 | INIT_BOARD_EXPORT(flash_blob_device_register); 397 | 398 | ``` 399 | 400 | # 四、设计flash驱动抽象层 401 | 接下来,为了方便后续使用,需要设计一个flash驱动抽象层,代码如下: 402 | flash_blob.h 403 | ```c 404 | #ifndef FLASH_BLOB_H 405 | #define FLASH_BLOB_H 406 | #include "rtthread.h" 407 | #include 408 | #include 409 | #include 410 | 411 | #define VERS 1 // Interface Version 1.01 412 | 413 | #define UNKNOWN 0 // Unknown 414 | #define ONCHIP 1 // On-chip Flash Memory 415 | #define EXT8BIT 2 // External Flash Device on 8-bit Bus 416 | #define EXT16BIT 3 // External Flash Device on 16-bit Bus 417 | #define EXT32BIT 4 // External Flash Device on 32-bit Bus 418 | #define EXTSPI 5 // External Flash Device on SPI 419 | 420 | #define SECTOR_NUM 16 // Max Number of Sector Items 421 | 422 | struct FlashSectors { 423 | unsigned long szSector; // Sector Size in Bytes 424 | unsigned long AddrSector; // Address of Sector 425 | }; 426 | 427 | typedef struct FlashDevice { 428 | unsigned short Vers; // Version Number and Architecture 429 | char DevName[128]; // Device Name and Description 430 | unsigned short DevType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ... 431 | unsigned long DevAdr; // Default Device Start Address 432 | unsigned long szDev; // Total Size of Device 433 | unsigned long szPage; // Programming Page Size 434 | unsigned long Res; // Reserved for future Extension 435 | unsigned char valEmpty; // Content of Erased Memory 436 | 437 | unsigned long toProg; // Time Out of Program Page Function 438 | unsigned long toErase; // Time Out of Erase Sector Function 439 | 440 | struct FlashSectors sectors[SECTOR_NUM]; 441 | }flash_dev_t; 442 | 443 | typedef struct { 444 | int (*Init)(uint32_t adr, uint32_t clk, uint32_t fnc); 445 | int (*UnInit)(uint32_t fnc); 446 | int (*EraseChip)(void); 447 | int (*EraseSector)(uint32_t adr); 448 | int (*ProgramPage)(uint32_t adr, uint32_t sz, uint8_t* buf); 449 | }flash_ops_t; 450 | 451 | typedef struct 452 | { 453 | flash_ops_t tFlashops; 454 | flash_dev_t *ptFlashDev; 455 | int *pPrgData; 456 | rt_slist_t slist; 457 | }flash_blob_t; 458 | 459 | extern void flash_dev_register(flash_blob_t *ptFlashDevice); 460 | extern bool target_flash_init(uint32_t flash_start, int32_t size); 461 | extern bool target_flash_uninit(uint32_t flash_start); 462 | extern int32_t target_flash_write(uint32_t addr, const uint8_t *buf, int32_t size); 463 | extern int32_t target_flash_erase(uint32_t addr, int32_t size); 464 | extern int32_t target_flash_read(uint32_t addr, const uint8_t *buf, int32_t size); 465 | #endif 466 | 467 | ``` 468 | flash_blob.c 469 | 470 | ```c 471 | #include "flash_blob.h" 472 | #include "stdio.h" 473 | #include "stdlib.h" 474 | #include "string.h" 475 | 476 | static bool s_bIsInit = false; 477 | static rt_slist_t _slist_head = RT_SLIST_OBJECT_INIT(_slist_head); 478 | register int *pPrgDataBase __asm("r9"); 479 | 480 | void flash_dev_register(flash_blob_t *ptFlashDevice) 481 | { 482 | rt_slist_init(&(ptFlashDevice->slist)); 483 | rt_slist_append(&_slist_head, &(ptFlashDevice->slist)); 484 | } 485 | 486 | static flash_blob_t * flash_dev_find(uint32_t flash_start) 487 | { 488 | rt_slist_t *node; 489 | rt_slist_for_each(node, &_slist_head) { 490 | flash_blob_t *ptFlashDevice = rt_slist_entry(node, flash_blob_t, slist); 491 | 492 | if(flash_start >= ptFlashDevice->ptFlashDev->DevAdr && 493 | flash_start < ptFlashDevice->ptFlashDev->DevAdr + ptFlashDevice->ptFlashDev->szDev) { 494 | return ptFlashDevice; 495 | } 496 | } 497 | return NULL; 498 | } 499 | 500 | bool target_flash_init(uint32_t flash_start, int32_t size) 501 | { 502 | if (flash_start % 4 != 0) { 503 | LOG_E("flash addr must be 4-byte alignment"); 504 | return NULL; 505 | } 506 | 507 | flash_blob_t *ptFlashDevice = flash_dev_find(flash_start); 508 | 509 | if(ptFlashDevice != NULL) { 510 | pPrgDataBase = ptFlashDevice->pPrgData; 511 | ptFlashDevice->tFlashops.Init(flash_start, 0, 0); 512 | return true; 513 | } 514 | 515 | return false; 516 | } 517 | 518 | bool target_flash_uninit(uint32_t flash_start) 519 | { 520 | flash_blob_t *ptFlashDevice = flash_dev_find(flash_start); 521 | 522 | if(ptFlashDevice != NULL) { 523 | pPrgDataBase = ptFlashDevice->pPrgData; 524 | ptFlashDevice->tFlashops.UnInit(flash_start); 525 | return true; 526 | } 527 | 528 | return true; 529 | } 530 | 531 | int target_flash_write(uint32_t addr, const uint8_t *buf, int32_t size) 532 | { 533 | flash_blob_t *ptFlashDevice = flash_dev_find(addr); 534 | 535 | if(ptFlashDevice != NULL) { 536 | pPrgDataBase = ptFlashDevice->pPrgData; 537 | 538 | while(size > 0) { 539 | uint32_t write_size = size > ptFlashDevice->ptFlashDev->szPage ? ptFlashDevice->ptFlashDev->szPage : size; 540 | 541 | if( 0 != ptFlashDevice->tFlashops.ProgramPage(addr, write_size, (uint8_t *)buf)) { 542 | LOG_E("Programming Failed"); 543 | return -1; 544 | } 545 | 546 | addr += write_size; 547 | buf += write_size; 548 | size -= write_size; 549 | } 550 | 551 | return size; 552 | } 553 | 554 | return -1; 555 | } 556 | 557 | int32_t target_flash_read(uint32_t addr, const uint8_t *buf, int32_t size) 558 | { 559 | flash_blob_t *ptFlashDevice; 560 | pPrgDataBase = ptFlashDevice->pPrgData; 561 | return size; 562 | } 563 | 564 | int32_t target_flash_erase(uint32_t addr, int32_t size) 565 | { 566 | int32_t wSector, wRemainLen; 567 | flash_blob_t *ptFlashDevice = flash_dev_find(addr); 568 | 569 | if(ptFlashDevice != NULL) { 570 | if (size > ptFlashDevice->ptFlashDev->szDev) { 571 | LOG_E("erase outrange flash size! addr is (0x%p)\n", (void *)(addr + size)); 572 | return -1; 573 | } 574 | 575 | pPrgDataBase = ptFlashDevice->pPrgData; 576 | wRemainLen = size; 577 | while(wRemainLen > 0) { 578 | if(0 != ptFlashDevice->tFlashops.EraseSector(addr)) { 579 | LOG_E("erase Failed! addr is (0x%p)\n", (void *)addr); 580 | return -1; 581 | } 582 | 583 | for(wSector = 0; wSector < SECTOR_NUM; wSector++) { 584 | if(ptFlashDevice->ptFlashDev->sectors[wSector + 1].szSector == 0XFFFFFFFF ) 585 | break; 586 | 587 | if(((addr - ptFlashDevice->ptFlashDev->DevAdr) < ptFlashDevice->ptFlashDev->sectors[wSector + 1].AddrSector) && 588 | ((addr - ptFlashDevice->ptFlashDev->DevAdr) >= ptFlashDevice->ptFlashDev->sectors[wSector].AddrSector) ) 589 | break; 590 | } 591 | 592 | addr += ptFlashDevice->ptFlashDev->sectors[wSector].szSector; 593 | wRemainLen -= ptFlashDevice->ptFlashDev->sectors[wSector].szSector; 594 | } 595 | 596 | return size; 597 | } 598 | 599 | return -1; 600 | } 601 | 602 | int32_t target_flash_verify (uint32_t addr, uint8_t *buf, int32_t size) 603 | { 604 | 605 | return size; 606 | } 607 | ``` 608 | 609 | # 五、快速使用 610 | 本项目借用了rtthread的自动初始化机制和链表,所以最快的使用方式是直接作为rtthread的软件包使用,使用方法如下: 611 | 612 | 1.在rtthread软件包中找到flash_blob,然后添加进工程。 613 | 2. 通过tools文件下的工具,生成对应的xxx.FLM.c文件,将xxx.FLM.c添加进工程,如果有多个flash器件,可以连续添加。 614 | 注意:多个设备的话每个flash的FlashDevice 的设备起始地址不可重叠,flash抽象层根据地址,自动选择相应的驱动。 615 | 3.由于flash驱动需要占用“r9”寄存器,所以需要在编译选项中添加`-ffixed-r9` 616 | 617 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/78a368cc80b4459c84549da58569a496.png) 618 | 619 | 以上步骤完成后,就可以快速使用了,例如将YMODEM接收到的数据,写到flash中,代码如下: 620 | 621 | 622 | ```c 623 | uint8_t *ymodem_call_back_receive(uint8_t *pchBuffer, uint16_t hwSize) 624 | { 625 | static char *s_pchFileName = NULL, *s_pchFileSize = NULL; 626 | static uint32_t s_wFileSize = 0, s_wRemainLen = 0, s_wOffSet = 0; 627 | 628 | static enum { 629 | START = 0, 630 | RECEIVE, 631 | END, 632 | } s_tState = {START}; 633 | 634 | switch(s_tState) { 635 | case START: 636 | s_wOffSet = 0; 637 | s_pchFileName = (char *)&pchBuffer[0]; 638 | s_pchFileSize = (char *)&pchBuffer[strlen(s_pchFileName) + 1]; 639 | s_wFileSize = atol(s_pchFileSize); 640 | 641 | LOG_D("Ymodem file_name:%s", s_pchFileName); 642 | LOG_D("Ymodem file_size:%d", s_wFileSize); 643 | 644 | if(target_flash_init(APP_PART_ADDR, s_wFileSize) == false) { 645 | LOG_E("target flash uninit."); 646 | return NULL; 647 | } 648 | 649 | if(target_flash_erase(APP_PART_ADDR, s_wFileSize) < 0) { 650 | LOG_E("target flash erase error."); 651 | return NULL; 652 | } 653 | 654 | s_tState = RECEIVE; 655 | break; 656 | 657 | case RECEIVE: 658 | s_wRemainLen = s_wFileSize - s_wOffSet; 659 | 660 | if(hwSize > s_wRemainLen) { 661 | hwSize = s_wRemainLen; 662 | s_tState = END; 663 | } 664 | 665 | if(target_flash_write(APP_PART_ADDR + s_wOffSet, pchBuffer, hwSize) < 0) { 666 | LOG_E("target flash write data error."); 667 | return NULL; 668 | } 669 | s_wOffSet += hwSize; 670 | 671 | break; 672 | 673 | case END: 674 | target_flash_uninit(APP_PART_ADDR); 675 | s_tState = START; 676 | break; 677 | } 678 | 679 | return s_chBuffer; 680 | } 681 | ``` 682 | 683 | -------------------------------------------------------------------------------- /gen_pack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Version: 2.7 3 | # Date: 2023-05-22 4 | # This bash script generates a CMSIS Software Pack: 5 | # 6 | 7 | set -o pipefail 8 | 9 | # Set version of gen pack library 10 | # For available versions see https://github.com/Open-CMSIS-Pack/gen-pack/tags. 11 | # Use the tag name without the prefix "v", e.g., 0.7.0 12 | REQUIRED_GEN_PACK_LIB="0.8.4" 13 | 14 | # Set default command line arguments 15 | DEFAULT_ARGS=(-c "v") 16 | 17 | # Pack warehouse directory - destination 18 | # Default: ./output 19 | # 20 | # PACK_OUTPUT=./output 21 | 22 | # Temporary pack build directory, 23 | # Default: ./build 24 | # 25 | # PACK_BUILD=./build 26 | 27 | # Specify directory names to be added to pack base directory 28 | # An empty list defaults to all folders next to this script. 29 | # Default: empty (all folders) 30 | # 31 | PACK_DIRS=" 32 | src 33 | inc 34 | port 35 | " 36 | 37 | # Specify file names to be added to pack base directory 38 | # Default: empty 39 | # 40 | PACK_BASE_FILES=" 41 | LICENSE 42 | flash_blob.c 43 | flash_blob.h 44 | README.md 45 | " 46 | 47 | # Specify file names to be deleted from pack build directory 48 | # Default: empty 49 | # 50 | # PACK_DELETE_FILES=" 51 | # " 52 | 53 | # Specify patches to be applied 54 | # Default: empty 55 | # 56 | # PACK_PATCH_FILES=" 57 | # 58 | # " 59 | 60 | # Specify addition argument to packchk 61 | # Default: empty 62 | # 63 | # PACKCHK_ARGS=() 64 | 65 | # Specify additional dependencies for packchk 66 | # Default: empty 67 | # 68 | # PACKCHK_DEPS=" 69 | # 70 | # " 71 | 72 | # Optional: restrict fallback modes for changelog generation 73 | # Default: full 74 | # Values: 75 | # - full Tag annotations, release descriptions, or commit messages (in order) 76 | # - release Tag annotations, or release descriptions (in order) 77 | # - tag Tag annotations only 78 | # 79 | # PACK_CHANGELOG_MODE="" 80 | 81 | # 82 | # custom pre-processing steps 83 | # 84 | # usage: preprocess 85 | # The build folder 86 | # 87 | function preprocess() { 88 | # add custom steps here to be executed 89 | # before populating the pack build folder 90 | return 0 91 | } 92 | 93 | # 94 | # custom post-processing steps 95 | # 96 | # usage: postprocess 97 | # The build folder 98 | # 99 | function postprocess() { 100 | # add custom steps here to be executed 101 | # after populating the pack build folder 102 | # but before archiving the pack into output folder 103 | return 0 104 | } 105 | 106 | ############ DO NOT EDIT BELOW ########### 107 | 108 | function install_lib() { 109 | local URL="https://github.com/Open-CMSIS-Pack/gen-pack/archive/refs/tags/v$1.tar.gz" 110 | local STATUS=$(curl -sLI "${URL}" | grep "^HTTP" | tail -n 1 | cut -d' ' -f2 || echo "$((600+$?))") 111 | if [[ $STATUS -ge 400 ]]; then 112 | echo "Wrong/unavailable gen-pack lib version '$1'!" >&2 113 | echo "Check REQUIRED_GEN_PACK_LIB variable." >&2 114 | echo "For available versions see https://github.com/Open-CMSIS-Pack/gen-pack/tags." >&2 115 | exit 1 116 | fi 117 | echo "Downloading gen-pack lib version '$1' to '$2' ..." 118 | mkdir -p "$2" 119 | curl -L "${URL}" -s | tar -xzf - --strip-components 1 -C "$2" || exit 1 120 | } 121 | 122 | function load_lib() { 123 | if [[ -d ${GEN_PACK_LIB} ]]; then 124 | . "${GEN_PACK_LIB}/gen-pack" 125 | return 0 126 | fi 127 | local GLOBAL_LIB="/usr/local/share/gen-pack/${REQUIRED_GEN_PACK_LIB}" 128 | local USER_LIB="${HOME}/.local/share/gen-pack/${REQUIRED_GEN_PACK_LIB}" 129 | if [[ ! -d "${GLOBAL_LIB}" && ! -d "${USER_LIB}" ]]; then 130 | echo "Required gen_pack lib not found!" >&2 131 | install_lib "${REQUIRED_GEN_PACK_LIB}" "${USER_LIB}" 132 | fi 133 | 134 | if [[ -d "${GLOBAL_LIB}" ]]; then 135 | . "${GLOBAL_LIB}/gen-pack" 136 | elif [[ -d "${USER_LIB}" ]]; then 137 | . "${USER_LIB}/gen-pack" 138 | else 139 | echo "Required gen-pack lib is not installed!" >&2 140 | exit 1 141 | fi 142 | } 143 | 144 | load_lib 145 | gen_pack "${DEFAULT_ARGS[@]}" "$@" 146 | 147 | exit 0 -------------------------------------------------------------------------------- /inc/flash_blob.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright 2022 KK (https://github.com/WALI-KANG) * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | * * 16 | ****************************************************************************/ 17 | 18 | #ifndef FLASH_BLOB_H 19 | #define FLASH_BLOB_H 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #if USE_PERF_COUNTER == ENABLED 26 | #include "perf_counter.h" 27 | #else 28 | #ifndef safe_atom_code 29 | #include "cmsis_compiler.h" 30 | #define safe_atom_code() \ 31 | for( uint32_t SAFE_NAME(temp) = \ 32 | ({uint32_t SAFE_NAME(temp2)=__get_PRIMASK(); \ 33 | __disable_irq(); \ 34 | SAFE_NAME(temp2);}),*SAFE_NAME(temp3) = NULL; \ 35 | SAFE_NAME(temp3)++ == NULL; \ 36 | __set_PRIMASK(SAFE_NAME(temp))) 37 | 38 | 39 | #endif 40 | #endif 41 | 42 | #define VERS 1 // Interface Version 1.01 43 | 44 | #define UNKNOWN 0 // Unknown 45 | #define ONCHIP 1 // On-chip Flash Memory 46 | #define EXT8BIT 2 // External Flash Device on 8-bit Bus 47 | #define EXT16BIT 3 // External Flash Device on 16-bit Bus 48 | #define EXT32BIT 4 // External Flash Device on 32-bit Bus 49 | #define EXTSPI 5 // External Flash Device on SPI 50 | 51 | #define SECTOR_NUM 16 // Max Number of Sector Items 52 | 53 | #define FLASH_DRV_VERS (0x0100+VERS) 54 | 55 | #define SECTOR_END 0xFFFFFFFF, 0xFFFFFFFF 56 | 57 | struct FlashSectors { 58 | unsigned long szSector; // Sector Size in Bytes 59 | unsigned long AddrSector; // Address of Sector 60 | }; 61 | 62 | typedef struct FlashDevice { 63 | unsigned short Vers; // Version Number and Architecture 64 | char DevName[32];// Device Name and Description 65 | unsigned short DevType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ... 66 | unsigned long DevAdr; // Default Device Start Address 67 | unsigned long szDev; // Total Size of Device 68 | unsigned long szPage; // Programming Page Size 69 | unsigned long Res; // Reserved for future Extension 70 | unsigned char valEmpty; // Content of Erased Memory 71 | 72 | unsigned long toProg; // Time Out of Program Page Function 73 | unsigned long toErase; // Time Out of Erase Sector Function 74 | 75 | struct FlashSectors sectors[SECTOR_NUM]; 76 | } flash_dev_t; 77 | 78 | typedef struct { 79 | int32_t (*Init)(uint32_t adr, uint32_t clk, uint32_t fnc); 80 | int32_t (*UnInit)(uint32_t fnc); 81 | int32_t (*EraseChip)(void); 82 | int32_t (*EraseSector)(uint32_t adr); 83 | int32_t (*Program)(uint32_t adr, uint32_t sz, uint8_t* buf); 84 | int32_t (*Read)(uint32_t adr, uint32_t sz, uint8_t* buf); 85 | } flash_ops_t; 86 | 87 | typedef struct flash_blob_t flash_blob_t; 88 | typedef struct flash_blob_t{ 89 | flash_dev_t const *ptFlashDev; 90 | flash_ops_t tFlashops; 91 | } flash_blob_t; 92 | 93 | extern void flash_dev_register(flash_blob_t *ptFlashDevice); 94 | extern bool target_flash_init(uint32_t addr); 95 | extern bool target_flash_uninit(uint32_t addr); 96 | extern int32_t target_flash_write(uint32_t addr, const uint8_t *buf, size_t size); 97 | extern int32_t target_flash_erase(uint32_t addr, size_t size); 98 | extern int32_t target_flash_read(uint32_t addr, uint8_t *buf, size_t size); 99 | #endif 100 | -------------------------------------------------------------------------------- /kk.flash_blob.pdsc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | kk 5 | flash_blob 6 | An abstraction layer for address-based FLASH management and operation. 7 | https://github.com/Aladdin-Wang/flash_blob/ 8 | https://github.com/Aladdin-Wang/flash_blob/issues 9 | LICENSE 10 | 11 | 12 | 13 | 14 | https://github.com/Aladdin-Wang/flash_blob.git 15 | 16 | 17 | 18 | 19 | Active development ... 20 | 21 | 22 | 23 | Initial release of flash_blob. 24 | 25 | 26 | Initial release of flash_blob. 27 | 28 | 29 | 30 | 31 | 32 | FLASH 33 | C Language 34 | Data Structures 35 | 36 | 37 | 38 | 39 | 40 | An abstraction layer for address-based FLASH management and operation. 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | An abstraction layer for address-based FLASH management and operation. 52 | 53 | 54 | -------------------------------------------------------------------------------- /port/GD32/GD32_FLASH_DEV.c: -------------------------------------------------------------------------------- 1 | #include "flash_blob.h" 2 | #ifdef GD32E50X 3 | #include "gd32e50x_fmc.h" 4 | static flash_dev_t const FlashDevice = { 5 | FLASH_DRV_VERS, // Driver Version, do not modify! 6 | "GD32E50X 512kB Flash", // Device Name (512kB) 7 | ONCHIP, // Device Type 8 | 0x08000000, // Device Start Address 9 | 0x00080000, // Device Size in Bytes (512kB) 10 | 0x00002000, // Programming Page Size 11 | 0, // Reserved, must be 0 12 | 0xFF, // Initial Content of Erased Memory 13 | 100, // Program Page Timeout 100 mSec 14 | 6000, // Erase Sector Timeout 6000 mSec 15 | // Specify Size and Address of Sectors 16 | 0x2000, 0x000000, // Sector Size 8kB (64 Sectors) 17 | SECTOR_END 18 | }; 19 | #endif // GD32E50X_512 20 | 21 | #ifdef GD32F30X_CL 22 | #include "gd32f30x_fmc.h" 23 | static flash_dev_t const FlashDevice = { 24 | FLASH_DRV_VERS, // Driver Version, do not modify! 25 | "GD32F30X 256kB Flash", // Device Name (512kB) 26 | ONCHIP, // Device Type 27 | 0x08000000, // Device Start Address 28 | 0x00040000, // Device Size in Bytes (256kB) 29 | 0x00000800, // Programming Page Size 30 | 0, // Reserved, must be 0 31 | 0xFF, // Initial Content of Erased Memory 32 | 100, // Program Page Timeout 100 mSec 33 | 6000, // Erase Sector Timeout 6000 mSec 34 | // Specify Size and Address of Sectors 35 | 0x0800, 0x000000, // Sector Size 2kB (128 Sectors) 36 | SECTOR_END 37 | }; 38 | #endif // GD32E50X_256 39 | 40 | -------------------------------------------------------------------------------- /port/GD32/GD32_FLASH_PRG.c: -------------------------------------------------------------------------------- 1 | #include "flash_blob.h" 2 | #include "gd32e50x_fmc.h" 3 | #include "GD32_FLASH_DEV.c" 4 | /* 5 | * Initialize Flash Programming Functions 6 | * Parameter: adr: Device Base Address 7 | * clk: Clock Frequency (Hz) 8 | * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 9 | * Return Value: 0 - OK, 1 - Failed 10 | */ 11 | 12 | static int32_t Init(uint32_t adr, uint32_t clk, uint32_t fnc) 13 | { 14 | fmc_unlock(); 15 | return (0); 16 | } 17 | 18 | /* 19 | * De-Initialize Flash Programming Functions 20 | * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 21 | * Return Value: 0 - OK, 1 - Failed 22 | */ 23 | 24 | static int32_t UnInit(uint32_t fnc) 25 | { 26 | fmc_lock(); 27 | return (0); 28 | } 29 | 30 | /* 31 | * Erase complete Flash Memory 32 | * Return Value: 0 - OK, 1 - Failed 33 | */ 34 | 35 | static int32_t EraseChip(void) 36 | { 37 | fmc_mass_erase(); 38 | return (0); 39 | } 40 | 41 | /* 42 | * Erase Sector in Flash Memory 43 | * Parameter: adr: Sector Address 44 | * Return Value: 0 - OK, 1 - Failed 45 | */ 46 | static int32_t EraseSector(uint32_t adr) 47 | { 48 | fmc_page_erase(adr); 49 | return (0); 50 | } 51 | 52 | /* 53 | * Program Page in Flash Memory 54 | * Parameter: adr: Page Start Address 55 | * sz: Page Size 56 | * buf: Page Data 57 | * Return Value: 0 - OK, 1 - Failed 58 | */ 59 | static int32_t ProgramPage(uint32_t addr, uint32_t sz, uint8_t* buf) 60 | { 61 | int32_t result = 0; 62 | uint32_t end_addr = addr + sz; 63 | while (addr < end_addr) { 64 | if (fmc_word_program(addr, *((uint32_t *)buf)) == FMC_READY) { 65 | if (*(uint32_t *)addr != *(uint32_t *)buf) { 66 | result = 1; 67 | break; 68 | } 69 | addr += 4; 70 | buf += 4; 71 | } else { 72 | result = 1; 73 | break; 74 | } 75 | } 76 | return result; 77 | } 78 | 79 | static flash_blob_t flash_device = { 80 | .tFlashops.Init = Init, 81 | .tFlashops.UnInit = UnInit, 82 | .tFlashops.EraseChip = EraseChip, 83 | .tFlashops.EraseSector = EraseSector, 84 | .tFlashops.ProgramPage = ProgramPage, 85 | .tFlashops.Read = NULL, 86 | .ptFlashDev = &FlashDevice, 87 | }; 88 | 89 | __attribute__((constructor)) 90 | static int flash_blob_device_register(void) 91 | { 92 | flash_dev_register(&flash_device); 93 | return 0 ; 94 | } 95 | -------------------------------------------------------------------------------- /port/STM32/STM32_FLASH_DEV.c: -------------------------------------------------------------------------------- 1 | #include "flash_blob.h" 2 | 3 | #ifdef STM32F10x_16 4 | #include "stm32f1xx_hal.h" 5 | static struct FlashDevice const FlashDevice = { 6 | FLASH_DRV_VERS, // Driver Version, do not modify! 7 | "STM32F10x Low-density Flash", // Device Name (16kB) 8 | ONCHIP, // Device Type 9 | 0x08000000, // Device Start Address 10 | 0x00004000, // Device Size in Bytes (16kB) 11 | 1024, // Programming Page Size 12 | 0, // Reserved, must be 0 13 | 0xFF, // Initial Content of Erased Memory 14 | 100, // Program Page Timeout 100 mSec 15 | 500, // Erase Sector Timeout 500 mSec 16 | 17 | // Specify Size and Address of Sectors 18 | 0x0400, 0x000000, // Sector Size 1kB (128 Sectors) 19 | SECTOR_END 20 | }; 21 | #endif 22 | 23 | #ifdef STM32F10x_128 24 | #include "stm32f1xx_hal.h" 25 | static struct FlashDevice const FlashDevice = { 26 | FLASH_DRV_VERS, // Driver Version, do not modify! 27 | "STM32F10x Med-density Flash", // Device Name (128kB/64kB/32kB) 28 | ONCHIP, // Device Type 29 | 0x08000000, // Device Start Address 30 | 0x00020000, // Device Size in Bytes (128kB) 31 | 1024, // Programming Page Size 32 | 0, // Reserved, must be 0 33 | 0xFF, // Initial Content of Erased Memory 34 | 100, // Program Page Timeout 100 mSec 35 | 500, // Erase Sector Timeout 500 mSec 36 | 37 | // Specify Size and Address of Sectors 38 | 0x0400, 0x000000, // Sector Size 1kB (128 Sectors) 39 | SECTOR_END 40 | }; 41 | #endif 42 | 43 | #ifdef STM32F10x_512 44 | #include "stm32f1xx_hal.h" 45 | static struct FlashDevice const FlashDevice = { 46 | FLASH_DRV_VERS, // Driver Version, do not modify! 47 | "STM32F10x High-density Flash",// Device Name (512kB/384kB/256kB) 48 | ONCHIP, // Device Type 49 | 0x08000000, // Device Start Address 50 | 0x00080000, // Device Size in Bytes (512kB) 51 | 1024, // Programming Page Size 52 | 0, // Reserved, must be 0 53 | 0xFF, // Initial Content of Erased Memory 54 | 100, // Program Page Timeout 100 mSec 55 | 500, // Erase Sector Timeout 500 mSec 56 | 57 | // Specify Size and Address of Sectors 58 | 0x0800, 0x000000, // Sector Size 2kB (256 Sectors) 59 | SECTOR_END 60 | }; 61 | #endif 62 | 63 | #ifdef STM32F10x_1024 64 | #include "stm32f1xx_hal.h" 65 | static struct FlashDevice const FlashDevice = { 66 | FLASH_DRV_VERS, // Driver Version, do not modify! 67 | "STM32F10x XL-density Flash",// Device Name (1024kB/768kB) 68 | ONCHIP, // Device Type 69 | 0x08000000, // Device Start Address 70 | 0x00100000, // Device Size in Bytes (1024kB) 71 | 1024, // Programming Page Size 72 | 0, // Reserved, must be 0 73 | 0xFF, // Initial Content of Erased Memory 74 | 100, // Program Page Timeout 100 mSec 75 | 500, // Erase Sector Timeout 500 mSec 76 | 77 | // Specify Size and Address of Sectors 78 | 0x0800, 0x000000, // Sector Size 2kB (512 Sectors) 79 | SECTOR_END 80 | }; 81 | #endif 82 | 83 | #ifdef STM32F10x_CL 84 | #include "stm32f1xx_hal.h" 85 | static struct FlashDevice const FlashDevice = { 86 | FLASH_DRV_VERS, // Driver Version, do not modify! 87 | "STM32F10x Connectivity Line Flash",// Device Name (256kB/128kB/64kB) 88 | ONCHIP, // Device Type 89 | 0x08000000, // Device Start Address 90 | 0x00040000, // Device Size in Bytes (256kB) 91 | 1024, // Programming Page Size 92 | 0, // Reserved, must be 0 93 | 0xFF, // Initial Content of Erased Memory 94 | 100, // Program Page Timeout 100 mSec 95 | 500, // Erase Sector Timeout 500 mSec 96 | 97 | // Specify Size and Address of Sectors 98 | 0x0800, 0x000000, // Sector Size 2kB (128 Sectors) 99 | SECTOR_END 100 | }; 101 | #endif 102 | 103 | #ifdef STM32H7x_2048 104 | #include "stm32h7xx_hal.h" 105 | static struct FlashDevice const FlashDevice = { 106 | FLASH_DRV_VERS, // Driver Version, do not modify! 107 | "STM32H7x_2048", // Device Name 108 | ONCHIP, // Device Type 109 | 0x08000000, // Device Start Address 110 | 0x00200000, // Device Size in Bytes (2048kB) 111 | 1024, // Programming Page Size 112 | 0, // Reserved, must be 0 113 | 0xFF, // Initial Content of Erased Memory 114 | 100, // Program Page Timeout 100 mSec 115 | 6000, // Erase Sector Timeout 6000 mSec 116 | 117 | // Specify Size and Address of Sectors 118 | 0x20000, 0x000000, // Sector Size 128kB (16 Sectors) 119 | SECTOR_END 120 | }; 121 | 122 | #endif -------------------------------------------------------------------------------- /port/STM32/STM32_FLASH_DRV.c: -------------------------------------------------------------------------------- 1 | #include "flash_blob.h" 2 | #include "STM32_FLASH_DEV.c" 3 | /* 4 | * Initialize Flash Programming Functions 5 | * Parameter: adr: Device Base Address 6 | * clk: Clock Frequency (Hz) 7 | * fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 8 | * Return Value: 0 - OK, 1 - Failed 9 | */ 10 | 11 | static int32_t Init(uint32_t adr, uint32_t clk, uint32_t fnc) 12 | { 13 | HAL_FLASH_Unlock(); 14 | return (0); 15 | } 16 | 17 | /* 18 | * De-Initialize Flash Programming Functions 19 | * Parameter: fnc: Function Code (1 - Erase, 2 - Program, 3 - Verify) 20 | * Return Value: 0 - OK, 1 - Failed 21 | */ 22 | 23 | static int32_t UnInit(uint32_t fnc) 24 | { 25 | HAL_FLASH_Lock(); 26 | return (0); 27 | } 28 | 29 | /* 30 | * Erase complete Flash Memory 31 | * Return Value: 0 - OK, 1 - Failed 32 | */ 33 | 34 | static int32_t EraseChip(void) 35 | { 36 | int32_t result = 0; 37 | uint32_t PAGEError = 0; 38 | FLASH_EraseInitTypeDef EraseInitStruct; 39 | EraseInitStruct.TypeErase = FLASH_TYPEERASE_MASSERASE; 40 | #if defined(FLASH_BANK2_END) 41 | EraseInitStruct.Banks = FLASH_BANK_2; 42 | if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) 43 | { 44 | result = 1; 45 | } 46 | #endif 47 | EraseInitStruct.Banks = FLASH_BANK_1; 48 | if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) 49 | { 50 | result = 1; 51 | } 52 | return result; 53 | } 54 | 55 | /* 56 | * Erase Sector in Flash Memory 57 | * Parameter: adr: Sector Address 58 | * Return Value: 0 - OK, 1 - Failed 59 | */ 60 | static int32_t EraseSector(uint32_t adr) 61 | { 62 | int32_t result = 0; 63 | uint32_t PAGEError = 0; 64 | 65 | /*Variable used for Erase procedure*/ 66 | FLASH_EraseInitTypeDef EraseInitStruct; 67 | #if defined(FLASH_BANK2_END) 68 | /* Fill EraseInit structure*/ 69 | EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES; 70 | EraseInitStruct.PageAddress = adr; 71 | EraseInitStruct.NbPages = 1; 72 | if(adr > FLASH_BANK1_END){ 73 | EraseInitStruct.Banks = FLASH_BANK_2; 74 | }else{ 75 | EraseInitStruct.Banks = FLASH_BANK_1; 76 | } 77 | 78 | if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) 79 | { 80 | result = 1; 81 | } 82 | #else 83 | EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; 84 | EraseInitStruct.Sector = (adr - FLASH_BANK1_BASE) / FLASH_SECTOR_SIZE;; 85 | EraseInitStruct.NbSectors = 1; 86 | EraseInitStruct.Banks = FLASH_BANK_1; 87 | EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; 88 | if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK) 89 | { 90 | result = 1; 91 | } 92 | #endif 93 | return result; 94 | } 95 | 96 | /* 97 | * Program Page in Flash Memory 98 | * Parameter: adr: Page Start Address 99 | * sz: Page Size 100 | * buf: Page Data 101 | * Return Value: 0 - OK, 1 - Failed 102 | */ 103 | static int32_t ProgramPage(uint32_t addr, uint32_t sz, uint8_t* buf) 104 | { 105 | int32_t result = 0; 106 | uint32_t write_granularity = FLASH_NB_32BITWORD_IN_FLASHWORD * 4; 107 | uint32_t write_size = write_granularity; 108 | uint32_t end_addr = addr + sz - 1,write_addr; 109 | uint8_t write_buffer[32] = {0}; 110 | write_addr = (uint32_t)buf; 111 | __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR); 112 | while (addr < end_addr) { 113 | if(end_addr - addr + 1 < write_granularity) 114 | { 115 | write_size = end_addr - addr + 1; 116 | for(size_t i = 0; i < write_size; i++) 117 | { 118 | write_buffer[i] = *((uint8_t *)(write_addr + i)); 119 | } 120 | write_addr = (uint32_t)((uint32_t *)write_buffer); 121 | } 122 | if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr, write_addr) == HAL_OK) { 123 | for(uint8_t i = 0; i < write_size; i++) 124 | { 125 | if (*(uint8_t *)(addr + i) != *(uint8_t *)(write_addr + i)) 126 | { 127 | result = 1; 128 | break; 129 | } 130 | } 131 | addr += write_granularity; 132 | write_addr += write_granularity; 133 | } else { 134 | result = 1; 135 | break; 136 | } 137 | } 138 | return result; 139 | } 140 | 141 | const flash_blob_t onchip_flash_device = { 142 | .tFlashops.Init = Init, 143 | .tFlashops.UnInit = UnInit, 144 | .tFlashops.EraseChip = EraseChip, 145 | .tFlashops.EraseSector = EraseSector, 146 | .tFlashops.Program = ProgramPage, 147 | .tFlashops.Read = NULL, 148 | .ptFlashDev = &FlashDevice, 149 | }; 150 | 151 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # flash_blob 2 | 3 | ## 1、介绍 4 | 5 | 利用MDK的FLM文件快速生成通用flash驱动。 6 | - 同时支持多个flash芯片级联,根据地址自动选择flash驱动。 7 | - 可以作为直接操作 `片内 Flash` 和 `SPI Flash` 的后端实现。 8 | 9 | ### 1.1、API接口 10 | ```c 11 | extern bool target_flash_init(uint32_t flash_start, int32_t size); 12 | extern bool target_flash_uninit(uint32_t flash_start); 13 | extern int32_t target_flash_write(uint32_t addr, const uint8_t *buf, int32_t size); 14 | extern int32_t target_flash_erase(uint32_t addr, int32_t size); 15 | extern int32_t target_flash_read(uint32_t addr, const uint8_t *buf, int32_t size); 16 | ``` 17 | 18 | ### 1.2、目录结构 19 | 20 | | doc | 文档 | 21 | | ----- | ------------ | 22 | | src | 源代码 | 23 | | inc | 头文件 | 24 | | tools | 驱动生成工具 | 25 | 26 | ### 1.3、许可证 27 | 28 | Agile Upgrade 遵循 `Apache-2.0` 许可,详见 `LICENSE` 文件。 29 | 30 | 31 | 32 | ## 2、使用 flash_blob 33 | 1. 在rtthread软件包中找到flash_blob,然后添加进工程。 34 | 35 | 2. 添加对应芯片的代码进工程,如果有多个flash器件,可以连续添加。 36 | 37 | 注意:多个设备的话每个flash的FlashDevice 的设备起始地址不可重叠,flash抽象层根据地址,自动选择相应的驱动。 38 | 39 | 以上步骤完成后,就可以快速使用了,例如将YMODEM接收到的数据,写到flash中,代码如下: 40 | 41 | 42 | ```c 43 | uint8_t *ymodem_call_back_receive(uint8_t *pchBuffer, uint16_t hwSize) 44 | { 45 | static char *s_pchFileName = NULL, *s_pchFileSize = NULL; 46 | static uint32_t s_wFileSize = 0, s_wRemainLen = 0, s_wOffSet = 0; 47 | 48 | static enum { 49 | START = 0, 50 | RECEIVE, 51 | END, 52 | } s_tState = {START}; 53 | 54 | switch(s_tState) { 55 | case START: 56 | s_wOffSet = 0; 57 | s_pchFileName = (char *)&pchBuffer[0]; 58 | s_pchFileSize = (char *)&pchBuffer[strlen(s_pchFileName) + 1]; 59 | s_wFileSize = atol(s_pchFileSize); 60 | 61 | LOG_D("Ymodem file_name:%s", s_pchFileName); 62 | LOG_D("Ymodem file_size:%d", s_wFileSize); 63 | 64 | if(target_flash_init(APP_PART_ADDR, s_wFileSize) == false) { 65 | LOG_E("target flash uninit."); 66 | return NULL; 67 | } 68 | 69 | if(target_flash_erase(APP_PART_ADDR, s_wFileSize) < 0) { 70 | LOG_E("target flash erase error."); 71 | return NULL; 72 | } 73 | 74 | s_tState = RECEIVE; 75 | break; 76 | 77 | case RECEIVE: 78 | s_wRemainLen = s_wFileSize - s_wOffSet; 79 | 80 | if(hwSize > s_wRemainLen) { 81 | hwSize = s_wRemainLen; 82 | s_tState = END; 83 | } 84 | 85 | if(target_flash_write(APP_PART_ADDR + s_wOffSet, pchBuffer, hwSize) < 0) { 86 | LOG_E("target flash write data error."); 87 | return NULL; 88 | } 89 | s_wOffSet += hwSize; 90 | 91 | break; 92 | 93 | case END: 94 | target_flash_uninit(APP_PART_ADDR); 95 | s_tState = START; 96 | break; 97 | } 98 | 99 | return s_chBuffer; 100 | } 101 | ``` 102 | 103 | -------------------------------------------------------------------------------- /src/SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | 3 | cwd = GetCurrentDir() 4 | src = Glob('*.c') 5 | CPPPATH = [cwd + '/../inc'] 6 | 7 | group = DefineGroup('flash_blob', src, depend = ['PKG_USING_FLASH_BLOB'], CPPPATH = CPPPATH) 8 | 9 | Return('group') -------------------------------------------------------------------------------- /src/flash_blob.c: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright 2022 KK (https://github.com/WALI-KANG) * 3 | * * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); * 5 | * you may not use this file except in compliance with the License. * 6 | * You may obtain a copy of the License at * 7 | * * 8 | * http://www.apache.org/licenses/LICENSE-2.0 * 9 | * * 10 | * Unless required by applicable law or agreed to in writing, software * 11 | * distributed under the License is distributed on an "AS IS" BASIS, * 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 13 | * See the License for the specific language governing permissions and * 14 | * limitations under the License. * 15 | * * 16 | ****************************************************************************/ 17 | #include "flash_blob.h" 18 | #include "flash_blob_cfg.h" 19 | /* Array containing flash devices and their configurations */ 20 | static const flash_blob_t * const flash_table[] = FLASH_DEV_TABLE; 21 | 22 | /* Length of the flash_table array */ 23 | static const size_t flash_table_len = sizeof(flash_table) / sizeof(flash_table[0]); 24 | 25 | /* 26 | * Function: flash_dev_find 27 | * Description: Finds the flash device based on the specified address. 28 | * Parameters: 29 | * - addr: Flash memory address to find. 30 | * Returns: Pointer to the flash_blob_t structure if found, NULL otherwise. 31 | */ 32 | static const flash_blob_t * flash_dev_find(uint32_t addr) 33 | { 34 | for (uint16_t i = 0; i < flash_table_len; i++) { 35 | if(addr >= flash_table[i]->ptFlashDev->DevAdr && 36 | addr < flash_table[i]->ptFlashDev->DevAdr + flash_table[i]->ptFlashDev->szDev) { 37 | return flash_table[i]; 38 | } 39 | } 40 | 41 | return NULL; 42 | } 43 | /* 44 | * Function: target_flash_init 45 | * Description: Initializes the flash memory based on the specified address. 46 | * Parameters: 47 | * - addr: Flash memory address to initialize. 48 | * Returns: True if initialization is successful, false otherwise. 49 | */ 50 | bool target_flash_init(uint32_t addr) 51 | { 52 | if (addr % 4 != 0) { 53 | /*flash addr must be 4-byte alignment*/ 54 | return false; 55 | } 56 | 57 | const flash_blob_t *ptFlashDevice = flash_dev_find(addr); 58 | 59 | if(ptFlashDevice != NULL) { 60 | ptFlashDevice->tFlashops.Init(addr, 0, 0); 61 | return true; 62 | } 63 | 64 | return false; 65 | } 66 | /* 67 | * Function: target_flash_uninit 68 | * Description: Uninitializes the flash memory based on the specified address. 69 | * Parameters: 70 | * - addr: Flash memory address to uninitialize. 71 | * Returns: True if uninitialization is successful, false otherwise. 72 | */ 73 | bool target_flash_uninit(uint32_t addr) 74 | { 75 | const flash_blob_t *ptFlashDevice = flash_dev_find(addr); 76 | 77 | if(ptFlashDevice != NULL) { 78 | ptFlashDevice->tFlashops.UnInit(addr); 79 | return true; 80 | } 81 | 82 | return true; 83 | } 84 | /* 85 | * Function: target_flash_write 86 | * Description: Writes data to the flash memory. 87 | * Parameters: 88 | * - addr: Flash memory address to start writing. 89 | * - buf: Pointer to the data to be written. 90 | * - size: Number of bytes to write. 91 | * Returns: Number of bytes actually written. 92 | */ 93 | int target_flash_write(uint32_t addr, const uint8_t *buf, size_t size) 94 | { 95 | const flash_blob_t *ptFlashDevice = flash_dev_find(addr); 96 | 97 | safe_atom_code(){ 98 | if(ptFlashDevice != NULL) { 99 | if (addr % 4 != 0) { 100 | /*addr must be 4-byte alignment*/ 101 | size = 0; 102 | continue; 103 | } 104 | 105 | if ((addr - ptFlashDevice->ptFlashDev->DevAdr + size) > ptFlashDevice->ptFlashDev->szDev) { 106 | /*write outrange flash size*/ 107 | size = 0; 108 | continue; 109 | } 110 | 111 | if(ptFlashDevice->tFlashops.Program) { 112 | if( 0 != ptFlashDevice->tFlashops.Program(addr, size, (uint8_t *)buf)) { 113 | /*Programming Failed*/ 114 | size = 0; 115 | continue; 116 | } 117 | } else { 118 | size = 0; 119 | continue; 120 | } 121 | } 122 | } 123 | return size; 124 | } 125 | 126 | /* 127 | * Function: target_flash_read 128 | * Description: Reads data from the flash memory. 129 | * Parameters: 130 | * - addr: Flash memory address to start reading. 131 | * - buf: Pointer to store the read data. 132 | * - size: Number of bytes to read. 133 | * Returns: Number of bytes actually read. 134 | */ 135 | int target_flash_read(uint32_t addr, uint8_t *buf, size_t size) 136 | { 137 | const flash_blob_t *ptFlashDevice = flash_dev_find(addr); 138 | 139 | safe_atom_code(){ 140 | if(ptFlashDevice != NULL) { 141 | if (addr % 4 != 0) { 142 | /*addr must be 4-byte alignment*/ 143 | size = 0; 144 | continue; 145 | } 146 | 147 | if ((addr - ptFlashDevice->ptFlashDev->DevAdr + size) > ptFlashDevice->ptFlashDev->szDev) { 148 | /*read outrange flash size*/ 149 | size = 0; 150 | continue; 151 | } 152 | 153 | if(ptFlashDevice->tFlashops.Read) { 154 | if( 0 != ptFlashDevice->tFlashops.Read(addr, size, (uint8_t *)buf)) { 155 | /*Read Failed*/ 156 | size = 0; 157 | continue; 158 | } 159 | } else { 160 | for (uint16_t i = 0; i < size; i++, buf++, addr++) { 161 | *buf = *(uint8_t *) addr; 162 | } 163 | } 164 | } 165 | } 166 | 167 | return size; 168 | } 169 | 170 | /* 171 | * Function: target_flash_erase 172 | * Description: Erases a specified portion of the flash memory. 173 | * Parameters: 174 | * - addr: Flash memory address to start erasing. 175 | * - size: Number of bytes to erase. 176 | * Returns: Number of bytes actually erased. 177 | */ 178 | int target_flash_erase(uint32_t addr, size_t size) 179 | { 180 | size_t wSector = 0; 181 | size_t wEraseSize = 0; 182 | const flash_blob_t *ptFlashDevice = flash_dev_find(addr); 183 | if(ptFlashDevice != NULL) { 184 | safe_atom_code(){ 185 | if (size > ptFlashDevice->ptFlashDev->szDev) { 186 | /*erase outrange flash size */ 187 | wEraseSize = 0; 188 | continue; 189 | } 190 | 191 | while(wEraseSize < size) { 192 | if(ptFlashDevice->tFlashops.EraseSector) { 193 | if(0 != ptFlashDevice->tFlashops.EraseSector(addr)) { 194 | /*erase Failed*/ 195 | break; 196 | } 197 | } else { 198 | break; 199 | } 200 | 201 | for(wSector = 0; wSector < SECTOR_NUM; wSector++) { 202 | if(ptFlashDevice->ptFlashDev->sectors[wSector + 1].szSector == 0XFFFFFFFF ) 203 | break; 204 | 205 | if(((addr - ptFlashDevice->ptFlashDev->DevAdr) < ptFlashDevice->ptFlashDev->sectors[wSector + 1].AddrSector) && 206 | ((addr - ptFlashDevice->ptFlashDev->DevAdr) >= ptFlashDevice->ptFlashDev->sectors[wSector].AddrSector) ) 207 | break; 208 | } 209 | 210 | addr += ptFlashDevice->ptFlashDev->sectors[wSector].szSector; 211 | wEraseSize += ptFlashDevice->ptFlashDev->sectors[wSector].szSector; 212 | } 213 | } 214 | return wEraseSize; 215 | } 216 | 217 | return 0; 218 | } 219 | 220 | --------------------------------------------------------------------------------