买了块 K230,带触摸屏和摄像头。牌子是创乐博,本来以为是杂牌,看下来似乎是用的官方板的方案,型号 CanMV-K230 V3。接口挺丰富,网口、USB OTG、USB 转串口、HDMI、音频都有,还有两个 MIPI CSI,是同步的,可以做双目,就是没有 USB-A。
这板子大学生打电赛用得多,我印象里也是靠开箱即用和预置例程火起来的。6 TOPS 的算力,这个价位拿来做视觉性价比相当强。

K230 小核 C908 跑 Linux,大核跑 RT-Smart。我后面打算在小核上跑自己写的内核,引导这一段得先摸清楚,顺便想把官方的 Buildroot 换成 Ubuntu,屏幕和摄像头都要能用。
拉 u-boot
嘉楠没有单独的 u-boot 仓库。k230_linux_sdk 里是上游 u-boot 2022.10 加一个 overlay 目录,SDK 构建时把 buildroot-overlay/boot/uboot/u-boot-2022.10-overlay 整个盖到上游源码上。我在本地建了个仓库,上游 v2022.10 一个分支,main 和 dev 两版 overlay 各盖一个分支,git diff 就能看嘉楠改了什么。两版 overlay 差了一百多个文件,dev 的新得多。
之前听说这个 u-boot 有检测、得配合它的内核用。看了 board/canaan/common/k230_img.c,老的启动命令 k230_boot 确实会查 K230 固件头(magic、SHA256,开安全启动还验 RSA/SM2 签名)。但新版 SDK 的默认 env 不走它:
bootcmd=run blinux;
blinux=k230_set_dtb && ext4load mmc ${mmc_boot_dev_num}:1 0x3000000 /fw_jump_add_uboot_head.bin && ext4load mmc ${mmc_boot_dev_num}:1 0x200000 /${k} && ext4load mmc ${mmc_boot_dev_num}:1 0x2200000 /${dtb} && bootm 0x3000000 - 0x2200000;bootm 只校验 OpenSBI fw_jump 外面那层 uImage 头,fw_jump 再跳到 0x200000 的 Image,内核本身不查。u-boot 自己跑在 M 模式(RISC-V 的 Kconfig 默认 RISCV_MMODE,k230 的 defconfig 没改),走 fw_jump 进内核才是 S 模式、有 SBI。DRAM 从 0 开始,不是 RISC-V 常见的 0x80000000。
先刷官方镜像
先刷嘉楠官网的 CanMV-K230_V3P0_linux_v0.6.4_nncase_v2.9.0.img.gz 试板子。串口不用飞线,Type-C 口后面有颗 CH342,电脑上出来两个口,/dev/ttyACM0 是小核 Linux 的 UART0,/dev/ttyACM1 是大核的。
插上以后没进系统,停在了 u-boot:
Unknown command '0`30`30`30`30`30`3' - try 'help' K230#
这串乱码是电脑上的 ModemManager 发的。它把新插的 ACM 设备当猫,往里发 AT 探测,u-boot 的 bootdelay 只有 1 秒,一个字符就把自动启动打断了。之后每次重新插 Type-C 都有可能这样,手动敲 boot 就行。
在 u-boot 里顺手看了下内存:
K230# bdinfo ... memory[0] [0x0-0x3fffffff], 0x40000000 bytes flags: 0
我这块是 1GB。设备树里写的是 512MB,u-boot 开机自己探测容量(detect_ddr_size()),bootm 时再用 fdt_fixup_memory() 改写 DTB 里的 memory 节点,所以以实测为准。
boot 进去,OpenSBI v1.4 → Linux 6.6.36 → canaan login:,板子没问题。
自己组 Ubuntu
版本选 24.04。Ubuntu 从 25.10 开始 riscv64 要求 RVA23,C908 不够。
做法是官方镜像的 u-boot 和 boot 分区不动,第二个分区换成 debootstrap 出来的 rootfs:
sudo debootstrap --arch=riscv64 --components=main,universe --include=systemd-sysv,udev,... noble ubuntu-rootfs https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/
启动参数不用改,u-boot 在 env 里没有 bootargs 的时候用写死的一条:
root=/dev/mmcblk1p2 loglevel=8 rw rootdelay=4 rootfstype=ext4 console=ttyS0,115200 earlycon=sbi
没有 init=,systemd 能直接接管。厂商内核的 config 用 extract-ikconfig 抽出来看了,systemd 要的 DEVTMPFS、CGROUPS、FHANDLE、AUTOFS 这些都开着。内核模块和 AIC8800 WiFi 固件从官方 rootfs 里用 debugfs rdump 掏出来,放进新 rootfs,不用挂载也不用 root。另外加了个首次开机扩满分区的 service。
第一次启动:
Welcome to Ubuntu 24.04 LTS! ... k230 login:
systemctl --failed 是空的,可用内存 972MB,WiFi 驱动自己加载了,wlan0、wlan1 都在。
屏幕不亮
接着发现屏幕一直黑着。开机日志里显示驱动是起来了的:
[drm] Initialized canaan-drm 1.0.0 20230501 for soc:display-subsystem on minor 0 canaan-drm soc:display-subsystem: [drm] bpp/depth value of 32/24 not supported canaan-drm soc:display-subsystem: [drm] No compatible format found canaan-drm soc:display-subsystem: [drm] *ERROR* fbdev: Failed to setup generic emulation (ret=-22)
原因是fbdev 要 32 位色,驱动给不了,/dev/fb0 建不出来,开机命令行也就上不了屏。我想绕一下,在 u-boot 里临时加 video=DSI-1:480x800-16 让它用 16 位色。fb0 是建出来了,紧接着:
[CRTC:46:canaan_crtc] vblank wait timed out [drm] *ERROR* flip_done timed out [drm] *ERROR* [CRTC:46:canaan_crtc] commit wait timed out
每次往屏幕写都要等 10 秒超时,串口跟着卡,systemd 起得极慢。屏幕背光亮,画面全黑,我往 fb0 里写满随机数也没出雪花。
这时候我一度以为是驱动或者屏有问题。先拿嘉楠的 CanMV MicroPython 固件(v1.8)刷了一遍,屏幕亮了,有字,硬件没问题。
回到 Linux 用 modetest 直接看 DRM:
31 0 0 0,0 0,0 0 0x00000001 formats: NV12 NV21 NV16 NV61 33 0 0 0,0 0,0 0 0x00000001 formats: AR24 AR12 AR15 RG24 RG16 BG24
v0.6.4 这版内核的主图层(plane 31)只收 YUV,RGB 只能放在叠加层上。而且旧接口不支持:
failed to set mode: Function not implemented
换成 atomic,主图层给 NV12,叠加层给 ARGB8888:
modetest -M canaan-drm -a -s 48@46:480x800@NV12 -P 33@46:480x800@AR24
彩条出来了,驱动和屏都是好的。开机命令行要走 fbcon,嘉楠压根没打算支持这条路,SDK 里他们自己的 Ubuntu 脚本 distribution/rootfs_ubuntu_gen.sh 是让 Qt 直接走 DRM:
export QT_QPA_FB_DRM=1
...
{ "name": "HDMI1", "format": "argb8888" }那个 video= 参数我没存进 env,断电重插就恢复了。
摄像头:先在官方镜像上跑通
嘉楠的摄像头链路是 vvcam 内核模块加 isp_media_server,v4l2-drm 把画面直接送上屏。官方镜像的 rcS 里是这么拉起来的:
modprobe vvcam_isp modprobe vvcam_mipi modprobe vvcam_vb modprobe vvcam_isp_subdev modprobe vvcam_video ISP_MEDIA_SENSOR_DRIVER=/usr/lib/libvvcam.so /usr/bin/isp_media_server > /dev/null 2> /tmp/isp.err.log &
刷回官方镜像跑 v4l2-drm -d 1 -w 480 -h 800,能出画面:
poll: 59.99, display: 59.99, [0]: 30.00, poll: 60.02, display: 60.02, [0]: 27.55,
但方向转了 90 度。屏是竖的 480x800,摄像头是横的。我试了 -w 800 -h 480,结果一半是紫色底色,另一半是拉伸出来的针状马赛克,v4l2-drm 不会自己旋转。
驱动里其实有一层带 rotation 属性的视频图层(modetest 里是 plane 43),libdisplay 也会找它。SDK dev 分支的 v4l2-drm 已经加了参数:
--rotation N Rotation: 0=0°, 1=90°, 2=180°, 3=270°
v0.6.4 发布镜像里的版本没有。
Ubuntu 上 ISP 起不来
同时我把 v0.6.4 的这几个文件拷进 Ubuntu 试过(板子没网,靠串口 base64 推过去,580KB 传了 219 秒)。依赖只多一个 libmxml,glibc 要求最高 2.33,Ubuntu 的 2.39 够用。服务能起,但:
ERROR : [ISP_MEDIA_ISP_DEVICE] CamDevice Creat Isp Device Handle Failed, ret is 1 ERROR : [ISP_MEDIA_ISP_EVENT] /dev/v4l-subdev0 Port 0 Chn 0 Create isp device failed, ret is 19
内核日志里 ISP 刚 open 就 release,MIPI 和 sensor 都没碰到。SDK dev 分支给 Debian 准备的 vvcam/deb/etc/vvcam/isp_start.sh 里有这么一段:
# Debian + CONFIG_RT_GROUP_SCHED: default sched_rt_runtime_us blocks SCHED_RR # even for root (chrt EPERM). Disable bandwidth limit so isp_media_server # can create SCHED_RR IRQ/media threads (needed to avoid motion jitter). if [ -w /proc/sys/kernel/sched_rt_runtime_us ]; then echo -1 > /proc/sys/kernel/sched_rt_runtime_us fi
厂商内核确实开着 CONFIG_RT_GROUP_SCHED。systemd 会把进程放进子 cgroup,子 cgroup 默认没有实时配额;Buildroot 不用 systemd,碰不到这个。
旋转要新版程序,ISP 的修法也在新版脚本里,干脆整套用 dev 源码编。
用 dev 源码整套编
SDK 带 Dockerfile(Ubuntu 22.04),我宿主机是 26.04,gcc 太新,就在容器里编:
docker build -f tools/docker/Dockerfile -t k230-linux-sdk tools/docker
文档写的工具链是玄铁 V2.10.1,dev 分支的 defconfig 已经换了:
BR2_TOOLCHAIN_EXTERNAL_PATH="/opt/toolchain/Xuantie-900-gcc-linux-6.6.0-glibc-x86_64-V3.0.2/"
下载地址在 tools/install_toolchain_and_depend.sh 里,Xuantie-900-gcc-linux-6.6.0-glibc-x86_64-V3.0.2-20250410.tar.gz,536MB,MD5 对得上。解压后挂到容器的 /opt/toolchain:
docker run --rm -h k230 -v ~/my_k230:/home/l/my_k230 -v ~/my_k230/toolchain:/opt/toolchain -w /home/l/my_k230/k230_linux_sdk --entrypoint /bin/bash k230-linux-sdk -c "make CONF=k230_canmv_v3_defconfig"
Build complete! Image: /home/l/my_k230/k230_linux_sdk/output/k230_canmv_v3_defconfig/images/sysimage-sdcard.img.gz
容器里是 root 编的,output 下全是 root 的文件,再起一次容器 chown -R 1000:1000 改回来。产物里除了 u-boot、OpenSBI、内核、设备树,images/deb/ 下还有一组给 Ubuntu/Debian 用的包,k230-vvcam.deb 里就是 isp_media_server、新版 v4l2-drm、vvcam 模块、各家 sensor 的配置和那个 vvcam.service。
新镜像这样拼:前 128MiB(SPL、u-boot、env、boot 分区)直接取 SDK 编出来的 sysimage-sdcard.img,第二分区放 Ubuntu。rootfs 里的模块和固件换成新编的,k230-vvcam.deb 的内容装进去,apt 补一个 libmxml1,之前手工拷进去的旧版组件删掉。新版 SDK 出的是 MBR 分区表,v0.6.4 是 GPT,整卡重写没影响。
新系统:摄像头型号对不上
新卡开机,vvcam.service 正常,isp_media_server 起来了。跑 v4l2-drm -d 1 报错,看了下节点名,新版多了个设备,摄像头挪到了 video2:
video0 mvx video1 canaan-non-ai-2d video2 vvcam-video.0.0
换 -d 2 还是不出图,/tmp/isp.err.log:
gc2093: i2c read reg 03f0 error 121(Remote I/O error) gc2093: i2c read reg 03f0 error 121(Remote I/O error) gc2093: read chip id failed on i2c-0 csi-2 mclk-0 chip_clk-1
新版 libvvcam 不再挨个试 sensor,按设备树里写的型号来:
&v4l2isp {
dev0-sensor-name = "gc2093";
dev0-sensor-mode = <0>;
dev0-sensor-i2c-bus = <0>; /* alias i2c0 = &i2c4 */
dev0-sensor-mclk-id = <0>; /* CSI2 MCLK1 / IO13 */
dev0-sensor-use-chip-clk = <1>;
};gc2093 的地址上没应答,我手上这个摄像头没印型号。V3 上另一款常见的是 OV5647,就在电脑上把两份 dtb 改了:
fdtput -t s k230-canmv-v3-lcd.dtb /soc/v4l2-isp dev0-sensor-name ov5647
推到板子的 /boot 替换,原文件备份成 .gc2093,重启:
vvcam-isp-subdev vvcam-isp-subdev.0: parse_sensor_dt: port 0 sensor=ov5647 mode=0 i2c_bus=0 csi_idx=2 mclk_id=0 use_chip_clk=1 ... ov5647: probe ok, chip id 0x5647, i2c addr 0x36
是 OV5647。这个改动只在卡上,SDK 源码里还是 gc2093,重新编会被覆盖。
--rotation 1 画面倒了 180 度,换成 3 就正了:
v4l2-drm -d 2 -w 800 -h 480 --rotation 3
poll: 59.98, display: 59.98, [0]: 21.99, poll: 60.03, display: 60.03, [0]: 20.67,
帧率只有 21 左右,官方 v0.6.4 镜像上是 28~30。屏幕一直是 60Hz,掉的是摄像头这一路。新版 isp_start.sh 加载驱动时多了个参数:
modprobe vvcam_video mcm_mask=1
它把第 0 路切成了 MCM 模式,传感器数据先写进内存,ISP 再读回来处理,比直通多一趟内存搬运。我怀疑是这里,还没上板验证。
就算不丢帧,上限也只有 30。OV5647 是颗老传感器,驱动里的模式表是 1920x1080 30 帧、2592x1944 10 帧、1280x960 45 帧、1280x720 60 帧、640x480 90 帧,默认用的第一个。

这么强的 NPU 配个这么拉的摄像头,真是掉价,不过官方V3 设备树里默认写的是 GC2093, 有可能是这个杂牌为了成本换的便宜摄像头
又查了一下,支持度高,开箱即用的是micropython,不是linux,我说咋这么麻烦
我要赚赏金
