RSS

RSS Follow体验

TTRss,RSS,RSSHub,订阅,激活码

Views:  times Updated on January 21, 2025 Posted by elmagnifico on January 21, 2025

Foreword

RSS难得新增一员,体验一下Follow,看看和TTRss有啥不一样的,有啥改进的点

Follow

https://follow.is/

image-20250121163433442

Follow支持opml导入,所以TTRSS的内容可以直接导进来,对应也有配套的APP,不用自己去找支持TTRSS的订阅APP,生态整体比较完整

Follow整个界面也更加现代一些,TTRSS则是更简洁,更快,动效上明显多了很多内容

相比TTRSS可能需要设置各种插件来爬取文章内容,Follow似乎把这一块都简化了,更容易上手一些

image-20250121163729432

打开某一个blog的文章,是可以看到有多少人也一起订阅了这个网站的内容

image-20250121164005886

甚至可以看到我自己的的网站被多少人订阅了,我可以看到具体谁阅读过我的文章

认证

如果这是你的订阅源,需要本人认证,可以通过下面的三种方式

image-20250121164539778

大致看了下,内容和描述都要插入一些奇怪的东西,不喜欢,还是插入RSS标签好一点

对应我Blog中的feed.xml增加标签即可

---
layout: null
---
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Elmagnifico's Blog</title>
    <description>后端与嵌入式、maya与游戏 | elmagnifico, Back-end engineer &amp; Embedded System Lover | 这里是elmagnifico的个人博客,君子之交淡如水。</description>
    <link>https://elmagnifico.tech/</link>
    <atom:link href="https://elmagnifico.tech/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sun, 23 Feb 2025 01:14:03 +0800</pubDate>
    <lastBuildDate>Sun, 23 Feb 2025 01:14:03 +0800</lastBuildDate>
    <generator>Jekyll v4.3.2</generator>
    <follow_challenge>
        <feedId>55855418052542483</feedId>
        <userId>101522814280570880</userId>
    </follow_challenge>    
    
      <item>
        <title>Kconfig与CMake初步模块化工程</title>
        <description>## Foreword

使用CMkae+Kconfig最小化的创建一个可以模块化的工程,可以适用于大部分MCU类型的工程,并且有一定程度的扩展性。



## 需求环境

环境需要的东西比较多,要安装4个独立程序才行,对比IDE一键安装,是复杂了一些



#### Arm GNU Toolchain

&gt; https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads

Arm交叉编译的环境,这个是编译的必需品,选择10.3的经典版本,实际上选择最新版(13.3和14.2)也没问题

Version 10.3-2021.10

&gt; https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads



14.2.Rel1 版本

&gt; https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-mingw-w64-x86_64-arm-none-eabi.exe

安装时勾选加入环境变量,省的手动添加



#### MinGW64

&gt; https://github.com/niXman/mingw-builds-binaries/releases

还需要mingw64的make工具链,需要手动加入环境变量,最好顺手把`mingw32-make.exe`改成`make.exe`,不然用起来很麻烦



#### CMake

&gt; https://cmake.org/download/

安装时勾选加入环境变量,省的手动添加



#### Kconfig-Python

还需要python环境和对应的包,某些情况可能会用到离线包,这里也一起说明

![image-20250222185916218](https://img.elmagnifico.tech/static/upload/elmagnifico/202502221859309.png)

注意python安装时的tcl/tk 这个需要安装,否则会缺少tkinter,打不开kconfig gui

核心就是这两个包得安装

pip install kconfiglib pip install windows-curses




如果使用whl的离线安装包,就可以这样安装

pip install windows_curses-2.4.1-cp313-cp313-win_amd64.whl pip install kconfiglib-2.2.2-py2.py3-none-any.whl




## 架构





#### 结构

核心就几个文件夹,把各个模块解耦,组合到一起就行了

- application,系统的入口,启动文件,调用各个module完成业务
- driver,驱动层,驱动各种具体的设备
- module,任务层,实现各种业务细节
- platform,硬件层,给driver层提供硬件接口,实际使用的MCU的HAL或者标准驱动层
- rtos,使用的操作系统
- tools,完成Kconfig转换的代码
- cmake,编译器或者跨平台的相关CMake代码

这里重点是如何利用CMake和Kconfig进行组合,不是架构内的细节代码要怎么写



#### 使用方法

配置工程

python -m guiconfig


生成头文件

python tools/kconfig.py Kconfig .config autoconf.h kconfigLog.txt .config


预设

cmake -S . -B build/Debug –preset Debug


make

cmake –build build/Debug –target project-debug-make


编译

cmake –build build/Debug –target project-debug-build


清空

cmake –build build/Debug –target project-debug-clean




## 解析

#### 实现细节

如果是Makefile+Kconfig,平常也是把Kconfig的宏引入到Makefile中,同理CMake+Kconfig也需要,否则只能通过宏去括起来整个文件,那种方式就非常麻烦

ifdef CONFIG_XXX … endif


- 有很多代码都需要挨个加这个宏,那可太费劲了



最关键的点就是这里,将Kconfig生成的宏利用CMake读取进来,并且转换成CMake的宏

```cmake
# Add sources and includes
file(READ &quot;autoconf.h&quot; config_content)
#message(STATUS &quot;Found config macro: ${config_content}&quot;)
string(REGEX MATCHALL &quot;CONFIG_[A-Z0-9_]+&quot; config_macros ${config_content})

foreach(macro ${config_macros})
    #message(STATUS &quot;Found config macro: ${macro}&quot;)
    set(${macro} ${macro})
    #target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE ${macro})
endforeach()

还有一种方式就是通过python独去Kconfig,然后转成一个CMake文件,再被主CMakeLists引入,不过这样就需要依赖python,我不太喜欢,实现代码如下

import os


def parse_config_file_cmake(config_file):
    config_vars = {}
    with open(config_file, 'r') as f:
        for line in f:
            # Skip comments and empty lines
            if line.startswith('#') or line.strip() == '':
                continue
            # Parse configuration line
            key, value = line.strip().split('=', 1)
            # Remove 'CONFIG_' prefix from the key name
            key = key[7:] if key.startswith('CONFIG_') else key
            # Handle quotes in the value
            if value.startswith('&quot;') and value.endswith('&quot;'):
                value = value[1:-1]
            # Add to dictionary
            config_vars[key] = value
    return config_vars


def write_cmake_file(config_vars, cmake_file):
    with open(cmake_file, 'w') as f:
        f.write('# Automatically generated by config_to_cmake.py\n')
        for key, value in config_vars.items():
            f.write('set({} &quot;{}&quot;)\n'.format(key, value))


def convert_config_to_cmake(config_file, cmake_file):
    config_vars = parse_config_file_cmake(config_file)
    write_cmake_file(config_vars, cmake_file)


def parse_config_file_header(config_file):
    config_vars = []
    with open(config_file, 'r') as f:
        for line in f:
            # Skip comments and empty lines
            if line.startswith('#') or line.strip() == '':
                continue
            # Parse configuration line
            key, value = line.strip().split('=', 1)
            # If the value is 'y', replace it with a space
            value = value.strip()
            if value == 'y':
                value = '1'
            # Remove 'CONFIG_' prefix from the key name
            key = key[7:] if key.startswith('CONFIG_') else key
            # Add to list
            config_vars.append((key, value))
    return config_vars


def write_config_header(config_vars, header_file):
    with open(header_file, 'w') as f:
        f.write('// Automatically generated by config_to_header.py\n')
        f.write('#ifndef __KCONFIG_H__\n')
        f.write('#define __KCONFIG_H__\n\n')

        for key, value in config_vars:
            f.write('#define {} {}\n'.format(key, value))

        f.write('\n#endif // __KCONFIG_H__')


def convert_config_to_header(config_file, header_file):
    config_vars = parse_config_file_header(config_file)
    write_config_header(config_vars, header_file)


config_file = '.config'       # Path to your .config file
cmake_file = 'kconfig.cmake'  # Path to the generated kconfig.cmake file
header_file = 'kconfig.h'     # Path to the generated kconfig.h file

# Convert .config file to kconfig.cmake file
convert_config_to_cmake(config_file, cmake_file)

# Convert .config file to kconfig.h file
convert_config_to_header(config_file, header_file)

每个需要做选择的地方都放置一个kconfig,用来生成宏,在对应上级的位置引入这个kconfig

menu &quot;Driver Configuration&quot;
  config LED
      bool &quot;LED&quot;

  config Motor
      bool &quot;Motor&quot;
endmenu

有了宏以后就可以通过宏来控制文件编译

# sources

if(CONFIG_LED)
    target_sources(${CMAKE_PROJECT_NAME} PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/driver/led.c
    )
    # include
    target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE 
        ${CMAKE_CURRENT_SOURCE_DIR}/driver
    )
endif()

if(CONFIG_MOTOR)
    file(GLOB_RECURSE DRIVER_SOURCES
        # 加入文件夹
        ${CMAKE_CURRENT_SOURCE_DIR}/driver/motor/*.*
    )
    target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${DRIVER_SOURCES})

    # include
    target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE 
        ${CMAKE_CURRENT_SOURCE_DIR}/driver/motor
    )
endif()

小技巧

利用 dir /b指令可以快速获取当前文件夹内各文件的名称,从而给AI去帮我们写模板化的代码(AI无法获取文件结构)

image-20250222175500661

image-20250223003214247

不仅仅kconfig可以这么处理,实际上cmake也可以这么处理,快速将已有的文件加入到某个编译选型内

  • 注意检查,copilot偶尔会写错,比如之前把某两个字母顺序写倒了,编译半天总是报错,才发现路径不对

其他

记录一些CMake的操作

CMake排除文件,使用正则的方式进行排除

file(GLOB_RECURSE USER_SOURCES
    &quot;libs/*.*&quot;
)

将含有math/vector路径、desperate的文件排除
list(FILTER USER_SOURCES EXCLUDE REGEX &quot;desperated&quot;)
list(FILTER USER_SOURCES EXCLUDE REGEX &quot;math/vector&quot;)

还有一种使用删除进行删除

file(GLOB_RECURSE FILE *.cpp *.c *.h)
 
file(GLOB_RECURSE WEBRTC_FILE webrtc_test.*  ${CMAKE_CURRENT_SOURCE_DIR}/src/webrtc_*)
 
file(GLOB_RECURSE WEBRTC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/webrtc/*)
list(REMOVE_ITEM FILE ${WEBRTC_FILE})
list(REMOVE_ITEM FILE ${WEBRTC_DIR})

CMake有两种方式添加库,或者说叫添加.a文件

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

一种是需要你把原本的库命名为lib开头的,一种是通过-l指定非lab开头的库

.cmake和CMakeLists的区别

  • .cmake,是通过include()方式进行引入的,本质上是嵌入主cmake文件中的,所以是按照顺序执行的
  • CMakeLists,是通过add_subdirectory()方式引入,不嵌入主cmake中,执行顺序是比主cmake优先的

.cmake由于是嵌入,所以也就不存在什么局部变量全局变量一说,都是在主cmake中的,所以通用,但是CMakeLists不是,他作为子部分,默认情况下他的变量只能他自己使用,只有cmake自己的全局变量是可以被使用的,用户自定义的是无法使用的。

但是也有办法使用主cmake的变量

set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/xxx/link.ld CACHE STRING &quot;Linker Script Path&quot; FORCE)

首先是将变量变成CACHE,这样这个变量就会被全局缓存,但是这种方式只是会保存这个变量,也就是子cmake可以用主cmake的这个变量的值,但是子cmake不能反过来设置这个值

如果要设置这个值,必须使用后面的FORCE关键字来修改

总而言之子CMakeLists的作用域类似于编程语言中的函数,写在其中的内容的作用域也类似于一个函数内的各种变量的作用域

Summary

还没有完全优化完成,比如同时编译四五个配置,怎么把这四五个配置暂存下来,方便后续编译,而不是每次都要切换配置去编译,还有CMake和Kconfig能不能只写其中一个,另外一个自动生成?

目前对于模块的引入能不能自动识别加入,而不用手动写引入的部分。

CMake和Kconfig这种模式还有一个小问题,生成的宏是直接文件级别跳过编译的,但是如果要同时兼容IDE或者VSCode这类的时候,能不能将这些关联导入到这些IDE里,否则IDE里总是显示不正常的,快速跳转也会跳到错误的位置上

Quote

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

</description> Sat, 22 Feb 2025 00:00:00 +0800 https://elmagnifico.tech/2025/02/22/Kconfig-CMake/</link> https://elmagnifico.tech/2025/02/22/Kconfig-CMake/

    <category>Kconfig</category>
    
    <category>CMake</category>
    
    
  </item>

  <item>
    <title>VSCode Kconfig插件</title>
    <description>## Foreword

还是决定自己写一个Kconfig通用插件,这里记录一下相关的内容

KconfigLib

  • Kconfig :是一款Linux可视化配置文件格式。
  • Kconfiglib:是一款基于Kconfig格式实现的Linux可视化配置工具。

还是先玩明白目前的python系是怎么显示和处理Kconfig的

查了一下guiconfig.py 似乎可以以更好的形式来显示Kconfig配置内容

安装

pip install kconfiglib
pip install windows-curses

测试

正常情况下可以通过这种方式调用menuconfig,从而显示kconfig内容

python D:\\Python\\Python312\\Lib\\site-packages\\menuconfig.py

移除绝对地址以后,可以使用下面这种方式调用

python -m menuconfig

image-20250113171459928

如果使用guiconfig,可以看到界面是类似QT的形式了,可以直接选择而不是嵌入在命令行中了

python -m guiconfig

image-20250113171851173

突然发现这种方式似乎就够目前使用了,VSCode的版本甚至可以往后放一下

生成配置时使用下面的命令就可以生成一个config.h文件

python -m genconfig

也可以自定义一个.h的配置

python -m genconfig --header-path hello.h

kconfig-frontends

深入调研以后发现,kconfig还有qt的版本,那理论上也有windows的版本,qt转windows还是比较简单的

理论上这些kconfig都是来源于kconfig-frontends

> https://github.com/uvc-ingenieure/kconfig-frontends

NuttX似乎都是基于kconfig-qconf的

目前看到各个框架似乎都抛弃了kconfig-frontends,迎接KconfigLib去了

> https://github.com/RT-Thread/rt-thread/pull/9050

kconfig-frontends似乎长年不更新,而KconfigLib里有一些新特性可以简化Kconfig

Summary

先凑活用,日后还有需要再继续在这里完善

Quote

> https://zhuanlan.zhihu.com/p/6068151451 > > https://github.com/LuckkMaker/apm32-kconfig-example > > https://blog.csdn.net/wenbo13579/article/details/127464764 > > https://boozlachu.medium.com/using-kconfig-for-own-projects-deb21e0d1804 > > https://www.jianshu.com/p/00ce218ab598 > > https://bbs.21ic.com/icview-3412466-1-1.html

</description> Thu, 23 Jan 2025 00:00:00 +0800 https://elmagnifico.tech/2025/01/23/VSCode-Kconfig-Extension/</link> https://elmagnifico.tech/2025/01/23/VSCode-Kconfig-Extension/

    <category>build</category>
    
    
  </item>

  <item>
    <title>2024游戏短评</title>
    <description>## Foreword

一年一度短评环节又到了

steam自建的2024年游戏回顾

> https://store.steampowered.com/replay/76561198094167163/2024

今年游玩

今年一共玩了44款游戏,比去年还多一些,60%都是新游戏

大侠立志传

image-20240210202138020

大侠立志传确实是又一个金庸群侠传,很不错

神仙醋确实继承了以前金庸群侠传的衣钵,今年还看到了神仙醋他们的的纪录片,想做一个这样的小游戏确实不容易,醋神他们也是暴死了好几次,靠着一款产品续命,才能养活这样一个单机游戏,而单机游戏想要长期收入是不太可能的,做完一款就必须投入到下一款去了。对于制作人来说,相当于是一直在绞尽脑汁挖掘灵感,制作游戏,否则就会陷入没有产出,收入下滑的问题。

对于武侠游戏、开放世界、像素风,这个类型来说,盘子确实比较小,能带来的收入是有限的,而且最重要的是他有很大可能会暴死,只要玩家不买账,那么这一年或者两年的投入直接打水漂,对于团队来说这种风险很高,得要有一帮子多年的老人才能玩的下去。

对于神仙醋熟悉的领域来说,会买账的大多也是当年的一些人了,更年轻的人他们想要的东西,有些时候其实和老人的审美或者角度,已经不一样了。想要再成功一次可能是比较难的,而对于一些经典模式的复刻反而是比较稳妥的实现。

Super Raft Boat Together

image-20250122154654538

游戏内容过少了,难度相对也比较简单,定价58,实在是有点吃亏,8块可能比较合适

熔炉密林

image-20250122154854504

手残的一年,但是把熔炉密林打通了,新DLC也随便打通了。游戏看起来简单,但是实际操作有很多技巧,这些游戏内都不是很容易摸索出来,需要外部攻略,游戏又非常肝,需要反复刷很久才能遇到想要的装备。

土豆兄弟

image-20250122155108327

从家庭组白嫖的游戏,也挺好玩的,自由组合搭配各种build,节奏比吸血鬼幸运者快一些,能玩的组合很多种

暖雪

image-20250122155223543

前几年出的Roguelite游戏,今年才有空白嫖玩一下,操作很爽,自己摸到了一个套路,直接全通关了。游戏内build也蛮丰富的,我估计就玩了10%不到的可能性

传说法师1/2Demo

image-20250122155600570

传说法师重温了1代,确实挺难的,找了几个组合还是有点手残不好打,打击感超棒的。

传说法师2代也同时发布了,不过2代有点不好玩,Demo流程很短,很多东西都没解锁,手残玩起来很难。

后续2代发布以后,直接就褒贬不一了

Hades

image-20250122155428853

家庭分享,哈迪斯整体操作手感是真的不错,对比暖雪、熔炉、传说法师等等,画面细节、北欧众神、各种搞笑的小桥段,整个游戏刷是一方面,但是剧情在循序渐进,让你有一个明显的盼头,知道下一次刷他会出现什么东西。 一代基本都是近战或者类似近战的操作,非常耐玩,手残也能打过最终的boss。

Hades2

image-20250122160021461

哈迪斯2就有点难评了,2代以妹妹为操作对象,妹妹是个法师,很多操作都改变了,2代需要很多长按,蓄力类的操作来打伤害,和一代的那种按一下就能打出来伤害来说,操作显得极其难受。2代的进阶操作或者说更高伤害都需要长按,不知道为什么要这么设计,这样手感就变得很差,虽然打通了,但是总是爽不起来。

由于2代是EA,所以很多内容都还没完善,有点意思的是2代在游戏中可以直接进入1代场景,也顺便看了一下2代的游戏代码,竟然就是lua直接控制的,那其实1代就给出来一个游戏框架了,2代只需要填入素材和数值,就可以直接套模板,所以一二代可以直接实现穿越式的体验,甚至很多Mod或者修改,都是直接修改源码中的条件,如果有人愿意再深入一下,完全可以以Hades为基础,搞一个自己的游戏。

Darkest Dungeon

image-20250122160558786

之前先玩过了铁轨与墓穴,EA的时候很简单,说是和这个游戏差不多,所以来看看,再看到这个游戏这么多年了还有人在玩,在直播,不可思议,所以试试。一试发现这游戏太难了,打了N个mod,简化了游戏内容,我都觉得难得要死,不会再玩第二次了。暗黑地牢,是真的黑暗,一个人玩都能玩的心惊胆战,团灭了三次,每次都想弃坑,好不容易培养的4个6级顶级天赋的人直接变坟堆,气得要死。

最后开着加速器总算是把游戏剧情全打通了,这还是最简单的难度,最高难度不敢想象,一个个都是数值管理大师。

火山的女儿

image-20250122161230253

额,一个养女儿的游戏,不是这类游戏受众,只能说来体验一下为什么这么多人玩这个,感觉算是以前的4399集大成者,缝合了很多内容进来,但是比较巧妙的是作者没有让每个东都变得很难受,控制了一下复杂度,大多数情况都可以稍微思考一下找到解,没有让系统爆炸,没有那么紧迫的感觉,慢慢养女儿就行。只能说还可以,但是我不喜欢。

刀剑江湖路

image-20250122161703034

看起来是想做一个武侠+DNF+金庸群侠传的游戏,甚至可能还有一点太吾的意思,不过系统不完善,目前剧情很多也没做完,分支也没有,主角很多时候的选择完全没用。虽然是DNF式的横板战斗搓招,但是手感差的要死,操控起来很难受。

如果有老菊之类的再给他做个长视频,游戏机制再完善完善,估计会很有意思

超级键盘侠

img

一个挺恶搞的游戏,游戏里梗比较多,但是套路有点少

Glyphica Typing Survival

image-20250122162311858

> https://elmagnifico.tech/2024/05/30/Glyphica/

一个打字游戏,非常有意思,之前评论过了不再重复,作者也在加班加点搞中文版本,很不错

夜族崛起

image-20250122162512018

化身吸血鬼,培养血奴,害怕太阳,把吸血鬼复刻到游戏里,整体还能联机、生存、剧情、刷刷刷、还需要一些操作,有build可玩,就是后期肝度实在是太高了,游戏里的极品道具都非常难获取,估计上百小时才能刷满一套。游戏完成度比较高,还在持续更新。

Abiotic Factor

image-20250122163000517

非生物因素,把恐怖元素结合进来的生存游戏,多人联机会比较好,游戏内容还不完善,剧情也没有写完,还是等后续更新吧

小丑牌

image-20250122163408774

策略游戏,有意思,一出就风靡全球,简单的玩法,一上手就都懂了,一局时间相对比较短,由此开启一个新游戏模式,各种丑牌、甚至麻将等等模仿者接接踵而至。

Chained together

image-20250122163646639

平台跳跃游戏,但是能联机,这种游戏以前也有很多,但是这个游戏他把人链接在一起了,游戏中间可以有保存点,错了还能重来,疯狂压力队友,贼搞笑,不过游戏目前场景比较少,如果后续有自定义工坊等等,内容更多了,可能比较欢乐。

戴森球计划

image-20250122165149276

一直想玩一直拖着,拖到这游戏都4年了,很像程序员开发的东西,整个流水线的配置和写代码式的,不过游戏目标不知道是什么,没有动力玩下去了

杀戮台球

image-20250122165759497

杀戮台球,把策略和各种build带到了台球里,也不是很贵,玩起来比较轻松

中华一商

image-20250122170412252

免费领的游戏,就是过程有点繁琐,中间偏后期很无聊

ASTRONEER

image-20250122170711615

当年玩的时候游戏内容还很少,后面越发展内容越多了,后期的玩法就是3D版本的戴森球了,自动化工厂流水线,每年大概有几个活动事件,总体也是挂机自动化的干活

POE2

image-20250122170941291

poe的二代,当年就听说开发了,现在才出来,二代相对一代去掉了系统里很多很繁杂的设定,整个系统变得简单了。

玩了一个月差不多终局毕业了,就是这个游戏排骨人太多了,基本全都是来刷了赚钱的,没有好好玩游戏的,有点难受

雨中冒险:回归

image-20250122171632696

雨冒一代,操作有点难受,反人类,不知道为啥重置不愿意改一下,整体玩法一般般,还是二代好玩

失落城堡2

img

之前失落城堡各种父子玩耍,现在总算出二代了,玩了Demo,但是bug一堆,玩法深度也一般,等到EA版本出来,果然直接跌下神坛,多半好评了。

已买待玩

雾锁王国

image-20250122171401542

这个去年就要玩来着,还是没玩,据说是类似夜族崛起的那种模式,不过还在EA,还能等正式版

绝世好武功

image-20250122173111981

1.0版本总算出了,可以重新体验一下了

大江湖之苍龙与白鸟

image-20250122172524627

这游戏跳票这么久,大侠同时期的游戏,现在都还没做完,等他实际完成看看吧

博德之门3

image-20250122172705184

今年可能会试试这个游戏,看看他是什么样的模式,为什么这么多好评

赛博朋克

image-20250122173511688

可能会玩一下

巫师3

image-20250122173551196

可能会玩一下

小缇娜的奇幻之地

image-20250122173621321

无主的番外篇,据说无主4也要出了,可以体验一下

诺兰德

image-20250122173744967

一直没空玩,在等他EA结束,似乎现在可以了

新游期待

主要是一些Demo游戏的游玩体验,感觉不错

咒印链接

image-20250122163950327

即时战斗策略游戏,游戏里可以得到各种各样的链接或者道具,连在一起就可以形成各种comb,最终通关。有点以前POE的宝石链接的意思,但是可以连得更长,更多种形式,目前Demo很不错,期待后期发售

腐朽默示

img

一个像素风,但是可以生存、建造、打僵尸、招小弟的游戏,可惜只能单人,目前玩起来还可以。

如果能支持多人联机估计会更好

息风谷战略

img

策略游戏,但是回合战斗,同时又能自动战斗,还能养成,养老婆的游戏,Demo的可玩内容已经挺多的了,不过玩法也比较单一,目前深度不是很够,build玩法没有明显构建起来

Sephiria

image-20250122164847406

像素风的Roguelite游戏,游戏难度比较高,也是背包管理类型,格子各种搭配可以组合出来一些特效,手感也不错。

Hell Clock

image-20250122172904062

玩的时间不长,但是作为Rougelite+刷子游戏,总体build还是有点意思的

Monster Hunter Wilds

image-20250122165921390

怪猎荒野,试玩,但是感觉机器带不动了,卡卡的,任务引导也莫名其妙的,手感似乎和上一代差不多,等2.28发售再说吧

梦之形

image-20250122170240448

Roguelite游戏,MOBA风格,刷起来很爽,支持联机,已经打通全程,今年在日本还在展会遇到了制作组,挺不错的

其他

活侠传

没玩,但是出了果然就挨骂了,剧本不错,但是评论两极分化

黑神话:悟空

image-20250122174023146

云完了,没有玩,挺不错的,不过不是我喜欢的类型

Summary

还是有一些期待2025的 </description> Wed, 22 Jan 2025 00:00:00 +0800 https://elmagnifico.tech/2025/01/22/Game-Comment5/</link> https://elmagnifico.tech/2025/01/22/Game-Comment5/

    <category>Game</category>
    
    
  </item>

  <item>
    <title>SES Threads 自定义JS脚本</title>
    <description>## Foreword

最近又遇到了一些奇怪的bug,意外发现调试手段上还是有所欠缺,而SES的js脚本可以做更多自定义的工具,可以进一步改造方便疑难杂症使用

Threads Script

需要注意Threads Script用的是Java Script,而不是Jlink Script(前面查了半天发现不对),虽然都简称成了js,但是Jlink Script应该只有Jlink的一些批处理程序才会使用这种方式写脚本

之前有说过SES的RTOS Awareness好像有点问题,老是显示不全,不如Ozone的

image-20250121152552349

貌似这个问题在我提给官方以后修复了

image-20250121152724232

之前有提到显示操作系统线程情况的脚本,这里发现其实这个东西可以自定义的程度还是蛮高的

/*********************************************************************
*               SEGGER MICROCONTROLLER GmbH                          *
*       Solutions for real time microcontroller applications         *
**********************************************************************
*                                                                    *
*       (c) 1995 - 2019  SEGGER Microcontroller GmbH                 *
*                                                                    *
*       www.segger.com     Support: support@segger.com               *
*                                                                    *
**********************************************************************
*                                                                    *
*       Please note:                                                 *
*                                                                    *
*       Knowledge of this file may under no circumstances            *
*       be used to write a similar product                           *
*                                                                    *
*       Thank you for your fairness !                                *
*                                                                    *
**********************************************************************
*                                                                    *
*       Current version number will be inserted here                 *
*       when shipment is built.                                      *
*                                                                    *
**********************************************************************

----------------------------------------------------------------------
File        : FreeRTOSPlugin_CM7.js
Purpose     : Ozone FreeRTOS-awareness JavaScript Plugin for Cortex-M7
---------------------------END-OF-HEADER------------------------------
*/

/*********************************************************************
*
*       Local Functions
*
**********************************************************************
*/

/*********************************************************************
*
*       GetTaskStackGrowDir
*  
* Function description
*   Indicates if the task stack grows in positive (1) or negative (-1) memory direction
*
* Notes
*   (1) The stack grow direction depends on the FreeRTOS-port.
*       Currently, a positive grow direction is only used on the PIC architecture.
*/
function GetTaskStackGrowDir() {    
  return -1;
}

/*********************************************************************
*
*       GetTaskPrioName
*  
* Function description
*   Returns the display text of a task priority
*
* Parameters
*   Priority: task priority (integer)
*/
function GetTaskPrioName(Priority) {
    
  var sName;
  
  if (Priority == 0) {
    sName = &quot;Idle&quot;
  } else if (Priority == 1) {
    sName = &quot;Low&quot;
  } else if (Priority == 2) {
    sName = &quot;Normal&quot;;
  } else if (Priority == 3) {
    sName = &quot;High&quot;;
  } else if (Priority == 4) {
    sName = &quot;Highest&quot;;
  } else {
    return Priority.toString();
  } 
  sName = sName + &quot; (&quot; + Priority + &quot;)&quot;;
  return sName;
}

/*********************************************************************
*
*       GetTaskName
*  
* Function description
*   Returns the display text of a task name
*
* Parameters
*   tcb:   task control block (type tskTCB)
*   Addr:  control block memory location
*/
function GetTaskName(tcb, Addr) {
    
   var sTaskName;
   
   sTaskName = Debug.evaluate(&quot;(char*)(*(tskTCB*)&quot; + Addr + &quot;).pcTaskName&quot;);
   if (sTaskName == undefined) {
     sTaskName = Debug.evaluate(&quot;(char*)(*(TCB_t*)&quot; + Addr + &quot;).pcTaskName&quot;);
   }
   
   if (tcb.uxTCBNumber != undefined) {
     sTaskName = &quot;#&quot; + tcb.uxTCBNumber + &quot; \&quot;&quot; + sTaskName + &quot;\&quot;&quot;;
   }
   return sTaskName;
}

/*********************************************************************
*
*       GetTaskID
*  
* Function description
*   Returns the display text of a task ID
*
* Parameters
*   tcb:   task control block (type tskTCB)
*   Addr:  control block memory location
*/
function GetTaskID(tcb, Addr) { 
   return  &quot;0x&quot; + (tcb.uxTCBNumber == undefined ? Addr.toString(16) : tcb.uxTCBNumber.toString(16));
}

/*********************************************************************
*
*       GetTCB
*  
* Function description
*   Returns the task control block of a task
*
* Parameters
*   Addr: control block memory location
*/
function GetTCB(Addr) { 
  var tcb;
  tcb = Debug.evaluate(&quot;*(tskTCB*)&quot; + Addr);
  if (tcb == undefined) {
    tcb = Debug.evaluate(&quot;*(TCB_t*)&quot; + Addr);
  }
  return tcb;
}

/*********************************************************************
*
*       GetTaskNotifyStateStr
*  
* Function description
*   Returns the display string of a task notify state
*
* Parameters
*   tcb: task control block (type tskTCB)
*/
function GetTaskNotifyStateStr(tcb) {

   if (tcb.ucNotifyState == undefined) {
     return tcb.eNotifyState == undefined ? &quot;N/A&quot; : tcb.eNotifyState.toString();
   } else {
     return tcb.ucNotifyState.toString()
   }
}

/*********************************************************************
*
*       GetTaskStackInfoStr
*  
* Function description
*   Returns a display text of the format &quot;&lt;free space&gt; / &lt;stack size&gt;&quot;
*
* Parameters
*   tcb: task control block (type tskTCB)
*
* Notes
*   (1) pxEndOfStack is only available when FreeRTOS was compiled with 
*       configRECORD_STACK_HIGH_ADDRESS == 1
*/
function GetTaskStackInfoStr(tcb) {
  var FreeSpace;
  var UsedSpace;
  var Size;
  //                                GrowDir == 0     GrowDir == 1
  //
  // pxStack       |  Low Addr   |  Stack Top     |  Stack Base    |
  //               |             |                |                |
  // pxTopOfStack  |             |  Stack Pointer |  Stack Pointer |       
  //               |             |                |                |
  // pxEndOfStack  |  High Addr  |  Stack Base    |  Stack Top     |
  // 
  if (GetTaskStackGrowDir() == 1) { // stack grows in positive address direction
  
    if (tcb.pxEndOfStack == undefined) {
      UsedSpace  =  tcb.pxTopOfStack - tcb.pxStack;
      FreeSpace  =  undefined;
      Size       =  undefined;  
    } else {
      FreeSpace  =  tcb.pxEndOfStack - tcb.pxTopOfStack;
      UsedSpace  =  tcb.pxTopOfStack - tcb.pxStack;
      Size       =  FreeSpace + UsedSpace;  
    }
  } else {    // stack grows in negative address direction
  
    if (tcb.pxEndOfStack == undefined) {
      FreeSpace  =  tcb.pxTopOfStack - tcb.pxStack;     
      UsedSpace  =  undefined;      
      Size       =  undefined;
    } else {
      FreeSpace  =  tcb.pxTopOfStack - tcb.pxStack;     
      UsedSpace  =  tcb.pxEndOfStack - tcb.pxTopOfStack;      
      Size       =  FreeSpace + UsedSpace;
    }   
  }   
  return FreeSpace + &quot; / &quot; + (Size == undefined ? &quot;N/A&quot; : Size);   
}

function GetLabel1(tcb) {
  var FreeSpace;
  var UsedSpace;
  var Size;
  //                                GrowDir == 0     GrowDir == 1
  //
  // pxStack       |  Low Addr   |  Stack Top     |  Stack Base    |
  //               |             |                |                |
  // pxTopOfStack  |             |  Stack Pointer |  Stack Pointer |       
  //               |             |                |                |
  // pxEndOfStack  |  High Addr  |  Stack Base    |  Stack Top     |
  // 
  if (GetTaskStackGrowDir() == 1) { // stack grows in positive address direction
  
    if (tcb.pxEndOfStack == undefined) {
      UsedSpace  =  tcb.pxTopOfStack - tcb.pxStack;
      FreeSpace  =  undefined;
      Size       =  undefined;  
    } else {
      FreeSpace  =  tcb.pxEndOfStack - tcb.pxTopOfStack;
      UsedSpace  =  tcb.pxTopOfStack - tcb.pxStack;
      Size       =  FreeSpace + UsedSpace;  
    }
  } else {    // stack grows in negative address direction
    // FreeRTOS -1 ,go on here
    if (tcb.pxEndOfStack == undefined) {
      FreeSpace  =  tcb.pxTopOfStack - tcb.pxStack;     
      UsedSpace  =  undefined;      
      Size       =  undefined;
    } else {
      FreeSpace  =  tcb.pxTopOfStack - tcb.pxStack;     
      UsedSpace  =  tcb.pxEndOfStack - tcb.pxTopOfStack;      
      Size       =  FreeSpace + UsedSpace;
    }   
  }   
  return &quot;0x&quot;+tcb.pxTopOfStack.toString(16) + &quot; / &quot; + &quot;0x&quot;+tcb.pxStack.toString(16);   
}

function GetLabel2(addr) {
  return addr.toString(16);   
}

function GetLabel3(param) {
  return param.toString(16);   
}

/*********************************************************************
*
*       AddTask
*  
* Function description
*   Adds a task to the task window
*
* Parameters
*   Addr:            memory location of the task's control block (TCB)
*   CurrTaskAddr:    memory location of the executing task's control block (TCB)
*   sState:          state of the task (e.g. &quot;executing&quot;)
*/
function AddTask(Addr, CurrTaskAddr, sState) {
  var tcb;
  var sTaskName; 
  var sPriority;
  var sRunCnt;
  var sMutexCnt;
  var sTimeout;
  var sStackInfo;
  var sID;
  var sNotifiedValue;
  var sNotifyState; 
  var Lable1
  var Lable2
  var Lable3

  tcb            = GetTCB(Addr);
  sTaskName      = GetTaskName(tcb, Addr);
  sID            = GetTaskID(tcb, Addr);
  sStackInfo     = GetTaskStackInfoStr(tcb);
  sPriority      = GetTaskPrioName(tcb.uxPriority);
  sNotifyState   = GetTaskNotifyStateStr(tcb);
  sTimeout       = (tcb.Timeout          == undefined ? &quot;N/A&quot; : tcb.Timeout.toString());
  sRunCnt        = (tcb.ulRunTimeCounter == undefined ? &quot;N/A&quot; : tcb.ulRunTimeCounter.toString());
  sNotifiedValue = (tcb.ulNotifiedValue  == undefined ? &quot;N/A&quot; : tcb.ulNotifiedValue.toString());
  sMutexCnt      = (tcb.uxMutexesHeld    == undefined ? &quot;N/A&quot; : tcb.uxMutexesHeld.toString());

  // custom labels
  Lable1  = GetLabel1(tcb);
  Lable2  = GetLabel2(Addr);
  Lable3  = GetLabel3(0);
 
  if (Addr == CurrTaskAddr) {
    sState = &quot;executing&quot;;
  } 
  Threads.add(sTaskName, sRunCnt, sPriority, sState, sTimeout, sStackInfo, sID, sMutexCnt, sNotifiedValue, sNotifyState, Lable1,Lable2,Lable3,(Addr == CurrTaskAddr) ? undefined : Addr);
}

/*********************************************************************
*
*       AddList
*  
* Function description
*   Adds all tasks of a task list to the task window
*
* Parameters
*   List:            task list (type xLIST)
*   CurrTaskAddr:    memory location of the executing task's control block (TCB)
*   sState:          common state of all tasks within 'list'
*/
function AddList(List, CurrTaskAddr, sState) {
  var i;
  var Index;
  var Item;
  var TaskAddr;

  if ((List != undefined) &amp;&amp; (List.uxNumberOfItems &gt; 0)) {
      
    Index = List.xListEnd.pxNext;
    
    for (i = 0; i &lt; List.uxNumberOfItems; i++) {
        
      Item = Debug.evaluate(&quot;*(xLIST_ITEM*)&quot; + Index);

      TaskAddr = Item != 0 ? Item.pvOwner : 0;

      if (TaskAddr != 0) {
        AddTask(TaskAddr, CurrTaskAddr, sState);
      }
      Index = Item.pxNext;

      if (i &gt; 1000) { // infinite loop guard
        break;
      }
    }
  }
}

/*********************************************************************
*
*       API Functions
*
**********************************************************************
*/

/*********************************************************************
*
*       init
*  
* Function description
*   Initializes the task window
*/
function init() {
    
  Threads.clear();
  Threads.newqueue(&quot;Task List&quot;);
  Threads.setColumns(&quot;Name&quot;, &quot;Run Count&quot;, &quot;Priority&quot;, &quot;Status&quot;, &quot;Timeout&quot;, &quot;Stack Info&quot;, &quot;ID&quot;, &quot;Mutex Count&quot;, &quot;Notified Value&quot;, &quot;Notify State&quot;,&quot;Lable1&quot;,&quot;Lable2&quot;,&quot;Lable3&quot;);
  Threads.setColor(&quot;Status&quot;, &quot;ready&quot;, &quot;executing&quot;, &quot;blocked&quot;);
}

/*********************************************************************
*
*       update
*  
* Function description
*   Updates the task window
*/
function update() {
  var i;
  var pList;
  var List;
  var MaxPriority;
  var CurrTaskAddr;

  Threads.clear();

  if((Debug.evaluate(&quot;pxCurrentTCB&quot;) == 0) || (Debug.evaluate(&quot;pxCurrentTCB&quot;) == undefined)) {
    return;
  }
  MaxPriority  = Debug.evaluate(&quot;uxTopReadyPriority&quot;);  
  CurrTaskAddr = Debug.evaluate(&quot;pxCurrentTCB&quot;);

  for (i = MaxPriority; i &gt;= 0; i--) {
    List = Debug.evaluate(&quot;pxReadyTasksLists[&quot; + i + &quot;]&quot;);
    AddList(List, CurrTaskAddr, &quot;ready&quot;);
  }

  pList = Debug.evaluate(&quot;pxDelayedTaskList&quot;);
  if (pList != 0) {
    List = Debug.evaluate(&quot;*(xLIST*)&quot; + pList);
    AddList(List, CurrTaskAddr, &quot;blocked&quot;);
  }

  pList = Debug.evaluate(&quot;pxOverflowDelayedTaskList&quot;);
  if (pList != 0) {
    List = Debug.evaluate(&quot;*(xLIST*)&quot; + pList);
    AddList(List, CurrTaskAddr, &quot;blocked&quot;);
  }

  List = Debug.evaluate(&quot;xSuspendedTaskList&quot;);
  if (List != 0) {
    AddList(List, CurrTaskAddr, &quot;suspended&quot;);
  } 
}

/*********************************************************************
*
*       getregs
*
* Function description
*   Returns the register set of a task.
*   For ARM cores, this function is expected to return the values
*   of registers R0 to R15 and PSR.
*
* Parameters
*   hTask: integer number identifiying the task.
*   Identical to the last parameter supplied to method Threads.add.
*   For convenience, this should be the address of the TCB.
*
* Return Values
*   An array of unsigned integers containing the tasks register values.
*   The array must be sorted according to the logical indexes of the regs.
*   The logical register indexing scheme is defined by the ELF-DWARF ABI.
*
**********************************************************************
*/
function getregs(hTask) { 
  var i;
  var SP;
  var LR;
  var Addr;
  var tcb;
  var aRegs = new Array(17);

  tcb  =  GetTCB(hTask);
  SP   =  tcb.pxTopOfStack;
  Addr =  SP;
  
  /* the following registers are pushed by the FreeRTOS-scheduler */
  //
  // R4...R11
  //
  for (i = 4; i &lt; 12; i++) {
    aRegs[i] = TargetInterface.peekWord(Addr); 
    Addr += 4;
  }
  //
  // EXEC_RET
  //
  LR = TargetInterface.peekWord(Addr);
  Addr += 4;
  //
  // S16...S31
  //
  if ((LR &amp; 0x10) != 0x10) { // FP context has been saved?
    Addr += 4*16; // skip S16..S31
  }
  /* the following registers are pushed by the ARM core */
  //
  // R0...R3
  //
  for (i = 0; i &lt; 4; i++) {
    aRegs[i] = TargetInterface.peekWord(Addr);  
    Addr += 4;
  }
  //
  // R12, LR, PC, PSR
  //
  aRegs[12] = TargetInterface.peekWord(Addr); 
  Addr += 4;
  aRegs[14] = TargetInterface.peekWord(Addr);  
  Addr += 4;
  aRegs[15] = TargetInterface.peekWord(Addr); 
  Addr += 4;
  aRegs[16] = TargetInterface.peekWord(Addr); 
  Addr += 4;
  //
  // S0..S15
  //
  if ((LR &amp; 0x10) != 0x10) { // FP context has been saved?
    Addr += 4*18; // skip S0...S15
  }
  if (aRegs[16] &amp; (1&lt;&lt;9)) { // Stack has been 8-byte aligned
    Addr += 4;
  }
  //
  // SP
  //
  aRegs[13] = Addr;
  
  return aRegs;
}

/*********************************************************************
*
*       getContextSwitchAddrs
*
*  Functions description
*    Returns an unsigned integer array containing the base addresses 
*    of all functions that complete a task switch when executed.
*/
function getContextSwitchAddrs() {
  
  var aAddrs;
  var Addr;
  
  Addr = Debug.evaluate(&quot;&amp;vTaskSwitchContext&quot;);
  
  if (Addr != undefined) {
    aAddrs = new Array(1);
    aAddrs[0] = Addr;
    return aAddrs;
  } 
  return [];
}

/*********************************************************************
*
*       getOSName()
*
*  Functions description:
*    Returns the name of the RTOS this script supplies support for
*/
function getOSName() {
  return &quot;FreeRTOS&quot;;
}

这里主要是增加了三个标签的显示

image-20250121152852234

可以显示栈的起始指针和当前指针位置、TCB指针位置,还多了一个标签待定,随便定义显示什么

整个脚本里主要显示的表格是Threads,只要在它里面去加内容就行了

function init() {
    
  Threads.clear();
  Threads.newqueue(&quot;Task List&quot;);
  Threads.setColumns(&quot;Name&quot;, &quot;Run Count&quot;, &quot;Priority&quot;, &quot;Status&quot;, &quot;Timeout&quot;, &quot;Stack Info&quot;, &quot;ID&quot;, &quot;Mutex Count&quot;, &quot;Notified Value&quot;, &quot;Notify State&quot;,&quot;Lable1&quot;,&quot;Lable2&quot;,&quot;Lable3&quot;);
  Threads.setColor(&quot;Status&quot;, &quot;ready&quot;, &quot;executing&quot;, &quot;blocked&quot;);
}

剩下显示具体变量也好,还是什么其他东西也好,都可以使用这个函数来完成

Debug.evaluate(&quot;pxCurrentTCB&quot;)

他会把对应的变量转成实际的内容返回

如果要调试某个函数,还可以通过这种方式来获取到具体的函数名称

Debug.getfunction(address)

如果相加额外的表格列,对应加上一个数字即可

function init()
{
  ...
  Threads.setColumns2(&quot;Timers&quot;, &quot;Id(Timers)&quot;, &quot;Name&quot;, &quot;Hook&quot;, &quot;Timeout&quot;, &quot;Period&quot;, &quot;Active&quot;);
function update()
{
  ...
  Threads.add2(&quot;Timers&quot;, &quot;0x1FF0A30&quot;, &quot;MyTimer&quot;, &quot;0x46C8 (Timer50)&quot;, &quot;50(550)&quot;, &quot;50&quot;, &quot;1&quot;);

image-20250121154225511

每次更新脚本以后只需要Reload一下,就可以直接加载允许了,不需要断开jlink重连什么的,还是非常方便的

特别是如果遇到踩内存或者需要遍历各个队列什么的,就可以用js脚本直接写一个然后输出到外部框里就行了

其他

虽然官方文档里有Echo,类似console.log,但是试了一下在js这里写这个内容是完全无效的,可以正常执行,但是结果不知道输出到哪里去了

WScript.Echo(s) 

这个小插件的报错有点不友好,很容易看不出来具体错误是什么,要怎么改

而又缺少对这个小插件调试的手段,只能一遍遍试着跑,有点蛋疼

Summary

好用的脚本还是得多积累积累,下次遇到问题才能派上用场

Quote

> https://studio.segger.com/index.htm?https://studio.segger.com/ide_debug_expressions.htm

</description> Tue, 21 Jan 2025 00:00:00 +0800 https://elmagnifico.tech/2025/01/21/JS-SES-Ozone/</link> https://elmagnifico.tech/2025/01/21/JS-SES-Ozone/

    <category>SES</category>
    
    
  </item>

  <item>
    <title>RSS Follow体验</title>
    <description>## Foreword

RSS难得新增一员,体验一下Follow,看看和TTRss有啥不一样的,有啥改进的点

Follow

> https://follow.is/

image-20250121163433442

Follow支持opml导入,所以TTRSS的内容可以直接导进来,对应也有配套的APP,不用自己去找支持TTRSS的订阅APP,生态整体比较完整

Follow整个界面也更加现代一些,TTRSS则是更简洁,更快,动效上明显多了很多内容

相比TTRSS可能需要设置各种插件来爬取文章内容,Follow似乎把这一块都简化了,更容易上手一些

image-20250121163729432

打开某一个blog的文章,是可以看到有多少人也一起订阅了这个网站的内容

image-20250121164005886

甚至可以看到我自己的的网站被多少人订阅了,我可以看到具体谁阅读过我的文章

认证

如果这是你的订阅源,需要本人认证,可以通过下面的三种方式

image-20250121164539778

大致看了下,内容和描述都要插入一些奇怪的东西,不喜欢,还是插入RSS标签好一点

对应我Blog中的feed.xml增加标签即可

---
layout: null
---
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;rss version=&quot;2.0&quot; xmlns:atom=&quot;http://www.w3.org/2005/Atom&quot;&gt;
  &lt;channel&gt;
    &lt;title&gt;{{ site.title | xml_escape }}&lt;/title&gt;
    &lt;description&gt;{{ site.description | xml_escape }}&lt;/description&gt;
    &lt;link&gt;{{ site.url }}{{ site.baseurl }}/&lt;/link&gt;
    &lt;atom:link href=&quot;{{ &quot;/feed.xml&quot; | prepend: site.baseurl | prepend: site.url }}&quot; rel=&quot;self&quot; type=&quot;application/rss+xml&quot; /&gt;
    &lt;pubDate&gt;{{ site.time | date_to_rfc822 }}&lt;/pubDate&gt;
    &lt;lastBuildDate&gt;{{ site.time | date_to_rfc822 }}&lt;/lastBuildDate&gt;
    &lt;generator&gt;Jekyll v{{ jekyll.version }}&lt;/generator&gt;
    &lt;follow_challenge&gt;
        &lt;feedId&gt;55855418052542483&lt;/feedId&gt;
        &lt;userId&gt;101522814280570880&lt;/userId&gt;
    &lt;/follow_challenge&gt;    
    {% for post in site.posts limit:10 %}
      &lt;item&gt;
        &lt;title&gt;{{ post.title | xml_escape }}&lt;/title&gt;
        &lt;description&gt;{{ post.content | xml_escape }}&lt;/description&gt;
        &lt;pubDate&gt;{{ post.date | date_to_rfc822 }}&lt;/pubDate&gt;
        &lt;link&gt;{{ post.url | prepend: site.baseurl | prepend: site.url }}&lt;/link&gt;
        &lt;guid isPermaLink=&quot;true&quot;&gt;{{ post.url | prepend: site.baseurl | prepend: site.url }}&lt;/guid&gt;
        {% for tag in post.tags %}
        &lt;category&gt;{{ tag | xml_escape }}&lt;/category&gt;
        {% endfor %}
        {% for cat in post.categories %}
        &lt;category&gt;{{ cat | xml_escape }}&lt;/category&gt;
        {% endfor %}
      &lt;/item&gt;
    {% endfor %}
  &lt;/channel&gt;
&lt;/rss&gt;

认证完成以后就会有一个小勾了

image-20250121165407063

分享

这是我多年积攒下来的一些订阅源,直接用Follow就可以分享,甚至还可以出售

> https://app.follow.is/share/lists/104444993002142720

image-20250121165600901

看起来似乎Follow想通过这种订阅或者说活跃转化成对应的虚拟货币进而维持或者是激励??

这里是我自己的归类,似乎和上面的一样,只是订阅这个人可以订阅到他设置的所有订阅源?

> https://app.follow.is/share/users/101522814280570880

image-20250121171022818

看了下RSSHub,在这里竟然是有人提供RSSHub实例然后收费,帮你爬取一些内容,普通人部署了RSSHub也可以加入其中,目前官方应该是免费给你用的,可能某一天官方就不免费了,就要你自己去爬取内容了?那我为什么不用TTRSS,直接全集成好了,还不要去给别人掏钱,费这个鸟劲。

Summary

目前Follow已经是公测阶段,可以免费加入了,但是功能不是完全体的,要完全功能还是得邀请码激活,这个比较麻烦,我也是问人要了一个才能导入甚至分享订阅的

目前看Follow有点意思,但是不多,比我的TTRSS其实没好多少,很多页面也是完全爬不到,还得配合RSSHub来一起使用抓取内容,那我感觉就没啥必要了,不能直接整合这个东西,体验上还是差一点的

目前看起来这个生态只是个金字塔,只有顶端流量大的人有的赚,其他人只能被收割,看起来并不是一个能循环起来的东西。前一段时间的火爆,现在很少看到相关内容了,估计后续生态起不来依然会沉寂到死

他只能对现有RSS的订阅再次划分,而不能产生新的内容,甚至想依靠这种划分来收割一波,信息差这么好利用吗,而至于你订阅源是否能得到利益,那就很难说了,订阅源作为核心的产出者,没有得到应有的流量,反而把利润分给了分发者和服务部署的人。生产者和消费者被剥削了,中介从中获利?

Quote

> https://app.follow.is/share/lists/104444993002142720

</description> Tue, 21 Jan 2025 00:00:00 +0800 https://elmagnifico.tech/2025/01/21/RSS-Follow/</link> https://elmagnifico.tech/2025/01/21/RSS-Follow/

    <category>RSS</category>
    
    
  </item>

  <item>
    <title>nRF-Kconfig插件解析</title>
    <description>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;

<p>之前刚好看过一点VScode插件原理,这里就能用上了,看一下nRF-Kconfig是怎么实现的,改一个通用版本来,方便大家使用</p>

<h2 id="nrf-kconfig">nRF-Kconfig</h2>

<p>Kconfig插件的安装目录在这里</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>C:\Users\用户名.vscode\extensions\nordic-semiconductor.nrf-kconfig-2024.12.13 </code></pre></div></div>

<p>实际这个插件也没有加密或者混淆什么的,可以直接看</p>

<h3 id="尝试build">尝试build</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode:prepublish"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build &amp;&amp; npm run changelog"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rimraf dist &amp;&amp; tsx ./scripts/build.js –production"</span><span class="p">,</span><span class="w"> </span><span class="nl">"watch"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type-check"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc –noEmit"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lintfix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts –fix nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"changelog"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/changelog.ts kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run –prefix ../ copyright"</span><span class="p">,</span><span class="w"> </span><span class="nl">"buildHeaderFiles"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/buildHeaderFiles.ts"</span><span class="p">,</span><span class="w"> </span><span class="nl">"update-build-number"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node ../scripts/updateBuildNumber.js kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copy-css"</span><span class="p">:</span><span class="w"> </span><span class="s2">"copyfiles </span><span class="se">\"</span><span class="s2">src//.css</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">src/**/.scss</span><span class="se">\"</span><span class="s2"> out/nrf-kconfig &amp;&amp; copyfiles </span><span class="se">\"</span><span class="s2">../common/src//.css</span><span class="se">\"</span><span class="s2"> </span><span class="se">\"</span><span class="s2">../common/src/**/.scss</span><span class="se">\"</span><span class="s2"> out/common"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build-tests"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc -p . –outDir out"</span><span class="p">,</span><span class="w"> </span><span class="nl">"watch-tests"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsc -p . -w –outDir out"</span><span class="p">,</span><span class="w"> </span><span class="nl">"pretest"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build-tests &amp;&amp; npm run build &amp;&amp; npm run copy-css"</span><span class="p">,</span><span class="w"> </span><span class="nl">"test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node ./out/nrf-kconfig/test/runTests.js"</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span></code></pre></div></div>

<p>直接使用Package.js中的script部分,可以直接build或者prepublish</p>

<p>可能会遇到下面的错误</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm : 无法加载文件 D:\nodejs\npm.ps1,因为在此系统上禁止运行脚本 </code></pre></div></div>

<p>查看脚本允许策略,可以看到返回值是严格,这样不允许npm调用ps1的脚本</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Get-ExecutionPolicy</span><span class="w"> </span></code></pre></div></div>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163319195.png" alt="image-20250102163319155" /></p>

<p>修改策略,改为允许远程运行</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">Set-ExecutionPolicy</span><span class="w"> </span><span class="nx">RemoteSigned</span><span class="w"> </span></code></pre></div></div>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163501426.png" alt="image-20250102163501393" /></p>

<p>修改完成,再次进行build,正常了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102163648430.png" alt="image-20250102163648400" /></p>

<p>依然提示缺少rimraf命令,但是至少说明脚本跑起来了,这个问题后面再处理</p>

<p>安装一下rimraf就行了,实际上后续还会有tsx也缺少,一起都安装了</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <span class="nb">install</span> <span class="nt">–save</span> rimraf npm <span class="nb">install</span> <span class="nt">–save</span> tsx </code></pre></div></div>

<p>继续往下跑就会发现,这样copy的代码缺少一个script文件夹,这里面包含了各种打包的脚本文件,这里只能先到此为止了</p>

<h3 id="源码分析">源码分析</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"contributes"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">这里注册了各种使用的按钮</span><span class="w"> </span><span class="nl">"commands"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.add"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kconfig: Add build folder"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.showGui"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Kconfig: Show Config GUI"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Go to Symbol Definition"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Expand Subtree"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Collapse Subtree"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.ctx.setActive"</span><span class="p">,</span><span class="w"> </span><span class="nl">"category"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Set Active Context"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"$(edit)"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"menus"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"webview/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canGoToSymDefinition == true"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canExpandSubtree == true"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"canCollapseSubtree == true"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"commandPalette"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.goToSymDefinition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.expandSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"command"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig.collapseSubtree"</span><span class="p">,</span><span class="w"> </span><span class="nl">"when"</span><span class="p">:</span><span class="w"> </span><span class="s2">"never"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"view/item/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> </span><span class="nl">"editor/context"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"submenus"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">这里是一些这个插件的一些设置参数</span><span class="w"> </span><span class="nl">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"object"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Settings for nRF Kconfig"</span><span class="p">,</span><span class="w"> </span><span class="nl">"properties"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"kconfig.zephyr.base"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Override location of Zephyr. This is only necessary if you want to use nRF Kconfig by itself and the main extension is not installed."</span><span class="p">,</span><span class="w"> </span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"machine-overridable"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"kconfig.python"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"string"</span><span class="p">,</span><span class="w"> </span><span class="nl">"markdownDescription"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Location of Python executable. Python 3 is required to use nRF Kconfig. If no location is specified, then the PATH of the current workspace is used."</span><span class="p">,</span><span class="w"> </span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"machine-overridable"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"kconfig.liveValue"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"boolean"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Show changed values in conf files as ghost assignments on the same line as their original assignment."</span><span class="p">,</span><span class="w"> </span><span class="nl">"scope"</span><span class="p">:</span><span class="w"> </span><span class="s2">"application"</span><span class="p">,</span><span class="w"> </span><span class="nl">"default"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">这里主要是对文件解析和提示的配置,什么类型文件才会被调用解析器进行解析、高亮、补全等</span><span class="w"> </span><span class="nl">"languages"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">language定义一个数组,每个数组表示一种支持的语言类型</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">id是每个语言的标识符</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kconfig"</span><span class="p">,</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">别名</span><span class="w"> </span><span class="nl">"aliases"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">特指文件名</span><span class="w"> </span><span class="nl">"filenames"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">特指符合某些规则的文件名</span><span class="w"> </span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig."</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">这个里面是语言的具体规则,应该是内容识别的规则(折叠段识别)</span><span class="w"> </span><span class="nl">"configuration"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./language-configuration.json"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"dark"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/images/dark/kconfig.svg"</span><span class="p">,</span><span class="w"> </span><span class="nl">"light"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/images/light/kconfig.svg"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">下面是特指了一些文件内容,相当于是exclude,排除了某些文件</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">单独指定了他们是什么类型的语言文件,防止误识别</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"python"</span><span class="p">,</span><span class="w"> </span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig.py"</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"cmake"</span><span class="p">,</span><span class="w"> </span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig.cmake"</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"css"</span><span class="p">,</span><span class="w"> </span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Kconfig.css"</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"properties"</span><span class="p">,</span><span class="w"> </span><span class="nl">"filenamePatterns"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"_defconfig"</span><span class="p">,</span><span class="w"> </span><span class="s2">"*.conf"</span><span class="p">,</span><span class="w"> </span><span class="s2">".config"</span><span class="p">,</span><span class="w"> </span><span class="s2">".config.sysbuild"</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"viewsWelcome"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"__metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3c214a32-71ea-4549-a492-0f972af076a5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c9e8387e-6a8a-4101-bcc0-52d8bf37ca83"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherDisplayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nordic Semiconductor"</span><span class="p">,</span><span class="w"> </span><span class="nl">"targetPlatform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"undefined"</span><span class="p">,</span><span class="w"> </span><span class="nl">"isApplicationScoped"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"isPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"hasPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"installedTimestamp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1734425172758</span><span class="p">,</span><span class="w"> </span><span class="nl">"pinned"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"preRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gallery"</span><span class="p">,</span><span class="w"> </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="mi">2490675</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="err">#</span><span class="w"> </span><span class="err">前面安装的几个依赖的模块,这里其实有提示</span><span class="w"> </span><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"rimraf"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^6.0.1"</span><span class="p">,</span><span class="w"> </span><span class="nl">"tsx"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.19.2"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">

</span></code></pre></div></div>

<p>重点还是看<code class="language-plaintext highlighter-rouge">kconfig.showGui</code>做了些啥</p>

<p>正常应该有一个js调用的脚本,但是没找到,只看到了py文件,找到了py的入口,直接让Copilot帮我分析了一下,大致是启动了一个LSP的语言分析(language symbol phrase)服务,然后这个服务和什么东西用的是rpc通信。</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">name</span> <span class="o">==</span> <span class="s">"main"</span><span class="p">:</span> <span class="n">args</span> <span class="o">=</span> <span class="n">parse_args</span><span class="p">()</span>

&lt;span class=&quot;n&quot;&gt;srv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;KconfigServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;srv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;srv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Server starting&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;debug&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wait_for_debugger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;srv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;srv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

<p>目前看起来这个LSP服务主要是用来做代码提示、符号信息、Kconfig内容查找?,不知道和具体文件解析有没有关系。</p>

<p>syntaxes下的kconfig.tmGrammar.json是kconfig高亮的配置文件</p>

<p>目前看应该还有一个前端或者是什么东西,通过rpc通信,将符号信息送给Kconfig进行分析,然后返回对应的信息</p>

<p>比如平常常用的查找定义,客户端发送请求,指定文件和符号所在行和字符(这里为啥不用指定字符的长度?不是很懂)</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nl">"method"</span><span class="p">:</span><span class="w"> </span><span class="s2">"textDocument/definition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"params"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"textDocument"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:///path/to/file"</span><span class="p">},</span><span class="w"> </span><span class="nl">"position"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>

<p>KconfigServer接收请求并处理</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">handler</span><span class="p">(</span><span class="s">‘textDocument/definition’</span><span class="p">)</span> <span class="k">def</span> <span class="nf">handle_definition</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">params</span><span class="p">):</span> <span class="n">sym</span> <span class="o">=</span> <span class="bp">self</span><span class="p">.</span><span class="n">get_sym</span><span class="p">(</span><span class="n">params</span><span class="p">)</span> <span class="k">if</span> <span class="n">sym</span><span class="p">:</span> <span class="k">return</span> <span class="n">loc</span><span class="p">(</span><span class="n">sym</span><span class="p">)</span> </code></pre></div></div>

<p>KconfigServer接着发送响应</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"jsonrpc"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="nl">"result"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"uri"</span><span class="p">:</span><span class="w"> </span><span class="s2">"file:///path/to/definition"</span><span class="p">,</span><span class="w"> </span><span class="nl">"range"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"start"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">},</span><span class="w"> </span><span class="nl">"end"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="nl">"line"</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"> </span><span class="nl">"character"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>

<p>客户端接收并处理响应,跳转到符号定义位置,那么这次通信就算完成了</p>

<p>但是这个问题,似乎就是nRF Kconfig插件其实没加载,表面上看安装了,但是真正的加载和注册命令是需要extension的其他插件来完成的,虽然代码里写着独立运行,实际上并不可行</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="err">configuration:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">title:</span><span class="w"> </span><span class="err">‘nRF</span><span class="w"> </span><span class="err">Kconfig’</span><span class="p">,</span><span class="w"> </span><span class="err">type:</span><span class="w"> </span><span class="err">‘object’</span><span class="p">,</span><span class="w"> </span><span class="err">description:</span><span class="w"> </span><span class="err">‘Settings</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig’</span><span class="p">,</span><span class="w"> </span><span class="err">properties:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">‘kconfig.zephyr.base’:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">type:</span><span class="w"> </span><span class="err">‘string’</span><span class="p">,</span><span class="w"> </span><span class="err">description:</span><span class="w"> </span><span class="err">‘Override</span><span class="w"> </span><span class="err">location</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">Zephyr.</span><span class="w"> </span><span class="err">This</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">only</span><span class="w"> </span><span class="err">necessary</span><span class="w"> </span><span class="err">if</span><span class="w"> </span><span class="err">you</span><span class="w"> </span><span class="err">want</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig</span><span class="w"> </span><span class="err">by</span><span class="w"> </span><span class="err">itself</span><span class="w"> </span><span class="err">and</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">main</span><span class="w"> </span><span class="err">extension</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">not</span><span class="w"> </span><span class="err">installed.’</span><span class="p">,</span><span class="w"> </span><span class="err">scope:</span><span class="w"> </span><span class="err">‘machine-overridable’</span><span class="p">,</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="err">‘kconfig.python’:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">type:</span><span class="w"> </span><span class="err">‘string’</span><span class="p">,</span><span class="w"> </span><span class="err">markdownDescription:</span><span class="w"> </span><span class="err">‘Location</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">Python</span><span class="w"> </span><span class="err">executable.</span><span class="w"> </span><span class="err">Python</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">required</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">use</span><span class="w"> </span><span class="err">nRF</span><span class="w"> </span><span class="err">Kconfig.</span><span class="w"> </span><span class="err">If</span><span class="w"> </span><span class="err">no</span><span class="w"> </span><span class="err">location</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">specified</span><span class="p">,</span><span class="w"> </span><span class="err">then</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">PATH</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">current</span><span class="w"> </span><span class="err">workspace</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">used.’</span><span class="p">,</span><span class="w"> </span><span class="err">scope:</span><span class="w"> </span><span class="err">‘machine-overridable’</span><span class="p">,</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="err">‘kconfig.liveValue’:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">type:</span><span class="w"> </span><span class="err">‘boolean’</span><span class="p">,</span><span class="w"> </span><span class="err">description:</span><span class="w"> </span><span class="err">‘Show</span><span class="w"> </span><span class="err">changed</span><span class="w"> </span><span class="err">values</span><span class="w"> </span><span class="err">in</span><span class="w"> </span><span class="err">conf</span><span class="w"> </span><span class="err">files</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">ghost</span><span class="w"> </span><span class="err">assignments</span><span class="w"> </span><span class="err">on</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">same</span><span class="w"> </span><span class="err">line</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">their</span><span class="w"> </span><span class="err">original</span><span class="w"> </span><span class="err">assignment.’</span><span class="p">,</span><span class="w"> </span><span class="err">scope:</span><span class="w"> </span><span class="err">‘application’</span><span class="p">,</span><span class="w"> </span><span class="err">default:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span></code></pre></div></div>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102193317417.png" alt="image-20250102193317336" /></p>

<p><code class="language-plaintext highlighter-rouge">contributes.ts</code>中主要是用TS写的这个<code class="language-plaintext highlighter-rouge">contributes</code>的字段内容,打包过程中会被用来替换package.json中的内容。而<code class="language-plaintext highlighter-rouge">submenus</code>则是由build脚本填充的,由于缺少这一块,所以这里就空了</p>

<h3 id="流程梳理">流程梳理</h3>

<p><code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>中的<code class="language-plaintext highlighter-rouge">KconfigServer</code>类实现了一个Kconfig语言服务器协议(LSP)服务器。这个服务器通常由一个支持LSP的客户端(如Visual Studio Code或其他支持LSP的编辑器)调用和使用。以下是调用和使用<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>的典型流程:</p>

<h4 id="1-客户端如vs-code启动lsp服务器">1. 客户端(如VS Code)启动LSP服务器</h4>

<p>当用户在支持LSP的编辑器中打开一个Kconfig文件时,编辑器会根据配置启动相应的LSP服务器。</p>

<p>在VS Code中,这通常通过<code class="language-plaintext highlighter-rouge">package.json</code>中的<code class="language-plaintext highlighter-rouge">contributes</code>和<code class="language-plaintext highlighter-rouge">activationEvents</code>字段配置。例如:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{ "contributes": { "languages": [ { "id": "kconfig", "extensions": [".kconfig"], "configuration": "./language-configuration.json" } ], "commands": [ { "command": "extension.kconfigServer", "title": "Start Kconfig Server" } ] }, "activationEvents": [ "onLanguage:kconfig" ] } </code></pre></div></div>

<ul> <li>这里的分析推敲,其实反向验证了package.json内的内容</li> </ul>

<h4 id="2-启动lsp服务器">2. 启动LSP服务器</h4>

<p>当满足激活条件(如打开Kconfig文件)时,编辑器会启动LSP服务器进程。</p>

<p>这个进程会运行<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>中的代码,创建并启动<code class="language-plaintext highlighter-rouge">KconfigServer</code>实例。</p>

<h4 id="3-客户端与lsp服务器通信">3. 客户端与LSP服务器通信</h4>

<p>启动后,客户端和LSP服务器通过JSON-RPC协议进行通信。</p>

<p>客户端发送各种LSP请求(如<code class="language-plaintext highlighter-rouge">textDocument/didOpen</code>、<code class="language-plaintext highlighter-rouge">textDocument/didChange</code>、<code class="language-plaintext highlighter-rouge">textDocument/completion</code>等)到服务器,<code class="language-plaintext highlighter-rouge">KconfigServer</code>类处理这些请求,并返回相应的响应。</p>

<h4 id="4-处理请求和响应">4. 处理请求和响应</h4>

<p><code class="language-plaintext highlighter-rouge">KconfigServer</code>类中的方法(如<code class="language-plaintext highlighter-rouge">handle_initialize</code>、<code class="language-plaintext highlighter-rouge">handle_completion</code>、<code class="language-plaintext highlighter-rouge">handle_definition</code>等)处理来自客户端的请求。</p>

<h4 id="5-服务器发送通知">5. 服务器发送通知</h4>

<p>除了处理请求,<code class="language-plaintext highlighter-rouge">KconfigServer</code>还可以主动发送通知给客户端,例如发布诊断信息(<code class="language-plaintext highlighter-rouge">publish_diags</code>方法)或通知状态变化(<code class="language-plaintext highlighter-rouge">notify_status</code>方法)。</p>

<h3 id="nrf-connect-extension-pack">nRF-connect-extension-pack</h3>

<p>由于上面所有的逻辑都是copilot直接给出来的,实际在这份工程中并没有找到对应的代码,所以不是清楚是怎么一步步调用<code class="language-plaintext highlighter-rouge">kconfiglsp.py</code>的。怀疑这部分的JS代码是直接内嵌在了nRF的整体扩展中,所以单独只安装一个kconfig插件是无法直接显示kconfig的结果的,必须要配合大插件包才能正常启动</p>

<p>这里再把<code class="language-plaintext highlighter-rouge">nordic-semiconductor.nrf-connect-extension-pack-2024.9.5</code>的包拿出来分析一下,看看具体内容是怎么来的</p>

<p>看了一下pack的package.json,发现他好像只是一个整合包,实际并没有什么具体的代码</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nrf-connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"displayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nRF Connect for VS Code Extension Pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Recommended extensions for development with the nRF Connect SDK"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisher"</span><span class="p">:</span><span class="w"> </span><span class="s2">"nordic-semiconductor"</span><span class="p">,</span><span class="w"> </span><span class="nl">"homepage"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/index.html"</span><span class="p">,</span><span class="w"> </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"images/icon.png"</span><span class="p">,</span><span class="w"> </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2024.9.5"</span><span class="p">,</span><span class="w"> </span><span class="nl">"bugs"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://devzone.nordicsemi.com"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"engines"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.80.0"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"categories"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"Extension Packs"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"extensionPack"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-connect"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-terminal"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-devicetree"</span><span class="p">,</span><span class="w"> </span><span class="s2">"nordic-semiconductor.nrf-kconfig"</span><span class="p">,</span><span class="w"> </span><span class="s2">"ms-vscode.cpptools"</span><span class="p">,</span><span class="w"> </span><span class="s2">"trond-snekvik.gnu-mapfiles"</span><span class="p">,</span><span class="w"> </span><span class="s2">"twxs.cmake"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./dist/extension.js"</span><span class="p">,</span><span class="w"> </span><span class="nl">"activationEvents"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"*"</span><span class="w"> </span><span class="p">],</span><span class="w"> </span><span class="nl">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"vscode:prepublish"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run build – –notypecheck"</span><span class="p">,</span><span class="w"> </span><span class="nl">"build"</span><span class="p">:</span><span class="w"> </span><span class="s2">"rimraf dist &amp;&amp; tsx ./scripts/build.js –production"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lint"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"lintfix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ../scripts/lint.ts –fix connect-extension-pack"</span><span class="p">,</span><span class="w"> </span><span class="nl">"copyright"</span><span class="p">:</span><span class="w"> </span><span class="s2">"npm run –prefix ../ copyright"</span><span class="p">,</span><span class="w"> </span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tsx ./scripts/build.js –watch"</span><span class="w"> </span><span class="p">},</span><span class="w"> </span><span class="nl">"__metadata"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"50d64e34-3ee8-4e0c-b79d-97a3eab4581d"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherId"</span><span class="p">:</span><span class="w"> </span><span class="s2">"c9e8387e-6a8a-4101-bcc0-52d8bf37ca83"</span><span class="p">,</span><span class="w"> </span><span class="nl">"publisherDisplayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nordic Semiconductor"</span><span class="p">,</span><span class="w"> </span><span class="nl">"targetPlatform"</span><span class="p">:</span><span class="w"> </span><span class="s2">"undefined"</span><span class="p">,</span><span class="w"> </span><span class="nl">"isApplicationScoped"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"isPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"hasPreReleaseVersion"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"installedTimestamp"</span><span class="p">:</span><span class="w"> </span><span class="mi">1734424781886</span><span class="p">,</span><span class="w"> </span><span class="nl">"pinned"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"preRelease"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> </span><span class="nl">"source"</span><span class="p">:</span><span class="w"> </span><span class="s2">"gallery"</span><span class="p">,</span><span class="w"> </span><span class="nl">"size"</span><span class="p">:</span><span class="w"> </span><span class="mi">77034</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div>

<p>再仔细看一下extension.js的代码</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objCreate</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objProperty</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objPropertyDescriptor</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyDescriptor</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objPropertyNames</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getOwnPropertyNames</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">objGet</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">getPrototypeOf</span><span class="p">,</span> <span class="nx">objHasOP</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">t</span> <span class="k">in</span> <span class="nx">e</span><span class="p">)</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">t</span><span class="p">,</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="nx">e</span><span class="p">[</span><span class="nx">t</span><span class="p">],</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">})</span> <span class="p">},</span> <span class="nx">d</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">,</span> <span class="nx">t</span><span class="p">,</span> <span class="nx">s</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">e</span> <span class="o">&amp;&amp;</span> <span class="k">typeof</span> <span class="nx">e</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span> <span class="o">||</span> <span class="k">typeof</span> <span class="nx">e</span> <span class="o">==</span> <span class="dl">"</span><span class="s2">function</span><span class="dl">"</span><span class="p">)</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="k">of</span> <span class="nx">objPropertyNames</span><span class="p">(</span><span class="nx">e</span><span class="p">))</span> <span class="o">!</span><span class="nx">objHasOP</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nx">i</span> <span class="o">!==</span> <span class="nx">t</span> <span class="o">&amp;&amp;</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">i</span><span class="p">,</span> <span class="p">{</span> <span class="na">get</span><span class="p">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">e</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="p">(</span><span class="nx">s</span> <span class="o">=</span> <span class="nx">objPropertyDescriptor</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="nx">i</span><span class="p">))</span> <span class="o">||</span> <span class="nx">s</span><span class="p">.</span><span class="nx">enumerable</span> <span class="p">});</span> <span class="k">return</span> <span class="nx">n</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">f</span> <span class="o">=</span> <span class="p">(</span><span class="nx">n</span><span class="p">,</span> <span class="nx">e</span><span class="p">,</span> <span class="nx">t</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span><span class="nx">t</span> <span class="o">=</span> <span class="nx">n</span> <span class="o">!=</span> <span class="kc">null</span> <span class="p">?</span> <span class="nx">objCreate</span><span class="p">(</span><span class="nx">objGet</span><span class="p">(</span><span class="nx">n</span><span class="p">))</span> <span class="p">:</span> <span class="p">{},</span> <span class="nx">d</span><span class="p">(</span><span class="nx">e</span> <span class="o">||</span> <span class="o">!</span><span class="nx">n</span> <span class="o">||</span> <span class="o">!</span><span class="nx">n</span><span class="p">.</span><span class="nx">__esModule</span> <span class="p">?</span> <span class="nx">objProperty</span><span class="p">(</span><span class="nx">t</span><span class="p">,</span> <span class="dl">"</span><span class="s2">default</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="nx">n</span><span class="p">,</span> <span class="na">enumerable</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">})</span> <span class="p">:</span> <span class="nx">t</span><span class="p">,</span> <span class="nx">n</span><span class="p">)),</span> <span class="nx">C</span> <span class="o">=</span> <span class="nx">n</span> <span class="o">=&gt;</span> <span class="nx">d</span><span class="p">(</span><span class="nx">objProperty</span><span class="p">({},</span> <span class="dl">"</span><span class="s2">__esModule</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="o">!</span><span class="mi">0</span> <span class="p">}),</span> <span class="nx">n</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">y</span> <span class="o">=</span> <span class="p">{};</span> </code></pre></div></div>

<p>使用 b函数将属性从 <code class="language-plaintext highlighter-rouge">source</code> 对象定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不是直接使用 <code class="language-plaintext highlighter-rouge">source</code> 对象,有几个好处:</p>

<p><strong>封装和模块化</strong>:</p>

<ul> <li>通过将属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,可以更好地封装和模块化代码。这样可以避免直接暴露 <code class="language-plaintext highlighter-rouge">source</code> 对象的内部实现细节。</li> </ul>

<p><strong>动态扩展对象</strong>:</p>

<ul> <li>可以动态地将属性添加到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不需要修改 <code class="language-plaintext highlighter-rouge">source</code> 对象。这在需要扩展对象功能时非常有用。</li> </ul>

<p><strong>控制属性访问</strong>:</p>

<ul> <li>使用 <code class="language-plaintext highlighter-rouge">Object.defineProperty</code> 可以精确控制属性的行为,例如设置属性为只读、可枚举等。在这个例子中,属性被定义为 getter 函数,并且是可枚举的。</li> </ul>

<p><strong>避免命名冲突</strong>:</p>

<ul> <li>如果直接使用 <code class="language-plaintext highlighter-rouge">source</code> 对象,可能会与现有对象的属性发生命名冲突。通过将属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,可以避免这种冲突。</li> </ul>

<p><strong>灵活性</strong>:</p>

<ul> <li>可以根据需要选择性地将某些属性定义到 <code class="language-plaintext highlighter-rouge">target</code> 对象上,而不是将整个 <code class="language-plaintext highlighter-rouge">source</code> 对象暴露出来。</li> </ul>

<p>就是通过Object.defineProperty实现一个代理类,后续代码基本都是围绕这个目标来做的。</p>

<p>最终把一些按钮或者触发器注册进了VSCode,这些id就对应上了具体的界面按钮和后续的事件触发机制</p>

<p>可以看到就是在这里把kconfig相关的指令注册进去了,前面报的kconfig不存在,也是因为connect没有启动,这里没有加载</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250103120131658.png" alt="image-20250103120131588" /></p>

<p>所以还是得去看connect部分的代码</p>

<h3 id="nordic-semiconductornrf-connect">nordic-semiconductor.nrf-connect</h3>

<p>查看extension.js,格式化以后发现这个js的行数有12w,这就有点离谱了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250103163028743.png" alt="image-20250103163028633" /></p>

<p>copilot直接放弃工作了,让他读一下直接不会了</p>

<p>看了一下打包文件夹是dist,应该是套在某个框架上写完以后重新打包成了一个.js,所以这个内容就很难反向查了</p>

<h2 id="summary">Summary</h2>

<p>还是重写一个更快,这里能借鉴的只有Kconfig LSP这个部分了</p> </description> Thu, 02 Jan 2025 00:00:00 +0800 https://elmagnifico.tech/2025/01/02/VSCode-Kconfig/</link> https://elmagnifico.tech/2025/01/02/VSCode-Kconfig/

    <category>build</category>
    
    
  </item>

  <item>
    <title>Zephyr build 架构</title>
    <description>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;

<p>Zephyr 工程的整个构建体系是怎样的,这里做一个具体的分析和学习</p>

<p>最近小米的Vele 也开源了,不过由于缺少文档,而且架构非常庞大,光是仓库就传了两三百个,想看明白不太容易,但是基础的CMake、Kconfig等等全都有。</p>

<h2 id="cmake">CMake</h2>

<ol> <li><strong>创建构建目录</strong>:保持源代码目录整洁。</li> <li><strong>使用 CMake 生成构建文件</strong>:配置项目并生成适合平台的构建文件。</li> <li><strong>编译和构建</strong>:使用生成的构建文件执行编译和构建。</li> <li><strong>清理构建文件</strong>:删除中间文件和目标文件。</li> <li><strong>重新配置和构建</strong>:处理项目设置的更改。</li> </ol>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191052534.png" alt="img" /></p>

<p>CMake 推荐使用 <strong>“Out-of-source”</strong> 构建方式,即将构建文件放在源代码目录之外的独立目录中</p>

<h3 id="cmake例程">CMake例程</h3>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191134890.png" alt="image-20241219113430856" /></p>

<p>一个比较简单的CMake例程</p>

<blockquote> <p>https://github.com/elmagnificogi/MyTools/tree/master/CMake/MyProject1</p> </blockquote>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include &lt;iostream&gt; </span> <span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"Hello, CMake!"</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </code></pre></div></div>

<p>cpp文件</p>

<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifndef MAIN_H #define MAIN_H </span> <span class="c1">// Declarations of module functions</span>

<span class="cp">#endif // MAIN_H </span></code></pre></div></div>

<p>头文件</p>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># 指定最低 CMake 版本</span> <span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.10<span class="p">)</span>
<span class="c1"># 定义项目名称和版本</span> <span class="nb">project</span><span class="p">(</span>MyProject1 VERSION 1.0<span class="p">)</span>

<span class="c1"># 设置 C++ 标准</span> <span class="nb">set</span><span class="p">(</span>CMAKE_CXX_STANDARD 11<span class="p">)</span> <span class="nb">set</span><span class="p">(</span>CMAKE_CXX_STANDARD_REQUIRED ON<span class="p">)</span>

<span class="nb">include_directories</span><span class="p">(</span><span class="si">${</span><span class="nv">PROJECT_SOURCE_DIR</span><span class="si">}</span>/include<span class="p">)</span>

<span class="c1"># 创建可执行文件目标</span> <span class="nb">add_executable</span><span class="p">(</span>MyExecutable <span class="si">${</span><span class="nv">PROJECT_SOURCE_DIR</span><span class="si">}</span>/src/main.cpp<span class="p">)</span> </code></pre></div></div>

<p>比较简单的CMakeLists.txt定义</p>

<p>新建build目录,将源码和编译路径分开</p>

<p>生成一个MinGW的Makefiles文件</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\CMake\bin\cmake.exe .. -G "MinGW Makefiles" </code></pre></div></div>

<ul> <li>不指定的话,默认是VS的工程文件</li> </ul>

<p>编译</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mingw32-make.exe </code></pre></div></div>

<p>测试</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>I:\cmake\CMakeTutorialCN\MyProject\build\src&gt;MyExecutable.exe Hello, CMake! </code></pre></div></div>

<p>稍微复杂一些,但是包含了库,移除了测试库,windows下拉不到这个测试库</p>

<blockquote> <p>https://github.com/elmagnificogi/MyTools/tree/master/CMake/MyProject0</p> </blockquote>

<p>CMake确实稍微步骤多了一些,组织编排就要跑一会,还是这么简单的工程</p>

<h2 id="基于nrf-untiled例程分析">基于nRF untiled例程分析</h2>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.20.0<span class="p">)</span> <span class="c1"># 寻找依赖包Zephyr</span> <span class="c1"># $ENV{Zephyr_BASE} 设置了 Zephyr 的根目录路径</span> <span class="nb">find_package</span><span class="p">(</span>Zephyr REQUIRED HINTS $ENV{Zephyr_BASE}<span class="p">)</span>

<span class="nb">project</span><span class="p">(</span>untitled<span class="p">)</span>

<span class="c1"># target_sources 指令用于为 CMake 目标添加源文件。这种方法的优点在于它可以将源文件添加到目标中,并且可以在 CMake 的生成阶段自动更新。</span> <span class="c1"># target: 要添加源文件的目标,通常是通过 add_executable 或 add_library 定义的目标。</span> <span class="c1"># INTERFACE: 指定这些源文件对其他目标是可见的,但不会包含在目标本身的构建中。这通常用于接口库。</span> <span class="c1"># PUBLIC: 指定这些源文件对其他链接到该目标的目标是可见的,同时也包含在目标本身的构建中。</span> <span class="c1"># PRIVATE: 指定这些源文件仅对目标本身可见,不会传播到链接到该目标的其他目标。</span> <span class="nf">target_sources</span><span class="p">(</span>app PRIVATE src/main.c<span class="p">)</span> </code></pre></div></div>

<p>编译时执行的 <code class="language-plaintext highlighter-rouge">build.py</code> 脚本中引用了 <code class="language-plaintext highlighter-rouge">Zephyr\scripts\west_commands\Zephyr_ext_common.py</code>,在这个脚本中设置了 <strong>Zephyr_BASE</strong> 的宏。</p>

<ul> <li>宏的值被设置为了之前sdk安装路径,比如I:\ncs\v2.7.0\Zephyr</li> </ul>

<p>untiled内容比较少,还是需要去看Zephyr内的CMake</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191553570.png" alt="image-20241219155330509" /></p>

<p>先看一眼nRF的SDK包构成</p>

<ul> <li>west 是Zephyr的生成工具</li> <li>modules里面是hal、lab库、文件系统、加密等等模块</li> <li>test测试库</li> <li>Zephyr的主要内容</li> </ul>

<p>Zephyr的包主要存储在这里,通过上面的CMake搜索到这里</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412191604069.png" alt="image-20241219160421036" /></p>

<p>Zephyr的CMake主要调用逻辑</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Zephyr/share/Zephyr-package/cmake/ZephyrConfigVersion.cmake Zephyr/share/Zephyr-package/cmake/Zephyr_package_search.cmake Zephyr/cmake/modules/version.cmake Zephyr/share/Zephyr-package/cmake/ZephyrConfig.cmake </code></pre></div></div>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20241219163220232.jpeg" alt="img" /></p>

<p>Zephyr主要是两个阶段,配置阶段、编译阶段</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/build-config-phase.svg" alt="Zephyr’s build configuration phase" /></p>

<p>根据这个图来说,最终Kconfig生成了autoconf.h,同时持久化了一个.config配置方便后续使用</p>

<p>Zephyr同时也生成了一个设备树,这个概念基本和Linux一样,都是通过设备树来注册驱动进去的,类似的设备树也有同样的一个.h文件。</p>

<p>最终这部分生成文件和产生的Makefile一起进行最后的编译,生成对应固件</p>

<h2 id="自定义kconfig">自定义Kconfig</h2>

<p>在untiled下面新建Kconfig文件,输入以下内容</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menu "Test Params setting" config TEST_ENABLE bool "Enable test work" default n help Will print debug information if enable.

config TEST_SHOW_STRING string "The show string info" default "Test 123"

config TEST_SHOW_INT int "The show int info" range 0 255 default 123

config TEST_TOP_ENABLE bool "Test Top Func" default n help Function Test Top

config TEST_SUB_0_ENABLE bool "Test Sub 0 Func" default n help Function Test Sub 0

config TEST_SUB_1_ENABLE bool "Test Sub 1 Func" default n depends on TEST_TOP_ENABLE help Function Test Sub 1

config TEST_SHOW_SUB_INT int default 456 if TEST_SUB_0_ENABLE &amp;&amp; TEST_SUB_1_ENABLE default 123

endmenu

注意一定要引用这个,不然加不进去,并且CMake不了

source "Kconfig.zephyr" </code></pre></div></div>

<p>重新build一下,然后打开Kconfig GUI就能看到对应显示了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/20250102155527462.png" alt="image-20250102155527389" /></p>

<p>这里比较麻烦的地方就是这个工程每次重新打开,这个GUI就打不开了,必须要点开nRF Connect才能重新打开,非常难受</p>

<h2 id="summary">Summary</h2>

<p>工程越大越复杂,还是希望能稍微控制一下复杂度</p>

<p>参考这个教程,但是这个教程写的一坨屎,为什么还会有人点星</p>

<blockquote> <p>https://github.com/shendeguize/CMakeTutorialCN</p> </blockquote>

<h2 id="quote">Quote</h2>

<blockquote> <p>https://blog.csdn.net/qq_37805392/article/details/141337664</p>

<p>https://zhuanlan.zhihu.com/p/705640074</p>

<p>https://docs.Zephyrproject.org/latest/build/index.html</p>

<p>https://blog.csdn.net/My_CSDN_IT/article/details/118180074</p>

<p>https://lgl88911.github.io/2020/03/08/Zephyr%E6%9E%84%E5%BB%BA%E8%BF%87%E7%A8%8B%E7%AE%80%E8%BF%B0/</p>

<p>https://www.cnblogs.com/jayant97/articles/17794813.html</p> </blockquote> </description> Fri, 20 Dec 2024 00:00:00 +0800 https://elmagnifico.tech/2024/12/20/Zephyr-Build/</link> https://elmagnifico.tech/2024/12/20/Zephyr-Build/

    <category>build</category>
    
    
  </item>

  <item>
    <title>配置管理工具之kconfig</title>
    <description>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;

<p>绕了这么大一圈,似乎只有kconfig是比较成熟的,能与之相媲美的管理工具很少</p>

<h2 id="kconfig">Kconfig</h2>

<h3 id="安装">安装</h3>

<p>在windows下使用Kconfig,至少得有python,否则界面等内容无法正常显示</p>

<p>python需要先安装这几个包</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python -m pip install windows-curses python -m pip install kconfiglib </code></pre></div></div>

<p>测试安装,可以正常显示命令</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menuconfig -h </code></pre></div></div>

<h3 id="测试">测试</h3>

<p>参考工程sample_1</p>

<blockquote> <p>https://github.com/bobwenstudy/test_kconfig_system</p> </blockquote>

<p>编译所有</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make all </code></pre></div></div>

<p>修改配置</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>menuconfig </code></pre></div></div>

<p>运行<code class="language-plaintext highlighter-rouge">main.exe</code>就能看到结果了</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>hello, world CONFIG_TEST_ENABLE CONFIG_TEST_SHOW_STRING: Test 123 CONFIG_TEST_SHOW_INT: 123 CONFIG_TEST_SUB_0_ENABLE CONFIG_TEST_SHOW_SUB_INT: 123 </code></pre></div></div>

<p>可能会报错,这个是因为curses库他需要终端执行,但是不识别你当前使用的终端,换一个就行了,比如cmd</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make 提示Redirection is not supported. </code></pre></div></div>

<p>这个例子里还只有一种config,还不能支持多config配置,可以看到主要就只能生成一个autoconfig.h</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">all</span><span class="o">:</span> <span class="nf">main.o</span> gcc main.o <span class="nt">-o</span> main <span class="nl">main.o</span><span class="o">:</span> <span class="nf">main.c autoconfig.h</span> gcc main.c <span class="nt">-c</span> <span class="nt">-o</span> main.o <span class="nl">clean</span><span class="o">:</span> del main.o main.exe

<span class="nl">autoconfig.h</span><span class="o">:</span><span class="nf">.config</span> python ../scripts/kconfig.py Kconfig .config autoconfig.h log.txt .config <span class="nl">.config</span><span class="o">:</span> menuconfig <span class="nl">menuconfig</span><span class="o">:</span> menuconfig </code></pre></div></div>

<h3 id="多config">多config</h3>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 默认编译test1 </span><span class="nv">APP</span> <span class="o">?=</span> app/test1 <span class="nv">OUTPUT_PATH</span> <span class="o">:=</span> output

<span class="nv">INCLUDE_PATH</span> <span class="o">:=</span> <span class="nt">-I</span><span class="nv">$(APP)</span> <span class="nt">-Idriver</span> <span class="nt">-I</span><span class="nv">$(OUTPUT_PATH)</span>

<span class="c"># 主要用户定义的配置是在每个app下的,这里也可以单独提取一个对应的版本目录来做

define user .config setting

</span><span class="nv">USER_CONFIG_SET</span> <span class="o">:=</span> <span class="nv">USER_CONFIG_SET</span> <span class="o">+=</span> <span class="nv">$(APP)</span>/prj.conf

<span class="c"># define menuconfig .config path </span><span class="nv">DOTCONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/.config

<span class="c"># 用户文件追踪

define user merged path

</span><span class="nv">USER_RECORD_CONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/user_record.conf

<span class="c"># define autoconfig.h path </span><span class="nv">AUTOCONFIG_H</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/autoconfig.h

<span class="c">#define Kconfig path </span><span class="nv">KCONFIG_ROOT_PATH</span> <span class="o">:=</span> Kconfig

<span class="c">#For windows work. </span><span class="nv">FIXPATH</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">subst</span> /,<span class="se">\,</span><span class="nv">$1</span><span class="nf">)</span>

<span class="nl">all</span><span class="o">:</span> <span class="nf">$(APP)/main.o driver/driver_test.o</span> gcc <span class="nv">$^</span> <span class="nt">-o</span> <span class="nv">$(OUTPUT_PATH)</span>/main.exe <span class="nl">$(APP)/main.o</span><span class="o">:</span> <span class="nf">$(APP)/main.c</span> gcc <span class="nv">$&lt;</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span> <span class="nl">driver/driver_test.o</span><span class="o">:</span> <span class="nf">driver/driver_test.c $(AUTOCONFIG_H)</span> gcc <span class="nv">$&lt;</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">clean</span><span class="o">:</span> del /q /s <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$(APP)</span>/main.o driver/driver_test.o <span class="nv">$(OUTPUT_PATH)</span><span class="nf">)</span>

<span class="c"># 根据对应的autoconfig的目录来生成 </span><span class="nl">$(AUTOCONFIG_H)</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> python ../scripts/kconfig.py <span class="nv">$(KCONFIG_ROOT_PATH)</span> <span class="nv">$(DOTCONFIG_PATH)</span> <span class="nv">$(AUTOCONFIG_H)</span> <span class="nv">$(OUTPUT_PATH)</span>/log.txt <span class="nv">$(DOTCONFIG_PATH)</span>

<span class="c"># 从用户的项目路径里拿刀用户定义的config </span><span class="nl">$(USER_RECORD_CONFIG_PATH)</span><span class="o">:</span> <span class="nf">$(USER_CONFIG_SET)</span> <span class="p">@</span><span class="nb">echo </span>Using user config. <span class="c"># create user_record.conf to record current setting. </span> <span class="err">@copy</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$^)</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$@)</span> <span class="c"># create .config by user config setting. </span> <span class="err">python</span> <span class="err">../scripts/kconfig.py</span> <span class="err">–handwritten-input-configs</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> <span class="err">$(DOTCONFIG_PATH)</span> <span class="err">$(AUTOCONFIG_H)</span> <span class="err">$(OUTPUT_PATH)/log.txt</span> <span class="err">$(USER_CONFIG_SET)</span>

<span class="k">export </span><span class="nv">KCONFIG_CONFIG</span><span class="o">=</span><span class="nv">$(DOTCONFIG_PATH)</span> <span class="nl">$(DOTCONFIG_PATH)</span><span class="o">:</span><span class="nf">$(USER_RECORD_CONFIG_PATH)</span> <span class="p">@</span><span class="nb">echo</span> .config updated

<span class="nl">menuconfig</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> <span class="c"># set KCONFIG_CONFIG=$(DOTCONFIG_PATH) </span> <span class="err">menuconfig</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> </code></pre></div></div>

<p>对应这样的配置就可以使用下面的方式来独立生成每个APP的配置,这样就可以同时维护多个版本,而要多一个APP,就再加一个配置就行了</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make APP=app\test1 make APP=app\test2 </code></pre></div></div>

<p>上面这个配置还少了一点,生成的都是同一个文件,同一个目录,互相覆盖,其实可以参考OpenWRT,每次不同配置生成都在不同文件夹里,然后对应文件名就更好了</p>

<h3 id="再次改进">再次改进</h3>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">APP</span> <span class="o">?=</span> app/test1 <span class="nv">OUTPUT_PATH</span> <span class="o">:=</span> output/<span class="nv">$(APP)</span> <span class="nv">TARGET</span> <span class="o">:=</span> main

<span class="nv">INCLUDE_PATH</span> <span class="o">:=</span> <span class="nt">-I</span><span class="nv">$(APP)</span> <span class="nt">-Idriver</span> <span class="nt">-I</span><span class="nv">$(OUTPUT_PATH)</span>

<span class="c"># define user .config setting </span><span class="nv">USER_CONFIG_SET</span> <span class="o">:=</span> <span class="nv">USER_CONFIG_SET</span> <span class="o">+=</span> <span class="nv">$(APP)</span>/prj.conf

<span class="c"># define menuconfig .config path </span><span class="nv">DOTCONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/.config

<span class="c"># define user merged path </span><span class="nv">USER_RECORD_CONFIG_PATH</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/user_record.conf

<span class="c"># define autoconfig.h path </span><span class="nv">AUTOCONFIG_H</span> <span class="o">:=</span> <span class="nv">$(OUTPUT_PATH)</span>/autoconfig.h

<span class="c">#define Kconfig path </span><span class="nv">KCONFIG_ROOT_PATH</span> <span class="o">:=</span> Kconfig

<span class="c">#For windows work. </span><span class="nv">FIXPATH</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">subst</span> /,<span class="se">\,</span><span class="nv">$1</span><span class="nf">)</span>

<span class="nl">all</span><span class="o">:</span> <span class="nf">$(APP)/main.o driver/driver_test.o</span> gcc <span class="nv">$^</span> <span class="nt">-o</span> <span class="nv">$(OUTPUT_PATH)</span>/<span class="nv">$(TARGET)</span>.exe

<span class="nl">$(APP)/main.o</span><span class="o">:</span> <span class="nf">$(APP)/main.c | $(OUTPUT_PATH)</span> gcc <span class="nv">$&lt;</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">driver/driver_test.o</span><span class="o">:</span> <span class="nf">driver/driver_test.c $(AUTOCONFIG_H)</span> gcc <span class="nv">$&lt;</span> <span class="nv">$(INCLUDE_PATH)</span> <span class="nt">-c</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="nl">$(OUTPUT_PATH)</span><span class="o">:</span> <span class="p">@</span><span class="nb">mkdir</span> <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$@</span><span class="nf">)</span>

<span class="nl">clean</span><span class="o">:</span> del /q /s <span class="nf">$(</span><span class="nb">call</span> FIXPATH, <span class="nv">$(APP)</span>/main.o driver/driver_test.o <span class="nv">$(OUTPUT_PATH)</span><span class="nf">)</span>

<span class="nl">$(AUTOCONFIG_H)</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> python ../scripts/kconfig.py <span class="nv">$(KCONFIG_ROOT_PATH)</span> <span class="nv">$(DOTCONFIG_PATH)</span> <span class="nv">$(AUTOCONFIG_H)</span> <span class="nv">$(OUTPUT_PATH)</span>/log.txt <span class="nv">$(DOTCONFIG_PATH)</span>

<span class="nl">$(USER_RECORD_CONFIG_PATH)</span><span class="o">:</span> <span class="nf">$(USER_CONFIG_SET)</span> <span class="p">@</span><span class="nb">echo </span>Using user config. <span class="c"># create user_record.conf to record current setting. </span> <span class="err">@copy</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$^)</span> <span class="err">$(call</span> <span class="err">FIXPATH,</span> <span class="err">$@)</span> <span class="c"># create .config by user config setting. </span> <span class="err">python</span> <span class="err">../scripts/kconfig.py</span> <span class="err">–handwritten-input-configs</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> <span class="err">$(DOTCONFIG_PATH)</span> <span class="err">$(AUTOCONFIG_H)</span> <span class="err">$(OUTPUT_PATH)/log.txt</span> <span class="err">$(USER_CONFIG_SET)</span>

<span class="k">export </span><span class="nv">KCONFIG_CONFIG</span><span class="o">=</span><span class="nv">$(DOTCONFIG_PATH)</span> <span class="nl">$(DOTCONFIG_PATH)</span><span class="o">:</span><span class="nf">$(USER_RECORD_CONFIG_PATH)</span> <span class="p">@</span><span class="nb">echo</span> .config updated

<span class="nl">menuconfig</span><span class="o">:</span><span class="nf">$(DOTCONFIG_PATH)</span> <span class="c"># set KCONFIG_CONFIG=$(DOTCONFIG_PATH) </span> <span class="err">menuconfig</span> <span class="err">$(KCONFIG_ROOT_PATH)</span> </code></pre></div></div>

<p>稍微修改一下,增加路径自动创建,这样后续就可以不同配置生成到不同目录中,不会互相覆盖了。同时也自动适配生成的文件名和路径分割符</p>

<p>当然这个Makefile还是有一些缺点,比如所有要编译的.c和.o都得一个个加,还是有点傻</p>

<h3 id="可视化">可视化</h3>

<p>Kconfig发展了这么久,难道没有人想着用VScode来做一下可视化嘛,事实上是有的,但是也都有限,不是很通用</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061636312.png" alt="image-20241206163640263" /></p>

<p>第一个插件只能提供kconfig的语法高亮,不能解决显示问题</p>

<p>第二个就是nRF Kconfig,下面的Konfig for Zephyr Project也停止开发,转而使用nRF Kconfig了</p>

<blockquote> <p>https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-kconfig</p> </blockquote>

<p>nRF Kconfig想要用起来还稍微有点麻烦,直接启动不了,所以单独开个段落说明如何使用。</p>

<p>至于非VScode的版本,基本上大部分都是依赖kconfiglib的python库完成显示,或者是结合一下TUI、Tkinter等比较轻量化的框架实现,而整体交互逻辑依然离不开比较原始的上下左右菜单式的</p>

<blockquote> <p>https://github.com/ulfalizer/Kconfiglib</p>

<p>https://github.com/cuinixam/kspl</p>

<p>https://github.com/CoryXie/SConf</p> </blockquote>

<h4 id="esp-idf">ESP-IDF</h4>

<p>其他Kconfig,比如ESP-IDF,就可以很好的体现esp的kconfig内容</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061638409.gif" alt="nRF Kconfig GUI" /></p>

<p>但是ESP是专用的,并不适用于其他开发环节或者板子,而且体积很大</p>

<h2 id="nrf-sdk-demo">nRF SDK Demo</h2>

<h3 id="demo体验">Demo体验</h3>

<p>由于要启动kconfig必须要配置完工程才能启动,所以记录一下nRF SDK是怎么启动、编译的</p>

<ul> <li>nRF也从SES逃离了,投入VSCode的怀抱了,我SES反馈的问题,SES官方就改了一个,其他的问题都视而不见,无语</li> </ul>

<p>首先安装nRF Connect Extendtion Pack</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171704070.png" alt="image-20241217170434968" /></p>

<p>其实相当于是插件包,我细看了一下,基本上需要的开发环境他都给你打包整合好了,设备树、Kconfig、Cmake、调试、Flash等等工具都有,相当于就是一个IDE了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171707735.png" alt="image-20241217170753692" /></p>

<p>选择一个工具链,然后等安装完成,这里直接选最新的就行了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171713349.png" alt="image-20241217171334285" /></p>

<p>继续安装SDK,选择一个路径安装,回车后等待完成</p>

<ul> <li>SDK都有点大,会拉很多Tag进来,最好提前准备好翻墙相关内容</li> <li>2.8.0的SDK貌似拉不下来?,所以切换到2.7.0才正常,对应前一步的toolchains也需要调整到2.7.0</li> </ul>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171758016.png" alt="image-20241217175825913" /></p>

<p>新建一个应用,然后添加编译配置,选择板子,构建配置,开始编译</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171844334.png" alt="image-20241217184447275" /></p>

<p>编译完成,但是注意一下新工程里面没有Kconfig,但是ACtion里已经可以显示Kconfig UI了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412171846273.png" alt="image-20241217184632182" /></p>

<h3 id="untitled">untitled</h3>

<p>经过测试nRF的Kconfig GUI无法直接打开前面的sample_2工程,显示非nRF工程</p>

<p>这里详细分析一下untitled的默认工程</p>

<p>untitled一共三个文件</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mian.c CMakeLists.txt prj.conf </code></pre></div></div>

<p>prj.conf中直接是空白的,main中也没啥特殊内容,主要就是CMakeLists可以被识别?</p>

<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cmake_minimum_required</span><span class="p">(</span>VERSION 3.20.0<span class="p">)</span> <span class="nb">find_package</span><span class="p">(</span>Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}<span class="p">)</span>

<span class="nb">project</span><span class="p">(</span>untitled<span class="p">)</span>

<span class="nf">target_sources</span><span class="p">(</span>app PRIVATE src/main.c<span class="p">)</span> </code></pre></div></div>

<p>实际CMakeLists中的内容也非常少</p>

<p>实际把prj.conf直接复制到sample_2中,Kconfig就可以打开了,但是发现打开的还是untitled的config,而不是sample_2的</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412181528609.png" alt="image-20241218152800544" /></p>

<p>关掉其他文件,再打开GUI,就无法启动了。</p>

<p>这里绑定有点深,必须CMakeLists,必须要在build以后才能用GUI,否则无法直接打开GUI界面</p>

<p>而添加Build Config以后,Build文件夹中的内容非常复杂,非常多内容</p>

<h3 id="nordic-semiconductornrf-kconfig">nordic-semiconductor.nrf-kconfig</h3>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412181732021.png" alt="image-20241218173215879" /></p>

<p>看了一眼nRF Kconfig,源码竟然还是python,也就是跑这个必须有对应的python环境,否则还跑不起来,怪不得那个工具链安装好久,原来在下python。python本质上还是调用kconfiglib库来完成kconfig的解析,最后再转成数据送给VS 前端去加载显示</p>

<p>目前看起来源码没有做加密或者混淆,所以要改一个非nRF强关联的版本,应该是比较容易的</p>

<h2 id="vscode-kconfig">vscode-kconfig</h2>

<p>回过头再看Zephyr的kconfig,这个竟然是开源项目,那么只需要搞定他的入口,就可以显示任何kconfig了</p>

<blockquote> <p>https://github.com/trond-snekvik/vscode-kconfig</p> </blockquote>

<p>后续计划一下改出来一个带UI的通用版本</p>

<h2 id="summary">Summary</h2>

<p>目前看只有Zephyr和nRF的Kconfig结合比较深,后续可能还会详细分析一下Zephyr的整体架构是什么样的,整个build是怎么串起来的</p>

<h2 id="quote">Quote</h2>

<blockquote> <p>https://github.com/bobwenstudy/test_kconfig_system</p>

<p>https://stackoverflow.com/questions/68748824/how-to-fix-redirection-is-not-supported</p>

<p>https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/get_started/install.html</p>

<p>https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/installation/install_ncs.html</p> </blockquote>

</description> Wed, 18 Dec 2024 00:00:00 +0800 https://elmagnifico.tech/2024/12/18/Macro-ManagerTools3/</link> https://elmagnifico.tech/2024/12/18/Macro-ManagerTools3/

    <category>build</category>
    
    
  </item>

  <item>
    <title>QEMU模拟运行FreeRTOS</title>
    <description>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;

<p>测试一下QEMU模拟运行FreeRTOS</p>

<h2 id="qemu">QEMU</h2>

<p>QEMU安装需要先安装MSYS2</p>

<p>直接下载安装</p>

<blockquote> <p>https://www.msys2.org/</p> </blockquote>

<p>安装完成以后,QEMU使用pacman包进行安装</p>

<blockquote> <p>https://www.qemu.org/download/#windows</p> </blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pacman -S mingw-w64-x86_64-qemu </code></pre></div></div>

<p>添加新的环境变量,把刚才安装的路径加进去</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\msys64\mingw64\bin </code></pre></div></div>

<p>查看版本,显示正确</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -version QEMU emulator version 9.1.1 Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers </code></pre></div></div>

<h2 id="freertos">FreeRTOS</h2>

<p>选择FreeRTOS 202212.01版本下载,包含所有演示项目的</p>

<blockquote> <p>https://freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/01-About-the-FreeRTOS-kernel/03-Download-freeRTOS/01-DownloadFreeRTOS</p> </blockquote>

<p>这里主要是测试使用<code class="language-plaintext highlighter-rouge">FreeRTOS/Demo/CORTEX_MPS2_QEMU_IAR_GCC/build/gcc</code>这个项目,需要至少arm-eabi-none-gcc的环境和make,否则进行不下去</p>

<p>进入到gcc目录,先编译一下</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061807331.png" alt="image-20241206180747253" /></p>

<p>正常编译完成</p>

<h2 id="测试">测试</h2>

<p>qemu启动模拟</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -kernel ./output/RTOSDemo.out -monitor none -nographic -serial stdio </code></pre></div></div>

<p>正常启动,并且task跑起来了,正在输出内容</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061917507.png" alt="image-20241206191724456" /></p>

<p>qemu这里的参数含义</p>

<ul> <li>-machine mps2-an385,这个是ARM的原型版,开发板模型</li> <li>-cpu cortex-m3,指定cpu架构</li> <li>-kernel ./output/RTOSDemo.out 指定使用的镜像文件</li> <li>-monitor none,显示是否要监控,启用的话类似串口,可以使用中断输入输出一些状态信息</li> <li>-nographic,不使用图形界面</li> <li>-serial stdio,串口通过标准终端输入和输出</li> <li>-s -S,链接调试器</li> </ul>

<h2 id="qemu-stm32">QEMU-STM32</h2>

<p>官方QEMU支持的STM32不是很多,而且用起来很麻烦,需要安装两个环境,而且很容易出问题</p>

<p>所以有人打包了QEMU,并且加入了比较多的ST的开源板子,支持的型号比较多</p>

<blockquote> <p>https://github.com/beckus/qemu_stm32</p> </blockquote>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412071552494.png" alt="image-20241207155204423" /></p>

<p>这个QEMU环境需要重新编译,耗时比较多,或者是从Ubuntu里调用,倒是不用打包成exe</p>

<p>bule bill 的工程用QEMU模拟</p>

<blockquote> <p>https://github.com/beckus/stm32_p103_demos</p> </blockquote>

<p>直接编译</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>

<p>编译完成以后每个demo下面都有对应的bin文件,可以直接拿来模拟使用</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-arm -M stm32-p103 -kernel main.bin </code></pre></div></div>

<p>同时RT-Thread,他们的IDE里也集成了专属的QEMU版本,也可以快捷完成模拟</p>

<h2 id="summary">Summary</h2>

<p>挺简单的,只是问题QEMU支持的现成芯片、板子比较少,需要自己去找对应的开发板,甚至自己开发,有点麻烦</p>

<h2 id="quote">Quote</h2>

<blockquote> <p>https://blog.csdn.net/weixin_42607526/article/details/142263258</p>

<p>https://freertos.org/zh-cn-cmn-s/Documentation/02-Kernel/03-Supported-devices/04-Demos/03-Emulation-and-simulation/QEMU/freertos-on-qemu-mps2-an385-model</p>

<p>https://www.cnblogs.com/asmer/p/16813129.html</p>

<p>https://www.qemu.org/docs/master/system/arm/stm32.html</p> </blockquote>

</description> Sat, 07 Dec 2024 00:00:00 +0800 https://elmagnifico.tech/2024/12/07/QEMU-FreeRTOS/</link> https://elmagnifico.tech/2024/12/07/QEMU-FreeRTOS/

    <category>FreeRTOS</category>
    
    <category>QEMU</category>
    
    
  </item>

  <item>
    <title>宏管理工具之lite-manager</title>
    <description>&lt;h2 id=&quot;foreword&quot;&gt;Foreword&lt;/h2&gt;

<p>体验一下群友的宏管理工具</p>

<h2 id="lite-manager">lite-manager</h2>

<blockquote> <p>https://gitee.com/li-shan-asked/lite-manager</p> </blockquote>

<p>群友的宏管理工具,主要在gitee上更新,github更新不及时,release文件可能不能用</p>

<p>主要是用来方便管理宏定义和生成Makefile,但是大部分设置还是要熟悉Makefile本身,你才能完成lm的配置文件编写,本质上并没有变换脚本语言或者什么的流程</p>

<p>lite-manager在这里有点类似于Kconfig</p>

<h3 id="环境">环境</h3>

<p>至少需要一个make和gun c的环境,之前系统里一直有一个MinGW32 13年的版本,gcc大概只有6,编译过不去(后来发现应该不是这个问题)</p>

<p>通过下面的方式在线安装MinGW64</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://github.com/Vuniverse0/mingwInstaller/releases/download/1.2.1/mingwInstaller.exe </code></pre></div></div>

<p>安装完成以后添加环境路径</p>

<p>如果环境里没有多的make,可以把<code class="language-plaintext highlighter-rouge">mingw32-make.exe</code>复制一个改名叫<code class="language-plaintext highlighter-rouge">make.exe</code>,不然最好还是保持原样,否则会影响到系统里其他地方的make使用</p>

<h3 id="链接脚本问题">链接脚本问题</h3>

<p>测试demo,发现无法正常运行,链接脚本无法识别-M的参数</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021915442.png" alt="image-20241202191533410" /></p>

<p>仔细看了一下Makefile,是生产的链接参数就是<code class="language-plaintext highlighter-rouge">-M</code>而不是<code class="language-plaintext highlighter-rouge">-Map</code></p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916144.png" alt="image-20241202191650116" /></p>

<p>还好lite-manager源码也是有的,修改一下生成脚本</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916464.png" alt="image-20241202191633425" /></p>

<p>再把生成的lm.exe拖到对应的测试目录下,进行测试,一切正常了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412021916406.png" alt="image-20241202191619373" /></p>

<ul> <li>如果不修改lm,make config时也显示不出来当前宏的状态</li> </ul>

<p>先生成Makefile</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./lm.exe -g Makefile -p hello </code></pre></div></div>

<p>然后可以生成config文件,并且查看宏定义情况</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make config </code></pre></div></div>

<p>编译</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make </code></pre></div></div>

<h3 id="规则">规则</h3>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412031107687.png" alt="image-20241203110700613" /></p>

<p>lm.cfg,用来定义宏的定义以及宏之间的关系</p>

<p>proj.cfg,用户定义实际想要的宏</p>

<p>.lm.mk,缓存</p>

<p>config.h,最终处理完约束条件后实际定义出来的宏</p>

<p>lm.cfg中判断条件是自上而下的,遇到冲突点,首先会关闭自身,然后文件之间是从左向右流动的</p>

<p>其中n,表示宏关闭,‘n’是正常使用n这个关键字</p>

<p>同时下面的文件编译的约束也可以同时使用宏来管控</p>

<p>多模块、多组件的工程目录</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├───build ├───subdirA | ├───mac_a.c | └───lm.cfg ├───subdirB | ├───mac_b.c | └───lm.cfg └───lm.cfg </code></pre></div></div>

<p>可以使用include直接把下面的配置加载进去</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include "subdirA/lm.cfg" include "subdirB/lm.cfg" </code></pre></div></div>

<p>lm的工程实例可以参考这里,有些技巧example里没有用到,可以看看实例是怎么跑的</p>

<blockquote> <p>https://gitee.com/li-shan-asked/ebraid</p> </blockquote>

<p>lite-manager 同时也有类似tui的交互版本,不过作者说比较老、过时,所以不推荐用了</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412031159028.png" alt="img" /></p>

<h3 id="makefile解析">Makefile解析</h3>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 自顶向下来看Makefile的设计

首先目标是hello 然后编译输出到build目录

</span><span class="nv">TARGET</span> <span class="o">:=</span> hello <span class="nv">BUILD_DIR</span> <span class="o">:=</span> build

<span class="c"># 定义一个伪指令,主要用来检测是否从lm的config中生成的

简单说基本上只要不是最终生成的目标,那都是伪指令

伪指令主要是完成一些辅助性的工作,比如clean,检测,切换配置,生成配置等等

检测是否有.lm.mk文件

</span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">check_lmmk</span> <span class="c"># wildcard自动寻找.lm.mk的文件 </span><span class="k">ifneq</span> <span class="nv">($(wildcard .lm.mk),)</span> <span class="k">-include</span><span class="sx"> .lm.mk</span> <span class="k">else</span> <span class="nl">check_lmmk</span><span class="o">:</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"Please run ‘make config’"</span> <span class="k">endif</span>

<span class="c"># 主要涉及使用的工具链,比如 arm-none-eabi-, 这里用默认gcc即可 </span><span class="nv">CC_PREFIX</span> <span class="o">?=</span> <span class="nv">CC</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>gcc <span class="c"># 汇编编译的文件来源是C++ 支持的是.S 如果要支持.s 也在这里修改 </span><span class="nv">AS</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>gcc <span class="nt">-x</span> assembler-with-cpp <span class="c"># copy命令 </span><span class="nv">CP</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>objcopy <span class="c"># 文件size section 字段分析 </span><span class="nv">SZ</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>size <span class="c"># 调试 debug的工具 </span><span class="nv">OD</span> <span class="o">=</span> <span class="nv">$(CC_PREFIX)</span>objdump <span class="c"># 生成hex文件 </span><span class="nv">HEX</span> <span class="o">=</span> <span class="nv">$(CP)</span> <span class="nt">-O</span> ihex <span class="c"># 生成bin文件 </span><span class="nv">BIN</span> <span class="o">=</span> <span class="nv">$(CP)</span> <span class="nt">-O</span> binary <span class="nt">-S</span> <span class="c"># 以上工具写的比较多,实际是为了以后适配使用,demo里可能没用到 </span> <span class="c"># 定义额外的宏、库、等路径 </span><span class="nv">CFLAGS</span> <span class="o">:=</span> <span class="nv">$(C_PATH)</span> <span class="nv">$(C_DEFINE)</span> <span class="nv">$(C_FLAG)</span> <span class="nv">$(CPP_FLAG)</span> <span class="c"># 同上,定义链接文件的来源和参数

链接时生成map文件

</span><span class="nv">LDFLAGS</span> <span class="o">:=</span> <span class="nv">$(LD_FLAG)</span> <span class="nv">$(LIB_PATH)</span> <span class="nt">-Wl</span>,-Map<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.map

<span class="c"># 这里定义生成全部的目标 </span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">all</span> <span class="nl">all</span><span class="o">:</span> <span class="nf">$(BUILD_DIR)/$(TARGET).exe elf_info</span>

<span class="c">#获取到所有要编译的目标对象,这里是从build目录中获取所有c和c++目标 </span><span class="nv">OBJECTS</span> <span class="o">=</span> <span class="nf">$(</span><span class="nb">addprefix</span> <span class="nv">$(BUILD_DIR)</span>/,<span class="nf">$(</span><span class="nb">notdir</span> <span class="nf">$(</span><span class="nb">patsubst</span> %.c, %.o, <span class="nf">$(</span><span class="nb">patsubst</span> %.cpp, %.o, <span class="nv">$(C_SOURCE)</span><span class="nf">))))</span> <span class="c"># 指定具体的.c文件的搜索路径 </span><span class="err">vpath</span> <span class="err">%.c</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(C_SOURCE)))</span> <span class="err">vpath</span> <span class="err">%.cpp</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(C_SOURCE)))</span> <span class="c">#获取到所有要编译的汇编对象 </span><span class="nv">OBJECTS</span> <span class="o">+=</span> <span class="nf">$(</span><span class="nb">addprefix</span> <span class="nv">$(BUILD_DIR)</span>/,<span class="nf">$(</span><span class="nb">notdir</span> <span class="nv">$(ASM_SOURCE:.S=.o)</span><span class="nf">))</span> <span class="err">vpath</span> <span class="err">%.S</span> <span class="err">$(sort</span> <span class="err">$(dir</span> <span class="err">$(ASM_SOURCE)))</span>

<span class="c"># 创建存储.o和.c文件依赖关系的.d文件

-MMD -MP -MF $(@:%.o=%.d) 是利用gcc自动生成依赖关系

将build路径下的所有c文件编译成.o文件

Makefile文件自身也是依赖文件,如果Makefile文件发生变动,也需要重新编译

-Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(&lt;:.c=.lst)):生成临时中间文件

-c 仅仅编译,不进行链接

$@是指这条指令的所有操作对象

$&lt;是第一个文件

</span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.c Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"CC </span><span class="nv">$&lt;</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span>&lt;:.c<span class="o">=</span>.d<span class="nf">))</span> <span class="se">\</span> <span class="nt">-Wa</span>,-a,-ad,-alms<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span>&lt;:.c<span class="o">=</span>.lst<span class="nf">))</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="c"># 同上,只不过此处仅编译c++文件 </span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.cpp Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"CC </span><span class="nv">$&lt;</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span>&lt;:.cpp<span class="o">=</span>.d<span class="nf">))</span> <span class="se">\</span> <span class="nt">-Wa</span>,-a,-ad,-alms<span class="o">=</span><span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span>&lt;:.cpp<span class="o">=</span>.lst<span class="nf">))</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="c"># 同上,此处仅编译.S的汇编文件 </span><span class="nl">$(BUILD_DIR)/%.o</span><span class="o">:</span> <span class="nf">%.S Makefile | $(BUILD_DIR)</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"AS </span><span class="nv">$&lt;</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(AS)</span> <span class="nt">-c</span> <span class="nv">$(CFLAGS)</span> <span class="nt">-MMD</span> <span class="nt">-MP</span> <span class="se">\</span> <span class="nt">-MF</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nf">$(</span><span class="nb">notdir</span> <span class="err">$</span><span class="o">(</span>&lt;:.S<span class="o">=</span>.d<span class="nf">))</span> <span class="nv">$&lt;</span> <span class="nt">-o</span> <span class="nv">$@</span>

<span class="c"># 生成exe文件 </span><span class="nl">$(BUILD_DIR)/$(TARGET).exe</span><span class="o">:</span> <span class="nf">$(OBJECTS) Makefile</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"LD </span><span class="nv">$@</span><span class="s2">"</span> <span class="p">@</span><span class="nv">$(CC)</span> <span class="nv">$(OBJECTS)</span> <span class="nv">$(LDFLAGS)</span> <span class="nt">-o</span> <span class="nv">$@</span> <span class="p">@</span><span class="nv">$(OD)</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.exe <span class="nt">-xS</span> <span class="o">&gt;</span> <span class="nv">$(BUILD_DIR)</span>/<span class="nv">$(TARGET)</span>.s <span class="nv">$@</span> <span class="p">@</span><span class="nb">echo</span> <span class="s1">’‘</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"Build Successful!"</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"ELF </span><span class="nv">$@</span><span class="s2">"</span>

<span class="nl">elf_info</span><span class="o">:</span> <span class="nf">$(BUILD_DIR)/$(TARGET).exe</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"=================================================================="</span> <span class="p">@</span><span class="nv">$(SZ)</span> <span class="nv">$&lt;</span> <span class="p">@</span><span class="nb">echo</span> <span class="s2">"=================================================================="</span>

<span class="c"># 生成build文件夹 </span><span class="nl">$(BUILD_DIR)</span><span class="o">:</span> <span class="p">@</span><span class="nb">mkdir</span> <span class="nv">$@</span>

<span class="c"># 清除指令 </span><span class="nl">.PHONY</span><span class="o">:</span> <span class="nf">config clean</span>

<span class="c"># 如果工程配置文件存在,那么就调用lm重新生成配置

这里主要是生成对应的 config.h,不是重新生成Makefile

</span><span class="nl">config</span><span class="o">:</span> <span class="nf">proj.cfg</span> <span class="p">@</span>./lm.exe <span class="nt">–projcfg</span> proj.cfg <span class="nt">–lmcfg</span> lm.cfg <span class="nt">–out</span> config.h <span class="nt">–mem</span> 50 <span class="p">@</span>./lm.exe <span class="nt">–rmdir</span> <span class="nv">$(BUILD_DIR)</span>

<span class="c"># 清空编译 </span><span class="nl">clean</span><span class="o">:</span> <span class="p">@</span>./lm.exe <span class="nt">–rmdir</span> <span class="nv">$(BUILD_DIR)</span>

</code></pre></div></div>

<h3 id="lmcfg解析">lm.cfg解析</h3>

<p>实际在hello的lm.cfg中只有一行,就是加了一个文件进去</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>SRC += hello.c </code></pre></div></div>

<p>这里指定了生成的目标是hello</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./lm.exe -g Makefile -p hello </code></pre></div></div>

<p>剩下的Makefile全都靠lm内部自动生成</p>

<p>以stm32f103的工程为例,相当于Makefile是一个通用的Makefile,而lm.cfg 相当于是一个PreMakefile,在这里直接定义Makefile要用的一些参数即可,剩下就是Makefile足够项目通用即可</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 添加link文件 </span><span class="nv">LDS</span> <span class="o">+=</span> stm32f10x_64KB_flash.ld <span class="c"># 添加汇编文件 </span><span class="nv">ASM</span> <span class="o">+=</span> startup_stm32f10x_md.s <span class="c"># 添加一些关联文件路径 </span><span class="nv">PATH</span> <span class="o">+=</span> lib/cmsis <span class="nv">PATH</span> <span class="o">+=</span> ./

<span class="c"># 添加源文件 </span><span class="nv">SRC</span> <span class="o">+=</span> stm32f10x_it.c <span class="nv">SRC</span> <span class="o">+=</span> main.c

<span class="c"># 增加宏定义 </span><span class="nv">DEFINE</span> <span class="o">+=</span> STM32F10X_HD USE_STDPERIPH_DRIVER <span class="c"># 编译参数 </span><span class="nv">CFLAG</span> <span class="o">+=</span> <span class="nt">-g</span> <span class="nt">-O2</span> <span class="nt">-mthumb</span> <span class="nt">-mcpu</span><span class="o">=</span>cortex-m3 <span class="nt">-march</span><span class="o">=</span>armv7-m <span class="nt">-Wl</span>,-Bstatic <span class="nt">-ffunction-sections</span> <span class="nt">-fdata-sections</span> <span class="nt">-nostdlib</span> <span class="nt">-ffreestanding</span> <span class="c"># 链接参数 </span><span class="nv">LDFLAG</span> <span class="o">+=</span> <span class="nt">-lnosys</span> <span class="nt">-Wl</span>,–cref <span class="nt">-Wl</span>,–no-relax <span class="nt">-Wl</span>,–gc-sections <span class="nt">-ffreestanding</span> <span class="nt">-Wl</span>,-Bstatic <span class="nt">-Wl</span>,–no-warn-rwx-segments <span class="c"># 汇编参数 </span><span class="nv">ASFLAG</span> <span class="o">+=</span> <span class="nt">-Wl</span>, <span class="nt">-Bstatic</span>

<span class="c"># 引入库文件里的lm </span><span class="k">include</span><span class="sx"> "hal/STM32F10x_StdPeriph_Driver/lm.cfg"</span>

</code></pre></div></div>

<h3 id="问题">问题</h3>

<p>使用当前比较通用的<code class="language-plaintext highlighter-rouge">arm-none-eabi</code>的老库</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GNU Arm Embedded Toolchain\10 2021.10 </code></pre></div></div>

<p>STM32F103的工程是无法正常编译的</p>

<p>如果没有看过类似的例子或者直接参考Makefile原工程,不可能上手直接使用</p>

<p>如果使用<code class="language-plaintext highlighter-rouge">Arm GNU Toolchain arm-none-eabi\13.3 rel1</code> 确实可以直接编译过</p>

<p>在不同的使用情景下,lite-manager带来的效果还是有一些差别的,假如你是一个对工程特别熟悉的人,那么对应的lm.cfg或者是proj.cfg中的宏都是可以随手改的。但是并不是每个做工程的人都对整个宏特别熟悉,此时就会出现给了一个不熟悉的人去修改,那么他就得一一对照整个lm.cfg里的约束关系和宏定义的可能,再去设置proj.cfg,如果只是几个宏,那随手就改了(几个宏为什么还要用lm?)</p>

<p>如果是几十个宏,此时要查表,再去决定这个宏可以用什么</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061512950.png" alt="image-20241206151256866" /></p>

<p>这个时候lm的结构就变成负担了,虽然lm可以显示最终结果,但是当你发现结果不对的时候,你依然需要逐项去排查是哪里冲突了</p>

<p>类比传统的Makefile工程来说,lm省去了你手动Makefile里各种if条件的书写,确实简化了一部分Makefile复杂的地方</p>

<p><img src="https://img.elmagnifico.tech/static/upload/elmagnifico/202412061529863.png" alt="image-20241206152950792" /></p>

<p>这里提一点我的想法,有一个比较通用的UI显示,类似于xml或者json的UI化显示,定义好一些规则,直接就能把这种宏配置管理给做了</p>

<ul> <li>事实上ESP32已经实现了这样的效果,VScode中一个插件就能把esp32的配置文件变成可视化的,极大方便了玩家DIY</li> </ul>

<h2 id="summary">Summary</h2>

<p>lite-manager 总体还是挺小的,显示也还凑活,代码是开放的,完全可以自定义一些</p>

<p>总体来说lm简化了Makefile,但是想要使用起来依然需要懂Makefile,需要给项目下的每个文件夹增加配置文件,类比.mk文件</p>

<h2 id="quote">Quote</h2>

<blockquote> <p>https://gitee.com/li-shan-asked/lite-manager/tree/noui/</p>

<p>https://blog.csdn.net/ysu_wangli/article/details/106677907</p> </blockquote>

</description> Tue, 03 Dec 2024 00:00:00 +0800 https://elmagnifico.tech/2024/12/03/Macro-ManagerTools2/</link> https://elmagnifico.tech/2024/12/03/Macro-ManagerTools2/

    <category>build</category>
    
    
  </item>

</channel> </rss>

```

认证完成以后就会有一个小勾了

image-20250121165407063

分享

这是我多年积攒下来的一些订阅源,直接用Follow就可以分享,甚至还可以出售

https://app.follow.is/share/lists/104444993002142720

image-20250121165600901

看起来似乎Follow想通过这种订阅或者说活跃转化成对应的虚拟货币进而维持或者是激励??

这里是我自己的归类,似乎和上面的一样,只是订阅这个人可以订阅到他设置的所有订阅源?

https://app.follow.is/share/users/101522814280570880

image-20250121171022818

看了下RSSHub,在这里竟然是有人提供RSSHub实例然后收费,帮你爬取一些内容,普通人部署了RSSHub也可以加入其中,目前官方应该是免费给你用的,可能某一天官方就不免费了,就要你自己去爬取内容了?那我为什么不用TTRSS,直接全集成好了,还不要去给别人掏钱,费这个鸟劲。

Summary

目前Follow已经是公测阶段,可以免费加入了,但是功能不是完全体的,要完全功能还是得邀请码激活,这个比较麻烦,我也是问人要了一个才能导入甚至分享订阅的

目前看Follow有点意思,但是不多,比我的TTRSS其实没好多少,很多页面也是完全爬不到,还得配合RSSHub来一起使用抓取内容,那我感觉就没啥必要了,不能直接整合这个东西,体验上还是差一点的

目前看起来这个生态只是个金字塔,只有顶端流量大的人有的赚,其他人只能被收割,看起来并不是一个能循环起来的东西。前一段时间的火爆,现在很少看到相关内容了,估计后续生态起不来依然会沉寂到死

他只能对现有RSS的订阅再次划分,而不能产生新的内容,甚至想依靠这种划分来收割一波,信息差这么好利用吗,而至于你订阅源是否能得到利益,那就很难说了,订阅源作为核心的产出者,没有得到应有的流量,反而把利润分给了分发者和服务部署的人。生产者和消费者被剥削了,中介从中获利?

Quote

https://app.follow.is/share/lists/104444993002142720