- {
- "name": "geehy_apm32",
- "title": "Geehy APM32",
- "description": "APM32 MCU",
- "homepage": "https://www.geehy.com/product/second/mcu",
- "license": "MIT",
- "keywords": [
- "dev-platform",
- "ARM",
- "Cortex-M",
- "Geehy",
- "APM32"
- ],
- "engines": {
- "platformio": "^5"
- },
- "repository": {
- "type": "git",
- "url": "https://gitee.com/quincyzh/geehy-apm32-platform.git"
- },
- "version": "0.0.1",
- "frameworks": {
- "geehy-apm32": {
- "script": "builder/frameworks/geehy_apm.py",
- "description": "Geehy APM32 MCU SDK",
- "homepage": "https://www.geehy.com/",
- "title": "Geehy APM32 SDK"
- }
- },
- "packages": {
- "toolchain-gccarmnoneeabi": {
- "type": "toolchain",
- "owner": "platformio",
- "version": ">=1.60301.0,<1.80000.0",
- "optionalVersions": [
- "~1.60301.0",
- "~1.80201.0",
- "~1.90201.0",
- "~1.120301.0"
- ]
- },
- "framework-geehy-apm32f4": {
- "type": "framework",
- "owner": "quincy-zh",
- "optional": false
- },
- "tool-openocd": {
- "type": "uploader",
- "optional": true,
- "owner": "platformio",
- "version": "~3.1200.0"
- },
- "tool-jlink": {
- "type": "uploader",
- "optional": true,
- "owner": "platformio",
- "version": "^1.63208.0"
- }
- }
- }
version 及其前面的,都是一些基本信息,也是字面含义,不再赘述。
关键的地方有两点:
1. frameworks 这个内容表明支持哪些 framework,以及对应的脚本。脚本主要用于说明编译准备(编译、链接参数,必备的文件等)。
2. packages 需要那些工具的支持。上面的内容对应了:
- 使用 gccarmnoneeabi 编译工具链。
- 使用 geehy-apm32f4 这个 framework 作为代码框架。
- 支持 openocd 和 jlink 作为下载工具。
platform.py 文件
这个文件是 Python 脚本,里面定义了一个名为 `Geehy_apm32Platform` 的类,类名与 platform.json 里的 `name` 对应。
这个类配置、检查本 platform 所需的必备参数:
- # Copyright 2014-present PlatformIO <contact@platformio.org>
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import os
- import sys
- import subprocess
- from platformio.managers.platform import PlatformBase
- IS_WINDOWS = sys.platform.startswith("win")
- class Geehy_apm32Platform(PlatformBase):
- def configure_default_packages(self, variables, targets):
- board = variables.get("board")
- board_config = self.board_config(board)
- build_core = variables.get(
- "board_build.core", board_config.get("build.core", "arduino"))
- build_mcu = variables.get("board_build.mcu", board_config.get("build.mcu", ""))
- frameworks = variables.get("pioframework", [])
-
- if "geehy-apm32" in frameworks:
- assert build_mcu, ("Missing MCU field for %s" % board)
- device_package = "framework-geehy_%s" % build_mcu[0:7]
- self.frameworks["geehy-apm-sdk"] = {"package": device_package}
- #if any(f in frameworks for f in ("cmsis", "stm32cube")):
- # self.packages["tool-ldscripts-ststm32"]["optional"] = False
- default_protocol = board_config.get("upload.protocol") or ""
-
- # configure J-LINK tool
- jlink_conds = [
- "jlink" in variables.get(option, "")
- for option in ("upload_protocol", "debug_tool")
- ]
- if board:
- jlink_conds.extend([
- "jlink" in board_config.get(key, "")
- for key in ("debug.default_tools", "upload.protocol")
- ])
- jlink_pkgname = "tool-jlink"
- if not any(jlink_conds) and jlink_pkgname in self.packages:
- del self.packages[jlink_pkgname]
- return PlatformBase.configure_default_packages(self, variables,
- targets)
- def install_package(self, name, *args, **kwargs):
- pkg = super().install_package(name, *args, **kwargs)
- if name != "framework-zephyr":
- return pkg
- if not os.path.isfile(os.path.join(pkg.path, "_pio", "state.json")):
- self.pm.log.info("Installing Zephyr project dependencies...")
- try:
- subprocess.run([
- os.path.normpath(sys.executable),
- os.path.join(pkg.path, "scripts", "platformio", "install-deps.py"),
- "--platform", self.name
- ])
- except subprocess.CalledProcessError:
- self.pm.log.info("Failed to install Zephyr dependencies!")
- return pkg
- def get_boards(self, id_=None):
- result = PlatformBase.get_boards(self, id_)
- if not result:
- return result
- if id_:
- return self._add_default_debug_tools(result)
- else:
- for key, value in result.items():
- result[key] = self._add_default_debug_tools(result[key])
- return result
- def _add_default_debug_tools(self, board):
- debug = board.manifest.get("debug", {})
- upload_protocols = board.manifest.get("upload", {}).get(
- "protocols", [])
- if "tools" not in debug:
- debug["tools"] = {}
- # J-Link, CMSIS-DAP
- for link in ("jlink", "cmsis-dap"):
-
- if link not in upload_protocols or link in debug["tools"]:
- continue
-
- if link == "jlink":
- assert debug.get("jlink_device"), (
- "Missed J-Link Device ID for %s" % board.id)
- debug["tools"][link] = {
- "server": {
- "package": "tool-jlink",
- "arguments": [
- "-singlerun",
- "-if", "SWD",
- "-select", "USB",
- "-device", debug.get("jlink_device"),
- "-port", "2331"
- ],
- "executable": ("JLinkGDBServerCL.exe"
- if IS_WINDOWS else
- "JLinkGDBServer")
- }
- }
- else:
- server_args = ["-s", "$PACKAGE_DIR/openocd/scripts"]
- if debug.get("openocd_board"):
- server_args.extend([
- "-f", "board/%s.cfg" % debug.get("openocd_board")
- ])
- else:
- assert debug.get("openocd_target"), (
- "Missed target configuration for %s" % board.id)
- server_args.extend([
- "-f", "interface/%s.cfg" % link,
- "-c", "transport select %s" % (
- "hla_swd" if link == "stlink" else "swd"),
- "-f", "target/%s.cfg" % debug.get("openocd_target")
- ])
- server_args.extend(debug.get("openocd_extra_args", []))
- debug["tools"][link] = {
- "server": {
- "package": "tool-openocd",
- "executable": "bin/openocd",
- "arguments": server_args
- }
- }
- debug["tools"][link]["onboard"] = link in debug.get("onboard_tools", [])
- debug["tools"][link]["default"] = link in debug.get("default_tools", [])
- board.manifest["debug"] = debug
- return board
- def configure_debug_session(self, debug_config):
- if debug_config.speed:
- server_executable = (debug_config.server or {}).get("executable", "").lower()
- if "openocd" in server_executable:
- debug_config.server["arguments"].extend(
- ["-c", "adapter speed %s" % debug_config.speed]
- )
- elif "jlink" in server_executable:
- debug_config.server["arguments"].extend(
- ["-speed", debug_config.speed]
- )
boards 文件夹
这个文件夹下是本 platform 相关的开发板描述文件。开发板的描述文件只要是声明了一些关于开发板的基本信息,如 MCU 内核、型号,支持的framework、调试工具等。
boards\apm32f407_tiny.json 这个文件的内容:
- {
- "build": {
- "core": "apm32",
- "cpu": "cortex-m4",
- "extra_flags": "-DAPM32F4 -DAPM32F40X",
- "f_cpu": "168000000L",
- "mcu": "apm32f407igt6",
- "product_line": "APM32F40x",
- "variant": "APM32F4xx"
- },
- "debug": {
- "default_tools": [
- "cmsis-dap"
- ],
- "jlink_device": "APM32F407IG",
- "openocd_target": "APM32F407IG",
- "svd_path": "APM32F40x.svd"
- },
- "frameworks": [
- "geehy-apm32"
- ],
- "name": "APM32F407 TINY",
- "upload": {
- "maximum_ram_size": 131072,
- "maximum_size": 1048576,
- "protocol": "cmsis-dap",
- "protocols": [
- "cmsis-dap",
- "jlink"
- ]
- },
- "url": "https://www.geehy.com/design/hardware_detail/32",
- "vendor": "Geehy"
- }
builder 文件夹
这个文件夹下的内容是 Scons 的脚本(其实也是 Python 的脚本),描述了几项内容:
- 使用哪种编译工具链
- 编译参数
- 烧录/调试工具及对应参数
- 等等
这些脚本一起配合,才能完成编译、烧录、调试等工作。
文件内容较多,就不贴出来了。讲几个我觉得重要的:
builder\main.py 文件
编译工具链的选择:
- env.Replace(
- AR="arm-none-eabi-gcc-ar",
- AS="arm-none-eabi-as",
- CC="arm-none-eabi-gcc",
- CXX="arm-none-eabi-g++",
- GDB="arm-none-eabi-gdb",
- OBJCOPY="arm-none-eabi-objcopy",
- RANLIB="arm-none-eabi-gcc-ranlib",
- SIZETOOL="arm-none-eabi-size",
- ARFLAGS=["rc"],
- SIZEPROGREGEXP=r"^(?:\.text|\.data|\.rodata|\.text.align|\.ARM.exidx)\s+(\d+).*",
- SIZEDATAREGEXP=r"^(?:\.data|\.bss|\.noinit)\s+(\d+).*",
- SIZECHECKCMD="$SIZETOOL -A -d $SOURCES",
- SIZEPRINTCMD='$SIZETOOL -B -d $SOURCES',
- PROGSUFFIX=".elf"
- )
上面的代码片段,指定了使用 ARM-GCC 作为编译工具链,还写了几个正则表达式,用于获取编译后附件的尺寸信息。
下面的代码片段指示了烧录工具必须的参数:
- upload_protocol = env.subst("$UPLOAD_PROTOCOL")
- debug_tools = board.get("debug.tools", {})
- upload_source = target_firm
- upload_actions = []
- if upload_protocol.startswith("jlink"):
- def _jlink_cmd_script(env, source):
- build_dir = env.subst("$BUILD_DIR")
- if not isdir(build_dir):
- makedirs(build_dir)
- script_path = join(build_dir, "upload.jlink")
- commands = [
- "h",
- "loadbin %s, %s" % (source, board.get(
- "upload.offset_address", "0x08000000")),
- "r",
- "q"
- ]
- with open(script_path, "w") as fp:
- fp.write("\n".join(commands))
- return script_path
- env.Replace(
- __jlink_cmd_script=_jlink_cmd_script,
- UPLOADER="JLink.exe" if system() == "Windows" else "JLinkExe",
- UPLOADERFLAGS=[
- "-device", board.get("debug", {}).get("jlink_device"),
- "-speed", env.GetProjectOption("debug_speed", "4000"),
- "-if", ("jtag" if upload_protocol == "jlink-jtag" else "swd"),
- "-autoconnect", "1",
- "-NoGui", "1"
- ],
- UPLOADCMD='$UPLOADER $UPLOADERFLAGS -CommanderScript "${__jlink_cmd_script(__env__, SOURCE)}"'
- )
- upload_actions = [env.VerboseAction("$UPLOADCMD", "Uploading $SOURCE")]
builder\frameworks\geehy_apm.py 文件
这个文件指示了 framework_geehy_apm (可以理解为 SDK)里头文件的目录,哪些文件需要编译~例如:
- sources_path = os.path.join(FRAMEWORK_DIR, "Libraries", "Device", "Geehy", "APM32F4xx", "Source")
- prepare_startup_file(sources_path)
- env.BuildSources(
- os.path.join("$BUILD_DIR", "FrameworkCMSIS"),
- sources_path,
- src_filter=[
- "-<*>",
- "+<system_%sxx.c>" % mcu[0:7],
- "+<gcc/startup_%s.S>" % PRODUCT_LINE.lower(),
- ]
- )
这段代码指示了 APM32F4xx SKD 里关于 CMSIS 部分需要加入编译的代码~
整理编写 framework: framework-geehy-apm32f4
前面提到,这个 framework 相当于厂商提供的 SDK。编写整理改部分内容所需要做的就是声明一些基本信息就可以。具体的编译参数,需要编译的文件,头文件的路径等信息,都在 `builder\frameworks\geehy_apm.py` 文件里有说明。
这个文件夹下除了 SDK 部分还需要两个文件:
.piopm 文件
这个文件其实也是 JSON 格式,内容如下:
- {
- "type": "tool",
- "name": "framework-geehy-apm32f4",
- "version": "0.0.1",
- "spec": {
- "owner": "quincy-zh",
- "id": "",
- "name": "framework-geehy-apm32f4",
- "requirements": null,
- "uri": null
- }
- }
关于这个文件有个注意事项:platform.json 文件中指定这个 framework 的信息要与这里的信息一致,否则编译是会报错说找不到 framework:
package.json 文件
这个文件也是说明本 framework 的信息,与上面那个文件的内容差不多,不知道是不是后续 PlatformIO 版本会淘汰这个~
- {
- "description": "PIO framework for Geehy APM32F4",
- "keywords": [
- "framework",
- "hal",
- "apm32",
- "geehy"
- ],
- "name": " framework-geehy-apm32f4",
- "version": "0.0.1",
- "homepage": "https://github.com/Quincy-Zh/framework-geehy-apm32f4.git"
- }
总结
利用 PlatformIO 官方提供的内容,很容易给那些官方不支持的 MCU 提供开发包。本文只是简单地介绍了一个起手式,还有很多地方不完善,使用中需要注意~~~
最后一个简短的声明:
1. 内容是基于 PlatformIO 官方的相关文件修改而来,可能有一些未使用到的内容,但是不影响使用^-^
2. 本文描述的内容仅供学习参考,如果使用与于正式产品,请自行评估安全性、合法性以及相关知识产权风险。
3. 本文使用 SDK 是修改自官方提供的 SDK,非 Geehy 官方作品。