VSCode开发K230,实现代码补全、烧录运行

本文章参考k230 vscode 实现代码补全以及烧录

环境需求

引言

众所周知,CanMV IDE 中没有对代码的自动补全,以及编码体验还有待提高,而 Vscode 中编码因为缺少 micropython 库的支持导致频繁报红线错误影响体验。所以本文将带你从 0 开始配置一个 vscode 编写 K230 代码的环境。

获取官方 API

要在 vscode 中实现代码补全,首先需要把 K230 中的 MicroPython API 获取出来,这里使用的是 UP 主的代码并根据官方 API 手册尽量添加了所有 API

import gc
import os
import sys

# --- 1. 配置 ---
SAVE_ROOT = "/sdcard/k230_full_stubs"

# 普通单文件模块列表
SINGLE_MODULES = [
    "_thread",
    "aidemo",
    "array",
    "audio",
    "binascii",
    "camera",
    "cmath",
    "collections",
    "cv_lite",
    "display",
    "errno",
    "fpioa_manager",
    "fft",
    "gc",
    "hashlib",
    "heapq",
    "image",
    "io",
    "i2c",
    "i2c_salve",
    "json",
    "kpu",
    "machine",
    "maix",
    "math",
    "multimedia",
    "neopixel",
    "network",
    "nncase_runtime",
    "nonai2d",
    "np",
    "os",
    "pin",
    "pwm",
    "random",
    "re",
    "rtc",
    "rtt_interface",
    "select",
    "sensor",
    "socket",
    "spi",
    "spi_lcd",
    "ssl",
    "struct",
    "sys",
    "time",
    "touch",
    "uart",
    "ujson",
    "ubinascii",
    "ucollections",
    "uctypes",
    "uerrno",
    "uhashlib",
    "uheapq",
    "uio",
    "ure",
    "urandom",
    "ustruct",
    "utime",
    "uzlib",
    "video",
    "wdt",
    "zlib",
    "Ucryptolib",
    "uos",
    "usb",
    "mpp",
]

# 包结构模块:key 为包名(小写),value 为子模块名(小写)
PACKAGE_MODULES = {
    "libs": ["AI2D", "AIBase", "PipeLine", "utils", "YOLO"],
    "ulab": ["numpy"],
    "mpp": ["payload_struct"],
    # media 也作为包处理
    "media": [
        "audio",
        "camera",
        "display",
        "image",
        "maix",
        "media",
        "sensor",
        "vencoder",
        "video",
        "wave",
        "player",
        "mp4format",
        "pyaudio",
        "vdecoder",
        "uvc",
    ],
}


# --- 2. 初始化环境 ---
def ensure_dir(path):
    try:
        os.mkdir(path)
    except:
        pass


print(f"Target Directory: {SAVE_ROOT}")
ensure_dir(SAVE_ROOT)


# --- 3. 核心写入函数 (通用) ---
def write_item(f, name, obj, indent_lvl=0):
    indent = "    " * indent_lvl
    if name.startswith("__"):
        return

    try:
        obj_type = str(type(obj))

        # 函数/方法
        if "function" in obj_type or "method" in obj_type:
            f.write(
                f"{indent}def {name}(*args, **kwargs):\n{indent}    pass\n\n"
            )

        # 类 (递归写入类成员)
        elif "class" in obj_type or "type" in obj_type:
            f.write(f"{indent}class {name}:\n")
            members = dir(obj)
            has_member = False
            for m in members:
                if not m.startswith("__"):
                    try:
                        has_member = True
                        # 递归调用自己来写类里面的方法/属性
                        val = getattr(obj, m)
                        write_item(f, m, val, indent_lvl + 1)
                    except:
                        pass
            if not has_member:
                f.write(f"{indent}    pass\n")
            f.write("\n")

        # 模块 (作为类处理,用于单文件生成模式)
        elif "module" in obj_type:
            # 只有在非包模式下才把模块写成类
            f.write(f"{indent}class {name}:\n")
            for m in dir(obj):
                if not m.startswith("__"):
                    try:
                        write_item(f, m, getattr(obj, m), indent_lvl + 1)
                    except:
                        pass
            f.write(f"{indent}    pass\n\n")

        # 变量/常量
        elif "int" in obj_type:
            f.write(f"{indent}{name} = {obj}\n")
        elif "str" in obj_type:
            f.write(f"{indent}{name} = '{obj}'\n")
        elif "list" in obj_type:
            f.write(f"{indent}{name} = []\n")
        elif "dict" in obj_type:
            f.write(f"{indent}{name} = {{}}\n")
        else:
            f.write(f"{indent}{name} = None\n")
    except:
        pass


# --- 4. 生成普通单文件模块 (os.py, sys.py 等) ---
def generate_single_file_stubs():
    print("--- Phase 1: Generating Standard Libraries ---")
    for mod_name in SINGLE_MODULES:
        # 跳过 media 相关,由 Phase 2 处理
        if mod_name.startswith("media"):
            continue

        print(f"--> Processing: {mod_name}")
        try:
            mod = __import__(mod_name)
            path = f"{SAVE_ROOT}/{mod_name}.py"
            with open(path, "w") as f:
                f.write(f'"""Stub file for {mod_name}"""\n\n')
                for attr in dir(mod):
                    write_item(f, attr, getattr(mod, attr), 0)
        except ImportError:
            print(f"    Skipped (Not found)")
        except Exception as e:
            print(f"    Error: {e}")
        gc.collect()


# --- 5. 生成  包结构 ---
def generate_package_stubs():
    print("\n=== Phase 2: Generating Package Structures ===")
    for pkg_name, sub_list in PACKAGE_MODULES.items():
        print(f"\n--- Generating package: {pkg_name} ---")
        pkg_dir = f"{SAVE_ROOT}/{pkg_name}"
        ensure_dir(pkg_dir)

        # 1) 生成 __init__.py
        init_path = f"{pkg_dir}/__init__.py"
        print(f"--> Writing {init_path}")

        try:
            pkg = __import__(pkg_name)
            with open(init_path, "w") as f:
                f.write(f'"""Stub for top-level {pkg_name} package"""\n\n')
                for attr in dir(pkg):
                    write_item(f, attr, getattr(pkg, attr), 0)

        except ImportError:
            print("    Error: media module not found!")
            return

        # 5.2 生成子模块文件
        for sub in sub_list:
            module_name = f"{pkg_name}.{sub}"
            file_path = f"{pkg_dir}/{sub}.py"

            print(f"--> Generating {module_name}")

            try:
                exec(f"import {module_name} as m_sub")
                m_sub = locals()["m_sub"]

                with open(file_path, "w") as f:
                    f.write(f'"""Stub for {module_name}"""\n\n')
                    for attr in dir(m_sub):
                        write_item(f, attr, getattr(m_sub, attr), 0)

                print(f"    Saved: {file_path}")
            except ImportError:
                print(f"    Skipped (Not found): media.{sub}")
            except Exception as e:
                print(f"    Error: {e}")
            gc.collect()


# --- 主程序入口 ---
if __name__ == "__main__":
    print("Pre-loading key modules...")

    generate_single_file_stubs()
    generate_package_stubs()
    print("\n================ DONE ================")
    print(f"Stubs saved to: {SAVE_ROOT}")
    print("Please download this folder to your PC.")

将代码拷贝到一个文件,在 CanMV IDE 中打开文件,连接上 K230 运行

完成后,可以在资源管理器中打开 K230 的 SD 卡,找到 mtp://CanMV/sdcard/k230_full_stubs 文件夹,随便点击一个 py 文件查看是有函数生成如图所示即代表成功。

如果想要增加某个模块的 API,自行加入到 SINGLE_MODULES 或 PACKAGE_MODULES 里,打开 CanMV IDE 再运行即可。

创建虚拟环境

因为 K230 使用的是 micropython,且 API 大部分供 K230 使用就可以了,所以这里先使用 miniconda 创建一个 K230 的虚拟环境,防止破坏其他环境。

创建完虚拟环境后,会输出环境的路径,在资源管理器中打开这个路径,打开其下面的 Lib\site-packages 文件夹,我这里是(E:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages)。

把 K230SD 卡中的 k230_full_stubs 文件夹里面整个复制到 site-packages 里面去。文件结构如下图

E:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\
      libs/
      media/
      os.py
      machine.py
      ...

放置完后,可以在环境中输入下面的指令测试是否有异常

python -c "import machine; print(machine)"
python -c "import media.display; print(media.display)"
python -c "import libs.ai2d; print(libs.ai2d)"

像下图中所示,说明正确导入了每个模块。

打开 vscode,安装上 python 必要插件后,选择安装的 K230 虚拟环境。创建一个新 py 文件。

此时再输入一些 MicroPython 的 API,就可以自动补全了。

烧录运行

在vscode中烧录运行K230的程序,作者已更新,现在直接在插件中搜索"CanMV Flasher"下载并安装即可。我们只需要将扩展拖入安装即可。安装后点击左下角选择python编译器,也就是刚才创建的虚拟环境的编译器,可以在环境中输入 where python ,正常情况下第一行的路径就是虚拟环境的编译器路径。然后打开 设备管理器 ,找到K230的端口号,输入到左下角就行。

报错

当点击运行或者烧录的时候出现下面错误,在 CanMV IDE 中断开连接。

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "e:\ProgramFiles\Miniconda\envs\K230\Scripts\ampy.exe\__main__.py", line 6, in <module>
    sys.exit(cli())
             ~~~^^
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\click\core.py", line 1485, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\click\core.py", line 1406, in main
    rv = self.invoke(ctx)
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\click\core.py", line 1870, in invoke
    super().invoke(ctx)
    ~~~~~~~~~~~~~~^^^^^
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\click\core.py", line 1269, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\click\core.py", line 824, in invoke
    return callback(*args, **kwargs)
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\ampy\cli.py", line 99, in cli
    _board = pyboard.Pyboard(port, baudrate=baud, rawdelay=delay)
  File "e:\ProgramFiles\Miniconda\envs\K230\Lib\site-packages\ampy\pyboard.py", line 147, in __init__
    raise PyboardError('failed to access ' + device)
ampy.pyboard.PyboardError: failed to access \\.\COM12
posted @ 2025-12-06 15:49  菩萨野蛮  阅读(5)  评论(0)    收藏  举报