MoreRSS

site iconelmagnifico | 云浅雪修改

程序员,架构师,无人机集群表演设计师,嵌入式工程师,maya插件开发者,多智能体研究者,独立游戏爱好者。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

elmagnifico | 云浅雪的 RSS 预览

记一次小米Su7 Ultra提车

2025-03-10 00:00:00

Foreword

Su7出的时候创始版没抢就有点后悔了,没想到后面一车难求,甚至开一年还能接近原价卖出去,属实离谱,雷总的饥饿营销+车子本身确实不错,让Su7一直维持供不应求的程度,二手车基本都是秒转。

对于我自己来说没啥开车的需求,出门都是打车,至少这三十年打车都能满足我。这次本不打算买的,但是基于Su7的盛况,Ultra也会供不应求,既然如此不如买来玩一下,拿个赛照,下下赛道,总不能浪费了我的模拟器(成天吃灰)。记录一下Su7 Ultra提车全过程,看看我一年后能亏多少。

小定

image-20250308185109126

24.10.29,提交了小定,也没打算买,反正小定只要1w,而且发售时也能退,就无所谓了,主要还是81w太贵了。

租车

25.1.18,租了一辆Su7 Pro给老爹过年玩了一个月,体验还行,本想租一个Max的,可惜没租到

大定

image-20250308185102502

25.2.27,81w降到52w,犹豫了2分钟,还是在22.02下了大定,选了现车

image-20250308185159000

不是很喜欢大鼻孔,还是低调一点,银色更好看一些

image-20250308182453864

由于是现车,选项有限,只能选了这个,最后总价541900。银色配黄色拉花非常难看,和销售及售后都联系过,无奈没有去掉拉花的服务。后续雷总直播里也说到了很多人不想要这个拉花,太难看了。

预交付

image-20250308192324581

之前销售是说大概三月五号左右车能到,不过由于要周转资金,还交不了全款,一直到三月六号支付了全款,最早一批似乎4号还是5号就已经拿到了车。

7号就在问销售能否8号提车,但是死活不给准确答复,一直到8号晚才收到短信提示车已经到了,可以提车了

image-20250308184932590

image-20250308185005324

销售基本立马就跟我确认了交付时间,9号上午10点就可以提车了。

提车之前还有一些东西可以提前处理,比如保险、物联网卡认证,小米把这一部分完全集成到了APP里,除了支付的部分还得去外面,其他的都可以一站式解决,还是很简单的,适合第一次买车的人

保险

image-20250308192937215

Ultra的保险只有两种,一个基础版(13327),一个尊享版(13918),就差在这个车上人员责任险这里

image-20250308192407705

据说深圳险比较贵,广州等其他城市便宜一些。支付完成以后立马就能拿到保单了

物联网卡

image-20250308193330540

这没啥说的,正常实名而已,到这里基本提车前的一些准备工作都做完了,剩下就是带身份证去现场提车和办理临牌了

签印合同

在小米汽车APP里面有一个独立的购车协议的页面,协议签印藏得很深,我甚至在没有签印合同的情况下就已经做完了支付和上面所有操作

image-20250308202019456

这很是疑惑,为什么可以在没合同之前做这些事情,这里的风险是谁承担的

image-20250308202042918

看起来这个签印合同更像是给公司或者贷款之类的需要实际合同才能打款准备的

提车

image-20250309235312671

3.9一大早去提车,在前面东西都做好的前提下,提车流程非常简单,也非常快。提车专员先是领你看一下车,确认交车的配置、车辆没啥外观明显的异常,然后就可以去申请指标和临牌,不需要排队,立马就能办理。全流程都只需要身份证和粤省事就行了

image-20250310000123419

牌照选择了官方的代办,只需要加268就行了,全流程都有人帮忙处理,只需要去一次车管所验车就行了,其他都有人帮忙处理

  • 官方再三叮嘱在上牌前不能动车的外观
  • 官方暂时没有去拉花的服务

image-20250309235807343

由于买的太早了,车管所还没有这个车的备案,还需要等通知,所以临牌最多可以续2次,一次一个月

  • 目前很多地库、停车场都支持了无牌进入,所以用临牌也不是大问题,就是高速还是得人工服务一下

image-20250310012617093

image-20250309235407521

办理完手续就开始正式交付车辆,提车专员会讲解车机相关功能,讲解完成,拍照合影,然后就会帮你把车开出去了,外面就可以直接开回家了

image-20250310000907575

车钥匙是2个NFC卡,同时也指导你把手机蓝牙和车绑定了,手机就可以直接解锁。

提车礼物(看其他人视频提前知道了):

  • 小米的双肩包
  • 小米的128g U盘,已经装车上了,行车记录仪使用
  • 小米的帽子
  • 小米的护照包
  • 小米的马克杯
  • Ultra专属捧花
  • 提车合影留念相片相框,这个没要
  • 一带零食和水

限制

image-20250309235720172

简单说300km以内最大速度只有140km/s,超过以后解禁,无论你是什么人都会有这个限制。

解禁

image-20250310011122062

当天就开满了300,在300公里的时候,停下来切到P档再切回D档,就会提示解锁安全模式,此时这个车就解锁了1500匹马力?并不是,需要你开启赛道模式,赛道模式需要一个学习和答题考试,大概十五分钟内就行了,我是在别人在开的时候答的题,基本都是常识,大概四五分钟就能考试通过了。默认的运动模式和Max是一样的673匹。

image-20250310001020140

赛道模式又分几种:

  • 排位赛,马力上限1548匹
  • 耐力赛,马力上限800匹
  • 漂移赛,马力上限300匹

赛道模式没有电子围栏,也就是不需要在赛道才能开启。

赛道模式下开启排位赛,加速度确实非常恐怖,提前做好心理准备,实际推背感非常猛

回头率

回头率不如当初的Su7发售,但是还是不错的,很多人都会回头看,殊不知车里三个长枪短炮拍着车外回头的人

Ultra Club

image-20250308185438136

Ultra订单界面还有一个Club的会员,看了一下,入会3999,送6w积分,大概就是6000,可以直接换购一个充电桩11kW(5999),刚好消耗完。

如果买其他东西,把商店买空都花不了6000,所以可以留一下看后续还能推出一些啥东西

Club说是只有2000会员,但是实际会员号能有1w个,按照顾问的话,后续可能会有赛道活动什么的,但是目前一问三不知,真不知道这个顾问有啥用,回复个问题慢的要死,还不如机器人,建议下次这样就别叫什么专属顾问了,还不如24小时客服秒回呢

问题

image-20250310003420061

第一个问题,在悬挂是最高的情况下,实际车比想象的要低一点的,这个大概有个三四cm?,前铲下面有一个类似挡泥板的东西,这个会额外凸出一点,在上一个小坡的时候蹭到了,边缘稍微有点卷边了,应该不是大问题

image-20250310003652525

好像这个突出来的是衬套?官方建议检查一下,有空再说吧

第二个问题,在正常行驶过程中,已经开启导航和hud,hud同时也有导航线路显示,但是如果此时再开赛道大师,开启排位或者任意模式,此时hud中的导航线路会消失,此时退出赛道大师、退出导航,重开hud,都无法在hud中显示导航线路。在无导航线路的情况下开了大概半小时以后,hud的导航线路自动恢复了。

  • 你可以在非赛道开启赛道模式,这个用户体验或者安全很难兼顾

第三个问题,今天几次智能泊车时,都出现车位刚开始提示有,但是泊车过程中刹停以后,车位消失,提示搜索但是持续一段时间都找不到。泊车时扫到玻璃门,一开始应该是没扫描到,直接往门方向走的,等到接近以后扫到了直接刹停了。

充电还遇到一点问题,今天用小鹏的专用桩,刚开始充电正常,中间吃饭时再看停止充电了,后续发现似乎是充电桩无响应了,原因未知,远程也无法重新开启充电,也无法停止充电。

  • 后续开启一键充电,还是非常方便的

特斯拉的超充,可以直接用,半小时不到从60%充到100%,对比家里的这个龟速,强太多了。

  • 官方的11分钟10-80%,没遇到这种桩不知道真假

其他问题

  • 座椅还是不支持四点式安全带
  • 按摩只有前排两座有,后排没有,按摩一段时间后会自动关闭,按摩力度比较轻
  • 座椅主动侧翼支撑,只有主驾有,其他人都没有
  • 方向盘的反毛皮有点热,不过手汗也全糊上去了hhhh
  • 声浪外放很小,还不如摩托,声浪有bug,有时候设置了但是没声音,要切换一下才有
  • su7 ultra实际续航里程应该不超过450km(长续航轮胎),对比MAX可能会有续航焦虑,大概少200km
  • 锁车不会自动关窗,之前租车就有这个问题,不知道为什么要这样设计
  • hud、左右镜子的调节似乎不会像座位一样设置独立保存,每次换人都要重新调节

一点美图

image-20250309235533978

7248f0f8ae6ba5d03b8be4a6ca55b29.jpg

Summary

暂时就遇到这些问题,后续再补充吧,目前看好像Su7 Ultra没有车友群,也没任何组织,官方似乎也有意推动这样?不是很懂

感谢我的小伙伴们一路肝爆到解禁

Clang-LLVM编译STM32

2025-03-06 00:00:00

Foreword

全网找不到几个LLVM工具链进行交叉编译的例子,如果只是Linux环境,那可能随便弄,但是Windows下要拉哪个库,环境变量什么的要怎么弄都没找到例子。东拼西凑了几个Blog的内容,总算是完整可以编译了

如果直接用Keil的AC6,那没啥难度,如果抛弃他,改用开源又要怎么弄

Clang-LLVM

GNU GCC编译流程

源代码 → GCC前端 → GIMPLE(中间表示) → RTL → 汇编代码 → GNU汇编器 → 目标代码

Clang/LLVM编译流程

源代码 → Clang前端 → LLVM IR → LLVM优化器 → LLVM后端 → 汇编代码 → 目标代码

同时也有各种魔改的类型,使用混合的,Clang的前端+GNU的后端,Clang的前端目前公认是比GNU要快很多的

环境

目前开源可用的LLVM的交叉编译工具链,只看到了这一个

https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases

官方版本的工具链目前还在建设阶段,根本没有Release可用

https://github.com/arm/arm-toolchain/tree/arm-software

下一个,解压,将bin目录加入到环境变量中

CMake、Make、Ninja(如果需要)也同样需要安装

如果是用MSYS2,可以通过下面的方式安装

安装make

pacman -S make

安装cmake

pacman -S cmake

安装ninja

pacman -S ninja

查看所有安装的包

pacman -Sg

编译

编译工程,以这个为例,他没有说明具体环境怎么弄,而LLVM最麻烦的就是环境问题了

https://github.com/wuwbobo2021/arm-llvm-stm32f103-blinky

他是make构建的整个工程,所以只需要make工具即可,目前LLVM需要人工指定libc、libm的目录或者引用

所以需要这么写

make "ARM_LIB_DIR=I:\llvm\LLVM-ET-Arm-19.1.5-Windows-x86_64\lib\clang-runtimes\arm-none-eabi\armv7m_soft_nofp"

需要指定你的LLVM的armv7m_soft_nofp路径,才能正确找到libc和libm

这样就能正常编译完成了

image-20250306152503416

分析对比

对比一下和arm-gnu-none-eabi的编译有什么区别

# output name
TARGET = arm-llvm-stm32f103-blinky

# debug build?
DEBUG = 0
# optimization
OPT = -O3 -flto

ASM_SOURCES = stm32f10x/startup_stm32f103xb.s
LD_SCRIPT = stm32f103c8tx_flash.ld

C_DIRS = arm \
         stm32f10x \
         .

C_DEFS = -DUSE_STDPERIPH_DRIVER \
         -DSTM32F10X_MD

# 主要这里指定库路径,不知道还没有没有啥办法自动寻找,而不用指定路径
# it must be set for libc and libm, like
# <LLVM Embedded Toolchain for Arm>/lib/clang-runtimes/arm-none-eabi/armv7m_soft_nofp
ARM_LIB_DIR =

# 这个路径却能自动寻找到
# leave empty if the original llvm/clang should be used
ARM_LLVM_PATH =

# 修改替换使用的一些工具
CC = $(ARM_LLVM_PATH)clang
AS = $(ARM_LLVM_PATH)clang
CP = $(ARM_LLVM_PATH)llvm-objcopy
SZ = $(ARM_LLVM_PATH)llvm-size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S

OPENOCD = openocd \
	      -f /usr/share/openocd/scripts/interface/stlink.cfg \
	      -f /usr/share/openocd/scripts/target/stm32f1x.cfg
GDB = gdb-multiarch

ifeq '$(findstring ;,$(PATH))' ';'
# Windows
RM = del /Q
else
RM = rm -f
endif

# 一些flag的写法或者表示和gnu不太一样了,需要独立区分
FLAGS = -mthumb -mcpu=cortex-m3 --target=thumbv7m-none-unknown-eabi -mfpu=none

C_INCLUDES = $(foreach d, $(C_DIRS), -I$(d)) -I$(ARM_LIB_DIR)/include
C_SOURCES = $(foreach d, $(C_DIRS), $(foreach c, $(wildcard $(d)/*.c), $(c)))
CFLAGS = $(C_DEFS) $(C_INCLUDES) $(FLAGS) -std=c99 -fdata-sections -ffunction-sections

OBJS = $(C_SOURCES:.c=.o) $(ASM_SOURCES:.s=.o)
LD_FLAGS = $(FLAGS) -Wl,--gc-sections -T$(LD_SCRIPT) -nostdlib -lc -lm -L $(ARM_LIB_DIR)/lib

ifeq ($(DEBUG), 1)
C_DEFS += -DDEBUG
CFLAGS += -g -gdwarf-2
else
CFLAGS += $(OPT)
LD_FLAGS += $(OPT)
endif

$(TARGET).bin: $(TARGET).elf
	$(BIN) $< $@

$(TARGET).elf: $(OBJS)
	$(CC) $(LD_FLAGS) $(OBJS) -o $@
	$(SZ) $@

%.o: %.c
	$(CC) -c $(CFLAGS) $< -o $@

%.o: %.s
	$(AS) -c $(FLAGS) $< -o $@

.PHONY: clean flash debug

clean:
	$(RM) $(foreach d, $(C_DIRS), $(d)/*.o) *.elf *.bin

flash: $(TARGET).bin
	$(OPENOCD) -c "program $(TARGET).bin preverify verify reset exit 0x08000000"

debug: $(TARGET).elf
	$(GDB) -iex "target extended | $(OPENOCD) -c 'gdb_port pipe'" \
	-iex 'monitor reset halt' -ex 'break main' -ex 'c' -ex '-' $<

这个的写法是与GNU最大的不同点,他的组合比较固定,没有GNU那么灵活

--target=thumbv7m-none-unknown-eabi -mfpu=none
--target=thumbv7m-unknown-none-eabihf -mfpu=fpv5-d16

如果是在CMakeLists中需要额外指定库文件位置,否则还是会提示问题

# Link directories setup
target_link_directories(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined library search paths
    "I:/llvm/LLVM-ET-Arm-19.1.5-Windows-x86_64/lib/clang-runtimes/arm-none-eabi/armv7m_soft_nofp/include"
)

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    # Add user defined libraries
    -l:libc.a    
    -l:libm.a      
)

常见问题

类似这种通不过cmake的test程序,这种都是库没定义好,缺少必要的库,导致连cmake内部的检测程序都无法正常调用编译器完成编译,进而提出错误

LLVM-ET-Arm-19.1.5-Windows-x86_64/bin/clang.exe"
is not able to compile a simple test program.

网上一般有两种解决方案,一种是直接修改对应报错的cmake的测试程序,一种是传递一个宏,让这个检测程序不运行,建议都别用。还是正常解决库路径的问题,否则后面还会有更多问题的

经典问题_exit没有定义,这个是GNU里,可以使用--specs=nosys.specs直接靠编译器帮我们实现一个替代,我们就不用写了

ld.lld: error: undefined symbol: _exit
>>> referenced by signal.c:151 ...
>>> referenced by abort.c:63 ...

但是似乎LLVM里没有这个东西,那就得手动实现一个

#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

extern "C" {
    // 系统调用实现
    void _exit(int status) { 
        while(1) {
            // 一个断点,方便调试
            __asm__ volatile("bkpt #0");
        }
    }
    
    int _close(int file) { return -1; }
    int _fstat(int file, struct stat *st) { 
        st->st_mode = S_IFCHR;
        return 0; 
    }
    int _isatty(int file) { return 1; }
    int _lseek(int file, int ptr, int dir) { return 0; }
    int _read(int file, char *ptr, int len) { return 0; }
    int _write(int file, char *ptr, int len) { return len; }
    
    // C++ 运行时支持
    void __cxa_pure_virtual() { while (1); }
    int __cxa_atexit(void (*destructor) (void *), void *arg, void *dso) { return 0; }
    void __cxa_finalize(void *f) {}
    void *__dso_handle = nullptr;
}

大致类似这样的一个文件即可,如果没有c++就去掉c++部分,其他的适配c

Summary

目前GNU和LLVM都可以正常编译完成整个工程了

对比两种工程编译时间:

LLVM Build completed: 00:04:27.449

GNU Build completed: 00:02:19.536

不知道哪里有问题,导致LLVM实际反而比GNU要慢,甚至慢了一倍

Quote

https://hustlei.github.io/2018/11/msys2-pacman.html

https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/blob/main/docs/migrating.md

https://github.com/astronmax/stm32-llvm

https://www.eet-china.com/mp/a103863.html

VSCode CMake工作流

2025-02-27 00:00:00

Foreword

之前看到CMake有这么多选项,感觉不简单,还是看下别人在这里设计了多少东西

CMake

整体来说CMake遵循这样一套结构

操作-操作预设(如果有的话)-操作目标(如果有的话)

比如

Configure-ConfigurePreset
Build-BuildPreset-Build_Target
Launch-Launch_Target
Test-TestPreset
Pack-PackPreset
Workflow-WorkflowPreset

Configure

这个不用多说,最基础的配置了

Kit

Kit是作为configure中的一个环境,也就是编译工具链的配置

image-20250226173151815

同时默认的CMake把工具链和生成配置类型,这个区分开了,实际我之前做的demo也区分开了二者

而这个Kit是不需要你写的,直接通过扫描环境就能识别,感觉这样更好,可以去掉自己写的工具链

  • 不过这样的话,脱离了这个环境就失去编译工具链的选择了

Variant

简单说Variant就是允许你对Kit和优化输出等级进行打包和重命名,这样的话不同编译工具链+优化输出等级就形成了一个新组合,这个组合就叫Variant,这样用起来其实就更灵活了

_images/custom_variant_selector.png

这只有2种元素进行组合,如果元素多了,这个组合就非常恐怖了,但是它可以自动帮你排列组合好

对于那种超级兼容性的大工程,做自动化流程的时候就比较方便

Build

Build就不说了,正常配置,Build内可以选择Target,这样的话其实很多东西都能写到配置文件里去,通过Target选择不同的编译结果

Launch

Launch包含调试和运行,也可以选择调试的目标

Debug都是调用外部程序完成的,本身没这个能力,launch可以做一些调试前或者后的操作

CTest

特性 / 框架 CTest JUnit PyTest Google Test
语言支持 C/C++ Java Python C++
集成度 与CMake紧密集成,适合CMake项目 与Java开发环境集成 Python生态中集成度高 良好的C++环境集成
测试类型支持 支持单元测试、集成测试、性能测试 主要支持单元测试 支持多种类型的测试 主要支持单元测试
可扩展性 通过CMake脚本可扩展 可以通过插件扩展 强大的插件系统 可以通过宏和参数化扩展
测试发现 自动化测试发现 一般 强大的自动化测试发现 自动化测试发现
结果报告 详细的测试结果报告 结果报告清晰 结果报告详细且支持插件扩展 结果报告详细
社区和文档 较小的社区,文档适中 大社区,丰富的文档 大社区,丰富的文档 大社区,丰富的文档
适用场景 适用于需要CMake构建系统的C/C++项目 适用于Java项目 适用于Python项目 适用于需要详细测试报告的C++项目
add_executable(TestInstantiator TestInstantiator.cxx)
target_link_libraries(TestInstantiator vtkCommon)
add_test(NAME TestInstantiator
         COMMAND TestInstantiator)

简单说就是可以直接执行测试指令,类似VS里面的单元测试,只不过这个测试是调用外部的程序完成的

实际上直接做代码覆盖测试也可以,需要引入gtest等额外的模块,同样的对于测试目标,也可以debug

image-20250226210212091

简单的编译完成以后,运行一下自己,就看到了测试结果

CTest只是一个调用方,不是具体的测试框架,测试框架可能得需要gtest之类的东西来做

CPack

CPack有两个作用,第一个自动添加缺少的包

cmake_policy(SET CMP0135 NEW)
find_package(GTest)
if(NOT GTest_FOUND)
message("GTest not found, download it...")
include(FetchContent)
FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/heads/main.zip)
FetchContent_MakeAvailable(googletest)
endif()

比如这里自动下载gtest的包

第二个就是自动打包,当编译测试都做完了,那就还剩下打包发布了,CPack就是用来将生成结果进行打包的,支持很多类型的打包

cmake_minimum_required(VERSION 3.0) 
project(MyProject) 
# 添加源代码
add_executable(MyProject main.cpp)
# 添加依赖库 
find_package(Boost REQUIRED)
target_link_libraries(MyProject PRIVATE Boost::boost)
# 安装文件
install(TARGETS MyProject DESTINATION bin)
# 打包 
set(CPACK_PACKAGE_NAME "MyProject")
set(CPACK_PACKAGE_VERSION "1.0.0") 
set(CPACK_GENERATOR "ZIP") 
include(CPack) 

运行之后就会自动打包为zip

Workflow

简单说workflow就可以把前面所有的东西组合在一起,他们可以一一对应起来变成一个个工作流

配置-构建-测试-打包

下面就是工作流的预设,这样的工作流可以简单的配置很多个

    "workflowPresets": [
        {
            "name": "wf",
            "description": "",
            "displayName": "工作流",
            "steps": [
                {
                    "type": "configure",
                    "name": "gcc13"
                },
                {
                    "type": "build",
                    "name": "all"
                },
                {
                    "type": "test",
                    "name": "test_Project"
                },
                {
                    "type": "package",
                    "name": "all"
                }
            ]
        }
    ]

点一下就可以把上面的步骤来一遍,直接实现CICD流程

image-20250227000640588

Summary

VSCode中CMake Tools相关的教程还是太少了,很多东西都对不上,而VSCode把各个之间的一些参数或者设置隐藏了,导致实际如果出问题想要调试找到具体位置有些困难,不如原生的CMake,是啥就是啥都能看到

Quote

https://blog.csdn.net/witton/article/details/130170686

https://blog.csdn.net/witton/article/details/130216777

https://blog.csdn.net/witton/article/details/145307607?spm=1001.2014.3001.5502

https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/cmake-presets.md#test

https://cmake.org/cmake/help/latest/manual/ctest.1.html#ctest-1

https://blog.csdn.net/m0_49302377/article/details/130264041

VSCode CMake Debug

2025-02-27 00:00:00

Foreword

VSCode CMake Debug 嵌入式MCU有好几种方案

Cortex-Debug

image-20250227161012980

OpenOCD

https://elmagnifico.tech/2023/07/22/Vllink-DapLink-Debug/

OpenOCD,需要专门的调试工具,ST-Link或者这里的VLink,支持CMSIS-DAP的

增加而一个build做为前置任务,然后启动openodcd

tasks.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "make",
            "args": [
            ],
            "group": "build"
        },
        {
            "label": "download",
            "type": "shell",
            "command": "openocd",
            "args": [
                "-f",
                "cmsis-dap.cfg",
                "-f",
                "stm32h7x.cfg",
                "-c",
                "program build/stm32h7_demo.elf verify reset exit"
            ],
            "group": "build"
        }
    ]
}

launch.json 主要给openocd输入调试配置

{
    "version": "0.2.0",
    "configurations": [
        {
            "cwd": "${workspaceRoot}",
            "executable": "./build/stm32h7_demo.elf",
            "name": "Debug Microcontroller",
            "request": "launch",
            "type": "cortex-debug",
            "showDevDebugOutput": false,
            "servertype": "openocd",
            "configFiles": [
                "cmsis-dap.cfg",
                "stm32h7x.cfg"
            ]
        }
    ]
}

JLink

顾名思义JLink需要配合JLink来使用

image-20250227114640546

需要注意JLink的对应路径也需要在环境变量里,否则可能调用不到

JLinkGDBServerCL.exe -singlerun -nogui -if swd -port 50000 -swoport 50001 -telnetport 50002 -device STM32H743VI

在VSCode里新建一个launch,主要是svd和elf路径,device填正确就行了

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug-jlink",
            "cwd": "${workspaceRoot}/",
            "executable": "${workspaceFolder}/build/bin/demo.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "jlink",
            "device": "STM32H743VI",
            "interface": "swd",
            "runToEntryPoint": "main",
            "showDevDebugTimestamps": true,
            "svdFile": "${workspaceRoot}/config/STM32H743.svd",
            // "preLaunchTask": "build",
            // "postDebugTask": "run"
        }
    ]
}

通过VSCode这种方式调用,启动非常慢,和SES对比就拉跨

Embedded Tools

之前用过的一个插件

https://elmagnifico.tech/2022/03/15/Embedded-Software-Development-VS-VSC/

img

本质上还是调用gdb然后配合openocd来实现调试的

        {
            "name": "Launch",
            "type": "cppdbg",
            "request": "launch",
            "cwd": "${workspaceFolder}",
            "program": "${workspaceFolder}/build/app/mxchip_azure_iot.elf",
            "MIMode": "gdb",
            "miDebuggerPath": "arm-none-eabi-gdb",
            "miDebuggerServerAddress": "localhost:3333",
            "debugServerPath": "openocd",
            "debugServerArgs": "-f board/stm32f4discovery.cfg",
            "serverStarted": "Listening on port .* for gdb connections",
            "filterStderr": true,
            "stopAtConnect": true,
            "hardwareBreakpoints": {
                "require": true,
                "limit": 6
            },
            "preLaunchTask": "Flash",
            "svdPath": "${workspaceFolder}/STM32F412.svd"
        }

Ozone

跳过VSCode,直接使用专业的Ozone来调试

https://elmagnifico.tech/2023/12/07/SES-Ozone-FreeRTOS/

配合我自己写的调用程序,很小30k,自动找到Ozone的路径,然后调用对应的调试文件,如果没有的话会自动新建一个

同样的烧写我也用了自己的程序去调用jlink完成烧写,比调用第三方插件可控性高多了

新建一个task,对应还有输入选择模板,否则一个配置要写一个task,太繁杂了

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Debug",
            "type": "shell",
            "command": "${workspaceFolder}/Ozone/flash.exe",
            "args": [
                "--o",
                "--path",
                "${input:debugPath}"
            ],
            "group": {
                "kind": "build",
                "isDefault": false
            },
            "problemMatcher": [],
            "options": {
                "env": {
                    "DEBUG_PATH": "${input:debugPath}"
                }
            }
        }
    ],
    "inputs": [
        {
            "id": "debugPath",
            "type": "pickString",
            "description": "Select the debug configuration",
            "options": [
                "${workspaceFolder}/Ozone/test.jdebug",
                "${workspaceFolder}/Ozone/test2.jdebug",
                "${workspaceFolder}/Ozone/test3.jdebug",
                "${workspaceFolder}/Ozone/test4.jdebug",
            ],
            "default": "${workspaceFolder}/Ozone/test.jdebug"
        }      
    ]
}

Summary

总体来说VSCode的调试不是很好用,还是原生的更好一些。

总感觉VSCode这里给的接口实在是太多了,选择太多了,但是每个选择其实做的都一般,没做到极致的水平

这些插件更像是一个整合工具,然后再接入另外一个整合工具,层层套娃,最后糊了一个勉强能用的东西

Quote

https://blog.csdn.net/pyt1234567890/article/details/122522700

https://blog.csdn.net/xiaoyuanwuhui/article/details/128085237

https://github.com/microsoft/vscode-cmake-tools/blob/main/docs/debug-launch.md

Kconfig与CMake初步模块化工程3

2025-02-25 00:00:00

Foreword

本篇解决一下CMake和VScode怎么整合到一起

VSCode

适配VSCode

image-20250224184343397

VSCode只需要一个CMake Tools,不需要安装其他CMake插件

image-20250224191713665

Kconfig格式化和代码高亮,还是用nRF的好一些,另外那个Kconfig会识别出错

image-20250225193642891

C/C++提示全家桶也需要安装,会影响到代码提示和跳转体验

缺少 Select a Kit

vscode cmake 缺少选择Select a Kit,主要原因是目录已经有了CMakePresets.json,有预设的情况下不会给你选kit,这个问题找了半天,发现官方文档就写了

image-20250224174751611

但是你搜索的命令又有,只能说官方弄的有点乱,而且本身使用预设和可以选kit我觉得也不矛盾

CMake:Scan for compiles

去掉预设文件以后,这个kit果然就有了,就可以正常选择了

image-20250224174840500

实际上这一步根本不需要,建议不要浪费实际调整这个,预设文件早就把这些设置好了,唯一需要处理的就是搜索本地的编译器,否则VSCode不知道编译的gcc在哪里

CMake:Scan for compiles

修改status bar

默认的 status bar 实在是冗余太多了,从config,build,pack,ctest,cpack,workflow,很多用不上的我就给他隐藏了,而CMake Tools也支持自定义

{
    "cmake.options.statusBarVisibility": "visible",
    "cmake.options.advanced": { 

        "folder": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden",  
            "statusBarLength": 20,
            "projectStatusVisibility": "hidden", 
        }, 
        "configure": { 
            "projectStatusVisibility": "visible", 
        }, 
        "configurePreset": { 
            "statusBarVisibility": "visible",
            "inheritDefault": "visible", 
            "statusBarLength": 20
        }, 
        "kit": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "statusBarLength": 20 
        }, 
        "variant": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
        }, 
        "build": { 
            "statusBarVisibility": "visible", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "visible", 
        }, 
        "buildPreset": { 
            "statusBarVisibility": "hidden",
            "inheritDefault": "hidden", 
            "statusBarLength": 20 
        }, 
        "buildTarget": { 
            "statusBarVisibility": "hidden",
            "inheritDefault": "hidden", 
            "statusBarLength": 20 
        }, 
        "ctest": { 
            "statusBarVisibility": "hidden",
            "inheritDefault": "hidden", 
            "statusBarLength": 20, 
            "color": true,
            "projectStatusVisibility": "hidden", 
        }, 
        "cpack": { 
            "statusBarVisibility": "hidden",
            "inheritDefault": "hidden", 
            "statusBarLength": 20, 
            "color": true,
            "projectStatusVisibility": "hidden", 
        }, 
        "testPreset": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "statusBarLength": 20
        },
        "launchTarget": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "statusBarLength": 20 
        }, 
        "debug": { 
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "hidden", 
        },
        "launch": {
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "hidden", 
        },
        "workflow": {
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "hidden", 
        },
        "workflowPreset": {
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "hidden", 
        },
        "packagePreset": {
            "statusBarVisibility": "hidden", 
            "inheritDefault": "hidden", 
            "projectStatusVisibility": "hidden", 
        }
}
}

将上面代码加入到setting.json就可以隐藏大部分没用的细节了

宏未识别

修改配置文件

常用方法修改c_cpp_properties.json

image-20250224193655583

在其中手动加入对应的宏即可

image-20250224195104640

如果我们有多个配置,那就可以有多个配置不同的宏,然后通过右下角去切换C/C++的配置项,从而切换整体的宏定义

image-20250224201248076

  • 这里如果可以把CMake的defines输出给到c++那样就比较完美了

切换C++的配置和编译的configure不是一个配置,而且他们也不能联动,这就导致你改一下必须得手动切换配置才行

Kconfig的宏

对于一般的宏(区分feature、驱动等等,用来选择文件的宏),这样还是有点傻了,其实我们生成了”autoconf.h”,只需要把他引入项目就可以自动识别宏了

可能有些宏是用户自定义的,类似这样的

    # macro defines
    set(USER_MACRO_DEFINES
        "HSE_VALUE=8000000"
        "USE_HAL_DRIVER"
        "USE_FULL_LL_DRIVER"
        "USE_USB_OTG_FS"
        "STM32F767xx"
    )

这种方式稍微有点傻了,其实只要把他们全部加入到Kconfig里,利用Kconfig去生成这个宏即可,虽然Kconfig的宏是包含了CONFIG_这个头的,但是我们可以在CMake里通过一些手段去掉CONFIG_

还有一种办法是不去掉CONFIG,但是通过引入一个万能的头文件,里面预埋一个宏和其对应即可

#ifdef CONFIG_USE_HAL_DRIVER
	#define USE_HAL_DRIVER
#endif

#ifdef HSE_VALUE8000000
	#define HSE_VALUE 8000000
#endif

EXPORT_COMPILE_COMMANDS

经过一番研究,发现其实CMAKE有一个生成compile_commands.json的操作,这个会生成每个文件的编译命令,如果C++补全或者是高亮读取了这个文件,那么他就知道具体这个文件编译的时候使用了什么宏,那么对应宏是亮起还是灰掉,他就能智能识别

下面的命令就是在cmake编译的时候传递一个参数,让他自己生成这个json

"cmake.configureOnOpen": true,
"cmake.configureArgs": [
    "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
],
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"

实际上这个命令在我们CMakeLists的前几句就写了

image-20250225002207493

但是目前看效果不明显,似乎不会生效,感觉他需要额外再配置一下这个路径才能正确识别(前面的配置文件和自动识别会冲突)

image-20250225002812759

经过测试发现他自动识别了在CMakeLists中增加宏,但是Kconfig的.config里的宏并没有识别,还是需要将头文件引入,才能正常识别

如果把同样的宏放到被排除编译的文件中,会发现所有宏都处于不被激活的状态

image-20250225003002026

反查compile_commands.json也可以知道,这个文件直接就不存在,自然不会有任何反应

继续优化,可以通过C_CPP配置手动将某些文件夹或者文件给排除出去(这里理论上靠compile_commands.json应该也能实现啊)

{
    "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
    "C_Cpp.exclusionPolicy": "checkFilesAndFolders",
    "C_Cpp.files.exclude": {
        "**/node_modules": true,
        "**/build": true,
        "**/*.min.js": true
    },
    "cmake.configureOnOpen": true
}

文件排除

项目里一些不需要显示的内容可以通过配置进行排除,让整个目录看起来更清爽

    "files.exclude": {
        "**/Battery": true,
        "**/*.jlink": true,
        "**/*.emProject": true,
        "**/*.emSession": true,
        "**/*.bin": true,
        "**/*.bat": true,
        "**/Boot": true
    },

常见问题

第一次编译可能过不去,这是config没有正确生成

VSCode第一次可能找不到编译器,需要使用Scan搜索一下

第二次编译可能还是过不去,需要clean一下工程,然后重新再编译

Debug

有时候需要调试一下,为什么某些文件被加入了,可以通过下面的代码输出当前加入的源文件,进而确定问题

# 输出当前目标的所有源文件
get_target_property(TARGET_SOURCES ${CMAKE_PROJECT_NAME} SOURCES)
message(STATUS "Current target sources:")
foreach(source ${TARGET_SOURCES})
    message(STATUS "  ${source}")
endforeach()

不整合

如果不整合到VSCode中,也有一些办法可以用来简化工作量

#!/bin/bash

# 提示用户选择预设
echo "请选择一个预设:"
options=("Debug" "Release" "Test")

# 设置自定义提示符
PS3="请输入选项编号: "

select opt in "${options[@]}"
do
    case $opt in
        "Debug"|"Release"|"Test")
            PRESET=$opt
            break
            ;;
        *) echo "无效选项 $REPLY";;
    esac
done

echo "您选择了 $PRESET, 开始构建项目...请先确认config变换"

CONFIG_FILE="./$PRESET.config"
if [ ! -f "$CONFIG_FILE" ]; then
    echo "配置文件 $CONFIG_FILE 不存在,创建一个空白文件..."
    touch "$CONFIG_FILE"
fi

# change project properties
export KCONFIG_CONFIG=./$PRESET.config
python -m guiconfig
# generate .config file
echo "生成config文件..."
python tools/kconfig.py Kconfig $PRESET.config autoconf.h kconfigLog.txt $PRESET.config
# create a build directory and make project
echo "创建预设目录..."
cmake --preset $PRESET
# build project
echo "开始build..."
cmake --build --preset $PRESET

相当于是一键build,只需要选好build的配置和确认Kconfig配置就可以自动进行下一步了

Summary

VSCode作为一个独立编辑器,距离IDE还是有点差距,整体性就是差了那么一点。想要做到完美,那就得自己开发插件,把好几个插件结合到一起,把他们之间的数据传递给弥补上

Quote

https://blog.csdn.net/weixin_39306574/article/details/103763144

https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html#id1

https://github.com/microsoft/vscode-cmake-tools/blob/fad04c8b3fef0ba80b61df1856bc747770042dc9/docs/cmake-settings.md

Kconfig与CMake初步模块化工程2

2025-02-23 00:00:00

Foreword

继续上篇,解决一些未完成的问题

遗留问题

模块化

Linux内有一些模块化的操作,但是如果直接模仿,会发现无效。

比如类型中的tristate是三状态,比如y、m、n,表示启用模块、动态加载 、禁用,三种情况,但是如果直接像下面这么写是无法启用的,你会看到怎么设都是启动和禁止,没有动态加载的选项

menu "New Module Configuration"
    config NewModule
        tristate "NewModule"

    choice
        prompt "Feature selection"
        tristate

        config Feature1
            tristate "Feature 1"
            help
                "This is the first feature"
        config Feature2
            tristate "Feature 2"
            help
                "This is the second feature"
    endchoice
endmenu

只有开启modules 才能在三态中增加显示m状态

menu "New Module Configuration"
    config MODULES
        prompt "Enable Modules"
        def_bool y
        option modules

    config NewModule
        tristate "NewModule"

    choice
        prompt "Feature selection"
        tristate "Choose a feature"

        config Feature1
            tristate "Feature 1"
            help
                "This is the first feature"
        config Feature2
            tristate "Feature 2"
            help
                "This is the second feature"
    endchoice
endmenu

image-20250223144501643

开启以后就有了M选项,否则只有X和空

不过这种模块化,在MCU这里用不上,建议不要增加复杂度

自动加载Kconfig

现在如果新加了一个module,希望可以不改动架构的Kconfig的情况下,自动完成新文件夹内Kconfig的识别和加入

menu "Module Configuration"
  config Motors
      bool "Motors"

  config Flash
      bool "Flash"
source "module/*/Kconfig"
endmenu

通过通配符的方式可以自动索引第一级子目录的kconfig

据说这种方式可以索引所有子目录,但是实际我这里不行

source "module/**/Kconfig"

至于第二级、第三级就必须要在其上一级这么写了才能索引到,source只能索引从主kconfig出发的路径,就必须要写全所有路径,这就有点不合理

source "module/NewModule/*/Kconfig"
source "module/NewModule2/*/Kconfig"

第三级就要写更多了

source "module/NewModule/SubModule/*/Kconfig"

但是kconfiglib有拓展,它支持rsource,顾名思义,他就是相对路径,从当前kconfig路径开始算,上级路径会自动帮你引用

menu "Module Configuration"
  config Motors
      bool "Motors"

  config Flash
      bool "Flash"
rsource "*/Kconfig"
endmenu

这样就有效解决了,未知新模块需要写所有路径的问题,只需要写好引用下层路径即可

自动加载CMake

上面解决了Kconfig,同时还有一个CMake也得自动引用,二者才能搭配完成目的

一般作为独立模块,有可能其内部是一个xxx.cmake,也有可能是一个CMakeLists,先说两者差别。

如果是CMakeLists,那么这个模块是可以独立编译的,完全不依赖当前工程他就能自己编译完成。

如果是一个xxx.cmake,那这个模块是非独立的,他需要依赖当前工程中的一些东西,需要和工程整体一起编译

先说xxx.cmake,还是利用相对路径取下一级的所有cmake,然后被引用

# sources
if(CONFIG_FLASH)
    message(STATUS "Flash module is enabled")
    file(GLOB_RECURSE DRIVER_SOURCES
        # 加入文件夹
        ${CMAKE_CURRENT_LIST_DIR}/Flash/*.*
    )
    target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${DRIVER_SOURCES})

    # include
    target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE 
        ${CMAKE_CURRENT_LIST_DIR}/Flash
    )
endif()

# 引入当前路径下的所有CMake文件
message(STATUS "CMAKE_CURRENT_LIST_DIR: ${CMAKE_CURRENT_LIST_DIR}")
file(GLOB CMAKE_FILES "${CMAKE_CURRENT_LIST_DIR}/*/*.cmake")
foreach(CMAKE_FILE ${CMAKE_FILES})
    include(${CMAKE_FILE})
endforeach()

对应的模块内的NewModule.cmake,就可以写成这样,将自己内部的文件加入,如果还需要套娃,那么继续使用上面的方式即可

message(STATUS "NewModule Add")
# sources
if(CONFIG_NewModule)
    file(GLOB_RECURSE DRIVER_SOURCES
        # 加入文件夹
        ${CMAKE_CURRENT_LIST_DIR}/*
    )
    target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${DRIVER_SOURCES})

    # include
    target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE 
        ${CMAKE_CURRENT_LIST_DIR}
    )
endif()

demo对应工程里的NewModule和SubModule

如果是CMakeLists,那么要用下面的方式加入CMakeLists

# 引入当前路径下的所有子目录中的CMakeLists.txt文件
file(GLOB SUBDIRS RELATIVE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*)
foreach(SUBDIR ${SUBDIRS})
    if(IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${SUBDIR})
        if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/${SUBDIR}/CMakeLists.txt)
            add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/${SUBDIR})
        endif()
    endif()
endforeach()

demo对应工程里的NewModule2和SubModule2

Summary

以上解决方案或者示例都加入到了演示的仓库

Quote

https://github.com/LuckkMaker/apm32-kconfig-example

https://docs.zephyrproject.org/latest/build/kconfig/extensions.html