本文主要是介绍OpenOCD 添加 AIR105(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 前言
- 1 驱动实现方式的尝试
- 1.1 第一次尝试
- 1.2 第二次尝试
- 1.3 第三次尝试
- 1.4 第四次尝试
- 1.5 第五次尝试
- 2 测试
- 2.1 汇编传参问题
- 2.2 缓存
- 2.3 header
- 2.3.1 无法正常启动
- 2.3.2 串口烧录程序
- 2.4 烧录 .elf 文件
- 3 优化
- 3.1 DMA
- 3.2 多扇区擦除
- 4 附录1:调用 AIR105 FLM 算法的尝试
- 4.1 关于 Keil FLM
- 4.2 解析 FLM 文件
前言
-
1)上一篇文章介绍了 Air105 OpenOCD 驱动编写的成果,这篇文章想着记录一下中间遇到的问题,一是防止和我一样的小白踩坑(你就当真的听),二是成功不吆喝,如锦衣夜行。
-
2)ROM API 代码:
// air105_rom_falsh_api @ 0x00008010UL : #define ROM_QSPI_Init (*((void (*)(QSPI_InitTypeDef *)) (*(uint32_t *)0x8010))) #define ROM_QSPI_ReadID (*((uint32_t (*)(QSPI_CommandTypeDef *)) (*(uint32_t *)0x8014))) #define ROM_QSPI_WriteParam (*((uint8_t (*)(QSPI_CommandTypeDef *, uint16_t)) (*(uint32_t *)0x8018))) #define ROM_QSPI_EraseSector (*((uint8_t (*)(QSPI_CommandTypeDef *, uint32_t)) (*(uint32_t *)0x801C))) #define ROM_QSPI_EraseChip (*((uint8_t (*)(QSPI_CommandTypeDef *)) (*(uint32_t *)0x8020))) // This's a reasonable guess. #define ROM_QSPI_ProgramPage (*((uint8_t (*)(QSPI_CommandTypeDef *, DMA_TypeDef *, \uint32_t, uint32_t, uint8_t *)) (*(uint32_t *)0x8024))) #define ROM_QSPI_ReleaseDeepPowerDown (*((uint8_t (*)(QSPI_CommandTypeDef *)) (*(uint32_t *)0x802C)))
1 驱动实现方式的尝试
1.1 第一次尝试
- 1)刚开始时,根据 Air001 时的经验,想通过 OpenOCD 模拟 QSPI API 的方式实现,结果烧录巨慢(几 KB 大小的文件烧录要一分多钟),且烧录后无法正常启动,放弃(第一次尝试)。
- (1)现在想来,烧录慢应该是通过 OpenOCD 模拟寄存器操作只适合偶尔简单的操作,如解锁 FLASH,读取 FLASH SIZE 寄存器等操作。而烧录代码涉及到寄存器的频繁操作;
- (2)无法正常启动应该是第一个扇区中未写入 header 所致。
1.2 第二次尝试
-
1)既然操作 QSPI 寄存器不行,就考虑 ROM API 调用的方式。本来想参考 “为国产芯片增加OpenOCD Flash驱动----以AIC8800为例” 这篇文章,写完代码测试时发现运行算法的 target_run_algorithm() 函数一直报错。
-
2)由于此时还不太明白 OpenOCD 驱动运行逻辑,因此让我一度怀疑官方的 ROM API 到底是否可用。(从这里开始走弯路。)
-
3)上面提到的代码中的主要逻辑:
struct aic8800_rom_api_call_code_t {uint16_t ldrn_r3; // 0x4b01uint16_t blx_r3; // 0x4798uint16_t bkpt; // 0xbe00uint16_t nop; // 0xbf00uint32_t api_addr; // api addr };struct aic8800_rom_api_call_code_t aic8800_rom_api_call_code_example = {.ldrn_r3 = 0x4b01, /* LDR.N R3, [PC, #0x4]*/.blx_r3 = 0x4798, /* BLX R3 */.bkpt = 0xbe00, /* bkpt */.nop = 0xbf00, /* NOP */.api_addr = 0x12345678, };....... static int aic8800_probe(struct flash_bank *bank) {...... retval = target_read_buffer(target, AIC8800_ROM_APITBL_BASE, sizeof(rom_api_table), (uint8_t *)rom_api_table);if (retval != ERROR_OK)return retval;for (unsigned int i = 0; i < dimof(aic8800_bank->rom_api_call_code); i++) {init_rom_api_call_code(&aic8800_bank->rom_api_call_code[i], rom_api_table[i]);}...... }
- 上面通过 target_read_buffer() 函数,从 ROM API 的基地址读取出 ROM API 代码。
1.3 第三次尝试
-
1)既然需要运行 ROM 代码,那么我是不是可以把官方 AIR105.FLM 中的算法代码(没有源码)提取出来,然后参考 1.2 中的形式实现来运行?(灵感来自 “利用MDK的FLM文件生成通用flash驱动”)
-
2)说干就干,不过当我终于解析完 AIR105.FLM 文件,驱动写了一半时想到:AIR105.FLM 中的算法是通过 QSPI 实现的,里面有函数多层嵌套,OpenOCD 中该怎么实现呢?难度太大遂放弃,毕竟我是一个小白呢。
-
3)解析 AIR105.FLM 文件见 “附录1:调用 AIR105.FLM 算法的尝试”。
1.4 第四次尝试
-
1)再回头去仔细研究上面的大佬代码,这位大佬的情况好像是先从 FLASH 中读取出 ROM API 的代码,然后将代码写入到 SRAM 中运行的。再看一下 Air105 官方的 ROM API 地址间隔 4 个字节,不像是一个函数的大小啊。
-
2)经过 “文心一言” 的解答,原来这个是函数指针。那我是不是可以把这个指针指向的地方的代码读取出来运行?好像不行,因为我不知道要读取多长的数据才是一个完整的 ROM API 代码。
#define ROM_QSPI_ReadID (*((uint32_t (*)(QSPI_CommandTypeDef *))(*(uint32_t *)0x8014)))
-
3)不过大佬的文章仍然给了我启发,那就是将一个地址赋值给一个寄存器,然后可以直接调用。可能官方 ROM API 是没问题的,只是我调用的方式有问题?
LDR.N R3, [PC,#0x4] /* 将后面的入口地址载入 R3,已知的 ROM API 最多只需要用到 R0-R2 */ BLX R3 /* 调用 ROM API */
-
4)那么就验证一下,当时想到如果有问题就放弃这个项目了(因为在 1.2 小节中,我尝试了各种办法结果都是报错)。通过在 Keil5 中调用 ROM_QSPI_ReadID() 成功获得芯片的 ID,然后通过调试找到对应的汇编代码(这也是我认为 Keil5 唯二可取的地方):
0x01002424 F44F4000 MOV r0,#0x8000 0x01002428 6941 LDR r1,[r0,#0x14] 0x0100242A 2000 MOVS r0,#0x00 0x0100242C 4788 BLX r1 0x0100242E 4604 MOV r4,r0
-
(2)通过 BLX r1 指令来调用 0x8014 处的函数,且参数 NULL 保存在 r0 寄存器中。结果成功获得 id,如下:
-
(3)再结合大佬文章中的 BLX 调用 API 代码,那么可以确定,确实可以这样来调用 ROM API 的函数指针,只要我们把参数设置完就好了(乐极生悲的伏笔)
-
1.5 第五次尝试
-
1)当时想着先实现 ROM_QSPI_ReadID() ROM API 的调用,因为它参数少,且是 OpenOCD 烧录时最先调用的 .probe 中需要实现的。当看到程序输出下面的内容时,就差喜极而泣了:
AIR105 flash base 0x01001000 device id = 0x005E4016
-
2)路线选择完成,下面就把各个功能函数实现即可:
const struct flash_driver air105_flash = {.name = "air105",.commands = air105_command_handlers,.flash_bank_command = air105_flash_bank_command,.erase = air105_erase,.write = air105_write,.read = default_flash_read,.probe = air105_probe,.auto_probe = air105_auto_probe,.erase_check = default_flash_blank_check,.info = air105_get_info,.free_driver_priv = default_flash_free_driver_priv, };
-
3)其它情况:
- (1)为了减少烧录时间,当然是采用异步烧录了
- (2)驱动完成(见 air105.c),汇编算法完成(见 air105_write.S),编写过程中,通过 Keil5 测试 ROM API 时发现:
- a. 扇区擦除函数 ROM_QSPI_EraseSector(),参数 uint16_t 为扇区所在地址,那么擦除实现时就需要循环擦除多个扇区了,原来还以为 API 擦除的是指定地址到 FLASH 尾地址呢
- b. 页编程函数:ROM_QSPI_ProgramPage(),芯片的写入单位是页,那么就要除了实现异步烧录算法外,还要实现 ROM_QSPI_ProgramPage() 函数,防止不足页的情况
2 测试
2.1 汇编传参问题
-
1)通过 “flash write_image erase” 烧录后,程序无法运行,于是通过 “flash read_bank 0 [filename]” 将整个 FLASH 读取出来,与 HEX 文件对比:发现异步烧录算法写入数据不对(而且有时在写的过程中会报错),折腾了几天,中间几乎一行一行地查代码感觉没有问题啊。
-
2)最终在不经意间愤怒地质问 “文心一言”:“arm 汇编中 5 个参数的函数到底应该怎么传参?(概义如此)”,答:r0~r3 寄存器通常用于参数传递,多于 4 个参数就要放到栈里面去了。对不起,我是小白。下面是 QSPI API(与 ROM API 参数一致),有 5 个参数:
uint8_t QSPI_ProgramPage(QSPI_CommandTypeDef *cmdParam,DMA_TypeDef *DMA_Channelx,uint32_t adr,uint32_t sz,uint8_t *buf );
- (1)ARM 平台下,参数值传递按顺序存放在寄存器 r0,r1,r2,r3 里,超过 4 个参数值传递则放栈里。仔细想想也是,通用寄存器就那十几个,那我要是一个函数 20 个参数怎么办呢?
- (2)页编程函数 QSPI_ProgramPage() 有 5 个参数,则第 5 个参数需要去 sp 指针指向的位置去取。
-
3)同时中间想到,由于我们是异步烧录,那么我们需要几个寄存器来保存 FIFO 的起始、结束地址,烧录页数,读写指针等,这些寄存器在烧录过程中不能被外部改变。
- (1)原来想着我从 R12~R0 开始往下用,BLX 指令调用函数时可能只用到前面的几个寄存器,不会覆盖我后面的寄存器。
- (2)后来查看 AIR105.FLM 文件,以及《ARM Cortex-M3 权威指南》时发现,POP 和 PUSH 指令你值得拥有。
2.2 缓存
-
1)每次烧录成功后,通过 flash mdw 查看发现内容不对。
- mdw,Memory Display Word,显示内存字,即显示指定地址的一个 4 字节数据。
-
2)然后就一直通过 “flash fillw [address] [value] [length]” 写入数据,“flash erase_sector [bank] [first] [last]” 擦除扇区,“flash erase_check [bank]” 查看扇区的标记情况,这三个命令来测试。
-
3)偶然的机会发现,写入数据后,使用 “flash erase_check” 走一下,然后再通过 “flash mdw” 命令就可以看到正常值,然后就把 “flash erase_check” 对应的回调函数 “default_flash_blank_check” 添加到每次更新操作的后面,结果非常消耗时间。这样不行啊,再去翻 AIR105.FLM 解析内容,通过 “Cache_CleanAll()” 函数猜测可能是缓存问题。
-
4)找到官方数据手册,把清除缓存的功能实现一下,再测试,终于正常烧录了。 通过 “flash read_bank 0 [filename]” 命令读取出来后,与镜像 HEX 文件一致。
2.3 header
2.3.1 无法正常启动
-
1)程序已经烧录成功,校验也和镜像文件一致,但芯片无法正常启动。需要说明的是,此前的驱动中,我一直以为芯片的 FLASH 基地址为 0x01001000,且大小为 4092KB,因为官方文档这样说得,且 Link Script 中也如下指定(虽然心里也吐槽过,说好的 4MB FLASH 怎么少了 4KB 呢?)
MEMORY {RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 640KFLASH (rx) : ORIGIN = 0x01001000, LENGTH = 4092K }
-
2)这里是耗时最长的部分,经过大概一周的折腾,包括一行一行检查汇编代码(怀疑自已),去查 SC300 文档(以为 SC300 安全核有啥特性),查看 Cortex-M3/M4 权威指南(想找找 FLASH 寄存器的说明),所有的路都走不能,再总结一下现象如下:
- (1)先通过串口烧录一次,然后使用本驱动多次烧录相同的镜像,都可以正常运行
- (2)通过 flash read_bank 命令读取出烧录的镜像文件,发现和正常运行的镜像一致
2.3.2 串口烧录程序
-
1)OpenOCD 烧录无法正常运行,但串口烧录后却可以正常运行,观察下串口烧录的日志:
PS D:\_Workspace\11_MCU\07_AirM2M\Air105\Demos\air105_mh1903s> .\air105-uploader.exe COM17 .\cmake-build-debug\air105_mh1903s.bin >>> No signature key. Ignoring firmware signature >>> Port: COM17 >>> Firmware: 4312 bytes >>> Starting bootloader >>> Received ChipSN Packet >>> Boot Version: V1.3.0 >>> ChipID: 50790400 >>> ROM Version: Unknown >>> Series: S030 (MH1903S) >>> Serial: b'4131303551383854000000000000DAA9' >>> Initializing stage2 >>> Erasing flash memory >>> Erasing flash from 00000000 to 00003000 >>> Sending firmware >>> Writing @0x1001000 >>> Writing @0x1002000 >>> Upload finished >>> Resetting device
- 没发现什么特殊的,烧录地址也是在 0x01001000。就死马当做活马医,看看它是怎么实现烧录的。“马生,你发现了华点!!!”
-
2)发现了如下内容:
def update(self):if self.Option == 2:hash = hashlib.sha256(self.Data).digest()else:hash = hashlib.sha512(self.Data).digest()self.Hash[:len(hash)] = hashself.header = struct.pack("<6I", 0x5555AAAA, 0, self.Start, self.Length, self.Version, self.Option)self.header += self.Hashcrc = self.c32.compute(self.header[4:])self.header += struct.pack("<I", crc)if self.rsaKey != NULL_KEY:signData = self.header[4:]signData += b"\x00" * (256 - len(signData)) # Pad to 256 bytessignData = int.from_bytes(signData, byteorder='big')signed = pow(signData, self.rsaKey.d, self.rsaKey.n)self.signed_header = self.header[:4] + signed.to_bytes(self.rsaKey.size_in_bytes(), byteorder='big')
- 好像是对数据进行 SHA256 摘要,还有 CRC-32 签名的。
-
3)与此同时,官方 SDK 里面竟然有如下定义:
#define MHSCPU_FLASH_BASE (0x01000000UL)
-
4)尝试将 OpenOCD 中 Air105 驱动程序中的 FLASH 基地址由 0x01001000 修改为 0x01000000,然后通过 “flash mdw 0x01000000 32” 读取内容,结果:
> flash mdw 0x01000000 32 AIR105 flash base 0x00000000 device id = 0x005E4016 0x01000000: 5555aaaa 00000000 01001000 000010d8 00000000 00000002 5ac6ffb0 b06f56f2 0x01000020: 756ff9bb 38a5f690 afbaf183 0c26b5cb d8008a24 d76168e3 00000000 00000000 0x01000040: 00000000 00000000 00000000 00000000 00000000 00000000 2f91401d 00000000 0x01000060: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
- 从中可以发现,虽然后续内容不一致,但开始的 0x0x5555AAAA 与串口烧录代码中的一模一样。
-
5)再仔细研究一下,终于发现,原来 FLASH 0x01000000 ~ 0x01001000 一个扇区 4KB 大小中,保存的是 0x0x5555AAAA 魔数、默认值0、app 地址、app 数据长度、版本、摘要算法等内容。接下来我们只需要一一实现这些内容即可。
-
参考:sha256 C 语言实现:https://blog.csdn.net/qq_43176116/article/details/110388321
2.4 烧录 .elf 文件
-
1)我的目标是在 CLion 中使用 OpenOCD 烧录程序,而 CLion 中烧录的是 .elf 文件。问题是,无论在 Link Script 文件怎么配置 FLASH 的起始地址 ORIGIN,结果在烧录时,OpenOCD 获取到的都是 0x01000000
MEMORY {RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 640KFLASH (rx) : ORIGIN = 0x01001000, LENGTH = 4092K }
-
2)使用 “arm-none-eabi-readelf.exe -a .\air105_mh1903s.elf” 命令解析 .elf 文件,结果也是如此:
Program Headers:Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg AlignLOAD 0x000000 0x01000000 0x01000000 0x01cac 0x01cac RWE 0x10000LOAD 0x008000 0x20008000 0x01001cac 0x0042c 0x0045c RW 0x10000LOAD 0x000000 0x20000000 0x20000000 0x00000 0x08000 RW 0x10000
-
3)晚上睡觉时,找了几篇类似的文件了解一下 Link Script 文件:
- ARM Cortex-M文件那些事(6)- 可执行文件(.out/.elf):https://www.cnblogs.com/henjay724/p/8276677.html
- ARM Cortex-M文件那些事(2)- 链接文件(.icf):https://www.cnblogs.com/henjay724/p/8191908.html
- 链接脚本(Linker Scripts)语法和规则解析:https://www.cnblogs.com/jianhua1992/p/16852784.html
-
4)早上起来文章还没看,先用 “flash mdw 0x01001000 32” 读取烧录后的头 32 个字节发现,虽然 .elf 文件的内容从地址 0x01000000 开始,但是它实际烧录的程序代码依然是从 0x01001000 开始写入的。那问题不就解决了,我只需要在计算 header 时,对传入 .write 函数中的 buffer 进行 1 个扇区 4KB 的偏移不就行了。
-
5)结果不出所料,果然烧录成功。至此可以宣布,我的 OpenOCD 驱动终于可以支持 .elf、.hex、.bin 三种文件的烧录了。
3 优化
3.1 DMA
- 1)可以正常烧录以后,当然就想优化一下烧录速度了。首先从参数类型我们可以看到烧录 ROM API 是支持 DMA 的,那就尝试一下吧。
#define ROM_QSPI_ProgramPage (*((uint8_t (*)(QSPI_CommandTypeDef *, DMA_TypeDef *, uint32_t, uint32_t, uint8_t *))(*(uint32_t *)0x8024)))#define DMA_Channel_0 ((DMA_TypeDef *)DMA_BASE)
#define DMA_Channel_1 ((DMA_TypeDef *)(DMA_BASE + 0x58))
- 2)从 SDK 中可以看到 DMA 通道号的值,再使用 Keil5 进行 Debug 可以看到在 ROM_QSPI_ProgramPage() 调用时,r1 的值确实是 0x40008000,那么我们修改汇编代码:
movs r1, #0x4000 /* param of DMA_TypeDef */ lsls r1, #16 adds r1, #0x800 /* DMA always failure */// 还尝试了下面这种方式: ldr r1, =#0x4000800
- 还尝试了其它 DMA 通道号,结果发现:有时会出现死活烧录下进行的情况。此时测试不加 DMA 的情况下烧录,只比 Keil 慢 5~6 秒,就放弃了。
3.2 多扇区擦除
-
1)为了对比 Keil 烧录速度,在擦除扇区和烧录分别加入了计时功能,结果发现擦除 150 个扇区都需要十几秒,就想到设置擦除的最后扇区,让擦除算法自已运行,这样可以节省每次擦除算法的初始化和释放过程时间。
- 也就是由 OpenOCD 控制一个扇区一个扇区地擦除,修改为几十个扇区擦除一次。
-
2)结果发现,当一次擦除 50 个以上扇区时,发现错误的概率大大提高。慢慢减少每次擦除的扇区数,直到等于 20 时,还会偶尔出现错误。没办法,设置成每次擦除 15 个扇区,并加入错误重试功能。
/* erase sectors more than specified sectors will make ROM_QSPI_EraseSector() failed.* and there is a certain probability that the function returns successfully, but the* sector is not completely erased, this can lead to the program not running after being* burned.* The smaller the specified number of sectors, the better. it will always failed if more* than 60 sectors per erased.*/while (num_sectors > 0) {erase_last = erase_first + MIN(num_sectors, MAX_ERASE_SECTORS_ONCE);/* Loop over the selected sectors and erase them. */retval = ROM_QSPI_EraseSector(bank->target, erase_first, erase_last);if (retval != ERROR_OK) {LOG_WARNING("AIR105 erase sector from %d to %d error(retry):%d", erase_first, erase_last, retval);if (error_times++ > 5) {return retval;}continue;}erase_first = erase_last;num_sectors -= MAX_ERASE_SECTORS_ONCE;}
-
3)至此,AIR105 驱动终于开发完成了。皇天不负有心人,CLion 给的惊喜太大了:
-
-
命令行测试时,最高速度只有 40 KiB/s 左右,这一下提高了快 4 倍。
-
4 附录1:调用 AIR105 FLM 算法的尝试
4.1 关于 Keil FLM
-
1)首先查看一下 FLASH 相关信息(这里显示了 15 MB 的大小,但手册上说只有 4MB):
-
2)Keil 规定,一个 FLM 文件中要包含一些指定函数及结构:
-
(1)函数(FlashPrg.c):
函数名 是否必须 描述 Init 是 初始化 UnInit 是 反初始化 EraseSector 是 扇擦除 ProgramPage 是 页擦除 EraseChip 否 片擦除。 BlankCheck 否 Blank 检查。 Verify 否 校验。 -
(2)FLASH 说明结构体(FlashDev.c):
struct FlashDevice const FlashDevice = {FLASH_DRV_VERS, // Driver Version, do not modify!"New Device 256kB Flash", // Device Name ONCHIP, // Device Type0x00000000, // Device Start Address0x00040000, // Device Size in Bytes (256kB)1024, // Programming Page Size0, // Reserved, must be 00xFF, // Initial Content of Erased Memory100, // Program Page Timeout 100 mSec3000, // Erase Sector Timeout 3000 mSec// Specify Size and Address of Sectors0x002000, 0x000000, // Sector Size 8kB (8 Sectors)0x010000, 0x010000, // Sector Size 64kB (2 Sectors) 0x002000, 0x030000, // Sector Size 8kB (8 Sectors)SECTOR_END };
- (3)官方说明文档:
- https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/flashAlgorithm.html
- https://developer.arm.com/documentation/ka002873/latest
-
-
3)函数说明:
/*** FLASH 初始化,每次编程时调用** Keil 提供的示例代码中实现了三件事:* (1)设置 FLASH 读取间隔(LATENCY)* (2)解锁 FLASH* (3)增加独立看门狗(IWDG)喂狗时间(应该是防止程序烧录过程中喂狗时间到)** @param adr Device base address* @param clk Clock frequency(Hz)* @param fnc Function code: 1, Erase; 2, Program; 3,Verify.* @return status information: 0, on success; 1, on failure.*/ int Init (unsigned long adr, unsigned long clk, unsigned long fnc);/*** FLASH 反初始化,在一次擦除、编程、或校验后调用。** Keil 提供的示例代码:重新锁定 FLASH** @param fnc Function code: 1, Erase; 2, Program; 3, Verify.* @return status information: 0, on success; 1, on failure.*/ int UnInit (unsigned long fnc);/*** 扇区擦除** Keil 提供的示例代码:重新锁定 FLASH** @param addr Sector address* @return status information: 0, on success; 1, on failure.*/ int EraseSector (unsigned long adr);/*** 页编程** Keil 提供的示例代码为每次写半个字(即 2 个字节)** @param adr Page start address* @param sz Page size* @param buf Data to be written* @return status information: 0, on success; 1, on failure.*/ int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf);/*** 片擦除** @return status information: 0, on success; 1, on failure.*/ int EraseChip (void);/*** Blank 检查,检查指定块是否是指定内容 pat。** @param adr Block start address* @param sz Block size in bytes* @param pat Pattern to compare* @return status information: 0, block content == pattern pat; 1, block content != pattern pat.*/ int BlankCheck (unsigned long adr, unsigned long sz, unsigned char pat);/*** 数据校验,校验 FLASH 数据是否和 buf 内容一致** @param adr Start address* @param sz Size in bytes* @param buf Data to be compared* @return success, sum of adr+sz; failure, other number represents the failing address.*/ unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf);
4.2 解析 FLM 文件
-
1)通过 arm-none-eabi-readelf 命令,找到 FLM 中定义的必要函数所在位置。
$ arm-none-eabi-readelf.exe --helpUsage: readelf <option(s)> elf-file(s) Display information about the contents of ELF format files$ arm-none-eabi-readelf.exe -a Air105.FLMSymbol table '.symtab' contains 371 entries: Num: Value Size Type Bind Vis Ndx Name 297: 00000089 32 FUNC GLOBAL HIDDEN 1 BlankCheck 317: 00000d61 12 FUNC GLOBAL HIDDEN 1 EraseChip 318: 00000d6d 40 FUNC GLOBAL HIDDEN 1 EraseSector 319: 00000d95 332 FUNC GLOBAL HIDDEN 1 Init 320: 00000ffd 86 FUNC GLOBAL HIDDEN 1 ProgramPage 365: 00001f89 26 FUNC GLOBAL HIDDEN 1 UnInit
-
参考:https://blog.csdn.net/sinat_31039061/article/details/128350295
这篇关于OpenOCD 添加 AIR105(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!