??首先说一下,我之前的开发流程是:VSCode 编辑代码 + Keil 编译及调试。Keil 的调试功能虽然很强大,但是多数功能需要配合 ARM 自家的 ULINKpro 才可以用,例如 Performance Analyzer、Event Viewer 等。而我手头只有Jlink 和 ULINK 非 pro 版的…
??在最近的项目中,随着代码量的不断增加,Keil 的编译速度瓶颈越来越明显!有的问题往往是调试一分钟,编译半小时!编译过慢的问题已经严重影响工作效率,于是开始寻找一个替代品!
Ozone 调试
??起初,在 SEGGER 官网发现了一个名为 Ozone 的 Jlink 专用的调试器,非常小巧,调试也挺好用。不过,它仅仅就是个 Jlink 配套的调试器,不能编译代码。如果使用它,开发流程就是:VSCode 编辑代码 + Keil 编译 + Ozone 调试。 调试的话,需要的功能基本都是全的! ??实际情况是,Keil 本身的调试功能还是挺好用的,再单独用 Ozone 调试似乎也没啥必要。因此,在试用了一段时间之后,现在基本很少用 Ozone 调试了。
后文再讲如何在 Qzone 中添加华大 MCU
SEGGER Embedded Studio
??放弃 Ozone 调试之后,无奈继续使用 Keil 编译及调试,然后继续寻找代替方案。同样在 SEGGER 官网转悠的时候,发现了 SEGGER Embedded Studio 这个东西,试用了一下还是不错的!
- 自带多套编译套件:GCC、Clang(LLVM)、SEGGER自家编译套件(应该是基于 LLVM 改的),随便选择使用哪个。
- 可以配置使用 ARM 的编译器(Keil 自带的 ARMCC ),这是重点!!! 因为工作环境不允许使用别的编译器发布程序!
- 代码编辑功能用起来也还行(相比于 Keil 来说),与 VSCode 这一类相比还是有些逊色!比较明显的就是代码高亮!
- 非商用,免费无限制,而 Keil、IAR 均限制代码量大小!
??在后续的了解过程中,发现 SEGGER Embedded Studio 就是 SEGGER 买了 CrossWorks 的代码源代码,然后自己再加工一下改出来的!CrossWorks 本身是支持多种调试器的,SEGGER Embedded Studio 则进行了限制,只能支持自家的 Jlink!! 下面是这两个软件的对比图: ??这两者都使用在线的 Packages 来提供了对于不同厂家的 MCU 的支持。使用者可以直接从软件的 Packages Manger 在线下载自己对应的 MCU 支持包(和 Keil5 中的 Pack Installer 一样的作用)!下面是 两者的 Package Manager 的对比图: ??从支持的 MCU 来看,CrossWorks 要更胜一筹!我觉得,说 SEGGER Embedded Studio 就是 CrossWorks 的阉割版一点都不为过!SEGGER Embedded Studio 也就比 CrossWorks 多了 SEGGER 自家基于 LLVM 的编译套件而已!
??关于 CrossWorks 这里不多说,想要进一步了解它的自行去 CrossWorks 的官网:https://www.rowley.co.uk/。下面我们重点来说一下 SEGGER Embedded Studio,当然,这俩软件的配置及项目管理方式是一模一样的!只要搞懂其中一个,另一个肯定不在话下。
项目管理
??SEGGER Embedded Studio 的项目管理使用了 Solution + Project 的方式,相比于 Keil 的单 Project 项目管理(注:Keil 也支持 WorkSpace),不支持文件夹嵌套 不知道强多少。一个 Solution 下可以有多个 Project,Project 下可以有个多个文件或者文件夹,文件夹下又可以有文件夹或文件!但是,SEGGER Embedded Studio 的 Solution + Project 的配置却是相当混乱的,或者说是不容易理解的! ??上图中,我是经过整理之后(手动编辑了.emProject)的项目文件,默认的项目文件是有好几种配置的。导致新手根本不知道该怎么去更改配置。比如,更改 Solution 的各种配置,还是更改 Project 的各种配置。Solution 下的 Public Configuration 及 Private Configuration 下的各种配置与 Project 下的 Public Configuration 及 Private Configuration 下的各种配置有啥区别?如果在结合 .emProject 那就更难以理解了。
??根据目前我的理解,他们的范围由大到小依次为 Solution > Project > Configuration,后者可以继承前者的各种配置。Public Configuration 可以继承 Private Configuration 中的各种配置,我们实际的处理(编译调试)的项目,实际就是一个个的 Public Configuration。因此,那些通用配置一般都放在上层,下层直接继承!
编译套件选择
??前面我们说过,SEGGER Embedded Studio 本身就带了 GCC、Clang(LLVM)、SEGGER自家编译套件(应该是基于 LLVM 改的)这三种编译套件,在建立项目时,我们可以根据需要选择其中一种。但是这里有个前提,必须是 SEGGER Embedded Studio 所支持的 MCU 才可以。因为,MCU 的启动文件是特定于编译器的。例如,在 STM32 系列 MCU 中,都会带有不容编译器的启动文件: ??除此之外,其他有些文件也是特定于开发环境的,例如,Keil(ARMCC)的分散加载文件(.sct); IAR 的 ILINK Configuration File (.icf)文件等。
SEGGER Embedded Studio 默认(SEGGER 自家编译套件)也是使用 .icf 文件来生成最终的 elf 文件
??很不幸的是,SEGGER Embedded Studio 目前还不支持华大的 MCU。这也就意味着,我们不能在 SEGGER Embedded Studio 中建立华大的 MCU 的项目(华大的官网的开发环境支持包也没有提供对 SEGGER Embedded Studio 的支持)。
??幸运的是,SEGGER Embedded Studio 支持使用外部编译套件,我么可以直接将 Keil 的项目文件转成 SEGGER Embedded Studio 项目,编译套件选择外部的 ARMCC。 其中,选择 MCU 型号这一步,仍然需要我们自己来处理,SEGGER 的所有工具目前都不支持华大 MCU,下面我们来讲解如何处理!
添加华大 MCU 支持
??SEGGER 的所有工具目前都不支持华大 MCU 的,在选择 MCU 的列表中,是没有华大 MCU 的。在 SEGGER 的系列工具中(都是使用的 Jlink 的配置文件),都会有个名为 JLinkDevices.xml 的文件,这个文件,就是Jlink 支持的 MCU 的列表。注意:旧版本的 Jlink 没有这文件! 我们可以直接编辑这个文件,将华大MCU添加进去,如下:
<Device>
<ChipInfo Vendor="O2Micro" Name="OZ93510F160LN" Core="JLINK_CORE_CORTEX_M0" />
<FlashBankInfo Name="QSPI Flash" BaseAddr="0x60000000" MaxSize="0x00020000" Loader="Devices/O2Micro/O2Micro_OZ93510F160LN_QSPI.elf" LoaderType="FLASH_ALGO_TYPE_OPEN" />
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L176" WorkRAMAddr="0x20000000" WorkRAMSize="0x2000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/FlashHC32L17X_128K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L136" WorkRAMAddr="0x20000000" WorkRAMSize="0x2000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_64K" BaseAddr="0x0" MaxSize="0x10000" Loader="Devices/HDSC/FlashHC32L13X_64K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L130" WorkRAMAddr="0x20000000" WorkRAMSize="0x2000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_64K" BaseAddr="0x0" MaxSize="0x10000" Loader="Devices/HDSC/FlashHC32L13X_64K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F030" WorkRAMAddr="0x20000000" WorkRAMSize="0x2000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_64K" BaseAddr="0x0" MaxSize="0x10000" Loader="Devices/HDSC/FlashHC32F030_64K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L110x4" WorkRAMAddr="0x20000000" WorkRAMSize="0x800" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_16K" BaseAddr="0x0" MaxSize="0x4000" Loader="Devices/HDSC/FlashHC32L110_16K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L110x6" WorkRAMAddr="0x20000000" WorkRAMSize="0x1000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_32K" BaseAddr="0x0" MaxSize="0x8000" Loader="Devices/HDSC/FlashHC32L110_32K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F003" WorkRAMAddr="0x20000000" WorkRAMSize="0x800" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_16K" BaseAddr="0x0" MaxSize="0x4000" Loader="Devices/HDSC/FlashHC32F003_16K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F005" WorkRAMAddr="0x20000000" WorkRAMSize="0x1000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_32K" BaseAddr="0x0" MaxSize="0x8000" Loader="Devices/HDSC/FlashHC32F005_32K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L15" WorkRAMAddr="0x20000000" WorkRAMSize="0x1800" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/HC32L15.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F_M14" WorkRAMAddr="0x20000000" WorkRAMSize="0x2000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/HC32F_M14.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F46x" WorkRAMAddr="0x20000000" WorkRAMSize="0x10000" Core="JLINK_CORE_CORTEX_M4"/>
<FlashBankInfo Name="Flash_512K" BaseAddr="0x0" MaxSize="0x80000" Loader="Devices/HDSC/HC32F46x.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L19x" WorkRAMAddr="0x20000000" WorkRAMSize="0x8000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_256K" BaseAddr="0x0" MaxSize="0x40000" Loader="Devices/HDSC/FlashHC32L19X_256K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F19x" WorkRAMAddr="0x20000000" WorkRAMSize="0x8000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_256K" BaseAddr="0x0" MaxSize="0x40000" Loader="Devices/HDSC/FlashHC32F19X_256K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F17x" WorkRAMAddr="0x20000000" WorkRAMSize="0x4000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/FlashHC32F17X_128K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L17x" WorkRAMAddr="0x20000000" WorkRAMSize="0x4000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/FlashHC32L17X_128K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32F072" WorkRAMAddr="0x20000000" WorkRAMSize="0x4000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/FlashHC32F072_128K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
<Device>
<ChipInfo Vendor="HDSC" Name="HC32L07X" WorkRAMAddr="0x20000000" WorkRAMSize="0x4000" Core="JLINK_CORE_CORTEX_M0"/>
<FlashBankInfo Name="Flash_128K" BaseAddr="0x0" MaxSize="0x20000" Loader="Devices/HDSC/FlashHC32L07X_128K.FLM" LoaderType="FLASH_ALGO_TYPE_OPEN" AlwaysPresent="1"/>
</Device>
添加文件该文件之后,细心地就会发现,上面的代码中的 Loader 指向了一个文件夹中的特定文件,这些文件就是指定的 MCU 的下载算法文件。这些文件我们可以直接从华大官网的 MCU 软件包中获得。然后放到上面写的路径中 Devices/HDSC 中。 这一步处理完成后,我们就可以在 SEGGER 系列工具中选择华大的 MCU 了!
错误处理
??直接从 Keil 导入项目之后,编译可能会报错误,这些错误基本都是因为在自己的项目(Keil)中使用了编译编译环境(Keil)特定的文件导致或者说路径变化导致的!下面我说一下我遇到的几个问题
- 第一个错误就是路径问题。在 Keil 中,默认使用 Windows 路径风格:反斜线,例如: …\Directory 。而,SEGGER Embedded Studio 使用的是 Linux 路径风格:斜线,例如:…/…/Directory。如下,我的项目中有使用的命令,就会导致 SEGGER Embedded Studio 报错
- 输出文件夹变动导致的错误。项目在导入到 SEGGER Embedded Studio 后,我编译生成的文件路径会被放到 SEGGER Embedded Studio 定义的目录中(配置项中可更改),同样是上图,文件夹变化后(归根结底还是文件路径变化),编译也会报错。我们自行做对应的更改就可以了。
外设寄存器
??经过上面的处理之后基本编译 + 调试应该没有问题了。但是其中一个问题就是,在调试时,只能看到 CPU 的寄存器(Cortex-M 核定义的寄存器),MCU 外设寄存器还是没有的,这里也需要我们自己来处理。
??默认情况下,SEGGER Embedded Studio 使用的 xml 格式的外设寄存器定义文件。但是这个文件只有 SEGGER Embedded Studio 支持的 MCU 才有,我们自己添加的 MCU 是没有这个文件的。幸运的是,SEGGER Embedded Studio 兼容 ARM 定义的 .svd 的文件,我们可以为 SEGGER Embedded Studio 指定我们 MCU 的 SVD 文件。 MCU 的 SVD 文件可以在华大官网的 MCU 软件包中获得!至此,我们应该就可以愉快的编译加调试了!
遗留问题
- 目前在调试时发现, Watch某个变量不能正常工作,无法识别结构体变量。
参考
- SEGGER Embedded Studio 手册
|