Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

学而时习之

说明

  • 这是当前知识库的首页入口, 用于快速进入主要技术专题.
  • 内容以 mdBook 组织, 覆盖硬件, 嵌入式, 编程语言, 前端和开发工具等方向.

推荐阅读路径

  • 查硬件与器件, 优先进入 硬件嵌入式.
  • 查语言与框架, 优先进入 编程语言前端.
  • 查环境搭建, 部署和系统问题, 优先进入 工具.
  • 若要全量巡检文档, 直接看 知识库总索引知识库整理清单.

当前关注主题

  • 学习 egui 源码.
  • 分析 async, wake, unpin 与相关实现.
  • 学习 wgpu.
  • 解读 rustdesk 源码.

相关线索:

知识库结构

  • 硬件: 元器件, 设备与基础电路记录.
  • 嵌入式: 芯片平台, 协议, 工具链和板级开发.
  • 编程语言: Rust, Python, Dart, C#, C/C++, Java 与机器人相关笔记.
  • 前端: Web 框架, 构建工具, 图表和样式系统.
  • 工具: Linux, Windows, Docker, AI 工具, 远程协作和环境搭建.
  • 其他: 总索引和整理清单等治理文档.

目录边界

  • 首页只承担“全库入口”和阅读路线说明, 不承载具体技术细节.
  • 具体知识优先进入对应专题目录, 不在首页堆叠长篇正文.
  • 治理, 审计和维护机制统一放在 other/ 目录.

常读书, 读好书

构建说明

  • 本笔记由 mdBook 构建.
  • 若本地预览, 可执行 mdbook servemdbook build.

开源贴片项目

说明

  • 本页作为开源贴片项目资料入口, 当前先记录 OpenPnP 这条主线.
  • 适合自制贴片机, 上位机控制, 视觉对位和贴装流程调研场景.

当前入口

关注点

  • 设备层: 运动平台, 吸嘴, 相机, 供料器和坐标系标定.
  • 软件层: 元件库管理, 视觉识别, Mark 点校准和贴装任务编排.
  • 数据层: BOM, 坐标文件, 封装信息与贴装流程之间的衔接.

后续整理方向

  • 若后续继续补充, 可按 机械结构, 视觉校准, 供料器, 流程软件 继续拆专题.

硬件总览

说明

  • 本目录用于收纳更偏基础器件, 板级硬件, 通用设备的知识记录.
  • 若内容更偏 MCU 接线, 驱动时序, 模块接入, 外设扩展, 则优先放在 embedded/.
  • 若内容更偏传感器类别与测量对象, 则优先放在 传感器总览.

基础器件

设备档案

建议阅读路径

  • 查基础器件原理时, 先从 电阻, 电容, 晶振, 运算放大器 等页面开始.
  • 查具体设备档案, 拆解和观察记录时, 转到 设备记录总览.
  • 遇到 MCU 接入, 接口时序或板级调试问题时, 回到 embedded/.

与嵌入式目录的边界

二极管

N型半导体 和 P型半导体

在N型半导体中掺入五价元素, 在与周围的硅原子形成共价键后, 还多出一个电子, 多出的电子不受共价键的约束, 只需很少的能量就成为自由电子. N型半导体中, 自由电子是多子, 空穴是少子

在P型半导体中掺入三价元素, 在与周围的硅原子形成共价键后, 会就产生了一个空位, 当硅原子的外层电子填补空位时, 硅原子的共价键中便产生一个空穴. P型半导体中, 空穴是多子, 自由电子是少子

PN结

二极管 工作原理 How does a Diode work

张云 - PN结的动画

P型半导体和N型半导体构成PN结后, N区的电子(多子)会扩散到P区, 和P区的空穴结合, 于是N区产生了正离子, P区产生了负离子, 正离子和负离子之间形成了内电场, 内电场会阻碍扩散运动, 内电场也会让P区的电子(少子)向N区漂移

慢慢地, 当N区的电子浓度降低, 扩散运动减弱, 但是由于内电场是在不断增强, 所以漂移运动在不断增强, 直到扩散运动和漂移运动达到平衡

在无外电场和外部激发下, 参与扩散的多子与参与漂移的少子 数目相同, 从而达到动态平衡, 形成的这个内电场区域称之为PN结, 或: 阻挡层、耗尽层、空间电荷区.

PN结 的 正向电压

PN结 的 反向电压

在低掺杂的情况下, 耗尽层较宽, 当加很小的反向电压, 相当于是增强了内电场, 于是就加剧了少子的漂移运动, 同时多子的扩散作用也会加剧, 所以耗尽层会变窄, 并达到新的平衡.

在高掺杂的情况下, 耗尽层很窄, 当加很小的反向电压, 就会加剧少子的漂移运动, 在耗尽层形成很强的电场, 而直接破坏共价键, 使价电子脱离共价键的束缚, 产生电子-空穴对, 使电流急剧增大.

在很低的反向电压时, 就引起电流急剧增大, 这被称为齐纳击穿, 如果反向电压继续增大, 新产生的电子-空穴对被电场加速后又撞出其它的价电子, 载流子雪崩式倍增, 就产生了雪崩击穿.

为什么在掺杂浓度高的区域 空间电荷区更窄

结合的 N型半导体 和 P型半导体, 原子的密度应该是一样的, 只是掺杂的元素浓度不同.

因为N区电子与P区空穴结合才形成了N区正离子和P区的负离子, 这些正负离子是成对出现的, 如果掺杂浓度不同, 那么产生相同数量的正负离子数量所需的体积就不同. 掺杂浓度越高, 所需体积就越少, 所以 在掺杂浓度高的区域 空间电荷区更窄

稳压管工作原理

齐纳二极管又叫稳压二极管

稳压二极管的伏安特性, 如图.

正向偏置状态下, 稳压二极管表现为普通二极管的伏安特性, 即正向特性为指数曲线. 当反向电压增大到一定数值时则击穿, 击穿区的曲线很陡, 几乎平行于纵轴, 表现为很好的稳压特性

整流管工作原理

原理:

二极管整流利用了它具有单向导电性, 也就是电流只能从正极流向负极, 而不能从负极流向正极.

只有二极管两端加正向电压并且大于一定值时, 二极管才会导通, 导通后电阻很小, 相当于一根导线.

而在二极管两端加反向电压时, 二极管因为内部PN结的关系, 反向电流很小, 可以忽略不计, 可以看作是截止状态.

半波整流电路

参考: 二极管整流电路工作原理图解

半波整流电路:

半波整流电路波形:

220V/50Hz交流电经过变压器输出U2, U2也是正弦交流电压, 大小和方向不断变化.

当正半周流过二极管时, A点电位处于高电平, B点电位处于低电平, 二极管处于正向偏置, 此时二极管导通.

当负半周流向二极管时, A点电位低于B点电位, 二极管反偏, 处于截至状态, 没有电流流过.

产生的波形只在一个方向上变化, 称这种为脉动直流电

桥式整流电路

参考: 秒懂桥式整流工作原理(动画)

变压器u2正半周时电流通路:

变压器u2负半周时电流通路:

整个周期电流通路是这样的:

桥式整流电路输出波形与全波整流电路的一样, 也是全波波形, 所以整流后输出电压是整流前的0.9倍

总结:

  • 画图时要注意4只整流二极管连接方法.
  • 电源变压器次级线圈不需要抽头.
  • 每一个半周交流输入电压期间, 有2只整流二极管同时串联导通, 另外2只整流二极管截止.
  • 桥式整流电路输出波形是全波波形

TVS 瞬态抑制二极管

bilibli: https://www.bilibili.com/video/BV1bF411x7Kq/?spm_id_from=333.337.search-card.all.click&vd_source=4b8e4607ee8739eb33686a58fbd4c3a5

主要参数

  1. Vrwm截止电压

在Vrwm下, 认为TVS是不工作的, 即是不导通的. 要求Vrwm要大于工作电压, 否则工作电压大于Vrwm会导致TVS反向漏电流增大, 接近导通, 或者雪崩击穿, 影响正常电路工作. 通常选取截止电压为工作电压的1.1~1.2倍.

  1. IR漏电流

漏电流, 也称待机电流. 对于同功率和同电压的TVS, 在VRWM≤10V时, 双向TVS漏电流是单向TVS漏电流的2倍. 漏电流主要带来了功率的损耗, 或者是在模拟信号中, 会影响AD信号的采样值, 所以TVS的漏电流越小越好.

  1. VBR击穿电压

击穿电压, 指在V-I特性曲线上, 在规定的脉冲直流电流IT或接近发生雪崩的电流条件下测得TVS两端的电压.

  1. IPP峰值脉冲电流, VC钳位电压

IPP及VC是衡量TVS在电路保护中抵抗浪涌脉冲电流及限制电压能力的参数. 对于同型号TVS, 在相同IPP下的VC越小, 说明TVS的钳位特性越好.

IPP可能很大, 如: 20A

TVS钳位电压应小于后级被保护电路 可承受的瞬态安全电压, VC与TVS的雪崩击穿电压及IPP都成正比. TVS的 箝位电压Vc不能大于被防护电路可以承受的 电压Vmax, 否则会对电路造成损坏.

  1. 结电容CI

结电容是TVS中的寄生电容, 在高速IO端口保护需要重点关注, 过大的结电容可能会影响信号的质量. 所以漏电流越小越好.

一个管子的示例 SMF6.0CA

https://item.szlcsc.com/1600216.html

反向0~6.0V 处于截止 时 泄露电流为 800uA

反向关断电压为 6V, 反向击穿电压为7V, 钳位电压约为 10.3V, 峰值脉冲电流约为 19.4A

三极管

箭头方向

箭头只能从 P 到 N,所以根据箭头指向即可确定三极管是 PNP 型还是 NPN 型

基本电路

$\Delta u_{I}$ 表示输入信号, $\Delta u_{O}$ 表示输出信号

基极 发射极 输入信号 构成输入回路, 集电极,发射极输出信号构成输出回路.

三极管内部电流

NPN 管特点: 1. 上层的发射极掺杂浓度很高, 2. 中间的基极很薄, 掺杂浓度很低, 3. 下层的集电极面积很大

三极管的输入输出特性

三极管输出特性

为什么小电流 Ib 能控制大电流 Ic 的大小, 以及放大电路的原理

参考: 张云 - 三极管的动画

这里的三极管是双极型晶体管, 模电的放大电路和数电的简单逻辑电路里面都会用到. 有集电极 c、基极 b、发射极 e、以及两个 PN 结:集电结和发射结. 集电极面积比较大, 基极厚度薄而且载流子浓度比较低.

下图是个 NPN 型的三极管:

当发射结正偏时, 电荷分布会发生变化, 发射结宽度会变窄;相当于给电子打开了一扇 e 到 b 的大门

集电结反偏时, 电荷分布会也发生变化, 集电结宽度会变宽. 相当于打开了阻碍电子从 c 极跑出去的大门, 如下方动画所示:

b 极会接一个大电阻 RB 限制电流 Ib 的大小, 跑到 b 极的那些多余的电子就只好穿越集电结, 形成电流 Ic, 如下方动画所示:

如果基极电压翻倍, 电荷分布会继续发生变化, 发射结宽度会变得更窄, 这扇大门变得更宽了, 将会有更多的电子跑到 b 极. 如下方动画所示:

由于 RB 是大电阻, Ib 就算翻倍了也还是很小, 所以更多的电子会穿越集电结, 让 Ic 也翻倍. 如下方动画所示:

两个直流电源是可以合并到一起的, 再加上小信号 ui 和两个电容, 就得到了放大电路, 如下图所示:

如果电阻大小合适, 这个放大电路能够将小信号$u_i$放大成相位相反的大信号$u_{CE}$, 如下方动画所示:

红色为输入端, $u_i$的变化会影响$U_{BE}$, 把发射结看成一个小电阻, 红色的 Q 点就会沿黑线运动, 然后画出$i_B$的图像; 根据$i_C$=$\Beta i_B$,画出$i_C$的图像, 纵坐标从$\mu A$变成了$mA$; 而输出端有$U_{CE}=U_{CC}-I_CR_C$, 当$U_{CC}$、$R_C$不变时, $U_{CE}$与$I_C$反相

三极管导通条件

NPN b 极电压 大于 e 极电压 (如 0.7V), Vbe > Von

PNP e 极电压 大于 b 极电压 (如 0.7V), Veb > Von

N 管 P 管

上图中 P33 拉高后, N 管 导通, N 管 上端为低电平, 进而 P 管 导通

MOS管

参考: MOS管原理,非常详细

参考: 超低内阻mos管-MOS总结

三个极怎么判定

是N沟道还是P沟道

寄生二极管的方向如何判定

MOS管用作开关时在电路中的连接方法

应用

使用场景

一般主板上使用最多的是增强型MOS管

NMOS最多, 一般多用在信号控制上

其次是PMOS, 多用在电源开关等方面

耗尽型几乎不用

与三极管的区别

三极管是电流控制, MOS管是电压控制, 主要有如下的区别:

  1. 只容许从信号源取少量电流的情况下, 选用MOS管;在信号电压较低, 有容许从信号源取较多电流的条件下, 选用三极管.

  2. MOS管是单极性器件(靠一种多数载流子导电), 三极管是双极性器件(既有多数载流子, 也要少数载流子导电).

  3. 有些MOS管的源极和漏极可以互换运用, 栅极也可正可负, 灵活性比三极管好.

  4. MOS管应用普遍, 可以在很小电流和很低电压下工作.

  5. MOS管输入阻抗大, 低噪声, MOS管较贵, 三极管的损耗大.

  6. MOS管常用来作为电源开关, 以及大电流开关电路、高频高速电路中, 三极管常用来数字电路开关控制.

门电路

说明

  • 本文聚焦基础门电路, 触发器与 TTL 相关内容.
  • 更偏 MCU 板级驱动与电源切换的经验, 可参考 嵌入式电路应用笔记.

基本的门电路

触发器/RS触发器/D触发器/边沿性D触发器 工作原理 锁存器 工作原理

TTL触发器

参考自: 数字电子技术基础 - 3.5.2 TTL反相器的电路结构和工作原理

分析

设 $V_{CC}$ 为5v, 输入信号: $V_{IH} = 3.4V$, $V_{IL} = 0.2V$. 开启电压: $V_{ON}=0.7V$.

$v_I=V_{IL}=0.2V$

$T_1$ 的发射结必然导通. 导通后 $T_1$ 的基极电位被钳在 $v_{B1} = V_{IL} + V_{ON} = 0.9V$, $i_B = (V_{CC} - v_B) / R_1 = 1.025mA$. 根据 $T_1$ 的集电极回路, 可以计算 $T_1$ 的最大饱和电流为: $5v / 1.6k\Omega = 3.125mA$, 由于$\beta$一般在几十到几百, $3.125 / 1.025 \approx 3$远小于正常值, 因此 $T_1$ 工作在饱和状态, 所以 $v_{b2}$ 小于 $v_{b1}$, 所以 $T_2$ 截至, $v_{b5} = 0$, $T_5$ 截至. $T_2$ 截至, $r_{ce2}$ 很大, 所以 $v_{c2} \approx 5V$, $v_{b4} \approx 5V$, $T_4$ 导通, $v_e4 \approx 4.3V$, 经过D2后, $v_o \approx 4.3V - 0.7V = 3.6V$

$v_I=V_{IH}=3.4V$

电阻

文档说明

  • 本文档用于收纳电阻的基础概念, 标注方式, 以及热敏电阻等常见类型.
  • 更偏上拉, 下拉, 板级应用的内容, 可参考 嵌入式电路应用笔记.

阻值标注

三位数标注法

前两位是有效数字, 第三位是倍率.

示例:

  • 103 表示 10 000Ω
  • 104 表示 100 000Ω

常见概念

热敏电阻

PTC

正温度系数热敏电阻, 温度升高时阻值上升. 常见于自恢复保险, 加热, 保护等场景.

NTC

负温度系数热敏电阻, 温度升高时阻值下降. 常用于浪涌抑制, 温度补偿, 过流保护, 过热保护等场景.

嵌入式常见阻值经验

  • 4.7kΩ: 常用于 I2C 上拉.
  • 47kΩ / 100kΩ: 常用于启动脚, 检测脚, 弱上拉弱下拉场景.
  • 精度与温漂需要结合电路要求选择, 例如 1% 精度, 低 ppm 温漂.

相关文档

电容

文档说明

基本概念

电容常被概括为“通交流, 阻直流”.

定义式:

  • C = Q / V
  • 平行板模型可近似写为 C = εS / d

串联与并联

串联

  • 串联后总电容变小.
  • 每个电容分担总电压, 因此整体耐压能力会提高.

公式:

1/C = 1/C1 + 1/C2 + 1/C3 + ...

并联

  • 并联后总电容变大.
  • 各支路电压相同, 整体耐压受最弱的一只限制.

公式:

C = C1 + C2 + C3 + ...

常见用途

滤波电容

用于整流或电源输入输出位置, 让直流更平滑.

去耦电容

用于芯片电源附近, 提供局部瞬态电流, 抑制高频开关噪声沿电源线传播.

旁路电容

为交流或高频噪声提供一条低阻抗通路, 常用于把噪声旁路到地.

去耦与旁路的区别

  • 去耦更强调“给器件本地供能 + 切断噪声传播通路”.
  • 旁路更强调“给高频分量一条低阻抗泄放路径”.
  • 在很多实际板级设计中, 两者会同时出现, 甚至由同一组电容共同承担.

常见材质

C0G

  • 稳定性高, 损耗小.
  • 适合高频, 振荡, 谐振等场景.

X7R / X5R

  • 容值较大, 综合性能较好.
  • 常用于去耦, 旁路, 一般电源场景.

Y5V

  • 可用但稳定性较差.
  • 对温漂和容量变化不敏感的场景才适合使用.

嵌入式常见经验

  • 小电容更适合处理高频噪声.
  • 0.1uF 常作为数字电路附近的去耦电容起点.
  • 若电源路径阻抗较高, 往往还需要叠加 1uF ~ 10uF 的储能电容.

相关文档

晶振

文档说明

  • 本文档用于收纳晶振的基础理解与负载电容匹配问题.
  • 更偏 MCU 接入与板级布局的内容, 后续可继续扩展为应用专题.

无源晶振

晶振匹配介绍:

  • https://blog.csdn.net/qq_36347513/article/details/121246522

负载电容值 CL 匹配

参考:

  • https://www.genuway.com/3959.html
  • https://www.bilibili.com/video/BV1bF411x7Kq/

设计经验

  • 负载电容较大的谐振器, 往往需要更大的驱动能力.
  • 低功耗产品通常更偏向负载电容更小的方案.
  • 实际板级设计时, 还需要同时关注走线长度, 接地完整性, 干扰源距离等问题.

运算放大器

文档说明

  • 本文档用于收纳运放相关基础应用笔记.
  • 当前内容偏向模拟前端与板级调试经验, 后续可继续扩展为反相, 同相, 差分, 仪表放大器等专题.

常见关注点

  • 放大倍数是否稳定.
  • 输入失调, 噪声和带宽是否满足要求.
  • 单电源 / 双电源供电条件是否匹配.
  • ADC 前端的地线与参考电压处理是否合理.

比例运算放大电路中的平衡电阻

参考:

理解要点:

  • 平衡电阻常用于减小输入偏置电流带来的误差影响.
  • 在高阻值网络里, 这类误差往往更需要关注.

ADC 数字地 DGND 与模拟地 AGND

参考:

使用建议:

  • ADC 前端要优先考虑模拟地回流路径.
  • 若数字地噪声较大, 可能会直接影响采样稳定性和分辨率表现.

同向放大器

部分测试记录:

  • 1k: 10.305uV
  • 10k: 10.295uV
  • 100k: 0(205.46nV) 1uV(1.205uV) 10uV(10.203uV) 100uV(100.185uV) 999uV(999.006uV)
  • 300k: 0(4.565nV) 1uV(1.004uV) 10uV(9.999uV) 100uV(99.945uV) 999uV(998.406uV)
  • 500k: 0(-197.049nV) 1uV(802.027nV) 10uV(9.793uV) 100uV(99.703uV) 999uV(997.806uV)
  • 1000k: 0(-705.21nV) 1uV(292.798nV)

当前记录结论:

  • 在这组测试条件下, 100k 是相对合适的选择.

后续可补主题

  • 反相放大器与同相放大器的差异.
  • 差分放大与共模抑制.
  • 运放选型中的 GBW, SR, 输入偏置电流.
  • 低噪声前端与 ADC 驱动设计.

电源电路

文档说明

  • 本文档用于收纳供电, 稳压, DC-DC, LDO 等基础电源主题.
  • 更偏 MCU 板级去耦, 地分割, 局部供电经验的内容, 可参考 嵌入式电路应用笔记.

DC-DC

DC-DC 常用于高效率电源转换, 适合压差较大, 电流较大, 效率要求高的场景.

参考:

  • https://www.elecfans.com/dianyuan/451609.html

LDO

LDO 结构简单, 输出噪声通常更低, 但压差大时效率较差.

适合:

  • 低噪声模拟部分
  • 电流不大但稳定性要求高的部分

单片机供电系统分析

设计 MCU 供电时通常要同时考虑:

  • MCU 自身供电稳定性
  • 外设供电路径
  • 瞬态电流响应
  • 噪声隔离与地回流路径

电解电容与贴片电容替代

常见问题:

  • 10uF 电解电容能否替换为 22uF 贴片电容?

判断时需要综合考虑:

  • 实际有效电容值
  • 直流偏压下的容量衰减
  • ESR / ESL
  • 频率特性
  • 成本与体积

相关文档

设备记录总览

说明

  • 本目录保存偏设备档案, 拆解记录和使用观察的页面.
  • 这类页面不一定都是“通用知识”, 但很适合作为长期硬件档案沉淀.

当前设备

使用建议

  • 先把这里视为设备档案和使用观察记录, 不必要求每页都形成通用知识模板.
  • 若某个设备内容逐步沉淀出通用接线, 驱动或协议知识, 再考虑迁入更合适的专题目录.

目录边界

  • 本目录偏设备档案和拆解观察.
  • 若问题更偏电路应用, 选型和器件原理, 应回到 hardware/ 上层或 embedded/ 对应专题.

EC11 旋转编码器

说明

  • 本页记录 EC11 旋转编码器的原理图, 接线方式和方向判定思路.
  • 适合做菜单旋钮, 参数调节和本地人机输入时的硬件上手记录.

参考资料

原理图

基本使用

  1. 当按钮按下时, D 可以读取到低电平.
  2. 当旋转旋钮时, AB 会产生相位错开的脉冲信号.
  3. 只要在 A 的边沿采样 B 的电平, 就可以判断旋转方向.

常用判断思路:

  • A 状态变化后, 若 B 当前状态等于 A, 记为逆时针.
  • A 状态变化后, 若 B 当前状态不等于 A, 记为顺时针.

信号理解

A      +-----+     +-----+     +-----+
             |     |     |     |
             |     |     |     |
             +-----+     +-----+
B         +-----+     +-----+     +-----+
                |     |     |     |
                |     |     |     |
                +-----+     +-----+

 +--------------------------------------->
                CW direction

接线

      +--------+              +---------------------------------+
      |        |              |                                 |
      |      A +--------------+ GPIO_A (internal pull up)       |
      |        |              |                                 |
+-------+      |              |                                 |
|     | |  GND +--------------+ GND                             |
+-------+      |              |                                 |
      |        |              |                                 |
      |      B +--------------+ GPIO_B (internal pull up)       |
      |        |              |                                 |
      +--------+              +---------------------------------+
  • GPIO_A 上拉输入.
  • GPIO_B 上拉输入.

时序理解

一定位一脉冲 EC11 时序图:

  • 正转一格 -> 停顿 -> 连续正转 -> 停:

  • 反转一格 -> 停顿 -> 连续反转 -> 停:

可以把 A 视为时钟, B 视为数据:

  • 正转时, 在时钟下降沿处, 数据线为高电平; 或在时钟上升沿处, 数据线为低电平.
  • 反转时, 在时钟下降沿处, 数据线为低电平; 或在时钟上升沿处, 数据线为高电平.

一个便于记忆的经验总结是:

  • 在时钟下降沿处, A / B 反相时可视为正转.
  • 在时钟下降沿处, A / B 同相时可视为反转.

使用建议

  • 软件上要做去抖动, 否则一格旋转可能被识别成多次抖动触发.
  • 若同时使用按压功能, 还要单独处理按键消抖和长按逻辑.
  • 如果旋转方向和预期相反, 可以优先对调 A / B 两路接线或调整软件判断逻辑.

TFT_LCD ST7735 显示屏

说明

  • 本页记录 ST7735 小尺寸彩屏模块的原理图, 引脚理解和在 ESP32-C3 / Arduino 场景下的接线记录.
  • 适合做 SPI 彩屏上手, 驱动移植和引脚排查时的历史笔记整理入口.

模块资料

原理图

引脚描述

ST7735 笔记

  • 当前模块采用 4 线串口通信, 常见信号为 CS, SCL, SDA, D/C, 再加上 RES.
  • 记录中提到的分辨率为 132 x 162 像素.
  • 一次传输通常是“先发送命令, 再发送参数”.

数据传输中断恢复

  • 若传输中 RES 拉低, 驱动会丢弃当前已传输位并重置接口, 待 RES 恢复后重新开始下一次命令数据传输.
  • 若传输中 CS 打断当前事务, 驱动会丢弃当前数据并准备重新开始对应事务.

相关示意:

旋转理解

  • 屏幕旋转 90° 时, 需要交换 X / Y 坐标.
  • 屏幕旋转 270° 时, 除了交换 X / Y, 还要处理 Y 方向从下到上的映射.

ESP32-C3 应用记录

  • ESP32-C3 上, SPI0SPI1 主要用于存储器访问, 通常应使用通用 SPI2 驱动外设屏幕.
  • 早期排查过程中, 曾根据资料反复尝试过多组引脚, 最终通过参考 Arduino 项目确认了一组可用映射.

相关资料截图:

最终确认的引脚

  • SCL -> SPI_CLK -> IO4
  • SDA -> SPI_MOSI -> IO6
  • RES -> GPIO -> IO9
  • DC -> GPIO -> IO8
  • CS -> SPI_CS -> IO10

Arduino 记录

需要安装两个库:

  • Adafruit_GFX
  • Adafruit ST7735 and Adafruit ST7789 Library

并安装 esp32 boards.

参考代码:

使用建议

  • 先确认模块真实接口类型和分辨率, 不同 ST7735 模块的偏移和初始化参数可能不同.
  • 屏幕不亮时, 优先检查供电, 复位脚, 背光和 SPI 引脚是否接对.
  • 显示方向异常时, 重点回到坐标映射和显示窗口偏移配置排查.

兰科芯128G U盘

说明

  • 本页记录一款 128G U 盘的拆解与简单使用观察.
  • 当前内容以外观, 拆解照片和使用场景记录为主, 更适合作为设备档案页.
  • 若后续继续补测速, 主控和颗粒信息, 这页可以进一步升级成完整评测记录.

外观与拆解

当前可记录的信息

  • 容量标称为 128G.
  • 已保留拆解照片, 便于后续回看结构和器件布局.
  • 暂未系统记录主控型号与闪存颗粒编号.

使用场景

机械硬盘到 U 盘的数据迁移

  • 适合临时备份, 小规模文件迁移和现场资料拷贝.
  • 若数据重要, 仍建议配合校验和或二次备份.

手机转接使用

  • 在手机上通过 USB -> Type-C 转接头使用时, 观察到的速度大约在 30M ~ 50M.
  • 这类场景更依赖手机接口规格, 转接头质量和文件类型.

建议补充的测试项

1. 基础信息

  • 主控型号.
  • 闪存颗粒信息.
  • 实际可用容量.

2. 性能测试

  • 顺序读写测速.
  • 随机读写测速.
  • 小文件与大文件混合拷贝表现.

3. 稳定性测试

  • 长时间连续写入后的掉速情况.
  • 发热表现.
  • 在 Windows, Linux, Android 下的兼容性.

使用建议

  • 这类设备页更适合做“硬件档案 + 现场观察”记录, 不必强行写成通用知识页.
  • 若后续测得完整参数, 可把“照片记录”和“性能评测”拆成两个小节长期维护.
  • 如果只是查接口和转接器兼容性, 建议与其它移动存储设备页统一比较维度.

相关文档

红米note4拆解

说明

  • 本页记录 红米 Note 4 的拆解观察与部件识别要点.
  • 当前更偏设备档案与拆机记录, 不作为通用手机维修教程.
  • 若后续补充更多主板, 芯片与排线信息, 可继续升级为更完整的拆解页.

拆解提示

  • 该机型为金属机身, 可先从充电口附近尝试撬开小缝.
  • 拆后盖时要特别注意指纹模块排线, 避免直接拉扯导致损坏.
  • 拆机前建议先断电, 取出卡托, 并准备塑料撬片与吸盘等基础工具.

当前识别到的部件

  1. 指纹传感器
  2. 后置摄像头
  3. 前置摄像头
  4. 电池及其连接接口
  5. 侧边按键排线
  6. 未确认功能器件
  7. 射频天线
  8. 振动马达
  9. 红外模块
  10. 麦克风
  11. 显示屏

适合记录的后续信息

  • 主板正反面芯片布局.
  • 电池型号与额定参数.
  • 屏幕排线与指纹排线位置.
  • 充电口小板结构.
  • 维修时的高风险拆装点.

使用建议

  • 这类页面更适合做设备档案沉淀, 不必强行改写成普适教程.
  • 如果后续还有更多手机拆解记录, 建议统一拆解记录模板, 包括 拆机入口, 排线风险, 关键模块, 照片 等字段.

相关文档

嵌入式总览

说明

  • 本目录收录 MCU, SoC, 调试链路, 通信协议, 驱动与板级开发相关知识.
  • 若内容更偏通用器件, 拆解观察和硬件档案, 应优先放在 hardware/.
  • 若内容更偏桌面应用, Web 或通用编程语言, 应回到对应语言目录.

平台与芯片

基础主题

工具链与框架

板级设计与调试

总线, 模块与电路

阅读路径

  • 芯片平台上手优先从具体平台目录的 README.md 开始.
  • 需要查接口协议时, 先看 基本通信协议, 再看具体主题页.
  • 需要查模块接入时, 优先看 模块与扩展器件总览模块/README.md.
  • 需要看板级电路思路时, 再进入 电路/README.md.

补充记录

目录边界

  • embedded/ 放嵌入式工程知识和开发链路.
  • hardware/ 放器件档案, 拆解记录和设备观察.
  • tools/ 放辅助开发工具和运维工具.

ESP32 总览

说明

  • 本目录收录 ESP32 系列芯片相关的环境搭建, 调试, 日志, Rust 与应用记录.
  • 当前内容以 ESP32-C3 为主, 后续可继续按芯片型号和主题收敛为更稳定的入口结构.

快速入口

Rust 相关

无线与系统

建议阅读路径

  1. 初次上手优先看 ESP32-C3 环境搭建ESP-IDF 常用命令.
  2. 板级联调阶段重点看 nanoESP32-C3 和串口日志设置页面.
  3. Rust 方向时, 从 esp-rsespup Rust 环境 开始, 再看具体芯片实验记录.
  4. 做无线功能时, 优先看 蓝牙相关总结, 再结合官方 API 文档核对广播, GATT 和地址配置细节.

常见关注点

  • 区分 ESP-IDF, Arduino, Rust 三套开发链路, 避免环境变量和工具链相互污染.
  • Windows 下若串口, 烧录或监视器异常, 先检查驱动, 端口占用和目标芯片设置.
  • 芯片型号切换时, 先确认目标架构, Flash 配置, 下载方式和板级管脚定义是否一致.

ESP-IDF 常用命令

说明

  • 本文记录 ESP-IDF 开发中常用的构建, 清理和信息查看命令.
  • 更偏板级和具体开发板问题, 可参考 nanoESP32-C3.

常用命令

查看组件体积

idf.py size-components

重新生成配置与构建文件

idf.py reconfigure

常见配套命令

idf.py set-target esp32c3
idf.py menuconfig
idf.py build
idf.py flash
idf.py monitor
idf.py fullclean

使用建议

  • 目标芯片切换前, 先执行 set-target, 避免残留旧配置.
  • 遇到配置缓存异常时, 可尝试 reconfigurefullclean.
  • 关注 size-components 输出, 便于定位占空间较大的组件.

相关文档

esp-rs

说明

  • 本页记录基于 esp-rs 生态在 ESP32-C3 上跑通最小 Rust 无线示例的流程.
  • 当前重点不是完整项目模板, 而是快速验证 cargo, espflash, 示例工程和功能 feature 是否已经连通.
  • 如果只是做工具链安装, 应优先结合 espup rustrust 环境分析 一起看.

核心结论

  • 想快速验证 ESP32-C3 + Rust 是否可用, 最省事的方式是先跑官方或社区最小示例.
  • 无线相关示例通常依赖 feature 组合, 构建前先确认目标芯片, 示例名称和使能项一致.
  • 烧录问题优先检查串口, 芯片识别, 波特率和目标二进制是否匹配当前板子.

最小验证流程

安装烧录工具

cargo install cargo-espflash

获取示例仓库

git clone https://github.com/esp-rs/esp-wifi.git
cd esp-wifi/examples-esp32c3

构建示例

cargo build --features "wifi,embedded-svc"

烧录并运行

cargo espflash --features "wifi,embedded-svc" --example dhcp --speed 2000000

使用场景

  • 验证 ESP32-C3 的 Rust 工具链是否已经可用.
  • 快速确认 esp-wifi 示例在当前环境中能否完成构建和烧录.
  • 作为后续接入 Wi-Fi, DHCP, embedded-svc 的最小起点.

常见排查方向

  • 构建报错时, 先检查 Rust 目标平台, espup 版本和示例仓库分支是否匹配.
  • 如果是 feature 相关报错, 优先确认当前示例是否真的支持 wifi,embedded-svc 这一组合.
  • 烧录失败时, 先确认开发板端口, 驱动和下载模式是否正确.
  • 运行后没有预期日志时, 再去检查串口监视器和日志配置.

相关文档

espup Rust 环境

说明

  • 本页记录使用 espup 搭建 ESP Rust 工具链和生成 esp-idf-template 示例工程的过程.

  • 内容主要面向 ESP32-C3 / ESP32-S3 方向, 其中版本约束较强, 需要结合当前 ESP-IDF 版本重新核对.

  • 本页中的 master 指向经核对仍存在的 ESP-IDF 上游分支, 不是默认分支命名建议.

espup

https://github.com/esp-rs/espup

cargo install espup –git https://github.com/esp-rs/espup

espup install -t esp32c3 -e master

当前记录可用的版本是 v4.4. 如果上游版本策略变化, 请以官方文档和当前模板要求为准.

hello world

cargo generate --git https://github.com/esp-rs/esp-idf-template cargo

    参数:
        project name: esp32s3-hello
        ESP-IDF native build version: v4.4
        MCU: esp32s3
        Configure project to use Dev Containers: false
        STD support: true

cd esp32s3-hello
cargo build --release

espflash COM24 .\target\xtensa-esp32s3-espidf\release\esp32s3-hello --speed 2000000 --monitor

ESP32-C3 Rust 学习笔记

说明

  • 本页重点记录 esp-idf-sysESP32-C3 上的 native 构建流程与中间产物分析.
  • 适合在排查 esp-idf, bindgen, sdkconfigbuild.rs 之间关系时做深入参考.

esp-idf-sys native方式编译 分析

1. native编译

    1> 自动下载 esp-idf cmake ninja riscv-esp工具链

    2> 使用 cmake 编译 esp-idf, 配置文件是: sdkconfig 和 sdkconfig_defaults

        把 esp-idf-sys 中的 resources/cmake_project 作为一个基本的项目,
        (参考下: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-guides/build-system.html#cmake-esp-idf)
        复制到你的项目编译输出路径下:
            target/riscv32imc-esp-espidf/debug/build/esp-idf-sys-eac6c6bb186a6e4d/out
                .
                ├── build
                ├── CMakeLists.txt
                └── main.c

            sdkconfig 和 sdkconfig_defaults 是 从环境变量 ESP_IDF_SDKCONFIG 和 ESP_IDF_SDKCONFIG_DEFAULTS 中取到的
            这个项目就是一个 ESP-IDF hello world 的cmake项目
            编译从 ESP_IDF_SDKCONFIG 和 ESP_IDF_SDKCONFIG_DEFAULTS 中定义的组件, 并生成一个 libespidf.elf 可执行文件,这个文件可能只是为了生成依赖的库,没有其它作用
            生成的 库 会以 println!("cargo: ... ") 的形式输出?

        主要过程是:
          cmake::Config::new(&out_dir)
            .generator("Ninja")
            .out_dir(&out_dir)
            .no_build_target(true)
            .define("CMAKE_TOOLCHAIN_FILE", &cmake_toolchain_file)
            .always_configure(true)
            .pic(false)
            .asmflag(asm_flags)
            .cflag(c_flags)
            .cxxflag(cxx_flags)
            .env("IDF_PATH", &idf.esp_idf.worktree())
            .env("PATH", &idf.exported_path)
            .env("SDKCONFIG", sdkconfig)
            .env("SDKCONFIG_DEFAULTS", sdkconfig_defaults)
            .env("IDF_TARGET", &chip.to_string())
            .build();

        为了编译自定义组件, 应该需要考虑配置 sdkconfig_defaults 文件

    3>. 解析 cmake 的输出结果, 如: link_args sdkconfig_json, 把解析的结果返回 -- EspIdfBuildOutput

2. 生成rs代码

    一个bindgen需要的头文件 header_file:
        src/include/esp-idf/bindings.h

    执行下面 build code, 返回值是 bindings_file:
        bindgen::run(
            build_output
                .bindgen
                .builder()?
                .ctypes_prefix("c_types")
                .header(header_file.try_to_str()?)
                .blacklist_function("strtold")
                .blacklist_function("_strtold_r")
                .clang_args(build_output.components.clang_args())
                .clang_args(vec![
                    "-target",
                    if mcu == "esp32c3" {
                        // Necessary to pass explicitly, because of https://github.com/rust-lang/rust-bindgen/issues/1555
                        "riscv32"
                    } else {
                        // We don't really have a similar issue with Xtensa, but we pass it explicitly as well just in case
                        "xtensa"
                    },
                ]),
        )?;

    bindings_file 这里是: target/riscv32imc-esp-espidf/debug/build/esp-idf-sys-e842de6eba130a3d/out/bindings.rs
    这是 头文件中声明的 rust绑定. 同时, 这中间使用的各种 args 也会以 println!("cargo: ... "): 的形式输出

3. 在 esp-idf-sys 中刚好有 src/lib.rs:
    
    include!(env!("EMBUILD_GENERATED_BINDINGS_FILE"));

4. esp-idf-sys 的 src代码

    src/start.rs 定义了我们项目的入口, 及rust运行的基础, 我们的项目中 main 函数会在 src/start.rs 中被引用

上面几个过程就有了 esp32 的 c bind 的 crate 了.

上面的整个过程 就是: 
    编译esp-idf源码, 根据配置生成 各种.a
    生成 rust绑定
    编译 rust code

ESP32-S3 Rust 环境

说明

  • 本页记录在 Windows 上为 ESP32-S3 搭建 Rust + ESP-IDF 开发环境的过程.

  • 内容包含离线工具链安装, espup 使用和模板工程编译烧写, 但版本记录偏旧, 实操时需结合当前工具链核对.

  • 本页提到的 master 版本是当时 ESP-IDF 上游分支记录, 不是默认分支命名建议.

  1. 安装 esp32 的 toolchain

    https://dl.espressif.cn/dl/esp-idf/

    在 windows 上, 可以直接使用 离线安装的方式安装, 避免各种麻烦的事情发生.

  2. 安装 rust target for esp32s3

    根据 https://github.com/esp-rs/rust-build 的说明.

    git clone https://github.com/esp-rs/rust-build.git cd rust-build ./Install-RustToolchain.ps1 ./Export-EspRust.ps1

  3. 安装工具

    cargo install cargo-generate ldproxy espflash espmonitor

  4. 生成项目

    cargo generate –vcs none –git https://github.com/esp-rs/esp-idf-template cargo

    参数: ESP-IDF native build version: v4.4 MCU: esp32s3 Configure project to use Dev Containers: false STD support: true

  5. 编译

    一定要记得, 前两步中, 都会有设置环境变量. 否则找不到 rust 环境 或者 esp toolchain.

    cd 生成的项目路径

    cargo build –release

  6. 烧写

    espflash COM24 .\target\xtensa-esp32s3-espidf\release\esp32s3-demo –speed 921600 espmonitor COM24

2023.11.09

  1. 用 esp 的 windows 安装器 在线安装的方式, 安装了 master 版本的 idf (目前是 ESP-IDF 5.3 版本)

  2. 使用 espup 安装 rust 工具链

cargo install espup

# 这一步会在 USER 的PATH环境变量下 增加几十个路径, 极大可能会影响其他依赖 clang 的程序运行, 我的做法是 直接删掉 这些新增的与esp相关的路径. 不要删错了!!!
espup install esp32c3
  1. 打开安装 idf 后产生快捷方式 ESP-IDF 5.3 Powershell

在新终端中, 执行 ~\export-esp.ps1

  1. (可选: 设置代理 加快下载模版), 创建模版项目
cargo generate esp-rs/esp-idf-template cargo
  1. 编译
cd 进入项目路径

cmake build -vvv (由于环境变量中已经包含 idf 的路径, 所以不需要 下载 idf 到 .embuild 目录了)

cmake run

esp32c3 环境搭建

参考: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c3/get-started/index.html

搭建windows环境

参考: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c3/get-started/windows-setup.html

方式1

下载 esp-idf, 当前版本是4.4, 使用离线安装的方式, 安装时使用默认选项

默认会安装: 内置的 Python, 交叉编译器, OpenOCD, CMake 和 Ninja 编译工具, ESP-IDF

方式2

使用 vscode, 安装 esp idf 插件, 然后, 在 命令中执行: config esp extention, 根据相应的选项下载 esp环境所需要的文件.

创建项目

  1. 创建一个 hello-world 项目:

    使用特定的终端, 在开始菜单中打开: esp-idf 4.4 cmd

    cd C:\Espressif\frameworks\esp-idf-v4.4

    cp -r examples/get-started/hello_world .

    cd hello_world

    idf.py set-target esp32c3

    idf.py menuconfig

ESP32 应用程序的启动流程

说明

  • 本页用于记录 ESP-IDF 中应用程序从上电到进入用户代码的大致启动路径.
  • 适合在排查启动异常, Bootloader 行为和初始化顺序时快速定位参考资料.

参考资料

可以从哪些层面理解

  • ROM Bootloader: 芯片上电后最先运行的固化引导逻辑.
  • Second Stage Bootloader: 负责加载分区表, 校验镜像并跳转到应用入口.
  • Application startup: 完成运行时初始化, 再进入 app_main 或更上层框架入口.

使用建议

  • 如果问题发生在 app_main 之前, 优先回到 Bootloader, 分区表和镜像加载流程排查.
  • 如果问题只在某个芯片型号出现, 需要同时核对对应芯片的官方启动文档和 SDK 版本差异.
  • 若后续继续整理, 可补充“分区表 -> bootloader -> app_main”的具体调用链示意.

nanoESP32-C3

说明

  • 本页作为 nanoESP32-C3 板级开发记录的轻量总览页.
  • 当前仓库里已分散记录了调试, 串口控制台, ESP-IDF 常用命令和 Rust 相关内容.
  • 本页的目标是把这些入口收敛成一个可快速定位问题的板级索引.

适用场景

  • 初次拿到 nanoESP32-C3 开发板, 需要确认基础开发链路.
  • 调试串口日志, 下载方式, 板级连线和最小样例运行问题.
  • ESP-IDF, PlatformIO, Rust 三套开发方式之间切换时做入口导航.

建议阅读路径

  1. 环境和命令问题先看 ESP-IDF 常用命令.
  2. 板级串口与日志问题优先看 nanoESP32-C3 串口与日志设置.
  3. 如果是板子资料或开发板定位问题, 先看 nanoESP32-C3 开发板.
  4. 如果是 Rust 方向, 再看 esp-rs 与相关实验记录.

常见关注点

  • 下载前先确认目标芯片型号和串口端口没有选错.
  • 串口日志异常时, 优先检查输出通道, 波特率和控制台配置.
  • 调试器连不上时, 先排查 OpenOCD, 目标配置和线序.
  • 构建失败时, 先区分是 ESP-IDF, Arduino/PlatformIO, 还是 Rust 工具链问题.

相关主题

nanoESP32-C3 串口与日志设置

说明

  • 本文记录 ESP32-C3ESP-IDF 下的控制台串口, 波特率与日志输出相关设置.
  • 适合在调试串口冲突, 日志过多或产线静默启动时参考.

定制控制台波特率

menuconfig 中可调整:

Component config -> ESP System Settings
  -> Channel for console output (Custom UART)
  -> Channel for console secondary output (No secondary console)

典型配置示例:

  • UART TX on GPIO# = 21
  • UART RX on GPIO# = 20
  • UART console baud rate = 1000000

关闭颜色输出

Component config -> Log output -> Use ANSI terminal colors in log output

关闭 bootloader log

Bootloader config -> Bootloader log verbosity -> No output

关闭程序 log

Component config -> Log output -> Default log verbosity -> No output

关闭 console

Component config -> ESP System Settings -> Channel for console output

使用建议

  • 先统一下载口, 日志口和业务串口的分工, 避免引脚冲突.
  • 若高波特率下日志异常, 优先检查 USB 转串口链路与终端配置.
  • 量产或低噪声场景可关闭大部分日志, 但建议保留必要的错误级输出.

nanoESP32-C3 OpenOCD 调试

说明

  • 本页记录 nanoESP32-C3 开发板上使用 openocd-esp32 和 CMSIS-DAP 进行调试的过程.
  • 重点包括 hidapi 编译, openocd-esp32 构建, efuse 设置和实际调试体验问题.

使用 openocd 调试 esp32c3

编译 hidapi

编译 openocd-esp32 失败:
    configure: error: hidapi is required for the CMSIS-DAP Compliant Debugger

编译 hidapi:

git clone https://github.com/Dashlane/hidapi.git
cd hidapi
./bootstrap
./configure --enable-static --disable-shared
make clean
make
sudo make install

编译 openocd-esp32

git clone https://github.com/espressif/openocd-esp32.git
cd openocd-esp32
./bootstrap
./configure --enable-cmsis-dap
make -j
sudo make install

sudo systemctl restart udev

Burn the efuse

the efuse JTAG_SEL_ENABLE should be burned to enable the jtag function.

espefuse.py -p /dev/ttyACM0 burn_efuse JTAG_SEL_ENABLE

启动 openocd

设置 GPIO10 到 GND 用于选择 GPIO function 到 JTAG, 重新给开发板上电

openocd -f interface/cmsis-dap.cfg -f target/esp32c3.cfg -c "adapter_khz 10000"

能调试, 但有两个大问题: 1.速度特别慢,esp的日志输出需要12s,LED电平翻转需要3s 2.openocd启动后,gdb可能有5次只能成功连上1到2次 体验极差,还是打Log最方便了...

蓝牙相关总结

扫描的过程

  1. 主设备 主动 发起 Scan Request
  2. 广播设备接收到 Scan Request 后, 会发送 Scan Response
  3. 主设备会执行 Scan Response 的回调函数

启动广播

static uint8_t raw_ext_adv_data_1m[] = {
    // 长度 0x02, Flags, 0x06
    0x02, 0x01, 0x06,
    // 长度 0x02, Tx Power Level, 0xeb
    0x02, 0x0a, 0xeb,
    // 长度 0x0b, Complete Local Name, EH_BLE_TEST
    0x0b, 0x09, 'E', 'H', '_', 'B', 'L', 'E', '_', 'T', 'E', 'S', 'T'
};
uint8_t addr_1m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02};

esp_ble_gap_ext_adv_set_params(0, &ext_adv_params_1M);

esp_ble_gap_ext_adv_set_rand_addr(0, addr_1m);

esp_ble_gap_config_ext_adv_data_raw(0, sizeof(raw_ext_adv_data_1m), &raw_ext_adv_data_1m[0]);

esp_ble_gap_ext_adv_start(1, &ext_adv[0]);

ADVERTISING AND SCAN RESPONSE DATA FORMAT:

AD: Advertise Data Type PDU: Packet Data Unit

AD Type 的定义位于:

bluetooth-Assigned_Numbers.pdf – 2.3 Common Data Types

BLE ADVERTISING PACKET

The advertising channel PDUs serve following functions. • It broadcasts the data. • It helps in discovering slaves in order to connect with them. • There are different types of advertising PDUs each with different payload formats and functions.

➤ Advertising PDUs ( ADV_IND, ADV_DIRECT_IND, ADV_NONCONN_IND, ADV_SCAN_IND) ➤ Scanning PDUs (SCAN_REQ, SCAN_RSP) ➤ Initiating PDUs (CONNECT_REQ)

蓝牙地址

  1. 公共地址 需要向 IEEE 购买, 如: 4CE1000000C1
  2. 随机地址 CCE1000000C1, 最左边的C, 最高两个bit是11

mac[0] & 0x01 为true 表示一个多播地址

单播,多播和广播

蓝牙测试

RF 单载波测试: 选择 channel, 发射功率

测试指标: 发射功率 频偏

蓝牙广播, 扫描到广播时 可以获取 接收到的发射功率

GATT

GATT 是蓝牙协议栈的基础, 提供属性列表

Service 是 多个 att 的组合, 表示一个特定的服务, 如 电池服务 心跳服务

Profile 是 多个 Service 的组合, 一个 Profile 中包含多个 Service

adv 原始数据

static uint8_t raw_adv_data[128] = {
    // Flags: TYPE = 0x01。这个数据用来标识设备 LE 物理连接的功能。DATA 是 0 到多个字节的 Flag 值,每个 bit 上用 0 或者 1 来表示是否为 True。如果有任何一个 bit 不为 0,并且广播包是可连接的,就必须包含此数据。各 bit 的定义如下:

    // bit 0: LE 有限发现模式
    // bit 1: LE 普通发现模式
    // bit 2: 不支持 BR/EDR
    // bit 3: 对 Same Device Capable(Controller) 同时支持 BLE 和 BR/EDR
    // bit 4: 对 Same Device Capable(Host) 同时支持 BLE 和 BR/EDR
    // bit 5…7: 预留
    0x02, 0x01, 0x06,
    // Tx Power Level
    0x02, 0x0a, 0xeb,
    // Complete Local Name
    0x08, 0x09, 'E', 'H', '_', '8', '0', '3', '2'};

scan repoonse 原始数据

设置 public address

esp_iface_mac_addr_set(mac_addr, ESP_MAC_BT), 设置默认的蓝牙MAC地址

设置 esp_ble_gap_ext_adv_params_t 的 own_addr_type 为 BLE_ADDR_TYPE_PUBLIC

esp32 基地址

https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/api-reference/system/misc_system_api.html#mac

通过 esp_iface_mac_addr_set(base_mac, ESP_MAC_BASE) 设置基地址, 则 默认时:

Wi-Fi Station 为 base_mac,

Wi-Fi SoftAP 为 base_mac 最后一组字节后加 1,

蓝牙 base_mac 最后一组字节后加 2,

以太网 base_mac 最后一组字节后加 3

蓝牙学习入门

(1)

  1. 能基本描述 蓝牙广播 与 蓝牙扫描 的过程

  2. 修改 蓝牙广播数据, 设置 蓝牙发射功率

  3. 能看明白 某个蓝牙模块 广播过程中的事件, 在相应的事件中 可以添加自定义行为

  4. 使用 安卓NRF软件 验证 蓝牙广播数据 和 蓝牙发射功率

(2)

  1. 添加 GATT服务, 可以通过 安卓NRF软件 连接

ESP32 USB 烧写与日志输出

说明

  • 本页记录通过 USB Serial/JTAG Controller 进行 ESP32 烧写和日志输出的基本配置方法.
  • 适合作为 idf.py dfu, flash, monitor 的快速参考.

使用 USB 进行 烧写 与 日志输出

参考: https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-guides/dfu.html

idf.py menuconfig

    Component config → ESP System Settings → Channel for console output

    (X) USB Serial/JTAG Controller

idf.py dfu, 开始编译

idf.py -p COM8 flash, 使用 USB串口下载

idf.py -p COM8 monitor

esp-rs Rust 环境分析

说明

  • 本页从 esp-idf-sysembuild 角度说明 esp-rs 项目的底层构建链路.
  • 适合在排查 sdkconfig, build.rs 和 Rust feature 配置传递问题时快速建立整体认知.

esp-rs项目的编译 关键在于 esp-idf-sys库的编译, 这是 esp-idf 源码 的编译, 编译工具是 embuild, 底层是官方编译链 cmake编译, 这会编译所有的 components, 所有的 sdkconfig 中定义的功能使能的选项, 最终应该会合并成一个, 这应该可以通过 kconfig 工具实现, 可以通过解析这个合并的后的config, 把所有=y的选项解析出来, 以 cargo:rustc-cfg=… 的形式, 输出到rust的编译环境, 这样 esp32的组件库 和 rust库 就可以共享同一套配置了.

rust这边, 通过 features 和 功能选项, 动态引入模块, 选择性地 编译 rust代码

项目中, 在build.rs中, 这行的作用就是 拿到 sys库的配置:

STM32F103 BluePill 总览

说明

  • 本目录用于收纳 STM32F103 BluePill 板卡相关的环境与外设实验记录.
  • 当前内容不多, 但已经足够作为该板卡的最小入口页.

当前主题

使用建议

  • 初次上手优先看开发环境页.
  • 需要看具体外设配置时, 再进入对应实验页.

STM32笔记

时钟选择

查询 STM32F1/F7中文参考手册, “2.2 存储器组织结构”, 查看使用的外设挂载在哪个总线上, 总线的速度可以通过 STM32CUBEMX 软件中查看到

windows 开发环境搭建

安装软件包管理工具choco

administrator权限打开powershell, 执行:
	Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

使用choco安装(admin权限):
	make - 构建工具
	openocd - debug server
	gcc-arm-embedded - 编译工具链

choco install make openocd gcc-arm-embedded

需要设置环境变量?

安装非choco管理的软件

这下面的在choco中没有找到, 需要手动安装:

	stm32cubemx:
		https://www.st.com/zh/development-tools/stm32cubemx.html

	stlink 驱动, 调试使用
		https://my.st.com/content/my_st_com/zh/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-utilities/stsw-link009.html

使用 cmsisdap 调试 stm32

创建文件 dap-stm32.cfg, 添加以下内容:

	interface cmsis-dap
	transport select swd
	source [find target/stm32f1x.cfg]

烧写程序:
    openocd -f dap-stm32.cfg -c "program build/stm32f103rct6.bin 0x8000000 reset exit"


用vscode调试, 安装插件: 1.c/c++ 2.cortex-debug

打开 launch.json, 新添加一项调试配置:

	{
		"name": "Cortex Debug",
		"cwd": "${workspaceRoot}",
		"executable": "build/stm32f103rct6.elf",
		"request": "launch",
		"type": "cortex-debug",
		"servertype": "openocd",
		"configFiles": [
			"dap-stm32.cfg"
		]
	}

2022.0624 – stm32f103 bluepill openocd

硬件接口:

openocd 调试命令:

openocd -f interface/stlink.cfg -f board/stm32f103c8_blue_pill.cfg

bluepill

使用 rt-thread

git clone https://github.com/RT-Thread/rt-thread.git

cd bsp/stm32/stm32f103-blue-pill

根据模板生成独立项目:

scons –dist

cd bsp/stm32/stm32f103-blue-pill/dist/stm32f103-blue-pill

生成 vscode 代码提示的配置:

scons –target=vsc

编译:

scons -j10

烧写:

openocd -f interface/stlink.cfg -f board/stm32f103c8_blue_pill.cfg -c “program rtthread.bin 0x8000000 reset exit”

arm none eabi gcc

使用最新版本的, 下载地址: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/downloads

运行 gdb 时, 会遇到 依赖库找不到的问题, 根据报错信息 安装就行, 其中有一个没办法: 找不到libpython3.6m.so.1.0, 可以源码编译, 这里是一个hack的解决方法: ln -s /usr/lib/x86_64-linux-gnu/libpython3.7m.so.1.0 /usr/lib/x86_64-linux-gnu/libpython3.6m.so.1.0

vscode 断点调试

使用默认生成的 cortex-debug launch配置, 稍加修改,

“executable”: “./rt-thread.elf”, “servertype”: “openocd”, “configFiles”: [ “interface/stlink.cfg”, “board/stm32f103c8_blue_pill.cfg” ]

添加通过编译器添加一个宏

最好的方式是:

在项目路径下的 /SConscript 文件中, 主要是如下代码:

objs = [] group = DefineGroup(‘Defines’, [], depend = [‘’], CPPDEFINES = [‘BSP_USING_PWM2_CH1’]) objs += group Return(‘objs’)

生成 vsc 的代码提示

scons –target=vsc

每一次 scons 的配置文件改变以后, 如果头文件或者是Define有改变, 则需要重新生成

STM32 PWM 配置记录

说明

  • 本页记录 STM32F103 Bluepill 使用 STM32CubeMX 配置 PWM 输出的最小流程.
  • 适合快速回忆定时器参数, 输出频率和占空比之间的关系.

CubeMX 配置示意

核心参数

  • PSC / Prescaler: 定时器预分频, 先把输入时钟降到目标范围.
  • ARR / Period: 自动重装值, 决定 PWM 周期长度.
  • CCR / Pulse: 比较值, 决定高电平持续时间.
  • Channel: 选择具体输出通道, 例如 TIMx_CH1.

常用估算公式:

PWM 频率 = timer_clk / ((PSC + 1) * (ARR + 1))
占空比 = CCR / (ARR + 1)

最小配置步骤

  1. 选择一个定时器通道, 模式设为 PWM Generation CHx.
  2. 根据目标频率设置 PrescalerCounter Period.
  3. 设置 Pulse 作为默认占空比.
  4. 检查输出引脚是否映射到正确的 GPIO 复用功能.
  5. 生成代码后, 在初始化完成后调用 HAL_TIM_PWM_Start().

调试排查

  • 没有波形时, 先检查 GPIO 复用是否正确, 以及是否真的启动了 PWM 通道.
  • 频率不对时, 先确认 APB 定时器时钟, 再检查 PSCARR.
  • 占空比异常时, 重点核对 PulsePeriod 的比值.
  • 若示波器看到恒高或恒低, 通常是 CCR 设置越界或通道未启动.

相关文档

AG32 平台总览

说明

  • 本目录收录 AG32 平台相关的开发环境和资料入口记录.
  • 当前重点是 AG32VF407 这类带 MCU + FPGA 特性的国产平台上手资料.

当前文档

建议阅读路径

  1. 先看 AG32VF407 开发环境, 了解官方资料入口和软件来源.
  2. 再根据具体芯片型号确认工具链, 驱动和示例工程版本.
  3. 真正开始开发前, 先跑通官方示例, 再进入联合编程或外设验证.

后续可补主题

  • 烧录流程与调试接口.
  • 工程模板和目录结构.
  • 常见报错与版本兼容问题.

AG32VF407 开发环境

说明

  • 本页整理 AG32VF407 平台的开发资料入口, 方便后续回溯环境搭建步骤.
  • 该平台可作为 MCU + FPGA 联合开发的入门记录页.

参考资料

环境准备思路

  1. 先确认具体芯片型号, 板卡版本和配套资料是否一致.
  2. 从官方软件页面准备 IDE, 工具链, 驱动和示例工程.
  3. 结合官方教程完成最小工程编译, 下载和串口输出验证.
  4. 如果涉及 MCU + FPGA 联合开发, 再进入联合编程文档核对工程配置.

资料用途划分

  • 官方教程: 用来建立最小开发环境和基础工作流.
  • 官方软件下载页: 用来统一获取工具链和配套资源.
  • AGM 支持站: 适合查更细的专题说明和问题定位资料.
  • 联合编程说明: 重点关注工程目录, 配置项和跨侧协同方式.

使用建议

  • 优先以官方示例工程作为第一轮验证对象, 不要一开始就接复杂业务代码.
  • 若编译或下载失败, 先检查驱动, 连接方式和工具版本是否与教程一致.
  • 若后续继续整理, 可补充“创建工程 -> 编译 -> 下载 -> 调试”的完整闭环记录.

RK3566 开发环境与镜像构建

说明

  • 本页记录 RK3566 平台在 Ubuntu 环境下准备工具链, 编译 u-boot 与 Linux 内核, 制作启动镜像的流程.
  • 适合作为 Rockchip 平台开发环境初始化与镜像构建的实践记录.
  • 本页中出现的 master 是经核对后仍存在的上游仓库分支名, 不是本仓库默认分支的建议.

sudo apt install device-tree-compiler git vim genext2fs

安装 python2

使用 pyenv 安装并管理 python

目的是 不污染系统环境, 只需要 删除某个目录就可以卸载所有 python 环境

## 自动安装 pyenv 环境

curl https://pyenv.run | bash

## 执行后, 手动添加到 ~/.bashrc, 并执行

export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

安装 python2 最新版

pyenv install 2.7.18

使用

pyenv global 2.7.18

下载

准备目录

mkdir ~/develop && cd ~/develop

下载 u-boot 源码

git clone https://github.com/rockchip-linux/u-boot.git -b next-dev

下载 rkbin 到 u-boot 同级目录

git clone https://github.com/rockchip-linux/rkbin.git

下载 linux 内核 源码

git clone https://github.com/rockchip-linux/kernel.git -b develop-5.10

下载 编译环境 (Firmware and Tool Binarys)

mkdir -p prebuilts/gcc && cd prebuilts/gcc

git clone https://github.com/rockchip-toybrick/linux-x86.git

编译

编译 uboot

cd ~/develop/u-boot

./make.sh rk3566

会在当前路径下生成 uboot.img 和 rk356x_spl_loader_v1.18.112.bin 文件

编译 linux 源码

cd ~/develop

git clone https://github.com/rockchip-linux/kernel.git -b develop-5.10

cd ~/develop/kernel

make ARCH=arm64 CROSS_COMPILE=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- rockchip_linux_defconfig

# 生成 vmlinux 和 arch/arm64/boot/Image
make ARCH=arm64 CROSS_COMPILE=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- -j8 Image

# 生成设备树 arch/arm64/boot/dts/rockchip/rk3566-box-demo-v10.dtb
make ARCH=arm64 CROSS_COMPILE=../prebuilts/gcc/linux-x86/aarch64/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- -j8 dtbs

ls -alh arch/arm64/boot/dts/rockchip/rk3566-*

制作镜像文件

mkdir boot

cp arch/arm64/boot/Image boot/

cp arch/arm64/boot/dts/rockchip/rk3566-box-demo-v10.dtb boot/

mkdir boot/extlinux vi boot/extlinux/extlinux.conf

label rockchip-kernel-5.10
    kernel /Image
    fdt /rk3566.dtb
    append earlycon=uart8250,mmio32,0xff1a0000 root=PARTUUID=B921B045-1D rootwait rootfstype=ext4 init=/sbin/init

genext2fs -b 40960 -B $((1024)) -d boot/ -i 8192 -U boot_rk3566.img

生成的文件: boot_rk3566.img

烧写工具

https://doc.embedfire.com/linux/rk356x/quick_start/zh/latest/quick_start/flash_img/flash_img.html

1.2.1.1. 工具获取 镜像的烧录需要使用到烧录工具 RKDevTool 和驱动程序 DriverAssitant

分析 uboot

u-boot/include/configs/

evb_rk3568.h rk3568_common.h rockchip-common.h

定义了一些配置, 如: 分区, 可参考

// 如果 tftp 和 bootm, 未定义加载地址, 则默认使用该地址
#define CONFIG_SYS_LOAD_ADDR		0x00c00800
# 从tftp下载uImage镜像到 CONFIG_SYS_LOAD_ADDR 这个地址上, 并从该地址处 加载 并运行.
tftp uImage; bootm

uboot 的 bootm 命令

用于把内核加载到指定地址处运行

测试 img 镜像

Orangepi3b_1.0.2_ubuntu_focal_server_linux5.10.160.img

sudo fdisk -lu Orangepi3b_1.0.2_ubuntu_focal_server_linux5.10.160.img

可以看到有两个分区.

# 挂载分区

sudo mkdir -p /mnt/boot /mnt/linux

# 起始扇区: 61440, 每个扇区 512 bytes
sudo mount -o loop,offset=$((61440*512)) Orangepi3b_1.0.2_ubuntu_focal_server_linux5.10.160.img /mnt/boot
# sudo umount /mnt/boot

# 起始扇区: 2158592, 每个扇区 512 bytes
sudo mount -o loop,offset=$((2158592*512)) Orangepi3b_1.0.2_ubuntu_focal_server_linux5.10.160.img /mnt/linux
# sudo umount /mnt/linux

使用 orangepi-build 编译

下载 orangepi-build

mkdir ~/develop && cd ~/develop

git clone https://github.com/orangepi-xunlong/orangepi-build.git -b next

cd orangepi-build

忽略修改被覆盖

vim userpatches/config-default.conf

IGNORE_UPDATES=“yes”

编译

连续执行 sudo ./build.sh 分别编译 U-boot / Kernel

重新编译

uboot

git clone https://github.com/rockchip-linux/u-boot.git -b next-dev

编译工具

git clone https://github.com/rockchip-linux/rkbin.git

git clone https://github.com/rockchip-toybrick/linux-x86.git -b master

注: 这里的 master 是 rockchip-toybrick/linux-x86 当前仍在使用的上游分支名. 执行前仍建议先确认 tag 和分支状态.

安装基本依赖

sudo apt-get install build-essential libncurses-dev bison flex libssl-dev libelf-dev genext2fs vim git tree

ubuntu 22.04 交叉编译工具

wget https://mirrors.tuna.tsinghua.edu.cn/armbian-releases/_toolchain/gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz

tar -xvf gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu.tar.xz

linux 6.5.8 源码

wget https://mirrors.tuna.tsinghua.edu.cn/kernel/v6.x/linux-6.5.8.tar.xz

# 加压到了 linux-6.5.8 目录
tar -xvf linux-6.5.8.tar.xz

# 编译
cd linux-6.5.8

# 定制的 ./arch/arm64/configs/rockchip_linux_defconfig

export ARCH=arm64
export CROSS_COMPILE=../gcc-arm-11.2-2022.02-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-

make rockchip_linux_defconfig

# 生成文件: vmlinux 和 arch/arm64/boot/Image
make -j$(nproc) Image

#
make -j$(nproc) dtbs

mkdir boot

cp arch/arm64/boot/dts/rockchip/rk3566-box-demo.dtb boot/
cp arch/arm64/boot/Image boot/

mkdir boot/extlinux

vi boot/extlinux/extlinux.conf
label rockchip-kernel-6.5.8
    kernel /Image
    fdt /rk3566.dtb
    append earlycon=uart8250,mmio32,0xfe660000 root=PARTUUID=B921B045-1D rw rootwait rootfstype=ext4 init=/sbin/init

genext2fs -b 40960 -B 1024 -d boot/ -i 8192 -U boot_rk3566.img

使用 busybox 制作根文件系统

参考: https://www.cnblogs.com/zyly/p/17438770.html

wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2

tar -xvf busybox-1.36.1.tar.bz2

cd busybox-1.36.1

export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-

# 使用默认配置
make defconfig

make menuconfig

# 使用 动态编译
Settings --->
    -- Build Options
    [ ] Build static binary (no shared libs)
    [*] Build Shared libbusybox

make -j$(nproc)

# 根文件系统将输出在 rootfs 目录下
make install CONFIG_PREFIX=rootfs

这里的 rootfs 是这样的:

imxood@vm-imxood-pc:~/develop/busybox-1.36.1$ tree -L 1 rootfs/
rootfs/
├── bin
├── lib64
├── linuxrc
├── sbin
└── usr

这些文件不足以构成根文件系统, 需要进行完善.

添加库文件

cd rootfs mkdir -p lib ./usr/lib/ ./usr/lib64/

cp -a ../../gcc-arm-11.2-2022.02-x8664-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/lib/_so ./lib cp -a ../../gcc-arm-11.2-2022.02-x8664-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/lib64/_so ./lib64/ cp -a ../../gcc-arm-11.2-2022.02-x8664-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/lib/_so ./usr/lib/ cp -a ../../gcc-arm-11.2-2022.02-x8664-aarch64-none-linux-gnu/aarch64-none-linux-gnu/libc/usr/lib64/_so ./usr/lib64/

构建 etc 目录

mkdir etc

# 使用 busybox 的默认配置
cp -a ../examples/bootfloppy/etc/* ./etc/

修改 etc/inittab

这个文件是 init 进程解析的配置文件, 通过这个配置文件决定执行哪个进程, 何时执行

# 系统启动时
::sysinit:/etc/init.d/rcS

# 系统启动按下Enter键时
::askfirst:-/bin/sh

# 按下Ctrl+Alt+Del键时
::ctrlaltdel:/sbin/reboot

# 系统关机时
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

# 系统重启时
::restart:/sbin/init

以上内容定义了系统启动时, 关机时, 重启时, 按下 Ctrl+Alt+Del 键时执行的进程

修改 etc/init.d/rcS 文件

#!/bin/sh

# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a

# 挂载虚拟的 devpts 文件系统 用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# 使用 mdev 动态管理u盘和鼠标等热插拔设备
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug

# 扫描并创建节点
/sbin/mdev -s

修改 init.d 文件权限: chmod -R 777 etc/init.d/*

修改 etc/fstab

etc/fstab 存放的是文件系统信息. 在系统启动后执行 /etc/init.d/rcS 中的 /bin/mount -a 命令时, 自动挂载这些文件系统

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0

这里我们挂载的文件系统有三个 proc sysfs 和 tmpfs, 在内核中 proc 和 sysfs 默认都支持, 而 tmpfs 是没有支持的, 我们需要添加 tmpfs 的支持

修改 etc/profile

etc/profile 的作用是设置环境变量, 每个用户登录时都会运行它, 将文件内容修改为:

# 主机名
export HOSTNAME=zy

# 用户名
export USER=root

# 用户目录
export HOME=/root

# 终端默认提示符
export PS1="[$USER@$HOSTNAME:\$PWD]\# "

# 环境变量
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# 动态库路径
export LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH

构建 dev 目录

在 rootfs 目录, 创建 dev 文件夹:

mkdir dev

sudo mknod dev/console c 5 1 sudo mknod dev/null c 1 3

构建其他文件

mkdir mnt proc tmp sys root

tree -L 1
.
├── bin
├── dev
├── etc
├── lib
├── lib64
├── linuxrc
├── mnt
├── proc
├── root
├── sbin
├── sys
├── tmp
└── usr

制作 ext4 根文件系统 镜像

cd ~/develop/busybox-1.36.1

mkdir busybox_rootfs

# 创建 300M 的空镜像文件, 具体大小 视情况而定
dd if=/dev/zero of=busybox_ext4_rootfs.img bs=1M count=300

# 格式化为 ext4
mkfs.ext4 busybox_ext4_rootfs.img

sudo mount busybox_ext4_rootfs.img busybox_rootfs

sudo cp -af ./rootfs/* ./busybox_rootfs/

sudo umount busybox_rootfs

rm busybox_rootfs -rf

用 e2fsck 修复及检测镜像文件系统, resize2fs 减小镜像文件的大小

imxood@vm-imxood-pc:~/develop/busybox-1.36.1$ e2fsck -p -f busybox_ext4_rootfs.img
busybox_ext4_rootfs.img: 506/76800 files (0.2% non-contiguous), 26813/76800 blocks

imxood@vm-imxood-pc:~/develop/busybox-1.36.1$ resize2fs -M busybox_ext4_rootfs.img
resize2fs 1.46.5 (30-Dec-2021)
Resizing the filesystem on busybox_ext4_rootfs.img to 26912 (4k) blocks.
The filesystem on busybox_ext4_rootfs.img is now 26912 (4k) blocks long.

imxood@vm-imxood-pc:~/develop/busybox-1.36.1$ ls -alh busybox_ext4_rootfs.img
-rw-rw-r-- 1 imxood imxood 106M 10月 24 01:51 busybox_ext4_rootfs.img

最后得到 104MB 的 busybox_ext4_rootfs.img 镜像文件

使用 Ubuntu base 构建 rootfs

参考: https://www.cnblogs.com/zyly/p/17442055.html

下载 ubuntu-base 23.10

wget https://repo.huaweicloud.com/ubuntu-cdimage/ubuntu-base/releases/23.10/release/ubuntu-base-23.10-base-arm64.tar.gz

# 创建多级目录
mkdir -p rootfs/ubuntu

解压后的文件, 需要保留 ubuntu-base 中的文件权限及所有者, 解压时需要 root 权限或者 sudo 操作, 且使用-p 参数保留权限

sudo tar -xpvf ubuntu-base-23.10-base-arm64.tar.gz -C rootfs/ubuntu/

cd rootfs

安装 qemu

qemu-user-static 是一个仿真器, 可以选取 arm64 配置文件仿真开发板运行环境, 然后挂载下载的 ubuntu-base 文件, 从而构建 ubuntu 文件系统

sudo apt-get install qemu-user-static

由于下载的 ubuntu-base 是 aarch64 架构的, 因此需要拷贝 qemu-aarch64-static 到 ubuntu/usr/bin/ 下

sudo cp /usr/bin/qemu-aarch64-static ubuntu/usr/bin/

设置 ubuntu23 的软件源

sudo vim ubuntu/etc/apt/sources.list
deb http://mirrors.huaweicloud.com/ubuntu-ports/ mantic main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-backports main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-proposed main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-security main multiverse restricted universe
deb http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-updates main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ mantic main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-backports main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-proposed main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-security main multiverse restricted universe
deb-src http://mirrors.huaweicloud.com/ubuntu-ports/ mantic-updates main multiverse restricted universe

设置 DNS

为了可以联网更新软件, 我们拷贝本机的 dns 配置文件到根文件系统

sudo cp /etc/resolv.conf ubuntu/etc/resolv.conf

然后在/etc/resolv.conf 文件中添加 dns:

sudo vim ubuntu/etc/resolv.conf
nameserver 8.8.8.8
nameserver 114.114.114.114

挂载/卸载 ubuntu-base 文件系统

在 rootfs 目录下创建挂载脚本 mnt_ubuntu.sh

sudo vim mnt_ubuntu.sh

#!/bin/bash
mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc ${2}proc
    sudo mount -t sysfs /sys ${2}sys
    sudo mount -o bind /dev ${2}dev
    sudo mount -o bind /dev/pts ${2}dev/pts
    sudo chroot ${2}
}
umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}dev
}

if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : ${1}
    echo 2nd parameter : ${2}
fi

给脚本执行权限

sudo chmod +x mnt_ubuntu.sh

挂载 ubuntu-base 文件系统

mnt_ubuntu.sh -m ubuntu/

更新软件包仓库, 安装一些工具

apt update

apt install net-tools ethtool ifupdown psmisc nfs-common htop vim rsyslog iputils-ping language-pack-en-base sudo network-manager systemd openssh-sftp-server

安装桌面环境

# 需要3个G?
apt install xubuntu-desktop

sudo add-apt-repository multiverse

# (可选的安装一些软件包, 如 媒体播放器?)
sudo apt install xubuntu-restricted-extras

系统设置

修改密码

passwd root

设置主机名称和IP

echo "rk3566" > /etc/hostname
echo "127.0.0.1 localhost" >> /etc/hosts
echo "127.0.0.1 rk3566" >> /etc/hosts

配置DHCP

配置了网络DHCP, 这样系统启动以后就会自动设置好网络.

网卡eth0:

echo auto eth0 > /etc/network/interfaces.d/eth0
echo iface eth0 inet dhcp >> /etc/network/interfaces.d/eth0

无线网卡wlan:

echo auto wlan0 > /etc/network/interfaces.d/wlan0
echo allow-hotplug wlan0 >> /etc/network/interfaces.d/wlan0
echo iface wlan0 inet dhcp >> /etc/network/interfaces.d/wlan0

在实际测试中网口必须接入网线系统才能正常启动, 就是在不联网的情况下, 每次开机都要等待很久, 卡在网络连接上5分钟, 这里我们可以修改下面这个文件:

vim /lib/systemd/system/networking.service
TimeoutStartSec=2sec

修改系统重启 默认等待时间

重启开发板的时候, 如果有进程没有结束, 系统就会等待, 默认等待时间很长, 导致重启速度慢.

我们可以修改默认等待时间:

vim /etc/systemd/system.conf
#DefaultTimeoutStartSec=90s
DefaultTimeoutStopSec=2s
#DefaultTRestartSec=100ms

卸载文件系统

exit
./mnt_ubuntu.sh -u ubuntu/

制作根文件系统镜像

创建空镜像文件

dd if=/dev/zero of=ubuntu_ext4_rootfs.img bs=1M count=6144
mkfs.ext4 ubuntu_ext4_rootfs.img

挂载镜像文件到 ubuntu_rootfs

mkdir ubuntu_rootfs
sudo mount ubuntu_ext4_rootfs.img ubuntu_rootfs

将ubuntu的文件复制到该空文件夹中

sudo cp -af ./ubuntu/* ./ubuntu_rootfs/

卸载

umount ubuntu_rootfs
ls -alh ubuntu_ext4_rootfs.img
rw-rw-r-- 1 imxood imxood 6.0G 10月 25 00:40 ubuntu_ext4_rootfs.img

此时就得到了 ext4 根文件系统镜像 ubuntu_ext4_rootfs.img

用 e2fsck 修复及检测镜像文件系统, resize2fs 减小镜像文件的大小

e2fsck -p -f ubuntu_ext4_rootfs.img
resize2fs -M ubuntu_ext4_rootfs.img

du -sh ubuntu_ext4_rootfs.img

3.9G	ubuntu_ext4_rootfs.img

/home/imxood/develop/rootfs/ubuntu_ext4_rootfs.img

CH32V307VCT6 疑难问题

说明

  • 本页记录 CH32V307VCT6 在烧录, USB 下载和驱动适配过程中的典型问题.
  • 适合作为 WCH 平台联调和 WinUSB 驱动排障的快速参考.
  1. 无论如何烧写 都无法成功运行程序

烧写别的程序都能运行成功, 但是自己写的程序就是无法运行, 三四个小时后发现 原来是我修改过 LD文件中的存储布局, 但是 ISH软件中的 布局没有自动保持一致, 所以, 无论如何都跑不起来. 修改 ISH软件的“芯片内存布局“, 与固件的一致就跑起来了

使用 isp工具 通过 USB 或者 串口 下载程序

串口引脚是 PA9(TX) PA10(RX)

USB 开发

使用 例子 CH32V307EVT\EVT\EXAM\USB\USBHS\DEVICE\CH372Device

运行默认的代码, 连接电脑后, 驱动是 ch372设备驱动, 这个驱动 无法执行 usb_open 操作!!!

当修改 vid 之后, 就没有 ch372设备, 但是没有驱动了, 使用 zadig软件 安装驱动, 驱动选择 WinUSB 版本, 可以执行 usb的读写设备管理器中会多了一个: 通用串行总线设备/CH32V30x

WinUSB 支持 x64/x86

libusb-win32 (v1.2.6.0), 似乎支持x64, 在x86程序 无法open, 会提示 不正确的权限?

FPGA 总览

说明

  • 本目录收录 FPGA 开发环境, Verilog 基础, 仿真工具和特定平台记录.
  • 当前内容以入门实践和工具配置为主, 适合作为后续继续整理的入口页.

常用入口

建议阅读路径

  1. 初学语法和时序概念时, 先看 Verilog / FPGA 要点.
  2. 只做小型仿真实验时, 可先用 iverilog 跑最小 testbench.
  3. 需要更完整的波形和工程级调试时, 再看 ModelSim.
  4. 真正上板开发时, 再进入 Vivado 和具体平台工具链记录.

常见关注点

  • 先区分“语法与时序基础”和“工具链配置问题”.
  • 上板前要先处理时钟, 约束文件和引脚映射问题.
  • 遇到结果不符合预期时, 先用仿真验证逻辑, 再排查板级连接和综合实现问题.
  • 工程稍大时, 建议尽早建立 testbench 和模块化层次结构.

后续可补主题

  • 约束文件与引脚分配.
  • 时钟域与复位设计.
  • 状态机模板与仿真模板.
  • 资源利用率, 时序分析和上板调试流程.

Vivado

说明

  • Vivado 是 Xilinx FPGA 常用开发工具链, 适合做工程创建, 约束配置, 综合, 实现和下载调试.
  • 当前记录以 XC7A35 开发板为起点.

环境准备

  • 安装对应版本的 Vivado, 当前笔记基于 Vivado 2018.
  • 若开发板不在默认列表中, 还需要额外安装板卡描述文件.

板卡文件参考:

  • https://github.com/Digilent/vivado-boards

常见放置路径示例:

/develop/programs/Xilinx/Vivado/2018.3/data/boards/board_files

常见工作流

  • 创建工程并选择目标器件或板卡.
  • 添加 Verilog / VHDL 源码和约束文件.
  • 运行综合与实现.
  • 生成 bitstream 后下载到开发板.

排查建议

  • 先确认器件型号和约束文件是否匹配.
  • 时序不过时, 优先检查时钟定义, IO 约束和跨时钟域处理.
  • 下载失败时, 再检查 JTAG 连接, 驱动和硬件供电.

相关文档

Verilog / FPGA 要点

赋值规则

  • 阻塞赋值: =
  • 非阻塞赋值: <=

常见经验:

  • 组合逻辑优先使用阻塞赋值.
  • 时序逻辑优先使用非阻塞赋值.

避免锁存器

锁存器通常出现在组合逻辑描述不完整时.

常见规避方式:

  • if / else 要写完整.
  • case 要补齐 default.
  • 对输出信号要保证所有分支都有确定赋值.

组合逻辑与时序逻辑

组合逻辑

常见写法:

  • assign
  • always_comb 或等效组合逻辑块

时序逻辑

  • 常用寄存器把组合逻辑结果打一拍, 便于消除毛刺和做时序收敛.
  • 对总线输出, 加一级寄存器也更利于数据对齐.

学习建议

  • 先把组合逻辑, 时序逻辑, 时钟域和复位策略分清楚.
  • 再进入仿真, 约束和上板调试.
  • 出现异常时, 先用仿真确认功能, 再回头看时序与引脚约束.

iverilog

说明

  • iverilog 是轻量级 Verilog 仿真工具, 很适合做语法验证和小型实验.
  • 在正式上板前, 可先用它快速验证模块行为是否符合预期.

典型用途

  • 编译 Verilog 源码与 testbench.
  • 结合 vvp 执行仿真.
  • 配合波形文件查看时序行为.

使用建议

  • 先写最小可复现的 testbench.
  • 重点验证状态机切换, 计数器边界和接口时序.
  • 若项目复杂度提升, 再切换到更完整的仿真工具链.

参考

  • 教程: https://zhuanlan.zhihu.com/p/148795858
  • 相关文档: FPGA 总览

ModelSim

说明

  • ModelSim 是常见的 HDL 仿真工具, 适合做更完整的波形分析与模块联调.
  • 相比更轻量的命令行仿真工具, 它更适合工程级排错, 波形观察和交互式调试.

常见用途

  • 编译 Verilog / VHDL 工程.
  • 运行 testbench 并查看波形.
  • 做模块级联调和时序问题排查.
  • 观察信号状态和仿真日志输出.

基本工作流

  1. 创建或映射库目录.
  2. 编译源文件与 testbench.
  3. 启动仿真并加载顶层模块.
  4. 打开波形窗口观察关键时序.
  5. 根据日志和波形继续定位问题.

中文乱码处理

在菜单中进入:

Tools -> Edit Preferences -> By Name -> Source -> encoding

将编码设置为:

  • utf-8

使用建议

  • 仿真工程中的源码文件统一使用 UTF-8 编码.
  • 工程稍复杂时, 建议固定一个最小可复现 testbench, 便于快速回归.
  • 若波形或脚本仍异常, 再检查工具版本与项目文件默认编码.
  • iverilog 相比, ModelSim 更适合做完整工程级调试.

后续可补主题

  • vlog / vsim 常用命令.
  • 波形窗口常见操作.
  • 仿真脚本自动化.
  • Vivado / Quartus 工程的协同流程.

AGM 平台与工具链

说明

  • 本文记录 AGM 平台相关的 FPGA 开发环境搭建与项目编译流程.
  • 当前重点包括 Quartus II, ModelSim, verilog-format 和项目烧写流程.

环境准备

Quartus II 与 USB Blaster

  • 安装 Quartus II 13.1 官方安装包.
  • 安装 USB Blaster 驱动.
  • 如有历史环境需求, 再按项目要求处理兼容性或授权问题.

ModelSim

  • 安装 modelsim-win64-10.4-se.
  • 具体版本需与项目环境保持一致.

VSCode 中编辑 Verilog

verilog-format

  • 项目地址: https://github.com/ericsonj/verilog-format
  • 若直接使用存在问题, 需要按项目说明修改后重新编译.

常见步骤:

mvn clean package

执行 target/verilog-format.exe 前, 需要确保本机存在 JDK 环境.

其他工具

iverilog

  • Windows 下载地址: https://bleyer.org/icarus/
  • 插件安装说明: https://www.youtube.com/watch?v=vN1wzM0NO4c

AGM 项目流程

典型流程示例:

  1. 使用 pio unlock flash
  2. 进入烧写模式
  3. PlatformIO 中执行 Upload LOGIC 等自定义流程
  4. 进入正常模式

CustomIP

platformio.ini 中可配置:

ip_name = custom_ip
logic_dir = logic

常见流程:

  • 执行 Prepare LOGIC 生成 logic 目录.
  • 使用 QuartusII 打开 logic/example_board.qpf 编译.
  • 或使用 Supra 编译 logic/example_board.proj.

rt-thread

文档

https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/README

开发环境: 
    硬件: Art-pi stm32h750
    开发环境: Deepin OS

入门

环境:

    sudo apt-get install libncurses5-dev

    pip3 install --user scons 或 sudo apt-get install scons

    可选: sudo apt-get install qemu


代码:
    
    git clone https://github.com/RT-Thread/rt-thread.git

    cd rt-thread/bsp/stm32/stm32h750-artpi-h750

编译
    scons

烧写:

    openocd -f board/stm32h750b-disco.cfg

    arm-none-eabi-gdb -ex "target extended-remote :3333" ./rt-thread.elf

        monitor halt
        load

    telnet 127.0.0.1 4444

        load_image ./rt-thread.elf
        reset run

更新 在线包

使用 "scons --menuconfig" 后 会默认安装及初始化Env环境, 并在home目录下生成".env"目录

执行 "source ~/.env/env.sh" 才可以使用 pkgs 命令

pkgs --update

配置 wifi tcp 通信

git clone https://github.com/RT-Thread-Studio/sdk-bsp-stm32h750-realthread-artpi.git

cd sdk-bsp-stm32h750-realthread-artpi/projects/art_pi_wifi

先创建链接:
    ln -s ../../rt-thread rt-thread
    ln -s ../../libraries libraries

就可以运行 GUI 了:
    scons --menuconfig

生成 vscode 项目:
    scons --target=vsc

编译:
    scons -j 40

烧写:
    st-flash write rtthread.bin 0x8000000

烧写后, 看串口终端有了 输出. 网络初始化后,

扫描 wifi:
    wifi scan

加入热点:
    wifi join HOT-NAME HOT-PASSWORD

flash

Flash 存储是按块组织的, 在使用时也倾向于按块访问才更加高效.

    在写入数据时, 需要先将所写位置所属的块擦除, 不管你是不是只写一个字节. 所以如果要改写Flash中的数据, 总是先将数据所属的块缓存到内存中, 然后再在内存中改写好数据后又重新写回块, 这样就不会丢失数据, 但是开销很大.

    在读数据时, 往往也是先定位块的位置, 然后在块中顺序读取, 在不同块中 间断读取数据时非常低效的.

但是: NOR Flash 读取数据时可以做到任意的寻址而不会有太大的花销, 它的读操作是接近于RAM的,而写操作依然延续了按块擦除然后再按块写的特点

正因为这些特性,Flash通常用于存储不需要频繁改动的掉电不能丢失的数据

Rust嵌入式开发环境

vscode 开发环境, 硬件: stm32f746g-disco

安装必要的工具:

    cargo install cargo-generate
    rustup target add thumbv7em-none-eabihf

    rustup component add llvm-tools-preview
    cargo install cargo-binutils

生成项目:
    cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart

    cd <DIR>

重命名 .cargo/config 到 .cargo/config.toml (不改也可以, 都支持, 改了有高亮显示):

    [build]
    target = "thumbv7em-none-eabihf"     # Cortex-M4F and Cortex-M7F (with FPU)

修改 memory.x 中的 FLASH 和 RAM 的源地址及大小:
    FLASH : ORIGIN = 0x08000000, LENGTH = 1M
    RAM : ORIGIN = 0x20000000, LENGTH = 256K

更新一下 Cargo.toml 中依赖的版本.

编译:
    cargo build

查看大小
    cargo size --bin app
    cargo size --bin app -- -A

查看二进制信息:
    cargo readobj --bin app -- -file-headers

反汇编二进制文件:
    cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex

调试, 在launch文件中添加:
    {
		/* Configuration for the STM32F303 Discovery board */
		"type": "cortex-debug",
		"request": "launch",
		"name": "Debug (OpenOCD)",
		"servertype": "openocd",
		"cwd": "${workspaceRoot}",
		"preLaunchTask": "Cargo Build (debug)",
		"runToMain": true,
		"executable": "./target/thumbv7em-none-eabihf/debug/app",
		"configFiles": ["interface/stlink.cfg", "board/stm32f746g-disco.cfg"],
	}

优化

官方文档写的很清晰

The Embedded Rust Book - Optimizations: the speed size tradeoff

C语言

编译过程

预处理: 展开头文件及宏定义
	gcc -E -I./inc test.c -o test.i
	cpp test.c -I./inc -o test.i

编译: 将预处理的代码翻译成汇编代码
	gcc -S -I./inc test.c -o test.s

汇编: 将汇编代码翻译成机器码, 这一步生成二进制格式的目标文件
	as test.s -o test.o

链接: 将目标文件和库文件链接成最后的可执行程序
	ld -o test.out test.o inc/mymath.o ...libraries...

内存布局

栈区

	局部变量, 编译器在编译时已经确定了栈的大小


堆区

	malloc分配的内存, 程序员自己控制 分配与释放


数据区

	全局区(静态区)
		如果已初始化, 放在DATA段
		如果未初始化, 放在BSS段, 这里只保存必要的大小信息, 不占用可执行程序的大小, 加载程序时分配内存

	常量区
		常量字面量


代码区

	函数定义

大小端 位域

大小端:
    intel芯片用的是小端, 就是内存是递增的, 数据是按照字节存放的, 低位数据放在低地址上, 不符合人类的阅读顺序, 比如: 对int类型数据 它的数据是: b3 b2 b1 b0, 内存从低到高: b0 b1 b2 b3

结构体对齐

  1. 第一个成员的偏移量为0

  2. 其它成员的偏移量是其对齐数的整数倍

  3. 结构体的大小为最大对齐数的整数倍

    例子: struct One { char a; double b; short c; int d; char e; }; 规则1: a的偏移量是0 规则2: 由于b的对齐数是8,所以1个字节补7个字节,b的偏移量为 0 + 1 + 7 = 8 由于c的对齐数是2,c前面的长度是 8 + 8 = 16, 是2的倍数, 所以c的偏移量是 16 由于d的对齐数是4,d前面的长度是 16 + 2 = 18, 不是4的倍数, 补2个字节, d的偏移量是 18 + 2 = 20 由于e的对齐数是1,e前面的长度是 20 + 4 = 24, 是1的倍数, d的偏移量就是 24 整体的大小为 24 + 1 = 25 规则3: 由于25不是最大对齐数的整数倍, 所以补7个字节, 为32

    另外:

     如果使用了 #pragma pack(4) 这个宏可以改变最大对齐数, 这意味着比如double的对齐数就是4.
     也可以使用: __attribute__((__aligned__(4)))
    
     #pragma pack(4)
    
     struct One {
     	char a;
     	double b;
     	short c;
     	int d;
     	char e;
     };
    
     规则1: a的偏移量是0
     规则2:
     	由于b的对齐数是4,所以1个字节补3个字节,b的偏移量为 0 + 1 + 3 = 4
     	由于c的对齐数是2,c前面的长度是  4 + 8 = 12, 是2的倍数, 所以c的偏移量是 12
     	由于d的对齐数是4,d前面的长度是 12 + 2 = 14, 不是4的倍数, 补2个字节, d的偏移量是 14 + 2 = 16
     	由于e的对齐数是1,e前面的长度是 16 + 4 = 20, 是1的倍数, d的偏移量就是 20
     	整体的大小为 20 + 1 = 21
     规则3:
     	由于21不是最大对齐数的整数倍, 所以补3个字节, 为24
    

gcc中常用属性

用于设置编译器的一些特殊行为

设置对齐字节数:
	__attribute__((__aligned__(4)))

取消优化对齐, 按照实际字节数存储
	__attribute__((packed))

将函数或数据放到特定的代码段:
	__attribute__((section("section-name")))

阻止函数内联:
	__attribute__((noinline))

让函数总是内联:
	__attribute__((__always_inline__))

设置特定函数的优化级别, O0,O1,O2,O3:
	__attribute__((optmize("Ox")))

gcc 优化

优化级别

O0,O1,O2,O3,Os

优化方法

通过给gcc参数:

gcc -O2 ...

通过代码:

给这行代码以下的代码设置优化级别:
	#pragma GCC optimize ("O3")

给特定函数设置属性:
	__attribute__((optmize("O3")))

main函数之前发生的事

大概是:

  1. 设置栈帧
  2. 设置bss区域数据为0
  3. 如果需要, 执行 hardware/software init
  4. 配置参数
  5. 调用main
  6. 执行exit.

SDCC 编译

说明

  • SDCC 是面向 8 位和部分小型 MCU 的开源 C 编译器工具链.
  • 本页记录源码获取, 最小编译步骤和使用时的关注点, 适合快速搭建实验环境.

获取源码

最小编译流程

./configure --disable-pic14-port --disable-pic16-port
make -j

参数说明

  • --disable-pic14-port / --disable-pic16-port: 如果当前不需要对应目标架构, 可以先关闭以减少编译范围和依赖复杂度.
  • 若只是为了先跑通工具链, 优先保留最小目标集合, 等环境稳定后再逐步补齐其它 target.

使用建议

  • 先确认目标 MCU 是否在 SDCC 的支持范围内.
  • 若构建失败, 优先检查依赖库, shell 环境和源码版本是否匹配.
  • 若项目只是简单 8051 / 小型 MCU 实验, SDCC 往往足够轻量; 若目标是更复杂的 ARM 系列, 通常应优先使用对应官方或主流工具链.

后续方向

  • 后续可继续补 安装依赖, 常见 target, 链接脚本, 烧录流程与 IDE 集成 等内容.

PlatformIO

说明

  • 本页记录在 VSCode 中使用 PlatformIO 搭建 Arduino / ESP32 工程的要点.
  • 重点包括库管理, ESP-IDF 版本切换和串口监视器参数配置.

vscode platformio arduino 环境

vscode 插件中安装 platformio

创建新项目, 设置 esp32 项目

添加库

在 Library 中搜索并添加到项目中

修改默认 idf 版本

需要注意的是: platformio 当前使用的 esp32 idf 的版本是 4.4.7?

修改:

在 platformio.ini 文件:

设置:

platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.11/platform-espressif32.zip

即可使用 5.3.2.241224 版本

设置 monitor 的波特率

修改 platformio.ini 文件:

monitor_speed = 115200

platformio.ini 的更多设置, 参考

https://docs.platformio.org/en/latest/platforms/espressif32.html

GDB 用法

参考: 100个gdb小技巧

GDB 命令行中调试

连接stm32设备:
    openocd -f interface/stlink.cfg -f board/stm32f746g-disco.cfg,

运行GDB:
    arm-none-eabi-gdb -q target/thumbv7em-none-eabihf/debug/app

连接 openocd server:
    target remote :3333

加载程序:
    load

显示内存:
    x/2xw 0x60000000

    x/6xb 0x50040AB8

设置寄存器的值:
    set $pc=0x60011691
    set $sp=0x6004e5a0

继续:
    c

显示变量:
    info local VARIABLE_NAME

gdb命令的参数

设置
--command=FILE

调试 STM32F767

说明

  • 本页记录在 Ubuntu 环境中安装 stlink 并处理动态库加载问题的过程.
  • 适合作为 STM32 调试链路与 st-info 环境排障的快速参考.

ubuntu下, 安装stlink驱动

git clone https://github.com/stlink-org/stlink.git

mkdir build && cd build

# sudo apt install libusb-1.0-0-dev

cmake ..

make -j4

sudo make install

sudo systemctl restart udev

st-info --version

如果报错:
    error while loading shared libraries: libstlink.so.1: cannot open shared object file: No such file or directory

解决:

    ldd /usr/local/bin/st-info

    发现:
        libstlink.so.1 => not found
    但是, /usr/local/lib/libstlink.so.1, 是存在的

    执行:
        sudo ldconfig
    目的是更新动态链接库缓存, 便于搜索动态库, 问题解决了
    这个可以自定义动态库的路径, 比如 /etc/ld.so.conf

Black Magic Probe

说明

  • 本页记录把 STM32F103 板子改造成 Black Magic Probe 的最小流程.
  • 适合回看编译, 烧录 bootloader 和刷入 BMP 固件的关键步骤.

参考: https://paramaggarwal.medium.com/converting-an-stm32f103-board-to-a-black-magic-probe-c013cf2cc38c

编译 blackmagic

git clone https://github.com/blacksphere/blackmagic.git
cd blackmagic
make -j
make clean -j && make PROBE_HOST=stlink -j

得到:
    bootloader:
        src/blackmagic_dfu.bin
    bmp:
        src/blackmagic.bin

Flash new bootloader

flash烧写

使用 stlink 调试器, 使用软件 STM32CubeProgrammer 烧写 src/blackmagic_dfu.bin

串口烧写

使用 stm32loader 或是 STM32CubeProgrammer 通过 serial 烧写fw:
    设置 boot0 --> 1, boot1 --> 0, 从系统存储器启动, 即 串口下载模式

pip3 install --user stm32loader

stm32loader -p /dev/ttyUSB0 -e -w -v src/blackmagic_dfu.bin

Flash BMP

烧写之后(如果没有自动reset, 按reset按钮), 执行 dmesg, lsusb, 可以看到 usb 设备:
    ID 1d50:6017 OpenMoko, Inc. Black Magic Debug Probe (DFU)

sudo apt install dfu-util
sudo dfu-util -d 1d50:6017 -s 0x8002000:leave -D ./src/blackmagic.bin

执行后会有:
    /dev/ttyACM0 和 /dev/ttyACM1

第一个是 GDB server, 第二个是 Serial.

连接你的设备和调试器

调试:
    TARGET          DEBUGGER
    GND             GND
    SWDIO           PB14
    SWCLK           PA5
    POWER           3.3V

串口:
    TARGET          DEBUGGER
    RXD             PA3
    TXD             PA2

可参考: src/platforms/stlink/platform.h

/* Hardware definitions... */
#define TDI_PORT	GPIOA
#define TMS_PORT	GPIOB
#define TCK_PORT	GPIOA
#define TDO_PORT	GPIOA

#define TDI_PIN		GPIO7
#define TMS_PIN		GPIO14
#define TCK_PIN		GPIO5
#define TDO_PIN		GPIO6

#define USBUSART USART2
#define USBUSART_PORT GPIOA
#define USBUSART_TX_PIN GPIO2

DMA 笔记

说明

  • DMA 用于在外设与内存, 或内存与内存之间搬运数据, 以减少 CPU 直接参与的数据复制开销.
  • 在高吞吐采样, 显示刷新, 音频流和串口收发场景中都很常见.

为什么需要 DMA

  • CPU 逐字节搬运数据时, 会占用大量总线和计算资源.
  • DMA 可以让数据搬运和 CPU 计算并行进行, 提高整体效率.
  • 常见场景包括 ADC, SPI, I2S, UART, PWM 更新和显示控制器刷新.

Cache 一致性问题

  • CPU 对内存读写通常会经过 cache, 外设和 DMA 访问的却是主存中的真实数据.
  • 如果 DMA 写完数据后, CPU 仍读取旧 cache, 就可能看到“数据为空”或“数据没更新”的现象.
  • 如果 DMA 读取的数据来自 CPU 刚写入但尚未回写的 cache, 外设也可能拿到旧数据.

常见处理方式

  • DMA 接收完成后, 读取前先执行对应的 cache invalidate.
  • DMA 发送前, 先把待发送缓冲区执行 cache clean / flush.
  • 对实时性要求高的缓冲区, 可考虑放到非 cache 区域或使用专门的内存段.

排查建议

  • 先确认 DMA 通道, 触发源和中断是否真的工作.
  • 若寄存器看起来正常但数据异常, 优先怀疑 cache 一致性问题.
  • 检查缓冲区地址是否对齐, 生命周期是否足够长, 是否被其他任务复用覆盖.
  • 双缓冲或循环缓冲场景下, 还要确认“正在写”和“正在读”的边界是否清晰.

GPIO和AFIO

参考: STM32F10xxx参考手册.pdf

GPIO 功能描述

GPIO 配置寄存器 GPIO 数据寄存器 GPIO 置位/复位寄存器 GPIO 锁定寄存器

GPIO的多种工作模式

  • 输入浮空
  • 输入上拉
  • 输入下拉
  • 模拟输入
  • 开漏输出
  • 推挽式输出
  • 推挽式复用功能
  • 开漏复用功能

GPIO 基本结构

参考: STM32入门系列-GPIO结构

理解好了GPIO的内部结构, 那么GPIO的各种模式将非常清楚. 分析图中标注的数字部分:

  1. 保护二极管

加这两个保护二极管可以防止不正常电压进入芯片导致芯片烧毁.

当引脚电压大于 $V_{DD}$ 时, 上方的保护二极管导通, 输入被钳位到 $V_{DD}$, 当引脚电压低于 $V_{SS}$ 时, 下方的保护二极管导通, 输入被钳位到 $V_{SS}$.

尽管GPIO内部有这样的保护, 但是如果将引脚直连大功率器件, 要么器件不工作, 要么芯片烧毁. 如果要驱动大功率器件, 必须加 大功率及隔离电路驱动. 所以说GPIO引脚是做控制的, 不是做驱动使用的.

问题: 来了一个高于 $V_{DD}$ 的电压时, 输入是如何被钳位到 $V_{DD}$? 这个过程是怎样的?

答: 根据二极管的伏安特性, 在二极管导通后, 即使电流的指数级变化也无法引起电压的快速变化, 电压会稳定在 0.6~0.8V.

  1. 上下拉电阻

上拉电阻和下拉电阻旁都有一个开关, 通过配置这个开关, 可以控制开启上拉电阻或下拉电阻, 间接地控制输入引脚的默认电平, 这可以消除不确定状态的影响. 当开启上拉时 默认输入是高电平, 当开启下拉时 默认输入是低电平.

如果上拉和下拉都关断, 我们称这为浮空模式, 一旦配置成这种模式, 引脚的初始电压是不确定的, 如果用万用表量一下此模式下引脚的电平会发现只有1点几伏, 而且还不时改变.

STM32内部的上拉是一个弱上拉, 即此上拉电阻输出的电流很小, 如果想输出较大的电流, 就需要外接上拉电阻了.

问题: 图中, 读出那个部分是什么样的电路读取传进去的0或1的?

  1. PMOS和NMOS

P-MOS和N-MOS管组成的单元电路, 这让GPIO引脚具有了推挽和开漏两种输出模式. 在推挽输出模式中,

嵌入式元器件选型笔记

文档整理说明

常用阻容值

电阻

常用基础阻值记录:

  • 0.5Ω
  • 10Ω
  • 22Ω
  • 100Ω
  • 1kΩ
  • 1.5kΩ
  • 2.7kΩ
  • 4.7kΩ
  • 10kΩ
  • 47kΩ
  • 100kΩ
  • 1MΩ

应用习惯:

  • 100kΩ: BOOT0 / BOOT1 的上拉或下拉
  • 47kΩ: TF 卡上拉
  • 4.7kΩ: I2C 上拉

电容

常用基础容值记录:

  • 10pF
  • 20pF
  • 22pF
  • 10nF
  • 0.1uF
  • 1uF
  • 2.2uF
  • 4.7uF
  • 10uF

应用习惯:

  • 10pF: 32.768KHz 晶振常见搭配
  • 22pF: 8MHz 晶振常见搭配
  • 0.1uF: 轻触按键, Reset, 芯片 VCC 去耦
  • 10uF: 5V -> 3.3V LDO 输入输出侧常见储能电容

材质选择

  • C0G: 更稳定, 损耗小
  • X7R / X5R: 常见综合型选择
  • Y5V: 可用但稳定性较差

经验上:

  • 小容值优先考虑 C0G
  • 较大容值常见 X5R / X7R

后续整理方向

  • 可继续补充磁珠, TVS, ESD 保护器件的选型规则.
  • 可继续补充接口防护与电源完整性选型经验.

相关文档

嵌入式电路应用笔记

文档整理说明

上拉与下拉

上拉电阻

上拉电阻常用于:

  • 提高输入级的高电平输入电压
  • 在开漏 / 集电极开路输出中提供稳定高电平
  • 避免输入脚悬空

设计时需要注意:

  • 上拉电阻会带来额外灌电流
  • 阻值过小会增加功耗
  • 阻值过大则上升沿可能太慢

下拉电阻

下拉电阻常用于:

  • 确保按键未按下时输入为确定低电平
  • 抑制悬空输入带来的不确定状态
  • 对很短的尖脉冲起一定抑制作用

去耦, 旁路, 滤波

滤波电容

常用于电源整流或电源输入输出位置, 用来滤除交流成分, 让输出更平滑.

去耦电容

去耦电容主要用于:

  • 给有源器件提供局部瞬态电流
  • 降低高频开关噪声沿电源线传播
  • 在器件电源脚附近形成局部低阻抗回路

旁路电容

旁路电容更多用于给交流噪声提供低阻抗通路, 让高频干扰更容易泄放到地.

板级经验

  • 数字芯片附近常见 0.1uF 去耦电容
  • VCC 到总电源路径阻抗较高, 往往还要叠加 1uF ~ 10uF 储能电容
  • 高频噪声越强, 电容越要靠近芯片放置

MCU 供电与地处理

地隔离与磁珠

GND 回流较复杂时, 可考虑用小电阻或磁珠连接不同地分区, 以减小地电位浮动和耦合噪声.

数据锁存

常见于:

  • 8080 并口
  • I2C 锁存寄存器
  • 某些外设状态保持场景

器件选型问题

电解电容能否替换为贴片电容

典型问题:

  • 10uF 电解电容是否可用 22uF 贴片电容替代

需要综合考虑:

  • 有效电容
  • 直流偏压衰减
  • ESR / ESL
  • 高频性能
  • 实际负载电流变化

电源必须干净

例如某些触摸控制器或 ADC 参考电压输入, 对电源纹波与噪声很敏感. 如果参考源直接接到噪声较大的电源, 会直接影响采样精度.

RF 信号

RF 信号即射频信号. 一般来说, 频率越高, 布线, 回流路径, 屏蔽, 去耦, 阻抗连续性等问题越需要被认真对待.

相关文档

PCB 设计

说明

  • 本页整理晶振电容, I2C 上拉电阻, 工艺线宽和布线注意事项等 PCB 设计速记.
  • 适合作为打样前检查电源去耦, 走线规则和板厂工艺参数的参考页.

电容选择

32.768KHz晶振 20pf, 8MHz晶振 20pf

MCU 一个1uf, 其它100nf或10nf

一般外设或者传感器的VDD可以加一个100nf的电容, 滤去杂波

电阻选择

i2c 上拉电阻: 3.3v与5v, 2.2k 4.7k 10k

嘉立创工艺 线宽

嘉立创官网_工艺参数说明

铜厚1OZ 的单双面板 对应的最小线宽/线隙 均为 0.1mm(4mil)

焊盘边到线边间距 >= 0.1mm(4mil) (尽量大于此参数)

字符高度 >= 1mm(40mil) (特殊字体 中文 掏空字符可能需更高)

字符线宽 >=0.15mm(6mil) (低于此值可能印不出来)

字符 与 露铜焊盘间隙 >= 0.15mm(6mil)

布线注意事项

极好的模拟/数字混合信号的电路板布局布线注意事项

PCB常用走线宽度

PCB 线宽

1699709127840

天线匹配

说明

  • 本页总结射频链路中天线匹配的作用, 常见网络和布局调试建议.
  • 适合作为板级天线预留与匹配调优的入门速查页.

作用

  • 天线匹配的核心目的是降低反射, 提高发射效率与接收灵敏度.
  • 在射频链路中, 匹配不好往往会直接表现为距离变短, 功耗变高或一致性变差.

常见网络

  • 常见做法是预留 L 型或 PI 型匹配网络.
  • 实际焊接时往往先预留若干 电阻, 电容, 电感位置, 再依据实测结果微调.

布局建议

  • 匹配器件应尽量靠近天线馈点.
  • 走线尽量短, 减少无意义分支和阻抗突变.
  • 射频区域要保证连续参考地, 并避免高速数字信号干扰.

调试建议

  • 理论值只能作为起点, 最终仍需依赖实测.
  • 若条件允许, 优先使用网络分析仪或频谱相关工具进行调试.
  • 天线外壳, 塑料件, 电池和地平面变化, 都可能改变最终匹配结果.

参考

  • 参考: https://blog.csdn.net/huibei_wuhan/article/details/115495723

TVS 管

说明

  • 本页总结 TVS 管在电源入口与接口防护中的作用, 选型和布局要点.
  • 适合作为浪涌与瞬态过压防护设计的快速参考页.

作用

  • TVS 用于抑制浪涌和瞬态过压, 常见于电源入口, 通信接口和外接连接器附近.
  • 它的核心目标是把尖峰电压快速钳位到可接受范围内, 保护后级芯片.

极性判断

  • 常见封装上带白色线条的一侧, 一般对应负极标记.
  • 用在直流电源防护时, 通常按反向方式并联在被保护节点两端.

选型要点

  • 先看反向工作电压是否覆盖正常工作电压.
  • 再看钳位电压是否低于后级器件可承受范围.
  • 同时关注脉冲功率, 封装尺寸和单向 / 双向类型.

布局建议

  • 尽量靠近接口或电源入口放置.
  • 回流路径要短, 接地路径要低阻抗.
  • 常与保险丝, 磁珠, 共模电感, RC 滤波等方案配合使用.

相关文档

RS485

说明

  • RS485 是一种差分串行通信标准, 适合长距离, 多节点, 抗干扰要求较高的工业场景.
  • Modbus RTU 等协议经常跑在 RS485 物理层之上.

负载能力

  • 标准 RS485 接收器的输入阻抗常按 12kΩ 视作 1 个单位负载.
  • 标准驱动器理论上最多可驱动 32 个单位负载.
  • 若某芯片是 1/8 Unit Load, 则同一总线可挂载的节点数会更多.

收发器常见引脚

SIT3485ESA 一类芯片为例:

  • DI: 驱动器输入, 接 MCU TX
  • RO: 接收器输出, 接 MCU RX
  • DE: 发送器输出使能, 高电平有效
  • RE: 接收器输出使能, 常见为低电平有效

常见控制方式:

  • RE=0 时允许接收
  • DE=1 时允许发送
  • 可由 MCU 控制 RE / DE, 实现半双工自动切换

板级设计要点

  • 总线两端通常需要终端电阻, 常见为 120Ω.
  • 需要根据网络结构考虑上拉 / 下拉偏置, 避免总线悬空.
  • 分支过长会带来反射, 布线要尽量保持干净.
  • 与强干扰设备并线时, 要注意接地与隔离方案.

自动收发

  • 一种常见做法是用 MCU GPIO 控制 RE / DE.
  • 也可结合三极管等外围电路实现自动收发.
  • 某些 USB 转串口芯片可用 TNOW 等引脚辅助控制方向切换.

参考

  • 自动收发电路: https://blog.csdn.net/qq_33056691/article/details/107876091
  • TNOW 方向控制: https://blog.csdn.net/WCH_TechGroup/article/details/124798207
  • 相关文档: Modbus

基本通信协议 笔记

说明

  • 本页整理嵌入式开发中常见的 I2C, SPI 等基础通信协议概念与读写流程示例.
  • 适合作为总线时序, 应答机制和 EEPROM 访问方式的速查入口.

i2c 协议

开始信号: SCL在高电平期间, 数据线由高变为低
停止信号: SCL在高电平期间, 数据线由低变为高
应答信号:
    主机写从机时, 每写完一个字节, 从机在下一个时钟周期将数据线拉低, 以告诉主机操作有效.
    主机读从机时, 每读完一个字节, 主机在下一个时钟周期将数据线拉低, 以告诉从机操作有效, 最后一个字节不发应答,直接发停止信号

注: 时钟线为高电平期间的数据线上的电平改变都被认为是起始和停止信号,所以数据改变必须要在时钟为低电平时改变

基本过程

i2c时序

i2c芯片EEPROM FM24V10

设备地址: 0x50

初始化

    ...

设置速度, 设置主模式

    i2c_config(SPEED|MASTER)

向i2c_dev, 从设备地址dev_addr, 从设备的起始地址0x0001处, 写数据0x02 0x03

    buf[] = [0x00, 0x01, 0x02, 0x03]
    write(i2c_dev, dev_addr, buf, 4)

从i2c_dev, 从设备地址dev_addr, 从设备的起始地址0x0001处, 读数据

    buf[] = [0x01, 0x00]
    write(i2c_dev, dev_addr, buf, 2)

    buf[2] = [0x00]
    read(i2c_dev, dev_addr, buf, 2)

I2C 补充时序图

完整的数据传输

I2C 写流程

i2c write操作

  1. 发送从设备地址和写标记.
  2. 发送目标寄存器地址.
  3. 连续写入数据字节.

I2C 读流程

i2c read操作

  1. 先发送从设备地址和写标记.
  2. 发送目标寄存器地址, 然后发出 restartstop.
  3. 再发送从设备地址和读标记.
  4. 连续读取数据, 最后一个字节不应答并结束通信.

spi协议

spi芯片EEPROM FM25V10

设备地址: 0x50

写使能:
buf[] = [0x06]
write(spi_dev, buf)

从0x000000, 写0x01 0x02:
buf[] = [0x02 0x00 0x00 0x00 0x01 0x02]
write(spi_dev, buf)

从0x000000, 读两个字节:
buf[] = [0x03 0x00 0x00 0x00 0xff 0xff]
write(spi_dev, buf)

UART 笔记

说明

  • 本页记录 UART 的 printf 重定向, 中断配置, 收发 FIFO 和 RTOS 场景下的数据处理方式.
  • 适合作为串口初始化, 发送接收策略和 IDLE 中断处理的快速参考.

实现 printf

GNUC

int _write(int fd, char *ptr, int len) {
    uart_write(ptr, len);
    return len;
}

协议分析

https://blog.csdn.net/weixin_44625313/article/details/120015565

配置过程

中断模式

选择时钟源

使能时钟

配置uart

    既读又写: TX_RX
    8字节数据位, 1个开始位, 1个停止位, 无奇偶校验: 8 data bit, 1 start bit, 1 stop bit, no parity
    设置波特率: 115200

使能uart

设置中断优先级, NVIC_SetPriority(USARTx_IRQn, 5);
使能中断, NVIC_EnableIRQ(USARTx_IRQn);

UART 发送数据

连续发送多个数据时, 数据寄存器可能会满, 导致剩余数据丢失

应该这么做:

while (1) { 等待 TX 空闲 连续发送最大 UART_TX_FIFO_SIZE 数量的数据 }

UART_TX_FIFO_SIZE 可能是 16

UART 接收数据

UART_RX_FIFO_SIZE 可能是 32

使用 RTOS 时

一般是 在中断中 把数据放入到 BUF 中, 在 RX超时 或者 RX到达(人为设置的)阈值时, 发送信号.

在独立的task中 根据信号, copy 出 buf 并处理.

IDLE中断

总线空闲状态IDLE中断: 当一帧数据传输结束之后,总线会维持高电平空闲,此时会触发MCU的IDLE中断.

可用该中断 处理数据传输完成 事件.

DTR / RTS 控制线

一键下载电路中, 常会使用 DTR / RTS 控制线参与复位或进入下载模式.

  • 初始上电时, MODEM 联络信号 DTRRTS 默认常见为高电平.
  • 本记录里约定低电平表示“就绪”.

常见控制方式:

#![allow(unused)]
fn main() {
serial.write_data_terminal_ready(true);  // 拉低 DTR, 表示就绪
serial.write_data_terminal_ready(false); // 拉高 DTR, 表示未就绪

serial.write_request_to_send(true);      // 拉低 RTS, 表示就绪
serial.write_request_to_send(false);     // 拉高 RTS, 表示未就绪
}

SPI 协议

说明

  • 本页记录 SPI 极性相位, FIFO 传输和寄存器 + 写缓冲 + 读缓冲的基本处理流程.
  • 适合作为底层驱动实现时的收发时序速记页.

SPI分析

设置spi参数: polar, phase polar, 极性: CPOL=0则, 当SCLK=0时, SPI处于空闲状态; CPOL=1则, 当SCLK=1时, SPI处于空闲状态; phase, 相位: CPHA=0则, 数据采样在第1个边沿, 即上升沿; CPHA=1则, 数据采样在第2个边沿, 即下升沿;

spi一次最多传输FIFO大小的数据, 比如: 64 bytes

要写的缓冲区有: REG BUF (reg_len), WRITE BUF (write_len), READ BUF (read_len) 把上述BUF数据依次放入SPI FIFO: reg len –> write len –> read len (写0, dummy)

设置FIFO阈值: FIFO_SIZE / frame_size - 1

使能spi, 开始传输

等待中断…

从FIFO中依次读取所有数据: reg_len(丢弃) –> write_len(丢弃) –> read_len

Modbus

说明

  • Modbus 是工业现场最常见的通信协议之一, 常见形态包括 Modbus RTU, Modbus ASCII, Modbus TCP.
  • 在 MCU 与下位机场景中, 最常见的是跑在 RS485 总线上的 Modbus RTU.

常见形态

Modbus RTU

  • 以二进制帧传输, 效率较高.
  • 常与 RS485 收发器搭配使用.
  • 帧边界通常依赖总线空闲时间判断.

Modbus TCP

  • 跑在以太网上, 不再依赖串口波特率与校验位.
  • 常见于上位机, 网关和工业控制器之间的集成场景.

RTU 报文基本组成

典型报文通常包含:

  • 从站地址
  • 功能码
  • 数据区
  • CRC16

常见功能码包括:

  • 0x03: 读保持寄存器
  • 0x04: 读输入寄存器
  • 0x06: 写单个保持寄存器
  • 0x10: 写多个保持寄存器

开发要点

  • 先明确寄存器地址模型, 字节序和缩放规则.
  • 收发方向控制要和 RS485 使能脚时序匹配.
  • 超时, 重发和异常码处理要提前设计.
  • 主从双方要统一波特率, 数据位, 停止位, 奇偶校验.

相关文档

i2c 笔记

i2c传输过程

完整的数据传输

slave address
    第1个字节: 高7位为slave地址码, 最低1位为读/写控制, 读写控制: 0为写 1为读
    8个时钟周期

空闲状态
    i2c的两条信号线, scl为高, sda为高

start信号
    主机发起通信, scl为高期间, sda由高到低的跳变;
    1个时钟周期

ack信号
    scl为高, sda为低
    如果slave检测到7位slave地址与自己的地址相同, 产生ack信号
    1个时钟周期

data信号
    MBS优先
    8个时钟周期

ack信号
    简单说: 1个字节后的第9个脉冲, ACK: scl为高, sda为低 NACK: scl为高, sda为高.
    详细说: 发送方每发送1个字节, 就在其后的1个脉冲期间释放sda, 由接收方反馈一个应答信号: 应答信号是低电平时, 规定为有效应答位(ACK), 表明成功接收了该字节; 应答信号是高电平时, 规定为非应答位(NACK), 表明该字节未成功接收.

data信号

.
.
.

stop信号
    scl为高期间, sda由低到高的跳变

i2c write

i2c write操作

1. slave addr 和 0
2. 要写入的 reg address
3. 要写入的 data

ps: 开始信号, 发送地址+写位, 等待ack, 发送数据(每发送1个byte就会等待1个ack), 停止信号

i2c read

i2c read操作

1. slave addr 和 0
2. 要读入的 reg address
3. slave addr 和 1
4. 读

ps: 开始信号, 发送地址+写位, 发送数据(reg address), 停止信号
    开始信号, 发送地址+读位, 读数据(每读完1个byte发送1个ack), 停止信号

i2c 读写 fm24v10 eeprom

slave_addr: 1010 A2 A1 A16
    A2 A1, 对应于8pin中的A1 A2, 用于识别同一个总线上的不同fm24v10 有4种选择, A16 是页选择位
    只有不处理A2 A1 pin的话, slave address是: 1010 00 0, 即0x50

multiple bytes write:
    Start, slave_addr + w, addr msb, addr lsb, data byte, data byte, ...
    example:
        0x50, 0x00, 0x00, 0x01, 0x02, 0x03

multiple bytes read:
    Start, slave_addr + w, addr msb, addr lsb
    Start, slave_addr ignore X(p s) + r, read ...
    example:
        0x50, 0x00, 0x00
        0x51, read 3 bytes

USB 用法笔记

说明

  • 本页记录 USB 设备开发中常见的端点, 描述符, libusb 传输和 WinUSB 相关要点.
  • 适合作为做上位机访问, 设备枚举和 WinUSB 驱动接入时的实践速记.

libusb

端点地址: 0~3 位是端点号, 第 7 位是方向(0-OUT,1-IN)

如 0x81 表示 EP0x01 的 In 端点 如 0x1 表示 EP0x01 的输出

libusb:

当执行读操作时, 端口如: 0x81 0x82 0x83 ...
当执行写操作时, 端口如: 0x1   0x2  0x3 ...

数据包的最大长度, 是设置在 配置描述符中的, 最大值不能超过 1024

libusb 当使用 bulk 传输 读取数据时, 读取的 size 时, 可以设置很大, 如 10240, 根据 bulk 传输 的定义:

  1. 当传输的数据长度 小于 数据包的最大长度时, 认为传输结束, bulk 传输的回调函数被执行
  2. 当传输的数据长度 大于 数据包的最大长度时, 需要多次传输数据包, 只有最后传输结束时, bulk 传输的回调函数才会执行

USB 从机 发送数据到主机的过程:

  1. 复制数据到 EP 对应的缓冲区中
  2. 设置 EP 的发送长度
  3. 发送 ACK 给主机

USB 用法笔记

  1. 写好各种描述符后, 设备正常运行

  2. 上位需要有相应的驱动, 因为每一个驱动对应一个设备, 上位机操作的是设备. 没有的话, 无法 Open usb device.

    1> 使用 zadig 安装驱动.

    2> 使用 WinCID

    3> 上面是我喜欢的方式, 其它的, 不知道…, 略.

WinCID

关键是三个描述符的解析, 就可以实现:

#define WCID_VENDOR_CODE 0x17

// WCID, 微软 WinUSB
const uint8_t WCID[] = {
    0x12,
    0x03,
    'M', 0x00,        /* wcChar0 */
    'S', 0x00,        /* wcChar1 */
    'F', 0x00,        /* wcChar2 */
    'T', 0x00,        /* wcChar3 */
    '1', 0x00,        /* wcChar4 */
    '0', 0x00,        /* wcChar5 */
    '0', 0x00,        /* wcChar6 */
    WCID_VENDOR_CODE, /* bVendorCode */
    0x00,             /* bReserved */
};

uint8_t WINUSB_ExtendedCompatId_Descritpor[] = {
    0x28, 0x00, 0x00, 0x00, /* dwLength */
    0x00, 0x01,             /* bcdVersion */
    0x04, 0x00,             /* wIndex */
    0x01,                   /* bCount */
    0, 0, 0, 0, 0, 0, 0,    /* Reserved */
    /* WCID Function  */
    0x00, /* bFirstInterfaceNumber */
    0x01, /* bReserved */
    /* CID */
    'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
    /* sub CID */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0, 0, 0, 0, 0, 0, /* Reserved */
};

uint8_t WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[] = {
    ///////////////////////////////////////
    /// WCID property descriptor
    ///////////////////////////////////////
    0x8e, 0x00, 0x00, 0x00, /* dwLength */
    0x00, 0x01,             /* bcdVersion */
    0x05, 0x00,             /* wIndex */
    0x01, 0x00,             /* wCount */

    ///////////////////////////////////////
    /// registry propter descriptor
    ///////////////////////////////////////
    0x84, 0x00, 0x00, 0x00, /* dwSize */
    0x01, 0x00, 0x00, 0x00, /* dwPropertyDataType */
    0x28, 0x00,             /* wPropertyNameLength */
    /* DeviceInterfaceGUID */
    'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00,  /* wcName_20 */
    'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00,  /* wcName_20 */
    't', 0x00, 'e', 0x00, 'r', 0x00, 'f', 0x00,  /* wcName_20 */
    'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00,  /* wcName_20 */
    'U', 0x00, 'I', 0x00, 'D', 0x00, 0x00, 0x00, /* wcName_20 */
    0x4e, 0x00, 0x00, 0x00,                      /* dwPropertyDataLength */
    /* {1D4B2365-4749-48EA-B38A-7C6FDDDD7E26} */
    '{', 0x00, '1', 0x00, 'D', 0x00, '4', 0x00, /* wcData_39 */
    'B', 0x00, '2', 0x00, '3', 0x00, '6', 0x00, /* wcData_39 */
    '5', 0x00, '-', 0x00, '4', 0x00, '7', 0x00, /* wcData_39 */
    '4', 0x00, '9', 0x00, '-', 0x00, '4', 0x00, /* wcData_39 */
    '8', 0x00, 'E', 0x00, 'A', 0x00, '-', 0x00, /* wcData_39 */
    'B', 0x00, '3', 0x00, '8', 0x00, 'A', 0x00, /* wcData_39 */
    '-', 0x00, '7', 0x00, 'C', 0x00, '6', 0x00, /* wcData_39 */
    'F', 0x00, 'D', 0x00, 'D', 0x00, 'D', 0x00, /* wcData_39 */
    'D', 0x00, '7', 0x00, 'E', 0x00, '2', 0x00, /* wcData_39 */
    '6', 0x00, '}', 0x00, 0x00, 0x00,           /* wcData_39 */
};

请求:

// Setup包的处理
if (SetupReqCode == WCID_VENDOR_CODE) {
    printf("WCID_VENDOR_CODE\n");
    switch (pSetupReqPak->wIndex) {
        case 0x04:
            pDescr = WINUSB_ExtendedCompatId_Descritpor;
            len = WINUSB_ExtendedCompatId_Descritpor[0];
            printf("0x04, SetupReqLen: %u, len: %u\n", SetupReqLen, len);
            break;

        case 0x05:
            pDescr = WINUSB_ExtendedProperty_InterfaceGUID_Descritpor;
            len = WINUSB_ExtendedProperty_InterfaceGUID_Descritpor[0];
            printf("0x05, SetupReqLen: %u, len: %u\n", SetupReqLen, len);
            break;

        default:
            errflag = 0xff;
            break;
    }

    // 请求的数据长度 大于 实际需要的数据长度 更大
    if (SetupReqLen > len)
        SetupReqLen = len;
    len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
    memcpy(pEP0_DataBuf, pDescr, len);
    pDescr += len;
}

// 字符串描述符
case USB_DESCR_TYP_STRING: {
    // 描述符索引
    switch ((pSetupReqPak->wValue) & 0xff) {

        ...

        case 0xEE:
            pDescr = WCID;
            len = WCID[0];
            printf("USB_DESCR_TYP_STRING WCID, SetupReqLen: %u, len: %u\n", SetupReqLen, len);
            break;

        ...
    }
    break;
}

USB 协议

需要什么可以在官方文档上搜索关键字: https://www.usb.org/documents

下面很多截图来自: USB 中文网 https://www.usbzh.com

USB 标准请求、类特定请求总结

USB 描述符

USB 描述符

设备描述符

配置描述符

字符串描述符

接口描述符

HID 描述符

接口类定义

https://www.usb.org/defined-class-codes

接口关联描述符

端点描述符

I2S

说明

  • I2S 是数字音频接口协议, 常用于 MCU, 音频 Codec, DAC, ADC 之间传输音频数据.
  • 它关注的是音频数据按位串行传输, 不直接负责设备枚举和高层控制.

常见信号

BCLK

  • 位时钟, 决定每一位数据的移出节奏.
  • 常可理解为: 采样率 * 位宽 * 声道数.
  • 在很多资料里, BCLK 也会写作 SCLK.

WS / LRCK

  • 字选择或左右声道选择信号.
  • 常用来区分当前数据属于左声道还是右声道.
  • 它的频率通常与采样率一致.

SD

  • 串行数据线, 以二进制形式传输音频采样值.
  • 数据常按高位到低位输出.

与 PCM 的关系

  • PCM 更偏“音频采样数据的表示方式”.
  • I2S 更偏“这些采样数据如何在线路上被串行传输”.
  • 实际项目里常说“通过 I2S 传输 PCM 数据”.

开发要点

  • 先确认主从模式, 谁输出时钟.
  • 再确认采样率, 位宽, 单声道 / 双声道, 左对齐 / 标准 I2S 模式等参数.
  • 若接音频 Codec, 还要同时配置其控制接口, 常见是 I2C.
  • 出现爆音, 杂音或声道错位时, 优先检查时钟和对齐方式.

参考

  • PCM 数据介绍: https://blog.csdn.net/ZHONGCAI0901/article/details/116131776
  • I2S 介绍: https://blog.csdn.net/ZHONGCAI0901/article/details/116205427

蓝牙笔记

说明

  • 本页汇总蓝牙测试, HCI 扫描参数和连接参数等常见基础概念.
  • 适合作为 BLE 广播, 连接间隔, 超时和主从机角色的速查页.

蓝牙基本测试

蓝牙地址是: { 0xCC, 0x3B, 0xE3, 0x82, 0xBA, 0xC0 }, 实际MAC地址字符串为: C0:BA:82:E3:3B:CC

1, 被测设备开启广播 2, 测试设别扫描广播, 测得RSSI, 比较RSSI的阈值

HCI

上层的蓝牙协议栈, 会调用底层的 HCI层 实现上层的功能.

HCI层 (功能举例): 发送 设置扫描参数 发送 扫描使能

BLE HOST 扫描

扫描窗口(scan window): 一次扫描进行的时间宽度. 扫描间隔(scan interval): 两个连续的扫描窗口的起始时间之间的时间差, 包括扫描休息的时间和扫描进行的时间

这个窗口决定了扫描占空比, 如 扫描间隔为 100 ms, 扫描窗口为 10ms, 占空比为 10%, 这两个参数都要是 0.625ms 的整数倍.
占空比 最低为 0.4% ?
扫描设备一直在广播信道上运行

active: 是否主动扫描, 配置为1则是主动扫描, 0则是被动扫描

filter_policy: 扫描筛选策略, 也就是说接受任何广播数据或者仅仅接受白名单设备的广播数据包

timeout: 扫描超时, 超过指定的时间后, 没有扫描到设备将停止扫描

scan_phys: 扫描的物理层速度

connection interval

大数据传递时: 通讯数据包是连续传递的, 主机会选择 最小连接间隔 来进行通讯.

无数据传递时: 通讯是空闲状态, 主机会选择 最大连接间隔 来定期询问从机状态, 以保持连接不中断, (在空闲时, 由于使用 最大连接间隔 作为通讯周期, 会影响到程序的下一个命令的发送时间)

在实际应用的时候, 有时候需要发送数据快一点, 这个时候我们可以把连接间隔改小一点, 这样发送数据快, 但是功耗大, 当把数据发完的时候, 可把连接间隔改小一点, 这样可以降低功耗, 达到一个均衡, 比如穿戴设备连接上的时候, 可能需要快速把本地数据发给手机, 这个时候就可以把连接间隔设置小点, 传送完毕后间隔设置大点, 这样功耗就不会太大.

slave latency

Slaver设备没有数据要发时, 跳过一定数目的 ConnectionEvent的值, Rang: 0-499.

应用: 在距离远或者干扰大的时候 无数据通信的时候, 把这个值设大可以减少掉线概率

比如防丢器: 放在口袋里面假如这个参数设置为0 表示规定时间内必须响应从机, 不然就以为是蓝牙断开了, 假如设置为5, 假如信号不好的时候, 即使中间丢了4个, 只接受了1个就表示连接了, 因为他会跳过其中4个 , 保证了不掉线的概率

timeout

超时时间, 就是两个设备在连接的这段时间没有发生通讯而导致连接自动断开的值, Range(10ms—–32s)

连接超时时间, 用在信号不太好的情况下, 给对方一点时间, 超过这个时间通信就建立失败

主机与从机

主机决定连接参数的值 (connection interval, slave latency, timeout),从机可以请求更新这些参数,主机决定是不是接受,接受的值是多少。所以是会出现手机接受参数后和从机请求的参数有偏差,或者甚至是拒绝(ios)

PWM 通用入口

说明

  • PWM 常用于电机调速, LED 调光, 蜂鸣器驱动和各种功率控制场景.
  • 当前仓库中更具体的上手示例, 可参考 stm32cubemx 配置 pwm.

关键参数

频率

  • 频率决定一个周期重复的速度.
  • 在定时器场景里, 常由时钟频率, 预分频和自动重装值共同决定.

占空比

  • 占空比表示高电平持续时间占整个周期的比例.
  • 常见表达是 duty = compare / period.

常见场景

  • 驱动无源蜂鸣器输出不同音调.
  • 控制风扇, 电机或加热器功率.
  • 作为 FOC 或电机驱动的基础波形输出.
  • 用于背光, LED 呼吸灯等亮度控制.

设计要点

  • 先确认目标频率是否会引起可闻噪声或开关损耗问题.
  • 若直接驱动功率器件, 还要关注死区, 栅极驱动和续流路径.
  • 占空比更新时要注意同步时机, 避免毛刺.
  • 某些高级定时器支持互补输出, 刹车输入和中心对齐模式.

相关文档

FOC

说明

  • FOC 即磁场定向控制, 常用于无刷电机与伺服电机控制.
  • 它的目标是把定子电流分解为励磁分量和转矩分量, 从而实现更平滑, 更高效的电机控制.

基本理解

  • 位置环: 决定“目标转到哪里”.
  • 速度环: 决定“多快转过去”.
  • 电流环: 决定“电机当前该输出多大力矩”.

常见闭环关系可以理解为:

  • 位置误差 e = 目标位置 - 实际位置
  • Uq 常用于表示转矩相关控制量, 常见简化写法是 Uq = Kp * e

依赖条件

要做好 FOC, 通常需要:

  • 稳定的 PWM 输出
  • 电机相电流采样
  • 转子位置反馈, 如编码器或估算器
  • 合适的控制周期与中断调度

调试建议

  • 先确认硬件驱动级是否正常, 再进入闭环调参.
  • 位置环, 速度环, 电流环建议分层调试, 不要一次全部打开.
  • 编码器方向, 零点和极对数要先校准.
  • 电源与采样噪声会直接影响控制稳定性.

参考

片上 Flash 读写

说明

  • 某些 MCU 允许从 Code Flash 中划出一块区域, 作为参数存储或掉电保存区域.
  • 设计这类区域时, 首先要确认扇区大小, 擦除粒度和链接脚本中的地址布局.

分区要点

ch32v307 一类芯片为例, 需要先根据手册与链接脚本确认可用地址:

FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
USER  (rw) : ORIGIN = 0x0040800,  LENGTH = 32K

若映射到运行地址, 则用户区可能对应:

  • 0x8040800 ~ 0x8048800

开发要点

  • 地址必须满足擦除扇区对齐要求.
  • 写入前通常需要 unlock.
  • 修改前往往要先按页或按扇区擦除.
  • 写完后要重新 lock, 并做好读回校验.

风险点

  • 若与程序区重叠, 可能直接导致程序异常或无法启动.
  • 频繁擦写要关注寿命与掉电保护.
  • 参数结构最好带版本号, 校验和或冗余副本.

适用场景

  • 保存出厂参数, 校准值, 序列号.
  • 保存少量配置项或升级状态标记.
  • 在没有外部 EEPROM / FRAM 时做轻量持久化.

相关文档

Intel HEX 文件格式

说明

  • 本页整理单片机常见 Intel HEX 文本格式中记录类型, 地址和校验含义.
  • 适合作为烧录文件解析和下载工具调试时的快速参考.

单片机的hex文件格式

Intel HEX 文件是由一行行符合Intel HEX 文件格式的文本所 构 成的ASCII 文本文件

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

04 0000 05 18005B483C

04 4个字节的数据 05 表示开始线性地址, 入口地址

3C 表示前面数据的校验数据, 校验方法: 0x100-前面字节累加和

02 0000 04 1800 E2

02 2个字节的数据 0000 起始地址 04 表示扩展线性地址类型, 0x1800 为扩展线性地址

10 0000 00 3C3CA5A5C3C35A5A02000000BFFFFFFF36

10 16字节的数据 0000 起始地址 00 数据类型 3C3CA5A5C3C35A5A02000000BFFFFFFF 数据 36 校验码

地址为: 0x18000000

看门狗

说明

  • 看门狗用于在程序跑飞, 死锁或长时间阻塞时, 自动拉起系统复位.
  • 在嵌入式系统中, 常见有独立看门狗和窗口看门狗两类.

窗口看门狗

看门狗

使能后, 常见会在以下两种情况下触发 RESET:

  • 在计数寄存器值仍大于窗口值时, 过早重写计数寄存器.
  • 计数器递减到下限以下, 发生超时.

使用建议

  • 喂狗动作应放在“主流程正常完成”的检查点, 不要随便在任意中断里无条件喂狗.
  • 对有 RTOS 的系统, 可把关键任务健康状态汇总后再统一喂狗.
  • 初始化阶段, 长耗时 Flash 擦写或升级流程中, 要提前考虑喂狗窗口.
  • 如果系统支持, 复位后要记录复位原因, 方便排查异常来源.

调试建议

  • 先确认超时周期是否覆盖最慢正常路径.
  • 故意制造阻塞, 死循环或任务卡死, 验证看门狗是否按预期复位.
  • 对窗口看门狗, 还要验证“喂得太早”是否同样能触发保护.

相关文档

HarmonyOS 嵌入式仿真

说明

  • 本文记录 HarmonyOSstm32f4 相关仿真环境下的依赖安装与问题排查.
  • 当前更偏环境搭建笔记, 适合作为第一次把仿真链路跑通时的速查页.
  • 如果后续继续整理, 建议再补“示例工程入口”和“实际构建输出”两部分内容.

适用场景

  • 在 Linux 环境下准备 HarmonyOS 的嵌入式仿真工具链.
  • 遇到 Node.js, xpm, qemu-arm, openocd 或系统依赖缺失时快速排查.
  • 想先把仿真环境跑通, 再进入具体工程构建和调试阶段.

环境安装

安装 Node.js

curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs

安装 xpm 与仿真工具

sudo npm install --global xpm
xpm install --global @xpack-dev-tools/qemu-arm@latest
xpm install --global @xpack-dev-tools/openocd@latest

常见报错

dash 兼容问题

sudo dpkg-reconfigure dash

选择 No.

alsa/asoundlib.h 缺失

sudo apt-get install libasound2-dev

asm/unistd.h 或构建依赖缺失

sudo apt install pkg-config

排查思路

  • 先区分是系统依赖缺失, 还是工具链版本不匹配.
  • 如果问题发生在安装阶段, 优先检查 npm, xpm 和系统包是否完整.
  • 如果问题发生在构建阶段, 再进一步核对交叉编译环境和示例工程配置.

相关文档

TensorFlow Lite Micro

说明

  • 本页记录在 STM32 平台上搭建 TensorFlow Lite Micro 示例环境的过程.
  • 内容偏向旧版 mbed + GCC_ARM 工具链, 实际使用时需结合当前官方仓库与板卡支持情况校对.

搭建环境

TensorFlow Lite: https://www.tensorflow.org/lite/microcontrollers

搭建参考: https://github.com/tensorflow/tensorflow/tree/master/tensorflow/lite/micro/examples/hello_world#deploy-to-STM32F746

主机操作系统: ubuntu20
开发板: STM32f746 NG DISCO

pip3 install --user mbed-cli mercurial

设置 gnu gcc 工具链:
    mbed config -G GCC_ARM $HOME/programs/gcc-arm-none-eabi-9-2020-q2-update/bin

mbed config --list

下载TensorFlow源码:
    git clone https://github.com/tensorflow/tensorflow.git

生成basic的项目:
    cd tensorflow
    make -f tensorflow/lite/micro/tools/make/Makefile TARGET=mbed TAGS="CMSIS disco_f746ng" generate_hello_world_mbed_project

会生成目录 mbed:
    cd tensorflow/lite/micro/tools/make/gen/mbed_cortex-m4/prj/hello_world/mbed

mbed环境:
    mbed config root .
    mbed deploy

    pip3 install -r mbed-os/requirements.txt --user

    执行下面的snippet, 修改默认的C++98到C++11
    python -c 'import fileinput, glob;
    for filename in glob.glob("mbed-os/tools/profiles/*.json"):
        for line in fileinput.input(filename, inplace=True):
            print line.replace("\"-std=gnu++98\"","\"-std=c++11\", \"-fpermissive\"")'

    mbed compile -m DISCO_F746NG -t GCC_ARM

    生成文件:
        ./BUILD/DISCO_F746NG/GCC_ARM/mbed.bin

    把这个文件烧写到 DISCO_F746NG 上就可以了

我使用的安装stlink工具, https://github.com/stlink-org/stlink/releases
包括: st-flash st-info st-util
    sudo apt install stlink-tools

烧写:
    st-flash write ./BUILD/DISCO_F746NG/GCC_ARM/mbed.bin 0x8000000

模块与扩展器件总览

说明

  • 本文档用于承载“嵌入式可直接接入的模块, 外设, 扩展芯片”相关内容.
  • 这类内容强调接线方式, 驱动时序, 外设扩展, 模块使用方法.
  • 通用基础器件原理优先放在 hardware/, 传感器优先放在 传感器总览.

当前模块文档

建议阅读路径

  • 需要查模块接线和接入方式时, 优先看这里和 模块/README.md.

  • 需要查具体测量器件时, 转到 传感器总览.

  • 需要查基础器件原理和板级电路时, 转到 hardware/电路 专题.

  • ws2812 使用笔记

  • 4x4 键盘矩阵

  • Arduino 接口笔记

常见扩展器件

IO 扩展

CH423

两线串行接口的远程 I/O 扩展芯片, 可用于单片机以两线进行 16 位数码管显示或 I/O 扩展应用. 输入端为兼容 I2C 的两线接口, 输出端为 8 位双向输入输出口和 16 位大电流输出口.

74HC165 / 74HC595

移位寄存器, 常用于输入扩展, 输出扩展, 数码管或 LED 扫描等场景.

目录边界

  • ws2812, 键盘矩阵, IO 扩展器, Arduino 外设接口: 归入模块与扩展器件.
  • bme280, 加速度计, 陀螺仪, 温湿度计: 归入传感器.
  • 二极管, 三极管, MOS 管, 门电路, 电源电路基础: 归入 hardware/.

蜂鸣器

说明

  • 本页整理有源与无源蜂鸣器的区别, 驱动方式和选型思路.
  • 适合作为提示音器件选择与 GPIO / PWM 驱动设计的速查页.

类型区分

有源蜂鸣器

  • 内部自带振荡电路.
  • 一般只需给定电压或 GPIO 开关信号, 就能发出固定频率声音.
  • 适合告警音, 状态提示音等简单场景.

无源蜂鸣器

  • 需要外部提供振荡信号, 常见做法是使用 PWM 驱动.
  • 可通过频率变化输出不同音调.
  • 更适合提示音, 音阶, 简单旋律等场景.

设计要点

  • 先确认工作电压和驱动电流, 不要直接超过 MCU GPIO 的输出能力.
  • 电流较大时, 通常需要三极管或 MOS 管做开关驱动.
  • 若是电磁式器件, 关断时要关注反向尖峰与电源干扰问题.

选型建议

  • 只需要固定提示音时, 优先考虑有源蜂鸣器.
  • 需要可变音调或提示旋律时, 优先考虑无源蜂鸣器.
  • 对声音大小有要求时, 还要同时关注供电电压, 谐振频率和腔体结构.

相关文档

音频功率放大器

说明

  • 本页记录小功率音频放大器在嵌入式板卡中的用途与设计关注点.
  • 适合作为蜂鸣器, 小喇叭等场景下音频输出级选型的基础参考.

使用要点

  • 某些芯片带有多个使能脚, 可用于切换不同增益档位.
  • 设计时要同时确认供电电压, 输出负载阻抗和散热条件.
  • 放大器输入端容易受噪声影响, 电源去耦和地线回流要尽量简单清晰.

场景判断

  • 只需要简单提示音时, 可优先考虑蜂鸣器直接驱动方案.
  • 需要更高音量或驱动小喇叭时, 再考虑音频功率放大器.
  • 音频功率放大器不等同于通用运算放大器, 选型时不要混用.

相关文档

传感器总览

说明

  • 传感器文档用于记录“感知外部物理量”的器件, 如温度, 湿度, 加速度, 气压等.
  • ws2812 属于发光驱动类模块, 不再归入传感器, 主文档位于 ws2812 使用笔记.
  • 历史英文别名入口已移除, 后续统一以当前总览页为准.

当前主题

建议阅读路径

  • 需要按“测量对象”查找时, 优先从本页定位到具体器件或待补主题.
  • 需要看灯带, 键盘矩阵, IO 扩展器等可接入模块时, 转到 模块与扩展器件总览.
  • 若问题更偏供电, 滤波, 接口保护和布线, 转到 hardware/电路 专题.

环境传感器

  • bme280: 当前仍是占位记录, 后续可补充为独立文档.

运动传感器

  • DA213B: 三轴加速度相关记录, 当前先保留在本总览中作为待扩展主题.

后续整理建议

目录边界

  • 传感器页主要放“感知外部物理量”的器件与模块.

  • 发光驱动, 通信扩展, 键盘矩阵等非感知类模块不再并入本目录主干.

  • 传感器类主题优先按“测量对象”或“器件型号”建独立文档.

  • 驱动模块, 灯带, 键盘矩阵, IO 扩展器等内容优先归入“模块与扩展器件”体系.

  • 通用电路原理, 二极管, 三极管, MOS 管等基础器件优先归入 hardware/.

模块与扩展器件归档

说明

  • 本目录保存 MCU 常见模块与扩展器件的具体记录页.
  • 与上层 模块与扩展器件总览 的关系是: 上层负责导航与归类, 本目录负责具体条目.

当前条目

使用建议

  • 先看上层总览确认器件类别.
  • 再进入本目录的具体页面查看接线, 用法或注意点.

Arduino 接口笔记

说明

  • 本文记录 STM32F746 开发板上 Arduino 兼容接口与实际 MCU 引脚的对应查找方法.
  • 更偏模块归类的内容, 可回到 模块与扩展器件总览.

引脚编号查找位置

可先查看对应开发板变体文件, 例如:

variants/STM32F7xx/F746B(E-G)T_F746N(E-G)H_F750N8H_F756BGT_F756NGH/variant_generic.cpp

重点关注其中的数组:

  • digitalPin
  • analogInputPin

查找方法

  • 先从原理图确认 Arduino 接口对应的 MCU 端口, 例如 PA_15.
  • 再到 digitalPinanalogInputPin 数组中查找该端口的位置.
  • 数组中的索引, 就是 Arduino 代码里使用的引脚编号.

例如:

  • PA_15digitalPin 数组中的索引为 15, 则 Arduino 代码中使用的引脚号就是 15.

排查建议

  • 先确认所选 boardvariant 是否一致.
  • 区分“Arduino 引脚编号”和“MCU 原始端口名”, 不要混用.
  • 若某个引脚不可用, 还要检查其是否被复用为下载, 调试, 晶振或其他外设功能.

ws2812 使用笔记

芯片手册

立创商城 – ws2813 芯片手册 – 由于ws2813手册是中文的,且兼容ws2812

数据发送速度可达800Kbps

数据协议采用单线归零码的通讯方式,像素点在上电复位以后,DIN端接受从控制器传输过来的数据,首先 送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整 形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。

数据传输时间

时序波形

数据传输方法

24bit 数据结构

典型应用电路图

STM32F7 控制 ws2812

PWM + DMA

数据发送速度是800kHz, 表示 ws2812 的输入频率为800kHz, 我用的是 TIM3 PWM CH1, 使用内部时钟, 即TIM3所属总线APB1的CLOCK, 速度为108mHz, 为了得到800kHz, 108mHz = 0.8mHz * 5 * 27, 设置 TIM3 的预分频 prescaler 为 5-1, 设置 自动重载值 ARR 为 27-1, 所以 PWM 的 PULSE的取值范围是 0~26, 根据 ws2812 的“数据传输时间“, 可以计算得: 0码的占空比大约为 $220 / (220 + 580)\approx0.275$, 1码的占空比大约为 $580 / (220 + 580)\approx0.725$, 所以设置 0码的 Pulse 为 $0.275 * 27 \approx 7$, 1码的 Pulse 为 19.

PWM参数:

    CH Polarity: 空闲时刻的电平状态
        "High" 表示空闲为高, 所以有效电平为"Low", 即低电平有效
        "Low" 表示空闲为低, 所以有效电平为"High", 即高电平有效

        输出比较极性的指的是你在比较匹配之后输出口输出的极性

    Mode:
        PWM Mode 1, CNT<CRRx为有效电平
        PWM Mode 2, CNT>CRRx为有效电平

    当向上递增时, 设置 CH Polarity 为 "High", 选择 "PWM Mode 1", 则: High为有效电平, CNT < CRRx 时 为High

配置好了以后别忘了, 启动PWM:
    HAL_TIM_PWM_Start(&htimX, TIM_CHANNEL_X);

4x4 键盘矩阵

说明

  • 本页记录 4x4 矩阵键盘的基本接线方式, 扫描思路和调试要点.
  • 这类模块常用于菜单输入, 数字输入和低成本本地控制面板.

模块示意

基本原理

  • 4 根行线 + 4 根列线组合成 16 个按键位置.
  • 通过轮流驱动行线或列线, 读取另一侧电平变化, 就可以判断哪个键被按下.
  • 相比每个按键单独占一个 GPIO, 矩阵键盘能明显节省引脚资源.

常见扫描流程

  1. 先将所有列线配置为输入, 行线逐个输出有效电平.
  2. 每次只拉低或拉高一行, 再读取所有列线状态.
  3. 如果某一列出现有效变化, 即可根据“当前行 + 当前列”定位按键.
  4. 对全部行循环扫描, 就能得到完整按键状态.

调试要点

  • 要处理按键抖动, 否则一次按下可能被识别成多次触发.
  • 若支持长按或组合键, 需要在扫描结果之上再做状态机处理.
  • 接线前先确认模块排针顺序, 不同厂家标注可能不完全一致.
  • 若列线始终无变化, 优先检查 GPIO 模式, 上拉下拉配置和扫描时序.

适用场景

  • 简单密码输入或功能菜单选择.
  • 仪器, 控制面板, 工业设备的人机输入界面.
  • LCD, 数码管, 蜂鸣器 搭配组成低成本本地交互方案.

相关文档

嵌入式电路记录

说明

  • 本目录用于保存偏板级应用的具体电路记录.
  • 与上层 嵌入式电路应用笔记 的关系是: 上层负责抽象共性, 本目录保留具体方案.

当前条目

使用建议

  • 查通用原理和应用边界时, 优先看上层总览页.
  • 查具体方案示例时, 再进入当前目录.

主副电路切换

常见需求

  • 主电源掉电后自动切到备用电源.
  • USB 供电与电池供电之间自动切换.
  • 两路信号源或两套电路之间按优先级切换.

常见实现方式

二极管或电源 OR

  • 结构简单, 成本低.
  • 代价是压降较大, 效率一般.

PMOS 理想二极管方案

  • 压降低于普通二极管方案.
  • 更适合低压供电和对效率敏感的场景.

继电器或模拟开关

  • 更适合做信号切换或完全隔离.
  • 需要额外关注切换延迟, 接触寿命和控制逻辑.

设计要点

  • 防止两路电源互相倒灌.
  • 明确优先级, 切换门限和上电时序.
  • 对 MCU 场景, 要确认切换过程中是否需要储能电容做过渡保持.

参考

MOS 管驱动电路

常见场景

  • NMOS 低边开关, 适合驱动继电器, 灯带, 电机等负载.
  • PMOS 高边开关, 适合做正电源路径控制.
  • 大电流或高频切换时, 常需要专用栅极驱动器.

设计要点

  • 栅极通常要串联小电阻, 以抑制振铃和过大尖峰电流.
  • 栅极还应配置上拉或下拉电阻, 避免上电悬空误导通.
  • 不要把阈值电压 Vgs(th) 当成“完全导通电压”.
  • 高边 NMOS 驱动通常需要自举或升压驱动方案.

选型建议

  • 先看负载电流和开关频率.
  • 再看 Rds(on), 栅极电荷, 封装散热能力和驱动电压.
  • 若负载感性较强, 还要考虑续流路径和关断尖峰吸收.

参考

  • 参考: https://www.eet-china.com/mp/a69256.html
  • 基础器件原理可参考 MOS 管

LVGL

环境

官方文档

https://docs.lvgl.io/master/overview/index.html

simulator on PC

模拟器是为了方便在电脑端开发并验证效果

  1. 下载 Eclipse CDT IDE 环境

    https://github.com/gnu-mcu-eclipse/org.eclipse.epp.packages/releases/

  2. 安装 SDL2

    On Linux:

     sudo apt-get update && sudo apt-get install -y build-essential libsdl2-dev
    
  3. 下载 eclipse’s simulator project:

    git clone –recursive https://github.com/lvgl/lv_sim_eclipse_sdl.git

  4. 使用cmake编译这个项目

    cd lv_sim_eclipse_sdl mkdir build cd build cmake .. make -j

  5. 打开 eclipse CDT, 导入 simulator project

  6. 在 “Run Configurations…” 配置编译 “C/C++ Application”

    编译命令是:

     make -j -C ${workspace_project_locations}/build
    

    这样就可以编译并运行了

例子

f746 disco:

    git clone --recursive https://github.com/lvgl/lv-port_stm32f746_disco.git

lvgl

设置 宽和高.

lv_disp_drv_t disp_drv; disp_drv.hor_res = LV_HOR_RES_MAX; disp_drv.ver_res = LV_VER_RES_MAX;

有两种方式设置旋转屏幕:

  1. lvgl soft 实现, 这耗时较大

    disp_drv.sw_rotate = 1; disp_drv.rotated = LV_DISP_ROT_90;

  2. 硬件实现, 速度快, 消耗小

    disp_drv.rotated = LV_DISP_ROT_90; // 这种情况, 这个只用来 确定 分辨率

    并设置 CONFIG_LV_DISPLAY_ORIENTATION

    硬件调用: st7735s_set_orientation(uint8_t orientation) 可能需要 设置屏幕 X.Y对调, X方向从上到下, Y方向从下到上 等等

GUI Guider

https://www.nxp.com/design/software/development-software/gui-guider:GUI-GUIDER

嵌入式资源收集

说明

  • 本页用于收集值得长期保留的嵌入式库, 工具链和资料入口.
  • 当前先记录轻量级数据存储方向的代表项目, 后续再逐步扩展.

FlashDB

  • 项目地址: https://armink.gitee.io/flashdb/#/zh-cn/
  • 定位: 面向资源受限设备的轻量级嵌入式数据库.
  • 适用场景: 参数配置持久化, 简单状态存储, 事件日志或时序数据落盘.
  • 选型关注点: Flash 擦写寿命, 页大小, 掉电一致性, 与 RTOS / 裸机系统的集成成本.

整理建议

  • 后续可继续补充 文件系统, KV 存储, Bootloader, 协议栈, 调试工具 等方向.
  • 若某个方向内容持续增多, 建议从本页拆成独立专题页.

设计总览

说明

  • 本目录收录偏视觉设计与设计工具使用记录的文档.
  • 当前内容不多, 但适合继续作为轻量设计知识入口保留.
  • 若内容更偏前端实现, 应优先放到 frontend/ 而不是本目录.

当前主题

阅读路径

  • 需要看 3D 建模工具和操作记录时, 优先看 Blender 笔记.
  • 需要看在线界面设计工具时, 优先看 即时设计笔记.

目录边界

  • 本目录只保留设计工具, 设计流程和视觉制作相关内容.
  • 实际前端组件实现和样式框架知识, 应继续放在 frontend/.

Blender 笔记

文档整理说明

  • 本文档当前主要记录 Blender 相关环境依赖, 尤其是 Linux 下 AMD 显卡驱动与配套组件安装.
  • 后续如补充建模, 渲染, 插件使用, 可继续按主题扩展.

Linux 下安装 AMD 显卡驱动

RX 590 显卡驱动 Linux 版下载:

  • 官方入口: https://www.amd.com/zh-hans/support/
  • 安装脚本说明: https://amdgpu-install.readthedocs.io/en/latest/install-script.html

我使用过的安装方式:

./amdgpu-pro-install -y --opencl=pal,legacy

安装 AMD 驱动

apt install firmware-linux firmware-linux-nonfree libdrm-amdgpu1 xserver-xorg-video-amdgpu

安装 Vulkan

apt install mesa-vulkan-drivers libvulkan1 vulkan-tools vulkan-utils vulkan-validationlayers

安装 OpenCL

apt install mesa-opencl-icd
apt install ocl-icd-* opencl-headers
apt install clinfo
clinfo

查看显卡

lspci -nn | grep VGA

Deepin 标题栏高度调整

mkdir -p ~/.local/share/deepin/themes/deepin/dark

编辑 titlebar.ini:

[Active]
height=24

[Inactive]
height=24

其他安装记录

apt-get install firmware-amd-graphics libgl1-mesa-dri libglx-mesa0 mesa-vulkan-drivers xserver-xorg-video-all

若遇到 dpkg-deb: 错误: 粘贴 子进程被信号(断开的管道) 终止了, 可尝试:

sudo dpkg -i --force-overwrite /var/cache/apt/archives/firmware-amd-graphics_20190114-2_all.deb

插件与相关项目

CAD Sketcher

  • 安装说明: https://hlorus.github.io/CAD_Sketcher/installation/

screencast_keys

  • 当前仅保留插件名称, 后续可继续补充使用笔记.

即时设计笔记

说明

  • 本页记录 即时设计 中高频快捷键与基础操作技巧.
  • 适合作为 UI 设计, 标注和组件排版时的轻量速查页.

快捷键

复制图形

  • 按住 Alt, 拖动某个形状.
  • 或者使用 Ctrl + C / Ctrl + V.

显示间距

  • 按住 Alt, 再把鼠标移动到间距的位置.

约束拖动角度

  • 按住 Shift, 会自动约束到垂直, 水平或 45°.

输入框小技巧

  • 数字输入框可以进行简单计算, 比如 409+9.
  • 这类小计算适合做尺寸微调, 不必切到外部计算器.

适用场景

  • 快速搭建界面草图与组件排版.
  • 做尺寸校对, 间距检查和布局微调.
  • 在设计与前端协作时快速确认视觉稿细节.

后续整理建议

  • 可继续补充组件约束, 对齐, 标注, 切图, 导出规范等内容.
  • 若后续内容增多, 可考虑拆成“快捷键”, “布局”, “组件”, “交付”几个小节.

立创 EDA 笔记

说明

  • 本页记录 立创 EDA 中与仿真模型相关的轻量备忘.
  • 当前内容聚焦 2N2222 的 SPICE 模型参数, 适合作为原理图仿真时的快速参考.

2N2222 仿真模型

.model 2N2222 NPN(IS=1E-14 VAF=100 BF=200 IKF=0.3 XTB=1.5 BR=3 CJC=8E-12 CJE=25E-12 TR=100E-9 TF=400E-12 ITF=1 VTF=2 XTF=3 RB=10 RC=.3 RE=.2 Vceo=30 Icrating=800m mfg=Philips)

参数理解示例

BF

  • 表示理想条件下的最大正向电流放大系数 β.
  • 在仿真中会影响三极管放大区的行为表现.

其它参数

  • IS: 反映结饱和电流的近似量级.
  • VAF: 与 Early 效应相关.
  • CJC, CJE: 结电容相关参数.
  • TR, TF: 反映动态响应特性.

使用建议

  • 仿真模型适合做趋势验证, 不等于真实器件在所有工况下的精确表现.
  • 如果后续要整理 立创 EDA 更系统的使用方法, 可继续补原理图库, 封装库和仿真流程说明.

编程总览

说明

  • 本目录收录通用编程语言, 框架, 后端, 脚本语言与机器人相关笔记.
  • 前端知识已优先收敛到 前端总览, 历史 Web 示例工程归档在 program/web.
  • 若只是查语言或工具链知识, 优先从本页进入.

主要方向

建议阅读路径

  • 语言基础与工具链问题, 先从对应语言总览页进入.
  • 如果目标是历史工程样例, 再进入 program/web/, program/rust/code/ 等归档目录.
  • 若问题明显偏浏览器或前端工程, 优先回到 frontend/ 总览.

高风险任务入口

  • 系统编程, 绑定和工具链问题优先看 RustC/C++.
  • 脚本自动化, 远程运维和快速原型优先看 Python.
  • 跨端 GUI 和移动端实验优先看 Dart 与 Flutter.
  • 托管代码与原生库互调优先看 C# 总览.
  • 机器人中间件与设备协同优先看 ROS2机器人技术总览.

目录边界

  • frontend/ 主要放前端框架, Web 构建和浏览器相关知识.
  • program/ 更偏语言本身, 工具链, 原生开发和跨语言交互.
  • program/web/ 当前主要作为历史示例工程归档, 不再承担核心知识导航职责.

Rust 总览

说明

  • 本文档作为 Rust 语言与常用开发流程的总览入口.
  • 旧版零散 Rust 记录中关于 Pin / Unpin 的内容已并入本文.
  • 更偏概念速记的内容可参考 Rust 要点, 工具类内容可参考 Rust 工具, 调试排错可参考 Rust 调试技巧.

建议阅读路径

  • 初学语法和所有权问题时, 先看 Rust 要点 与当前总览页.
  • 需要工具链, 构建和排错时, 优先看 Rust 工具, Rust 调试技巧, Rust 问题汇总.
  • 需要框架或平台专题时, 再进入 egui, Dioxus, Tauri, Rust for Android, wasm 等专题页.

专题入口

代码示例归档

目录边界

  • program/rust/ 主要承载语言知识, 框架使用和工具链专题.
  • 可运行型历史实验工程集中放在 program/rust/code/program/rust/test_usb/.
  • 若内容更偏前端 UI 或 Web 工程归档, 应回到 frontend/program/web/.

在 .config/config.toml 中设置默认编译目标

# out-dir 需要添加编译选项 cargo build -Z unstable-options

[build]
target = "i686-pc-windows-msvc"
out-dir = "D:/programs/ehTestCore_V1.2.1.13/plugin"

在 Cargo.toml 中指定 target, nightly

cargo-features = ["per-package-target"]

[package]
default-target = "i686-pc-windows-msvc"

TAB 键 自动补全

参考: https://rust-lang.github.io/rustup/installation/index.html#enable-tab-completion-for-bash-fish-zsh-or-powershell

for linux

mkdir -p ~/.local/share/bash-completion/completions rustup completions bash >> ~/.local/share/bash-completion/completions/rustup

cargo 命令

安装常用的工具:

    # 扩展cargo, 允许对 dependencies 的增删改
    # 查看依赖关系
    # 基于模板生成项目
    # 宏展开
    cargo install cargo-edit cargo-tree cargo-generate cargo-expand

    ps:
        cargo-edit 部分命令:
            cargo add <crate>
            cargo add <crate> --allow-prerelease, 允许使用beta版本的库
            cargo rm <crate>
            cargo upgrade

        cargo-tree 部分命令:
            cargo tree
            cargo tree -e features
            cargo tree -f "{p} {f}"

    # 文档生成, 及插件: 用于数学表达式, 生成流程图表
    cargo install mdbook mdbook-katex mdbook-mermaid

# 创建项目
cargo new hello_cargo

# 编译 并生成可执行程序
cd hello_cargo
cargo build
cargo build --release
cargo build --target thumbv7m-none-eabi

# 编译 但不生成 可执行程序
cargo check

# 运行目标程序 (也可以一步构建项目)
cargo run

# 创建
cargo new hello_world <--bin>, 创建一个二进制程序
cargo new hello_world --lib, 创建一个库

# 参数

不使用默认的features:
    --no-default-features

    指定features:
        --features="FEATURE1 FEATURE2 ..."

# 显示详细的编译信息, 包括 build.rs 中的 print
cargo build -vv

# 安装 binutils 工具
cargo install cargo-binutils
rustup component add llvm-tools-preview

VSCode 中 Rust 插件

1. rust-analyzer
2. Crates
3. Even Better TOML
4. CodeLLDB

Ownership

1. Rust 中的每一个变量都有一个 owner
2. 在同一时刻 owner 只有一个.
3. 当 owner 离开作用域时, 它的值将会被丢弃

References and Borrowing

& 引用
* 解引用

mut 可变
默认不可变

借用, 即 获取引用作为函数参数

ps:
   1. 在任意给定时刻, 只能拥有一个可变引用或任意数量的不可变引用 之一 (而不是两者)
   2. 引用必须总是有效的

Lifetimes

第一条规则是每一个是引用的参数都有它自己的生命周期参数

第二条规则是如果只有一个输入生命周期参数, 那么它被赋予所有输出生命周期参数

第三条规则是如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self, 那么所有输出生命周期参数被赋予 self 的生命周期

Pin 与 Unpin

参考: https://folyd.com/blog/rust-pin-unpin/

Pin 自身是一个智能指针包装器, 因为它实现了 Deref 和受约束的 DerefMut.

Pin 包裹的内容通常应该是某种指针类型, 而不是普通值类型, 例如 Pin<u32> 一般没有意义.

Pin 的核心语义是“钉住”被包裹对象, 防止其在内存中移动. 这个语义是否真正生效, 取决于目标类型是否实现了 Unpin.

  • 如果 T: Unpin, 那么 Pin<P<T>> 基本等价于 P<T>, “钉住”语义不会真正限制移动.
  • 如果 T: !Unpin, 那么从被 Pin 包裹到被销毁期间, 必须保证它保持在固定位置.

Unpin 是一个 auto trait, 编译器默认会为大多数类型自动实现它. 常见例外包括:

  • PhantomPinned
  • async/await 降糖后生成的某些 Future 状态机
#![allow(unused)]
fn main() {
pub struct Pin<P> {
    pointer: P,
}

impl<P: Deref> Deref for Pin<P> {
    ...
}

impl<P: DerefMut<Target: Unpin>> DerefMut for Pin<P> {
    ...
}
}

一个典型的 !Unpin 例子:

#![allow(unused)]
fn main() {
struct Test {
   a: String,
   b: *const String,
   _marker: PhantomPinned,
}
}

如何理解 Pin<P<T>>

  • 如果 P<T> 符合 Unpin, 那么 Pin<P<T>> 不会真正把它“钉住”.
  • 如果 P<T> 不符合 Unpin, 那么从被 Pin 包裹到销毁期间, 都要保证它不发生移动.

Rust 的 安装与卸载

参考链接:
    https://www.rust-lang.org/zh-CN/tools/install

安装:
    curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

版本号:
    rustc --version

升级:
    rustup update stable
    rustup update

使用 不同 版本:
    rustup default stable
    rustup default nightly

添加工具:
    cargo install cargo-generate
    cargo install cargo-edit
    cargo install mdbook

卸载:
    rustup self uninstall

使用特定的 rust 版本

参考: https://doc.rust-lang.org/edition-guide/rust-2018/rustup-for-managing-rust-versions.html

rustup toolchain install nightly-2020-11-19
rustup toolchain list
rustup default nightly-2020-11-19

ps:
    "2020-11-19" 这个时间是在 rust 的 git 中的tag上找的

# 安装 nightly 版本的工具链
rustup toolchain install nightly

# 对当前项目使用 nightly
rustup override set nightly

# 对所有项目使用 nightly
rustup default nightly

指定 默认编译器

创建 rust-toolchain.toml 文件, 添加:

[toolchain]
channel = "nightly-2024-01-05"
components = [
  "cargo",
]

cargo 指定编译器 报错:

'cargo.exe' binary, normally provided by the 'cargo' component, is not applicable to ..

rustup component remove cargo
rustup component add cargo

一些额外的命令

输出 详细的编译命令

cargo build --build-plan -- -Z unstable-options > build.json

riscv

rustup target add riscv32imac-unknown-none-elf

stm32

rustc install thumbv7m-none-eabi
rustup target install thumbv7m-none-eabi
cargo build --target thumbv7m-none-eabi

vscode 中 rust 的插件

1. rust-analyzer
2. Crates
3. Better TOML
4. CodeLLDB

Rust hello_world

编写点一个程序:
    vim main.rs

    fn main() {
        println!("Hello, world!");
    }

编译:
    rustc main.rs

运行:
    ./main

Rust 编译过程

Rust 编译器(rustc)是一个编译器前端, 它负责把文本代码一步步编译到 LLVM 中间码(LLVM IR), 再交给 LLVM, 最终生成机器码, 所以 LLVM 是编译器后端.

宏展开 –> 语法语义分析 –> 生成抽象语法树 –> LLVM IR (LLVM 中间语言) –> 通过 LLVM 生成机器码

使用 Cargo 创建项目

创建项目 hello_cargo:
    cargo new hello_cargo

cd hello_cargo

编译 并生成可执行程序:
    cargo build
    cargo build --release

编译 但不生成 可执行程序:
    cargo check

运行目标程序 (也可以一步构建项目):
    cargo run

创建:
    cargo new hello_world <--bin>, 创建一个二进制程序
    cargo new hello_world --lib, 创建一个库

kurbo
    曲线工具

lyon
    路径细分库, 可以用于基于GPU的2D图形渲染


tokio
    https://github.com/tokio-rs/tokio

embedded_graphics
    mcu gui

embedded_sdmmc

GUI 开发

iced

sudo apt install libssl-dev

官方的例子:

    git clone https://github.com/hecrj/iced.git

    cd iced

    cargo build

    // --features glow,glow_canvas
    cargo run --package todos

// 如果无法运行, 就安装下面的软件包
sudo apt install libvulkan1 mesa-vulkan-drivers vulkan-utils

iced: 编译 并 运行所有的 examples

#!/bin/bash

examples=(iced_core iced_futures iced_graphics iced_native iced_style iced_glow iced_glutin iced_winit iced_web iced_wgpu bezier_tool iced clock color_palette counter custom_widget download_progress events game_of_life geometry integration pane_grid pick_list pokedex progress_bar qr_code scrollable solar_system stopwatch styling svg todos tour)

for example in ${examples[@]}; do
    cargo build --verbose --package $example
done

for example in ${examples[@]}; do
    echo "start run: cargo run --verbose --package $example"
    cargo run --verbose --package $example
done

iced 例子

学习基本的布局:
    target/debug/pane_grid
    examples/tour

学习canvas:
    examples/clock

bevy, 游戏引擎

git clone https://github.com/bevyengine/bevy
cargo run --example breakout

libusb, usb 库

https://stackoverflow.com/questions/1710922/how-to-install-pkg-config-in-windows


在windows上找不到libusb库, 在 ~/.cargo/config 中添加:

[target.x86_64-pc-windows-msvc.'usb-1.0']
rustc-link-search = ['D:\libs\64bit']
rustc-link-lib = ['libusb-1.0']

wasm 开发

安装 wasm-pack:

    curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

cargo install cargo-generate

cargo generate --git https://github.com/rustwasm/wasm-pack-template -n wasm-app

cd wasm-app
wasm-pack build

npm init wasm-app www

cd www
yarn

cd ../pkg
yarn link

cd ../www
yarn link "wasm-app"

vue vite rust wasm

yarn create @vitejs/app my-vue-app --template vue

cd my-vue-app
yarn add -D vite-plugin-rsw

yarn

cargo generate --git https://github.com/rustwasm/wasm-pack-template -n wasm-app

vite.config.js:

    import { defineConfig } from "vite";
    import vue from "@vitejs/plugin-vue";
    import ViteRsw from 'vite-plugin-rsw';

    // https://vitejs.dev/config/
    export default defineConfig({
        plugins: [
            vue(),
            ViteRsw({
                mode: "release",
                crates: ["wasm-app"],
            }),
        ],
    });

异步

impl Future for Server

Server实例就可以直接使用 .await 了

汇编

https://godbolt.org/ 二进制浏览器

rust 编译选项

在 build.rs 中

#![allow(unused)]
fn main() {
println!("cargo:rustc-link-arg=-fPIC");
}

在 .config/config.toml 中

[target.arm-unknown-linux-gnueabihf]
rustflags = [
    # "-Clink-arg=-Tsrc/linker.ld",
    # "-Cforce-frame-pointers=yes"

    # "-C", "linker=arm-linux-gnueabihf-ld",
    "-C", "linker=arm-linux-gnueabihf-gcc",
    "-C", "relocation-model=pic",
    "-C", "target-feature=+crt-static",

    # LLD (shipped with the Rust toolchain) is used as the default linker
    # "-C", "link-arg=-Tlink.x",
]

调试 rust dll

.vscode\launch.json

{
    "type": "lldb",
    "request": "launch",
    "name": "ehTestCore",
    "program": "xxx.exe",
    "args": [],
    "cwd": "xxx",
    "stopOnEntry": false,
    "sourceLanguages": [
        "rust"
    ],
    "sourceMap": {
        "/rustc/73c9eaf21454b718e7c549984d9eb6e1f75e995c": "C:/Users/maxu/.rustup/toolchains/nightly-x86_64-pc-windows-msvc/lib/rustlib/src/rust"
    }
},

rust 的标准库 调试, 需要添加下面的内容, 其中 rustc 后面的 commit-hash 可以通过命令获得:

"sourceMap": {
    "/rustc/73c9eaf21454b718e7c549984d9eb6e1f75e995c": "C:/Users/maxu/.rustup/toolchains/nightly-x86_64-pc-windows-msvc/lib/rustlib/src/rust"
}

rustc -Vv | grep commit-hash | cut -d' ' -f 2

静态 c++ 运行时库

[build-dependencies] static_vcruntime = “2.0”

rust-toolchain.toml

[toolchain]
channel = "1.77.2"

windows 系统编译报错, #![cfg_attr(feature = "stdsimd", feature(stdsimd))]

[toolchain]
channel = "nightly-2024-02-05"

打印配置

rustc –print cfg

显示版本

cargo –version –verbose

Rust 要点

关于泛型

item: &impl Summary, 就是泛型, 等效于 item: T where T: Summary

前面的写法, 只是语法糖, 真实的是后面的写法

泛型就是单态化, 也就是特定类型单独实现, rust不存在所谓的泛型, 只是为每一个特定类型生成了 特定类型的函数

这里, 直接就生成了特定类型(具体类型)的函数调用, 此处的泛型, 只是起限定作用了, 只是编译器检查就行了, 保证 类型正确性, 并生成特定类型的函数

于是, 这里的泛型函数, 就属于静态分发, 类型啥的都是确定的, 实现trait实际上都直接为原始data作为参数, 生成了函数, 没有vtable

关于闭包

当创建一个闭包的时候, 实际上rust生成了一个匿名的struct, 它包含函数指针, 和 引用或捕获的变量, 这个匿名struct会根据闭包是否使用可变引用或不可变引用或捕获, 自动实现 FnOnce, FnMut, Fn, 并创建匿名结构体的实例

当调用的时候, 就是执行这三个中的call函数, 第一个参数分别是: self, &self, &mut self. 于是执行完成后, 对于self, 即FnOnce 这个匿名struct所有权已经回收了, 只能执行一次. 对于 &self 和 &mut self, 即 FnMut 和 Fn, 是可以继续执行的.

Rust 迭代器技巧

说明

  • 本页整理 Rust 迭代器与集合处理中常见的小技巧和代码片段.
  • 适合作为 zip, chain, flatten, fold, windows, chunks 等方法的速查页.

小技巧

  • length - index 常表示“从 index 到结尾还剩多少长度”.
  • index - length 常表示“从当前位置回退指定长度后的起点”, 使用时要注意不要越界.
  • index + length 常表示“从 index 开始, 长度为 length 的结束位置”, 通常是一个不含终点的开区间边界.

zip 把两个 vec 打包

  • 适合把两个等长或可并行消费的序列组合成元组流.

chain 链接两个可迭代对象

  • 适合把两个同类型迭代器首尾串起来统一处理.

flatten 展开嵌套集合

#![allow(unused)]
fn main() {
let data = vec![vec![1, 2, 3, 4], vec![5, 6]];
let flattened = data.into_iter().flatten().collect::<Vec<u8>>();
assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]);
}

fold 求和

#![allow(unused)]
fn main() {
data.iter().fold(0u32, |acc, &x| acc + x as u32)
}

tuple_windows 生成相邻窗口

#![allow(unused)]
fn main() {
let mut v = Vec::new();
for (a, b) in (1..5).tuple_windows() {
    v.push((a, b));
}
assert_eq!(v, vec![(1, 2), (2, 3), (3, 4)]);
}

chunks 后分组拼接字符串

#![allow(unused)]
fn main() {
let output = raw.chars().collect::<Vec<char>>();
let output = output.chunks(count).map(|v| v.iter().collect::<String>());

result = if invert {
    output.rev().collect::<Vec<String>>().join(&add_str)
} else {
    output.collect::<Vec<String>>().join(&add_str)
};
}

windows 滑动窗口

#![allow(unused)]
fn main() {
let result = "abcdef";
result.chars().collect::<Vec<char>>().windows(3).for_each(|v| {
    println!("{v:?}");
});
}

输出:

['a', 'b', 'c']
['b', 'c', 'd']
['c', 'd', 'e']
['d', 'e', 'f']

chunks 固定分块

#![allow(unused)]
fn main() {
let result = "abcdefg";
result.chars().collect::<Vec<char>>().chunks(3).for_each(|v| {
    println!("{v:?}");
});
}

输出:

['a', 'b', 'c']
['d', 'e', 'f']
['g']

Rust 宏

说明

  • 本页整理 Rust 宏相关的基础概念, 常见工具链和使用注意点.
  • 适合在阅读过程宏, 声明宏或绑定生成代码时做快速定位.
  • 若问题更偏代码生成生态, 常与 syn, quote, proc-macro2 一起出现.

宏的常见分类

声明宏

  • macro_rules! 为主.
  • 适合做语法样板消除, 批量生成实现和简单 DSL.

过程宏

  • 需要单独的 proc-macro crate.
  • 常见类型包括 derive, attribute-like, function-like 宏.
  • 适合做结构化代码生成和语法树级改写.

常见工具库

syn

  • 用于把 Rust 代码解析成 Rust 语法树.
  • 过程宏里最常见的解析库之一.

quote

  • 用于把模板代码生成 TokenStream.
  • 常和 syn 配合, 实现“解析 -> 变换 -> 生成”流程.

proc-macro2

  • 为过程宏生态提供更通用的 TokenStream 类型.
  • 便于在非 proc-macro 上下文中共享逻辑.

常见技巧

ident 转换成字符串

#![allow(unused)]
fn main() {
stringify!(ident)
}
  • 常用于调试, 日志输出和生成说明文本.
  • 注意它返回的是源代码层面的字面内容, 不是运行时变量值.

bindgen 生成代码的命名警告

如果是通过 bindgen 生成的代码, 常会遇到 snake case 或命名风格警告.

可在 lib.rs 中局部禁用:

#![allow(unused)]
fn main() {
#[allow(nonstandard_style)]
mod bindings;
}
  • 更推荐把这类豁免限制在自动生成模块内, 不要扩大到整个 crate.

使用建议

  • 简单代码复用优先用函数和 trait, 不要一开始就上宏.
  • 宏一旦复杂, 应优先保证错误信息可读, 不要只追求炫技式写法.
  • 过程宏调试时, 常配合 cargo expand 查看展开结果.

常见问题

  • 宏报错但定位不清时, 先看展开后的代码.
  • 过程宏的输入和输出都应尽量保持结构稳定, 否则很难维护.
  • 若只是为了减少几行重复代码, 未必值得引入宏复杂度.

相关文档

Rust 工具

文档整理说明

  • 本文档用于记录 Rust 生态中的常用工具, 包括 Cargo 扩展, 打包工具, 二进制分析工具, 以及日常命令行环境配置.
  • 旧版 Cargo 扩展工具记录已收敛到本文, 后续统一以当前页面为准.

常用 Cargo 扩展

cargo install cargo-generate cargo-binutils cargo-tree cargo-expand cargo-edit cargo-wix cargo-bundle

cargo-edit

用于升级, 增删依赖:

cargo install cargo-edit
cargo upgrade

cargo-machete

检查未使用的 crate:

cargo install cargo-machete
cargo machete

cargo-udeps

检查未使用的依赖:

cargo install cargo-udeps
cargo udeps

需要 nightly 环境, 结果并不是 100% 准确.

cargo-bloat

显示不同 crate 占用输出文件大小的情况:

cargo install cargo-bloat
cargo bloat --release --crates

cargo-autoinherit

自动将 workspace 中项目的依赖转到 workspace 共享配置:

cargo install cargo-autoinherit
cargo autoinherit

也可以根据日志检查是否依赖了多个不同版本的 crate.

打包

cargo-dist

打包工具:

cargo install cargo-dist

在需要打包的 crate 目录下执行:

dist init
dist build

参考项目: https://github.com/axodotdev/axolotlsay

oranda

用于给 cargo-dist 打包后的文件创建静态站点:

cargo install oranda --locked --profile=dist
oranda build

二进制与证书工具

upx

用于压缩可执行文件:

upx --best --lzma target/release/your_executable

下载地址: https://github.com/upx/upx/releases

feluda

检查是否存在限制性证书:

cargo install feluda
feluda

Nushell

# 设置默认编辑器
$env.config.buffer_editor = "code"

# 打开配置
config nu

# 在配置中添加别名
alias cc = claude --dangerously-skip-permission

# 在配置中添加自动完成
source "E:/git/rust/nu_scripts/custom-completions/git/git-completions.nu"
source "E:/git/rust/nu_scripts/custom-completions/cargo/cargo-completions.nu"
source "E:/git/rust/nu_scripts/custom-completions/npm/npm-completions.nu"

Rust 后端开发

说明

  • 本页保留 Rust 后端学习资源和接口调试工具入口.
  • 当前内容偏“起步资源导航”, 适合作为后续继续扩展的占位页.

学习项目

  • 一个较基础的学习项目: https://github.com/noxue/banquet

接口调试工具

  • 一个方便写接口和调试请求的工具: https://www.apipost.cn/download.html

记录

后续可补

  • Web 框架选型, 例如 Axum, Actix Web, Poem.
  • 配置管理, 日志, 数据库和中间件组织方式.
  • 接口测试与压测工具链.

Rust 索引性能讨论

原始讨论

  • Reddit: https://old.reddit.com/r/rust/comments/1rhb97r/is_there_any_significant_performance_cost_to/
  • 主题: array.get(idx).ok_or(Error::Whoops)array[idx] 的性能差异
  • 发帖时间: 2026-02-28 19:15:51 UTC, 即北京时间 2026-03-01 03:15:51

问题核心

原帖主要在问 3 件事:

  1. array.get(idx).ok_or(Error::Whoops) 相比 array[idx] 是否有明显性能损耗.
  2. array.get(idx).ok_or(Error::Whoops) 是否比手写 if idx < array.len() 更快.
  3. 在大量索引访问, 但又不方便改成迭代器时, 应该优先选择哪种写法.

讨论中的高价值结论

1. 大多数情况下, .get(idx)array[idx] 都要做边界检查

讨论里比较一致的观点是: 只要编译器不能证明索引一定合法, 这两种写法通常都需要边界检查.

  • array[idx] 的越界结果是 panic.
  • array.get(idx) 的越界结果是 None.
  • 再接 ok_or(...)ok_or_else(...) 时, 只是把 Option 转成 Result.

也就是说, 真正决定开销的核心往往不是语法表面, 而是:

  • 编译器能不能消掉边界检查.
  • 错误对象是否需要提前构造.
  • 这段代码是不是热路径.

2. ok_or_else(...) 通常比 ok_or(...) 更合适

这是讨论里最明确, 也最有实用价值的一点.

Rust 官方文档明确说明:

  • Option::ok_or(err) 会急切地计算参数 err.
  • Option::ok_or_else(f) 会延迟到 None 时才调用闭包构造错误.

因此, 如果错误值构造并不便宜, 或错误类型带有析构成本, 更推荐写成:

#![allow(unused)]
fn main() {
array.get(idx).ok_or_else(|| Error::Whoops)
}

这可以避免在成功路径上白白创建错误对象.

3. 手写 if 一般不会天然比 .get(...).ok_or_else(...) 更快

讨论中有人用 Compiler Explorer 对比汇编, 结论是:

  • 在开启优化后, 这几种写法经常会被内联成非常接近的机器码.
  • 如果编译器已经能看懂控制流, 手写 if 未必有额外优势.

所以, 对大部分普通代码来说:

  • 不要凭感觉假设某种写法一定更快.
  • 先写可读性更好的版本.
  • 真进入性能热点后, 再基于 benchmark 或汇编做决定.

4. array[idx] 更适合表达 “这里按不变式保证一定合法”

讨论里也有比较稳妥的经验法则:

  • 如果索引一定合法, 比如常量索引, 前面已经检查过, 或由严格不变式保证, 可以使用 array[idx].
  • 如果索引可能非法, 或你更想把错误显式传递出去, 用 .get(idx) 更安全, 语义也更清楚.

这其实是在性能之外, 进一步强调 API 语义:

  • [] 偏向 “越界就是逻辑错误”.
  • .get() 偏向 “越界是可恢复分支”.

5. 真想绕过边界检查, 通常只有 get_unchecked

讨论里也提到, 如果目标是彻底避免边界检查, 最直接的方式通常是 get_unchecked.

但这意味着:

  • 进入 unsafe.
  • 越界会变成未定义行为, 而不是 panic 或 None.
  • 只有在 profiler 已经证明这里是关键热点, 且你能严格证明索引合法时, 才值得考虑.

换句话说, get_unchecked 不是 .get()[] 的日常替代品, 而是性能极限优化手段.

结合 Rust 文档与源码的补充分析

ok_orok_or_else

Rust 官方文档已经明确指出:

  • ok_or 是 eager 的.
  • ok_or_else 是 lazy 的.

因此, 原帖里的写法如果要继续保留 Result 风格, 更建议替换为:

#![allow(unused)]
fn main() {
let value = array
    .get(idx)
    .ok_or_else(|| Error::Whoops)?;
}

array[idx] 最终也会走到 slice 索引逻辑

从 Rust 标准库源码可以看到:

  • 数组的索引实现会委托给 slice.
  • slice 的 get 和索引逻辑底层都依赖 SliceIndex.

这说明讨论里的判断是合理的: 它们在抽象层面不同, 但底层都离不开 “索引合法性检查” 这个核心问题.

实用建议

日常代码

优先写可读, 安全, 语义清晰的版本:

#![allow(unused)]
fn main() {
let value = array
    .get(idx)
    .ok_or_else(|| Error::Whoops)?;
}

适用场景:

  • 越界属于正常错误分支.
  • 想把错误向上传递.
  • 更在意代码意图清晰, 而不是极限微优化.

已知索引一定合法

可以直接写:

#![allow(unused)]
fn main() {
let value = array[idx];
}

适用场景:

  • 索引已经在前面检查过.
  • 索引由数据结构不变式保证.
  • 越界一旦发生, 就是程序逻辑 bug.

性能极端敏感路径

按这个顺序处理:

  1. 先用 benchmark 或 profiler 确认这里真是热点.
  2. 看 release 汇编, 不要凭直觉优化.
  3. 只有在收益明确时, 再考虑 unsafe get_unchecked.

一句话结论

对这个 Reddit 帖子的结论做一句话总结:

array.get(idx).ok_or(Error::Whoops) 通常不会比 array[idx] 慢到值得担心, 但应优先改成 ok_or_else(...); 真正的差异更多取决于错误构造成本, 编译器能否消除边界检查, 以及这段代码是不是热点.

参考链接

  • Reddit 原帖: https://old.reddit.com/r/rust/comments/1rhb97r/is_there_any_significant_performance_cost_to/
  • Rust Option 文档: https://doc.rust-lang.org/std/option/enum.Option.html#method.ok_or_else
  • Rust 数组索引源码: https://doc.rust-lang.org/src/core/array/mod.rs.html#382-384
  • Rust slice 索引源码: https://doc.rust-lang.org/src/core/slice/index.rs.html#214-220

egui 学习

2022 4.13

根据不同的布局, 比如: 自上到下布局中,组件 自左到右/居中/自右到左 放置, 这里涉及到一个 frame_rect 和 widget_rect,分别表示 组件需要的空间大小, 组件本身的大小(包含margin)

还有一个justify, 这个参数用来控制一个组件是否需要铺满这个宽或高, 就是 widget_rect 的宽或高是满的

如果是垂直布局 水平居中对齐 或 水平布局 垂直居中对齐, 则 frame_rect 的宽或高是满的

拉伸边界线 无法让panel变宽, 原因是 拉伸改变了 ui.placer.region 的 max_rect, 但是最终生成的 Shape 是根据当前ui 的 ui.placer.region.min_rect,(根据代码中min_rect的注释, 有点难理解它的意思) 每一次分配一个新空间 (最终调用 allocate_space_impl), 会根据布局计算出需要的Rect, 然后修改 ui.placer.region的 min_rect 和 max_rect, 再移动 cursor 以确定下一次分配空间的位置.

2022 4.12 笔记

当实际分配的宽度不够某个窗口时, 窗口就会收缩, 而不管窗口有多大的宽度

关于 frame_rect 和 widget_rect,

frame_rect 表示: 根据布局得到一个空间, 用于放置 widget. widget_rect 表示: 一个 widget 的实际形状.

比如说: 水平居中布局, 这个 frame_rect 的宽度 等于 ui 的有效宽度

2022 3.13 笔记

当执行 App 的 update时, CtxRef对象 已经从Input得到了足够的数据, 比如获取到了 屏幕尺寸 鼠标移动 按键 等等.

当执行 egui::SidePanel::right("side_panel").min_width(250.0) 时, 只是构建了一个基本的 Panel对象, 当执行 panel.show(ctx, |ui|{}) 时, 会根据 当前有效区域 和 屏幕尺寸 构建一个 Ui 对象. panel 的layer_id 是 LayerId::background() Ui对象中, 会构建一个 Painter对象, Placer对象

Placer::new(max_rect, Layout::default()) 根据当前有效区域, 默认布局是 从上到下, 向左对齐,

Layout

方向:
    从上到下
    从下到上
    从左到右
    从右到左

对齐:
    Min: 左或上
    Center: 水平或垂直居中
    Max: 右或下

main_dir 主轴方向

main_wrap, 当到达主轴方向结束的地方时, 就截断

main_align, 主轴上 对齐方向

铺满规则

宽度铺满, 两种情况:
    1. 主轴垂直 且 水平方向 居中对齐
    2. 主轴水平 且 主轴的justify 为true

高度铺满, 两种情况:
    1. 主轴水平 且 垂直方向 居中对齐
    2. 主轴垂直 且 主轴的justify 为true

Ui对象

每次创建Ui对象时, ui的有效区域已经确定好了,

    ui.max_rect() 表示 ui的有效区域

layout, 包含了 组件的排列方式, 比如: 从上到下? 超出截断? 高度或宽度铺满?

placer, 包含了 min_

选中时 显示边框

参考 crates\egui\src\widgets\selected_label.rs

ScrollArea::vertical(), 宽度是自适应的, 如果需要父容器

Dioxus

说明

  • Dioxus 是 Rust 生态中的 UI / 全栈应用框架, 可覆盖 Web, Desktop, Mobile 等多平台.
  • 本页记录安装, 项目初始化和 Android 环境准备的最小入口.

适用场景

  • 使用 Rust 构建前端界面或跨平台客户端.
  • SSR, 全栈表单, 路由和组件化 UI 实验.
  • 希望在 Rust 生态内统一管理界面逻辑和业务代码.

安装

rustup target add wasm32-unknown-unknown
cargo install dioxus-cli@0.7.0-alpha.3

创建项目

dx new test_dioxus

初始化时可按需选择:

  • 全栈
  • 路由
  • tailwind

常见开发路径

  • 纯 Web 场景: 优先从 wasm 目标和前端路由开始.
  • 桌面应用场景: 先验证最小窗口与热重载流程.
  • 全栈场景: 明确前后端边界, 再决定是否启用 SSR 和服务端逻辑.

Android 环境

# 参考: https://dioxuslabs.com/learn/0.7/guides/platforms/mobile#android
export JAVA_HOME="D:\programs\Android\Android Studio\jbr\bin"
export ANDROID_HOME="D:\programs\Android\Sdk"
export NDK_HOME="$ANDROID_HOME/ndk/29.0.14206865"
export PATH="$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools"

emulator -list-avds

使用建议

  • 先明确目标平台, 不同平台的构建链路和依赖差异很大.
  • 若只是移动端打包实验, 可对照 Rust for Android 一起看.
  • 若是桌面壳 + Web 技术栈方案, 也可以同时对比 Tauri 的取舍.

相关文档

Tauri

2022.0927

yarn create tauri-app, 使用 yarn + svelte 开发

运行:
    yarn
    yarn tauri dev


unocss:

    参考 vite 中配置 unocss: https://github.com/unocss/unocss/tree/main/packages/vite

    1. 使用了 https://uno.antfu.me/ 提供的 默认配置 unocss.config.js (点击 顶部的设置图标)
    2. yarn add -D unocss
    3. 在 vite.config.ts 中配置:

        import Unocss from 'unocss/vite'

        export default {
            plugins: [
                Unocss({ /* options */ }),
            ],
        }

    4. 在 main.ts 中使用

        import 'uno.css'

2022.0827

yarn create tauri-app

选择 yarn, svelte-ts

2022.07.14

cargo install tauri-cli --version "^1.0.0"

cargo tauri --help

mkdir tauri_started && cd tauri_started

cargo tauri init

根据提示:

✔ What is your app name? · tauri_started
✔ What should the window title be? · hello world
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../web
✔ What is the url of your dev server? · ../web

cargo tauri dev

遇到一个错误 libpangoft2: undefined reference to `pango_font_get_hb_font'

临时解决方法: cp /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0 src-tauri/target/debug/deps/

安装 tauri 环境

依赖:

    sudo apt update && sudo apt install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev appmenu-gtk3-module libgtk-3-dev squashfs-tools

npm:

    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
    . ~/.bashrc
    nvm install node --latest-npm
    nvm use node
    npm config set registry https://registry.npm.taobao.org
    npm config get registry

    npm install -g yarn
    yarn config set registry https://registry.npm.taobao.org


cargo install tauri-bundler --force

yarn global add @vue/cli

vue create tauri-app

cd tauri-app

yarn add element-ui tauri

yarn tauri init

yarn serve
yarn build

yarn tauri dev
yarn tauri build --debug

yarn tauri info

nvs

export NVS_HOME="$HOME/.nvs"
git clone https://github.com/jasongin/nvs "$NVS_HOME"
. "$NVS_HOME/nvs.sh" install

nvs remote node https://npm.taobao.org/mirrors/node/
nvs remote

nvs ls
nvs add lts

echo 'nvs use latest' >> ~/.bashrc

问题

__tauri_message__ does not live long enough

https://github.com/tauri-apps/tauri/discussions/4317

解决方法: https://github.com/tauri-apps/tauri/issues/2533

release 默认启用 dev tools 功能

给 tauri crate 添加 devtools features, 就可以了

Makepad

说明

  • Makepad 是 Rust 生态中的 GUI / 创作型应用框架之一.
  • 本页当前主要保留打包与发布方向的入口说明.

打包说明

  • 官方文档: https://book.makepad.rs/zh/guide/appendix/packaging-guide

当前关注点

  • 桌面端分发方式.
  • 资源文件和运行时依赖的打包方式.
  • 不同平台上的发布差异, 例如 Windows, macOS, Linux.

使用建议

  • 如果只是验证界面与交互, 先跑开发模式即可, 不必一开始就做打包.
  • 真正准备发布时, 优先核对官方 Packaging Guide, 再根据目标平台整理脚本.
  • 若后续积累更多内容, 可继续补充安装流程, 示例项目结构和常见报错排查.

wgpu

创建 buffer, 并指定 buffer 的格式, 如下: UNIFORM 格式

#![allow(unused)]
fn main() {
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
    label: Some("egui_uniform_buffer"),
    contents: bytemuck::cast_slice(&[UniformBuffer {
        screen_size: [0.0, 0.0],
    }]),
    usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
});
}

写 buffer 数据

#![allow(unused)]
fn main() {
self.queue.write_buffer(&self.uniform_buffer.buffer, 0, data);
}

绑定组, 是为了把 Buffer 与 Shader 关联.

如下: 定义了 一个 绑定组布局, 用于 表明 Buffer 的用处, 此处用在 Uniform格式

#![allow(unused)]
fn main() {
let uniform_bind_group_layout =
    device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
        label: Some("egui_uniform_bind_group_layout"),
        entries: &[wgpu::BindGroupLayoutEntry {
            binding: 0,
            visibility: wgpu::ShaderStages::VERTEX,
            ty: wgpu::BindingType::Buffer {
                has_dynamic_offset: false,
                min_binding_size: None,
                ty: wgpu::BufferBindingType::Uniform,
            },
            count: None,
        }],
    });

let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
    label: Some("egui_uniform_bind_group"),
    layout: &uniform_bind_group_layout,
    entries: &[wgpu::BindGroupEntry {
        binding: 0,
        resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
            buffer: &uniform_buffer.buffer,
            offset: 0,
            size: None,
        }),
    }],
});
}
[[group(0), binding(0)]] var<uniform> r_locals: Locals;

创建 渲染管道布局 时, 会设置 绑定组布局

#![allow(unused)]
fn main() {
let render_pipeline_layout =
    device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: Some("Render Pipeline Layout"),
        bind_group_layouts: &[&uniform_bind_group_layout, &texture_bind_group_layout],
        push_constant_ranges: &[],
    });
}

在创建渲染管道时, 指定的 vertex 或 fragment 的 buffers, 会 作为输入参数, 传递到 shader 的 入口函数

vertex 的 step_mode 参数用于决定 顶点Buffer 是如何移动的

当设置为: VertexStepMode::Instance 时, ``` render_pass.draw(0..4, 0..self.font_instance_num); ``` 表示:
    每一个 font_instance 增加时, vertex_buffer 向前移动一个单位, 相同的vertex 会遍历4次(``` builtin(vertex_index) ``` 就是 从0开始依次到3)

当设置为: VertexStepMode::Vertex 时, 。。。

fragment 的 blend 参数用于决定 fragment shader如何处理采样Color

当为 wgpu::BlendState::REPLACE 时, 整个输出成单色?
当为 wgpu::BlendState::ALPHA_BLENDING 时, 可以通过 fragment shader 控制输出?
#![allow(unused)]
fn main() {
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
    label: Some("Render Pipeline"),
    layout: Some(&render_pipeline_layout),
    vertex: wgpu::VertexState {
        module: &shader,
        entry_point: "vs_main",
        buffers: &[wgpu::VertexBufferLayout {
            array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
            step_mode: wgpu::VertexStepMode::Vertex,
            attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32],
        }],
    },
    fragment: Some(wgpu::FragmentState {
        module: &shader,
        entry_point: "fs_main",
        targets: &[wgpu::ColorTargetState {
            format: config.format,
            blend: Some(wgpu::BlendState::REPLACE),
            write_mask: wgpu::ColorWrites::ALL,
        }],
    }),
    // ...
});
}

当创建 pipeline 时, 指定 基本形状 primitive 时, wgpu::FrontFace::Ccw 表示如果顶点是逆时针 则三角形朝前, 否则 朝后. CullMode::Back 表示 切除朝后的 三角形

#![allow(unused)]
fn main() {
primitive: wgpu::PrimitiveState {
    topology: wgpu::PrimitiveTopology::TriangleList,
    strip_index_format: None,
    front_face: wgpu::FrontFace::Ccw,
    cull_mode: Some(wgpu::Face::Back),
    ...
}
}
[[stage(vertex)]]
fn vs_main(
    [[location(0)]] a_pos: vec2<f32>,
    [[location(1)]] a_tex_coord: vec2<f32>,
    [[location(2)]] a_color: u32,
) -> VertexOutput {

}

[[stage(vertex)]]
fn vs_conv_main(
    [[location(0)]] a_pos: vec2<f32>,
    [[location(1)]] a_tex_coord: vec2<f32>,
    [[location(2)]] a_color: u32,
) -> VertexOutput {

}

创建的 bind_group 会定义 buffer 的数据信息,

#![allow(unused)]
fn main() {
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
    label: Some("egui_uniform_bind_group"),
    layout: &uniform_bind_group_layout,
    entries: &[wgpu::BindGroupEntry {
        binding: 0,
        resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
            buffer: &uniform_buffer.buffer,
            offset: 0,
            size: None,
        }),
    }],
});
}

在 wgsl 中 引用 bind_group buffer 数据

[[group(0), binding(0)]] var<uniform> r_locals: Locals;

更新数据 到 wgsl的buffer 中,

#![allow(unused)]
fn main() {
self.queue.write_buffer(&self.uniform_buffer.buffer, 0, data);
}

配置 render_pass, 使之 开始渲染

#![allow(unused)]
fn main() {
render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
}

wgsl 坐标系

顶点坐标系

正中心是 (0,0), x 和 y 的范围是 (-1, 1)

(-1,1) (0,1) (1,1) . . (-1,0) (0,0) (1,0) . . (-1,1) (0,1) (1,1)

纹理坐标系

纹理的绘制

[[group(0), binding(0)]] var t_diffuse: texture_2d<f32>; [[group(0), binding(1)]] var s_diffuse: sampler;

图片数据会被加载到 Texture 中, 设置到 Bind Group 中, 在 Shader 中, 会在上面的代码中被截获, 最后会在 [[stage(fragment)]] 中 使用 textureSample(t_diffuse, s_diffuse, in.tex_coor) 根据输入的纹理坐标 对纹理采样, 由于每一个顶点 会处理 fragment, 每一个 vertex坐标 对应一个 texture坐标, 如果 texture坐标 是 铺满 (0.0, 0.0) 到 (1.0, 1.0) 则图像上铺的纹理是完整的.

wsgl 其它细节

@builtin(position) 表示顶点的输出位置, 如果想要看到点 就必须设置, 否则什么也没有

@builtin(vertex_index)执行 render_pass.draw(0..3, 0..1); 时, vertex_index 被设置额 3 次, 依次是 0,1,2

[[builtin(instance_index)]] instance_idx: u32 执行 render_pass.draw(0..3, 0..1) 时 的第二个参数

out.clip_position = vec4<f32>(model.position, 1.0); 在 shader 中, wgpu中 位置 和 颜色 一般用 4D向量 表示

fragment 的 shader, 返回值表示 顶点颜色

[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
    return textureSample(t_diffuse, s_diffuse, in.tex_coords);
}

fontdue

说明

  • 本页记录 fontdue 字体库与 TTF 基础度量知识.
  • 适合在做文本布局, 字形栅格化和字体度量排查时快速回看核心概念.

ttf 基本知识

摘录自: https://www.modb.pro/db/175187

  1. Baseline, Origin和Layout

基线(Baseline)是一条假想的线,用于在呈现文本时“引导”字形。它可水平 可垂直。位于基线上的一个虚拟点被称为笔位置(Pen Position)或原点(Origin),用来定位字形

对于水平布局(Horizontal Layout)来说,字形只是被放置在基线上,通过从左至右或从右至左地增加笔位置来呈现文本。相邻两个笔位置之间水平距离被称为步进宽度(Advance Width),步进宽度由具体的字形来决定,从图中也可以发现不同字母的步进宽度是不同的

对于垂直布局(Vertical Layout)来说,字形以基线为中心,基线两侧字形等宽

  1. Ascent,Descent 和Bounding Box

从基线到字体轮廓最高点的距离叫做Ascent,中文貌似最贴合的翻译叫做“上坡度”,从基线到字体轮廓最低点的距离叫做Descent,也可以叫做“下坡度”,一般来说Ascent是正值,Descent是负值

边界框(Bounding Box)是一个虚拟框,它被用来尽可能紧密地围住字形,边界框有四个参数:xMin,yMin,xMax和yMax。在FreeType API中边界框也被简称为bbox。如下图中 蓝框就是每个字形的边界框

  1. Bearings 和 Advances

3个轴承(Bearings)的概念:

(1) 左侧轴承(Left Side Bearing),它指的是从当前笔位置到字形边界框左侧边缘的水平距离。大多情况下水平布局字形的左侧轴承是正数,垂直布局字形的左侧轴承是负数。左侧轴承也被称为X轴承(bearingX)或被简写为lsb

(2) 顶部轴承(Top Side Bearing)也被称为Y轴承(bearingY),它是指当前笔位置到字形边界框顶部的垂直距离,水平布局字形的顶部轴承通常是正数,垂直布局通常是负数。

(3) 右侧轴承(Right Side Bearing)简称rsb,它仅用于水平布局,用于描述字形边界框右侧和步进宽度之间的距离,大多情况下为正数。

步进宽度(Advance Width)指的是两个相邻笔位置(Pen Position)之间的水平距离,对于水平布局来说,步进宽度总是正数,对于垂直布局而言总是零。步进宽度在FreeType API中也被称为advanceX。除了步进宽度,还有步进高度(Advance Height)的概念,它指的是两个相邻笔位置之间的垂直距离,也常被称为advanceY。对水平布局来说它总为零,对垂直布局来说总为正数。

还有两个简单的度量:字形宽度(Glyph Width)和字形高度(Glyph Height)。

ttf 总的分析

先从水平方向上来看,粉色的线是基线,基线上的两个实心小黑块是笔位置。相邻两个笔位置之间的水平距离就是步进宽度advanceX,从图中可以看出:左侧轴承lsb + 字符宽度Width + 右侧轴承rsb = 步进宽度advanceX。

再从纵向上来看,Ascent + |Descent| = 字符高度Height,对于单个字形而言,顶部轴承和Ascent是一样的。图中的蓝色方框就是字形的边界框,边界框的四个参数xMin,xMax,yMin和yMax也用蓝色标注出来了。

另外,图中箭头的方向也有含义,字体布局中默认向右和向上的方向为正,这和我们常见的坐标轴方向是一样的。所以上图中除去Descent为负值,其他度量都为正值,这也是|Descent|要加绝对值的原因

接下来看看垂直布局下字符的字形度量:

垂直布局的字形以基线为中心,基线两侧字形等宽。和水平布局相同,粉色的线是基线,黑色方块是笔位置。相邻两个笔位置之间的垂直距离是步进高度advanceY。

左侧轴承是当前笔位置到边界框左侧的距离,顶部轴承是到边界框最顶部的距离,而垂直布局中没有右侧轴承的概念。上图中的箭头方向也指示了度量值的正负,除了高度Height和宽度Width为正数,其他的度量都为负数。

Fontdue 字体库

基本用法:

#![allow(unused)]
fn main() {
// Read the font data.
    let font = PATH;
    // Setup the configuration for how the font will be parsed.
    let settings = fontdue::FontSettings {
        scale: SIZE,
        ..fontdue::FontSettings::default()
    };
    // Parse it into the font type.
    let font = fontdue::Font::from_bytes(font, settings).unwrap();
    // Rasterize and get the layout metrics for the character at a size.
    let (metrics, bitmap) = font.rasterize(CHARACTER, SIZE);
}

guillotiere

说明

  • 本页记录 guillotiere 在纹理图集分配场景中的用途与设计思路.
  • 适合作为字体渲染或贴图打包时的库选型速记.

使用 guillotiere 这个库, 专门用来实现纹理地图集, 用于管理 纹理的坐标信息. 每创建一个地图集, 就创建一个配套的纹理. 每一次有新的字符, 就在地图集上分配一个字符的宽高的空间, 就可以从地图集上获取到一个输出的位置和大小, 用这个输出的位置 更新纹理上 该位置的数据. 每当一个地图集上的空间不足时, 需要分配新的地图集和纹理. 更新纹理时 可以缓存 字符 地图集编号 纹理编号等信息, 方便对同一个文字的再查寻.

有一种情况是, 如果一个字符串中 一个字符在 地图集0 上, 另一个字符在 地图集1 上, 由于 不同的纹理, 需要多个 绑定组, 这就不太好了. 是否可以 只使用一个 地图集, 在不够用时, reallocate?

OpenCV

opencv 源码编译

参考: https://www.runoob.com/opencv/opencv-install-cpp.html

CMake Clang Git

https://github.com/opencv/opencv/archive/refs/tags/4.11.0.zip

解压, 进入解压后的目录

cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=out -DBUILD_opencv_java=OFF -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_STATIC_LIBS=OFF -DBUILD_SHARED_LIBS=ON -DBUILD_opencv_world=ON -DCMAKE_CXX_FLAGS=“-DOPENCV_DISABLE_EIGEN_TENSOR_SUPPORT=1” -B build -S .

cmake –build build –parallel 20 –config Release

cmake –build build –target install –config Release

说明:

windows系统中, 需要加上 --config Release, 否则无效, 为Debug

设置环境变量

Mesa Windows 构建记录

说明

  • 本页记录在 Windows 上构建 Mesa 的一次实验流程.
  • 当前内容更偏“软件栈准备 + Meson/Ninja 构建链路”速记.
  • 因为文件位于 Rust 目录下, 这里可把它理解为图形栈与桌面图形相关依赖的周边记录.

Python 与构建环境

通过 uv 安装 Python:

uv python install 3.13
uv add fastapi --default-index https://mirrors.aliyun.com/pypi/simple/

可通过环境变量控制镜像源:

  • UV_DEFAULT_INDEX: Python 包索引.
  • UV_PYTHON_INSTALL_MIRROR: Python 安装镜像, 例如 https://mirror.nju.edu.cn/github-release/indygreg/python-build-standalone/.

额外工具

winflexbison

  • 下载: https://github.com/lexxmark/winflexbison/releases
  • 解压后把 win_bison.exe, win_flex.exe 重命名为 bison.exe, flex.exe.
  • 再把它们加入环境变量.

rc.exe

将下面路径加入 PATH:

C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64

准备构建目录

mkdir mesa_builds
cd mesa_builds
uv venv --python 3.13
.\.venv\Scripts\activate
uv pip install meson ninja packaging mako cmake pyyaml

下载源码

git clone --branch mesa-24.3.4 --depth=1 https://gitlab.freedesktop.org/mesa/mesa.git mesa-24.3
git clone --branch mesa-25.1.7 --depth=1 https://gitlab.freedesktop.org/mesa/mesa.git mesa-25.1.7

Meson 配置与构建

cd mesa-24.3

uv run meson setup builddir --buildtype=release -Dplatforms=windows -Dgallium-drivers="softpipe,llvmpipe" -Dvulkan-drivers= -Dllvm=enabled -Degl=enabled -Dgallium-opencl=disabled -Dshared-glapi=enabled -Dprefix=E:\builds\mesa_builds\install

uv run meson setup builddir
uv run meson setup builddir --backend=vs
uv run meson configure --backend=vs
uv run ninja -C builddir -j22
uv run ninja -C builddir install

当时记录到的配置结果

  • Platform: windows
  • OpenGL: YES
  • LLVM: YES
  • Gallium Drivers: llvmpipe softpipe zink d3d12
  • Vulkan Drivers: NO

使用建议

  • 真正开始构建前, 先把 Python, Meson, Ninja, Bison/Flex, rc.exe 这些前置项一次配好.
  • 如果只是为了桌面图形栈实验, 通常先跑通 llvmpipe / softpipe 会更稳.
  • 切版本时, 建议固定 Mesa tag, LLVM 版本和 Meson 参数, 避免排查成本失控.

Servo 构建记录

说明

  • 本页记录在 Deepin 系统上编译和运行 Servo 的过程与踩坑.
  • 内容适合用于了解 Servo 构建命令, 调试方式和样式属性生成链路.

在 Deepin 系统上 编译 servo

git clone https://github.com/servo/servo
cd servo

根据 servo 的 git 上的编译方法 (参考), 遇到错误: gstream 版本1.16, 但是我系统中 只有 1.14, 无法编译 media, 已经一番折腾搞定了编译, 并成功运行起来了

复制 mach 文件为 mach.py, 去掉开头几行, 在 vscode 中边调试运行 mach.py, 从代码中学习!!~~~

1. 修改文件 python/servo/bootstrap.py, 在388行 "distrib.lower() not in" 的下面, 添加一行:
    'deepin',

2. 安装依赖
    ./mach bootstrap

3. 编译, 由于至少需要 gstream 版本1.16 问题, 无法编译 media库, 所以使用 dummy media
    ./mach build --dev --media-stack dummy --with-layout-2020 --verbose

    实际运行的命令是:
        rustup run --install nightly-2022-04-11 cargo build --manifest-path ./ports/winit/Cargo.toml --features "media-dummy native-bluetooth egl layout-2020" --timings -v

        或

        cd ./ports/winit && cargo build --features "media-dummy layout-2020" --timings -v

4. 运行
    ./mach run tests/html/about-mozilla.html

调试 servo 的配置:

    {
        "type": "lldb",
        "request": "launch",
        "name": "debug servo",
        "program": "${workspaceFolder}/target/debug/servo",
        "args": [
            "tests/html/about-mozilla.html"
        ],
        "cwd": "${workspaceFolder}",
        "sourceLanguages": [
            "rust"
        ],
    },

调试时, 渲染界面出现很慢, 几分钟后才出现? 直接运行立即就有了? 为啥???

生成的 properties.rs 这个文件很重要, 定义了 Dom 的所有 属性列表 和 属性解析 等,

NON_CUSTOM_PROPERTY_ID_COUNT 属性数量

在文件 components/style/properties/build.py 中
    通过 components/style/properties 中 shorthands 和 longhands 目录下 的 .mako.rs 文件
    生成 properties.rs

Candle

说明

  • Candle 是 Hugging Face 推出的 Rust 机器学习推理框架.
  • 本页当前记录 Windows + CUDA 环境的最小安装注意点.

参考

  • 官方安装指南: https://huggingface.github.io/candle/guide/installation.html

Windows 11 安装 CUDA

  1. 执行 nvidia-smi 查看当前驱动支持的 CUDA 版本.

  1. 到 CUDA 归档页面下载匹配版本的安装包.
  1. 完成安装后, 再确认 nvcc 与 CUDA 运行库是否已正确进入环境.

MSVC 编译器路径

在 Windows 上编译部分依赖时, 还需要确保 cl.exe 所在目录已加入 PATH.

示例路径:

D:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\bin\Hostx64\x64

后续可补

  • candle-core / candle-nn 的最小示例.
  • CPU 与 CUDA 特性切换方式.
  • Windows 与 Linux 的差异化安装记录.

bindgen

说明

  • bindgen 用于根据 C 或 C++ 头文件自动生成 Rust FFI 绑定代码.
  • 适合需要和已有原生库交互, 又不想手写大量 extern 声明的场景.
  • 更原始的安装记录已并入当前文档体系, 后续统一以本页为准.

根据 C 头文件生成 Rust 接口

#![allow(unused)]
fn main() {
#[derive(Debug)]
pub struct CargoCallbacks;

impl ParseCallbacks for CargoCallbacks {
    fn process_comment(&self, comment: &str) -> Option<String> {
        println!("comment: {}", comment);
        None
    }
}

let bindings = Builder::default()
    .default_enum_style(EnumVariation::Rust {
        non_exhaustive: true,
    })
    .generate_comments(true)
    .header("src/error.h")
    .prepend_enum_name(false)
    .parse_callbacks(Box::new(CargoCallbacks))
    .layout_tests(false)
    .generate()
    .expect("Unable to generate bindings");

bindings
    .write_to_file("src/bindings.rs")
    .expect("Couldn't write bindings!");
}

常见关注点

  • header(...): 指定输入头文件.
  • default_enum_style(...): 控制枚举映射成 Rust 风格还是常量风格.
  • layout_tests(false): 可避免部分平台差异导致的布局测试问题.
  • parse_callbacks(...): 可在生成时接管注释, 重建依赖追踪等行为.

使用建议

  • 先把 C 侧头文件和依赖链梳理干净, 再生成绑定会更稳.
  • 生成结果通常建议进入专门文件, 例如 bindings.rs, 不要手写和自动生成混在一起.
  • 生成失败时, 常见排查方向是 libclang, 头文件搜索路径和平台宏定义.

windows-rs

说明

  • windows-rs 用于在 Rust 中调用 Windows API, WinRT API 和相关系统类型.
  • 本页聚焦一个常见的字符串传参与返回值转换场景: String -> HSTRING -> PCWSTR.
  • 若你在排查 FFI 或平台绑定问题, 也可以结合 Rust 问题汇总 一起看.

适用场景

  • 在 Rust 桌面程序中调用 Windows Shell, 窗口, 文件系统等系统 API.
  • 需要把 Rust 字符串安全地转换为 Windows 侧常见的 UTF-16 表示.
  • 需要从返回的宽字符串结果再转回 Rust 侧可用的文本类型.

常见依赖

[dependencies]
windows = { version = "0.62", features = [
  "Win32_UI_Shell",
  "Win32_Foundation",
] }
  • 实际 features 需要按目标 API 所在模块启用.
  • 若编译时报模块未找到, 首先检查 features 是否覆盖到对应命名空间.

示例: 设置与读取 AppUserModelID

#![allow(unused)]
fn main() {
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use windows::core::{HSTRING, PCWSTR};
use windows::Win32::UI::Shell;

pub fn set_current_process_explicit_app_user_model_id() {
    let app_id = HSTRING::from("EhExt");

    unsafe {
        match Shell::SetCurrentProcessExplicitAppUserModelID(PCWSTR::from_raw(app_id.as_ptr())) {
            Ok(_) => {
                log::info!("SetCurrentProcessExplicitAppUserModelID ok");
            }
            Err(error) => {
                log::info!("SetCurrentProcessExplicitAppUserModelID error: {error}");
            }
        }
    }
}

pub fn get_current_process_explicit_app_user_model_id() {
    unsafe {
        match Shell::GetCurrentProcessExplicitAppUserModelID() {
            Ok(value) => {
                let value = OsString::from_wide(value.as_wide())
                    .to_string_lossy()
                    .into_owned();
                log::info!("GetCurrentProcessExplicitAppUserModelID: {value}");
            }
            Err(error) => {
                log::info!("GetCurrentProcessExplicitAppUserModelID error: {error}");
            }
        }
    }
}
}

理解要点

HSTRING

  • 适合和 WinRT / Windows 相关 API 交互.
  • 内部是 UTF-16 表示, 常见于现代 Windows 绑定类型.

PCWSTR

  • 表示只读宽字符串指针.
  • 常用于把 UTF-16 数据传给底层 Windows API.

返回值回到 Rust 字符串

  • 对宽字符结果, 常见做法是借助 OsString::from_wide 转换.
  • 转成 String 时, 可使用 to_string_lossy() 作为兼容路径.

生命周期注意点

  • PCWSTR::from_raw(app_id.as_ptr()) 依赖底层数据在调用期间仍然有效.
  • 因此要像示例那样先把 HSTRING 绑定到局部变量, 不要直接把临时值内联成悬空指针来源.
  • 如果 API 需要长期持有这段指针, 还要进一步确认其所有权与释放策略.

排障建议

  • 编译不过时, 先查 featuresuse 路径是否正确.
  • 运行时报错时, 结合返回的 HRESULTwindows crate 错误信息定位.
  • 如果只是字符串互转有问题, 先把问题缩小到 HSTRING, PWSTR, PCWSTR 三类类型之间的区别.

延伸阅读

  • 若问题更偏原生互操作, 可继续看 libc, bindgen, Windows API 等相关页面.
  • 若目标是桌面 GUI 工程, 还要结合实际框架对窗口句柄和消息循环的封装方式一起看.

libc 与 C 运行时互操作

说明

  • 本页记录 Rust 与 C 运行时交互时的一个实际片段: 自定义 printf.
  • 适合处理“C 库输出和宿主程序日志时序不同步”的场景.

自定义 printf

遇到过这样一种情况: Rust 调用 C 函数时, 最终编译出的进程由 C# 宿主启动, printf 的输出相对 Rust 日志存在滞后, 因此用 Rust 导出的 r_printf 替代 C 侧的 printf.

C 侧替换方式:

int r_printf(const char *format, ...);

#define printf r_printf
#define DBGPRINTF r_printf

Rust 侧实现:

#![allow(unused)]
fn main() {
use std::io::Write;
use printf_compat::{format, output};

#[no_mangle]
pub unsafe extern "C" fn r_printf(ss: *const i8, mut args: ...) -> i32 {
    let mut s = String::new();
    let bytes_written = format(ss, args.as_va_list(), output::fmt_write(&mut s));
    print!("{}", s);
    std::io::stdout().flush().unwrap();
    bytes_written
}
}

使用场景

  • C 库和 Rust 日志需要统一输出顺序.
  • 宿主程序对 stdout 的刷新时机较敏感.
  • 需要在 Rust 侧接管 C 风格可变参数打印入口.

注意

  • 这种做法更偏工程层兼容技巧, 要注意 ABI 和可变参数实现方式.
  • 若跨平台使用, 需要进一步验证不同目标平台上的可用性.

rusb

说明

  • rusblibusb 的 Rust 封装, 适合做跨平台 USB 设备枚举, 打开设备和基础收发实验.
  • 本页记录 Windows + vcpkg 环境下跑通官方示例的最小步骤, 以及常见排查点.

适用场景

  • 枚举当前主机上的 USB 设备.
  • 在用户态程序中直接与特定 USB 设备通信.
  • 为后续 HID, 自定义设备协议或板级工具开发做基础验证.

安装 libusb

vcpkg install libusb --triplet=x64-windows
vcpkg install libusb --triplet=x86-windows

环境变量

$env:VCPKGRS_TRIPLET="x64-windows"
$env:VCPKGRS_DYNAMIC=1

下载与运行示例

git clone https://github.com/a1ien/rusb.git
cd rusb
cargo run --example read_device

需要的运行库

将以下文件复制到项目根目录或确保其在运行时可被找到:

  • vcpkg\installed\x64-windows\bin\libusb-1.0.dll
  • vcpkg\installed\x64-windows\lib\libusb-1.0.lib

常见排查点

  • 架构必须一致, 例如程序为 x64, 则 libusb 也应使用 x64-windows.
  • Windows 上若设备无法打开, 先确认该设备是否已绑定兼容 libusb 的驱动.
  • 若只是想先验证环境, 优先运行官方示例做枚举, 不要一开始就接入完整业务逻辑.
  • 如果编译阶段找不到依赖, 优先检查 VCPKGRS_TRIPLETVCPKGRS_DYNAMIC 是否生效.

代码中可显式设置的变量

#![allow(unused)]
fn main() {
std::env::set_var("VCPKGRS_TRIPLET", "x64-windows");
std::env::set_var("VCPKGRS_DYNAMIC", "1");

// std::env::set_var("TARGET", "x86_64-pc-windows-msvc");
// std::env::set_var("HOST", "x64-windows-static");
// std::env::set_var("OPT_LEVEL", "1");
// std::env::set_var("CARGO_CFG_TARGET_FEATURE", "crt-static");
// std::env::set_var("VCPKGRS_TRIPLET", "x64-windows-static-md");
}

建议阅读路径

  • 先跑官方示例确认 libusb + rusb 环境正确.
  • 如果需要回看仓库内的早期 USB 验证目录, 可继续看 test_usb 实验目录说明.
  • 再根据目标设备补充 VID/PID, 权限和驱动层面的适配.
  • 如果问题落在 USB 抓包或链路排查, 可配合 Wireshark 一起看.

Regex

说明

  • 本页记录 Rust regex 库中常用的匹配, 捕获与替换模式.
  • 适合处理模板变量, 占位符和结构化字符串提取.
  • 如果你的目标是轻量文本扫描, regex 往往比手写字符串切分更稳定.

依赖

[dependencies]
regex = "1"

基础示例: 提取 ${...} 占位符

#![allow(unused)]
fn main() {
use regex::Regex;

let re = Regex::new(r"\$\{([_0-9a-zA-Z.]+)\}").unwrap();

for cap in re.captures_iter("aa${var0}bb, aa${var1.a.b}bb") {
    println!("full = {}", &cap[0]);
    println!("name = {}", &cap[1]);

    if let Some(group) = cap.get(1) {
        println!(
            "start = {}, end = {}, value = {}",
            group.start(),
            group.end(),
            group.as_str()
        );
    }
}
}

这个例子做了什么

  • 匹配 ${...} 形式的占位符.
  • 0 组是完整匹配结果, 例如 ${var0}.
  • 1 组是括号内捕获内容, 例如 var0var1.a.b.
  • 通过 cap.get(1) 可以拿到位置范围和原始字符串切片.

常见 API

is_match

#![allow(unused)]
fn main() {
let re = Regex::new(r"^\d+$").unwrap();
assert!(re.is_match("12345"));
}
  • 只关心“是否匹配”时最直接.

find_iter

#![allow(unused)]
fn main() {
let re = Regex::new(r"\d+").unwrap();
for item in re.find_iter("a1 b22 c333") {
    println!("{}", item.as_str());
}
}
  • 适合只提取命中片段, 不需要捕获组时使用.

captures_iter

  • 适合需要按组提取结构化内容时使用.
  • 模板变量, 路径参数和局部语法解析都很常见.

replace_all

#![allow(unused)]
fn main() {
use regex::Regex;

let re = Regex::new(r"\$\{([_0-9a-zA-Z.]+)\}").unwrap();
let output = re.replace_all("hello ${name}", |caps: &regex::Captures| {
    match &caps[1] {
        "name" => "Rust".to_string(),
        other => format!("<missing:{other}>"),
    }
});

assert_eq!(output, "hello Rust");
}
  • 适合做模板替换和批量文本重写.

使用建议

  • 正则表达式应尽量预编译并复用, 不要在高频循环里反复 Regex::new.
  • 模式复杂时, 优先先写测试样例, 再逐步补充分组.
  • 如果只是判断前后缀或简单切分, 可以优先用字符串 API, 不必上正则.

常见注意点

  • Rust regex 不支持回溯引用和环视这类 PCRE 风格高级特性.
  • 想提高可读性时, 可以把复杂模式拆分到多个小正则或先做预过滤.
  • 对用户输入做替换时, 要明确区分“匹配模式”和“替换模板”两侧的转义语义.

适用场景

  • 模板变量替换.
  • 配置文件中的占位符扫描.
  • 日志, 路径或协议字段中的局部提取.

serde_json

说明

  • serde_json 是 Rust 里最常见的 JSON 序列化与反序列化库之一.
  • 本页当前记录“带标签枚举风格”的结构化消息示例.

基本示例

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "req")]
struct DefineSerial {
    serial: String,
    port: String,
    baudrate: String,
    databits: String,
    flowcontrol: String,
    parity: String,
    stopbits: String,
}

let v = json!({
    "req": "DefineSerial",
    "serial": "DeviceName00",
    "port": "COM0",
    "baudrate": "2000000",
    "databits": "",
    "flowcontrol": "",
    "parity": "",
    "stopbits": "",
});
}

这个例子说明了什么

  • #[serde(tag = "req")] 适合把结构体或枚举消息编码成带类型标签的 JSON.
  • json!(...) 适合快速构造测试数据或临时消息体.
  • 这种模式常用于 IPC, WebSocket, 串口协议桥接和前后端消息交互.

使用建议

  • 若消息类型较多, 更推荐用枚举统一表达协议而不是散落多个结构体.
  • 若字段需要兼容老协议, 可继续配合 rename, default, skip_serializing_if 等属性使用.

Rust crate 工具集

说明

  • 本页整理常见 Rust crate 的用途速记, 包括枚举工具, 系统信息, 通道和参数解析等方向.
  • 适合作为选型时的快速索引页, 后续可继续按主题拆细.

rust语言的工具类

strum

可以实现 ToString, 字符串 到 枚举 的转换, 枚举遍历, 枚举类型的成员数量, 枚举成员静态消息, 枚举成员静态属性 等等

derive_more

自动实现 builtin traits, 如 From Into FromStr TryInto IntoIterator AsRef AsMut 等等

rand 随机数

bstr

实现 \r\n 二进制字符串分割

let lines: Vec<&[u8]> = data.lines().collect();

sorted_vec

数组或不重复的集合, 排序

flume

超快的 mpmc channel实现

系统功能

machineid-rs 获取 加密的 独一无二的 MachineID/HWID/UUID

sys-info 专注于获取系统信息, 包括: 主机名, 启动时间, CPU信息, 系统类型, 磁盘使用, 内存使用, 进程数, linux系统 release信息

sysinfo 网络信息(包括网卡, 收发数据量), 内存使用情况, 进程管理, CPU使用情况, 系统信息(系统名,用户名,内核版本,主机名), 等等

portpicker 选择可用网络端口

参数解析

clap

structopt

virtual-serialport

进程内 串口模拟

Rust for Android 总览

说明

  • 本目录收录 Android SDK / NDK 环境搭建, adb 调试, 以及 Rust GUI / 应用框架在 Android 上的实验记录.
  • 内容以“跑通最小链路”的历史实测为主, 版本信息偏旧, 使用时请结合当前 Android Studio 与 NDK 版本核对.

常用入口

使用建议

  • 先完成 Android Studio, SDK, NDK, adb 与设备连接配置.
  • 再按技术路线选择 Tauriegui/android-activity 继续验证.
  • 如果只是验证移动端链路, 建议先跑最小示例, 不要一开始叠加复杂业务.

Rust for Android 基本环境设置

说明

  • 记录 Linux / Deepin 环境下配置 Android SDK, NDK, adb 与 Rust 目标的基本流程.
  • 文中的 Android Studio 与 NDK 版本属于历史实测记录, 使用前请结合当前版本核对.

安装 SDK 与必要工具

  1. 下载 Android Studio: https://developer.android.com/studio
  2. 将 Android Studio 解压到 /opt.
  3. 启动 /opt/android-studio/bin/studio.sh, 通过图形界面安装 SDK 与工具链.

SDK Tools 记录:

  • NDK (Side by side): 23.1.7779620
  • Android SDK Command-line Tools (latest)

界面记录:

设备连接与 adb

手机连接 Linux 主机后, 先参考官方文档确认调试方式:

  • https://developer.android.com/studio/run/device

查看设备 ID:

dmesg -w

示例记录: 18d1:4ee7.

安装平台工具:

apt-get install android-sdk-platform-tools-common

创建 /etc/udev/rules.d/50-android.rules:

SUBSYSTEM=="usb",ATTRS{idVendor}=="18d1",ATTRS{idProduct}=="4ee7",MODE="0666"

重启 udev:

sudo service udev reload
sudo service udev restart

将当前用户加入 plugdev 组:

sudo usermod -a -G plugdev maxu

常用 adb 命令:

adb start-server
adb devices

Rust 目标与 Bevy 验证

参考:

  • https://github.com/bevyengine/bevy/tree/main/examples#android

安装目标与工具:

rustup target add aarch64-linux-android armv7-linux-androideabi
cargo install cargo-apk

环境变量示例:

export ANDROID_SDK_ROOT=~/Android/Sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/23.1.7779620
export PATH=/opt/android-studio/jre/bin:$ANDROID_SDK_ROOT/platform-tools:$PATH

运行示例:

cargo apk run --example android

查看日志:

adb logcat -c
adb logcat | grep rust.example.hello_world
adb logcat | grep RustStdoutStderr

Tauri for Android

说明

  • 记录 Tauri 项目接入 Android 的初始化步骤和 Gradle 代理配置.
  • 当前内容以 Windows 环境为主, 命令更接近早期 Tauri v2 alpha 记录.

前置环境

安装 Android Studio, 并设置环境变量:

ANDROID_HOME=E:\Android\Sdk
NDK_HOME=%ANDROID_HOME%\ndk\23.2.8568313
PATH=%PATH%;C:\Program Files\Android\Android Studio\jre\bin;%ANDROID_HOME%\platform-tools

初始化命令

yarn create tauri-app --alpha
cd <project>
yarn
yarn tauri android init
yarn tauri android dev
  • 如果下载速度较慢, 最好先配置代理.

Android 项目代理

项目内生效

修改 src-tauri\gen\android\test_tauri_v2\gradle.properties, 添加:

systemProp.socks.proxyHost=127.0.0.1
systemProp.socks.proxyPort=1080
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=1080

全局生效

在用户目录下 .gradle\gradle.properties 中添加:

systemProp.socks.proxyHost=127.0.0.1
systemProp.socks.proxyPort=7890
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=7890
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=7890

问题记录

  • Windows 10 设备调试相关设置可参考: https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development

egui for Android

说明

  • 记录基于 android-activity 示例在 Android 上运行 egui 的最小流程.
  • 适合验证 cargo-ndk / cargo-apk 与 Gradle 的打包链路是否已经连通.
  • 如果只是做 Android 基础环境准备, 应优先先看 基本环境设置.

适用场景

  • 快速验证 Rust GUI 在 Android 上能否完成编译和打包.
  • 对照 cargo-ndkcargo-apk 两套构建方式的差异.
  • 在代理环境下排查 Gradle 依赖下载问题.

前置环境

  1. 下载 Android Studio, 并用它安装 SDK / NDK.
  2. 设置环境变量:
ANDROID_SDK_ROOT=D:\programs\Android\Sdk
ANDROID_NDK_ROOT=%ANDROID_SDK_ROOT%
dk.1.7779620
  1. 安装编译工具:
rustup target add aarch64-linux-android
cargo install cargo-ndk

下载示例

git clone https://github.com/rib/android-activity.git
cd examples/agdk-egui

编译方式

使用 cargo-ndk + Gradle:

cargo ndk -t arm64-v8a -o app/src/main/jniLibs/ build
./gradlew build

使用 cargo-apk:

cargo apk build
cargo apk run

代理配置

如果网络较差, 可在 gradle.properties 中追加:

systemProp.socks.proxyHost=127.0.0.1
systemProp.socks.proxyPort=1080
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=1080

重新构建:

./gradlew build

常见关注点

  • 打包失败时, 先检查 Android SDK / NDK 路径和目标架构是否一致.
  • Gradle 拉依赖失败时, 再考虑代理, 仓库源和证书环境问题.
  • 如果只是想验证链路是否通, 优先先跑最小示例, 不要一开始叠加复杂业务代码.

相关文档

rust 源码编译

准备

使用 choco 下载依赖

使用管理员权限:

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(‘https://chocolatey.org/install.ps1’))

choco -v

使用 choco 安装必要软件

choco install -y cmake llvm python3 git

添加 cmake 到 系统变量 PATH: C:\Program Files\CMake\bin

ninja

python -m pip install ninja

添加 ninja 路径 到环境变量: C:\Python312\Lib\site-packages\ninja\data\bin

rust 源码

git clone https://github.com/rust-lang/rust.git

cd rust

./x setup

选择 "e dist", 并选择 忽略 "Ci", 将生成 config.toml 文件

追加以下信息 到 config.toml

[llvm]
# don't download and build llvm
download-ci-llvm = false

[build]
compiler-docs = false

cargo-native-static = true

[rust]

debug = true
debuginfo-level = 2

debug-logging = true

incremental = true

为了加快编译, 添加配置到 Cargo.toml

[profile.release.package.rustc_mir_build]
opt-level = 0

[profile.release.package.rustc_driver]
opt-level = 0

./x build

使用 stage0 compiler 编译 std

使用 stage0 compiler 编译 rustc, 生成 stage1 compiler

使用 stage1 compiler 编译 std

rustup toolchain link stage1 build/host/stage1, 创建工具链

rustup override set stage1, 指定 项目 使用的 toolchain, 可以直接: cargo build

rustup default stage1, 指定 全局的 toolchain

cargo +stage1 build, 临时使用 stage1

./x build –keep-stage 1, 再次编译时, 不添加 –keep-stage 时, 默认会清除 stage1 和 stage2, 但修改了 std库 应该不能有这个选项.

./x build proc-macro-srv-cli

./x build –stage 2 proc-macro-srv-cli

rust-analyzer and IntelliJ Rust plugin 使用一个叫 rust-analyzer-proc-macro-srv 的组件 处理 过程宏.

./x build –stage 2 compiler/rustc

使用 stage1 compiler 编译 rustc, 生成 stage2 compiler

使用 stage2 compiler 编译 std

./x doc

./x doc –stage 1

编译文档

项目配置 (使用 vscode)

下面 第 2 3 步 是为了配置 rust-analyzer, 不配置的话, vscode中 代码提示 宏展开 会出错.

  1. rustup override set stage2, 指定项目默认的 toolchain (非全局), 会设置在 ${ENV:USER}.rustup\settings.toml

  2. 设置 rust-analyzer

修改配置 .vscode\settings.json, 添加两行 (确保路径存在):

"rust-analyzer.procMacro.server": "D:/develop/rust/rust/build/x86_64-pc-windows-msvc/stage2-tools-bin/rust-analyzer-proc-macro-srv.exe",
"rust-analyzer.procMacro.enable": true
  1. 添加下列路径 到 PATH 环境变量 (rust-analyzer-proc-macro-srv.exe 依赖的动态库路径):

D:\work\rust_projects\rust\build\x86_64-pc-windows-msvc\stage1-std\x86_64-pc-windows-msvc\release\deps

分析的过程如下:

rust-analyzer-proc-macro-srv.exe 打开闪退.

使用 visual studio 的命令行工具, 如: 在 "开始" 中 搜索 "Powershell", 出现的 " Developer Powershell for vs 2022"

    dumpbin /dependents D:/develop/rust/rust/build/x86_64-pc-windows-msvc/stage2-tools-bin/rust-analyzer-proc-macro-srv.exe

在输出的内容中, 有如下:

```
Image has the following dependencies:

    kernel32.dll
    rustc_driver-425c7e0f724b33ed.dll
    std-b1b46c42ec629488.dll
    KERNEL32.dll
```

经过搜索 发现, 这两个路径位于: D:\work\rust_projects\rust\build\x86_64-pc-windows-msvc\stage1-std\x86_64-pc-windows-msvc\release\deps

在 rustc 项目中 配置 vscode rust-analyzer

./x setup vscode

快速检测错误

./x check src/tools/rustdoc

给出建议

./x suggest

快速的编译

./x build library

./x build library –keep-stage 1

调试技巧

调试 类型布局

#![feature(rustc_attrs)]

#[rustc_layout(debug)]
type T<'a> = &'a u32;

Rust for WebAssembly

说明

  • 本页整理 Rust 编译到 WebAssembly 的最小流程与工具选型.
  • 适合刚开始做浏览器端, 前端集成或轻量跨平台模块时快速建立正确工程骨架.
  • 若你已经拿到了 .wasm 文件并只想做 JS 胶水层处理, 可继续看 wasm-bindgen CLI.

适用场景

  • 把 Rust 逻辑编译成浏览器可加载的 WebAssembly 模块.
  • 在前端工程中复用 Rust 的解析, 算法或性能敏感逻辑.
  • 使用 Trunkwasm-pack 快速搭建 Rust + Web 的开发流程.

最小工程准备

1. 创建库项目

cargo new --lib test-wasm
cd test-wasm
  • WebAssembly 模块通常以库项目形式输出, 便于给 JS 或宿主环境调用.

2. 在 Cargo.toml 中声明库类型

[lib]
crate-type = ["cdylib"]
  • cdylib 适合输出给外部宿主使用的动态库产物, 也是生成 .wasm 的常见配置.
  • 这一项应放在 Cargo.toml, 不是 .cargo/config.toml.

3. 安装目标平台

rustup target add wasm32-unknown-unknown
  • wasm32-unknown-unknown 是最常见的通用 WebAssembly 目标.

常见构建方式

方式 1: 使用 wasm-pack

cargo install wasm-pack
wasm-pack build --target web
  • 适合快速生成浏览器可用的包结构.
  • --target web 适合直接在浏览器中通过 ES Module 加载.

方式 2: 手动 cargo build + wasm-bindgen

cargo build --target wasm32-unknown-unknown --release
wasm-bindgen --target web --out-dir ./pkg ./target/wasm32-unknown-unknown/release/test_wasm.wasm
  • 适合需要精细控制输出目录, 模块目标和生成结果时使用.
  • 这条链路和 wasm-bindgen CLI 是直接配套关系.

方式 3: 使用 Trunk

cargo install trunk
trunk serve
  • 适合项目中已经有 index.html 的前端应用型工程.
  • Trunk 会负责静态资源, .wasm 构建和本地开发服务器, 更适合做页面应用.

推荐目录与配置理解

Cargo.toml

  • 放和包本身相关的配置, 例如 [package], [dependencies], [lib].
  • crate-type = ["cdylib"] 这类配置应写在这里.

.cargo/config.toml

  • 放 Cargo 行为配置, 例如默认 target, linker, alias.
  • 如果你希望默认构建到 WebAssembly, 可以这样写:
[build]
target = "wasm32-unknown-unknown"
  • 这一项不应写到 Cargo.toml[build] 中.

工具怎么选

wasm-pack

  • 希望快速出一个前端可消费的包.
  • 希望少记命令, 先把最小闭环跑通.

wasm-bindgen CLI

  • 已经有自己的构建流程.
  • 需要明确控制 web, bundler, nodejs 等输出目标.

Trunk

  • 工程本身就是 Rust 驱动的 Web 应用.
  • 项目根目录已有 index.html, 想要更接近前端 dev server 体验.

常见注意点

  • 不是所有 Rust crate 都能直接编译到 WebAssembly, 尤其是依赖线程, 文件系统或原生系统 API 的库.
  • 如果项目要被浏览器调用, 通常还需要引入 wasm-bindgen 导出函数接口.
  • 调试构建问题时, 先确认目标平台, crate-type, 工具链命令和输出路径是否一致.

相关文档

wasm-bindgen CLI

说明

  • wasm-bindgen 常用于把 Rust 生成的 .wasm 模块包装成更方便给 Web 侧调用的 JavaScript 接口.
  • 若项目已使用 trunkwasm-pack, 也可以根据工具链选择更适合的工作流.
  • 本页更偏 wasm-bindgen CLI 的最小使用与目标平台区分.

安装

cargo install -f wasm-bindgen-cli

基本用法

wasm-bindgen [options] ./target/wasm32-unknown-unknown/release/crate.wasm

常见输出方式

wasm-bindgen --target web --out-dir ./pkg ./target/wasm32-unknown-unknown/release/app.wasm
wasm-bindgen --target bundler --out-dir ./pkg ./target/wasm32-unknown-unknown/release/app.wasm
wasm-bindgen --target nodejs --out-dir ./pkg ./target/wasm32-unknown-unknown/release/app.wasm
  • web: 直接面向浏览器原生模块加载场景.
  • bundler: 适合配合 Vite, Webpack 等前端构建工具.
  • nodejs: 适合命令行或服务端脚本环境.

典型流程

  1. 先用 cargo build --target wasm32-unknown-unknown --release 生成原始 .wasm.
  2. 再用 wasm-bindgen 生成 JS 胶水代码和 TypeScript 声明.
  3. 最后由前端项目或运行时环境加载输出目录中的文件.

适合场景

  • 已经有现成 Rust crate, 想自己控制输出目录和打包方式.
  • 需要明确区分浏览器, bundler 和 Node.js 三种目标环境.
  • 想把生成的产物接入已有前端工程, 而不是重新搭完整模板.

使用建议

  • 若项目只是常规 WebAssembly 前端应用, 优先评估 trunkwasm-pack, 通常会更省心.
  • 若需要精细控制输出目录, 模块目标和集成方式, wasm-bindgen CLI 会更灵活.
  • 浏览器侧问题排查时, 先确认目标平台, 生成产物路径和静态资源服务方式是否一致.
  • 若后续继续整理, 可补 wasm-pack, trunk, worker 场景和 TS 类型生成实践.

Rust 命令速记

说明

  • 本页用于积累零散但高频的 Rust 命令.
  • 如果命令已经形成完整工具链知识, 可继续回到 Rust 工具.
  • 适合在切换工具链, 新目标平台或排查构建问题时做快速速查.

工具链

安装 Windows 下的 nightly GNU 工具链:

rustup toolchain install nightly-gnu

查看当前工具链:

rustup show
rustup toolchain list

添加目标平台:

rustup target add wasm32-unknown-unknown
rustup target add aarch64-linux-android

Cargo 常用命令

cargo check
cargo build
cargo build --release
cargo run
cargo test
cargo clean

查看依赖树:

cargo tree
cargo tree -e features

展开宏:

cargo expand

常见使用场景

  • cargo check: 适合快速做语义检查, 比完整构建更快.
  • cargo build --release: 适合确认发布构建是否能通过.
  • cargo tree: 很适合排查重复依赖和 feature 组合问题.
  • cargo expand: 适合排查宏展开后的真实代码结构.

建议阅读路径

  • 命令太碎时, 先把它们按“工具链”, “构建”, “调试”, “打包”分类.
  • 一旦某类命令形成稳定工作流, 优先回收至 Rust 工具 或相关专题页.
  • 目标平台相关命令, 可继续结合 Rust for Android 总览Rust WASM 一起看.

Rust 调试技巧

说明

  • 本页整理 Rust 项目在本地调试时的高频做法.
  • 当前重点是 VSCode + CodeLLDB, 回溯信息和多 crate 工程排错.

VSCode + CodeLLDB

常见做法是使用 CodeLLDB 插件来调试 Rust 程序.

launch.json 示例:

{
    "type": "lldb",
    "request": "launch",
    "name": "Cargo launch",
    "cargo": {
        "args": [
            "-Z",
            "unstable-options",
            "-C",
            "${workspaceFolder}/ProjectName",
            "build"
        ]
    },
    "args": ["ProjectArg1", "ProjectArg2"]
}

配置理解

  • type = lldb: 表示使用 CodeLLDB 调试器.
  • cargo.args: 控制编译参数和项目路径.
  • args: 用于传递程序运行参数.
  • 多 crate 项目里, 要特别确认目标 crate 和二进制名称是否正确.

常见调试入口

运行时崩溃

可优先打开回溯信息:

RUST_BACKTRACE=1 cargo run

若需要更完整信息:

RUST_BACKTRACE=full cargo run

单元测试排错

cargo test -- --nocapture

适用场景:

  • 需要看到测试里的输出日志.
  • 需要先缩小失败范围, 再进入断点调试.

使用建议

  • 先用日志和回溯缩小范围, 再进入图形调试器, 效率通常更高.
  • 如果项目依赖 nightly 或特殊构建参数, 建议把调试配置固定在 .vscode/launch.json 中.
  • 遇到跨线程或异步问题时, 优先构造最小复现样例.
  • 若后续继续整理, 可补 gdb/lldb 命令, tokio 调试和 wasm 侧排错笔记.

Rust 问题汇总

说明

  • 本页用于收集 Rust 开发中的高频问题与排查线索.
  • 当前仓库中的问题与技巧仍主要分散在专题页中, 本页先作为统一入口保留.

常见方向

建议排查顺序

  1. 先判断问题属于环境, 编译, 运行时还是架构设计层面.
  2. 再回到对应专题页, 不要把所有问题都堆在一个总表里排查.
  3. 若问题涉及跨语言, 多平台或工具链组合, 优先缩小最小复现范围.
  4. 若问题和版本强相关, 记得记录 Rust 版本, crate 版本和目标平台.

相关文档

Rust for Windows 7

说明

  • 本页记录 Rust 项目在 Windows 7 环境下可用的工具链版本与兼容处理方式.
  • 重点包括固定 rustup 版本, 使用 thunk 适配旧系统 API, 以及用 YY-Thunks 分析可执行文件兼容性.

windows7 中支持的 target

rustup install 1.77.2

指定项目的默认目标 rustup override set 1.77.2-x86_64-pc-windows-msvc

或者, 创建文件 rust-toolchain.toml, 写入:

[toolchain]
channel = "1.77.2-x86_64-pc-windows-msvc"

使用 thunk 的方式

https://github.com/felixmaker/thunk

cargo install thunk-cli

安装依赖的环境

(1) 解压 https://github.com/Chuyu-Team/VC-LTL5/releases/download/v5.2.2/VC-LTL-Binary.7z

设置环境变量 VC_LTL 到解压后的目录

(2) 解压 https://github.com/Chuyu-Team/YY-Thunks/releases/download/v1.1.8/YY-Thunks-Objs.zip

设置环境变量 YY_THUNKS 到解压后的目录, 目录下有 objs 目录

编译

thunk –help, 可以看到 可以支持 xp / win7

thunk –os win7 –arch x64 – –release

windows 可执行程序, windows api 分析

https://github.com/Chuyu-Team/YY-Thunks/releases

YY.Depends.Analyzer.exe 可以分析 编译出的可执行程序 有哪些 windows api, 及 支持的 windows api 的系统型号

使用: .\YY.Depends.Analyzer.exe “D:\Temp\virtualbox_share\learn_image_viewer_win7_64\learn_image_viewer.exe”

根据输出的提示, 输出的分析文件为: learn_image_viewer.exe.md

可以看到 windows api 下的接口, 是否支持目标平台. 如果不满足的话, 且 “YY-Thunks Ready” 为 “No”, 那么 应该无法运行

Rust 游戏开发

说明

  • 本目录收录 Rust 在游戏, 窗口系统, 输入与渲染方向的学习记录.
  • 当前重点围绕窗口创建, 引擎入口和图形基础.

主题入口

阅读路径

  • 若偏游戏引擎体验, 可先从 Bevy 开始.
  • 若偏窗口, 事件循环和跨平台输入, 可先从 winit 开始.
  • 若后续要进入底层渲染, 再继续看 wgpu.
  • 若目标是浏览器平台, 还可以继续看 Rust for WebAssembly.

适用场景

  • 学习 Rust 游戏引擎生态的基本组成.
  • 了解窗口系统, 输入处理和图形渲染之间的分层关系.
  • 为后续图形应用或小游戏实验选择合适的技术起点.

相关文档

bevy 一个游戏引擎

code

git clone https://github.com/bevyengine/bevy
cargo run --example breakout

state

主要的数据结构:

#![allow(unused)]
fn main() {
// 用于操作游戏state, 比如 设置/替换/退出/进入 state
enum ScheduledOperation<T: Component + Clone + Eq> {
    Set(T),
    Replace(T),
    Pop,
    Push(T),
}

// 状态切换 如果是 PreStartup 则会进入 Startup
// 后面几个是 (leaving, entering), 表示从 leaving状态 切换到 entering状态
enum StateTransition<T: Component + Clone + Eq> {
    PreStartup,
    Startup,
    // The parameter order is always (leaving, entering)
    ExitingToResume(T, T),
    ExitingFull(T, T),
    Entering(T, T),
    Resuming(T, T),
    Pausing(T, T),
}
}

初始化状态, 并添加特定状态下要执行的系统集:

#![allow(unused)]
fn main() {
// 添加一个 state, 即此处的GameState, 设置初始状态为 Playing
// 并添加一个systemset, 设置 run_criteria 为 state_cleaner
App::build()
    .add_state(GameState::Playing)
    .add_system_set(SystemSet::on_enter(GameState::Playing).with_system(setup.system()))

// 这个函数主要用于处理 state.scheduled 和 state.transition
fn state_cleaner<T: Component + Clone + Eq>(
    mut state: ResMut<State<T>>,
    mut prep_exit: Local<bool>,
)
{
    match state.scheduled.take() {

        Some(ScheduledOperation::Set(next)) => {
            state.transition = Some(StateTransition::ExitingFull(
                state.stack.last().unwrap().clone(),
                next,
            ));
        }
        Some(ScheduledOperation::Replace(next)) => { ... }
        Some(ScheduledOperation::Push(next)) => { ... }
        Some(ScheduledOperation::Pop) => { ... }
        None => match state.transition.take() {
            Some(StateTransition::ExitingFull(p, n)) => { ... }
            Some(StateTransition::Pausing(p, n)) => { ... }
            Some(StateTransition::ExitingToResume(p, n)) => { ... }
            Some(StateTransition::PreStartup) => { ... }
        }
}

// 当执行 state.set(GameState::GameOver), 会设置 scheduled 的值, 就会改变 state.transition 的值, 后面特定的system_set就启动
pub fn set(&mut self, state: T) -> Result<(), StateError> {
    if self.stack.last().unwrap() == &state {
        return Err(StateError::AlreadyInState);
    }

    if self.scheduled.is_some() {
        return Err(StateError::StateAlreadyQueued);
    }

    self.scheduled = Some(ScheduledOperation::Set(state));
    Ok(())
}

}

这会添加两个systemset, 其中的system会按照添加的顺序添加到, 在stage中运行时, 这些systems会自上而下运行

.add_state(GameState::Playing) 这会初始化状态并放入全局组件中, 会添加一个 systemset, 并带有 state_cleaner 的 run_criteria, 用于处理 判断状态是否满足预期, 是否应该运行.

当执行 SystemSet::on_enter, SystemSet::on_exit, SystemSet::on_update时, 会添加不同的run_criteria, 用于判断特定的状态发生时是否应该执行. 比如:

#![allow(unused)]
fn main() {
pub fn on_update(s: T) -> RunCriteriaDescriptor {
    (|state: Res<State<T>>, pred: Local<Option<T>>| {
        // 当判断当前的state是预期的状态时, 同时事务还未开始时, return true, 表示执行
        state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none()
    })
    .system()
    .config(|(_, pred)| *pred = Some(Some(s.clone()))) // 这会把传入的状态即期望的状态保存到这个 system 的第二个参数, 即pred
    .chain(should_run_adapter::<T>.system())
    .after(DriverLabel::of::<T>())
    .label_discard_if_duplicate(StateCallback::Update.into_label(s))
}
}
#![allow(unused)]
fn main() {
// 初始化状态
Self {
    stack: vec![initial],
    transition: Some(StateTransition::PreStartup),
    scheduled: None,
    end_next_loop: false,
}

}

当 systemset 第一次运行时, stage.scheduled 为 None, 则设置 state.transition = Some(StateTransition::Startup), 表示执行Startup事务. 并 设置 prep_exit 为 true, 表示第一次运行完成.

先有 scheduled 再根据 scheduled 判断 transaction

当执行 state.set(GameState::GameOver) 时, 会验证: 1.当前的 stage.stack 的最后一项不能是要设置的状态 2.stage.scheduled 需要是空的, 然后更新状态: self.scheduled = Some(ScheduledOperation::Set(state))

2022.01.15

添加 WindowPlugin 插件
    会 添加大量的窗口初始化事件:
        WindowResized
        CreateWindow
        WindowCreated
        WindowCloseRequested
        CloseWindow
        CursorMoved
        ...

添加 WinitPlugin 插件
    初始化 winit 环境, 创建窗口, 并设置 runner(处理事件的loop)

winit

EventLoop::new() 创建一个 事件Loop 它会先创建 platform_impl::EventLoop::new(), 依赖于 每一个特定平台的 事件循环

platform_impl::EventLoop::new()这个函数中又会 创建EventLoopWindowTarge, 它包含 WindowTarget, 这是个特定平台的 窗口目标

根据编译环境, 选择不同的 WindowTarget, 这取决于 当前的 编译环境, 如果是 wasm32-unknown-unknown , 则是 web, 如果是 linux, 则是 x11 或 wayland

每一个 WindowTarget 中是由特定平台实现的

    src/platform_impl/web 这表示一个特定的 platform, 这里表示了 window, monitor 和 event_loop 的逻辑, 就是 管理窗口, 管理显示器, 管理事件.

    src/platform_impl/web/web_sys 这表示 这个platform下的特定 backend, 表示 实现 platform 的底层

    在 web 端的 window, 实际上只是 canvas, 事件管理就是 管理canvas 的所有事件, 所以 浏览器的 window 的 resize事件, 是无法通过winit捕获的

OpenRA 构建记录

说明

  • 本页记录在 Deepin 系统上编译 OpenRA 的依赖准备与构建流程.
  • 虽然项目本体偏 C# / .NET, 但当前归档在 Rust 目录中作为游戏方向的历史构建记录保留.

参考: https://github.com/OpenRA/OpenRA/blob/bleed/INSTALL.md

配置 C# 环境

参考: https://docs.microsoft.com/zh-cn/dotnet/core/install/linux-scripted-manual#scripted-install

cd ~/Downloads
wget https://dot.net/v1/dotnet-install.sh

chmod +x ./dotnet-install.sh
./dotnet-install.sh -c 5.0

默认安装到了 ~/.dotnet 路径下, 这个路径需要配置到环境变量下

echo "export PATH=~/.dotnet:$PATH" >> ~/.bashrc
. ~/.bashrc

安装 linux 依赖包

sudo apt install libfreetype6 libopenal1 liblua5.1-0 libsdl2-2.0-0

获取代码 并编译

git clone https://github.com/OpenRA/OpenRA.git

cd OpenRA
make -j10

./launch-game.sh

Rust 代码示例归档

说明

  • 本目录保存 Rust 相关的实验工程, 示例代码和技术验证项目.
  • 这里以可运行样例归档为主, 不等同于稳定的知识文档.
  • 若只是查概念或用法, 优先返回 program/rust/ 下的主文档.

当前归档目录

解析与模板

系统与工具

图形与渲染

网络与服务

Web 与桌面混合实验

第一批低优先级迁出候选

适合如何使用

  • 需要回看某类技术验证的最小工程时, 可按目录名检索.
  • 需要把旧实验沉淀为正式知识页时, 应把结论回收至 program/rust/ 主文档.
  • 需要运行某个样例时, 先检查各自的 Cargo.toml, package.json 和本地工具链版本.

归档边界

  • 本目录保留的是样例工程上下文, 不是面向检索的知识总结.
  • 某个样例一旦有稳定复用价值, 应升级为正式文档, 而不是无限扩写样例目录 README.
  • 长期无维护价值的实验目录, 后续可继续评估是否迁出主知识树.

Rust 代码集锦

说明

  • 本页用于收集可复用的 Rust 代码片段.
  • 当前先保留一个“轻量状态码类型 + 宏生成常量”的示例, 用来展示新类型封装与批量常量定义的写法.
  • 如果后续片段继续增多, 建议再按“错误处理”, “并发”, “宏技巧”, “序列化”拆分小节.

示例: 状态码新类型封装

#![allow(unused)]
fn main() {
use std::fmt;
use std::num::NonZeroU8;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StatusCode(NonZeroU8);

impl StatusCode {
    pub const fn as_u8(self) -> u8 {
        self.0.get()
    }

    pub fn canonical_reason(self) -> Option<&'static str> {
        canonical_reason(self.as_u8())
    }
}

macro_rules! status_codes {
    (
        $(
            $(#[$docs:meta])*
            ($num:expr, $konst:ident, $phrase:expr);
        )+
    ) => {
        impl StatusCode {
            $(
                $(#[$docs])*
                pub const $konst: StatusCode = StatusCode(
                    unsafe { NonZeroU8::new_unchecked($num) }
                );
            )+
        }

        fn canonical_reason(num: u8) -> Option<&'static str> {
            match num {
                $(
                    $num => Some($phrase),
                )+
                _ => None,
            }
        }
    };
}

status_codes! {
    (1, SUCCESS, "Success");
    (2, FAIL, "Fail");
    (3, PROCESSING, "Processing");
    (4, READY, "Ready");
}

impl Default for StatusCode {
    fn default() -> StatusCode {
        StatusCode::READY
    }
}

impl From<StatusCode> for u8 {
    fn from(status: StatusCode) -> u8 {
        status.as_u8()
    }
}

impl fmt::Debug for StatusCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "StatusCode({})", self.as_u8())
    }
}

impl fmt::Display for StatusCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{} {}",
            self.as_u8(),
            self.canonical_reason().unwrap_or("<unknown status code>")
        )
    }
}
}

这个例子值得参考的点

1. 用新类型包装原始值

  • 业务语义比裸 u8 更清晰.
  • 对外暴露的接口也更容易限制非法输入范围.

2. 用宏集中声明常量

  • 避免多处重复写常量和说明文本.
  • canonical_reason 和常量定义保持同源.

3. 统一常见 trait

  • Default, Display, Debug, From 都是很常见的外围接口.
  • 片段一旦准备复用, 这类基础 trait 通常值得一起补齐.

关键注意点

  • NonZeroU8 不能保存 0, 因此常量值绝不能写成 0.
  • 如果你的业务语义里 0 是合法状态码, 应改用普通 u8 或换一种编码方式.
  • unsafe { NonZeroU8::new_unchecked(...) } 必须保证传入值恒不为 0, 否则属于未定义行为.

适用场景

  • 小型协议状态码封装.
  • 内部错误码或流程状态枚举的轻量实现.
  • 需要兼顾数值表示和可读文本说明的场景.

后续扩展方向

  • 增加 TryFrom<u8> 支持外部输入校验.
  • 增加 serde 支持, 便于序列化和接口传输.
  • 当状态集合稳定且语义强时, 也可以直接评估是否改为 enum.

Python 总览

说明

  • 本目录收录 Python 基础记录, 环境管理, 进程与网络, GUI 以及若干工具实践.
  • 若问题先落在环境层, 通常从 Condapyenv 入手更高效.

基础与环境

并发与任务

系统与网络

GUI

建议阅读路径

  1. 环境问题优先从 Condapyenv 入手.
  2. 语法和日常脚本问题优先查 Python 技巧, Python 工具, Python 正则速记.
  3. 并发调度看 asyncioDramatiq, 远程执行和自动化可看 Paramiko.
  4. 桌面界面需求优先看 PySide2.

目录说明

  • 当前目录同时混有一些历史脚本, 示例工程和实验文件.
  • 后续可继续把“正式知识文档”和“零散脚本样例”逐步拆开管理.

python3 笔记

说明

  • 本页整理 Python 3 开发中最常用的环境配置, 调试入口和基础速查.
  • 历史版本安装命令会保留为参考, 但更推荐结合当前环境使用 pyenv 或系统包管理器做统一管理.
  • 若问题更偏具体库和脚本技巧, 可继续看 Python 技巧, 创建进程, socket 笔记.

常见库线索

  • readchar: 读取键盘输入的轻量库.
  • fabric, paramiko: 远程 SSH 执行与自动化运维常用库.
  • 若要做环境隔离与包管理, 优先考虑 venvpyenv 配合使用.

环境与版本管理

使用 pyenv

sudo apt-get install --no-install-recommends make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
curl https://pyenv.run | bash
pyenv install -v 3.11.0
pyenv global 3.11.0
pyenv versions
python --version
  • 适合在一台机器上维护多版本 Python.
  • 与项目级隔离环境配合时更方便.

历史系统安装记录

sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.7
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2
sudo update-alternatives --config python3
  • 这类记录更适合旧系统或历史环境回溯.
  • 现代环境里, 不建议频繁修改系统默认 python3 指向.

源码安装记录

wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz
tar -xvf Python-3.6.2.tar.xz
cd Python-3.6.2
./configure
make
sudo make install
  • 更适合做特殊版本验证或独立安装测试.
  • 若只是日常开发, 维护成本通常高于 pyenv.

调试与开发

VSCode 调试 Python 多进程

multiprocessing.set_start_method('spawn', True)

launch.json 中补充:

{
  "subProcess": true
}
  • 对多进程程序调试很实用.
  • Windows 和部分跨平台场景下, spawn 更容易获得一致行为.

查看对象与类型

len(obj)
type(obj)
isinstance(obj, class_name)
  • 这些是最基础的调试与排查入口.
  • 遇到数据结构不确定时, 先确认类型往往最省时间.

包与环境排查

安装 pip

curl https://bootstrap.pypa.io/get-pip.py | python3 - --user
  • 更适合用户目录安装场景.
  • 若系统已经由包管理器提供 pip, 不一定需要重复覆盖.

搜索包是否已安装

方法 1:

try:
    from pip._internal.utils.misc import get_installed_distributions
except ImportError:
    from pip import get_installed_distributions

方法 2:

import pkg_resources

dists = [d for d in pkg_resources.working_set]
  • 现代脚本里更推荐直接用 importlib.metadatapip list 做外部检查.
  • 历史代码里仍可能遇到上面两种写法.

平台信息速查

import os
import sys
import platform

os.name
sys.platform
platform.system()
platform.platform()
platform.version()
platform.architecture()
platform.machine()
platform.node()
platform.processor()
platform.uname()
  • 适合排查系统差异, 架构差异和平台相关 bug.
  • 写跨平台脚本时, 最常用的是 sys.platformplatform.system().

常见问题记录

SSL 证书导致 pip 拉取失败

报错示例:

Could not fetch URL https://... There was a problem confirming the ssl certificate

处理思路:

sudo apt-get install apt-transport-https ca-certificates software-properties-common
python3 get-pip.py --user --trusted-host pypi.tuna.tsinghua.edu.cn
  • 先检查系统时间和 CA 证书是否正常.
  • 若处于代理或镜像环境, 还要排查代理和镜像配置.

pip 升级后入口异常

ImportError: cannot import name 'main'

常见处理:

curl https://bootstrap.pypa.io/get-pip.py | python3 - --user
  • 这类问题通常和旧版 pip 启动脚本不兼容有关.

setuptools 版本过低

pkg_resources.VersionConflict: (setuptools 20.7.0, Requirement.parse('setuptools>=40.0'))

处理方式:

pip3 install -U --user setuptools

matplotlib 无法弹窗显示

UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown
  • 先检查当前后端是否支持图形界面.
  • 远程环境, WSL 或无桌面环境下, 往往需要改用保存图片或切换后端.

阅读路径

Python 工具

说明

  • 本页汇总 Python 开发中更偏“工具链”和“辅助库”的记录.
  • 环境管理优先看 Condapyenv 使用说明.

当前主题

  • psutil: 用于查看进程, CPU, 内存, 网络等系统信息.
  • Paramiko: Python 下常用 SSH 客户端库.
  • Dramatiq: 偏任务队列与后台任务处理.
  • PySide2 笔记: 偏 GUI 工具链.

使用建议

  • 先区分当前问题是“环境管理”, “系统交互”, “网络访问” 还是 “GUI”.
  • 同一类工具尽量沉淀到专题页, 本页主要保留导航作用.

Python 技巧

说明

  • 本页整理日常 Python 开发里高频但容易散落的小技巧.
  • 当前先从“临时按模块运行源码”展开, 并补充几个常用场景.
  • 适合做快速速查页, 不替代完整环境管理文档.

不安装包, 直接按模块运行

对于开源模块, 若当前只想临时运行源码而不安装, 可直接指定 PYTHONPATH:

PYTHONPATH="$PYTHONPATH:$(pwd)" python3 -m log_server "$@"

适用场景

  • 临时调试源码仓库.
  • 本地快速验证某个模块入口.
  • 不希望污染当前虚拟环境时的短期实验.

其它高频技巧

查看模块导入路径

import module_name
print(module_name.__file__)
  • 适合排查到底加载的是哪个版本的模块.

快速展开字典参数

params = {"name": "demo", "age": 18}
func(**params)
  • 很适合做配置传参和函数适配.

pathlib 代替字符串拼路径

from pathlib import Path
config = Path('config') / 'app.toml'
  • 在跨平台脚本里更稳定, 可读性也更好.

命令行临时运行一段代码

python -c "print('hello')"
  • 适合做快速验证和一次性脚本.

使用建议

  • 适合临时调试, 本地实验和快速验证源码仓库.
  • 若后续需要长期使用, 仍建议补齐正式安装或虚拟环境配置.
  • 若项目依赖较多, 最终还是建议配合虚拟环境或 Conda / pyenv 使用.

相关文档

Conda

说明

  • 本页记录 Conda 的常用环境管理命令.
  • 适合快速切换 Python 版本, 隔离依赖环境和导出环境配置.

conda activate 指令无效

conda init powershell

执行后重启 PowerShell.

查看环境信息

列出当前环境中的 Python

conda list python

查看所有环境

conda info --envs

创建与切换环境

conda search python
conda create --name om6681 python=3.9.6
conda activate om6681
conda deactivate

conda create --name py310 python=3.10
conda activate py310

导出与删除环境

conda env export > environment.yml
conda remove -n py310 --all

使用建议

  • 新项目优先为每个项目单独创建环境.
  • 若系统里同时存在多个 Python 管理方案, 建议明确区分 Condapyenv 的职责.
  • 团队协作时, 建议同时保留 environment.yml 方便复现.
  • 如果环境越来越重, 可以定期清理不再使用的旧环境.

pyenv 使用说明

说明

  • pyenv 用于在同一台机器上管理多个 Python 版本.
  • 适合做项目隔离, 老项目兼容和测试不同解释器版本的场景.

自动安装 pyenv

curl https://pyenv.run | bash

根据终端提示, 将下面的命令添加到 ~/.bashrc:

export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

如果需要 pyenv-virtualenv, 继续追加:

eval "$(pyenv virtualenv-init -)"

Ubuntu 手动安装

git clone https://github.com/pyenv/pyenv.git ~/.pyenv

常见使用流程

查看可安装版本

pyenv install --list

安装指定版本

pyenv install 3.11.9

设置全局或局部版本

pyenv global 3.11.9
pyenv local 3.10.14

使用建议

  • 新项目优先用 pyenv local 在项目目录内固定版本.
  • 多项目并存时, 不要只依赖系统自带 Python.
  • 若命令未生效, 优先检查 shell 初始化脚本是否已重新加载.
  • 若后续继续整理, 可补 pyenv-virtualenv, 构建依赖和 Windows 替代方案说明.

Python 正则速记

说明

  • 本页记录 Python re 模块中高频但容易忘的表达式写法.
  • 适合做文本抽取, 日志清洗和简单格式校验时快速查阅.

后向断言

  • (?<=...) 表示正向后向断言, 即要求前面满足条件, 但匹配结果本身不包含前面的内容.
  • (?<!...) 表示负向后向断言.

示例 1

想要匹配 def, 但要求前面是 abc:

m = re.search(r'(?<=abc)def', 'abcdefaaaa')
m.group(0)
# 'def'

示例 2

想要匹配 \w+, 但要求前面是 -:

m = re.search(r'(?<=-)\w+', 'spam-egg')
m.group(0)
# 'egg'

常见速记

  • 匹配数字: \d+
  • 匹配单词字符: \w+
  • 非贪婪匹配: .*?
  • 行首行尾: ^...$
  • 分组提取: ( ... )
  • 命名分组: (?P<name>...)

使用建议

  • Python 正则字符串尽量写成原始字符串, 例如 r'\d+', 避免反斜杠转义混乱.
  • 先用最小样例验证表达式, 再扩大到真实文本, 可以减少误匹配.
  • 若只是简单前缀, 后缀或包含判断, 未必一定要上正则, 直接字符串方法通常更易维护.

补充

  • 这类断言适合做“前后文约束但不消耗字符”的匹配.
  • 复杂表达式建议补测试样例, 避免后续维护时无意改坏边界条件.

asyncio 学习笔记

asyncio 过程分析

Task 被包装成 Handle

	coroutine 被包装成 TaskStepMethWrapper 作为 Handle 的 callback 参数

	Handle
		self._context = context
		self._loop = loop
		self._callback = callback

	handle = events.Handle(callback, args, loop, None)



create_future 的过程就是 获取 task 的过程

	ensure_future(coro_or_future, *, loop=None)
		if loop is None:
			loop = events.get_event_loop()
		task = loop.create_task(coro_or_future):
			task = tasks.Task(coro, loop=self, name=name):
				call_soon:
					_call_soon:
						handle = events.Handle(callback, args, self, context)
						self._ready.append(handle)
		return task


获取到 task 后就可以执行 future.add_done_callback, 给 task 添加回调函数, 这里就可以想起两个函数了:
	asyncio.ensure_future 和 asyncio.create_task, 返回值可以 add_done_callback 设置完成或异常时回调函数


从 asyncio.run 传入 第一个 corintine 时, 会经过 create_future 过程, 于是 _ready 中有了第一个 future 函数,
于是开始 run_until_complete 的运行


_UnixSelectorEventLoop.run_until_complete
	self.run_forever


_UnixSelectorEventLoop.run_forever
	events._set_running_loop(self)
	while True:
		self._run_once()
		if self._stopping:
			break


_UnixSelectorEventLoop._run_once

	event_list = self._selector.select(timeout)
	self._process_events(event_list):
		if selectors.EVENT_READ and reader is not None:
			self._add_callback(reader), 开始处理读的数据了

	ntodo = len(self._ready)
		for i in range(ntodo):
			handle = self._ready.popleft()
			if handle._cancelled:
				continue
			else:
				handle._run():
					self._context.run(self._callback, *self._args)

在这里的self._context.run中, 就会直接进入我们的 coroutin



当某一个 handle 执行到 await 时:
	比如遇到 queue.get() 时:

		会去看 queue 是不是空的, 如果是的那么会创建新的 future, 同时等待 await future

		while self.empty():
			getter = self._loop.create_future()
			self._getters.append(getter)
			try:
				await getter
		每一次 _ready 列表都会弹出一项, 然而这里每次执行后都会增加新的 future


如果遇到 gather 函数, 那么会在 _UnixSelectorEventLoop._ready 中添加多个future, 并运行下一次的 _run_once

gather 函数本身只是个非 async 函数, 只是它生成了 _GatheringFuture 对象, 含有 children 成员

executor 给 loop 发io消息: loop._add_callback(reader) --> 让 reader 读消息


await async_func, 直接运行 async_func 函数

loop.run_in_executor(None, func_name, func_args), 主要运行 workItem, 返回一个 asyncio future

	默认 executor 是一个 concurrent 的线程池

	executor 的 future --> concurrent.futures._base.Future 与 loop 的 new_future --> _asyncio.Future 关联到一起:
		new_future = loop.create_future()
		_chain_future(future, new_future):

			destination.add_done_callback(_call_check_cancel)
			source.add_done_callback(_call_set_state)
			当 asyncio future 执行回调 _call_check_cancel 时, 会同步更新 concurrent future
			当 concurrent future 执行回调 _call_set_state 时, 会同步更新 asyncio future

		_copy_future_state(source, dest):
			result = source.result()
			dest.set_result(result)

所以这里在 run_in_executor 中是可以做cpu密集型的工作了


执行 await future 时, 就意味着 要执行下一次 _run_once 了



call_soon_threadsafe(callback, *args, context=None):
	handle = self._call_soon(callback, args, context)
    self._write_to_self()
    return handle

Dramatiq

说明

  • Dramatiq 是一个偏轻量的 Python 后台任务处理框架.
  • 适合做异步任务分发, 延迟执行和简单队列消费场景.

适用场景

  • Web 应用中的异步邮件, 消息通知和批处理任务.
  • 需要基于 Redis 或其他 broker 做轻量任务队列.
  • 希望比重量级任务系统更快上手的后台作业处理方案.

项目

安装 Redis

sudo apt install redis

安装 Dramatiq

pip install --user dramatiq[redis,watch]

Redis 配置为外网可访问

编辑 /etc/redis/redis.conf:

  • 注释掉: bind 127.0.0.1

重启服务:

sudo systemctl restart redis.service

使用建议

  • 如果用于生产环境, 还需要继续补鉴权, 防火墙和网络隔离配置.
  • 开发阶段可以先在本机跑最小 worker 验证消息收发, 再考虑部署拓扑.
  • 若任务复杂度继续增长, 建议同时规划重试策略, 监控和任务幂等性.

创建进程

说明

  • 本页整理 Python 中创建子进程的常见方式, 以 subprocess 标准库为主.
  • 适合处理命令执行, 日志转发, 外部工具调用和构建脚本集成.
  • 若任务只是简单执行一次命令, 优先考虑 subprocess.run; 若要持续读取输出, 再使用 Popen.

常见选择

subprocess.run

  • 适合一次性执行命令并等待结束.
  • 代码更短, 参数也更直观.
import subprocess

result = subprocess.run(
    ["python", "--version"],
    capture_output=True,
    text=True,
    check=False,
)

print(result.returncode)
print(result.stdout)
print(result.stderr)

subprocess.Popen

  • 适合需要边执行边读取输出, 或者自行控制生命周期的场景.
  • 构建工具, 长时间任务和守护型进程包装中最常见.

阻塞式读取完整输出

import subprocess as sp

with sp.Popen(
    cmd,
    stdout=sp.PIPE,
    stderr=sp.PIPE,
    shell=True,
) as proc:
    stdout, stderr = proc.communicate()
    lines = stdout.decode("utf-8", errors="replace").splitlines()

适合场景:

  • 命令执行时间较短.
  • 需要一次性拿到完整标准输出和标准错误.
  • 后续逻辑依赖进程结束后的完整结果.

实时读取日志输出

import subprocess as sp

with sp.Popen(
    cmd,
    stdout=sp.PIPE,
    stderr=sp.STDOUT,
    shell=True,
) as proc:
    for line in iter(proc.stdout.readline, b""):
        text = line.decode("utf-8", errors="replace").rstrip()
        print(text)

适合场景:

  • 需要边运行边展示日志.
  • 构建命令, 下载命令和长时间任务.
  • 需要把外部程序输出实时转发到 UI 或终端.

常用参数

cwd

  • 指定子进程工作目录.
  • 很适合执行项目内脚本或构建命令.

env

  • 指定或覆盖环境变量.
  • 适合临时注入代理, token 或构建参数.

text=True

  • 让输出直接按文本处理, 避免手工 decode.
  • 但实时读取二进制流时, 仍可能需要手工控制字节流处理.

timeout

  • 避免命令无限卡住.
  • 用于自动化脚本时很常见.

使用建议

  • 若命令参数来自外部输入, 尽量避免直接 shell=True 拼接字符串.
  • 优先使用列表参数形式, 例如 ['git', 'status'], 这样更安全.
  • 实时展示日志时, 通常把 stderr 合并到 stdout 会更简单.
  • 如果进程可能产出大量输出, 不要同时长期不消费 stdout / stderr, 否则可能阻塞.

常见问题

1. 为什么命令在终端能跑, 在脚本里失败

  • 先检查 cwd 是否正确.
  • 再检查环境变量是否完整, 例如 PATH.
  • 最后确认是否依赖 shell 特性, 比如通配符, 管道或重定向.

2. 为什么实时日志没有输出

  • 有些程序默认对非交互环境启用缓冲.
  • 可尝试程序自身的“无缓冲”参数, 或改为逐行刷新输出.

3. 为什么 shell=True 有风险

  • 因为字符串拼接命令容易引入命令注入问题.
  • 自动化场景里尤其要避免把用户输入直接拼进 shell 命令.

相关文档

socket 笔记

说明

  • 本页整理 Python socket 标准库中最常见的地址解析与 TCP 连接用法.
  • 适合做网络诊断工具, 简单客户端, 端口探测和协议实验.
  • 若只是做高层 HTTP 请求, 通常优先使用更上层库, 不必直接从 socket 开始.

地址解析: socket.getaddrinfo

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

返回值中的每一项通常是:

(family, type, proto, canonname, sockaddr)

典型示例:

import socket

infos = socket.getaddrinfo(
    "example.org",
    80,
    proto=socket.IPPROTO_TCP,
)

for family, socktype, proto, canonname, sockaddr in infos:
    print(family, socktype, proto, canonname, sockaddr)

这个函数适合做什么

  • 根据主机名和端口解析出可连接的地址列表.
  • 同时兼容 IPv4 和 IPv6.
  • 在自己实现连接逻辑时, 可以先拿到所有候选地址再逐个尝试.

最小 TCP 客户端示例

import socket

infos = socket.getaddrinfo(
    "example.org",
    80,
    type=socket.SOCK_STREAM,
    proto=socket.IPPROTO_TCP,
)

family, socktype, proto, _, sockaddr = infos[0]

with socket.socket(family, socktype, proto) as sock:
    sock.settimeout(5)
    sock.connect(sockaddr)
    sock.sendall(b"GET / HTTP/1.0
Host: example.org

")
    data = sock.recv(4096)
    print(data.decode("utf-8", errors="replace"))

更简单的连接方式

import socket

with socket.create_connection(("example.org", 80), timeout=5) as sock:
    sock.sendall(b"GET / HTTP/1.0
Host: example.org

")
    print(sock.recv(4096))
  • 对简单 TCP 客户端来说, socket.create_connection 往往更直接.
  • 它会自动处理一部分地址解析和连接尝试逻辑.

常见概念

family

  • 常见值包括 AF_INETAF_INET6.
  • 分别对应 IPv4 和 IPv6.

type

  • SOCK_STREAM 通常表示 TCP.
  • SOCK_DGRAM 通常表示 UDP.

sockaddr

  • 是真正拿来传给 connect 的地址信息.
  • IPv4 和 IPv6 对应的元组结构略有差异.

使用建议

  • 做网络探测时, 建议显式设置超时, 例如 settimeout(5).
  • 需要同时兼容 IPv4 / IPv6 时, 优先通过 getaddrinfo 获取候选地址.
  • 如果只是实现简单客户端, 优先考虑 socket.create_connection.
  • 若要写长期稳定的协议服务, 应再补充异常处理, 重试, 超时和资源清理策略.

常见问题

connect 卡住很久

  • 多半是没有设置超时.
  • 也可能是 DNS 解析慢, 网络被代理或防火墙阻断.

地址解析结果很多

  • 这是正常现象, 尤其是同时返回 IPv4 和 IPv6 时.
  • 可以按需求优先选择某个 family.

为什么不用更高层库

  • 原始 socket 更适合做协议学习, 底层排查和轻量实验.
  • 业务开发里若协议已经成熟, 通常优先选择更高层封装.

相关文档

Paramiko

说明

  • Paramiko 是 Python 中常用的 SSH 自动化库.
  • 适合远程命令执行, 文件传输和运维脚本编写场景.

SSH 客户端基本用法

from paramiko import SSHClient, AutoAddPolicy

connect_kwargs = dict(
    hostname=hostname,
    port=port,
    username=username,
    timeout=timeout,
)
if password:
    connect_kwargs['password'] = password

client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())

client.connect(**connect_kwargs)
transport = client.get_transport()

channel = transport.open_session()
channel.settimeout(timeout)
channel.set_combine_stderr(True)
channel.exec_command('ls')

常见能力

  • 执行远程命令.
  • 通过 SFTP 上传和下载文件.
  • 复用一个连接上的多个 Channel.
  • 结合超时, 日志和重试做自动化运维脚本.

源码简单分析

  • 一个 client 中会维护一个 Transport 作为传输线程.
  • Transport 内部持有 packetizer, 负责基于远程 socket 收发数据.
  • 更高级的通信抽象是 Channel, 多个 Channel 共享同一个 Transport.

认证过程线索

  • client.connect()
  • self._transport.auth_publickey(username, key)
  • AuthHandler._request_auth()

使用建议

  • 生产脚本中尽量不要长期使用 AutoAddPolicy, 应显式管理主机指纹.
  • Channel 的数据没有及时读取时, 服务端可能暂停继续发送.
  • 当服务端没有及时消费你发送的数据时, 写入也可能阻塞, 因此超时和流控都很重要.
  • 若后续继续整理, 可补 SFTP, 跳板机, 长连接复用和异常重试示例.

PySide2 笔记

说明

  • 本页记录 PySide2 常见 GUI 组件的最小使用方式.
  • 当前重点是多标签页和多页面切换相关控件.

常见控件

  • QTabWidget: 适合直接展示标签页切换.
  • QStackedWidget: 适合把页面切换和导航组件拆开管理.
  • QTabBar: 适合需要自定义标签行为时单独使用.

实现多标签页

参考:

选择建议:

  • 页面结构比较标准时, 优先用 QTabWidget.
  • 需要自己控制侧边栏, 顶部菜单或向导流程时, 优先用 QStackedWidget.

最小示例

from PySide2.QtWidgets import QApplication, QLabel, QTabWidget

app = QApplication([])
widget = QTabWidget()
widget.addTab(QLabel("第一页"), "Tab 1")
widget.addTab(QLabel("第二页"), "Tab 2")
widget.resize(400, 240)
widget.show()
app.exec_()

使用建议

  • 若页面较多, 建议把每个页面封装成独立 QWidget 类.
  • 若标签内容需要动态刷新, 尽量把状态更新逻辑和页面切换逻辑分开.
  • 若后续继续整理, 可补充信号槽, 菜单栏, 对话框和线程协作相关示例.

Dart 与 Flutter 总览

说明

  • 本目录收录 Dart, FlutterFlutter + Rust 相关记录.
  • 如果目标是桌面或移动端应用, 可先看 Flutter 学习笔记; 若需要跨语言绑定, 再看 flutter_rust_bridge.

语言基础

Flutter 应用开发

Flutter 与 Rust

建议阅读路径

  1. 初次接触先看 dart 学习笔记Flutter 学习笔记.
  2. 遇到环境或编译问题时, 再看 Flutter 引擎编译.
  3. 若项目需要与 Rust 联动, 按 Flutter + Rust 环境 -> flutter_rust_bridge 的顺序继续.

整理建议

  • 当前内容更偏“学习记录 + 工具链搭建”.
  • 后续可继续补 状态管理, 桌面端打包, 平台通道, 发布流程 等主题.

Dart 学习笔记

说明

  • 本页作为 Dart 语言的轻量入口页, 用于串起语法学习, 官方文档和 Flutter 生态关系.
  • 适合刚开始接触 Dart 时做快速导航.
  • 若目标是应用开发, 建议结合 Flutter 相关页面一起看.

学习路径

快速上手

系统学习

建议先掌握的主题

  • 变量, 函数和控制流.
  • 类, 继承与 mixin.
  • Future, async/await.
  • 集合类型与空安全.

Dart 与 Flutter 的关系

  • Dart 是语言本身.
  • Flutter 是基于 Dart 的 UI 框架与应用开发生态.
  • 学 Dart 时不必一开始就绑定 Flutter, 但进入应用开发后通常会一起使用.

使用建议

  • 如果目标是尽快上手语法, 先看 Samples 和 Cheatsheet.
  • 如果需要系统学习语言机制, 再进入 Language Tour.
  • 真正进入应用开发时, 建议结合 Flutter 学习笔记 一起看.

后续可继续补充的方向

  • 泛型与类型系统.
  • 异步编程模型.
  • 包管理与 pubspec.yaml.
  • 测试与工程结构.

Flutter 学习笔记

说明

  • 本页记录 Flutter 环境初始化, 桌面开发, Android 开发和常见代理配置.
  • 内容横跨 Windows 与 Linux 场景, 更适合作为综合备忘录.

基础环境

国内镜像环境变量示例:

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

安装说明:

  • https://flutter.cn/docs/get-started/install

常用初始化命令:

flutter config --no-analytics
flutter --disable-telemetry
dart --disable-analytics
flutter doctor

桌面开发

参考:

国内镜像可切换为:

export PUB_HOSTED_URL=https://mirrors.tuna.tsinghua.edu.cn/dart-pub
export FLUTTER_STORAGE_BASE_URL=https://mirrors.tuna.tsinghua.edu.cn/flutter

启用桌面能力:

flutter upgrade
flutter config --enable-<platform>-desktop

常见平台选项:

  • flutter config --enable-linux-desktop
  • flutter config --enable-macos-desktop
  • flutter config --enable-windows-desktop
  • flutter config --enable-web
  • flutter config --enable-android

Linux 桌面开发常见依赖:

sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev libblkid-dev liblzma-dev

创建与运行应用:

flutter create flutter_desktop
cd flutter_desktop
flutter run -d windows
flutter run -d macos
flutter run -d linux

构建 release:

flutter build windows
flutter build macos
flutter build linux

性能调试:

flutter run -d linux --trace-skia --profile
flutter screenshot -d linux --type=skia --observatory-uri=<Observatory-URI>

Android 环境

Android Studio 下载:

  • https://developer.android.com/studio

常见环境变量:

D:\programs\Android\Sdk\platform-tools
ANDROID_SDK_ROOT=D:\programs\Android\Sdk
ANDROID_NDK_ROOT=%ANDROID_SDK_ROOT%\ndk\26.3.11579264

初始化 Android 开发环境:

flutter doctor --android-licenses
flutter create start_flutter_android
flutter build apk
flutter build apk --debug

网络不好的话配置代理

gradle.properties 中追加:

systemProp.socks.proxyHost=127.0.0.1
systemProp.socks.proxyPort=1080
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=1080

Gradle 下载失败

参考:

  • https://github.com/whichow/Notebook/blob/master/Development/Android/Build/%E8%A7%A3%E5%86%B3Gradle%E4%B8%8B%E8%BD%BD%E8%B6%85%E6%97%B6%E9%97%AE%E9%A2%98.md

可在 gradle/wrapper/gradle-wrapper.properties 中把远程 distributionUrl 改成本地文件路径, 例如:

distributionUrl=file:///D:/programs/gradle/gradle-7.6.3-all.zip

然后执行:

./gradlew build

其他

  • iOS 构建: flutter build ios
  • Flutter 升级: flutter upgrade

Flutter + Rust 环境

说明

  • 本页用于整理 FlutterRust 混合工程的环境准备和最小集成思路.
  • 更完整的桥接流程可参考 flutter_rust_bridge.

安装准备

  • 安装 Rust 工具链.
  • 安装 Dart / Flutter 环境.
  • 安装 protobuf 等桥接所需工具.
  • 根据目标平台补齐 Android SDK, Windows 构建工具或其他平台依赖.

最小集成思路

  1. 先分别确认 flutter doctorcargo --version 都能正常工作.
  2. 在 Flutter 工程外或工程内建立 Rust 库 crate.
  3. 通过桥接工具生成 Dart 侧绑定代码.
  4. 在 Flutter 侧调用生成的接口, 再处理打包和运行库分发问题.

常见关注点

  • 平台架构要一致, 尤其是 x64, arm64, Android ABI 等目标不能混用.
  • 原生库输出路径要稳定, 否则 Flutter 侧很容易在运行时找不到动态库.
  • 若使用代码生成桥接, 要把生成命令纳入构建流程, 避免接口变更后两侧不同步.

建议阅读顺序

flutter_rust_bridge

说明

  • flutter_rust_bridge 用于在 FlutterRust 之间生成跨语言绑定代码.
  • 适合在保留 Flutter UI 的同时, 把性能敏感或可复用逻辑放到 Rust 侧实现.

参考资料

最小接入思路

  1. Flutter 工程中创建一个独立的 Rust 库.
  2. Rust 中定义对外 API.
  3. 通过 flutter_rust_bridge_codegen 生成 Dart 侧桥接代码.
  4. Flutter 页面中调用生成后的接口完成联调.

Rust 部分

Flutter 项目中创建一个 Rust 库项目:

cargo new native --lib

Cargo.toml 中设置:

[lib]
crate-type = ["staticlib", "cdylib", "rlib"]

添加依赖:

cargo add flutter_rust_bridge

示例 API:

#![allow(unused)]
fn main() {
pub fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}
}

Flutter 部分

flutter pub add ffi flutter_rust_bridge freezed_annotation
flutter pub add --dev ffigen build_runner freezed

生成 Dart 代码

flutter_rust_bridge_codegen --rust-input native/src/api.rs --dart-output lib/bridge_generated.dart

联调关注点

  • Rust 导出库的产物路径要与 Flutter 构建流程对应.
  • 修改 Rust API 后, 记得重新生成桥接代码.
  • 复杂参数类型要优先确认是否已被当前版本的生成器良好支持.
  • 排错时先做最小 greet 示例, 再逐步引入异步, 流或复杂结构体.

建议阅读顺序

  1. 先看本页跑通最小跨语言调用.
  2. 再结合 Flutter + Rust 环境 检查本地工具链.
  3. 真正进入业务后, 再补线程模型, 错误传递和打包发布相关笔记.

Flutter 引擎编译

说明

  • 本页记录一个较早期的 Flutter Engine Windows x86 编译尝试.
  • 当前更适合作为历史链路记录, 使用时请结合当前 Flutter Engine 版本核对.

Flutter 源码编译 x86 架构

1. 安装 Windows 构建环境

  • 在 Visual Studio 中安装 C++ 开发环境.
  • 如果提示缺少 msdia140.dll, 通常还需要确认以下组件:
    • Windows Software Development Kit
    • Debugging Tools for Windows

2. 设置环境变量

$env:DEPOT_TOOLS_WIN_TOOLCHAIN="0"
$env:GYP_MSVS_OVERRIDE_PATH="D:\Program Files\Microsoft Visual Studio\2022\Community"

3. 下载代码

gclient config https://github.com/flutter/engine.git@375834305d15a70b16f8a66eadb0b61ed2c5bf24
gclient sync --no-history -v

4. 生成与编译

.\flutter\tools\gn --target-os=win --windows-cpu=x86 --runtime-mode=jit_release --no-goma

记录结论

  • 当时的记录结论是: x86 架构未能顺利编译通过.
  • 如果后续继续排查, 需要优先确认对应提交版本是否仍支持该目标架构.

后续可补

  • depot_tools 完整安装流程.
  • ninja 构建命令.
  • 失败日志与依赖版本对应关系.

Flutter 异常处理

说明

  • 本页记录 Flutter 开发中常见异常排查思路.
  • 当前先从 shared_preferences 数据损坏导致的 FormatException 场景展开.
  • 后续可继续补 UI, 网络, 状态管理和平台通道相关异常.

典型问题: shared_preferences 报错

报错示例:

FormatException: Unexpected character (at character 1)

常见原因

  • shared_preferences.json 被写入了错误内容.
  • 历史版本程序写入了不兼容格式.
  • 程序异常退出导致持久化数据损坏.

当前处理方式

  • 找到应用对应的 shared_preferences.json 文件.
  • 若文件内容明显异常, 例如 NULLNULL 之类的损坏数据, 删除该文件.
  • 重新打开软件, 让程序重新生成配置文件.

排查建议

1. 先确认是否是持久化数据损坏

  • 看报错是否集中出现在首帧启动或读取配置阶段.
  • 检查最近是否发生过版本升级或异常退出.

2. 再确认是否存在兼容性问题

  • 不同版本字段结构变化时, 旧数据可能无法直接解析.
  • 若使用了自定义序列化, 更要检查字段默认值和空值处理.

3. 做恢复策略

  • 对高风险配置文件考虑加默认值兜底.
  • 解析失败时尽量做回退, 不要让应用直接卡死在启动阶段.

使用建议

  • 本页更适合作为异常排查线索页, 不替代正式日志分析.
  • 真正修复时, 建议同时补数据兼容与损坏恢复逻辑.
  • 若后续异常记录增多, 建议按 存储, 网络, UI, 平台通道 再拆小节.

C# 总览

说明

  • 本目录收录 C# 调试, 动态库导出, 与原生代码互操作相关笔记.
  • 当前内容偏向桌面应用, DLL 导出和 C# <-> C/C++ 边界联调场景.

适用场景

  • 需要把 C# 代码编译成动态库供外部调用.
  • 需要在 C# 中通过 P/Invoke 调用原生 DLL.
  • 需要排查托管代码和原生库之间的数据类型, 调用约定和导出符号问题.

常用入口

建议阅读顺序

  1. 先看 C# 笔记, 熟悉当前仓库保留的基础记录.
  2. 若目标是导出库, 再看 C# 生成动态库.
  3. 若目标是跨语言联调, 继续看 调用 C DLL 的两种方式.

常见关注点

  • 区分 x86 / x64 架构, 避免加载阶段就失败.
  • 明确字符串编码, 结构体布局和内存释放责任.
  • 确认导出函数签名, 调用约定和异常边界.

C# 笔记

说明

  • 本页记录 C# 在调试外部程序和与原生库交互时的常见做法.
  • 当前重点是 DLL 调试方式和 C# <-> Rust 的回调绑定.

从 DLL 项目调试外部 EXE

参考: https://learn.microsoft.com/zh-cn/visualstudio/debugger/how-to-debug-from-a-dll-project?view=vs-2022

常见做法:

  1. 设置 DLL 自动编译到目标可执行程序所在路径.
  2. 在项目中配置外部启动程序 exe.
  3. 让调试器以宿主程序启动, 再进入 DLL 断点排查.

适用场景:

  • 插件式程序开发.
  • 需要调试被宿主程序动态加载的类库.

C# 与 Rust 交互

让第三方 DLL 调用 C# 函数

C# 中创建 delegate, 再把函数指针传给原生侧:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void DelegatTxHandler([MarshalAs(UnmanagedType.LPUTF8Str)] string msg, int id);

private IntPtr msg_handle_ptr;

void send_message([MarshalAs(UnmanagedType.LPUTF8Str)] string msg, int id) {
}

void Register() {
    msg_handle_ptr = Marshal.GetFunctionPointerForDelegate(new DelegatTxHandler(send_message));
    bind_msgHandler(msg_handle_ptr);
}

[DllImport("rust_dll", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void bind_msgHandler(IntPtr func);
#![allow(unused)]
fn main() {
pub type TxHandler = unsafe extern "C" fn(msg: *const c_char, id: i32);

#[no_mangle]
extern "C" fn bind_TxHandler(func: TxHandler) {
    unsafe {
        tx_handler = Some(func);
    }
}
}

联调注意点

  • CallingConvention 要与原生侧保持一致.
  • 字符串编码要提前约定, 例如统一 UTF-8.
  • 委托对象需要持有引用, 避免被 GC 提前回收.
  • 导出函数命名和实际绑定函数要完全一致, 避免入口找不到.

排错建议

  • 先验证最简单的数字或字符串回调, 再引入结构体和异步调用.
  • 若加载 DLL 失败, 先检查架构, 依赖库和输出路径.
  • 若回调崩溃, 优先检查签名, 生命周期和内存所有权边界.

C# 生成动态库

说明

  • 本页记录使用 .NET 8AOT 方式生成可被 C 语言直接调用的动态库.
  • 适合做 C# -> 原生导出 的最小验证, 也适合给 C/C++ 或其他语言调用 .NET 逻辑时做桥接实验.

目标

  • 使用 .NET 8 类库项目.
  • UnmanagedCallersOnly 暴露导出函数.
  • 通过 PublishAot + NativeLib=Shared 生成共享库.

基本步骤

  1. 使用 Visual Studio 2022 创建 .NET 8 类库 项目.
  2. 在导出函数上使用 UnmanagedCallersOnly.
  3. 打开 PublishAot 并以共享库方式发布.
  4. 在调用侧根据导出名和调用约定加载动态库.

示例代码

using System.Runtime.InteropServices;

namespace TestCSharpLib
{
    public class OneClass
    {
        [UnmanagedCallersOnly(EntryPoint = "Add")]
        public static int Add(int a, int b)
        {
            return a + b;
        }

        [UnmanagedCallersOnly(EntryPoint = "Greet")]
        public static void Greet()
        {
            Console.WriteLine("你好呀");
        }
    }
}

项目配置示例

<PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <PlatformTarget>x64</PlatformTarget>
    <OutputType>Library</OutputType>
</PropertyGroup>

发布命令

dotnet publish -p:NativeLib=Shared -r win-x64 -c Release

输出目录

bin/Release/net8.0/win-x64/native/

常见注意点

  • 导出函数签名要尽量简单稳定, 先从整数, 指针或明确定义的结构体开始.
  • 调用侧和生成侧的架构必须一致, 例如都使用 x64.
  • 字符串, 内存释放和异常边界要提前约定清楚, 避免跨语言调用时出现未定义行为.
  • 若只想在 C# 内部复用逻辑, 并不需要走原生动态库导出方案.

调用 C DLL 的两种方式

说明

  • 本页整理 C# 调用原生 C DLL 时最常见的两类方式.
  • 一个偏静态声明, 一个偏动态加载, 适合根据项目形态做取舍.

方式一: DllImport

  • 适合函数签名稳定, 调用关系明确的场景.
  • 由运行时自动完成符号绑定, 写法最直接.
[DllImport("demo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int a, int b);

优点:

  • 声明简单.
  • 可读性高.
  • 适合固定接口和少量导出函数.

方式二: 动态加载函数指针

  • 适合插件化, 可选加载或运行时决定 DLL 路径的场景.
  • 常见做法是 LoadLibrary + GetProcAddress, 再转换为委托.

典型思路:

  1. 动态加载 DLL.
  2. 获取目标导出函数地址.
  3. 通过 Marshal.GetDelegateForFunctionPointer 转为委托.
  4. 调用委托.

选择建议

  • 接口固定且数量不多时, 优先用 DllImport.
  • 需要运行时切换版本, 动态探测能力或做插件系统时, 再考虑函数指针方式.
  • 如果目标是跨语言长期维护, 要尽量把导出接口做得简单稳定.

联调关注点

  • CallingConvention 必须和原生侧一致.
  • 字符串编码, 结构体布局和内存释放责任要提前约定.
  • x86 / x64 架构不一致时, 往往在加载阶段就会失败.
  • 复杂对象尽量不要直接跨边界传递, 优先用基本类型或序列化数据.

相关文档

C 与 C++ 总览

说明

  • 本目录收录 C, C++, Windows API, Qt, 汇编, OCR 和电路仿真等开发笔记.
  • 若问题偏语言基础或编译工具链, 可先从本页进入; 若偏 GUI 或桌面开发, 可再看 QtWindows API 相关页面.

阅读路径

  • 语言基础和工具链问题, 优先看 C 语言笔记, C++ 笔记, gcc 笔记.
  • Windows 平台开发问题, 优先看 Windows API 与异步 IO 模型页面.
  • Qt 桌面应用问题, 先看 Qt 总览, 再进入 QtUsb, Qwt 等专题.
  • 电路仿真与实验类问题, 先看 SPICE 仿真总览, 再深入 ngspice, LTspice.

语言与工具链

Windows 与系统

Qt 与桌面应用

仿真与图形化工具

OCR 与文本识别

目录边界

  • 本目录保留语言, 原生开发与桌面工具链相关知识.
  • 若问题更偏前端, Rust, Python 或嵌入式, 通常应回到对应目录继续查找.

C语言笔记

大小端: 小端低低

小端, 低位在低地址:
    uint8_t data[] = {0x01, 0x02};
    uint32_t d = *((uint32 *)data);

    d ==> 0x0201

结构体

!!! 这三条直接影响结构体对齐:
    <1> __attribute__((aligned(ALIGNED_SIZE))), 结构体的对齐字节是ALIGNED_SIZE, 2的次方
    <2> #pragma pack(ALIGNED_SIZE), 结构体的对齐字节是ALIGNED_SIZE, 2的次方
    <3> 使用__attribute__ ((__packed__))修饰时, 结构体不要对齐

内存对齐

目的:
    1. 平台移植
    2. 更好性能, 访问未对齐的内存, 处理器需要两次访问内存, 而对齐的只需要访问一次


三条规则:
    1. 首元素偏移量是0
    2. 成员对齐, 每个成员的偏移量是当前成员大小的整数倍
    3. 结构体对齐, 其对齐字节数是结构体最大成员字节数, 结构体的总大小是对齐字节数的整数倍

位域

信息在存储时, 并不需要完整的字节, 可能需要操作某些位, 这时就该使用位域了

位段成员必须声明为 (unsigned) int8_t, int16_t 或 int32_t 类型

规则:

    1) 如果后面的位域字段与前一个位域字段之和小于前一个位域字段类型的位宽, 则后面一个紧邻前一个存储, 直到不能存储
    2) 如果后面的位域字段与前一个位域字段之和大于前一个位域字段类型的位宽, 则后面一个将从新的存储单元开始
    3) 结构体对齐, 其对齐字节数是结构体最大成员字节数, 结构体的总大小是对齐字节数的整数倍

    struct bit_field
    {
        uint8_t a : 4; /* 独占一个 uint8_t */
        uint8_t b : 5; /* 4+5 > 8, 从新的存储单元开始存放*/
        uint8_t : 3;   /* 空域, 5+3 = 8, 这个位域和前一个位域刚好填满一个存储单元*/
        uint32_t c : 3; /* 独占一个 uint32_t */
    };

    sizeof(struct bit_field) --> 4 byte

“#“常见的用法

#  				把符号转换成字符串常量
## 				把两个符号拼成一个变量
__VA_ARGS__  	c99新增, 用于表示...参数内容
##__VA_ARGS__  	当可变参数长度为0时, 会自动去除前面不必要的","

c++学习笔记

左值引用和右值引用

左值和右值的区分标准在于能否获取地址

左值引用

左值的定义表示的是可以获取地址的表达式,它能出现在赋值语句的左边,对该表达式进行赋值

int i = 10;

int &ii = i; // i是个有地址的变量, ii对i进行引用, 访问ii就是直接访问i

int c = i;

int d = ii;

movl	$10, -28(%rbp)

leaq	-28(%rbp), %rax
movq	%rax, -16(%rbp)

movl	-28(%rbp), %eax
movl	%eax, -24(%rbp)

movq	-16(%rbp), %rax
movl	(%rax), %eax
movl	%eax, -20(%rbp)

四段汇编分别对应了四行代码

汇编分析:

第1行: 把立即数10传送到地址为-28(%rbp)的内存(变量i)上

第2~3行: 把i的地址放到寄存器rax中, 再把rax的内容传送到地址为-16(%rbp)的内存(变量ii)上, 所以对ii的访问就是直接访问变量i的地址上的内容

第4~5行: 把地址-28(%rbp)为的内存(变量i)的内容传送到寄存器eax, 再把eax的内容放到地址为-24(%rbp)的内存(变量c)中

第6~8行: 这里表示对ii变量的使用, 把地址为-16(%rbp)的内存(变量ii)的内容传送到寄存器rax, 再把rax中的值作为内存地址, 把这个内存地址中的值放入到eax寄存器, 再把eax寄存器数据传送到地址为-20(%rbp)的内存(变量d)中, 简单的说, 访问ii, 就是先拿到引用值的地址, 取这个地址上的数据, 把数据传送到目标内存上

右值引用

int &&iii = 10;
int e = iii;


movl	$10, %eax
movl	%eax, -40(%rbp)
leaq	-40(%rbp), %rax
movq	%rax, -16(%rbp)

movq	-16(%rbp), %rax
movl	(%rax), %eax
movl	%eax, -28(%rbp)

两段汇编分别对应了两行代码

汇编分析:

第1~4行: 把立即数10放入到寄存器eax中, 把eax的内容放入到地址为-40(%rbp)的内存(为10分配的临时内存)中, 把这个临时内存的地址传送到寄存器rax, 把rax的值传送到地址为-16(%rbp)的内存(变量iii)中

第5~7行: 即访问iii, 就是先拿到引用值的地址, 取这个地址上的数据, 把数据传送到目标内存上

总结:

左值引用和右值引用很类似,
左值引用是用一个变量保存引用的目标的地址, 右值引用是先给右值分配临时栈内存, 再用一个变量访问这块内存

gcc 笔记

gcc笔记

安装gcc9

sudo add-apt-repository ppa:jonathonf/gcc-9.0
sudo apt install g++-9

attribute用法集锦

__attribute__((weak)), 设置弱函数

“arm-none-eabi-gdb –version“报错: libncurses.so.5: cannot open shared object file

sudo apt install libncurses5

arm-none-eabi-gcc 的 “-specs” gcc定义

来自对文档的解读: gcc-arm-none-eabi-9-2020-q2-update/share/doc/gcc-arm-none-eabi/pdf/gcc/gcc.pdf

    3.19 Specifying Subprocesses and the Switches to Pass to Them

查看默认的 specs:
    arm-none-eabi-gcc -dumpspecs

语法:

    %command:
        向 spec 文件处理器 发起一个命令

        %include <file>
            搜索文件, 并把文件的内容插入到 spec file 当前的位置

        %include_noerr <file>
            同上, 当file not found 时, 不会报错

        %rename old_name new_name
            重命名

            一个例子:

                %rename lib old_lib

                *lib:
                --start-group -lgcc -lc -leval1 --end-group %(old_lib)

            重命名一个 spec string, 也就是把一个叫"lib"的 spec string 名字 重命名叫 "old_lib"
            创建一个叫 "lib" 的 spec string, 并用"%(old_lib)"表示追加上原来的 spec string

    *[spec_name]:
        创建、覆盖 或 删除 一个 spec string

        通过 -dumpspecs 输出中可以看到,默认已经有了 asm, cpp, link 等这些 spec string 了

    +[spec_name]:
        追加 一个 spec string

    [suffix]:
        对特定后缀处理, 比如:

            .ZZ:
                z-compile -input %i

        这表明对 .ZZ 后缀的文件 都会被传递给 "z-compile -input %i" 命令, %i 是输入文件

            .ZZ:
            @c++

        这里是 @language, 指定 c++ 的后缀别名, 和命令行参数 "-x" 很相似


    对于 spec strings 的 “%”符号的常用用法

    %(name)             在所在位置替换 spec string name 的内容
    %{S:X}              如果 gcc参数 有 "-S"选项, 那么替换 "-S" 为 "X"
    %{!S:X}             如果 gcc参数 没有 "-S"选项, 那么替换 "-S" 为 "X"
    %:function(args)    function 有很多,
                        replace-outfile 这个 spec 函数 需要两个参数, 用后一个替换前一个, 例子:
                            %{fgnu-runtime:%:replace-outfile(-lobjc -lobjc-gnu)}
    %G                  不知道啥意思: Process the libgcc spec. This is a spec string for deciding which GCC support
                        library is included on the command line to the linker.

解读 -spec=nosys.spec

gcc-arm-none-eabi-9-2020-q2-update/arm-none-eabi/lib/thumb/v7-m/nofp/nosys.specs

    %rename link_gcc_c_sequence                nosys_link_gcc_c_sequence

    *nosys_libgloss:
    -lnosys

    *nosys_libc:
    %{!specs=nano.specs:-lc} %{specs=nano.specs:-lc_nano}

    *link_gcc_c_sequence:
    %(nosys_link_gcc_c_sequence) --start-group %G %(nosys_libc) %(nosys_libgloss) --end-group

把默认的 link_gcc_c_sequence 换个名字, 叫 "nosys_link_gcc_c_sequence"
定义了 3个 spec strings:
    nosys_libgloss, 定义了一个链接库的选项: -lnosys
    nosys_libc, 有"!"的那个, 如果 gcc参数中没有 "-specs=nano.specs" 那么把 "-specs=nano.specs" 替换为 "-lc"
                没有"!"的那个, 如果 gcc参数中有 "-specs=nano.specs" 那么把 "-specs=nano.specs" 替换为 "-lc_nano"
    link_gcc_c_sequence, 先使用原来"link_gcc_c_sequence"定义的 spec string
                         再追加上新的 "--start-group %G %(nosys_libc) %(nosys_libgloss) --end-group"
                         并替换上面已定义的"nosys_libc"和"nosys_libgloss"

gcc 优化

gcc 优化

gcc中指定优化级别的参数有:-O0、-O1、-O2、-O3、-Og、-Os、-Ofast

默认为 -O0

-Og 是在 -O1 的基础上,去掉了那些影响调试的优化,所以如果最终是为了调试程序,可以使用这个参数

Boost 总览

说明

  • 本目录保存 Boost 相关的环境搭建记录与示例代码.
  • 当前内容更偏 Windows + CMake + MinGW-w64 工具链场景, 适合作为历史配置与示例归档入口.
  • 若问题只是普通 C++ 工具链或构建链, 应优先回到 C 与 C++ 总览.

当前内容

阅读路径

  1. 先看环境搭建文档, 明确 MinGW-w64, cmake, ninja, Boost 版本约束.
  2. 再看 examples/ 中的最小示例, 理解 Boost 各模块的基础用法.
  3. 若问题已经回到通用构建链, 再去看 CMake 总览 或其他工具链文档.

适用场景

  • 在 Windows 上用 MinGW-w64 构建 Boost.
  • 回看 Boost.Asio, Boost.Program_options 等历史示例写法.
  • 排查旧工程中的 find_package(Boost) 版本适配问题.

使用建议

  • 本目录保留的是历史配置与示例上下文, 不建议直接把旧版本组合当成当前默认方案.
  • 若其中某个 Boost 模块的知识点已稳定, 建议后续单独提炼成正式专题页.
  • 示例目录若继续扩张, 可再细分为“构建安装”和“模块示例”两类入口.

Windows 10 下 CMake + Boost + MinGW-w64 环境搭建

说明

  • 本页记录在 Windows 10 下搭建 CMake + Boost + MinGW-w64 开发环境的过程.
  • 适合作为旧版 C/C++ 构建环境准备与 Boost 编译链配置的参考页.

下载mingw-w64

方法1:

	下载: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/8.1.0/

	选择 MinGW-W64 GCC-8.1.0 --> x86_64-posix-seh

	解压出去, 把bin目录放到环境变量下, mingw的c++开发环境就算是好了


方法2:

	choco.exe info mingw --verbose
	choco.exe install mingw

这两种方法是一样的, choco这个软件管理工具使用的是sourceforge.net/projects/mingw-w64, 这里方法一我只是把choco的下载链接取出来了, 直接使用

下载 cmake ninja

我有python3的环境:

	pip install --user cmake ninja

没有pip环境, 只好到cmake和ninja的官方网站下载了

用 gcc 编译 boost

我当前用的是cmake 3.18.2, 这个版本会对boost的版本选择造成影响, 3.18.2应该只支持到boost 1.73, 这个是set(Boost_DEBUG ON) cmake的输出可以看出来, 不同版本支持的情况不同, 因为每一个cmake版本都适配了对应的可以find_package的boost版本

下载 boost_1.73 https://dl.bintray.com/boostorg/release/1.73.0/source

解压 boost_1.73.0 源码到 D:\temp\boost_1_73_0 目录

用cmd

cd D:\temp\boost_1_73_0\tools\build
.\bootstrap.bat gcc

mkdir D:\temp\boost-build
.\b2.exe --prefix="D:\temp\boost-build" install
set PATH=%PATH%;D:\temp\boost-build\bin;

cd D:\temp\boost_1_73_0
b2 --build-dir="D:\temp\boost_1_73_0\build" --build-type=complete --prefix="D:\programs\boost_1_73_0" toolset=gcc install

最后就会安装到 D:\programs\boost_1_73_0 目录下

编译环境: I7-8665U windows 10 U盘 Wintogo, 4核8线程, 全速耗时 60 分钟

cmake example

cmake_minimum_required(VERSION 3.10)

project(hello_world)

# set(CMAKE_CXX_STANDARD 11)

# 这个Boost_DEBUG是非常有用的, 如果遇到missing的时候请分析输出中"search"相关, 是否是实际的情况
#     比如: 根据Boost_DEBUG输出, 我判断出没有找到正确的lib, 我编译出的带有arch, 找出的却不带, 另外安装的可能是vc编译的, 我却需要mingw编译的
set(Boost_DEBUG ON)
set(Boost_ARCHITECTURE "x64")


# 设置BOOST的安装路径, 如果设置了BOOST_ROOT环境变量, 这里就不需要了
# set(BOOST_ROOT "")

find_package(Boost REQUIRED COMPONENTS filesystem)

add_executable(hello_world hello_world.cpp)
target_link_libraries(hello_world Boost::filesystem)

nasm 学习笔记

非常详细的入门教程: https://cs.lmu.edu/~ray/notes/nasmtutorial

nasm官方pdf: https://www.nasm.us/xdoc/2.14.02/nasmdoc.pdf

ps: 学习nasm笔记, 有所疏漏, 在所难免

找了下常见寄存器的缩写, 以及常见寄存器的解释, 辅助理解, 并不是nasm的内容

AX  累加寄存器, Accumulator register
BX  基寄存器,   Base register
CX  计数寄存器, Count register
DX  数据寄存器, Data register

CS(Code Segment): 代码段寄存器
DS(Data Segment): 数据段寄存器
SS(Stack Segment): 堆栈段寄存器
ES(Extra Segment): 附加段寄存器

eax 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器
ebx 是"基地址"(base)寄存器, 在内存寻址时存放基地址
ecx 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器
edx 则总是被用来放整数除法产生的余数

跳转指令

传送指令

寄存器立即数

In this tutorial we only care about the integer registers and the xmm registers.

16个 64bit 整型寄存器:

R0  R1  R2  R3  R4  R5  R6  R7  R8  R9  R10  R11  R12  R13  R14  R15
RAX RCX RDX RBX RSP RBP RSI RDI

使用下面的names访问 lowest 32-bits of each register

R0D R1D R2D R3D R4D R5D R6D R7D R8D R9D R10D R11D R12D R13D R14D R15D
EAX ECX EDX EBX ESP EBP ESI EDI

使用下面的names访问 lowest 16-bits of each register

R0W R1W R2W R3W R4W R5W R6W R7W R8W R9W R10W R11W R12W R13W R14W R15W
AX  CX  DX  BX  SP  BP  SI  DI

使用下面的names访问 lowest 8-bits of each register

R0B R1B R2B R3B R4B R5B R6B R7B R8B R9B R10B R11B R12B R13B R14B R15B
AL  CL  DL  BL  SPL BPL SIL DIL

由于历史原因, R0 ~ R3 的 bit15 ~ bit8 被命名:

AH  CH  DH  BH

有16 XMM registers, 每一个 128 bits wide:

XMM0 ... XMM15

理解函数调用过程

1. 从左到右, 参数被传递到寄存器, 传递的顺序是:

    对于整型或指针: rdi, rsi, rdx, rcx, r8, r9
    对于浮点型, xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7

2. 额外的参数 pushed stack 中,从右向左, 在call函数完成后被弹出

3. 在参数 pushed 之后, 开始执行 call 指令, call会将函数的返回地址push到栈中, 所以当被调用的函数获取控制(即进入函数内部), 返回地址是在[rsp]中, 第一块内存(函数体开头)是从[rsp+8]开始

4. 在执行call之前, 栈指针rsp必须被对齐到16个字节, 由于执行call时, 在栈顶中放入返回地址
    所以当一个函数获取控制时, rsp未被分配, 这需要你自己做出额外的空间, 比如: push 一些空间, 或 sub rsp, 8

5. 要求被调用函数保存的寄存器是: rbp, rbx, r12, r13, r14, r15, 其它的寄存器都可以被被调用函数自由更改

6. 被调用者也应该保存XMCSR和x87控制字的控制位, 但是x87指令在64位代码中很少用, 所以你一般不需要担心这个

7. 整形被返回在rax或rdx:rax寄存器中, 浮点型被返回在xmm0 or xmm1:xmm0寄存器中

伪指令

伪指令并不是真正的X86机器指令, 伪指令包括:

DB, DW, DD, DQ, DT, DO, DY and DZ

their uninitialized counterparts RESB

RESW, RESD, RESQ, REST, RESO, RESY and RESZ

the INCBIN command

the EQU command

TIMES prefix

声明初始化变量

DB, DW, DD, DQ, DT, DO, DY and DZ

ps: DT, DO, DY and DZ  do not accept numeric constants as operands
; examples:
    db 0x55 ; just the byte 0x55
    db 0x55,0x56,0x57 ; three bytes in succession
    db ’a’,0x55 ; character constants are OK
    db ’hello’,13,10,’$’ ; so are string constants
    dw 0x1234 ; 0x34 0x12
    dw ’a’ ; 0x61 0x00 (it’s just a number)
    dw ’ab’ ; 0x61 0x62 (character constant)
    dw ’abc’ ; 0x61 0x62 0x63 0x00 (string)
    dd 0x12345678 ; 0x78 0x56 0x34 0x12
    dd 1.234567e20 ; floating−point constant
    dq 0x123456789abcdef0 ; eight byte constant
    dq 1.234567e20 ; double−precision float
    dt 1.234567e20 ; extended−precision float

声明未初始化的变量

RESB, RESW, RESD, RESQ, REST, RESO, RESY and RESZ 用于一个module的BSS段, 声明未初始化的存储空间

定义常量

message db ’hello, world’
msglen equ $-message

ps: '$'被确定在第一次定义的时候, 而不是在引用的时候

TIMES: 重复指令或数据

既可以用于常量, 也可以用于表达式

zerobuf: times 64 db 0

buffer: db ’hello, world’
        times 64−$+buffer db ' '

times 100 resb 1 and resb 100, 汇编组装时间不同, 功能相同

有效地址


wordvar dw 123
        mov ax, [wordvar]
        mov ax, [wordvar+1]
        mov ax, [es:wordvar+bx]

        mov eax, [ebx*2+ecx+offset]
        mov ax, [bp+di+8]

        mov ax, [dword eax+3]
        mov ax, [byte eax+offset]

INCBIN: 包含额外的二进制文件

incbin "file.dat" ; include the whole file
incbin "file.dat",1024 ; skip the first 1024 bytes
incbin "file.dat",1024,512

条件指令

在算术或逻辑指令或者比较指令之后, 处理器设置或清除它的状态标志flags, 最有趣的flags是:

s (sign)
z (zero)
c (carry)
o (overflow)

条件指令有三种格式:

j for conditional jump
cmov for conditional move
set for conditional set

比如:

1.  cmp x, y        ; 比较x和y
    je  LABEL       ; 相等就跳转至标号LEVEL
2.  cmovl  x, y     ; 如果x小于y,则 mov x, y
3.  setc x          ; 最后最后一个操作有进位标志"carry", 就x=1, 否则x=0

条件后缀:

s ns z nz c nc o no p np pe po e ne l nl le nle g ng ge nge a na ae nae b nb be nbe

一个简单的程序: 解析命令行参数

@import “test_cmd_args.nasm”

分析c源文件转成masn, 非nasm的过程, 以此学习函数调用过程

此处相关链接: https://eli.thegreenplace.net/2011/02/04/where-the-top-of-the-stack-is-on-x86

对目标文件汇编, 32位: gcc -S -m32 -o z.s disassembly.c

这个程序很到位, 看了很容易懂~

; filename: disassembly.c

int foobar(int a, int b, int c)
{
    int xx = a + 2;
    int yy = b + 3;
    int zz = c + 4;
    int sum = xx + yy + zz;

    return xx * yy * zz + sum;
}

int main()
{
    return foobar(77, 88, 99);
}

_foobar:
    ; ebp must be preserved across calls. Since
    ; this function modifies it, it must be
    ; saved.
    ;
    push    ebp

    ; From now on, ebp points to the current stack
    ; frame of the function
    ;
    mov     ebp, esp

    ; Make space on the stack for local variables
    ;
    sub     esp, 16

    ; eax <-- a. eax += 2. then store eax in xx
    ;
    mov     eax, DWORD PTR [ebp+8]
    add     eax, 2
    mov     DWORD PTR [ebp-4], eax

    ; eax <-- b. eax += 3. then store eax in yy
    ;
    mov     eax, DWORD PTR [ebp+12]
    add     eax, 3
    mov     DWORD PTR [ebp-8], eax

    ; eax <-- c. eax += 4. then store eax in zz
    ;
    mov     eax, DWORD PTR [ebp+16]
    add     eax, 4
    mov     DWORD PTR [ebp-12], eax

    ; add xx + yy + zz and store it in sum
    ;
    mov     eax, DWORD PTR [ebp-8]
    mov     edx, DWORD PTR [ebp-4]
    lea     eax, [edx+eax]
    add     eax, DWORD PTR [ebp-12]
    mov     DWORD PTR [ebp-16], eax

    ; Compute final result into eax, which
    ; stays there until return
    ;
    mov     eax, DWORD PTR [ebp-4]
    imul    eax, DWORD PTR [ebp-8]
    imul    eax, DWORD PTR [ebp-12]
    add     eax, DWORD PTR [ebp-16]

    ; The leave instruction here is equivalent to:
    ;
    ;   mov esp, ebp
    ;   pop ebp
    ;
    ; Which cleans the allocated locals and restores
    ; ebp.
    ;
    leave
    ret

AT&T 汇编基础

说明

  • 本页整理 AT&T 汇编语法中的寄存器, 立即数和寻址方式等基础概念.
  • 适合作为阅读 Linux 下汇编资料和反汇编输出时的入门速记页.

非常实用的AT&T教程 Assembly_In_Linux

寄存器前加 % 立即数前加 $

mov指令

mov的操作指令可用于以下类型的传送:

    立即数传送给通用寄存器
    立即数传送给内存
    通用寄存器传送给另一个通用寄存器
    通用寄存器传送给段寄存器
    段寄存器传送给通用寄存器
    通用寄存器传送给控制寄存器
    控制寄存器传送给通用寄存器
    通用寄存器传送给调试寄存器
    调试寄存器传送给通用寄存器
    内存位置传送给通用寄存器
    内存位置传送给段寄存器
    通用寄存器传送给内存位置
    段寄存器传送给内存位置

索引寻址(变址寻址)

语法: disp(base,index,scale)

即公式:  [base+index*scale+disp]

如:
    Movl 0x20(%ebx),%eax
    Addl (%ebx,%ecx,0x2),%eax
    Leal (%ebx,%ecx),%eax
    Subl -0x20(%ebx,%ecx,0x4),%eax

volatile 笔记

说明

  • volatile 用于告诉编译器: 该对象的值可能在程序显式控制之外发生变化.
  • 常见场景包括内存映射寄存器, 中断共享变量和底层设备访问.
  • 它解决的是“优化过度”问题, 不是“线程同步正确性”问题.

基本含义

  • 每次访问都应尽量直接从内存读取或写回.
  • 编译器不应把它当成普通变量那样随意消除访问或缓存到寄存器中长期复用.

典型场景

内存映射寄存器

  • 访问硬件寄存器时, 寄存器值可能由外设随时改变.
  • 若不加 volatile, 编译器可能优化掉看似“重复”的读取.

中断共享变量

  • 中断处理函数和主循环共享的标志位也常见 volatile.
  • 但如果涉及原子性和顺序要求, 仍要进一步考虑同步方案.

常见误区

误区 1: volatile 等于线程安全

  • 错.
  • volatile 不能替代原子操作, 锁或内存序控制.

误区 2: volatile 能解决所有并发可见性问题

  • 错.
  • 它主要限制编译器优化, 不自动建立完整的线程同步语义.

误区 3: 所有全局变量都该加 volatile

  • 错.
  • 滥用会降低优化效果, 也会掩盖真正的同步设计问题.

使用建议

  • 面向硬件寄存器和底层设备访问时, volatile 很常见.
  • 面向多线程同步时, 优先使用原子类型和同步原语.
  • 读代码时, 一旦看到 volatile, 应先问“这个值是谁在程序控制外改动它”.

相关文档

Windows API

说明

  • 本页整理 Windows API 的学习入口和几个高频概念.
  • 当前重点是窗口消息循环以及 SendMessage / PostMessage 的差异.

官方学习入口

消息循环的基本过程

基本过程是:

  1. 操作系统在消息队列中放入窗口消息, 例如 WM_LBUTTONDOWN.
  2. 程序调用 GetMessage 获取消息.
  3. 消息被填充到 MSG 结构中.
  4. 程序调用 TranslateMessageDispatchMessage.
  5. 系统进入窗口过程 WndProc 处理该消息.
  6. 窗口过程可以响应消息或忽略它.

SendMessage / PostMessage

参考:

核心区别:

  • SendMessage: 发送后等待处理结果, 更像同步调用.
  • PostMessage: 只把消息投递到队列, 不等待处理结果.

适用场景:

  • 需要立即获得处理结果时, 更适合 SendMessage.
  • 只想异步通知目标窗口时, 更适合 PostMessage.

使用建议

  • 做窗口自动化或消息模拟时, 先确认窗口句柄和目标消息类型是否正确.
  • 涉及跨线程或跨进程消息时, 要注意同步阻塞和权限问题.
  • 若后续继续整理, 可补 CreateWindow, WndProc, SetWindowLongPtr 和窗口枚举相关笔记.

Windows 异步通知 IO 模型与重叠 IO 模型

说明

  • 本页用于区分同步 IO, 异步 IO, 以及 Windows 场景下常被一起讨论的通知模型与重叠 IO.
  • 当前内容更偏概念速记和图示理解, 还没有展开到 IOCP 等更完整的工程化实现.

参考

  • https://www.cnblogs.com/weekbo/p/9875756.html
  • https://zhuanlan.zhihu.com/p/36344554

同步 IO 与 异步 IO

对设备的读写都可以看做是对文件的读写, 对文件读写一般都需要经过内核态和用户态的切换.

同步 IO 的特点:

  • 用户进程触发 I/O 操作后, 需要等待或轮询 I/O 是否就绪.
  • I/O 操作的发起者同时承担了等待和配合拷贝的职责.
  • 因为涉及从内核态到用户态的数据拷贝, 所以调用方通常会感受到阻塞.

异步 IO 的特点:

  • 用户进程触发 I/O 操作后立即返回, 可以继续处理自己的逻辑.
  • 内核在整个 I/O 完成后再通知用户进程.
  • 数据由内核负责从内核态拷贝到用户态缓冲区, 调用方不需要在等待阶段持续阻塞.

异步 IO 工作机制

核心思路是: 告知内核启动某个操作, 并让内核在整个操作完成后通知应用.

例如调用 aio_read 这类 POSIX 异步 I/O 接口时, 需要给内核传递描述符, 缓冲区指针, 缓冲区大小和文件偏移, 并指定操作完成后的通知方式. 调用返回后, 进程在等待 I/O 完成期间不需要被阻塞.

异步 IO 与同步 IO 对比

备注

  • 如果继续深入 Windows 网络与高并发模型, 通常还需要继续看 Overlapped I/O, Completion Port, WSARecv / WSASend 等 API 组合方式.

Qt 总览

说明

  • 本目录收录 Qt 基础安装, QtUsb, Qwt 以及历史 MXSpice 示例工程等内容.
  • 如果你只是处理日常 Qt 桌面开发问题, 优先看 Qt 学习笔记.
  • 如果问题已经细分到设备访问或工程图表, 再进入对应专题页.

阅读路径

基础开发

  • Qt 学习笔记
  • 适合查环境安装, 源码编译, 中文输入等常见问题.

设备接入

  • QtUsb
  • 适合做 USB 设备枚举和通信的快速验证.

图表与工程控件

  • Qwt
  • 适合做实时曲线, 仪表盘和工程类可视化界面.

历史示例工程

选型建议

  • 只需要标准 Widget 桌面界面时, 先从 Qt 学习笔记 开始.
  • 只需要 USB 访问能力时, 不一定非要引入完整封装库, 也可先验证底层 libusb.
  • 只需要简单图表时, 也可同时评估 Qt Charts 或自绘方案, 不必一开始就上 Qwt.

目录边界

  • 本目录聚焦 Qt Widget 生态及其相关扩展库.
  • 更偏语言基础, 编译器和系统 API 的内容, 应回到上层 C 与 C++ 总览.
  • 历史示例工程保留为参考资料, 不作为首选学习入口.

Qt 学习笔记

说明

  • 本页整理 Qt 在 Linux 场景下的安装与基础环境配置记录.
  • 当前重点是 Ubuntu 下安装 Qt Creator, 从源码编译 Qt 5.15, 以及中文输入问题的处理.

Ubuntu 安装 Qt

sudo apt-get install build-essential g++
sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev

Qt Creator 下载地址:

设置环境变量:

export QTDIR=$HOME/programs/Qt5.14.0/5.14.0/gcc_64
export PATH=$QTDIR/bin:$PATH

无法输入中文

cp /usr/lib/x86_64-linux-gnu/qt5/plugins/platforminputcontexts ~/programs/Qt5.14.0/Tools/QtCreator/lib/Qt/plugins/platforminputcontexts/

Ubuntu 源码编译 Qt 5.15

下载源码:

准备依赖:

sudo apt install clang llvm libfontconfig1-dev libfreetype6-dev libx11-dev libx11-xcb-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libxcb-glx0-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev libxcb-xinerama0-dev libxkbcommon-dev libxkbcommon-x11-dev

配置与编译:

./configure -opensource -confirm-license -skip qtlocation -skip qtvirtualkeyboard
make -j10
sudo make install

可选裁剪记录:

-skip qtlocation -skip qtwayland -skip qtscript

使用建议

  • 如果只是做常规桌面开发, 优先评估系统包或官方安装器, 通常会比手工源码编译更省心.
  • 真正需要源码编译时, 先明确是否要裁剪模块, 再一次性准备好依赖.
  • Qt Creator 中文输入异常时, 优先检查输入法插件目录和系统输入法环境.
  • 若后续继续整理, 可补 qmake / cmake, 信号槽, UI Designer 和部署打包相关笔记.

QtUsb

说明

  • QtUsb 是一个把 USB 访问封装到 Qt 风格接口中的第三方库.
  • 适合在 Qt 应用里快速接入基于 libusb / hidapi 的设备访问能力.

适用场景

  • 在 Qt 桌面程序中做 USB 设备枚举和基础通信.
  • 需要用 Qt 信号槽和对象模型封装底层设备访问逻辑.
  • 想先快速验证 USB 访问能力, 再决定是否直接下沉到原生 libusb 层.

获取源码

git clone https://github.com/fpoussin/QtUsb.git

安装依赖

sudo apt install libusb-1.0-0-dev libhidapi-dev

编译方式

  • 在 Qt Creator 中导入项目.
  • 解析依赖后直接编译即可.

常见注意点

  • 若目标平台不同, 还要进一步核查底层 libusb / hidapi 的可用性和设备权限.
  • Linux 下如果设备无法访问, 优先检查 udev 规则.
  • 如果只是验证底层 USB 能否工作, 也可以先用更直接的 libusb 示例做独立排查.

Qwt

说明

  • Qwt 是 Qt 生态中常见的绘图与仪表盘扩展库.
  • 适合做曲线图, 仪表盘, 标尺和工程类数据可视化控件.

适用场景

  • 需要在 Qt 桌面应用中展示实时曲线或工程图表.
  • 需要比原生 Qt Widget 更偏工程化的仪表盘和标尺控件.
  • 需要在 Qt Designer 中直接拖拽使用第三方图形控件.

参考

安装 Qwt

svn checkout https://svn.code.sf.net/p/qwt/code/trunk qwt-code
cd qwt-code/qwt
qmake
make -j10
sudo make install

添加到 Qt Designer

cp /usr/local/qwt-6.3.0-svn/plugins/designer/libqwt_designer_plugin.so Qt5.14.0/Tools/QtCreator/lib/Qt/plugins/designer

常见注意点

  • 不同 Qt 版本的插件目录可能不同, 使用前需先确认本地 Qt Creator 安装路径.
  • 若插件无法显示在 Designer 中, 先检查 Qt 版本, 插件 ABI 和安装目录是否匹配.
  • 若只是做简单图表, 也可以先评估是否使用 Qt Charts 或直接绘制自定义控件.

SPICE 仿真总览

说明

  • 本目录收录 ngspice, LTspice 和简单电路网表示例.
  • 若目标是先跑通命令行仿真, 可优先看 ngspiceex01; 若需要图形界面操作, 再看 LTspice.
  • 相关实验代码也分布在 ../qt/MXSpice/.

适用场景

  • 验证基础模拟电路的瞬态响应, 频率响应和直流工作点.
  • 快速比较不同参数组合对电路行为的影响.
  • 在实际打样前先做一轮低成本电路行为预演.

常用入口

建议阅读路径

  1. 先看 SPICE 示例 ex01, 熟悉最小网表结构.
  2. 若习惯命令行流程, 继续看 ngspice.
  3. 若需要原理图界面和交互式波形查看, 再看 LTspice 笔记Multisim 瞬态仿真收敛问题.

常见关注点

  • 模型是否匹配实际器件, 往往比求解器设置更关键.
  • 注意单位前缀, 初始条件和激励源定义是否正确.
  • 仿真结果只代表模型条件下的行为, 最终仍要回到真实器件和 PCB 验证.

ngspice笔记

linux 编译 ngspice

下载code:
    https://sourceforge.net/projects/ngspice/

mkdir release && cd release
../configure  --with-x --with-readline=yes --disable-debug

源码编译

sudo apt install bison flex automake libtool libxaw7-dev libreadline-dev

git clone https://git.code.sf.net/p/ngspice/ngspice

cd ngspice

./autogen.sh

mkdir -p debug && cd debug

../configure --with-ngshared --enable-cider --enable-xspice  --with-x --enable-stepdebug --enable-cpdebug

make -j4
sudo make install

cd ..
ngspice
source examples/soi/inv_tr.sp


如果发生错误: Undefined symbol error 'hcomp' when using libngspice.so
可能是这有的操作:
    Clean folder -> normal build -> shared lib build -> problem occurs.
请考虑, 应该可以解决:
    Clean folder -> shared lib build -> OK.

源码分析

var_alloc(char *name, struct variable *next)

void cp_vset(char *varname, enum cp_types type, void *value);

    设置一个参数列表: struct variable *variables; varname是不重复的


ngSpice_Init():

    cp_vset("rndseed", CP_NUM, 1);

    cp_vset("sharedmode", CP_BOOL, True);

    ft_cpinit(): Set some standard variables and aliases, etc, and init the ccom stuff
        cp_init(); Initialize io, cp_chars[], variable "history" in init.c
        cp_coms: 即所有的命令, 比如: print, plot, load, tran, op


ngSpice_Circ(netlist) 根据网表创建电路
    create_circbyline()
        更新circarray
        更新完后执行inp_spsource():




struct CKTcircuit: 电路结构体

struct circ

LTspice 笔记

说明

  • LTspice 适合用图形化方式快速搭建模拟电路并查看波形.
  • 若你已经有网表并倾向命令行流程, 可优先看 ngspice; 若更习惯拖拽元件和交互式调参, 则优先使用 LTspice.

适用场景

  • 快速验证 RC, 运放, 开关电源等模拟电路的基础行为.
  • 边画原理图边观察波形, 适合调参数和做直观对比.
  • 需要先在 GUI 中理解电路关系, 再逐步迁移到更复杂的仿真流程.

常用快捷键

  • 旋转元件: Ctrl + R
  • 镜像元件: Ctrl + E
  • 复制元件: Ctrl + 拖拽
  • 计算功耗: 按住 Alt, 鼠标移动到目标器件上.

最小使用流程

  1. 新建原理图并放置电压源, 电阻, 电容等元件.
  2. 使用 Wire 把器件正确连接, 并放置地 GND.
  3. 通过 Simulate 配置瞬态, 交流或直流扫描分析命令.
  4. 运行仿真后, 点击节点查看电压波形, 点击器件查看电流或功耗.
  5. 若结果异常, 先检查地是否存在, 元件数值是否合理, 以及仿真命令是否匹配目标场景.

常见排查点

  • 没有地时, 很多电路根本无法正常求解.
  • 波形不对时, 优先检查元件单位前缀, 例如 m, u, n, k, Meg.
  • 开关电源或非线性器件场景下, 先用较简单模型跑通, 再逐步引入复杂模型.
  • 若只是验证网表逻辑, 也可以回到 SPICE 示例 ex01ngspice 做交叉对照.

相关页面

SPICE 示例 ex01

说明

  • 通过一个最简单的分压电路演示 ngspice 的基本加载方式.
  • 适合用于快速验证网表语法和仿真命令是否可用.

电路目标

  • 输入一个直流电压源.
  • 用两个电阻组成最小分压网络.
  • 观察中间节点电压是否符合预期.

电路示意

运行

ngspice ex01.cir

关键网表

* title: simple circuit
.global gnd
v1 a 0 100v
r1 a b 10
r2 b 0 10
.end

如何理解结果

  • 节点 b 位于两个相等电阻中间, 理论上应得到一半电压.
  • 这个例子若能跑通, 说明最小网表格式, 电源, 电阻和求解流程都基本正常.

调试提示

  • 启动后可使用 listing 查看已加载网表.
  • 如果这个例子可以跑通, 再继续扩展更多器件和分析指令会更稳妥.
  • 若运行失败, 优先检查文件路径, 网表语法和 ngspice 是否正确安装.

Tesseract OCR

说明

  • 本页记录 Tesseract OCR 的构建入口和接入时需要关注的依赖.
  • 适合在 Windows 或本地原生项目中回忆“从源码到可用”的最小路线.

参考资料

构建关注点

  • Tesseract 通常还依赖 Leptonica.
  • Windows 下经常需要同时确认 CMake, 编译器工具链和第三方库路径.
  • 若使用包管理器, 可优先评估 vcpkg 这类更省事的方式.

常见接入流程

  1. 先准备 TesseractLeptonica 所需依赖.
  2. 确认 CMake 或 IDE 工程可以找到头文件和库文件.
  3. 完成最小命令行识别验证.
  4. 再接入自己的桌面程序或后端服务.

Windows 侧排查思路

  • 编译期失败: 先检查编译器版本和依赖库路径.
  • 运行期失败: 再检查动态库是否可被正确加载.
  • 识别效果差: 再回到训练数据, 输入图像质量和语言包配置.

后续可补主题

  • vcpkg 安装记录.
  • CMake 示例工程.
  • 语言数据文件放置路径.
  • 与 GUI 或批处理程序的集成示例.

Java 总览

说明

  • 本目录收录 Java 开发环境, 构建工具和基础项目流程相关记录.
  • 当前内容以本地环境搭建和 Maven 使用为主.

当前文档

建议阅读路径

  1. 初次搭建环境时, 先看 Java 开发环境.
  2. 先确认 JDK, JAVA_HOME, PATHMaven 均可正常工作.
  3. 真正进入项目后, 再补依赖管理, 打包, 调试和编码规范相关笔记.

后续可补主题

  • Gradle 使用记录.
  • Spring Boot 项目初始化.
  • 日志, 配置和测试结构约定.

Java 开发环境

说明

  • 本页整理本地 Java 开发环境搭建的最小步骤.
  • 当前重点是 JDK + Maven 的安装, 验证和镜像配置.

JDK 下载

选择建议:

  • 只做通用开发时, 先选 LTS 版本.
  • 团队项目要优先跟随项目已锁定的 JDK 主版本.

环境变量

常见配置项:

  • JAVA_HOME: 指向 JDK 根目录.
  • PATH: 需要包含 JAVA_HOME/bin.
  • JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8: 可减少默认编码不一致问题.

验证命令

java -version
javac -version

如果以上命令不可用, 优先检查 JAVA_HOMEPATH.

Maven

示例路径:

E:/programs/apache-maven-3.9.2/bin

验证命令:

mvn -v

Maven 镜像配置

修改 settings.xml:

<mirror>
    <id>aliyunmaven</id>
    <mirrorOf>*</mirrorOf>
    <name>阿里云公共仓库</name>
    <url>https://maven.aliyun.com/repository/public</url>
</mirror>

适用场景:

  • 国内网络环境下提升依赖下载速度.
  • 新机器初始化时减少首次拉取依赖的失败率.

最小项目验证

mvn archetype:generate
mvn package

建议至少完成一次“创建项目 -> 编译 -> 打包”闭环验证.

常见问题

  • javajavac 版本不一致: 说明环境变量可能指向了不同安装目录.
  • 中文乱码: 优先检查项目编码, 终端编码和 JAVA_TOOL_OPTIONS.
  • mvn 无法下载依赖: 先确认网络, 再检查镜像源和 settings.xml 位置.

JeecgBoot 环境与启动记录

说明

  • 本页记录 JeecgBoot 项目在本地环境中的前端依赖安装, Nacos 配置和后端启动流程.
  • 适合作为 Java 单体 / 微服务项目初始化和运行链路的历史实践记录.

安装 nodejs

https://npm.taobao.org/mirrors/node/latest-v15.x/node-v15.3.0-linux-x64.tar.xz

解压, 创建链接文件:
    sudo ln -s /develop/programs/node-v15.3.0-linux-x64/bin/node /usr/local/bin/
    sudo ln -s /develop/programs/node-v15.3.0-linux-x64/bin/npm /usr/local/bin/

taobao 镜像

npm config set registry https://registry.npm.taobao.org
npm config get registry

安装yarn

sudo npm install -g yarn
yarn config set registry https://registry.npm.taobao.org

前端项目初始化

# 下载依赖
yarn install

# 启动
yarn run serve

# 编译项目
yarn run build

# Lints and fixes files
yarn run lint

后端项目配置 – JeecgBoot 单体升级微服务启动

微服务通过 Nacos 实现服务注册发现和统一配置中心

docker  pull nacos/nacos-server
docker run -d -p 8848:8848 --env MODE=standalone  --name nacos  nacos/nacos-server

访问Nacos:
    localhost:8848/nacos
    账号: nacos/nacos


创建 jeecg.yaml, 文件存放路径:jeecg-boot-starter/jeecg-boot-starter-cloud/nacos/jeecg.yaml
创建 jeecg-dev.yaml, 文件存放路径:jeecg-boot-starter/jeecg-boot-starter-cloud/nacos/jeecg-dev.yaml

jeecg-dev.yaml 存储经常要修改的配置,一般需要个性化修改的如下:

    1、数据库的连接池修改
    2、redis 配置
    3、rabbitmq 配置
    4、xxljob 配置
    5、路由配置route 加载方式
    6、是否开启xxljob (默认不启用)

启动jeecg-cloud-system-start服务

将 jeecg-boot-module-system/pom.xml 中的 jeecg-boot-module-demo 依赖注释掉 (没有demo依赖可忽略此步骤)

修改pom文件中spring-boot-maven-plugin 打包插件configuration.skip=true

log cannot be resolved

手动安装 lombok

    https://projectlombok.org/download
    
    下载 https://projectlombok.org/downloads/lombok.jar

    用法说明:
        https://projectlombok.org/setup/eclipse
        https://blog.51cto.com/4925054/2127840

通过 eclipde 的 url 安装 lombok

    https://projectlombok.org/p2

后端项目启动

项目结构说明:
    ├─jeecg-boot-parent(父POM: 项目依赖、modules组织)
    │  ├─jeecg-boot-base-common(共通Common模块: 底层工具类、注解、接口)
    │  ├─jeecg-boot-module-system (系统管理模块: 系统管理、权限等功能) -- 默认作为启动项目
    │  ├─jeecg-boot-module-{?} (自己扩展新模块项目,启动的时候,在system里面引用即可) 

初始化数据库,要求mysql5.7+

-- 创建mysql库
create database `jeecg-boot` default character set utf8mb4 collate utf8mb4_general_ci;

-- 手工执行Sql脚步
source db/jeecgboot-mysql-5.7.sql

修改项目配置文件 (数据库配置、redis配置)

配置文件: jeecg-boot-module-system/src/main/resources/application-dev.yml
项目名称、端口号配置 (可以不改):
默认配置——  端口号是8080,项目名称是jeecg-boot

server:
    port: 8080
    servlet:
    context-path: /jeecg-boot

数据库配置:

    spring:
        datasource:
            dynamic: 
            datasource: 
                #主数据源
                master: 
                    url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false
                    username: root
                    password: root
                    driver-class-name: com.mysql.jdbc.Driver

Redis配置(配置redis的host和port):

    #redis 配置
    redis:
        database: 0
        host: 127.0.0.1
        lettuce:
        pool:
            max-active: 8   #最大连接数据库连接数,设 0 为没有限制
            max-idle: 8     #最大等待连接中的数量,设 0 为没有限制
            max-wait: -1ms  #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
            min-idle: 0     #最小等待连接中的数量,设 0 为没有限制
        shutdown-timeout: 100ms
        password: ''
        port: 6379

启动项目

/jeecg-boot-module-system/src/main/java/org/jeecg/JeecgSystemApplication.java
Run As "Java Application"

机器人技术总览

说明

  • 本目录聚焦机器人相关的感知, 控制, 通信和软件框架笔记.
  • 当前内容仍偏实验记录, 适合作为后续按主题拆分专题页的入口.

关键模块

  • 感知: 激光雷达, 超声波, 摄像头和状态检测.
  • 运动控制: 电机控制, 差分驱动, 姿态调整和执行机构联动.
  • 通信: UART, SPI, I2C, CAN 等设备互联链路.
  • 交互: 语音输入, 语音转文字, 灯带状态反馈等人机协作接口.

当前入口

建议阅读路径

  • 先看 机器人技术总览, 明确问题是感知, 控制, 通信还是中间件.
  • 做机器人中间件和节点协同时, 优先看 ROS2Dora 向导.
  • 若问题退化为串口, 总线, 电机驱动或嵌入式接线, 回到 embedded/ 继续查找.

目录边界

  • 本目录主要保留机器人系统层与中间件层知识.
  • 更底层的硬件接线, 外设时序和 MCU 驱动不在这里展开.

后续整理建议

  • 若后续内容增多, 可继续拆出 感知, 运动控制, 导航定位, 语音交互, 机器人中间件 等专题.

ROS2

说明

  • 本页记录 ROS2 的 Windows 安装后验证和常见环境问题.
  • 适合作为“先把环境跑通”的最小入口页.

参考资料

基本安装后验证

  • 配置好环境变量后, 先加载 local_setup.ps1.
  • 使用以下命令验证环境:
ros2 run demo_nodes_py talker

若终端能正常启动示例节点, 说明基础运行时已基本可用.

建议的最小检查项

  1. 确认 Python, Visual C++ 运行库和 ROS2 本体均已安装.
  2. 确认当前终端已加载 ROS2 的环境脚本.
  3. 先跑 demo_nodes_pydemo_nodes_cpp 做最小验证.
  4. 再进入工作区, 包创建和节点通信实验.

常见问题

failed to load any RMW implementations

可尝试安装 openssl 1.1.1:

choco install -y openssl --version 1.1.1.2100

排查方向:

  • 检查 RMW 实现是否正确安装.
  • 检查环境变量是否在当前终端生效.
  • 检查依赖库版本是否与当前 ROS2 发行版兼容.

后续可补主题

  • 工作区创建与 colcon build.
  • 包管理和依赖声明.
  • topic, service, action 的最小示例.
  • 常见中间件配置与跨机通信验证.

Dora 向导

说明

  • 本页记录 dora-rs 的安装方式与最小数据流项目创建流程.
  • 适合作为机器人数据流编排框架的快速上手入口.

dora 向导

https://dora-rs.ai/docs/guides/

安装 dora

这里使用 git release 的方式

https://github.com/dora-rs/dora/releases

下载对应平台, 解压 并设置环境变量 即可

RUST 语言

创建基本项目

dora new test_dora --kind dataflow

会自动创建 3 个节点 talker_1 talker_2 listener_1

cd test_dora

编译 3 个节点:

dora build .\dataflow.yml

运行这 3 个节点:

dora run .\dataflow.yml

python 语言

https://dora-rs.ai/docs/guides/getting-started/conversation_py

前端总览

说明

  • 本目录收录前端框架, 构建工具, 可视化, 样式与工程实践相关笔记.
  • 旧的示例工程和历史脚手架目录主要保留在 program/web/, 以便与知识文档分离.
  • 若只是检索知识点, 优先从本目录进入; 若要查看历史示例, 再进入 Web 示例项目归档.

阅读路径

  • 框架问题优先看 Vue, React, Svelte.
  • 构建链问题优先看 Vite, UnoCSS, Typescript.
  • 图表与可视化需求优先看 D3, ECharts, 前端图表工具.
  • 若要查看历史脚手架差异和旧实验工程, 再进入 program/web/ 归档目录.

框架与应用

构建与样式

图表与工具库

历史示例与归档

目录边界

  • frontend/ 主要保留正式知识页与专题页.
  • program/web/ 主要保留历史示例工程, 目录说明和兼容入口.

web

文档整理说明

  • 本页保留 Web 通用笔记.
  • 历史 Web 总页中与 Node.js / Yarn 环境相关的内容已合并到 Node.js 笔记.

使用 flex 中间元素铺满

js 对象展开运算符

state.obj = { ...state.obj, newProp: 123 }

typescript 模块解析

typescript 模块解析

使用 nvm 管理 nodejs

网址: https://github.com/nvm-sh/nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
nvm install --lts

npm config set registry https://registry.npm.taobao.org
npm config get registry

npm install -g yarn
yarn config set registry 'https://registry.npm.taobao.org'

生成tsconfig.json文件

yarn add -D typescript
yarn tsc --init

使用npm安装cordova, 执行cordova create报错

npm ERR! not a package.........@cordova-app-hello-world\index.js
卸载cordova, 使用yarm重新安装即可:
    sudo npm uninstall -g cordova
    sudo npm install -g yarn
    sudo yarn config set registry https://registry.npm.taobao.org
    sudo yarn global add cordova

package.json 版本约定号

~version

大概匹配某个版本

如果minor版本号指定了, 那么minor版本号不变, 而patch版本号任意

如果minor和patch版本号未指定, 那么minor和patch版本号任意

如:~1.1.2, 表示>=1.1.2 <1.2.0, 可以是1.1.2, 1.1.3, 1.1.4, ....., 1.1.n

如:~1.1, 表示>=1.1.0 <1.2.0, 可以是同上

如:~1, 表示>=1.0.0 <2.0.0, 可以是1.0.0, 1.0.1, 1.0.2, ....., 1.0.n, 1.1.n, 1.2.n, ....., 1.n.n


^version

兼容某个版本

版本号中最左边的非0数字的右侧可以任意

如果缺少某个版本号, 则这个版本号的位置可以任意

如:^1.1.2 , 表示>=1.1.2 <2.0.0, 可以是1.1.2, 1.1.3, ....., 1.1.n, 1.2.n, ....., 1.n.n

如:^0.2.3 , 表示>=0.2.3 <0.3.0, 可以是0.2.3, 0.2.4, ....., 0.2.n

如:^0.0, 表示 >=0.0.0 <0.1.0, 可以是0.0.0, 0.0.1, ....., 0.0.n

jquery

prop attr的区别:
    在这里, 我们可以将attribute理解为“特性”, property理解为为“属性”从而来区分俩者的差异。
    举个例子, option当做一个对象:
    var option = {
        selected:false,
        disabled:false,
        attributes:[],
        ...
    }
    attributes是自定义的键值对, property是对象自身的属性:selected、disabled、attributes...

## 报错:sh: 1: tsc: not found

需要安装typescript, 执行:sudo npm install -g typescript

chrome插件开发

[an example](https://developer.chrome.com/extensions/getstarted/ "略略")

div层调整zindex属性无效原因分析及解决方法

这种情况发生的条件有三个:

1、父标签 position属性为relative

2、问题标签无position属性(不包括static)

3、问题标签含有浮动(float)属性

eg:z-index层级不起作用, 浮动会让z-index失效


子元素浮动,父div没有自适应
clear:both 不管用?
给父元素添加: overflow:hidden

一个div内的多个同级div, 同级的div在同一行, 怎么保证它们高度一致?
margin-bottom


1,在当前窗口中跳转:
    window.location.href('url') 存在兼容问题,火狐不支持,可以使用 window.location='url';

    history.back();返回

2,学会使用data*属性,这是属于html5的标签属性, 传递参数时可以在路径中加上?key1=value1&key2=value2的形式

3,onbar事件
    使用taglib prefix ="c", 循环

4,使用锚点:
    <a name="anchor1"/>.........<a target="#anchor1"/>


软件 Artisteer firework ps 美图秀秀

textContent与innerHTNL的区别:textContent返回元素及其后代的文本内容,而innerHTML则返回HTML

ie和火狐遍历子节点chileNodes的区别:ie没有空格,火狐带空格

ul中的li居中:li的默认display为block,设置li为inline


对于浮动元素的高度为0,可以通过设置:如下:
    ul:after {
    content: "";
    display: table;
    clear: both;
    }


动画滚动:
$msg_container.animate({ scrollTop: $msg_container.prop("scrollHeight") });


/**
* 禁用右键菜单
*/
document.oncontextmenu = function(){
    event.returnValue = false;
};

/**
* 禁用选中功能
*/
document.onselectstart = function(){
    event.returnValue = false;
};

/**
* 禁用复制功能
*/
document.oncopy = function(){
    event.returnValue = false;
};

/**
* 禁用鼠标的左右键
*/
document.onmousedown = function(){
    if(event.which==1){//鼠标左键
        return false;
    }

    if(event.which==3){//鼠标右键
        return false;
    }
};

/**
* 获取键盘上的输入值
*/
document.onkeydown = function(){
    console.info(event.which);
    if(event.which==13){
        console.info("回车键");
    }
};

设置页面居中

background:url(\${sb.imageUrl }) no-repeat; background-size:150px 100px; background-position: center;

div居中:
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%); // 偏移自身的50%

指定宽度 剩余铺满

<div style="display: flex;">
    <div style="flex-grow: 1;"></div>
    <div style="width: 300px;"></div>
</div>

Node.js 笔记

国内源

npm config set registry https://registry.npmmirror.com

Linux 环境安装

sudo apt-get remove nodejs --purge

# https://github.com/nodesource/distributions
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs

node -v
npm -v

sudo npm install -g yarn
yarn config set registry 'https://registry.npmmirror.com'

npm

配置文件 $HOME/.npmrc
查看所有配置:
    npm config ls -l

查看包版本

npm ls electron     本地包版本号
npm ls electron -g  全局包版本号

yarn

yarn config list

常见用法

yarn global add typescript

# 前端构建工具
sudo yarn global add grunt-cli

ISSUE: You need to have Ruby and Sass installed and in your PATH for this task to work.
SOLVED: yarn add --save node-sass grunt-sass

工具

yarn global add npm-check
npm-check -u

electron

npm config set ELECTRON_MIRROR http://npm.taobao.org/mirrors/electron/ npm install –save-dev electron

vue 笔记

使用 Vite 工具

vite 文档

创建项目

yarn create @vitejs/app

历史脚手架: vue-cli

在较老的项目里, 也可能会看到基于 vue-cli 的初始化方式:

yarn global add vue-cli
vue init webpack vue-app

如果是新项目, 优先使用 Vite 方案.

vuejs

  1. 如果一个数据依赖于其他数据, 那么把这个数据设计为 computed.
  2. 如果你需要在某个数据变化时做一些事情, 使用 watch 来观察这个数据变化.

React 笔记

文档整理说明

  • React 主笔记已统一收敛到当前路径.
  • Node.js / npm / yarn 的基础环境说明统一整理在 Node.js 笔记.

历史脚手架: Create React App

较老的 React 项目里经常会看到 Create React App 方案:

sudo npm install -g create-react-app
create-react-app my-app

也可以直接使用:

npx create-react-app react-app

常见依赖

npm install react-redux uuid --save

入门资料

学习自 超全面详细一条龙教程!从零搭建React项目全家桶

使用 xc-app 快速搭建 Vite + Rust WASM 项目

yarn create xc-app
cd PROJECT_NAME
yarn
yarn dev

Svelte

说明

  • 本页整理 SvelteSvelteKit 的历史初始化记录与基础响应式要点.
  • 适合作为快速回忆“如何起项目”和“响应式语法怎么写”的入口页.

初始化项目

组件库模板

npx degit sveltejs/component-template my-new-component
cd my-new-component
yarn

SvelteKit 项目

yarn create svelte my-app
cd my-app
yarn
yarn dev -- --open

说明:

  • 以上命令属于当前知识库中的历史记录.
  • 具体脚手架命令可能随版本变化, 真正执行前应再核对官方文档.

集成 PixiJS

参考: https://svelte-pixi.com/docs/getting-started/creating-a-project

yarn add pixi.js svelte-pixi

适用场景:

  • Svelte 中快速接入 2D 图形或交互画布能力.
  • 用响应式状态驱动渲染参数变更.

生命周期与响应式

  • onMount: DOM 已挂载后执行.
  • beforeUpdate / afterUpdate: 组件更新前后触发.
  • $: 语句块可自动追踪依赖并重新计算.
$: {
    console.log('resized', resized)
}

使用建议

  • 页面较复杂时, 把状态, 视图和副作用拆到独立模块.
  • 第三方图形库接入时, 优先把实例化逻辑放到 onMount.
  • 若后续继续整理, 可补 store, layout, 路由SSR 相关专题.

electron+neon

neon

getting-started

Install the Neon CLI

Unix 依赖:
    Python 2.7 (Python 3 is not supported)
    make
    A proper C/C++ compiler toolchain, like GCC
安装:
    npm install --global neon-cli

验证:
    neon version

实践neon

# 创建项目
neon new thread-count

# 编译
cd thread-count
neon build --release

# 验证
node
> require('.')
hello node
{}
>

# 清理
neon clean

打包

方法1:

npm install electron-packager -g
electron-packager .

方法2:

electron-builder

使用 vue

npm install -g @vue/cli

rust+neon+electron

新建:
    neon new rust-api
    cd rust-api
    npm link

添加一个 electron basic
git clone https://github.com/electron/electron-quick-start
cd electron-quick-start

npm install rust-api
npm install electron-build-env neon-cli --save-dev

添加:
"scripts": {
    "run": "npm run build && npm run start",
    "start": "electron .",
    "build": "electron-build-env neon build rust-api",
    "release": "electron-build-env neon build rust-api --release"
}

    ps: 注意这里的"build"参数: rust-api

npm run

electron vue

electron-vue

添加vue-cli:
    npm install -g vue-cli

使用vue-cli创建新项目:
    vue init simulatedgreg/electron-vue electron-app

Error: Unable to install `vue-devtools`
解决:
    yarn add vue-devtools --dev
    编辑 src/main/index.dev.js:

    import { app, BrowserWindow } from "electron";

    // Install `vue-devtools`
    app.on("ready", async () => {
        // let installExtension = require('electron-devtools-installer')
        // installExtension.default(installExtension.VUEJS_DEVTOOLS)
        //   .then(() => {})
        //   .catch(err => {
        //     console.log('Unable to install `vue-devtools`: \n', err)
        //   })
        await new BrowserWindow.addDevToolsExtension(
            "node_modules/vue-devtools/vender"
        );
    });

运行:
    npm run dev

element-ui

element-ui

mockjs + axios 很香

vue项目中使用mockjs+axios模拟后台数据返回

vue3 webpack

Vue3+Electron整合方式

npm init -y

npm install -D webpack webpack-cli
npm install -D vue@next vue-loader@next @vue@compiler-sfc

npm install -D style-loader sass-loader node-sass css-loader

npm install -D url-loader

过程主要时:
    在 webpack config 中配置怎么解析vue文件,scss文件,scss样式,require路径,
    解析 entry 中指定的文件, output 指定了 输出的文件, 这就是一个打包的过程

webpack.config.js配置:

    const path = require('path')
    const { VueLoaderPlugin } = require('vue-loader')

    module.exports = {
        entry: './app.js',
        output: {
            filename: 'bundle.js',
            path: path.resolve(__dirname, 'dist')
        },
        module: {
            rules: [
                {
                    test: /\.vue$/,
                    loader: 'vue-loader'
                },
                // 普通的 `.scss` 文件和 `*.vue` 文件中的
                // `<style lang="scss">` 块都应用它
                {
                    test: /\.scss$/,
                    use: [
                        'style-loader',
                        'css-loader',
                        'sass-loader'
                    ]
                },
                // 图片加载, base64数据, 如: <img :src="require('@/img/vue_logo.png').default">
                {
                    test: /\.(png|jpg|gif)$/i,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 8192,
                            },
                        },
                    ],
                },
            ]
        },
        plugins: [
            new VueLoaderPlugin()
        ],
        resolve: {
            // 设置import或require时可以使用@作为路径, 如:
            alias: {
                '@': path.resolve('src')
            }
        }
    }

vue3 webpack electron

Vue3+Electron整合方式

npm install -D electron

npm install -D electron-builder

指定 目标环境为: electron-renderer
配置 vue项目的打包输出 以及 electron 的打包输出
在 package.json 中指定electron启动的入口文件 electron 的打包输出的js文件
并添加:
    "scripts": {
        "start": "electron .",
        "build": "./node_modules/.bin/webpack",
        "dist": "electron-builder"
    },
    "postinstall": "electron-builder install-app-deps",
    "build": {
        "files": [
            "./dist/**/*",
            "./index.html"
        ],
        "directories": {
            "output": "package"
        }
    },
"build" 用于 electron builder 需要打包哪些文件 以及 输出到哪里

webpack.config.js 配置:

const path = require('path')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
    mode: 'development',
    target: "electron-renderer",
    entry: {
        "bundle": ["./app.js"],
        "main": ["./main.js"]
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            // 普通的 `.scss` 文件和 `*.vue` 文件中的
            // `<style lang="scss">` 块都应用它
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            },
            // 图片加载, base64数据, 如: <img :src="require('@/img/vue_logo.png').default">
            {
                test: /\.(png|jpg|gif)$/i,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 8192,
                        },
                    },
                ],
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()
    ],
    resolve: {
        // 设置import或require时可以使用@作为路径, 如:
        alias: {
            '@': path.resolve('src')
        }
    }
}

vite-electron 创建 vite + electron 项目

yarn create vite-electron electron-app

使用 electron-vue-next (vite vue3 electron)

# 我使用默认配置
npm init electron-vue-next

electron 默认不能在渲染进程中使用node环境

上下文隔离

import 和 require 区别

在 typescript 使用 import
在 js 中使用 require

dplayer

用于测试的视频

ffmpeg 使用

# 生成 hls格式 视频
ffmpeg -i ./demo.mp4 -hls_time 10 -hls_list_size 0 -f hls test/demo.m3u8

~$ tree test/
test/
├── demo0.ts
├── demo1.ts
├── demo2.ts
├── demo3.ts
├── demo4.ts
├── demo5.ts
├── demo6.ts
└── demo.m3u8

ffmpeg

# 安装 模拟的 video loop设备
https://github.com/umlaeute/v4l2loopback --> make && sudo make install-all, 加载ko并运行

# 强制 format ?
# v4l2loopback-ctl set-caps /dev/video0 "UYVY:640x480"

# 创建新loop设备
sudo v4l2loopback-ctl add -n "loopy doopy" /dev/video7

# 删除设备
sudo v4l2loopback-ctl delete /dev/video7

# 安装 v412-ctl 工具 (video4linux)
sudo apt install v4l-utils

# 当 设备 没有输入时 停留 3000ms 后, 输出 空帧 (但是继续输入, ffplay似乎不动了?)
v4l2-ctl -d /dev/video0 -c timeout=3000

# 超时图片, 取代空帧 ?
v4l2loopback-ctl set-timeout-image -t 3000 /dev/video0 service-unavailable.png

# 查看设备列表
v4l2-ctl --list-devices

# 查看当前摄像头支持的视频压缩格式
v4l2-ctl -d /dev/video0 --list-formats

# 改变分辨率, 同时, 会根据输出文件后缀改变 video format
ffmpeg -i demo.mp4 -vf scale=1280x720 output.mkv

# 输出视频数据到 /dev/video0 设备上
# read video in the real framerate
# input file
# webcams expect raw video
# the same format used by my real one
ffmpeg -re -i pickle-rick.mkv -vcodec rawvideo -pix_fmt yuv420p -framerate 25 -f v4l2 /dev/video0

# 播放 /dev/video0 上的视频数据 (没有声音)
ffplay /dev/video0

# 记录 /dev/video0 设备上的数据 10s, 输出到 output.mkv 文件
ffmpeg -f v4l2 -framerate 25 -video_size 640x480 -i /dev/video0 -t 00:00:10 output.mkv

pkg-config --cflags --libs gstreamer-1.0

PixiJS 笔记

给 stage 绑定事件

PIXI.extensions.remove(PIXI.InteractionManager);

app = new PIXI.Application({
    antialias: true,
    backgroundColor: 0xffffff,
    view: canvas,
});

const { renderer, stage } = app;

renderer.addSystem(EventSystem, "events");

stage.interactive = true;
stage.hitArea = renderer.screen;

stage.addEventListener(
    "wheel",
    (wheel_event: Event) => {
        // e.preventDefault();
    },
    { passive: false }
);

通过 Dom 坐标 映射到 PixiJS view 上

只是简单地通过当前 Dom 坐标减去 Canvas Dom 的位置, 再考虑 resolution 分辨率之后的坐标. 得到这个点的 Canvas 坐标. 这属于全局坐标.

const point = new Point();
this.viewport.options.events.mapPositionToPoint(
    point,
    event.clientX,
    event.clientY
);
return point;

Display Object 的 worldTransform 属性

当前 Display ObjectworldTransform, 是相对于父元素的 transform.

Display Object 的 toLocal / toGlobal 函数

toLocal 用于把世界坐标系转换成局部坐标系, 可以用于判断点击的位置是否在指定物体内.

toGlobal 用于把局部坐标系的坐标转换到世界坐标系.

PixiJS transform

每一个 display object 都有一个 transform 对象.

Transform 对象中主要包含 local transform / world transform / position / scale / pivot / skew 这些信息.

本地坐标系指的是 local transform, 表示的是相对于父对象的平移, 缩放, 旋转.

世界坐标系指的是 world transform, 表示的是相对于 Canvas 的平移, 缩放, 旋转.

每当 display object 的 position / scale / pivot / skew 改变时, 都将引起本地坐标系的变化, 即 local transform 会改变.

同时, 这也将引起世界坐标系, 即 world transform, 发生改变.

Matrix 的 decompose 函数

参考: https://zhuanlan.zhihu.com/p/367163308

应用一个 matrix 到当前的 transform 上, 就是把矩阵中的变换转换成 skew / rotation / scale / position.

但是原来的 transform 的 pivot 会保持不变.

使用点 [pivot.x, pivot.y] 应用该矩阵就得到了 position.

Typescript 的一些用法记录

// 指定 对象数组 的变量类型
let data: { country: string; population: number }[];
// 对于 [undefined, undefined] | [number, number] 这样的数据类型的优雅处理
d3.extent(iPopulation) as [number, number];

默认的严格模式 约束太强, 取消严格模式

在 tsconfig.json 中, 设置: “strict”: false,

vite 中使用 csv

但又遇到错误: Cannot find module ‘data.csv’ or its corresponding type declarations.

yarn add -D @rollup/plugin-dsv

修改 vite.config.js:

import dsv from ‘@rollup/plugin-dsv’

defineConfig({ plugins: [ dsv(), // 添加这个插件 ], })

使用时 需要:

import CSV from ‘../data/data.csv’ let data = CSV as {date: string, close: number}[]

并在 src 目录下放置一个 csv-shim.d.ts 文件, 内容是:

declare module “*.csv” {}

根据条件 忽略字段

{
    pressDrag: true,
    wheel: false, // handled by Wheel plugin below
    ...(IS_READONLY_MODE ? {} : { keyToPress: ['Space'] }),
}

实现 遍历器

class XXX {
    ...

    [Symbol.iterator]() {
        let step = 1
        let ticks = this._ticks
        const length = this._domain.length
        const that = this

        // 根据 ticks数 判断遍历的次数
        if (length > this._ticks) {
            // 计算步长
            step = Math.ceil(length / this._ticks)
        }

        // 计算需要访问的索引列表
        const indexes = []
        for (let i = 0; i < this._ticks; i++) {
            indexes.push(i * step)
        }
        if (this._ticks * step < length) {
            indexes.push(length - 1)
            ticks++
        }

        let index = 0

        return {
        // 先根据 index, 判断是否需要退出, 再 准备数据, 更新index
        next(this) {
            const value = {
            data: that._domain[indexes[index]],
            index: indexes[index],
            //   text: that._tick_format(that._domain[index]),
            } as TickData<DATA>

            return {
            value,
            done: index++ === indexes.length,
            }
        },
        }
  }
}

实现 重载

class XXX {
    ticks();
    ticks(ticks: number);
    ticks(ticks?: number) {
        if (ticks) {
            this._ticks = ticks;
            return this;
        } else {
            return this._ticks;
        }
    }
}

Vite

说明

  • Vite 适合作为现代前端项目的默认开发服务器与构建工具.
  • 其特点是开发阶段启动快, 热更新轻量, 且插件生态较丰富.

常见使用场景

  • 初始化 Vue, React, Svelte 等前端项目.
  • 处理 TypeScript, CSS 和静态资源构建.
  • 通过插件扩展 Markdown, CSV, 图标和自动导入等能力.

引入 CSV 文件

参考: https://github.com/vitejs/vite/discussions/8271

处理思路:

  • 若只是运行时读取数据, 可把 CSV 当静态资源请求.
  • 若希望在构建期直接导入, 通常需要借助对应插件或转换流程.

使用建议

  • 先确认资源是要在构建期内联, 还是运行时单独请求.
  • CSV, Markdown, SVG 等资源, 可优先查对应插件生态.
  • 若项目已经接入 UnoCSS, 路由自动生成或图标插件, 建议统一在 vite.config.ts 管理.
  • 若后续继续整理, 可补“别名配置”, “代理转发”, “多环境变量”与“构建优化”示例.

UnoCSS

说明

  • UnoCSS 是原子化 CSS 引擎, 适合快速搭建样式系统.
  • 适合与 Vite 配合, 用较低心智成本构建可组合的样式规则.

参考资料

Vite 配置示例

// vite.config.ts
import UnoCSS from 'unocss/vite'
import { presetIcons, presetUno } from 'unocss'

export default {
  plugins: [
    UnoCSS({
      presets: [presetUno(), presetIcons()],
    }),
  ],
}

// main.ts
import 'uno.css'

常见能力

  • presetUno: 提供基础原子类规则.
  • presetIcons: 让图标也能按类名方式使用.
  • shortcuts: 适合为高频样式组合取别名.
  • rules: 适合补项目自定义原子规则.

使用建议

  • 若项目本身已采用原子化样式思路, UnoCSS 的接入会很自然.
  • 与图标预设组合时, 能减少手写样式和资源文件管理成本.
  • 团队协作时, 建议统一约定 shortcuts 和自定义规则的命名风格.
  • 若后续继续整理, 可补“图标配置”, “主题变量” 和 “与组件库协作”示例.

CSS 交互技巧

说明

  • 本页记录前端交互中容易忽略但非常实用的 CSS 技巧.
  • 当前重点围绕 pointer-events 和图层事件穿透场景.

禁用鼠标事件

可用于阻止绝对定位元素拦截 mouseover, mouseout, click 等事件:

pointer-events: none;

典型场景

  • 悬浮提示层只负责展示, 不应拦截底层按钮事件.
  • 覆盖在图表上的装饰层需要“可见但不可点”.
  • 需要让透明遮罩视觉存在, 但把交互继续交给下层节点.

SVG 遮罩捕获事件

SVG 交互中, 也可通过显式开启 pointer-events 来捕获光标事件:

<g class="brush" fill="none" pointer-events="all">
    <rect class="overlay" pointer-events="all" cursor="crosshair" x="20" y="20" width="460" height="260"></rect>
</g>

使用建议

  • 先区分“元素不响应事件”和“元素透明但仍需接收事件”这两类需求.
  • 对绝对定位浮层, pointer-events 往往比复杂的事件转发更直接.
  • 在图表, 画布和 SVG 场景里, 要特别留意事件究竟由容器, 遮罩还是具体图元接收.
  • 如果交互异常, 先用浏览器开发者工具检查当前命中元素和计算后的 pointer-events 值.

前端图表工具

说明

  • 本页用于整理前端常见图表与可视化工具的选型方向.
  • 不同工具的优势通常在“灵活度”, “上手速度”, “性能”之间取舍.

常见选择

D3.js

  • 更底层, 灵活度高.
  • 更适合高度定制化图形与交互.

webgl-plot

  • 适合高频刷新场景, 例如示波器效果.
  • 项目地址: https://github.com/danchitnis/webgl-plot
  • 示例: https://danchitnis.github.io/webgl-plot-examples/vanilla/index.html

ECharts

  • 适合快速构建标准业务图表.
  • 配置式开发体验更友好.

AntV G2

  • 适合偏分析型和语义化图表表达.
  • 可作为业务图表体系的另一种选择.

选型建议

  • 追求快速交付时, 优先评估 ECharts.
  • 追求高度可定制图形时, 优先评估 D3.
  • 追求高频绘制性能时, 可进一步评估 CanvasWebGL 方案.

D3

说明

  • D3 是面向数据驱动文档的前端可视化库, 擅长做高定制化 SVG / Canvas / DOM 图形.
  • 若只是快速出图, 可以优先评估 ECharts; 若需要细粒度控制数据绑定和图形渲染, 再考虑 D3.

安装

npm install d3
npm install -D @types/d3

引入

import * as d3 from 'd3'

常见能力

  • d3.scaleBand(): 适合离散类目轴.
  • d3.scaleLinear(): 适合连续数值映射.
  • d3.select() / d3.selectAll(): 选择和绑定节点.
  • d3.axisBottom() / d3.axisLeft(): 快速构建坐标轴.
  • 还可进一步组合过渡动画, 缩放, 拖拽和数据绑定能力.

典型使用思路

  1. 准备原始数据并计算比例尺 scale.
  2. 创建 SVG 容器并设置视口.
  3. 绑定数据到图形元素, 例如 rect, circle, path.
  4. 生成坐标轴和标签.
  5. 根据交互需要继续叠加 tooltip, 动画或缩放行为.

使用建议

  • 若需要高度定制化图形和交互, D3 很灵活.
  • 若只是快速出图, 可以先评估 ECharts 或其他高层图表库.
  • 在 React / Vue 中使用时, 建议先明确由谁管理 DOM, 避免框架和 D3 同时修改同一节点.

ECharts

说明

适合场景

  • 柱状图, 折线图, 饼图等标准业务图表.
  • 需要较快完成配置式图表开发的后台系统.
  • 对自定义程度要求不极端, 但需要生态和现成组件支持的场景.

最小示例

const option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
  },
  yAxis: {
    type: 'value',
  },
  series: [
    {
      type: 'line',
      data: [120, 200, 150, 80, 70],
    },
  ],
}

使用建议

  • 先把数据结构整理成 series / xAxis / yAxis 友好的形式.
  • 做多图表页面时, 建议先统一主题, 颜色和 tooltip 交互风格.
  • 如果定制程度越来越高, 再考虑 D3Canvas / WebGL 方案.
  • 若后续继续整理, 可补 dataset, 地图, 大屏适配和性能优化记录.

前端工具库

说明

  • 本页整理前端项目里高频使用的通用工具库.
  • 当前重点覆盖日期处理, 数据加工和模拟数据生成三类常见需求.

日期处理

moment.js

yarn add moment
import * as moment from 'moment'

moment('20230319', 'YYYYMMDD')
moment().format('YYYYMMDD')

适用场景:

  • 快速做日期解析和格式化.
  • 维护旧项目时兼容已有时间处理逻辑.

数据加工

lodash

yarn add -D @types/lodash
import * as _ from 'lodash'

_.debounce(fn, 300)
_.cloneDeep(data)
_.get(user, 'profile.name')

适用场景:

  • 深拷贝, 防抖节流, 路径读取和集合转换.
  • 处理老代码时统一常见工具函数风格.

模拟数据

faker

yarn add -D @faker-js/faker
import { faker } from '@faker-js/faker'

const item = {
  id: faker.string.uuid(),
  name: faker.person.fullName(),
}

适用场景:

  • 开发阶段快速生成表格, 表单和接口模拟数据.
  • 联调前搭本地假数据环境.

选型建议

  • 新项目应优先考虑体积, Tree Shaking 和替代方案的维护状态.
  • 若只是简单日期格式化, 可评估更轻量方案.
  • 若团队已有统一工具封装, 尽量优先复用团队内部工具层.

Node-RED

说明

  • Node-RED 是面向流程编排和可视化节点连接的低代码工具.
  • 本页当前以源码编译和二次开发场景为主, 普通使用者通常不必自己从源码构建.

适用场景

  • 快速拼装 IoT, API, 消息流和自动化任务流程.
  • 需要通过可视化节点方式整合设备, HTTP 接口和消息中间件.
  • 需要调试或定制 Node-RED 自身源码实现.

源码编译

git clone https://github.com/node-red/node-red.git
cd node-red
npm --registry=https://registry.npmmirror.com install
npm run build
npm start

常见理解方式

  • 普通用户: 更关注节点编排, 部署和运行方式.
  • 开发者: 更关注自定义节点, 插件结构和源码构建流程.
  • 运维场景: 更关注进程托管, 日志, 数据持久化和端口暴露方式.

使用建议

  • 使用 yarn 安装依赖时可能失败, 当前记录优先使用 npm.
  • 若只是使用现成发行版, 不一定需要走源码编译流程.
  • 源码编译更适合做二次开发或排查内部实现.
  • 普通使用场景可优先安装官方发布版本.

Web 示例项目归档

说明

  • 本目录主要保存历史 Web 示例工程, 旧脚手架代码和少量历史迁移记录.
  • 这里不再承担核心知识导航职责, 相关知识应优先回到 前端总览.
  • 若需要运行旧示例, 比较脚手架结构或回看历史目录布局, 再进入各子目录.

当前归档结构

第一批低优先级迁出候选

  • js/: 素材和零散脚本居多, 更适合作为非主知识型素材归档.
  • vite-svelte/: 早期脚手架快照, 主要保留历史对照价值.

如何使用本目录

查知识点

  • 不建议从这里开始.
  • 优先回到 frontend/ 中对应的正式知识页.

查旧工程结构

  • 适合回看 Create React App, vue-cli, 早期 Vite 等历史项目布局.
  • 更适合对照迁移, 不适合作为新项目模板来源.

跑历史示例

  • 先确认 Node 版本和包管理器是否与当年的依赖兼容.
  • 老项目常见问题包括锁文件失效, 依赖下线和构建工具版本不匹配.

目录边界

  • frontend/ 放正式知识文档.
  • program/web/ 放历史示例工程和迁移说明.
  • 若某个示例已经提炼出稳定知识点, 应回流到 frontend/ 而不是继续堆在这里.

当前状态

  • 仓库内主要旧路径兼容页已完成内部清理.
  • 后续统一以 frontend/ 正式文档和各示例目录 README 为准.

算法总览

说明

  • 本目录用于收纳算法与数据结构相关笔记.
  • 当前内容仍在持续建设, 但已经可以作为“后续该往哪些专题拆”的统一入口.
  • 适合在整理刷题, 面试准备或工程常用算法知识时先建立目录框架.

建议收录方向

基础数据结构

  • 数组
  • 链表
  • 队列
  • 哈希表
  • 堆与优先队列

常见算法主题

  • 排序
  • 查找
  • 动态规划
  • 贪心
  • 回溯
  • 分治
  • 图算法
  • 双指针与滑动窗口
  • 并查集与拓扑排序

建议写作结构

每篇算法文档建议至少包含:

  1. 问题定义与适用场景.
  2. 核心思路与关键不变量.
  3. 时间复杂度与空间复杂度.
  4. 最小示例或伪代码.
  5. 易错点与变体题型.

阅读路径建议

  • 如果目标是夯实基础, 先补数据结构专题, 再进入排序和查找.
  • 如果目标是刷题或面试, 可优先整理双指针, 动态规划, 图算法和回溯.
  • 如果目标是工程实践, 可优先关注哈希表, 堆, 图搜索和字符串处理.

后续整理建议

  • 后续可按“数据结构”与“算法专题”拆分子文档.
  • 若目录内容逐渐增多, 可继续拆出“模板技巧”和“典型题型”两层入口.

数学总览

说明

  • 本目录收录当前知识库中与基础数学相关的轻量文档.
  • 当前以微积分与线性代数为主, 更适合作为技术学习时的辅助索引.
  • 若后续内容继续增多, 可按 微积分, 线性代数, 概率统计 进一步拆分.

当前主题

建议阅读路径

  • 想理解变化率与线性近似时, 先看 微分.
  • 想理解累积量与面积问题时, 再看 积分.
  • 想补矩阵, 向量和线性变换基础时, 看 线性代数.

积分

说明

  • 积分常用于描述“累积量”, 例如面积, 路程, 总量和能量等.
  • 在数学上, 它和微分一起构成微积分的核心框架.

常见分类

不定积分

  • 表示一个函数的原函数族.
  • 结果通常要加积分常数 C.

定积分

  • 表示在某个区间上的累积效果.
  • 几何上常可理解为曲线与坐标轴围成的有向面积.

与微分的关系

  • 微分描述变化率.
  • 积分描述累积量.
  • 二者由微积分基本定理联系起来.

常见理解方式

1. 面积理解

  • 定积分可看成很多小矩形面积求和后的极限.
  • 适合直观理解“累积”这个概念.

2. 物理理解

  • 速度对时间积分得到路程.
  • 功率对时间积分得到能量.

3. 反向理解

  • 若已知变化率函数, 通过积分可以回到原始量的变化总和.

常见技巧

  • 换元积分.
  • 分部积分.
  • 利用对称性处理定积分.

典型公式

∫ x^n dx = x^(n+1)/(n+1) + C, 其中 n ≠ -1
∫ 1/x dx = ln|x| + C
∫ e^x dx = e^x + C
∫ sin x dx = -cos x + C

使用建议

  • 初学时先把“积分 = 累积量”建立为核心直觉.
  • 不定积分和定积分虽然写法相近, 但语义不同, 要注意区分.
  • 若后续继续扩展, 可单独补 换元积分, 分部积分, 定积分几何意义 等页面.

微分

说明

  • 微分用于描述函数在某一点附近的变化率, 是理解速度, 斜率和线性近似的基础.
  • 在工程和程序分析里, 它常对应系统对输入变化的敏感程度.

导数与微分

导数

  • 导数描述函数相对于自变量变化的快慢.
  • 几何上可理解为曲线切线的斜率.
  • 物理上常对应速度, 加速度, 电流变化率等概念.

微分

  • y = f(x), 则 dy 可看作函数在线性近似下的改变量.
  • dx 很小时, 常用 dy ≈ f'(x) dx 描述局部变化.

常见理解方式

1. 几何理解

  • 导数是切线斜率.
  • 微分是切线近似带来的函数改变量估计.

2. 物理理解

  • 位移对时间求导得到速度.
  • 速度对时间求导得到加速度.

3. 近似计算

  • 微分常用于快速估计“小变化带来多大结果偏移”.
  • 这在误差分析和工程近似里很常见.

典型公式

幂函数

d(x^n)/dx = n x^(n-1)

三角函数

d(sin x)/dx = cos x
d(cos x)/dx = -sin x

指数函数

d(e^x)/dx = e^x

常见法则

  • 和差法则.
  • 乘法法则.
  • 商法则.
  • 链式法则.

使用建议

  • 初学时优先先把“导数 = 变化率”这个核心直觉建立起来.
  • 公式记忆最好结合几何和物理意义, 不要只背符号.
  • 若后续继续扩展, 可单独拆出 链式法则, 隐函数求导, 泰勒展开 等页面.

线性代数

说明

  • 线性代数是理解向量, 矩阵, 线性变换与多维空间问题的基础.
  • 在图形学, 机器学习, 控制系统和物理建模中都非常常见.

向量

  • 向量可用于表示方向与大小.
  • 也是矩阵运算和几何变换的基础.
  • 在程序里常用来表示位置, 速度, 法线和特征表示.

点乘

点乘常用于:

  • 计算夹角关系.
  • 判断投影大小.
  • 衡量两个向量方向的一致程度.

直观理解:

  • 结果越大, 往往说明两个向量方向越接近.
  • 若点乘为 0, 常表示二者正交.

叉乘

叉乘常用于:

  • 求垂直于两个向量的方向.
  • 计算平行四边形面积.
  • 处理三维空间中的法向量问题.

注意:

  • 叉乘主要在三维空间语境里最常见.
  • 结果仍是一个向量, 方向由右手定则确定.

后续应补主题

  • 矩阵与矩阵乘法.
  • 行列式与可逆矩阵.
  • 特征值与特征向量.
  • 线性方程组与基变换.

工具总览

说明

  • 本目录收录开发环境, 系统工具, 容器, 数据库, AI 工具等使用记录.
  • 常用主题优先从本页和 SUMMARY 进入, 再深入具体笔记.
  • 零散旧文档会逐步并入更稳定的专题页.

阅读路径

  • 构建链和调试链问题, 优先看 编译工具, OpenOCD 与 GDB, Make, CMake, mdBook.
  • 系统环境问题, 优先看 Linux 总览, Ubuntu, Windows 总览, Shell 笔记.
  • 容器与服务部署问题, 优先看 Docker 总览, OpenResty, 阿里云服务, VPS 运维笔记.
  • 数据与 AI 相关问题, 优先看 SQL 总览, MongoDB, AI 工具总览, RAGFlow.

构建与文档

开发环境与媒体

系统与环境

容器与服务

数据与 AI

网络与远程协作

其他常用

目录边界

  • tools/ 主要放工具使用, 环境搭建, 运维和平台操作记录.
  • frontend/, program/, embedded/ 中若出现与具体技术栈强相关的内容, 优先留在各自专题目录中.
  • 若某页只是旧路径说明或历史兼容入口, 后续会继续收缩, 避免重复导航.

git 笔记

子模块

添加

git submodule add -b <BranchName> <Url>

同步

git submodule update –init –recursive

删除

rm -rf 子模块目录

vi .gitmodules 删除相关子模块条目

vi .git/config 删除相关子模块条目

rm .git/module/<Sub Module Dir> 删除子模块目录

git rm --cached <Sub Module Name>

分支

默认分支命名

当前大多数仓库默认分支已从 master 迁移为 main.

整理笔记和新建仓库时, 优先使用 main. 如果目标仓库仍然使用 master, 再按实际分支名替换命令中的 main.

Tag

创建 Tag

git tag <TagName>

推送 Tag

git push origin –tags

删除 Tag

git tag -d $(git tag -l)

git fetch

git push origin --delete $(git tag -l)

切换到 远程 tag

git checkout tags/v2.6.3 这里远程 TAG 是 v2.6.3

可用的国内镜像

来自: https://juejin.cn/post/7210744398640595005

github.com 前加一个 k

如: https://kgithub.com/midoks/mdserver-web

取消 git commit

git reset –soft HEAD^

git reset HEAD 指定文件或目录

第一次提交代码

git init

git remote add origin git@gitee.com:imxood/stm32h750_rt_app.git
git pull origin main

git branch --set-upstream-to=origin/main

git add .
git commit -m "."
git push

远程 url 变更, 更新本地 origin

git remote set-url origin GIT_URL

warning: CRLF will be replaced by LF

git config --global core.autocrlf false

执行 git status 时 中文路径乱码解决:

git config --global core.quotepath false

分支管理

Pr 修改

# 提交了Pr, 有错误, 你想修改, 那么可以先在你的分支上, 回退之前的一个版本:
git reset HEAD~1
# 添加新修改, 并提交
git add .
git commit -m "..."
# 强制提交, 覆盖之前错误的提交
git push -f
# push之后, 你的这次提交也会自动更新pr记录

删除分支

# 删除本地分支
git branch -d localBranchName
# 删除远程分支
git push origin --delete remoteBranchName
# 删除后 同步分支
git fetch -p

创建空的新分支

git checkout --orphan main, 创建没有commits的孤儿分支
git add . && git commit -m "."
git push --set-upstream origin main

删除历史记录

创建没有提交记录的新分支 并用这个分支 覆盖主分支

git checkout --orphan one, 创建没有commits的孤儿分支

git add . && git commit -m "."

git branch -d main 删除分支
git branch -m main 修改当前分支为 main

git push -f, 强制提交本地记录

可能报错: failed to push some refs to

可能解决办法:

    git fetch origin

    git merge origin/main

可能需要: git config --global --add --bool push.autoSetupRemote true

查看历史记录,不删除最新提交

git log 找到需要的历史提交 git checkout f35575193daaec87b40af180654ee4e6c844d71c 切换到指定的提交记录, 通过 git status 可以看出当前已切换 git checkout - 切换到最新的代码

git remote

git remote -v, 显示当前远程仓库信息
git remote set-url origin https://github.com/imxood/mdbook-katex.git, 设置远程仓库

workflow

参考: GitHub Actions 的元数据语法

参考: GitHub Actions 的工作流程语法

github reset api

参考: getting-started-with-the-rest-api

参考: Resources in the REST API

# 不使用认证将只有每小时60次请求的限制
curl -i https://api.github.com/users/imxood

# 使用认证将有每小时5000次请求
curl -i -u username:$token https://api.github.com/users/octocat

# Shell - Get latest release from GitHub
curl --silent "https://api.github.com/repos/$1/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")'

github actions

环境文件

\$GITHUB_PATH, 用于添加系统路径

    GITHUB_PATH 这个环境变量表示一个环境变量文件, 所有后续操作将会将这个文件中的值添加到系统Path中.

    echo "{path}" >> \$GITHUB_PATH
    或者是 打开 \$GITHUB_PATH 文件, 写路径

\$GITHUB_ENV, 用于设置环境变量

    跟上面是类似的.
    echo "{name}={value}" >> \$GITHUB_ENV

同步 fork 上游的代码

# 查看远程状态
git remote -v

# 添加 fork的上游仓库
git remote add upstream https://github.com/larksuite/rsmpeg.git

# 再次 查看状态是否被添加
git remote -v

# 获取上游的更新, 会被存储到本地分支 upstream/main
git fetch upstream

# 切换到本地要 merge 的分支 main
git checkout main

# 把 upstream/main 分支 merge 到本地 main
git merge upstream/main

# 提交
git push origin main

# 如果上游仓库仍然使用 master, 则把上面的 main 替换为 master

更新子模块

git submodule update –init –recursive

不包含 .submodule 时

git submodule init git submodule update

git 代理

git config –global http.proxy http://127.0.0.1:1080

git config –global https.proxy http://127.0.0.1:1080

git config –global –get http.proxy

git config –global –get https.proxy

修复 “Not possible to fast-forward, aborting”

当前示例默认当前分支为 main. 如果目标仓库使用的是 master 或其它分支, 请替换为实际分支名.

git pull origin main –rebase

修复冲突后,

执行: git rebase –continue

git submodule

git clone xxx.git

cd xxx

git submodule add zzz.git 将自动拉取该项目到当前目录下

本地项目 子模块

添加 Tag 并提交

git 使用 第 2 个帐号

cd ~/.ssh

ssh-keygen -t rsa

Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\maxu/.ssh/id_rsa):id_rsa_用户名

...

ssh-add id_rsa_zzgood25

在 ~/.ssh/config 中添加

Host github.com-用户名
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_用户名

clone 项目:

原本是: git clone git@github.com:xxx/xxxxxx.git

现在是: git clone git@github.com-用户名:xxx/xxxxxx.git

是否需要开启 “OpenSSH Authentication Agent” 服务?

验证服务:

ssh -T git@github.com-用户名

修改端口

~/.ssh/config

Host gitea
    HostName gitea.example.com
    Port 222
    User root
    IdentityFile ~/.ssh/id_rsa

自动保存 https 认证密码

git config –global credential.helper store

linux 中似乎是明文, 位于 ~/.git-credentials

mac 和 windows 不是?

在 git 中 使用 ssh 代理

参考: https://gist.github.com/chenshengzhi/07e5177b1d97587d5ca0acc0487ad677

windows

在 ~/.ssh/config 文件的开头 添加一行 就可以了:

ProxyCommand connect -H 127.0.0.1:1080 %h %p

我发现 crates/thirds/egui-toast 未被 git 管理, 执行: git submodule status 报错:

fatal: no submodule mapping found in .gitmodules for path 'crates/thirds/egui-toast'

解决办法:

git rm --cached crates/thirds/egui-toast -r

markdown 常用语法

数学公式

参考: DanielGavin - Markdown数学公式语法

参考: katex Supported Functions

行内与独行

  1. 行内公式: 将公式插入到本行内, 符号: $公式内容$, 如: $xyz$, $xyz$
  2. 独行公式: 将公式插入到新的一行内, 并且居中, 符号: $$公式内容$$, 如: $$xyz$$, $$xyz$$

上标、下标与组合

  1. 上标符号, 符号: ^, 如: $x^4$, $x^4$
  2. 下标符号, 符号: _, 如: $x_1$, $x_1$
  3. 组合符号, 符号: {}, 如: ${16}{8}O{2+}{2}$, ${16}{8}O{2+}{2}$

汉字、字体与格式

  1. 汉字形式, 符号: \mbox{}, 如: $V_{\mbox{初始}}$ $$V_{\mbox{初始}}$$

  2. 字体控制, 符号: \displaystyle, 如: $\displaystyle \frac{x+y}{y+z}$ $$\displaystyle \frac{x+y}{y+z}$$

  3. 下划线符号, 符号: \underline, 如: $\underline{x+y}$ $$\underline{x+y}$$

  4. 标签, 符号\tag{数字}, 似乎只能用独行形式, 如: $$\tag*{hi} x+y^{2x}$$, $$\tag{hi} x+y^{2x}$$ $$\tag*{hi} x+y^{2x}$$ $$\tag{hi} x+y^{2x}$$

  5. 上大括号, 符号: \overbrace{算式}, 如: $\overbrace{a+b+c+d}^{2.0}$ $$\overbrace{a+b+c+d}^{2.0}$$

  6. 下大括号, 符号: \underbrace{算式}, 如: $a+\underbrace{b+c}{1.0}+d$ $$a+\underbrace{b+c}{1.0}+d$$

  7. 上位符号, 符号: \stacrel{上位符号}{基位符号}, 如: $\vec{x}\stackrel{\mathrm{def}}{=}{x_1,\dots,x_n}$ $$\vec{x}\stackrel{\mathrm{def}}{=}{x_1,\dots,x_n}$$

占位符

  1. 两个quad空格, 符号: \qquad, 如: $x \qquad y$, $x \qquad y$
  2. quad空格, 符号: \quad, 如: $x \quad y$, $x \quad y$
  3. 大空格, 符号: , 如: $x \ y$, $x \ y$
  4. 中空格, 符号: , 如: $x : y$, $x : y$
  5. 小空格, 符号: ,, 如: $x , y$, $x , y$
  6. 没有空格, 如: $xy$, $xy$
  7. 紧贴, 符号: \!, 如: $x \! y$, $x ! y$

定界符与组合

  1. 括号, 符号: ()\big(\big) \Big(\Big) \bigg(\bigg) \Bigg(\Bigg)

    如: $()\big(\big) \Big(\Big) \bigg(\bigg) \Bigg(\Bigg)$ $$()\big(\big) \Big(\Big) \bigg(\bigg) \Bigg(\Bigg)$$

  2. 中括号, 符号: [], 如: $[x+y]$, $[x+y]$

  3. 大括号, 符号: { }, 如: ${x+y}$, ${x+y}$

  4. 自适应括号, 符号: \left \right, 如: $\left(x\right)$, $\left(x{yz}\right)$

    $\left(x\right)$, $\left(x{yz}\right)$

  5. 组合公式, 符号: {上位公式 \choose 下位公式}

    如: ${n+1 \choose k}={n \choose k}+{n \choose k-1}$ $${n+1 \choose k}={n \choose k}+{n \choose k-1}$$

  6. 组合公式, 符号: {上位公式 \atop 下位公式}

    如: $\sum_{k_0,k_1,\ldots>0 \atop k_0+k_1+\cdots=n}A_{k_0}A_{k_1}\cdots$ $$\sum_{k_0,k_1,\ldots>0 \atop k_0+k_1+\cdots=n}A_{k_0}A_{k_1}\cdots$$

四则运算

  1. 加法运算, 符号: +, 如: $x+y=z$ $$x+y=z$$

  2. 减法运算, 符号: -, 如: $x-y=z$ $$x-y=z$$

  3. 加减运算, 符号: \pm, 如: $x \pm y=z$ $$x \pm y=z$$

  4. 减甲运算, 符号: \mp, 如: $x \mp y=z$ $$x \mp y=z$$

  5. 乘法运算, 符号: \times, 如: $x \times y=z$ $$x \times y=z$$

  6. 点乘运算, 符号: \cdot, 如: $x \cdot y=z$ $$x \cdot y=z$$

  7. 星乘运算, 符号: \ast, 如: $x \ast y=z$ $$x \ast y=z$$

  8. 除法运算, 符号: \div, 如: $x \div y=z$ $$x \div y=z$$

  9. 斜法运算, 符号: /, 如: $x/y=z$ $$x/y=z$$

  10. 分式表示, 符号: \frac{分子}{分母}, 如: $\frac{x+y}{y+z}$ $$\frac{x+y}{y+z}$$

  11. 分式表示, 符号: {分子} \voer {分母}, 如: ${x+y} \over {y+z}$ $${x+y} \over {y+z}$$

  12. 绝对值表示, 符号: ||, 如: $|x+y|$ $$|x+y|$$

高级运算

  1. 平均数运算, 符号: \overline{算式}, 如: $\overline{xyz}$ $$\overline{xyz}$$

  2. 开二次方运算, 符号: \sqrt, 如: $\sqrt x$ $$\sqrt x$$

  3. 开方运算, 符号: \sqrt[开方数]{被开方数}, 如: $\sqrt[3]{x+y}$ $$\sqrt[3]{x+y}$$

  4. 对数运算, 符号: \log, 如: $\log(x)$ $$\log(x)$$

  5. 极限运算, 符号: \lim, 如: $\lim^{x \to \infty}{y \to 0}{\frac{x}{y}}$ $$\lim^{x \to \infty}{y \to 0}{\frac{x}{y}}$$

  6. 极限运算, 符号: \displaystyle \lim, 如: $\displaystyle \lim^{x \to \infty}{y \to 0}{\frac{x}{y}}$ $$\displaystyle \lim^{x \to \infty}{y \to 0}{\frac{x}{y}}$$

  7. 求和运算, 符号: \sum, 如: $\sum^{x \to \infty}{y \to 0}{\frac{x}{y}}$ $$\sum^{x \to \infty}{y \to 0}{\frac{x}{y}}$$

  8. 求和运算, 符号: \displaystyle \sum, 如: $\displaystyle \sum^{x \to \infty}{y \to 0}{\frac{x}{y}}$ $$\displaystyle \sum^{x \to \infty}{y \to 0}{\frac{x}{y}}$$

  9. 积分运算, 符号: \int, 如: $\int^{\infty}{0}{xdx}$ $$\int^{\infty}{0}{xdx}$$

  10. 积分运算, 符号: \displaystyle \int, 如: $\displaystyle \int^{\infty}{0}{xdx}$ $$\displaystyle \int^{\infty}{0}{xdx}$$

  11. 微分运算, 符号: \partial, 如: $\frac{\partial x}{\partial y}$ $$\frac{\partial x}{\partial y}$$

  12. 矩阵表示, 符号: \begin{matrix} \end{matrix}, 如: $\begin{matrix} a & b \ c & d \end{matrix}$ $$\begin{matrix} a & b \ c & d \end{matrix}$$

逻辑运算

  1. 等于运算, 符号: =, 如: $x+y=z$ $$x+y=z$$

  2. 大于运算, 符号: >, 如: $x+y>z$ $$x+y>z$$

  3. 小于运算, 符号: <, 如: $x+y<z$ $$x+y<z$$

  4. 大于等于运算, 符号: \geq, 如: $x+y \geq z$ $$x+y \geq z$$

  5. 小于等于运算, 符号: \leq, 如: $x+y \leq z$ $$x+y \leq z$$

  6. 不等于运算, 符号: \neq, 如: $x+y \neq z$ $$x+y \neq z$$

  7. 不大于等于运算, 符号: \ngeq, 如: $x+y \ngeq z$ $$x+y \ngeq z$$

  8. 不大于等于运算, 符号: \not\geq, 如: $x+y \not\geq z$ $$x+y \not\geq z$$

  9. 不小于等于运算, 符号: \nleq, 如: $x+y \nleq z$ $$x+y \nleq z$$

  10. 不小于等于运算, 符号: \not\leq, 如: $x+y \not\leq z$ $$x+y \not\leq z$$

  11. 约等于运算, 符号: \approx, 如: $x+y \approx z$ $$x+y \approx z$$

  12. 恒定等于运算, 符号: \equiv, 如: $x+y \equiv z$ $$x+y \equiv z$$

集合运算

  1. 属于运算, 符号: \in, 如: $x \in y$, $x \in y$
  2. 不属于运算, 符号: \notin, 如: $x \notin y$, $x \notin y$
  3. 不属于运算, 符号: \not\in, 如: $x \not\in y$, $x \not\in y$
  4. 子集运算, 符号: \subset, 如: $x \subset y$, $x \subset y$
  5. 子集运算, 符号: \supset, 如: $x \supset y$, $x \supset y$
  6. 真子集运算, 符号: \subseteq, 如: $x \subseteq y$, $x \subseteq y$
  7. 非真子集运算, 符号: \subsetneq, 如: $x \subsetneq y$, $x \subsetneq y$
  8. 真子集运算, 符号: \supseteq, 如: $x \supseteq y$, $x \supseteq y$
  9. 非真子集运算, 符号: \supsetneq, 如: $x \supsetneq y$, $x \supsetneq y$
  10. 非子集运算, 符号: \not\subset, 如: $x \not\subset y$, $x \not\subset y$
  11. 非子集运算, 符号: \not\supset, 如: $x \not\supset y$, $x \not\supset y$
  12. 并集运算, 符号: \cup, 如: $x \cup y$, $x \cup y$
  13. 交集运算, 符号: \cap, 如: $x \cap y$, $x \cap y$
  14. 差集运算, 符号: \setminus, 如: $x \setminus y$, $x \setminus y$
  15. 同或运算, 符号: \bigodot, 如: $x \bigodot y$, $x \bigodot y$
  16. 同与运算, 符号: \bigotimes, 如: $x \bigotimes y$, $x \bigotimes y$
  17. 实数集合, 符号: \mathbb{R}, 如: $\mathbb{R}$, $\mathbb{R}$
  18. 自然数集合, 符号: \mathbb{Z}, 如: $\mathbb{Z}$, $\mathbb{Z}$
  19. 空集, 符号: \emptyset, 如: $\emptyset$, $\emptyset$

数学符号

  1. 无穷, 符号: \infty, 如: $\infty$, $\infty$
  2. 虚数, 符号: \imath, 如: $\imath$, $\imath$
  3. 虚数, 符号: \jmath, 如: $\jmath$, $\jmath$
  4. 数学符号, 符号\hat{a}, 如: $\hat{a}$, $\hat{a}$
  5. 数学符号, 符号\check{a}, 如: $\check{a}$, $\check{a}$
  6. 数学符号, 符号\breve{a}, 如: $\breve{a}$, $\breve{a}$
  7. 数学符号, 符号\tilde{a}, 如: $\tilde{a}$, $\tilde{a}$
  8. 数学符号, 符号\bar{a}, 如: $\bar{a}$, $\bar{a}$
  9. 矢量符号, 符号\vec{a}, 如: $\vec{a}$, $\vec{a}$
  10. 数学符号, 符号\acute{a}, 如: $\acute{a}$, $\acute{a}$
  11. 数学符号, 符号\grave{a}, 如: $\grave{a}$, $\grave{a}$
  12. 数学符号, 符号\mathring{a}, 如: $\mathring{a}$, $\mathring{a}$
  13. 一阶导数符号, 符号\dot{a}, 如: $\dot{a}$, $\dot{a}$
  14. 二阶导数符号, 符号\ddot{a}, 如: $\ddot{a}$, $\ddot{a}$
  15. 上箭头, 符号: \uparrow, 如: $\uparrow$, $\uparrow$
  16. 上箭头, 符号: \Uparrow, 如: $\Uparrow$, $\Uparrow$
  17. 下箭头, 符号: \downarrow, 如: $\downarrow$, $\downarrow$
  18. 下箭头, 符号: \Downarrow, 如: $\Downarrow$, $\Downarrow$
  19. 左箭头, 符号: \leftarrow, 如: $\leftarrow$, $\leftarrow$
  20. 左箭头, 符号: \Leftarrow, 如: $\Leftarrow$, $\Leftarrow$
  21. 右箭头, 符号: \rightarrow, 如: $\rightarrow$, $\rightarrow$
  22. 右箭头, 符号: \Rightarrow, 如: $\Rightarrow$, $\Rightarrow$
  23. 底端对齐的省略号, 符号: \ldots, 如: $1,2,\ldots,n$, $1,2,\ldots,n$
  24. 中线对齐的省略号, 符号: \cdots, 如: $x_1^2 + x_2^2 + \cdots + x_n^2$, $x_1^2 + x_2^2 + \cdots + x_n^2$
  25. 竖直对齐的省略号, 符号: \vdots, 如: $\vdots$, $\vdots$
  26. 斜对齐的省略号, 符号: \ddots, 如: $\ddots$, $\ddots$

希腊字母

No.LowercaseUppercaseEnglishIPA
$1$$\alpha$$A$$alpha$/’ælfə/
$2$$\beta$$B$$beta$/’bi:tə/or/’beɪtə/
$3$$\gamma$$\Gamma$$gamma$/’gæmə/
$4$$\delta$$\Delta$$delta$/’deltə/
$5$$\epsilon$$E$$epsilon$/’epsɪlɒn/
$6$$\zeta$$Z$$zeta$/’zi:tə/
$7$$\eta$$H$$eta$/’i:tə/
$8$$\theta$$\Theta$$theta$/’θi:tə/
$9$$\iota$$I$$iota$/aɪ’əʊtə/
$10$$\kappa$$K$$kappa$/’kæpə/
$11$$\lambda$$\lambda$$lambda$/’læmdə/
$12$$\mu$$M$$mu$/mju:/
$13$$\nu$$N$$nu$/nju:/
$14$$\xi$$\Xi$$xi$/ksi/or/’zaɪ/or/’ksaɪ/
$15$$\omicron$$O$$omicron$/əu’maikrən/or/’ɑmɪ,krɑn/
$16$$\pi$$\Pi$$pi$/paɪ/
$17$$\rho$$P$$rho$/rəʊ/
$18$$\sigma$$\Sigma$$sigma$/’sɪɡmə/
$19$$\tau$$T$$tau$/tɔ:/or/taʊ/
$20$$\upsilon$$\Upsilon$$upsilon$/’ipsilon/or/’ʌpsilɒn/
$21$$\phi$$\Phi$$phi$/faɪ/
$22$$\chi$$X$$chi$/kaɪ/
$23$$\psi$$\Psi$$psi$/psaɪ/
$24$$\omega$$\Omega$$omega$/’əʊmɪɡə/or/oʊ’meɡə/

test

header1header2
$\omega$$\omega$

LaTeX 笔记

说明

  • 本页记录常见数学公式输入速记, 适合写技术文档, 论文和工程说明时快速查找.
  • 默认以行内公式为主, 复杂公式可再扩展到独立公式环境.

行内公式

  • 使用 $...$ 包裹行内公式.
  • 示例: $E = mc^2$, $f(x) = x^2$.

下标与上标

  • 单个字符下标: U$_Z$
  • 多字符下标: U_{Z,min}
  • 上标: V_{out}^{max}
  • 上下标同时使用时, 建议优先加 {} 保持结构清晰.

常见符号

  • 变化量: $\Delta U$
  • 乘号: $a \times b$
  • 正负号: $\pm$
  • 小于等于: $\leq$
  • 约等于: $\approx$

常见结构

  • 分数: $\frac{a}{b}$
  • 根号: $\sqrt{x}$
  • 自动伸缩括号: $\left( \frac{a+b}{c} \right)$
  • 单位或说明文字建议放入 \mathrm{} 或公式外普通文本中.

使用建议

  • 多字符上下标, 分数分子分母和复杂表达式尽量都加 {}.
  • 文档里若公式较多, 建议统一符号命名, 避免同一变量在不同段落含义漂移.

mdbook 构建工具

官方文档

安装

mdbook 用法

mdbook --help
mdbook <SUBCOMMAND> --help

mdbook init 初始化项目
mdbook init --theme, 初始化项目, 并复制默认的theme到你的项目目录
mdbook build, 编译生成静态文件
mdbook watch --open, 编译成静态文件并打开浏览器, 如果文件改变就重新build
mdbook serve --open, 编译成静态文件并创建server, 打开浏览器, 如果文件改变就重新build

book.toml 配置

output.html.print 是否开起打印功能, 打印功能是 它打开生成的一个包含所有内容的print.html页面, 然后打印这个文件…如果保存为pdf的话没有目录

no-section-label 这个可以去掉侧边栏的数字前缀

output.html.fold 可以控制sidebar是否可以折叠及默认行为

preprocessor

数学公式 mdbook-katex

流程图 mdbook-mermaid

cargo install mdbook-katex

cargo install mdbook-mermaid mdbook-mermaid install path/to/your/book

源码分析

  1. 使用 env_logger 输出log, 通过 RUST_LOG 环境变量 控制 log 的输出, 如: export RUST_LOG=mdbook, 启用可执行程序 mdbook 的 all logging

  2. 使用 clap 解析命令行参数

  3. 加载配置

    使用 toml 加载 book.toml文件, 并序列化成 BookConfig对象. BookConfig对象需要实现 serde 的 Deserialize

    使用 std::env 读取环境变量, 根据环境变量中 “MDBOOK_” 前缀的变量更新配置

    如: MDBOOK_BOOK__TITLE=“Hello” 即设置 book.title 的值为“Hello“

  4. 解析 SUMMARY.md 文件, 得到 book 对象, 包含了每一个章节的content.

    使用 pulldown-cmark 解析 markdown文件

    核心思想: pull parser, 像游标一样移动, 提供 self.stream.next() 这样的方法向前移动游标, 返回值为解析到的事件, 对特定事件处理并loop.

    比如: Event::Start(Tag::Heading(1)), 如果检查到这个事件就执行:

        let tags = collect_events!(self.stream, end Tag::Heading(1));
    

    这样就能拿到解析到的数据. 通过对不同的事件处理 并 loop, 完成解析工作.

    parse_numbered, 解析中间部分的列表. 即: Numbered Chapter

    parse_nested_numbered, 用于解析一个子列表

    parse_nested_item, 用于解析子列表中的某一项

    next_event() 中, 通过 parser.back.take() 若为None则向前移动游标, 否则就返回 back.take()的结果.

     例子1:
    
    - [首页](./README.md)
    
    具体的event过程:
       Parsing prefix items
       Next event: Some(Start(List(None)))
       Next event: Some(Start(Item))
       Next event: Some(Start(Link(Inline, Borrowed("./README.md"), Borrowed(""))))
       Next event: Some(Text(Borrowed("首页")))
       Next event: Some(End(Link(Inline, Borrowed("./README.md"), Borrowed(""))))
       Next event: Some(End(Item))
       Next event: Some(End(List(None)))
       Next event: Some(Start(Heading(1)))
       Back: Start(Heading(1))
    
    例子2:
    
    - [硬件]()
       - [晶体管](./hardware/晶体管.md)
       - [MOS管](./hardware/MOS管.md)
    
    具体的event过程:
       Parsing numbered chapters at level 0
       Next event: Some(Start(List(None)))
       Next event: Some(Start(Item))
       Next event: Some(Start(Paragraph))
       Next event: Some(Start(Link(Inline, Borrowed(""), Borrowed(""))))
       Next event: Some(Text(Borrowed("硬件")))
       Next event: Some(End(Link(Inline, Borrowed(""), Borrowed(""))))
       Found chapter: 1. 硬件 ([draft])
       Next event: Some(End(Paragraph))
       Next event: Some(Start(List(None)))
       Parsing numbered chapters at level 1.
       Next event: Some(Start(Item))
       Next event: Some(Start(Link(Inline, Borrowed("./hardware/晶体管.md"), Borrowed(""))))
       Next event: Some(Text(Borrowed("晶体管")))
       Next event: Some(End(Link(Inline, Borrowed("./hardware/晶体管.md"), Borrowed(""))))
       Found chapter: 1.1. 晶体管 (./hardware/晶体管.md)
       Next event: Some(End(Item))
       Next event: Some(Start(Item))
       Next event: Some(Start(Link(Inline, Borrowed("./hardware/MOS管.md"), Borrowed(""))))
       Next event: Some(Text(Borrowed("MOS管")))
       Next event: Some(End(Link(Inline, Borrowed("./hardware/MOS管.md"), Borrowed(""))))
       Found chapter: 1.2. MOS管 (./hardware/MOS管.md)
       Next event: Some(End(Item))
       Next event: Some(End(List(None)))
    
  5. preprocessor

    preprocessor 以子进程的方式被 mdbook主程序 调用, mdbook主程序 把 book 对象 以stdio 的方式传递给 preprocessor进程, preprocessor进程对每一个章节的内容进行预处理

  6. renderer工作过程

    默认使用的是 “html“渲染器, 用于把 preprocessor 的结果解析成html页面.

  7. 使用 notify 捕获特定路径下文件的changed事件

  8. 使用 warp 用于创建http server 和 websocket server, 提供 web服务, 并支持 reload

make 笔记

GNU make

文档整理说明

  • 本文档是旧版零散 make 记录收敛后的主文档.
  • 后续统一以当前路径为准, 不再保留根路径兼容页.

查看所有 targets

.PHONY: no_targets__ list
no_targets__:
list:
	sh -c "$(MAKE) -p no_targets__ | awk -F':' '/^[a-zA-Z0-9][^\$$#\/\t=]*:([^=]|$$)/ {split(\$$1,A,/ /);for(i in A)print A[i]}' | grep -v '__\$$' | sort"

变量

赋值
    x = yes
    x ?= yes, 如果 x 未定义则赋值, 否则不处理
    x := $(y) yes, 只使用前面已经确定的变量值
    x += yes, 追加新的值

延迟展开与立即展开
    x = $(y) yes, y 的值会根据后续定义继续变化
    x := $(y) yes, y 的值在当前行就确定

打印变量
    $(warning $(XXX))
    $(error $(XXX))

判断

ifeq ($(VALIDATION),1)
ifdef PLATFORM
$(error PLATFORM: $(PLATFORM) defined)
else
$(error PLATFORM: $(PLATFORM) undefined)
endif
endif

Makefile 规则

Targets: Prerequisites [;Command]
    Command

Command 必须以 [Tab] 开始.

当规则的 target 是一个文件时, 它的任何一个依赖文件被修改后, 在执行 `make <target>` 时这个目标文件都会被重新生成.

普通依赖与 order-only 依赖

target : normal-prerequisites | order-only-prerequisites
  • normal-prerequisites 是普通依赖.
  • order-only-prerequisites 只保证执行顺序, 不会因为其变化而触发目标重新生成.

示例:

test: | $(OUTPUT_DIR)

伪目标

.PHONY : clean

clean :
    [-]rm *.o *.bin *.lst a.out

自动变量

foo.o : foo.c
    cc -c $(CFLAGS) $^ -o $@
  • $@, 目标文件
  • $^, 所有依赖文件
  • $<, 第一个依赖文件

常用函数

过滤与替换

$(filter-out a.cpp, $(SRC)), 从 SRC 中排除 a.cpp

patsubst: 模式替换
obj = $(patsubst %.c, %.o, $(names))

subst: 普通字符串替换

foreach

$(foreach <var>,<list>,<text>), 循环

names := a b c d
files := $(foreach n,$(names),$(n).o)
得到 files 为 "a.o b.o c.o d.o"

wildcard / notdir / dir

wildcard: 扩展通配符
sources = $(wildcard *.c ./sub/*.c)

notdir: 去除路径
names = $(notdir $(sources))

dir: 只保留路径

call

call 函数是唯一一个可以创建定制化参数函数的引用函数

MAKECMDGOALS

"MAKECMDGOALS" 记录了命令行参数指定的终极目标列表

模式替换示例

foo := a.o b.o l.a c.o
bar := $(foo:%.o=%.c)

sets 'bar' to 'a.c b.c l.a c.c'

utils

# Function to traverse directory tree and find all files matching pattern from list
# Usage: $(call search_tree,src/,*.c *.cpp)
search_tree = $(sort $(if $1,$(foreach d,$(wildcard $1/*),$(call $0,$d,$2)$(filter $(subst *,%,$2),$d))))

# recursive wildcard
rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))

# Same as above, but removes directory prefix
search_dir = $(patsubst $1/%,%,$(call search_tree,$1,$2))

# Generates subdirectories recursively
# Usage $(call subdirs,path)
subdirs = $(filter-out .,$1 $(if $(filter-out .,$(call dirx,$1)),$(call $0,$(call dirx,$1))))

# Directory part of path but with trailing slash removed
dirx = $(sort $(patsubst %/,%,$(dir $1)))

# Changes \ to /, removes '/./' and trailing /.
normalize_path = $(patsubst %/.,%,$(subst /./,/,$(subst \,/,$1)))

# Converts relative path to absolute path
# Usage: $(call resolve_path,root,path_list)
resolve_path = $(call normalize_path,$(foreach p,$2,$(if $(call is_abs,$p),$p,$(addprefix $1/,$p))))

# Generates a build rule for a list of targets (T represent a target within a rule)
# Usage: $(call generate_rules,build_rule_name,targets)
generate_rules = $(foreach T,$2,$(eval $(value $1)))

# Escape given list of characters in text with escape character
# Usage: $(call esc,characters,escape_with,text)
esc = $(if $(firstword $1),$(subst $(firstword $1),$2$(firstword $1),$(call $0,$(wordlist 2, 100,$1),$2,$3)),$3)

# Modifies system PATH by prepending provided arguments. No spaces are allowed
# Usage $(call make_PATH,path1 path2)
make_PATH = $(eval export PATH = $(subst $(space),,$(foreach p,$1,$(call sys_path,$p)$(PATH_SEP)))$(PATH))

# Convert path to section name prefix: change / to . and remove src.
dir_to_sect = .$(subst src.,,$(subst /,.,$(basename $(1))))

CMake 笔记

说明

  • 本页汇总 CMake 常用命令, 语法片段, 变量与属性排查方式.
  • 适合作为写 CMakeLists.txt 或调试构建脚本时的速查页.

cmake 命令

cmake -B build 配置 编译目录 cmake –build build 运行 指定的编译路径 cmake –build build –target clean 清除编译目录 cmake -E remove_directory build

windows平台下指定架构: -A Win32 或 Win64 或 x64, 2019开始 无法使用 x86

cmake –trace-expand –loglevel=VERBOSE … –trace-expand 可以使变量值显示 –loglevel=VERBOSE 可以使message(VERBOSE …)有输出

cmake常用语法

set(ENV{WCHEVT_SDK_BASE} ${CMAKE_CURRENT_LIST_DIR})                     设置环境变量

include_directories(include)                                            设置头文件路径
link_directories(/usr/lib)                                              设置库路径

add_library(hello1 src/hello.cpp)                                       添加静态库
add_library(hello2 SHARED src/hello.cpp)                                添加动态库
add_executable(sayHello src/useHello.cpp)                               添加可执行程序
target_link_libraries(sayHello hello)                                   链接库

set(CMAKE_BUILD_TYPE Release)                                           设置编译类型, 默认就是Release

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")                    设置编译参数

set(CMAKE_CXX_STANDARD 11)

ADD_DEFINITIONS(-DmacroName -DmacroName=macroValue ......)              设置宏

file(GLOB SRC_FILES src/*.c src/*.h)                                    生成一个匹配条件的文件列表

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/exec/)         设置可执行程序的生成目录

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/)          设置动态库的生成目录

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib/static/)   设置静态库的生成目录

安装可执行程序或库:
install(
    TARGETS hello1 hello2
    RUNTIME DESTINATION bin
    ARCHIVE DESTINATION lib #安装静态库到/usr/local/lib目录
    LIBRARY DESTINATION lib #安装动态库到/usr/local/lib目录
)

install(
    #安装头文件到/usr/local/include目录
    FILES ${PROJECT_SOURCE_DIR}/include/hello.h DESTINATION include
)

install(
    DIRECTORIES ${PROJECT_SOURCE_DIR}/include/utils DESTINATION include
)

执行一个command,生成${TEST_FILE}文件
add_custom_command(OUTPUT  ${TEST_FILE}
    COMMAND echo "Generating log.txt file..."
    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_FILE} ${TEST_FILE}
    COMMENT  "This is a test"
)
目标Test1,会等待${TEST_FILE}的生成,然后执行
add_custom_target(Test1 ALL DEPENDS ${TEST_FILE})

执行Test1 command
add_custom_command(TARGET Test1
    PRE_BUILD
    COMMAND echo "executing a fake command"
    COMMENT "This command will be executed before building target Test1"
)

搜集所有在指定路径下的源文件的文件名
aux_source_directory(< dir > < variable >)

CMAKE_C_FLAGS 设置C编译选项
CMAKE_CXX_FLAGS 设置C++编译选项


-DCMAKE_INSTALL_RPATH=/usr 编译期与运行期的rpath都会多一条
-DCMAKE_SKIP_RPATH=FALSE 编译期与运行期都不忽略RPATH


# cmake 获取所有的 include_directories
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
  message(STATUS "dir='${dir}'")
endforeach()


# cmake 获取target所有的属性
# Get all propreties that cmake supports
execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST)

# Convert command output into a CMake list
STRING(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")
STRING(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}")

function(print_properties)
    message ("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}")
endfunction(print_properties)

function(print_target_properties tgt)

    if(NOT TARGET ${tgt})
      message("There is no target named '${tgt}'")
      return()
    endif()

    foreach (prop ${CMAKE_PROPERTY_LIST})
        string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" prop ${prop})
    # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i
    if(prop STREQUAL "LOCATION" OR prop MATCHES "^LOCATION_" OR prop MATCHES "_LOCATION$")
        continue()
    endif()
        # message ("Checking ${prop}")
        get_property(propval TARGET ${tgt} PROPERTY ${prop} SET)
        if (propval)
            get_target_property(propval ${tgt} ${prop})
            message(STATUS "${tgt} ${prop} = ${propval}")
        endif()
    endforeach(prop)
endfunction(print_target_properties)

cmake常用的变量

PROJECT_NAME       项目名称
PROJECT_SOURCE_DIR 工程的根目录
PROJECT_BINARY_DIR 运行cmake命令的目录
CMAKE_INCLUDE_PATH 环境变量,非cmake变量
CMAKE_LIBRARY_PATH 环境变量
CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeLists.txt所在的路径
CMAKE_SOURCE_DIR cmake配置文件的顶层目录
CMAKE_BINARY_DIR
CMAKE_PREFIX_PATH
CMAKE_FILES_DIRECTORY
CMAKE_INSTALL_PREFIX

cmake_minimum_required(VERSION 2.8)

find_package(PkgConfig)
pkg_check_modules(audio_processing REQUIRED audio_processing)

# pkg-config --libs --cflags audio_processing
pkg_check_modules:
    <audio_processing>_FOUND          ... set to 1 if module(s) exist
    <audio_processing>_LIBRARIES      ... only the libraries (w/o the '-l')
    <audio_processing>_LIBRARY_DIRS   ... the paths of the libraries (w/o the '-L')
    <audio_processing>_LDFLAGS        ... all required linker flags
    <audio_processing>_LDFLAGS_OTHER  ... all other linker flags
    <audio_processing>_INCLUDE_DIRS   ... the '-I' preprocessor flags (w/o the '-I')
    <audio_processing>_CFLAGS         ... all required cflags
    <audio_processing>_CFLAGS_OTHER   ... the other compiler flags

    <audio_processing>_LIBDIR
    <audio_processing>_PREFIX

对于/usr/local/lib/pkgconfig下的pc文件

example: /usr/local/lib/pkgconfig/audio_process.pc:
    libdir=/usr/local/lib
    includedir=/usr/local/include

    Name: audio_processing
    Description: Webrtc audio process module
    Version: 0.1
    Libs: -L${libdir} -laudio_processing
    Cflags: -DWEBRTC_AUDIO_PROCESSING_ONLY_BUILD -I${includedir}/webrtc -DWEBRTC_POSIX

libdir:
    指定了库的路径

includedir:
    指定了库的头文件路径

Libs:
    -L: ${audio_process_LIBRARY_DIRS}
    -l: ${audio_process_LIBRARIES}

Cflags中的-I:
    指定了${audio_process_INCLUDE_DIRS}

-DWEBRTC_POSIX:
    表明需要一个WEBRTC_POSIX宏

编译与调试总览

说明

  • 本目录收录构建链, 编译工具与调试链路相关文档.
  • 适合处理 Make, OpenOCD, GDB, 编译器与交叉编译环境问题.

当前主题

阅读路径

  • 通用构建链问题先看 编译工具.
  • 烧写, 断点, 调试链路问题优先看 OpenOCD 与 GDB.
  • 旧版零散编译记录已并入主文档, 后续统一以当前目录的正式页面为准.

编译工具

说明

  • 本页汇总 gcc, gdb, make, 链接器与交叉编译相关笔记.
  • 旧版零散编译记录已统一收敛到本页.

gcc安装

sudo apt install software-properties-common
sudo add-apt-repository ppa:ubuntu-toolchain-r/test

sudo apt install gcc-9 g++-9

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 90 --slave /usr/bin/g++ g++ /usr/bin/g++-9 --slave /usr/bin/gcov gcov /usr/bin/gcov-9
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 90 --slave /usr/bin/g++ g++ /usr/bin/g++-5 --slave /usr/bin/gcov gcov /usr/bin/gcov-5

sudo update-alternatives --config gcc

gcc源码编译

sudo apt install libgmp-dev libmpfr-dev libmpc-dev

svn checkout http://gcc.gnu.org/svn/gcc/trunk gcc-source

mkdir gcc-source/objdir

cd gcc-source/objdir

../configure --enable-languages=c,c++ --enable-checking=release --disable-multilib --disable-checking

make -j10

sudo make install

gcc选项

gcc -o a.out -g test.c, 生成带调试信息的可执行程序 a.out
gcc -E -o test.i -g test.c, 生成预处理文件test.i
gcc -S -o test.s test.c, 生成汇编文件

-Wa,<options>            Pass comma-separated <options> on to the assembler.
-Wp,<options>            Pass comma-separated <options> on to the preprocessor.
-Wl,<options>            Pass comma-separated <options> on to the linker.
-Xassembler <arg>        Pass <arg> on to the assembler.
-Xpreprocessor <arg>     Pass <arg> on to the preprocessor.
-Xlinker <arg>           Pass <arg> on to the linker.

"-Wl,-Map=test.map"与"-Xlinker -Map=test.map", 生成map文件, 效果相同

'f' stands for 'flag'.
ex:
    -fpic         # flag to set position independent code
    -fno-builtin  # don't recognize build in functions ...

'm' stands for mode.

objdump笔记

objdump -d ProgramName, 反汇编
objdump -S ProgramName, 带有源码信息的反汇编

objdump --dwarf ProgramName, 查看 dwarf 格式调试信息
objdump --stabs ProgramName, 查看 stabs 格式调试信息

objdump -t ProgramName, 查看 symbol table

gdb 使用笔记

GDB Shell:

    !!! help COMMAND, 下面的用法都可以加上前面的help, 查看细节

    set args --ARGS, 设置可执行程序启动参数

    pwd, 显示当前的工作目录

    下面的命令多半都可以使用缩写哟

Debug Command:

    基本操作:

        r, run, 启动程序

        where, 在哪, 到哪了, 打印所有栈帧的跟踪信息, 大概是bt的别名

        l, list, 向后查看源码

        l -, 向前查看源码

        l LINENUM, 查看指定行附近的代码
        l FILE:LINENUM, 查看指定文件的指定行附近的代码
        l FUNCTION, 查看指定FUNCTION附近的代码
        l LINENUM, 查看指定行附近的代码
        l *ADDRESS, 查看指定地址附近的代码

        set listsize 50, 设置默认列出的源码行数为50

        show listsize, 显示默认列出的源码行数

        set disassemble-next-line on, 显示下一行的反汇编信息

        -, 进入tui模式, Ctrl + X + A, 退出tui模式

    断点:

        b, 加断点
            b 16, 在当前第16行加断点
            b i=1, 在i=1时加断点
            b main, 在main函数入口添加断点

        d, delete
            d break, 删除所有的断点
            d break 1, 删除序号为1的断点

        i, info
            i b, 查看断点信息

        s, step, 单步走, 遇到函数则进入

        n, next, 单步走, 不会进入函数

        u, util
            u 16, 即运行到第16行

        finish, 完成并跳出当前的子函数

        c, continue, 继续到下一个断点

    info 查看信息:

        print VARIABLE, 打印变量

        display, 列出会自动显示的变量
            display variable1, 设置variable1自动显示值
            undisplay 1, 根据索引取消自动显示

    栈帧(即函数的上下文):

        bt, backtrace, 打印所有栈帧的跟踪信息

        frame N, 选择并打印栈帧, !!! 这个会影响某些info的结果

        info frame, 查看选择的栈帧信息, 默认当前帧, 由 frame N 选择

    寄存器:

        info registers, 查看所有的寄存器状态

        info args, 查看参数

        info locals, 查看局部变量

        info breakpoints, 查看所有断点

        print/x $eax, 查看特定寄存器的状态



Monitor Command:

    monitor help, 查看所有monitor支持的命令

    monitor reset, 重置设备

    monitor halt, 暂停cpu

    monitor reg, 查看所有寄存器内容

    monitor reg PC, 查看PC寄存器内容

    monitor targets, 查看目标状态

arm-none-eabi-gdb -q --readnow -ex "target remote 127.0.0.1:3333" -ex "monitor halt" -ex "load" -ex "b reset_isr" -ex "c" -ex "monitor resume" -ex "disconnect" -ex "q" app.elf

linker scripts

section [address] [(type)] :
    [AT(lma)]
    [ALIGN(section_align) | ALIGN_WITH_INPUT]
    [SUBALIGN(subsection_align)]
    [constraint]
    {
    output-section-command
    output-section-command
    …
    } [>region] [AT>lma_region] [:phdr :phdr …] [=fillexp]

OpenOCD 与 GDB

说明

  • 本页汇总 OpenOCD 的安装配置, 常用命令和与 GDB 联动的调试记录.
  • 适用于嵌入式烧录, 远程调试和板级 Bring-up 场景.

openocd 编译 安装

sudo apt install libusb-1.0-0-dev

git clone https://github.com/ntfreak/openocd.git

编译
    ./bootstrap
    ./configure
    make -j
    sudo make install

vscode 调试 openocd 本身, 编译:
    export CFLAGS="-O0"
    ./bootstrap
    ./configure --prefix=/develop/sources/openocd/output
    make -j
    make install

配置:
    {
        "type": "lldb",
        "request": "launch",
        "name": "debug openocd",
        "program": "${workspaceFolder}/output/bin/openocd",
        "args": ["-f", "board/stm32h750b-disco.cfg"],
        "cwd": "${workspaceFolder}"
    },

openocd调试stm32f7

openocd -f board/stm32f7discovery.cfg

在linux中当前用户执行openocd, 报错: unable to open CMSIS-DAP device 0xc251:0xf001

sudo vim /etc/udev/rules.d/cmsis-dap.rules, 添加一下内容(记得更改组):
    ATTRS{idVendor}=="c251", ATTRS{idProduct}=="f001", MODE="664", GROUP="maxu"

sudo udevadm control --reload

常用命令

openocd -f interface/cmsis-dap.cfg -f target/stm32h7x.cfg, 使用cmsis-dap调试stm32h750

openocd -f board/stm32f7discovery.cfg

telnet localhost 4444

program <filename> [address] [verify] [reset] [exit], 写可执行程序到flash上, 只有binary images才需要address

    ex: program output.bin 0x08000000 reset; halt; reg; resume; exit

halt, 进入挂起状态

reg [REG [value]] , 查看或设置寄存器, 可能需要在halt状态下

resume, 恢复到当前代码段

reset [run|halt|init], 重置, 进入特定状态, 默认是run

reg, 查看寄存器内容, 在halt状态下才会显示内容

targets, 显示所有目标

使用gdb和openocd实现远程下载

arm-none-eabi-gdb -q --readnow -ex "target extended-remote 3333" -ex "b main" -ex "monitor halt" -ex "load" -ex "c" -ex "monitor resume" -ex "echo ok\n" -ex "disconnect" -ex "q"
为了远程的下载程序到sram中, 我发现用gdb在load后自动设置的pc, 用i r看是对的, 用'continue'是可以的, 但是阻塞的, 用了'monitor resume', 但是用monitor reg pc查看pc, 却是每2次中1对1错, 很有规律,,,,,,,,,,用上面的命令: 先给程序加断点,'c', 自动走到断点处, 执行'monitor resume', 退出, 就解决了

openocd实现重启到指定地址

gcc 编译选项:
LDFLAGS += -Wl,-Map=output.map

openocd -f board/stm32f7discovery.cfg -c "program output.bin ${flash_to_addr} reset; halt; reg pc ${reset_entry}; resume; exit"

openocd 用法

如果你的设备支持openocd调试, 那么调试器连接上PC, 在PC上运行openocd, 就作为一个Server, 可调式, 可烧写

下面两种方法的过程都是一样的: 1. load image 2. 打印向量表的位置, 要显示2个字 3. 挂起状态 4. 设置sp指针, 设置pc指针 5. 恢复

向量表的地址, 符号在startup.s中, 根据符号在map文件中找到具体的地址

gdb 连接 openocd, 使用记录

调试文件需要在开发机上

(gdb) target remote 10.239.71.34:3333
Remote debugging using 10.239.71.34:3333
0x7000dd5a in ?? ()
(gdb) load app.elf
Loading section text, size 0x1e8 lma 0x60000000
Loading section text_2, size 0x1e14e lma 0x600001e8
Loading section .ARM.exidx, size 0x8 lma 0x6001e338
Loading section sw_isr_table, size 0x350 lma 0x6001e340
Loading section devconfig, size 0x294 lma 0x6001e690
Loading section shell_root_cmds_sections, size 0xc0 lma 0x6001e924
Loading section rodata, size 0x2161c lma 0x6001e9e4
Loading section datas, size 0x2079 lma 0x60050498
Loading section initlevel, size 0x194 lma 0x60052514
Loading section _static_thread_area, size 0x60 lma 0x600526a8
Loading section _k_mem_slab_area, size 0x54 lma 0x60052708
Loading section _k_mem_pool_area, size 0x1c lma 0x6005275c
Loading section _k_sem_area, size 0x168 lma 0x60052778
Loading section _k_mutex_area, size 0xc8 lma 0x600528e0
Loading section _k_queue_area, size 0x14 lma 0x600529a8
Start address 0x60011690, load size 271647
Transfer rate: 34 KB/sec, 9054 bytes/write.
(gdb) x/2xw 0x60000000
0x60000000 <_vector_table>:	0x6004e5a0	0x60011691
(gdb) set $pc=0x60011691
(gdb) set $sp=0x6004e5a0
(gdb) c
Continuing.

telnet连接openocd 使用记录

用telnet这种办法是远程操作, 调试文件需要在远程, 文件路径是启动openocd时的路径

mx@maxu-pc:/data$ telnet 10.239.71.34 4444

Trying 10.239.71.34...
Connected to 10.239.71.34.
Escape character is '^]'.
Open On-Chip Debugger
>
> load_image ./bin/app.elf
456784 bytes written at address 0x60000000
5368 bytes written at address 0x6006f850
downloaded 462152 bytes in 13.153211s (34.313 KiB/s)
>
> halt
>
> targets
    TargetName         Type       Endian TapName            State
--  ------------------ ---------- ------ ------------------ ------------
0* ose.cpu            cortex_m   little ose.cpu            halted
>
>
> ose.cpu mdw 0x60000000 2
0x60000000 60100000 600004b5                   ...`...`
> reg sp 0x60100000
sp (/32): 0x60100000
> reg pc 0x600004b5
pc (/32): 0x600004B5
>
> resume

关于"ose.cpu mdw", 这样内存读写命令, 可以通过"help mem"的形式搜索, 有很多类似的命令, 都可以实现查看内存

自定义proc

stmh7_cmsis.cfg:

    source [find interface/cmsis-dap.cfg]

    transport select swd

    source [find target/stm32h7x.cfg]

    proc flash () {
        init
        halt
        flash write_image erase rtthread.bin 0x08000000
        reset run
        shutdown
    }

使用:
    openocd -f stmh7_cmsis.cfg -c "flash ()"

使用 cmsisdap 调试 stm32

choco install make openocd gcc-arm-embedded

创建文件 dap-stm32.cfg, 添加以下内容:

    interface cmsis-dap
    transport select swd
    source [find target/stm32f1x.cfg]

烧写程序:
    openocd -f ocd-stm32.cfg -c "program build/stm32f103rct6.bin 0x8000000 reset exit"

打开 launch.json, 新添加一项调试配置:

{
	"name": "Cortex Debug",
	"cwd": "${workspaceRoot}",
	"executable": "build/stm32f103rct6.elf",
	"request": "launch",
	"type": "cortex-debug",
	"servertype": "openocd",
	"configFiles": [
		"dap-stm32.cfg"
	]
}

必要时, 可以添加 armToolchainPath, 但是 openocd 的路径好像不能添加, 只能设置环境变量

arm-none-eabi-gdb

info target
info mem 显示内存情况, 包括 ROM 和 RAM

where 显示当前程序执行位置

monitor target
monitor targets

monitor flash banks 查看 flash

VSCode 笔记

说明

  • 本页整理 VSCode 的常用配置, 插件开发, 编码设置和常见问题排查片段.
  • 适合作为编辑器使用经验和工作流技巧的长期速记页.

使用建议

  • 查通用编辑器配置时, 先从本页顶部开始快速定位问题关键词.
  • 若问题更偏插件开发, 可结合 VSCode 插件开发 专题页一起阅读.
  • 本页保留较多历史片段, 后续可继续按主题拆成更清晰的小节.

VSCode

使用技巧

正则替换

aabbccdd

正则搜索: ([A-Za-z0-9]{2}) 替换为: \x$1

结果为: \xaa\xbb\xcc\xdd

安装 VSCode

curl https://packages.microsoft.com/keys/microsoft.asc | gpg –dearmor > packages.microsoft.gpg

sudo install -o root -g root -m 644 packages.microsoft.gpg /usr/share/keyrings/

sudo sh -c ‘echo “deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main” > /etc/apt/sources.list.d/vscode.list’

sudo apt update && sudo apt install -y code-insiders

安装 remote develop 插件

Linux 下, 避免多次输入密码:

~/.ssh/config

Host *
ControlMaster auto
ControlPath  ~/.ssh/%r@%h-%p
ControlPersist  600

Windows 下, 拷贝公钥到 ssh server 中的 authorized_keys 文件

如果 Remote Host 有代理, 如果 wget 连接失败, 考虑下面的方法

~/.wgetrc

use_proxy=on
http_proxy=http://myproxy.proxy:xxxx
https_proxy=http://myproxy.proxy:xxxx

is unable to watch for file changes in this large workspace

sudo sh -c “echo ‘fs.inotify.max_user_watches=524288’ >> /etc/sysctl.conf” sudo sysctl -p

无法加载文件….ps1, 因为在此系统上禁止运行脚本

以管理员身份运行vscode
get-ExecutionPolicy, 显示Restricted, 表示状态是禁止的
set-ExecutionPolicy RemoteSigned
get-ExecutionPolicy, 就显示RemoteSigned

VSCode 中 Python multiprocessing

import multiprocessing
multiprocessing.set_start_method('spawn', True)

插件开发

安装环境

sudo apt install -y node npm
sudo npm install -g cnpm --registry=https://registry.npm.taobao.org
sudo cnpm install -g n
sudo n latest
sudo cnpm install -g yarn yo generator-code vsce

yarn config set registry https://registry.npm.taobao.org

开始

yo code, 使用TypeScript, Yarn, 生成一个vscode插件的初始化环境

cd ProjectName
yarn install

f5
vsce package
code --install-extension ***.vsix

npm run COMMAND, 运行package.json中的scripts命令

自动设置编码

"files.autoGuessEncoding": true

正则替换

https://www.jianshu.com/p/7935fdcb17d0

把 print 'abc' 替换为 print('abc')
搜索: print (.*)
替换: print($1)

\, 单斜杠为转义
(), 括号为模式匹配

调试时 16 进制 显示

在 watch 窗口, 右键表达式 -> 编辑表达式: 表达式,h

vscode 导出插件列表:

code --list-extensions > vscode-extensions.txt

VSCodium 安装插件列表:

Get-Content vscode-extensions.txt | ForEach-Object { codium --install-extension $_ }

VSCode 插件开发

说明

  • 本页记录 VSCode 插件的初始化, 发布和常见排错要点.
  • 适合作为从零创建扩展或准备发布时的最小操作清单.

初始化项目

npm config set registry https://registry.npmmirror.com
npm install -g yo generator-code
yo code

初始化时建议先明确:

  • 插件是命令型, 语言服务型, 还是 UI 面板型.
  • 是否需要 Webview, TreeView 或 LSP 支持.

插件发布

参考: https://juejin.cn/post/7076649162653040647

yarn global add vsce

权限准备

  1. 申请 Azure DevOps 的个人令牌.
  2. 创建一个发布人 publisher.
  3. 执行 vsce login <publisher name> 并输入 token.
  4. 执行 vsce publish.

令牌说明参考:

常见问题

  • 插件发布后安装成功但无法激活, 常见原因是依赖被放在 devDependencies.
  • 这类运行期依赖需要移动到正式依赖中, 再提升版本重新发布.
  • 若命令已注册但看不到, 先检查 package.json 中的 contributes 配置.
  • 若调试时不生效, 先确认 Extension Development Host 是否加载了当前工作区插件.

使用建议

  • 先做一个只注册命令的最小插件, 再逐步加复杂能力.
  • 发布前最好本地打包一次, 确认产物和依赖完整.
  • 若后续继续整理, 可补 Webview, LSP, 配置项和命令面板示例.

Trae 插件安装记录

说明

  • 本页记录在 Trae 中安装或手动下载扩展包的最小方法.
  • 当前内容主要用于处理“商店无法直接安装”或“需要固定插件版本”这类场景.

参考资料

手动拼接插件下载地址

通用格式:

https://marketplace.visualstudio.com/_apis/public/gallery/publishers/{publisher}/vsextensions/{extension}/{version}/vspackage

参数含义:

  • publisher: 发布者名称.
  • extension: 插件标识.
  • version: 目标版本号.

Dioxus 插件示例

最终下载地址:

https://marketplace.visualstudio.com/_apis/public/gallery/publishers/DioxusLabs/vsextensions/dioxus/0.6.0/vspackage

使用建议

  • 先从插件详情页确认 publisher, extension 和版本号.
  • 若只是为了临时安装, 建议优先使用官方商店流程.
  • 若需要离线分发或锁定版本, 再使用手动下载地址保存本地包.
  • 插件更新后若地址失效, 优先回到插件详情页确认最新版本号.

FFmpeg

说明

  • FFmpeg 是常用的音视频编解码, 转码, 封装和媒体处理工具链.
  • 本页当前以 Linux 环境下的源码编译和常见报错排查为主.

适用场景

  • 需要自定义编译选项或启用特定编解码库.
  • 系统仓库中的 FFmpeg 版本过旧, 需要自行升级.
  • 排查编译依赖缺失, pkg-config 检测失败或位置无关代码相关问题.

Deepin 编译准备

sudo apt install libasound2-dev libpulse-dev
sudo apt install libsdl2-dev

Ubuntu 编译

参考: https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu

sudo apt-get -y install autoconf automake build-essential cmake git-core libass-dev libfreetype6-dev libgnutls28-dev libsdl2-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev meson ninja-build pkg-config texinfo wget yasm zlib1g-dev
sudo apt-get install libx264-dev
sudo apt-get install libx265-dev libnuma-dev
sudo apt-get install libvpx-dev
sudo apt-get install libfdk-aac-dev
sudo apt-get install libmp3lame-dev
sudo apt-get install libopus-dev

git clone --branch release/4.4 https://github.com/ffmpeg/ffmpeg
cd ffmpeg
./configure --prefix="/develop/programs/ffmpeg_build" --enable-pic
make -j
make install

常见报错

gnutls not found using pkg-config

sudo apt-get install libunistring-dev

.rodata can not be used when making a PIE object

  • 重新配置时增加 --enable-pic.
  • 重新编译前建议先 make clean.

使用建议

  • 优先明确目标是“直接用发行版包”, 还是“必须自己编译”, 不要无谓增加维护成本.
  • 编译前先确定是否真的需要额外编解码库, 不然依赖会快速膨胀.
  • 若只是做日常转码或探测, 后续可继续补 ffmpeg, ffprobe 常用命令速记.

qemu_stm32

说明

  • 本页记录 qemu_stm32 的依赖安装, 源码获取和本地编译过程.
  • 适合作为 STM32 模拟运行环境的快速上手入口.

安装依赖

sudo apt-get install -y --no-install-recommends binutils-arm-none-eabi ca-certificates libnewlib-arm-none-eabi findutils gcc git libglib2.0-dev libfdt-dev libpixman-1-dev make openssh-client pkgconf python zlib1g-d

下载code

git clone https://github.com/beckus/qemu_stm32.git

编译

cd qemu_stm32 && mkdir -p build && cd build
../configure --disable-werror --enable-debug --target-list="arm-softmmu" --extra-cflags=-DSTM32_UART_NO_BAUD_DELAY --extra-cflags=-DSTM32_UART_ENABLE_OVERRUN --disable-gtk
make -j4
sudo make install

Bochs 上手指南

说明

  • 本页记录在 Linux 环境中下载, 编译并安装 Bochs 模拟器的过程.
  • 适合作为 x86 模拟调试环境的基础搭建参考.

下载代码, 编译

到https://sourceforge.net/projects/bochs/files/bochs, 下载bochs-2.6.9.tar.gz

tar -xf bochs-2.6.9.tar.gz -C $HOME/develop/software

# 安装编译所需依赖
sudo apt-get install build-essential g++ xorg-dev bison

cd $HOME/develop/software/bochs-2.6.9

# ./configure --prefix=$HOME/programs/bochs --enable-debugger --enable-disasm --enable-iodebug --enable-x86-debugger --with-x --with-x11
./configure --prefix=$HOME/programs/bochs --enable-disasm --enable-iodebug --enable-x86-debugger --with-x --with-x11 --enable-gdb-stub

make -j10 && make install

添加环境变量, ok

测试: bochs

Go 环境总览

说明

  • 本目录用于保存 Go 语言相关的环境安装与工具链记录.
  • 当前内容以 Ubuntu 环境配置为主, 后续可继续补 Windows, 包管理和代理配置等主题.

当前主题

使用建议

  • 若只是快速搭建 Go 开发环境, 先看 Ubuntu 安装记录.
  • 若后续内容增多, 可按 安装, 模块代理, 交叉编译, 调试工具 继续拆分.

Ubuntu 安装 Go

说明

  • 本页整理 Ubuntu 环境下安装和配置 Go 的常见方式.
  • 适合快速搭建开发环境, 校验 GOROOT / GOPATH, 以及处理旧项目的历史版本需求.
  • 若只是做现代 Go 开发, 通常优先使用官方二进制包或系统仓库提供的较新版本.

常见安装方式

方式 1: 使用系统仓库或 PPA

sudo add-apt-repository ppa:gophers/archive
sudo apt-get update
sudo apt-get install golang-1.11-go
  • 这是历史记录中的安装方式, 适合回看旧环境配置.
  • 版本号需要按实际仓库内容调整, 不建议机械照搬旧版本号.

方式 2: 使用官方二进制包

  • 适合希望明确控制安装版本时使用.
  • 安装完成后, 通常只需把 bin 目录加入 PATH.

环境变量示例

export PATH=/usr/lib/go-1.12/bin:$PATH
export GOROOT=/usr/lib/go-1.12
export GOPATH=~/develop/go
  • GOROOT 指向 Go 自身安装位置.
  • GOPATH 用于历史项目工作目录和缓存布局.
  • 新版 Go 已普遍基于 module 工作, 不必再把所有项目都放在 GOPATH/src 下.

安装后验证

go version
go env
which go
  • 先确认命令路径是否符合预期.
  • 再检查 GOROOT, GOPATH, GOMODCACHE 等环境是否正确.

常见建议

  • 新项目优先使用 Go Modules, 不要再依赖旧式 GOPATH 工程结构.
  • 如果系统里存在多套 Go, 要先确认当前 shell 实际命中了哪一个 go.
  • 对 CI 或长期维护项目, 建议固定版本来源, 避免不同机器之间版本漂移.

常见问题

go version 不是刚安装的版本

  • 多半是 PATH 中还有旧版 Go.
  • 先用 which gotype -a go 查看实际命中路径.

模块下载慢或失败

  • 检查代理配置与网络环境.
  • 必要时显式设置 GOPROXY.

旧项目仍依赖 GOPATH

  • 可以保留 GOPATH, 但应明确区分“历史兼容”与“现代模块化项目”两类工作流.

相关文档

主板记录

说明

  • 本页记录主板型号, BIOS 快捷键和通电自启相关设置.
  • 适合做装机, 断电恢复和远程运维前的基础配置速查.
  • 当前以 华南 X99-F8D 为主, 后续可继续补其它设备记录.

华南主板 X99-F8D

常用快捷键

  • F7: 选择开机启动项.
  • Del: 进入 BIOS.

设置上电自启

BIOS -> IntelRCSetup -> PCH Devices -> Restore AC after Power Loss -> Power On
  • 用于断电恢复后自动上电.
  • 做远程无人值守节点时非常重要.

运维检查清单

  • 确认来电自启已开启.
  • 确认系统盘启动顺序正确.
  • 记录 BIOS 版本和默认配置快照.
  • 如果机器承担服务端角色, 同时确认风扇策略和温度告警.

使用建议

  • BIOS 菜单名称可能会因版本不同略有差异, 调整前建议先拍照留档.
  • 远程无人值守设备优先确认“来电自启”是否已开启.
  • 若机器承担服务器, NAS 或采集节点角色, 建议同时检查 RTC 时间和掉电恢复策略.

可继续补充的内容

  • 主板芯片组和 CPU 兼容性.
  • BIOS 升级注意点.
  • NVMe 启动盘与 SATA 启动盘的兼容情况.
  • 风扇曲线与散热调校记录.

相关文档

AutoCAD

说明

  • 本页整理 AutoCAD 的常见使用场景与资料入口.
  • 不保留非官方破解下载链接, 建议通过官方试用, 正式授权或团队统一软件资产渠道获取.

适用场景

  • 机械与电气二维图纸整理.
  • DWG / DXF 文件格式相关的查看, 修改和出图.
  • 与工艺, 结构, 设备安装图协作时的基础 CAD 处理.

使用建议

  • 若只是查看或简单标注图纸, 可优先区分“查看需求”和“编辑需求”, 再决定是否必须使用完整 AutoCAD.
  • 若团队需要协作, 优先统一图层, 标注样式, 坐标单位和打印规范.
  • 若只是临时转换或审图, 可同时评估轻量级 DWG 查看工具, 降低安装和授权成本.

整理方向

  • 后续可继续补充 图层规范, , 标注, 打印, DXF 交换等常见主题.

Docker 总览

说明

  • 本目录收录 Docker 基础笔记, Dockerfile / docker-compose 速查, 以及若干历史部署配置.
  • 如果是日常镜像, 容器, 卷, 网络问题, 优先查看 Docker 基础与技巧.
  • 如果目标是回看某个旧部署目录, 再进入 gitlab/, service/, esp32/, idf/ 等子目录.

常用入口

阅读路径建议

  1. 刚接触 Docker 时, 先看 Docker 基础与技巧.
  2. 需要写镜像时, 再看 Dockerfile 用法.
  3. 需要编排多容器服务时, 再看 docker-compose 用法.
  4. 如果只是回看历史部署组合, 再进入 gitlab/service/ 这些归档目录.

配置归档

使用建议

  • 当前目录里有一批“可运行配置”和一批“知识笔记”, 使用时注意区分.
  • 对于历史 compose 文件, 建议先检查镜像版本, 端口占用和宿主机挂载路径, 不要直接照搬到新环境.
  • 若文档内容继续增多, 后续可把“通用知识”和“历史部署样例”进一步拆层管理.

docker的基本使用及技巧

linux 安装 docker

curl -fsSL https://get.docker.com | bash -s docker –mirror Aliyun

deepin 安装 docker

sudo apt install docker-ce

基本配置

sudo usermod -aG docker maxu

sudo apt install -y docker-compose

加速: curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io

修改镜像默认存储路径
vim /etc/docker/daemon.json:
{
    "registry-mirrors": [
        "....."
    ]
    "graph":"/data/docker"
}

sudo systemctl daemon-reload
sudo systemctl restart docker

sudo docker info

docker 图形化

docker pull portainer/portainer

安装Kitematic(docker的gui工具)

https://github.com/docker/kitematic/releases

aliyun daocker 镜像加速器

https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors

docker好用的命令:

docker ps -aq, 列出所有的容器 ID

docker stop $(docker ps -aq), 停止所有的容器

docker rm $(docker ps -aq), 删除所有的容器

docker rmi $(docker images -q), 删除所有的镜像

docker cp mycontainer:/opt/file.txt /opt/local/
docker cp /opt/local/file.txt mycontainer:/opt/, 复制文件

docker有了专门清理资源(container、image、网络)的命令docker, 1.13 中增加了 docker system prune的命令,针对container、image可以使用docker container prune、docker image prune命令

docker image prune --force --all
docker image prune -f -a: 删除所有不使用的镜像
docker container prune -f, 删除所有停止的容器

基本的docker命令

docker images 查看镜像列表

docker run -i -t -d -p 10000:10000 -p 10001:10001 ImageName –name ContainerName

-i, --interactive
-t, --tty, Allocate a pseudo-TTY
-d, --detach, Run container in background and print container ID

docker logs ContainName 查看日志

docker exec -it ContainName /bin/bash 进入终端

docker container stop ContainName && docker container rm ContainName 停止容器, 删除容器

docker start/stop/rm ContainName 直接操作容器

docker image rm ImageName 删除镜像

docker proxy

sudo mkdir -p /etc/systemd/system/docker.service.d

sudo vim /etc/systemd/system/docker.service.d/http-proxy.conf

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:1081/"

sudo vim /etc/systemd/system/docker.service.d/https-proxy.conf

[Service]
Environment="HTTPS_PROXY=http://127.0.0.1:1081/"

docker-compose

在 docker-compose.yml 所在路径下

docker-compose up

docker-compose up -d, 后台启动并运行容器

docker 学习笔记


# 交互式ubuntu
docker run -i -t -d ubuntu:18.04

# 创建一个容器, 并运行
docker run -d -p 8080:8080 -p 50000:50000 --name jenkins marcelmaatkamp/jenkins-docker-gitlab-ansible

docker run -d --name v2ray -v /etc/v2ray:/etc/v2ray -p 8000:8000 v2ray/official  v2ray -config=/etc/v2ray/config.json

# 查看容器信息
docker container ls

# 使用查看得到的ContainerID, 进入正在运行的容器中
docker exec -it c2e015bad753 /bin/sh

# 若修改了配置文件, 则需要: 停止容器, 并删除容器, 重新创建, 并运行
docker container stop v2ray && docker rm v2ray

tail -f /var/log/v2ray/access.log

# docker 的基本单位:
    image       一般由Dockerfile产生
    container   运行image之后会产生container
    volume
    plugin



# docker 命令帮助

# Commands:
    attach    Attach to a running container                 # 当前 shell 下 attach 连接指定运行镜像
    build     Build an image from a Dockerfile              # 通过 Dockerfile 定制镜像
    commit    Create a new image from a container's changes # 提交当前容器为新的镜像
    cp        Copy files/folders from the containers filesystem to the host path
              # 从容器中拷贝指定文件或者目录到宿主机中
    create    Create a new container                        # 创建一个新的容器,同 run,但不启动容器
    diff      Inspect changes on a container's filesystem   # 查看 docker 容器变化
    events    Get real time events from the server          # 从 docker 服务获取容器实时事件
    exec      Run a command in an existing container        # 在已存在的容器上运行命令
    export    Stream the contents of a container as a tar archive
              # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
    history   Show the history of an image                  # 展示一个镜像形成历史
    images    List images                                   # 列出系统当前镜像
    import    Create a new filesystem image from the contents of a tarball
              # 从tar包中的内容创建一个新的文件系统映像[对应 export]
    info      Display system-wide information               # 显示系统相关信息
    inspect   Return low-level information on a container   # 查看容器详细信息
    kill      Kill a running container                      # kill 指定 docker 容器
    load      Load an image from a tar archive              # 从一个 tar 包中加载一个镜像[对应 save]
    login     Register or Login to the docker registry server
              # 注册或者登陆一个 docker 源服务器
    logout    Log out from a Docker registry server         # 从当前 Docker registry 退出
    logs      Fetch the logs of a container                 # 输出当前容器日志信息
    port      Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
              # 查看映射端口对应的容器内部源端口
    pause     Pause all processes within a container        # 暂停容器
    ps        List containers                               # 列出容器列表
    pull      Pull an image or a repository from the docker registry server
              # 从docker镜像源服务器拉取指定镜像或者库镜像
    push      Push an image or a repository to the docker registry server
              # 推送指定镜像或者库镜像至docker源服务器
    restart   Restart a running container                   # 重启运行的容器
    rm        Remove one or more containers                 # 移除一个或者多个容器
    rmi       Remove one or more images
              # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
    run       Run a command in a new container
              # 创建一个新的容器并运行一个命令
    save      Save an image to a tar archive                # 保存一个镜像为一个 tar 包[对应 load]
    search    Search for an image on the Docker Hub         # 在 docker hub 中搜索镜像
    start     Start a stopped containers                    # 启动容器
    stop      Stop a running containers                     # 停止容器
    tag       Tag an image into a repository                # 给源中镜像打标签
    top       Lookup the running processes of a container   # 查看容器中运行的进程信息
    unpause   Unpause a paused container                    # 取消暂停容器
    version   Show the docker version information           # 查看 docker 版本号
    wait      Block until a container stops, then print its exit code
              # 截取容器停止时的退出状态值


## docker-compose 常用命令

Commands:
    build              Build or rebuild services            # 构建或重建服务
    bundle             Generate a Docker bundle from the Compose file
    config             Validate and view the compose file
    create             Create services
    down               Stop and remove containers, networks, images, and volumes
    events             Receive real time events from containers
    exec               Execute a command in a running container
    help               Get help on a command
    kill               Kill containers                      # 杀掉容器
    logs               View output from containers          # 显示容器的输出内容
    pause              Pause services
    port               Print the public port for a port binding # 打印绑定的开放端口
    ps                 List containers                      # 显示容器
    pull               Pull service images                  # 拉取服务镜像
    push               Push service images
    restart            Restart services                     # 重启服务
    rm                 Remove stopped containers            # 删除停止的容器
    run                Run a one-off command                # 运行一个一次性命令
    scale              Set number of containers for a service   # 设置服务的容器数目
    start              Start services                       # 开启服务
    stop               Stop services                        # 停止服务
    top                Display the running processes
    unpause            Unpause services
    up                 Create and start containers
    version            Show the Docker-Compose version information  # 创建并启动容器


# http://blog.justwe.site/2018/06/28/docker-arg-env
arg 是在 build 的时候存在的, 可以在 Dockerfile 中当做变量来使用
env 是容器构建好之后的环境变量, 不能在 Dockerfile 中当参数使用


# 终止整个服务集合
docker-compose stop

# 终止指定的服务 (这有个点就是启动的时候会先启动 depond_on 中的容器,关闭的时候不会影响到 depond_on 中的)
docker-compose stop nginx

# 查看容器的输出日志
docker-compose logs -f [services...]

# 构建镜像时不使用缓存(能避免很多因为缓存造成的问题)
docker-compose build --no-cache --force-rm

# 移除指定的容器
docker-compose rm nginx

# 启动systemctl
privileged: true
ENTRYPOINT [ "/sbin/init" ]

Dockerfile 用法

说明

  • 本页记录 DockerfileENTRYPOINTCMD 的职责边界.
  • 这两者最容易混淆, 但理解后写镜像启动逻辑会清晰很多.

ENTRYPOINT

  • 常用于定义容器启动后的固定主命令.
  • 适合那些“镜像的职责就是运行这个程序”的场景.

CMD

  • 常用于给启动命令提供默认参数.
  • 运行 docker run ... 时, 可以用额外参数覆盖它.

典型理解

  • ENTRYPOINT 决定“执行谁”.
  • CMD 决定“默认带什么参数”.
  • docker run <image> ... 往往更适合用于替换或补充 CMD.

组合示例

ENTRYPOINT ["python", "app.py"]
CMD ["--host", "0.0.0.0", "--port", "8000"]

上面的镜像默认会执行:

python app.py --host 0.0.0.0 --port 8000

使用建议

  • 固定应用入口时优先用 ENTRYPOINT.
  • 需要给用户留默认参数覆写空间时再配合 CMD.
  • 写生产镜像时, 还应继续关注基础镜像选择, 层数控制和缓存命中率.
  • 若后续继续整理, 可补 COPY, RUN, WORKDIR, 多阶段构建和镜像瘦身记录.

docker-compose 用法

说明

  • 本页记录 docker-compose 的常见启动方式和高频命令.
  • 当前内容以单机本地开发和部署排查中的最小操作为主.

指定一个 docker-compose.yml 运行

docker-compose -f ./docker-compose.yml up

常用命令

docker-compose up
docker-compose up -d
docker-compose down
docker-compose logs -f
docker-compose ps
docker-compose exec <service> sh

常见使用场景

后台启动服务

docker-compose up -d

查看某个服务日志

docker-compose logs -f <service>

进入容器排查问题

docker-compose exec <service> sh

使用提示

  • up -d 适合后台启动服务.
  • down 会停止并移除当前 compose 创建的容器和网络.
  • 如果项目同时维护多份配置, 可以继续叠加多个 -f 参数组合环境.
  • 新环境第一次启动前, 建议先检查镜像版本, 端口和挂载目录.

相关页面

部署自动化环境

说明

  • 本页保留一组非常早期的部署草稿命令.
  • 当前更适合作为后续整理 Redmine + PostgreSQL 部署专题时的过渡入口.

现有记录

docker pull redmine
sudo apt install -y postgresql

可以如何理解

  • Redmine 作为应用容器运行.
  • PostgreSQL 作为独立数据库依赖.
  • 这类组合后续通常还会继续补 数据卷, 备份, 反向代理, 邮件配置 等模块.

最小部署思路

  1. 先准备数据库服务并确认连接方式.
  2. 再启动应用容器并完成数据库连接配置.
  3. 之后再补数据卷, 反向代理和备份策略.
  4. 最后再处理 TLS, 邮件通知和升级回滚方案.

整理建议

  • 如果后续继续补完整部署流程, 建议明确拆成“应用镜像”, “数据库”, “卷与备份”, “反向代理” 4 个部分.
  • 更复杂的历史容器组合可以参考 GitLab 与 Taiga 部署记录.
  • 若未来需要长期维护, 建议把这里的草稿命令收敛到更正式的单应用部署文档中.

GitLab 与 Taiga 部署记录

说明

  • 本目录保留了一份历史 docker-compose.yml, 用于同时部署 GitLab, TaigaPostgreSQL.
  • 内容更适合视为旧部署样例, 不能直接视为当前最佳实践.

当前 compose 涉及的服务

  • gitlab: 使用 gitlab/gitlab-ce:latest.
  • taiga: 使用 benhutchins/taiga.
  • postgres: 为 Taiga 提供数据库.

适合如何使用

  • 回看历史多服务 compose 的组织方式.
  • 对照理解早期自建协作平台的部署思路.
  • 在真正重建环境前, 先提取其中仍有参考价值的端口, 卷和依赖关系设计.

使用前重点检查

  • external_url, SMTP, 端口映射和卷挂载路径都需要按当前环境重新核对.
  • 原文件中保留了多段注释和备选配置, 使用前建议先做精简.
  • 示例中的密码和主机名仅用于历史记录, 实际部署必须替换.
  • 若用于公网部署, 还需要额外补反向代理, TLS, 备份和升级策略.

更合理的整理方式

  1. GitLabTaiga 拆成各自独立的部署文档.
  2. 单独整理数据库与备份策略.
  3. 再把反向代理, 邮件和公网访问规则抽成公共章节.
  4. 最后再保留本页作为“历史组合部署样例”.

Redmine 插件记录

主题:

代码评审插件:

常用命令:

bundle install
bundle exec rake redmine:plugins:migrate RAILS_ENV=production

整理建议

  • 若后续继续治理 Docker 目录, 可把本页进一步压缩成“历史样例说明页”, 具体命令迁入更明确的专题页.

服务容器记录

说明

  • 本目录保留了一份面向常驻服务的 compose 配置.
  • 当前主要记录的是 v2ray 服务容器, 更适合作为历史部署样例而非通用最佳实践.

当前服务

v2ray

  • 镜像: v2ray/official
  • 端口: 1080, 1081
  • 挂载目录: /docker/v2ray:/etc/v2ray

使用方式

在本目录执行:

docker-compose up -d
docker-compose logs -f v2ray

适合如何使用

  • 把这里视为“历史配置样例”, 先理解服务组成和挂载方式.
  • 在新环境复用前, 先检查镜像名称, 端口, 配置目录和网络策略是否仍然合理.
  • 若要长期维护, 建议把实际部署版本, 环境变量和数据目录写成更正式的专题页.

使用建议

  • 在新环境中使用前, 请先确认配置目录, 端口和镜像来源是否仍然合适.
  • 若需要长期维护, 建议补充镜像版本固定, 数据目录说明和环境变量说明.
  • 若只是学习容器化思路, 更建议先看上层的 Docker 总览, Dockerfile, docker-compose 专题页.

备注

  • me.service 是历史 systemd 服务文件记录.

Nginx 笔记

说明

  • 本页记录 Nginx 的基础文件服务配置示例.
  • 适合作为静态目录暴露与自动索引配置的快速参考.

一个简单的文件服务

在conf.d目录中,新建一个.conf文件.

autoindex on;# 显示目录
autoindex_exact_size on;# 显示文件大小
autoindex_localtime on;# 显示文件时间

server {
    listen       8080 default_server;
    listen       [::]:8080 default_server;
    server_name  _;
    #root         /usr/share/nginx/html;
    root         /data/;
    location / {
    }
    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

Linux 总览

说明

  • 本目录收录 Linux 日常工具, 发行版记录, 桌面环境, Homebrew 与兼容层相关笔记.
  • 若问题偏通用命令或排障, 可先看 linux工具 / linux常用命令 / Linux 综合笔记; 若偏发行版, 再看 Ubuntu / Deepin / Manjaro.

阅读路径

  • 通用命令与工具链问题, 优先看 Linux 工具Linux 常用命令.
  • 系统级杂项排查, 优先看 Linux 综合笔记Linux 常见问题.
  • 发行版差异问题, 再看 Ubuntu, Deepin, Manjaro.
  • 包管理器补装工具场景, 可继续看 Homebrew.
  • 兼容运行 Windows 软件时, 再看 Wine 笔记.

常用入口

发行版与桌面

兼容层与扩展

  • Wine 笔记
  • make_system/ 下保留了一批历史脚本, 更适合视为资料归档, 不建议直接照搬执行.

适合如何使用

  • 先用总览页判断问题属于“命令排查”, “发行版差异”, 还是“兼容层 / 桌面环境”问题.
  • 遇到安装失败或环境异常时, 先查对应发行版页, 再回到更底层的命令或日志排查.
  • 历史页面里有不少版本敏感命令, 新机器执行前要先确认系统版本和镜像源状态.

目录边界

  • 本目录主要关注 Linux 系统环境与桌面工具层面的知识.
  • 若问题已经落到具体语言, 框架或数据库, 通常应回到对应专题目录继续看.
  • 若后续继续治理, 可逐步把“通用命令速查”和“发行版初始化记录”拆成更清晰的两层入口.

Linux 工具

说明

  • 本页整理 Linux 环境下常见的开发与系统工具使用记录.
  • 当前覆盖二进制分析, 截图工具, 逻辑分析器, Samba 共享和历史下载工具等主题.
  • 若只是查通用命令, 优先看 Linux 常用命令.

二进制与库分析

查看静态库 / 动态库中的符号与段信息

readelf -c libavutil.a
objdump -x libavutil.a
  • readelf 适合快速查看段信息.
  • objdump 信息更详细, 适合进一步分析对象文件结构.

桌面工具

flameshot

flameshot gui
  • 用于交互式截图.
  • 适合做界面标注, 错误记录和文档插图.

WPS 中文字体部分无显示

  • 这类问题通常和系统字体缺失有关.
  • 可参考字体安装与字体缓存刷新流程: https://mxy493.xyz/2019040840601/
  • 更新字体缓存后, 往往需要重启应用或重新登录桌面环境.

设备与调试工具

DSView 逻辑分析器

编译流程记录:

git clone https://github.com/DreamSourceLab/DSView.git
sudo apt-get install git-core build-essential cmake autoconf automake libtool pkg-config   libglib2.0-dev libzip-dev libudev-dev libusb-1.0-0-dev   python3-dev qt5-default libboost-dev libboost-test-dev libboost-thread-dev   libboost-system-dev libboost-filesystem-dev check libfftw3-dev

cd libsigrok4DSL
./autogen.sh
./configure
make -j
sudo make install
cd ..

cd libsigrokdecode4DSL
./autogen.sh
./configure
make -j
sudo make install
cd ..

cd DSView
mkdir build -p && cd build && cmake ..
make -j
sudo make install
  • 更适合做本地编译记录与环境追溯.
  • 真正排错时, 还要结合 libusb, udev 和 Qt 依赖逐项检查.

文件共享

Samba 共享

安装:

sudo apt-get install samba

示例配置:

[profiles]
comment = Share Folder
path = /develop/share
guest ok = yes
browseable = yes
public = yes
writable = yes
force users = nobody
force group = nogroup
force create mode = 0775
force directory mode = 0775
  • 修改后记得检查配置文件路径与服务重启流程.
  • Linux 访问 Windows 共享时, 还要结合挂载参数和权限处理一起排查.

参考: https://segmentfault.com/a/1190000039363538

历史工具记录

BT 下载工具 Tixati

  • 在界面里点击 Transfers -> Add, 打开 URL 后输入 BT 链接即可开始搜索与下载.
  • 这类记录更偏历史工具备忘, 当前可作为补充说明保留.

pyenv

git clone https://github.com/pyenv/pyenv.git ~/.pyenv
cd ~/.pyenv && src/configure && make -C src
  • 更完整的 Python 环境管理建议回到 Python 总览pyenv 相关页面继续整理.

使用建议

  • 本页适合保留“工具入口 + 最小命令 + 排查线索”, 不适合承载超长安装教程.
  • 某个工具已经形成稳定主题时, 建议单独升级为独立专题页.
  • 与桌面环境强相关的工具问题, 也可以回到发行版页面继续记录上下文.

相关文档

Linux 常用命令

说明

  • 本页整理 Linux 日常排查和下载场景里最常用的一批命令.
  • 当前以 curl, grep, find, tar, ss, ps, chmod 为主, 适合快速速查.
  • 若问题已经升级为系统层排障, 建议继续看 Linux 综合笔记.

网络与下载

curl

curl -v URL
curl -i URL
curl -L URL
curl -I URL
curl -L -o file.zip https://example.com/file.zip

常见含义:

  • -v: 输出详细请求和响应过程.
  • -i: 返回响应头和响应体.
  • -L: 自动跟随重定向.
  • -I: 只请求响应头.
  • -o: 指定输出文件名.

常见场景:

curl -I https://example.com
curl -X POST https://example.com/api -H "Content-Type: application/json" -d '{"name":"demo"}'

文本与文件查找

grep

grep -R "keyword" .
grep -n "keyword" file.txt
grep -RIn "keyword" src/
  • 适合查日志, 配置和源码中的关键字.

find

find . -name "*.log"
find . -type f -mtime -1
find . -type d -name build
  • 适合按名称, 类型和修改时间筛选文件.

压缩与归档

tar

tar -czf archive.tar.gz dir/
tar -xzf archive.tar.gz
tar -tf archive.tar.gz
  • 打包, 解压和查看压缩包内容都很常见.

进程与端口

ps

ps aux | grep python
  • 用于查看进程是否在运行.

ss

ss -tunlp
ss -lnt
  • 适合查看端口监听和网络连接状态.
  • 排查服务不通时通常比老的 netstat 更直接.

权限与执行

chmod

chmod +x script.sh
chmod 644 file.txt
chmod -R 755 dir/
  • 常用于脚本执行权限和基础文件权限调整.
  • 生产环境里不要对大目录粗暴 777.

使用建议

  • 排查接口问题时, 先用 curl -vcurl -I 看响应状态和重定向.
  • 查日志和源码时, grepfind 常常一起使用.
  • 涉及端口问题时, 优先看 ss -tunlp.
  • 更系统的服务管理和防火墙命令, 可继续看 Linux 综合笔记.

相关文档

Shell 笔记

说明

  • 本页记录常见 Shell 条件判断和命令拼接写法.
  • 适合快速回忆 test, 退出码判断和基础脚本控制流.

[[[

  • [ 本质上是 test 命令的一种写法, 对空格要求严格.
  • [[ 是更现代的 Shell 条件表达式, 在模式匹配和字符串判断上通常更安全.

示例:

if [ "$num1" -gt "$num2" ]; then
    echo ok
fi

if [[ -n "$name" && "$name" == test* ]]; then
    echo matched
fi

常见判断

[ $? -eq 0 ] && command
[ -f ./a.txt ]
[ -d ./build ]
[ -n "$value" ]
[ -z "$value" ]

使用建议

  • [ 和参数之间必须有空格, 否则很容易语法错误.
  • 涉及变量时尽量加引号, 避免空字符串或空格导致判断异常.
  • 只要脚本运行环境允许, 条件表达式优先使用 [[ ... ]] 可减少很多转义问题.
  • 链式执行时, 可用 && 表示前一步成功才继续, 用 || 表示失败时兜底.

Linux 运维笔记

说明

  • 本页整理常见的 Linux 系统维护与排障命令, 适合作为日常速查入口.
  • 内容以 Ubuntu / Debian 生态为主, 也覆盖部分通用命令行工具用法.
  • 若你只是查某个发行版的个性化配置, 可优先回到本目录其它专题页.

适合如何使用

  • 需要快速回忆包管理, 网络, 防火墙, 服务管理命令时, 先看本页.
  • 需要发行版特定配置时, 再看 Deepin, ArchLinux, manjaro笔记 等页面.
  • 需要 Wine, shell 或系统构建相关内容时, 继续进入 wine/, shell/, make_system/.

常用命令速查

软件包与系统信息

sudo apt update
sudo apt upgrade
sudo apt install <package>
sudo apt remove <package>
sudo apt autoremove
uname -a
lsb_release -a
  • 包管理优先使用系统仓库, 避免长期混用来源不明的第三方源.
  • 遇到依赖问题时, 可以先做 sudo apt update 再重新安装.

服务与启动项

systemctl status <service>
sudo systemctl start <service>
sudo systemctl restart <service>
sudo systemctl enable <service>
sudo systemctl disable <service>
journalctl -u <service> -f
  • 服务异常时, 一般先看 systemctl statusjournalctl 日志.
  • 涉及图形登录器切换时, 还要同时确认 display-manager.service 指向哪个实现.

磁盘与挂载

df -h
lsblk
sudo fdisk -l
mount | grep <path>
sudo mount /dev/<device> <mountpoint>
sudo umount <mountpoint>
  • 排查磁盘空间时优先看 df -h.
  • 排查设备识别与分区情况时优先看 lsblk.

网络与端口

ip addr
ip route
ss -tunlp
ping <host>
curl -I <url>
  • 端口占用优先用 ss -tunlp 查看.
  • 网络连通性问题通常按 网卡 -> 路由 -> DNS -> 目标服务 顺序检查.

防火墙速记

UFW 常用操作

sudo ufw enable
sudo ufw disable
sudo ufw status verbose
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp comment 'accept Apache'
sudo ufw allow 443/tcp comment 'accept HTTPS connections'
sudo ufw allow 1194/udp comment 'OpenVPN server'
sudo ufw allow 3000:4000/tcp
sudo ufw allow from 104.22.10.214
sudo ufw allow from 104.22.11.213 to any port 25 proto tcp
sudo ufw deny from 203.5.1.43
sudo ufw status numbered
sudo ufw delete 6
sudo ufw reload
  • 调整规则前先看 sudo ufw status numbered, 便于回滚和删除.
  • 开放端口时尽量明确协议, 端口和来源地址, 减少误暴露风险.

诊断辅助

sudo more /var/log/ufw.log
sudo tail -f /var/log/ufw.log
sudo ufw show listening
sudo ufw show added
sudo ufw reset
  • 规则改完不生效时, 可以先检查日志和监听情况.
  • reset 会清空现有规则, 使用前应确认是否需要备份当前配置.

常见系统操作

安装 squid

sudo apt install squid
systemctl status squid
  • 适合快速搭本地或局域网代理实验环境.
  • 生产环境还需要补充访问控制, 日志和转发策略配置.

切换 display manager

systemctl status display-manager.service
sudo dpkg-reconfigure gdm3
sudo dpkg-reconfigure sddm
  • 先确认当前实际运行的是 gdm3, sddm 还是其它显示管理器.
  • 切换桌面环境后无法进图形界面时, 这个步骤很常用.

Ubuntu 20 安装 Deepin 桌面

sudo add-apt-repository ppa:ubuntudde-dev/stable
sudo apt install ubuntudde-dde
systemctl status display-manager.service
sudo dpkg-reconfigure sddm
  • 若登录界面异常, 通常要检查显示管理器是否与桌面环境组合正确.
  • 更完整的 Deepin 相关环境配置可继续看 Deepin.

动态依赖排查

ldd

ldd /usr/local/bin/st-info
  • 用于查看 ELF 可执行文件依赖了哪些动态库.
  • 若输出里出现 not found, 说明对应动态库未安装, 未在搜索路径中, 或版本不匹配.
  • 很多 GUI 程序, 驱动工具和自己编译的二进制都适合先用它做依赖自检.

常见故障记录

GLIBC_2.28 not found

  • 这类报错通常意味着二进制构建环境高于当前系统运行环境.
  • 常见思路是: 降低构建环境版本, 使用目标系统兼容的容器重新构建, 或升级目标系统.
  • 不建议直接手工替换系统 glibc, 风险很高.

参考: https://blog.csdn.net/Youning_Yim/article/details/129343107

目录边界

  • 本页保留通用 Linux 运维与系统管理速记.
  • 发行版专属配置请放在各自页面.
  • 某个工具已经有独立专题时, 本页只保留导航和最小速记, 避免与专题页重复维护.

Linux 常见问题

说明

  • 本页收敛 Linux 环境下常见的证书, 包管理和初始化排错线索.
  • 当前内容先以 apt 和 HTTPS 证书问题为主, 后续可继续补网络, 图形环境和权限问题.
  • 若问题更偏系统命令和基础排查, 也可结合 Linux 综合笔记 一起看.

常见问题 1: HTTPS 证书导致包拉取失败

报错示例:

Error: Could not fetch URL https://...
There was a problem confirming the ssl certificate

处理方式:

sudo apt-get install apt-transport-https ca-certificates software-properties-common

排查顺序

1. 先看系统时间

  • 时间错误会直接导致证书校验异常.
  • 新装系统, 双系统切换和离线设备上最常见.

2. 再看 CA 证书包

  • 检查 ca-certificates 是否安装完整.
  • 必要时重新安装或更新证书缓存.

3. 再看代理或镜像源

  • 公司代理, 自签名证书和镜像站异常都可能导致 HTTPS 拉取失败.
  • 如果只是某一个源失败, 也要排查该源本身的证书与可用性.

4. 最后看网络链路

  • DNS 配置错误, 网络劫持和透明代理也会表现为证书问题.
  • 可先用 curl -v 单独验证目标 URL.

其它高频排查方向

apt update 很慢或卡住

  • 先确认镜像源是否可用.
  • 再确认 DNS 和代理配置.
  • 必要时切回官方源做对照测试.

安装包时依赖冲突

  • 先做 sudo apt update.
  • 再看是否混用了第三方源, PPA 或手工安装包.
  • 尽量避免长期混用发行版包和来源不明的二进制包.

命令找不到

  • 先看程序是否已安装.
  • 再检查 PATH 和 shell profile 是否正确加载.
  • 若刚安装完软件仍找不到命令, 可以先重开 shell 再验证.

使用建议

  • 涉及网络代理, 包源和 SSL 问题时, 建议优先记录完整报错, 不要只保留结论.
  • 新系统初始化时, 可以把“时间同步, 证书, 包源, DNS”作为第一轮检查项.
  • 若问题已经演变成发行版特有故障, 建议回到对应专题页继续记录.

相关文档

Ubuntu

说明

  • 本页记录 Ubuntu 下较常见的桌面与系统配置项, 当前重点是远程音频和合盖行为调整.
  • 适合作为日常开发机, 远程桌面机器或实验机的系统配置速查页.

ToDesk 音频依赖

sudo apt install pulseaudio

参考:

适用场景:

  • 远程桌面连接后没有声音.
  • 需要检查系统是否具备最基本的音频服务组件.

合盖功能

配置文件:

/etc/systemd/logind.conf

示例配置:

; 使用电池供电时, 合盖挂起
HandleLidSwitch=suspend
; 使用外部电源时, 合盖忽略
HandleLidSwitchExternalPower=ignore
; 使用扩展坞时, 合盖忽略
HandleLidSwitchDocked=ignore

常见使用场景

  • 外接显示器时希望合盖后继续运行.
  • 远程开发时希望合盖不影响任务执行.
  • 笔记本单独使用时仍希望保留默认挂起行为.

使用建议

  • 修改 logind.conf 后, 记得重启相关服务或直接重启系统再验证行为.
  • 做远程开发或外接显示器场景时, 通常需要重点确认“接电源合盖是否继续运行”.
  • 远程桌面无声音时, 优先检查 PulseAudio 是否存在, 服务状态是否正常, 以及远程工具自身的音频设置.
  • 若后续继续整理, 可补软件源, 驱动, Docker, 开发工具链和桌面远程运维常见问题.

Deepin

说明

  • 本页记录 Deepin 场景下的一些历史安装与环境配置操作.
  • 当前重点包括 Wine, Node.js, MySQL 和新版 clang 工具链的安装线索.
  • 这类记录偏系统初始化与兼容配置, 真正执行前应结合当前发行版版本再次确认.

安装 Wine

sudo apt install deepin-wine

适用场景:

  • 需要在 Deepin 环境下运行部分 Windows 程序.
  • 想直接复用系统已有的 Deepin Wine 兼容层.

安装 Node.js

历史下载记录:

https://npm.taobao.org/mirrors/node/latest-v15.x/node-v15.3.0-linux-x64.tar.xz

解压后创建链接:

sudo ln -s /develop/programs/node-v15.3.0-linux-x64/bin/node /usr/local/bin/
sudo ln -s /develop/programs/node-v15.3.0-linux-x64/bin/npm /usr/local/bin/

设置镜像源:

npm config set registry https://registry.npm.taobao.org
npm config get registry

使用建议:

  • 这类手工软链方式更适合历史环境记录.
  • 如果是新机器, 优先考虑更稳定的版本管理方案或系统包管理方式.

安装 MySQL

sudo apt install libmecab2
sudo apt install -f

sudo dpkg -i mysql-community-client-plugins_8.0.22-1debian10_amd64.deb
sudo dpkg -i mysql-community-client-core_8.0.22-1debian10_amd64.deb
sudo dpkg -i mysql-community-client_8.0.22-1debian10_amd64.deb
sudo dpkg -i mysql-client_8.0.22-1debian10_amd64.deb

sudo dpkg -i mysql-common_8.0.22-1debian10_amd64.deb
sudo dpkg -i mysql-community-server-core_8.0.22-1debian10_amd64.deb
sudo dpkg -i mysql-community-server_8.0.22-1debian10_amd64.deb

补充说明:

  • 安装过程中若弹出设置密码窗口, 这里的历史记录是“使用默认的加密方式”.
  • 登录 MySQL:
sudo mysql -uroot -p

安装新版 clang

编辑 /etc/apt/sources.list, 添加:

deb http://apt.llvm.org/buster/ llvm-toolchain-buster main
deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster main
deb http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main
deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-10 main
deb http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main
deb-src http://apt.llvm.org/buster/ llvm-toolchain-buster-11 main

导入 key 并安装:

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install clang-11

使用建议

  • Deepin 相关记录受系统版本影响较大, 真正执行前要先确认软件源是否仍可用.
  • 老版本手工安装包和软链配置, 更适合当作迁移参考, 不建议直接无脑复用.
  • 若后续继续整理, 可补系统源, 输入法, Docker, 远程桌面和 Deepin Wine 的实际排错记录.

Manjaro 笔记

说明

  • 本页记录 Manjaro 初始化安装, 国内源, 输入法, 开发环境和常用软件配置.
  • 大多数命令来自历史版本实测, 使用前请结合当前系统版本确认.

系统镜像与启动盘

下载镜像:

写入 U 盘:

dd if=manjaro-xfce-19.0.2-200311-linux54.iso of=/dev/sdb bs=16M

基础初始化

sudo pacman-mirrors -c China
sudo pacman -Syyu
sudo pacman -S vim

添加 archlinuxcn 源:

[archlinuxcn]
SigLevel = Optional TrustedOnly
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch
sudo pacman -Syy && sudo pacman -S archlinuxcn-keyring

中文与输入法

安装中文字体:

sudo pacman -S wqy-bitmapfont wqy-microhei wqy-microhei-lite wqy-zenhei

安装输入法:

sudo pacman -S kcm-fcitx fcitx-googlepinyin
sudo pacman -S fcitx-lilydjwg-git fcitx-configtool fcitx-sogoupinyin

~/.xprofile 示例:

export LC_ALL=zh_CN.UTF-8
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"

如果搜狗输入法异常, 可先卸载并清理配置:

sudo pacman -Rs fcitx-lilydjwg-git fcitx-configtool fcitx-sogoupinyin
cd ~/.config
rm -rf SogouPY SogouPY.users sogou-qimpanel fcitx

Shell 与开发环境

安装 zshoh-my-zsh:

sudo pacman -S zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
sudo usermod -s /bin/zsh USERNAME

可选主题记录: mortalscumbag, afowler, gentoo, gallois.

常用软件

sudo pacman -S visual-studio-code-bin
sudo pacman -S screenfetch
sudo pacman -S clang gdb
sudo pacman -S deepin-screenshot

使用建议

  • 新系统初始化时, 先完成镜像源, 系统更新和基础编辑器安装.
  • 输入法异常通常优先检查 fcitx 配置和 ~/.xprofile 环境变量.
  • AUR 或第三方源包较多时, 更新前应先确认依赖关系和兼容风险.
  • 若后续继续整理, 可补驱动安装, Docker, 虚拟化和开发机备份策略.

KDE

说明

  • 本页记录 KDE 桌面环境下的常用软件安装和系统配置操作.
  • 当前内容以 Ubuntu / KDE 场景为主, 也适合作为桌面环境问题的速记入口.

常用软件

sudo snap install blender aria2c

适用场景:

  • blender: 图形或建模相关工作.
  • aria2c: 多连接下载和大文件拉取.

Ubuntu 安装 AMD 显卡驱动

参考: https://www.amd.com/zh-hans/support/kb/release-notes/rn-amdgpu-unified-linux-20-20

./amdgpu-install --opencl=pal,legacy --no-dkms --headless

使用建议:

  • 安装前先确认内核版本和驱动支持范围.
  • 若只是远程计算或无桌面渲染需求, 可按需选择更精简的安装参数.

安装 OpenCL

排查方向:

  • 确认驱动与 OpenCL 运行时是否版本匹配.
  • 确认 GPU 是否已被系统和上层工具正确识别.

解除“输入密码以解锁密钥环”

sudo apt-get install seahorse

处理思路:

  • 在应用程序中搜索 seahorse.
  • 删除或重置对应的 keyring 项.
  • 重新登录后观察提示是否消失.

整理建议

  • 若后续继续补充, 可加入输入法, 缩放, 多屏和主题配置相关记录.
  • 若问题已偏向发行版级驱动或包管理, 应回到 Linux 总览 或对应发行版页继续看.

OpenWrt 总览

说明

  • 本目录收录 OpenWrt/LEDE 相关的构建与历史使用记录.
  • 当前内容以源码编译和路由器配置实验为主.

当前主题

阅读路径

  • 需要快速开始编译时, 先看 OpenWrt / LEDE 编译.
  • 需要回看路由器配置, 桥接和刷写实验时, 再看 OpenWrt 试玩笔记.

目录边界

  • 本目录保留固件构建与路由器系统实验.
  • 若问题更偏 Linux 通用环境, 应回到 tools/linux/.

OpenWrt / LEDE 编译

说明

  • 本页记录 LEDE/OpenWrt 源码构建的最小流程.
  • 适合作为本地尝试自定义固件时的环境准备速记.
  • 如果目标已经转向具体机型适配, 还需要继续补“目标配置”, “插件选择”与“刷写方式”记录.

适用场景

  • 第一次在本地准备 OpenWrt/LEDE 编译环境.
  • 想先跑通源码下载, feeds 更新和一次完整构建.
  • 排查下载阶段或编译阶段的基础失败问题.

manjaro 安装编译环境

yay -S openwrt-devel

下载代码

git clone https://github.com/coolsnowwolf/lede
cd lede

更新 feeds

./scripts/feeds update -a
./scripts/feeds install -a

配置与编译

make menuconfig
make -j11 download V=s
make -j11 V=s

常见关注点

  • 第一次构建前先确认磁盘空间和网络环境足够稳定.
  • download 阶段失败时, 优先先把依赖拉全再重新编译.
  • 真正面向具体路由器时, 要先确认目标架构, 分区布局和刷机方式.
  • 多线程编译数不要机械照抄, 应结合本机 CPU, 内存和 IO 情况调整.

相关文档

OpenWrt 试玩笔记

说明

  • 本页记录特定路由器与主机环境下的 OpenWrt 源码编译和功能选择实践.
  • 更适合作为具体机型与场景的实验记录, 与通用编译流程页互补.

openwrt 源码编译

参考: https://openwrt.org/docs/guide-developer/quickstart-build-images

Host系统: Ubuntu 18.04
Wifi设备: Newifi D2

对于 ubuntu18 or later:
    sudo apt install build-essential libncurses5-dev python unzip


git clone https://git.openwrt.org/openwrt/openwrt.git && cd openwrt


./scripts/feeds update -a
./scripts/feeds install -a

make menuconfig

# 选择要编译目标设备
Target System --> (X) MediaTek Ralink MIPS
Subtarget --> (X) MT7621 based boards
Target Profile --> (X) Newifi D2

# 添加硬盘格式支持(否则无法识别mount的设备)
Kernel modules --> Filesystems --> <*> kmod-fs-ext4 (移动硬盘EXT4格式选择)
Kernel modules --> Filesystems --> <*> kmod-fs-vfat(FAT16 / FAT32 格式 选择)
Kernel modules --> Filesystems --> <*> kmod-fs-ntfs (NTFS 格式 选择)

# 添加USB相关支持
Kernel modules --> USB Support --> <*> kmod-usb-core
Kernel modules --> USB Support --> <*> kmod-usb-ohci
Kernel modules --> USB Support --> <*> kmod-usb-storage
Kernel modules --> USB Support --> <*> kmod-usb-storage-extras
Kernel modules --> USB Support --> <*> kmod-usb2
Kernel modules --> USB Support --> <*> kmod-usb3

# 添加编码
Kernel modules --> Native Language Support --> <*> kmod-nls-cp936
Kernel modules --> Native Language Support --> <*> kmod-nls-iso8859-1
Kernel modules --> Native Language Support --> <*> kmod-nls-utf8

# 中文
LuCI --> Modules --> Translation --> <*> Chinese (zh-cn)

# Web GUI
LuCI ->Collections -> [*] luci.....LuCI interface with Uhttpd as Webserver

# 添加USB自动挂载
Base system --> <*> blockd.....Block device automounting

退出, 保存

安装桥接工具
./scripts/feeds install luci-proto-relay

vim package/base-files/files/etc/shadow, 设置密码(admin):
    root:$1$2nX0sqkM$XILd1/grLi/99Lgvp6Clz0:16922:0:99999:7:::

make -j12 V=s

# 生成文件位于: bin/targets/ramips/mt7621

烧写

http://192.168.1.1/

固件更新 ->

连接wifi

网络 --> 无线 --> 扫描 --> 选择热点, 加入网络 --> 密码, 5G通道就选择 US 149 --> ok

修改ip

18的版本, 只能通过ssh, 在设备上直接修改, 因为 There is a emergency revert feature in version 18 that will go back if the web GUI cannot be reached.

vim /etc/config/network

修改interface ’lan’中的 option ipaddr ‘192.168.1.1’

/etc/init.d/network restart

设置二级路由桥接

Homebrew

说明

  • 本页整理 Linux 环境下安装和使用 Homebrew 的最小步骤.
  • 适合在系统包管理器之外补充较新的开发工具或交叉编译工具链.
  • 若系统仓库已经满足需求, 通常优先使用发行版原生包管理器.

适用场景

  • 系统仓库版本过旧, 但又不想手工源码编译.
  • 需要快速补装某些开发工具, 并希望在多台机器间保持一致安装方式.
  • 想给用户态工具做隔离, 减少对系统环境的直接污染.

安装

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

安装后常见初始化动作:

brew doctor
brew --version
brew config

路径与环境变量

  • Linux 下常见安装位置包括 ~/.linuxbrew 或 Homebrew 当前默认目录.
  • 安装完成后需要把 brew 所在目录加入 PATH.
  • 若 shell 启动后找不到 brew, 先检查 profile 文件是否已写入环境变量.

常见用法

搜索包

brew search <name>

安装包

brew install <formula>

查看环境建议

brew doctor

历史记录中的交叉编译工具链示例

brew install --cask gcc-arm-embedded
  • 这类工具安装后, 建议立刻检查版本和可执行路径.

使用建议

  • 优先确认发行版自带包是否已经满足需求, 避免多套包管理器混用过深.
  • 对编译链工具, 安装后最好立刻检查版本和可执行路径.
  • 涉及系统级依赖时, brew 与发行版包并不一定完全兼容, 混搭要谨慎.
  • 如果团队机器较多, 应约定清楚到底用系统包, brew, 还是容器化工具链.

常见问题

brew 安装成功但命令找不到

  • 多半是 PATH 未生效.
  • 先检查 shell profile 是否加载了 Homebrew 环境脚本.

包装好但运行失败

  • 先看是否缺系统级依赖.
  • 再看该工具是否更适合由发行版原生包提供.

同一工具出现多个版本

  • 很可能是系统仓库和 brew 同时安装了同名程序.
  • 先用 whichtype -a 确认当前实际命中路径.

相关页面

Raspberry Pi 笔记

说明

  • 本页记录 Raspberry Pi OS 烧写, 首次启用 SSH, 换源和常见运维操作.
  • 适合作为树莓派上手配置和家庭服务折腾的基础入口.

使用 imager 烧写TF卡

https://mirrors.tuna.tsinghua.edu.cn/raspbian-images/raspbian_lite/images/raspbian_lite-2020-02-14/2020-02-13-raspbian-buster-lite.zip

烧写后, 先在boot分区中,添加一个文件名是“ssh“的文件, 无后缀, 空文件, 这样会默认开启ssh, 这样可以不用显示屏

修改密码

修改软件源, 并更新源

/etc/apt/sources.list

# deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
deb http://mirrors.aliyun.com/raspbian/raspbian/ buster main contrib non-free rpi

/etc/apt/sources.list.d/raspi.list

# deb http://archive.raspberrypi.org/debian/ buster main
deb http://mirrors.ustc.edu.cn/archive.raspberrypi.org/debian/ buster main

reboot

安装OMV

在树莓派上安装OMV:
https://github.com/OpenMediaVault-Plugin-Developers/docs

reboot

issues

  • 当前如果需要直接下载安装脚本, 优先使用 canonical raw 链接:

    https://raw.githubusercontent.com/OpenMediaVault-Plugin-Developers/installScript/master/install

  • 下面保留的是 2020 年访问失败时的历史输出, 用于说明 GitHub 重定向链路和代理问题.

    1. 不知为何刚安装好的树莓派上无法访问github上的资源

    –2020-04-18 04:01:08– https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install Resolving github.com (github.com)… 13.250.177.223 Connecting to github.com (github.com)|13.250.177.223|:443… connected. HTTP request sent, awaiting response… 302 Found Location: https://raw.githubusercontent.com/OpenMediaVault-Plugin-Developers/installScript/master/install [following] –2020-04-18 04:01:09– https://raw.githubusercontent.com/OpenMediaVault-Plugin-Developers/installScript/master/install Resolving raw.githubusercontent.com (raw.githubusercontent.com)… 0.0.0.0, :: Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|0.0.0.0|:443… failed: Connection refused. Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|::|:443… failed: Connection refused.

    处理方法: 使用代理, 配置 http_proxy, https_proxy

挂载硬盘, 设置共享, 添加用户权限

2021-1201

1. 系统镜像

    https://mirrors.ustc.edu.cn/raspberry-pi-os-images/raspios_armhf/images/raspios_armhf-2021-11-08/

    基本桌面环境

2. 使用 Etcher 烧写镜像

    deepin 应用市场安装 Etcher

VirtualBox 使用指南

说明

  • 本页记录 Linux 环境下安装和启用 VirtualBox 的最小步骤.
  • 适合快速搭建本地虚拟机实验环境或回忆内核模块相关配置.

安装

yay -S virtualbox linux56-virtualbox-host-modules virtualbox-ext-oracle
sudo gpasswd -a $USER vboxusers
sudo vboxreload
reboot now

步骤理解

  • 安装 VirtualBox 本体.
  • 安装对应内核版本的 host modules.
  • 把当前用户加入 vboxusers 组.
  • 重新加载模块并重启系统, 确保驱动生效.

使用建议

  • 安装时要特别注意宿主机当前内核版本与 host-modules 包是否匹配.
  • 若启动虚拟机时报权限或驱动错误, 优先检查用户组, 内核模块和 Secure Boot 相关限制.
  • 发行版升级内核后, 若 VirtualBox 失效, 往往要先回头检查模块是否需要重装.

Wine 笔记

说明

  • 本页记录 WineLinuxMint / Deepin 场景下的安装和中文字体配置.
  • 适合作为“先把 Windows 程序跑起来, 再继续处理字体和兼容问题”的速记页.

参考

LinuxMint 19 安装 Wine

sudo dpkg --add-architecture i386
wget -nc https://dl.winehq.org/wine-builds/winehq.key
sudo apt-key add winehq.key
sudo add-apt-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ focal main'

sudo apt update
sudo apt install --install-recommends winehq-devel

初始化 Wine 环境:

winecfg
  • 初次启动时会提示安装 monogecko, 按向导完成即可.

中文字体配置

sudo apt install fonts-wqy-microhei
cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ~/.wine/drive_c/windows/Fonts

Deepin Wine 兼容路径记录:

cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ~/.deepinwine/Deepin-WeChat/drive_c/windows/Fonts
cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ~/.deepinwine/Deepin-QQ/drive_c/windows/Fonts

注册表字体映射

wqy-microhei.reg 内容:

REGEDIT4

[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink]
"Lucida Sans Unicode"="wqy-microhei.ttc"
"Microsoft Sans Serif"="wqy-microhei.ttc"
"Microsoft YaHei"="wqy-microhei.ttc"
"微软雅黑"="wqy-microhei.ttc"
"MS Sans Serif"="wqy-microhei.ttc"
"Tahoma"="wqy-microhei.ttc"
"Tahoma Bold"="wqy-microhei.ttc"
"SimSun"="wqy-microhei.ttc"
"Arial"="wqy-microhei.ttc"
"Arial Black"="wqy-microhei.ttc"
"宋体"="wqy-microhei.ttc"
"新細明體"="wqy-microhei.ttc"

导入注册表:

wine regedit wqy-microhei.reg
LC_ALL=zh_CN.UTF-8 wine regedit wqy-microhei.reg

Deepin Wine 记录:

deepin-wine6-stable regedit wqy-microhei.reg

Deepin Wine 启动命令

编辑 /opt/deepinwine/tools/run.sh, 调整为:

WINE_CMD="LC_ALL=zh_CN.UTF-8 deepin-wine"

使用建议

  • 先完成基础安装和 winecfg, 再处理字体和语言环境.
  • 如果程序能启动但中文乱码, 优先检查字体文件与注册表映射.
  • 如果程序无法运行, 再回到 32 位依赖, 运行库和兼容层版本排查.
  • 若后续继续整理, 可补 winetricks, 常见运行库安装和图形驱动兼容记录.

Windows 总览

说明

  • 本目录收录 Windows 系统配置, 包管理, 服务封装, 远程访问和二进制分析等记录.
  • 若问题偏日常环境与系统设置, 优先看 Windows 笔记Windows 工具.
  • 若问题偏包管理或开发依赖, 可继续看 Chocolateyvcpkg.

常用入口

使用建议

  • 系统级操作尽量先确认管理员权限, 服务状态和环境变量再执行.
  • 和 Rust / C++ 工具链联动时, 常见入口是 Chocolatey, vcpkg, OpenSSH Server 和二进制导出查看工具.

Windows 笔记

说明

  • 本页整理 Windows 系统清理, 常见排障, 环境变量, 端口与系统设置的高频记录.
  • 当前内容更偏系统维护与问题排查, 适合作为 Windows 运维速查页.

系统清理

常见缓存目录

  • AppData\Local\Yarn\Cache: Yarn 缓存目录, 可用 yarn cache clean 清理.
  • AppData\Local\Temp: 临时文件目录, 可按需清理.
  • AppData\Roaming\Tencent: 某些软件缓存目录, 清理前先确认无业务依赖.

程序依赖与运行错误

0xc000007b

  • 这类错误常和运行库位数不匹配, 缺失 DLL 或运行时环境异常有关.
  • 典型排查方向:
    • 目标程序是 32 位还是 64 位.
    • 依赖 DLL 位数是否一致.
    • VC 运行库是否完整.

查看程序依赖

环境变量

cmd

set A=a/b/c
set A=a/b/c;%A%

PowerShell

ls env:
$env:PATH
$env:A="a/b/c"
$env:A="a/b/c;$env:A"
  • 临时环境变量只对当前会话有效.
  • 长期环境变量建议通过系统设置统一配置.

端口与进程排查

查看进程占用端口

netstat -ano | findstr 6220
  • 6220 为端口号.
  • 查到 PID 后, 再结合任务管理器或 taskkill 继续处理.

进程异常退出排查

  • 打开 eventvwr.msc.
  • 路径: Windows 日志 -> 应用程序.
  • 可按时间和事件来源筛选 Application Error, Windows Error Reporting.

串口与设备

清除 COM 端口占用记录

  • 打开 regedit.
  • 路径: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter\Devices.
  • 删除不再需要的历史 COM 端口映射前, 建议先备份注册表.

删除 VirtualBox 虚拟网卡

  • 先启用目标虚拟网卡, 让其出现在列表中.
  • 再在 VirtualBox 的虚拟网卡管理界面中删除.

安全与系统设置

关闭防火墙后禁用通知

关闭实时保护

界面与桌面问题

桌面图标间距异常

  • 注册表路径: HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics.
  • 可调整 IconSpacingIconVerticalSpacing.
  • 修改前建议先导出备份.

网络与浏览器

Edge 禁止 HTTP 自动重定向到 HTTPS

  • 打开 edge://net-internals/#hsts.
  • Delete domain security policies 中删除对应域名策略.

版本与授权问题

Windows 10 LTSC 授权异常记录

  • 可通过调整 SoftwareProtectionPlatform 下的 SkipRearm 等配置后结合 slmgr -REARM 处理.
  • 这类操作涉及系统授权机制, 修改前建议备份并确认版本适用性.

使用建议

  • 本页优先记录高频系统维护与排障线索, 不宜继续无节制堆积零散命令.
  • 若某个主题已经形成稳定专题, 应拆分到独立页面.
  • 与包管理和服务部署相关的内容, 可继续看 Chocolatey, NSSM, OpenSSH Server 等专题页.

Windows 工具

说明

  • 本页汇总 Windows 下常用终端工具, 进程管理命令和若干自动化安装片段.
  • 内容以日常开发环境整理为主, 适合作为“工具与命令入口页”.

终端工具

进程管理

按名称或 PID 结束进程:

taskkill /f /pid <PID>
taskkill /f /im <ProcessName.exe>

PowerShell 安装片段

静默安装 Python 示例

.\python-3.9.2-amd64.exe /passive InstallAllUsers=1 PrependPath=1 Include_test=0 Include_tcltk=0 TargetDir=$python_dir
Start-Sleep -Seconds 1
$p = Get-Process -Name "python-3.9.2-amd64"
Wait-Process -Id $p.id

静默安装 MSI 示例

$p = Start-Process ".\putty-64bit-0.74-installer.msi" -ArgumentList '/passive INSTALLDIR=$putty_dir' -PassThru -Wait
Print-Status "putty" $p.ExitCode $p.StandardError

包管理工具

服务与远程

其他工具线索

使用建议

  • 本页适合保留“工具入口 + 最小命令 + 跳转关系”, 不适合堆积过长系统设置细节.
  • 若某个工具已经形成独立知识页, 本页只保留导航和最小提示.
  • 与系统维护相关的更长记录, 应回到 Windows 笔记.

wsl 使用

启用 WSL2 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

启用虚拟机平台 dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

启用 Hyper-V dism.exe /online /enable-feature /featurename:Microsoft-Hyper-V /all /norestart

设置 WSL2 为默认 wsl –set-default-version 2

遇到错误

Installing, this may take a few minutes… WslRegisterDistribution failed with error: 0x800701bc Error: 0x800701bc WSL 2 ??????????????????

解决方法 (下载 Linux 内核更新包):

https://learn.microsoft.com/zh-cn/windows/wsl/install-manual#step-4—download-the-linux-kernel-update-package

启用 wsl

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

netsh interface ip show config

netsh interface ip set address “vEthernet (Default Switch)” dhcp

netsh interface ip set address name=“Network Interface Name” static [IP address] [Subnet Mask] [Gateway]

https://docs.microsoft.com/zh-cn/virtualization/hyper-v-on-windows/user-guide/setup-nat-network

  1. 以管理员身份打开 PowerShell 控制台

  2. 创建内部交换机

New-VMSwitch -SwitchName “SwitchName” -SwitchType Internal

  1. 查找刚创建的虚拟交换机的接口索引

Get-NetAdapter

  1. 使用 New-NetIPAddress 配置 NAT 网关

New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex 24

  1. 使用 New-NetNat 配置 NAT 网络

New-NetNat -Name MyNATnetwork -InternalIPInterfaceAddressPrefix 192.168.0.0/24

sudo vim /etc/wsl.conf

添加:

[network] generateResolvConf = false

sudo ip addr del $(ip addr show eth0 | grep ‘inet\b’ | awk ‘{print $2}’ | head -n 1) dev eth0 sudo ip addr add 172.16.0.19/24 broadcast 172.16.0.255 dev eth0

sudo ip route add 0.0.0.0/0 via 172.16.0.1 dev eth0

sudo vim /etc/resolv.conf

修改为 nameserver 172.16.0.1

sudo crontab -e

新增:

@reboot /usr/sbin/netplan apply

ip addr | grep eth0

cat /etc/resolv.conf

输出: … nameserver 172.19.0.1

修改 172.19.0.1 到:

修改

查询 wsl 已分配的 ip 列表

wsl hostname -I

wsl –list –running wsl –list –online

终止指定版本

wsl –terminate Ubuntu-24.04

wsl –install -d ubuntu

wsl –list –all -v

删除 ubuntu wsl –unregister ubuntu-24.04

安装 ubuntu wsl –install -d ubuntu-24.04

设置默认用户

wsl –shutdown ubuntu2404.exe config –default-user root

设置默认 Ubuntu-24.04 wsl –setdefault Ubuntu-24.04

关机

wsl –shutdown

/setdefaultuser

sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i ‘s/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g’ /etc/apt/sources.list sudo sed -i ‘s/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g’ /etc/apt/sources.list

指定用户登录

wsl -u xxx

20241222

wsl –list –online wsl –install -d ubuntu

wsl –list –all -v

wsl –unregister Ubuntu-18.04

wsl –install -d Ubuntu-18.04

设置自动补全

Ubuntu-22.04, root 用户默认没有自动补全

https://www.cnblogs.com/chencoolandclear/p/16464454.html

设置桥接网络

在 “Hyper-V 管理器” 中

新增一个虚拟交换机 VETH

设置 外部网络, 选择指定网卡

在 Windows 的 ~/.wslconfig 中, 设置

[wsl2] networkingMode = bridged vmSwitch = “VETH” ipv6 = true

这种方式 仍有效, 但 微软已弃用

设置镜像网络

在 Windows 的 ~/.wslconfig 中, 设置

[wsl2] networkingMode = mirrored

就可以直接访问 127.0.0.1 了

下载 choco

用administrator执行powershell:
    Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

查看是否安装成功:
    choco --version

查找软件包:
    https://chocolatey.org/search

下载 LxRunOffline

LxRunOffline wiki

choco install LxRunOffline

下载 Ubuntu

https://github.com/tianon/docker-brew-ubuntu-core/tree/dist-amd64

安准 wsl

LxRunOffline.exe i -n ubuntu20 -d D:\WSL\ubuntu20 -f "D:\develop\software\windows\wsl\ubuntu-focal-core-cloudimg-amd64-root.tar.gz" -s

名称: ubuntu20
路径: D:\WSL\ubuntu
并创建缩略图

配置 ubuntu

sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
apt update
apt install -y sudo vim bash-completion python3 python3-pip

# update-alternatives --install /usr/bin/python python /usr/bin/python3.8 2

useradd -m -s /bin/bash imxood
新增用户: imxood

passwd imxood
设置密码

usermod -aG sudo imxood
添加到sudo组

id -u imxood
为了设置默认用户, 查看用户id, 应该是1000

lxrunoffline su -n ubuntu20 -v 1000
添加id是1000的用户为默认用户

vim /etc/wsl.conf

[automount]
enabled = true
root = /mnt/
options = "metadata,umask=22,fmask=11"
mountFsTab = false

vim ~/.bashrc

#Fix mkdir command has wrong permissions
if grep -q Microsoft /proc/version; then
    if [ "$(umask)" == '0000' ]; then
        umask 0022
    fi
fi

wsl 使用代理

根据 ip addr 查看 wsl 中 linux 的 ip, 在 windows 中 ipconfig 查看该网段下的 ip.

在当前 wsl 的 linux 用户的 bash 环境中设置 ~/.bashrc:

export ALL_PROXY=http://172.29.160.1:1080

重命名主机名

sudo hostnamectl set-hostname NEW_HOSTNAME

设置国内软件源

wget -qO- https://linuxmirrors.cn/main.sh | sudo bash

使用 UI

sudo apt install xfce4 xfce4-terminal gtk2-engines-pixbuf

更改默认终端

sudo apt remove gnome-terminal

sudo update-alternatives –config x-terminal-emulator

sudo add-apt-repository ppa:deadsnakes/ppa

sudo apt install python3.8 python3-pip

sudo update-alternatives –install /usr/bin/python3 python3 /usr/bin/python3.6 1 sudo update-alternatives –install /usr/bin/python3 python3 /usr/bin/python3.8 1

sudo update-alternatives –list python3

sudo update-alternatives –config python3

当执行 python3 -c “import apt_pkg” 时 报错: No module named ‘apt_pkg’

修复:

cd /usr/lib/python3/dist-packages

sudo mv apt_pkg.cpython-36m-x86_64-linux-gnu.so apt_pkg.so

设置清华源:

python3 -m pip install –upgrade pip python3 -m pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

安装新的 llvm

wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -

echo “deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main” | sudo tee -a /etc/apt/sources.list sudo apt-get update

sudo apt install llvm-19-dev

编译 Mesa 显卡驱动

依赖

sudo apt install libclc-dev python3-setuptools
libdrm-dev libelf-dev libgbm-dev libpciaccess-dev libx11-dev
libxext-dev libxdamage-dev libxfixes-dev libxrandr-dev
libglvnd-dev libvulkan-dev libwayland-dev wayland-protocols

pip3 install meson ninja cmake mako

cargo install bindgen-cli

安装 glslang

sudo apt-get install vulkan-tools

低于 ubuntu19 需要编译

git clone https://github.com/KhronosGroup/glslang.git

cd glslang

git checkout 7.10.2984

python3 update_glslang_sources.py

mkdir build && cd build

cmake ..

make -j4

sudo make install

libdrm 版本过低 手动编译

git clone https://gitlab.freedesktop.org/mesa/drm.git

cd drm

git checkout libdrm-2.4.124

meson builddir/

ninja -C builddir/ install

代码

git clone https://gitlab.freedesktop.org/mesa/mesa.git

cd mesa

git checkout 24.3

meson setup build ninja -C build/ sudo ninja -C build/ install

ubuntu 系统

windows docker desktop 无法 设置 proxy

移除 “networkingMode = mirrored”

[wsl2]
networkingMode = mirrored

Windows 11 安卓子系统

说明

  • 本页记录在 Windows 11 中启用安卓子系统与安装 APK 工具的基本过程.
  • 适合作为 WSA 环境开启与测试安装链路的简要参考.

Windows11 安装安卓子系统

参考: https://blog.csdn.net/m0_63834988/article/details/132116092

在 Windows 的应用市场, 安装:

  1. 设置虚拟化环境

    启用或关闭Windows功能

    启用: Hyper-V, 虚拟机平台

  2. Windows Amazon AppStore (安装前需要设置 地区(中国受限), 如: 美国)

    可能需要 Amazon帐号, 安装好后, 打开, 开启 开发者选项

  3. apk安装程序

wsatools

Chocolatey

使用管理员权限

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString(‘https://chocolatey.org/install.ps1’))

choco -v

避免频繁的确认信息

choco feature enable -n allowGlobalConfirmation

安装到指定路径

设置环境变量

安装文件路径 (安装过程中的下载文件/中间文件):

ChocolateyInstall: D:\ChocolateyInstall

设置 安装包(bin 路径之类) 安装的路径 (这个似乎没啥用?) (默认好像在 C:\Program Files):

ChocolateyToolsLocation: D:\choco-pkgs

安装软件

chocolatey 查看软件包

choco list

基本环境

choco install cmake git llvm notepadplusplus 7zip everything clash-for-windows windhawk

stm32 环境环境

choco install make openocd gcc-arm-embedded

缩小任务栏图标

执行 https://github.com/valinet/ExplorerPatcher Release 中的小工具 (具体使用没细究), 再设置:

注册表: 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced

TaskbarSmallIcons 为 1 就可以了

恢复 Win10 右键菜单

使用 cmd, 普通权限:

设置: reg add “HKCU\Software\Classes\CLSID{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32” /f /ve

移除: reg.exe delete “HKCU\Software\Classes\CLSID{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32” /va /f

执行后需要重启 资源管理器

alt+tab 快捷键

禁止切换浏览器标签: 设置 -> 多任务处理 -> 对齐或按 Alt+Tab 时显示应用中的标签页

不显示选项卡

vcpkg

  1. 设置 vcpkg

git clone https://github.com/microsoft/vcpkg

cd vcpkg

.\bootstrap-vcpkg.bat -disableMetrics

  1. 设置项目

添加 当前路径 到环境变量中.

.\vcpkg integrate install

.\vcpkg search [search term]

vcpkg list 查看已经安装的库

vcpkg help triplet

.\vcpkg\vcpkg install libusb –triplet=x64-windows-static-md

安装 ffmpeg

vcpkg install ffmpeg:x64-windows

rust opengl-sys

vcpkg install openssl:x64-windows-static-md –debug

–debug 参数, 用于查看编译过程

MSYS2 笔记

说明

  • 本页整理 MSYS2 环境变量继承, pacman 常用命令和国内镜像配置.
  • 适合作为 Windows 下 MinGW / GCC 开发环境搭建的快速参考.

mysy2 中使用 系统环境变量

添加环境变量:

MSYS2_PATH_TYPE=inherit

重启 MSYS2后, 即可

pacman 基本命令

pacman -Syu 更新源, 升级所有软件包

pacman -Ss glib2 搜索glib库 pacman -S msys/glib2-devel 安装该库

搭建 64位开发环境:

pacman -S --needed base-devel mingw-w64-x86_64-toolchain

搭建 32位开发环境:

pacman -S base-devel mingw-w64-i686-toolchain

<!-- pacman -S mingw-w64-i686-cmake 安装32位cmake -->

国内源

在 msys64\etc\pacman.d 目录下: 分别是: mirrorlist.mingw32 mirrorlist.mingw64 mirrorlist.msys

mirrorlist.mingw32

##
## 32-bit Mingw-w64 repository mirrorlist
##

## 清华大学镜像源
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/
## 中国科学技术大学镜像源
Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686/
## 北京理工大学镜像源
Server = http://mirror.bit.edu.cn/msys2/mingw/i686/
## 上海交通大学镜像源
Server = https://mirrors.sjtug.sjtu.edu.cn/msys2/mingw/i686/

mirrorlist.mingw64

##
## 64-bit Mingw-w64 repository mirrorlist
##

## 清华大学镜像源
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64/
## 中国科学技术大学镜像源
Server = http://mirrors.ustc.edu.cn/msys2/mingw/x86_64/
## 北京理工大学镜像源
Server = http://mirror.bit.edu.cn/msys2/mingw/x86_64/
## 上海交通大学镜像源
Server = https://mirrors.sjtug.sjtu.edu.cn/msys2/mingw/x86_64/

mirrorlist.msys

##
## MSYS2 repository mirrorlist
##

## 清华大学镜像源
Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch/
## 中国科学技术大学镜像源
Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch/
## 北京理工大学镜像源
Server = http://mirror.bit.edu.cn/msys2/msys/$arch/
## 上海交通大学镜像源
Server = https://mirrors.sjtug.sjtu.edu.cn/msys2/msys/$arch/

NSSM

说明

  • NSSM 是一个把普通程序包装成 Windows 服务的轻量工具.
  • 适合没有原生 Windows Service 支持的脚本, 命令行工具或守护程序.
  • 官方下载页: https://nssm.cc/download

适用场景

  • 把常驻命令行程序注册为系统服务.
  • 为服务设置启动命令, 工作目录和日志路径.
  • 在 Windows 上为 Python, Node.js, Go 等程序补一个稳定的服务壳层.

基本流程

1. 安装服务

nssm install MyService
  • 执行后会弹出图形界面, 让你填写可执行程序路径, 启动参数和工作目录.

2. 启动服务

nssm start MyService

3. 停止服务

nssm stop MyService

4. 删除服务

nssm remove MyService confirm

关键配置项

Application

  • 指向真正要运行的可执行文件.
  • 若目标是脚本, 通常这里填解释器路径, 参数里再写脚本路径.

Startup directory

  • 很多服务启动失败都和工作目录不正确有关.
  • 依赖相对路径, 配置文件或日志目录的程序尤其要注意.

Arguments

  • 填程序启动参数.
  • 建议先在普通终端里验证命令可独立运行, 再迁移到服务配置.

I/O 日志

  • 可把标准输出和标准错误重定向到文件.
  • 很适合排查服务无法启动或启动后秒退的问题.

使用建议

  • 先确认程序本身可以稳定前台运行, 再包装为服务.
  • 若服务启动失败, 优先检查工作目录, 环境变量和依赖文件路径.
  • 对脚本语言程序, 还要确认解释器路径和虚拟环境路径是否固定.
  • 如果服务依赖网络, 数据库或其它服务, 也要检查启动顺序和重试策略.

常见问题

服务启动后立刻退出

  • 多半是目标程序本身异常退出.
  • 先看 NSSM 配置的 stdout / stderr 日志.

服务里能运行, 手工双击却不正常

  • 通常是工作目录或环境变量不同.
  • 优先比较当前目录, PATH, 配置文件位置.

需要更新程序版本

  • 可以先停服务, 替换程序文件, 再重新启动.
  • 若可执行路径或参数结构变化较大, 建议重新检查 NSSM 配置.

相关文档

Windows 安装 OpenSSH Server

说明

  • 本页记录在 Windows 上手动安装 OpenSSH Server 的基本流程.
  • 适合较早版本系统, 手动部署 Win32-OpenSSH, 或需要显式控制服务配置的场景.

下载与安装

下载并解压后, 使用管理员权限打开 PowerShell, 执行:

.\install-sshd.ps1

如果提示“在此系统上禁止运行脚本”, 可临时调整执行策略:

Set-ExecutionPolicy AllSigned

配置文件位置

C:\ProgramData\ssh

常见 sshd_config 配置项:

Port 22
ListenAddress 0.0.0.0
PubkeyAuthentication yes
PasswordAuthentication no
PermitEmptyPasswords no

服务与防火墙

  • 放行 TCP 22.
  • 在服务管理器中确认并启动:
    • OpenSSH Authentication Agent
    • OpenSSH SSH Server

常见检查项

  • 确认 sshd 服务是否已安装并处于运行状态.
  • 确认防火墙已放行目标端口.
  • 确认登录用户公钥放置位置与权限正确.
  • 若是局域网外访问, 还要检查路由器映射与公网入口.

使用建议

  • 新版本 Windows 已经逐步内置 OpenSSH 能力, 优先检查系统功能里是否可直接启用.
  • 若使用公钥登录, 应优先禁用纯密码登录以降低风险.
  • 安装后建议立即用局域网客户端做一次实际连接验证.

相关文档

查看 dll 或 lib 文件函数定义

说明

  • 本页记录 Windows 下查看动态库和静态库导出符号的常用方式.
  • 对排查 ABI, 位数不匹配和缺失依赖很有帮助.

查看 DLL 导出函数

打开 Developer PowerShell for VS 2022 或带有 dumpbin 的开发者命令行:

dumpbin /exports demo.dll > output.txt

查看库的位数:

dumpbin /headers demo.dll

输出开头常能看到 machine (x86)machine (x64).

查看 EXE 依赖的 DLL

dumpbin /dependents app.exe
  • 当程序启动时提示缺少 DLL 或直接闪退时, 这条命令很实用.

查看 C# DLL

  • 可使用 Visual Studio 自带的 ildasm 打开 .NET 程序集.
  • 适合查看托管 DLL 中的类型, 方法和元数据结构.

查看 LIB 导出成员

dumpbin /linkermember Test.lib > output.txt

快速查看位数

dumpbin /headers Test.lib | findstr machine

SQL 总览

说明

  • 本目录收录关系型数据库的安装, 初始化和基本运维记录.
  • 当前以 MySQLPostgreSQL 为主, 更偏开发环境与常用管理操作.

常用入口

阅读路径

  • 如果目标是快速创建本地开发库, 可先看 MySQL 的初始化命令.
  • 如果要处理角色, 认证和远程连接, 可优先看 PostgreSQL.
  • 非关系型数据库记录当前位于工具目录的其他页面, 例如 MongoDB.

目录边界

  • 本目录主要保留关系型数据库的基础安装和使用记录.
  • 更偏服务部署或容器化内容时, 通常与 Docker, VPS, 阿里云服务 等页面一起看会更完整.

MySQL

安装开源版本

sudo apt install mariadb-server

sudo mysql_secure_installation

    ps: 不修改root用户的密码, 使用系统的认证

sudo mariadb

创建新用户admin:
    create user 'admin'@'localhost' identified by 'admin';

查看当前所有用户:
    select user from mysql.user;

给新用户admin添加root权限:
    grant all on *.* to 'admin'@'localhost';
    flush privileges;

删除用户:
    delete from user where user = "admin";
    flush privileges;

数据库的创建与删除:
    create database test_db;
    drop database test_db;

查看所有的数据库:
    show databases;

选中数据库:
    use test_db;

显示数据库中所有表:    
    show tables;

带有默认编码与字符集:
    create database if not exists `db-mcms-open` default charset utf8 collate utf8_general_ci;

查询默认端口号:
    show global variables like 'port';

创建数据库

字符集: utf8mb4, 排序规则: utf8mb4_general_ci

PostgreSQL 使用

笔记来自: https://cloud.tencent.com/developer/article/1632506

安装

ubuntu 安装命令:
	sudo apt install postgresql postgresql-contrib

PostgreSQL contrib 软件包,它可以提供 PostgreSQL 数据库系统的一些额外特性

一旦安装完成, PostgreSQL 服务将会自动启动

验证:
	sudo -u postgres psql -c "SELECT version();"
输出:
	                            version
	-------------------------------------------------------------------------------------------------------------------------------
	PostgreSQL 12.4 (Ubuntu 12.4-0ubuntu0.20.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.3.0-10ubuntu2) 9.3.0, 64-bit
	(1 row)

PostgreSQL 基础

PostgreSQL 数据库访问权限是通过角色来处理的。一个角色代表一个数据库用户或者一个数据库用户组。

PostgreSQL 支持多种身份认证方式。最常用的方法如下:

	Trust - 只要满足pg_hba.conf定义的条件,一个角色就可以不使用密码就能连接服务器
	Password - 通过密码,一个角色可以连接服务器。密码可以被存储为 scram-sha-256, md5, 和 password(明文)。
	Ident - 仅仅支持 TCP/IP 连接。它通常通过一个可选的用户名映射表,获取客户端操作系统用户名。
	Peer - 和 Ident 一样,仅仅支持本地连接。

	PostgreSQL 客户端身份验证通常被定义在pg_hba.conf文件中。默认情况下,对于本地连接,PostgreSQL 被设置成身份认证防范 peer。
	ubuntu: /etc/postgresql/12/main/pg_hba.conf

	为了以postgres用户身份登录 PostgreSQL 服务器,首先切换用户,然后使用psql工具访问 PostgreSQL:
		sudo su - postgres
		psql

	从这里开始,你可以与 PostgreSQL 实例交互。退出 PostgreSQL Shell,输入:
		\q

	你也可以不切换用户,而使用sudo命令访问 PostgreSQL:
		sudo -u postgres psql

	通常,postgres用户仅仅在本地被使用。

PostgreSQL 基本操作

仅仅超级用户和拥有CREATEROLE权限的角色可以创建新角色。

在下面的例子中,我们创建一个名称为john的角色,一个名称为johndb的数据库,并且授予数据库上的权限:

  1. 创建一个新的 PostgreSQL 角色:

    sudo su - postgres -c “createuser john”

  2. 创建一个新的 PostgreSQL 数据库:

    sudo su - postgres -c “createdb johndb”

想要授权用户操作数据库,连接到 PostgreSQL shell:

sudo -u postgres psql

并且运行下面的 query:

grant all privileges on database johndb to john;

启用远程访问 PostgreSQL 服务器

默认情况下,PostgreSQL 服务器仅仅监听本地网络接口:127.0.0.1

为了允许远程访问你的 PostgreSQL 服务器,打开配置文件postgresql.conf并且在CONNECTIONS AND AUTHENTICATION一节添加 listen_addresses = '*'
ubuntu: /etc/postgresql/12/main/postgresql.conf

	sudo nano /etc/postgresql/12/main/postgresql.conf
	#------------------------------------------------------------------------------
	# CONNECTIONS AND AUTHENTICATION
	#------------------------------------------------------------------------------

	# - Connection Settings -

	listen_addresses = '*'     # what IP address(es) to listen on;

保存文件并且重启 PostgreSQL 服务:

	sudo service postgresql restart

使用ss工具验证修改:

	ss -nlt | grep 5432

输出显示 PostgreSQL 服务器正在监听所有的网络接口(0.0.0.0):

	LISTEN  0        244              0.0.0.0:5432           0.0.0.0:*
	LISTEN  0        244                 [::]:5432              [::]:*

下一步就是配置服务器接受远程连接,编辑pg_hba.conf文件。

下面是一些例子,显示不同的用户场景:

	# TYPE  DATABASE        USER            ADDRESS                 METHOD

	# The user jane can access all databases from all locations using md5 password
	host    all             jane            0.0.0.0/0                md5

	# The user jane can access only the janedb from all locations using md5 password
	host    janedb          jane            0.0.0.0/0                md5

	# The user jane can access all databases from a trusted location (192.168.1.134) without a password
	host    all             jane            192.168.1.134            trust

最后一步就是在你的防火墙上打开端口5432端口。

假设你正在使用UFW来管理你的防火墙,并且你想允许从192.168.1.0/24子网过来的访问,你应该运行下面的命令:

	sudo ufw allow proto tcp from 192.168.1.0/24 to any port 5432

确保你的防火墙被配置好,并仅仅接受来自受信任 IP 范围的连接。

常用操作

查询所有用户:
	select * from pg_user;

指定用户登录,并打开数据库, 查询所有表:
	psql --user x2gouser_maxu --password -h localhost --dbname x2go_sessions
查询所有表:
	\dt

AI 工具总览

说明

  • 本目录收录本地模型部署, OCR, RAG 和 AI 工作流相关记录.
  • 若只是做日常模型接入或知识库实验, 可优先从本页进入.

本地模型与 Agent

RAG 与知识库

OCR 与文档解析

建议阅读路径

  1. 想先把本地工具跑起来时, 先看 ClaudeAutoGen Studio + Ollama 本地部署.
  2. 想做知识库问答和文档检索时, 再看 RAGFlow.
  3. 想做图片或文档 OCR 时, 优先看 Chandra OCR.
  4. 想理解更上层的 Agent / 工作流组织方式, 再看 大模型工作流.

使用建议

  • 如果目标是本地 Agent 或工作流编排, 先看 AutoGen大模型工作流.
  • 如果目标是知识库问答, 可优先看 RAGFlow.
  • 如果目标是图片或文档识别, 可先看 Chandra OCR.
  • 新增 AI 工具页时, 建议至少写清“适用场景, 依赖条件, 最小启动步骤, 常见坑点”.

Claude

说明

  • 本页记录本地使用 Claude CLI 时的高频命令, MCP 配置和插件安装方式.
  • 适合作为“先跑起来, 再逐步接入工具”的操作速记页.

运行 Claude

claude --dangerously-skip-permissions

使用提醒:

  • 该参数会跳过一部分权限确认流程.
  • 仅在你明确理解当前环境和风险时再使用.

配置文件

常见配置文件位置:

~/.claude.json

如果命令行添加 MCP 失败, 可以考虑直接编辑配置文件.

添加 MCP

pdf-reader-mcp

pnpm add @sylphlab/pdf-reader-mcp
claude mcp add --transport stdio pdf-reader-mcp -- npx @sylphlab/pdf-reader-mcp

context7

claude mcp add -s user context7 -- npx -y @upstash/context7-mcp

mcp-server-time

npx @michaellatman/mcp-get@latest install mcp-server-time
claude mcp add --transport stdio time -- uvx mcp-server-time

KiCAD-MCP-Server

命令行示例:

claude mcp add --transport stdio KiCAD-MCP-Server -e "PYTHONPATH=C:\Program Files\KiCad\9.0\lib\python3\dist-packages" -e "NODE_ENV=production" -e "LOG_LEVEL=info" -- node E:\git\python\KiCAD-MCP-Server\dist\index.js

如果命令行参数不好处理, 可以直接修改 ~/.claude.json:

{
  "mcpServers": {
    "kicad": {
      "command": "node",
      "args": ["E:\git\python\KiCAD-MCP-Server\dist\index.js"],
      "env": {
        "PYTHONPATH": "C:\Program Files\KiCad\9.0\lib\python3\dist-packages",
        "NODE_ENV": "production",
        "LOG_LEVEL": "info"
      }
    }
  }
}

插件安装

安装 LSP 插件

/plugin marketplace add boostvolt/claude-code-lsps
/plugin install rust-analyzer@claude-code-lsps

安装 planning-with-files

/plugin marketplace add OthmanAdi/planning-with-files
/plugin install planning-with-files@planning-with-files

使用建议

  • 先保证基础 CLI 可运行, 再逐步添加 MCP 和插件.
  • 每添加一个 MCP 后, 先单独验证其是否可正常启动.
  • 若多个工具一起接入, 建议优先把路径, 环境变量和日志级别写进配置文件统一管理.
  • 若后续继续整理, 可补“常见报错”, “配置迁移” 和 “工具组合方案”笔记.

大模型工作流

说明

  • 本页记录围绕本地大模型工作流的一些环境准备与运行要点.
  • 当前重点是 WSL2 + Ubuntu 24.04 + Docker + NVIDIA GPU 的加速链路, 以及容器代理配置.

在 WSL2 的 Ubuntu 24.04 中为 Docker 启用 NVIDIA GPU

先运行 nvidia-smi 查看 GPU 驱动信息.

安装 CUDA Toolkit

根据平台选择对应脚本, 这里是 Ubuntu WSL:

# 修改为适配 24.04 的路径
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/12.9.1/local_installers/cuda-repo-wsl-ubuntu-12-9-local_12.9.1-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-12-9-local_12.9.1-1_amd64.deb
sudo cp /var/cuda-repo-wsl-ubuntu-12-9-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-9

为 Docker 配置 GPU Runtime

sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

验证:

docker run --rm --gpus all nvidia/cuda:12.0.0-base nvidia-smi

1Panel 中的 Docker 代理配置

容器 -> 配置 -> 全部配置:

{
  "proxies": {
    "http-proxy": "http://127.0.0.1:1080",
    "https-proxy": "http://127.0.0.1:1080",
    "no-proxy": "localhost,127.0.0.1"
  }
}

使用建议

  • GPU 链路排错时, 先确认宿主机驱动, 再确认 WSL, 最后确认 Docker Runtime.
  • 容器可见 GPU 但模型仍跑不起来时, 优先检查镜像内依赖版本和运行参数.
  • 网络环境受限时, 代理配置常常决定镜像拉取和模型下载能否顺利完成.
  • 若后续继续整理, 可补 Ollama, vLLM, 向量数据库和工作流编排的组合实践.

AutoGen Studio + Ollama 本地部署

说明

  • 本页记录 AutoGen StudioOllama 的本地联调流程.
  • 适合快速搭起一个本地多 Agent / 工作流实验环境.

安装 Conda

常见环境变量路径示例:

C:/Users/maxu/anaconda3
C:/Users/maxu/anaconda3/Scripts
C:/Users/maxu/anaconda3/Library/bin

创建环境

conda init
conda create -n autogenstudio python=3.11
conda activate autogenstudio
pip install autogenstudio
autogenstudio ui --port 8081

接入本地模型

在界面中新增模型时, 可按如下方式配置:

  • 模型名: codestral:22b
  • 接口地址: http://127.0.0.1:11434/v1

最小联调思路

  1. 先启动 Ollama 并确认模型可在本地正常响应.
  2. 再启动 AutoGen Studio 的 Web UI.
  3. 在界面里添加模型配置并做一次最小对话验证.
  4. 最后再尝试 Agent 模板, 工具调用和多步骤工作流.

常见关注点

  • 模型名需要与本地 Ollama 实际可用模型一致.
  • 端口和 API 路径要与本机服务配置一致.
  • 若界面能打开但模型不可用, 优先检查本地推理服务是否正常响应.
  • 若后续继续整理, 可补 Agent 模板, 工作流设计与常见报错排查.

RAGFlow

说明

  • 本页记录 RAGFlow 的本地 Docker 部署流程.
  • 适合先快速跑起一个可访问的 RAG 服务实例, 再逐步接入模型和知识库.

使用 Docker 部署

git clone https://github.com/infiniflow/ragflow.git
cd ragflow/docker

docker compose -f docker-compose-CN.yml up -d

docker logs -f ragflow-server

部署后通过浏览器访问:

http://127.0.0.1

最小使用流程

  1. 先确认 Docker 与 docker compose 可正常使用.
  2. 启动服务并观察关键容器日志.
  3. 能打开 Web 页面后, 再配置模型与嵌入服务.
  4. 最后再做文档导入, 切片和检索测试.

常见关注点

  • 启动前先确认 Docker 和端口占用情况.
  • 若在国内网络环境下部署, docker-compose-CN.yml 往往更方便.
  • 真正用于知识库实验时, 还需要继续补模型接入, 文档导入和检索参数配置.
  • 若页面可打开但问答不可用, 优先检查模型接口, 向量化服务和存储组件状态.

后续可补主题

  • 模型供应商和 API 配置.
  • 文档切片策略.
  • 检索参数与召回调优.
  • 用户权限和持久化目录说明.

Chandra OCR

说明

  • Chandra OCR 是知识库中保留的一条 OCR 工具线索页.
  • 当前重点是记录它在“图片文字识别”方向上的定位, 便于后续与其他 OCR 方案做选择比较.

适用场景

  • 快速验证截图, 文档图片或界面文本的识别效果.
  • Tesseract OCR 这类传统方案对比识别质量和接入成本.
  • 作为本地 OCR 工具链整理时的候选项记录.

使用时建议关注

  • 是否支持本地部署或离线运行.
  • 中文与英文混排场景的识别效果.
  • 表格, 公式和复杂版式是否可接受.
  • 接口方式是命令行, SDK 还是服务化调用.

整理建议

  • 若后续继续使用, 可补安装方式, 示例命令和输入输出格式.
  • 若只是工具线索, 可继续保留本页作为 OCR 选型入口, 再把具体实操收敛到更正式的专题页.

关联阅读

SSH

说明

  • 本页整理 SSH 的常见登录, 公钥投递和权限排错方法.
  • 适合新机器初始化和远程连接失败时快速排查.

常用命令

复制公钥到远程服务器

ssh-copy-id USERNAME@HOSTNAME

直接登录

ssh USERNAME@HOSTNAME

首次初始化建议

  1. 先确认服务器可通过密码或控制台方式进入.
  2. 再使用 ssh-copy-id 投递公钥.
  3. 验证公钥登录成功后, 再考虑关闭密码登录.

Permission denied (publickey)

参考: https://www.cnblogs.com/lsgxeva/p/16851306.html

一个常见原因

  • 服务器默认关闭了密码登录, 导致初始化阶段无法完成公钥投递.

可检查 /etc/ssh/sshd_config 中是否包含:

PasswordAuthentication yes

其他常见原因

  • 用户家目录或 ~/.ssh 权限不正确.
  • authorized_keys 内容未正确写入目标用户.
  • 登录用户名错误, 实际写入到了别的账号目录.

权限可参考:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

使用建议

  • 优先使用公钥登录, 再按需关闭密码登录.
  • 若公钥已配置仍失败, 再检查用户目录权限, ~/.ssh 权限和 authorized_keys 内容.
  • 多台服务器时, 建议配合 ~/.ssh/config 做主机别名管理.

Wireshark

说明

  • Wireshark 适合做抓包分析, 协议排查和网络链路定位.
  • 常见误区是把“捕获过滤器”和“显示过滤器”混用, 导致抓不到包或过滤结果异常.

过滤指定端口

tcp.port == 8080
tcp.srcport == 8080
tcp.dstport == 8080
tcp.port == 8080 or udp.port == 8080
tcp.port >= 1000 and tcp.port <= 2000
tcp.port in {80 443}
http and tcp.port == 8080

常见过滤条件

地址过滤

ip.addr == 192.168.1.10
ip.src == 192.168.1.10
ip.dst == 192.168.1.10

协议过滤

dns
http
tls
arp
icmp

内容检索

frame contains "error"
http.request.method == "POST"
dns.qry.name contains "example"

抓包排查流程

  1. 先确认抓包网卡和抓包时机是否正确.
  2. 抓包阶段尽量少用过严的捕获过滤器, 先把流量保留下来.
  3. 进入分析阶段后, 再用显示过滤器逐步收敛到目标连接.
  4. 先看 DNS, TCP 三次握手, TLS 握手, 再看应用层请求和响应.
  5. 若怀疑重传, 超时或 MTU 问题, 继续关注 tcp.analysis.* 相关字段.

使用建议

  • 先区分“捕获过滤器”和“显示过滤器”, 避免抓包阶段就把流量漏掉.
  • 排查单个服务时, 优先从端口过滤开始, 再叠加 http, dns, tls 等协议条件.
  • 分析 HTTPS 问题时, 若无法解密应用层内容, 至少也能先观察连接建立, 证书交换和重传行为.
  • 对比正常与异常抓包, 往往比单独看一份抓包更容易定位问题.

RustDesk

说明

  • RustDesk 是 Rust 生态中常见的远程桌面项目之一.
  • 本页当前以源码构建为主, 适合做本地编译和二次开发排查.

编译参考

Windows 构建

vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
cargo build --release
cargo run --release

常见关注点

  • 构建前先确认 vcpkg 环境与依赖架构一致.
  • 若 Rust 侧编译失败, 再回头检查系统库是否已正确集成到构建环境中.
  • 若只是使用远程桌面功能, 通常不必自行构建, 可优先使用官方发布版本.
  • 若要做二次开发, 建议先把最小构建流程跑通, 再进入 UI 或网络层修改.

Clash

说明

  • 本页记录 Clash Verge 中通过脚本动态追加规则的方式.
  • 适合需要为少量域名单独指定直连或代理策略的场景.

Clash Verge 自定义规则脚本

打开配置目录后, 修改 profiles\Script.js:

/**
 * 配置中的规则 `config.rules` 是一个数组, 通过新旧数组合并来添加.
 * @param prependRule 添加的数组
 */
const prependRule = [
  // 配置为直连
  "DOMAIN,auto.startravel.top,DIRECT",

  // 配置为国外流量
  "DOMAIN-SUFFIX,trae.ai,🔰国外流量",
];

function main(config) {
  let oldrules = config["rules"];
  config["rules"] = prependRule.concat(oldrules);
  return config;
}

这个脚本做了什么

  • 在原有规则数组前面插入自定义规则.
  • 让命中的域名优先走你手工追加的策略.
  • 常见用法包括指定域名直连, 代理或分流到特定策略组.

使用建议

  • 自定义规则通常应放在原规则前面, 才能确保优先匹配.
  • 修改后注意在客户端里重新加载配置.
  • 如果规则越来越多, 更建议按域名分类整理, 避免脚本文件过于混乱.

Squid 用法

说明

  • 本页记录 Squid 的基础转发规则, 白名单 / 黑名单配置和上游代理设置方式.
  • 适合作为基于 ACL 规则的代理分流配置速查页.

squid 用法

# basic 设置:

# 默认端口
# http_port 3128

# 添加3个规则, 这些规则:

# 白名单
acl cn    dstdom_regex 'cn.url_regex.lst'
# 黑名单
acl gfwed dstdom_regex 'gfw.url_regex.lst'
# 定制黑名单
acl custom dstdom_regex 'custom.url_regex.lst'

# 应用这些规则:

# 必须转发
never_direct  allow gfwed
# 必须转发
never_direct  allow custom
# 直连
never_direct  deny  cn

# 请求转发
cache_peer 127.0.0.1 parent 1080 0 no-query name=ssr

MongoDB

说明

  • 本页记录 MongoDB 在 Windows 和 Docker 场景下的常见运维操作.
  • 当前重点包括密码重置, 容器启动和本地副本集初始化.

Windows 重置密码

# 如果服务已启动, 先关闭服务
net stop MongoDB

# 以无密码方式运行 MongoDB 服务
C:\Program Files\MongoDB\Server\8.0\bin\mongod.exe --dbpath C:\Program Files\MongoDB\Server\8.0\data --port 27017 --noauth

cd C:\Program Files\MongoDB\Server\8.0\bin
.\mongod.exe --config mongod.cfg --noauth

# 启动客户端
C:\Program Files\MongoDB\Server\8.0\bin\mongosh.exe

在客户端中创建管理员用户:

use admin
db.createUser({
  user: "admin",
  pwd: "你现在想设的新密码666",
  roles: [{ role: "root", db: "admin" }]
})

show dbs

Docker 运行 MongoDB

docker run -d --name mongodb \
  -v D:/docker/mongodb/data:/data/db \
  -v D:/docker/mongodb/configdb:/data/configdb \
  -p 27017:27017 \
  -e MONGODB_INITDB_ROOT_USERNAME=root \
  -e MONGODB_INITDB_ROOT_PASSWORD=mx123456 \
  --restart=always \
  mongodb/mongodb-community-server:7.0.15-ubuntu2204

Windows 创建副本集

安装 mongodbmongosh 后, 修改:

C:\Program Files\MongoDB\Server\8.0\bin\mongod.cfg

配置两处关键项:

bindIp: 0.0.0.0

replication:
  replSetName: rs0

重启 MongoDB 服务后, 打开 mongosh.exe 并检查:

rs.status()

如果提示 no replset config has been received, 执行初始化:

rs.initiate({_id:"rs0",members:[{_id:0,host:"localhost:27017"}]})

也可顺手创建管理员用户:

use admin
db.createUser({ user: 'root', pwd: '111111', roles: [{ role: "root", db: "admin" }] });

使用建议

  • Windows 下做密码重置时, 先确认当前服务确实已经停掉, 避免端口冲突.
  • 初始化副本集前, 先把 bindIpreplSetName 配对设置好.
  • Docker 场景中, 数据卷路径和认证信息最好提取到更稳定的部署脚本中.

OpenResty

说明

  • 本页记录 OpenResty / Nginx 场景下一个常见配置: 上传文件大小限制.
  • 适合排查大文件上传时报 413 Request Entity Too Large 一类问题.

client_max_body_size

可在 server 或更高层级中设置上传文件的最大尺寸:

server {
    listen 80;
    listen 443 ssl;
    listen 443 quic;
    server_name auto.iotim.com;

    client_max_body_size 200m;

    ...
}

使用建议

  • 该值应根据业务实际大小设置, 例如 50m, 100m, 200m.
  • 若通过反向代理转发到后端服务, 还要同时确认后端服务本身的请求体大小限制.
  • 修改后记得执行配置检查并重载服务.
  • 如果上传链路很长, 还要一起检查超时配置和临时目录空间.

常见命令

nginx -t
nginx -s reload

适合排查的场景

  • 文件上传直接返回 413.
  • 前端上传中断, 但后端日志没有进入业务处理.
  • 反向代理层和应用层对最大请求体限制不一致.

后续可补主题

  • 反向代理基础配置.
  • location 匹配和转发规则.
  • Lua 扩展与动态路由.
  • HTTPS 与证书管理笔记.

iceoryx

说明

  • iceoryx 是一个偏底层的零拷贝进程间通信中间件.
  • 本页记录源码拉取和最小编译命令, 适合回忆本地试编流程.

获取源码

git clone https://github.com/eclipse-iceoryx/iceoryx.git
cd iceoryx

CMake 构建

cmake -Bbuild -Hiceoryx_meta -DBUILD_TEST=ON -DINTROSPECTION=OFF -DBINDING_C=ON -DEXAMPLES=ON
cmake --build build -j 20

参数理解

  • BUILD_TEST=ON: 构建测试代码, 方便验证环境是否完整.
  • INTROSPECTION=OFF: 关闭 introspection 组件, 减少当前构建内容.
  • BINDING_C=ON: 启用 C 语言绑定.
  • EXAMPLES=ON: 同时构建示例程序, 便于后续上手.

使用建议

  • 初次接触时先把示例和测试跑通, 再接入自己的工程.
  • 若编译失败, 优先检查 CMake, 编译器版本和系统依赖是否齐全.
  • 若后续继续整理, 可补运行模型, RouDi, 发布订阅示例和调试方法.

VPS 运维笔记

说明

  • 本页记录新购 VPS 的最小初始化步骤和常用运维命令.
  • 适合做个人服务部署, 远程开发环境和轻量实验机的快速检查清单.

初始检查清单

  1. 确认系统版本, 时区和 SSH 连接方式.
  2. 完成普通用户创建和权限配置.
  3. 配置防火墙与必要端口.
  4. 再安装业务运行时, 容器或面板工具.

mdserver-web

安装:

curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | sudo bash

使用提醒:

  • 部署后记得开放面板和业务所需端口.
  • 建议先确认面板仅绑定可信地址, 或配合额外认证方式使用.

新用户与权限

sudo useradd -m -s /bin/bash xxx
sudo usermod -a -G sudo,adm,dialout xxx
sudo passwd xxx

建议:

  • 日常维护优先使用普通用户, 减少长期直接使用 root.
  • 若涉及串口或设备透传, 再按需补充用户组.

Rust 环境

~/.bashrc 中设置:

export RUSTUP_DIST_SERVER="https://rsproxy.cn"
export RUSTUP_UPDATE_ROOT="https://rsproxy.cn/rustup"

安装:

curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh

Cargo 镜像

写入 ~/.cargo/config:

[source.crates-io]
replace-with = 'rsproxy'

[source.rsproxy]
registry = "https://rsproxy.cn/crates.io-index"

[registries.rsproxy]
index = "https://rsproxy.cn/crates.io-index"

[net]
git-fetch-with-cli = true

防火墙

sudo ufw status
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

使用建议:

  • 先只开放真正需要的端口, 不要一开始全部放开.
  • 每新增服务后, 重新核对监听端口和外部暴露范围.

运维建议

  • 定期做系统更新和基础备份.
  • 优先启用 SSH 密钥登录, 再考虑关闭密码登录.
  • 对公开服务至少增加日志, 监控和异常告警的最小闭环.
  • 如果一台机器上承载多个服务, 建议配合 Docker 或反向代理统一管理入口.

uv

uv git

https://github.com/astral-sh/uv

使用代理

设置 HTTP_PROXY HTTPS_PROXY 环境变量

安装 python

uv python install 3.13

查询 python 列表

uv python list

默认 Python 设置为 3.13

uv python pin 3.13

使用默认 python 运行

uv run python –version

卸载 python

uv python uninstall 3.13

uv 可用的 python

uv python find

虚拟环境管理

创建

uv venv –python 3.13

激活

..venv\Scripts\activate

技术线索

说明

  • 本页用于暂存那些“暂时资料不多, 但后续很可能扩展成独立专题”的技术线索.
  • 当前内容更像一个轻量主题清单, 方便后续决定是否拆页或并入现有目录.

OpenMV

  • 可归类为嵌入式机器视觉方向的轻量入口.
  • 若后续补充内容, 可围绕 图像采集, 串口通信, 颜色识别, 目标跟踪 等主题展开.
  • 如果和单片机项目结合较多, 也可以进一步并入 embedded/ 目录中的视觉专题.

CoreMark

  • 常见于嵌入式和处理器性能基准测试场景.
  • 后续可补充:
    • 编译参数对结果的影响.
    • 不同 MCU / CPU 平台的跑分口径.
    • 结果解读时需要注意的主频, 编译器和优化级别.

使用建议

  • 当前页面更适合作为“选题池”, 不建议长期堆积过多零散概念.
  • 一旦某个主题积累到足够多的命令, 图片或实测记录, 就应尽快拆成独立专题页.

阿里云服务

说明

  • 本页当前作为阿里云常用服务记录的轻量入口, 重点先放在 OSS Bucket.
  • 适合作为后续继续整理对象存储, 上传下载, 权限控制和 SDK 使用记录的占位页.
  • 如果后续内容增多, 建议继续拆成 OSS, ECS, CDN, 函数计算 等专题页.

当前主题

OSS Bucket

常见关注点

  • Bucket 创建与地域选择.
  • 访问权限控制, 例如公网读写, 私有读写和 STS 临时授权.
  • SDK 与签名上传.
  • 生命周期规则和静态资源托管.

建议整理方向

  • ossutil / SDK 常用命令.
  • 浏览器直传与回调配置.
  • CDN 和自定义域名绑定.
  • 跨地域访问延迟和成本控制.

使用建议

  • 先明确当前需求是“对象存储”, “静态资源托管”, 还是“临时上传下载授权”.
  • 涉及权限问题时, 优先区分 Bucket 权限, RAM 权限和临时令牌三层配置.
  • 真正进入实现阶段后, 建议补具体命令和 SDK 示例, 不要只保留官方链接入口.

治理文档总览

说明

  • 本目录保存知识库治理, 审计, 分类和维护流程相关文档.
  • 这些文档不直接承载业务知识, 但用于长期维护知识库结构质量.

当前文档

使用建议

  • 需要看全库目录现状时, 优先看 知识库总索引.
  • 需要看问题, 缺口和治理结果时, 优先看 知识库整理清单.
  • 需要按文档角色治理时, 优先看 知识库分类清单.
  • 需要处理目录边界, 归档目录和兼容页时, 看对应专项清单.
  • 需要沿用本轮之后的整理流程时, 以 知识库维护机制 为准.

知识库总索引

统计

  • 共统计 328 篇 Markdown 文档, 不含 SUMMARY.md.
  • 按目录分组列出, 标题优先取页面首个 Markdown 标题.
  • 链接相对当前文档生成, 便于后续人工巡检与搬运.

(root)

(root)

algorithm

algorithm

design

design

embedded

embedded

embedded/ag32

embedded/eide

embedded/esp32

embedded/fpga

embedded/rk3566

embedded/rt-thread

  • art-pi - embedded/rt-thread/art-pi.md

embedded/stm32f103_bluepill

embedded/wch

embedded/模块

embedded/电路

frontend

frontend

frontend/react

frontend/vue

hardware

hardware

hardware/设备

math

math

other

other

program

program

program/assembly

program/c#

program/c_c++

program/c_c++/boost

program/c_c++/nasm

program/c_c++/qt

program/c_c++/qt/MXSpice

program/c_c++/spice

program/dart

program/java

program/java/JeecgBoot

program/python

program/python/asyncio

program/python/dramatiq

  • Dramatiq - program/python/dramatiq/Readme.md

program/python/pyside2

program/robot

program/robot/dora

program/rust

program/rust/candle

  • Candle - program/rust/candle/candle.md

program/rust/code

program/rust/code/call-process

program/rust/code/css-parser

program/rust/code/css-parser-macro

program/rust/code/fonts

program/rust/code/hbs-examples

program/rust/code/libp2p-learn

program/rust/code/nom-examples

  • nom 示例 - program/rust/code/nom-examples/README.md

program/rust/code/notify-copy

program/rust/code/pest-examples

  • pest 示例 - program/rust/code/pest-examples/README.md

program/rust/code/poem-examples

  • Poem 示例 - program/rust/code/poem-examples/README.md

program/rust/code/raqote-examples

program/rust/code/serial-demo

program/rust/code/svelte-web

program/rust/code/tera-examples

  • Tera 示例 - program/rust/code/tera-examples/README.md

program/rust/code/wgpu-examples

  • wgpu 示例 - program/rust/code/wgpu-examples/README.md

program/rust/code/windows-api-demo

program/rust/game

program/rust/library_usage

  • rusb - program/rust/library_usage/rusb.md

program/rust/rust for android

program/rust/test_usb

program/web

program/web/js

program/web/react

program/web/react/ant-app

program/web/react/demo

program/web/react/my-app

program/web/react/react-app

program/web/react/react-demo

program/web/react/react-demo/wasm-test

program/web/vite-svelte

program/web/vue

program/web/vue/demo

program/web/vue/vue-app

tools

tools

tools/ai

tools/android

tools/bochs

tools/cmake

tools/compile

tools/docker

tools/docker/gitlab

tools/docker/service

tools/go

tools/iceoryx

  • iceoryx - tools/iceoryx/iceoryx.md

tools/linux

tools/linux/wine

tools/make

tools/openwrt

tools/qemu_stm32

tools/sql

tools/squid

tools/vscode

tools/windows

知识库整理清单

当前状态

  • src/ 下共统计 328 篇 Markdown 文档.
  • src/SUMMARY.md 当前直接索引 290 个文档入口.
  • 未被 SUMMARY 直接收录的文档共 38 篇.
  • SUMMARY 当前存在 0 个失效路径引用.
  • 当前识别出 41 个总览页, 35 个归档页, 0 个兼容页.

本轮治理结论

  • 一级主题目录均已有总览页或明确主文档承接, 导航主干已稳定.
  • SUMMARY 已收敛为高价值入口导航, 历史样例目录通过上层归档页承接.
  • 兼容页已收敛为轻量旧路径说明, 不再承担主知识内容.
  • 治理文档已集中在 src/other/, 后续批次可继续按同一机制刷新.

分目录概览

  • (root): 共 2 篇, SUMMARY 直达 2 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • algorithm: 共 1 篇, SUMMARY 直达 1 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • design: 共 4 篇, SUMMARY 直达 4 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • embedded: 共 73 篇, SUMMARY 直达 72 篇, 总览页 9 篇, 归档页 1 篇, 兼容页 0 篇, 短页候选 0 篇.
  • frontend: 共 17 篇, SUMMARY 直达 17 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • hardware: 共 15 篇, SUMMARY 直达 15 篇, 总览页 2 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • math: 共 4 篇, SUMMARY 直达 4 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • other: 共 8 篇, SUMMARY 直达 4 篇, 总览页 1 篇, 归档页 0 篇, 兼容页 0 篇, 短页候选 0 篇.
  • program: 共 125 篇, SUMMARY 直达 94 篇, 总览页 13 篇, 归档页 33 篇, 兼容页 0 篇, 短页候选 0 篇.
  • tools: 共 79 篇, SUMMARY 直达 77 篇, 总览页 11 篇, 归档页 1 篇, 兼容页 0 篇, 短页候选 0 篇.

仍需持续补写的短页候选

  • 当前未识别到需要优先补写的短页候选.

兼容页治理状态

  • 当前共识别 0 个兼容页.
  • 其中 0 个页在仓库内已无普通文档引用, 可继续作为删除候选观察.
  • 删除兼容页前仍需同步清理 知识库总索引, 目录总览页和正文中的历史说明文字.

归档目录治理状态

  • program/web/ 继续作为历史 Web 示例归档, 由 program/web/README.md 统一承接.
  • program/rust/code/ 继续作为 Rust 技术验证归档, 由 program/rust/code/README.md 统一承接.
  • program/c_c++/qt/MXSpice/ 继续作为 Qt + ngspice 历史工程归档, 不进入主导航主干.

SUMMARY 审计

  • 失效路径: 0.
  • 重复目标: 0.

知识库分类清单

当前状态

  • src/ 下共统计 328 篇 Markdown 文档.
  • SUMMARY 直接收录 290 篇, 未直接收录 38 篇.
  • 角色统计: 总览页 41, 专题页 252, 归档页 35, 兼容页 0.
  • 动作统计: 保留 293, 通过上层入口访问 0, 归档保留 35, 兼容保留 0, 删除候选 0, 补写后评估加入SUMMARY 0.
  • SUMMARY 失效路径数: 0.

全量分类

(root)

  • OpenProject.md | 标题: 开源贴片项目 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • README.md | 标题: 学而时习之 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口

algorithm

  • algorithm/README.md | 标题: 算法总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口

design

  • design/blender.md | 标题: Blender 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • design/lceda笔记.md | 标题: 立创 EDA 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • design/README.md | 标题: 设计总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • design/即时设计.md | 标题: 即时设计笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口

embedded

  • embedded/485.md | 标题: RS485 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/ag32/AG32笔记.md | 标题: AG32VF407 开发环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/ag32/README.md | 标题: AG32 平台总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/arduino.md | 标题: Arduino 接口笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/blackmagic.md | 标题: Black Magic Probe | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/C语言.md | 标题: C语言 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/dma笔记.md | 标题: DMA 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/eide/sdcc编译.md | 标题: SDCC 编译 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp-rs.md | 标题: esp-rs | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp.md | 标题: ESP-IDF 常用命令 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32c3-rust学习笔记.md | 标题: ESP32-C3 Rust 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32c3.md | 标题: nanoESP32-C3 OpenOCD 调试 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32c3_ble.md | 标题: 蓝牙相关总结 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32c3_uart.md | 标题: nanoESP32-C3 串口与日志设置 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32c3环境搭建.md | 标题: esp32c3 环境搭建 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/esp32s3-rust.md | 标题: ESP32-S3 Rust 环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/espup-rust.md | 标题: espup Rust 环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/nanoESP32-C3.md | 标题: nanoESP32-C3 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/README.md | 标题: ESP32 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/esp32/rust环境分析.md | 标题: esp-rs Rust 环境分析 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/usb烧写方式.md | 标题: ESP32 USB 烧写与日志输出 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/esp32/应用程序的启动流程.md | 标题: ESP32 应用程序的启动流程 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/flash.md | 标题: 片上 Flash 读写 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/FOC.md | 标题: FOC | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/fpga/AGM.md | 标题: AGM 平台与工具链 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/fpga/fpga.md | 标题: Verilog / FPGA 要点 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/fpga/iverilog.md | 标题: iverilog | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/fpga/modelsim.md | 标题: ModelSim | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/fpga/README.md | 标题: FPGA 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/gdb.md | 标题: GDB 用法 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/GPIO和AFIO.md | 标题: GPIO和AFIO | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/HarmonyOS.md | 标题: HarmonyOS 嵌入式仿真 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/hex.md | 标题: Intel HEX 文件格式 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/i2c笔记.md | 标题: i2c 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/i2s.md | 标题: I2S | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/lvgl.md | 标题: lvgl | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/modbus.md | 标题: Modbus | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/pcb.md | 标题: PCB 设计 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/platformio.md | 标题: PlatformIO | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/pwm笔记.md | 标题: PWM 通用入口 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/README.md | 标题: 嵌入式总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/rk3566/rk3566.md | 标题: RK3566 开发环境与镜像构建 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/rt-thread/art-pi.md | 标题: art-pi | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/Rust嵌入式.md | 标题: Rust嵌入式开发环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/spi协议.md | 标题: SPI 协议 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/stm32f103_bluepill/pwm.md | 标题: STM32 PWM 配置记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/stm32f103_bluepill/README.md | 标题: STM32F103 BluePill 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/stm32f103_bluepill/stm32开发环境.md | 标题: STM32笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/TensorFlowLite.md | 标题: TensorFlow Lite Micro | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/tvs管.md | 标题: TVS 管 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/uart.md | 标题: UART 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/usb.md | 标题: USB 用法笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/vivado.md | 标题: Vivado | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/watchdog.md | 标题: 看门狗 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/wch/ch32v307vct6.md | 标题: CH32V307VCT6 疑难问题 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/传感器.md | 标题: 传感器总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/单片机模块.md | 标题: 模块与扩展器件总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/基本通信协议.md | 标题: 基本通信协议 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/天线匹配.md | 标题: 天线匹配 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/嵌入式资源收集.md | 标题: 嵌入式资源收集 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/总结.md | 标题: 嵌入式知识总结 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • embedded/模块/README.md | 标题: 模块与扩展器件归档 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/模块/ws2812.md | 标题: ws2812 使用笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/模块/按键.md | 标题: 4x4 键盘矩阵 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/电子元件.md | 标题: 嵌入式元器件选型笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/电路/README.md | 标题: 嵌入式电路记录 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • embedded/电路/主副电路切换.md | 标题: 主副电路切换 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/电路/几种MOS管驱动电路.md | 标题: MOS 管驱动电路 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/电路知识.md | 标题: 嵌入式电路应用笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/蓝牙笔记.md | 标题: 蓝牙笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/蜂鸣器.md | 标题: 蜂鸣器 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/调试.md | 标题: 调试 STM32F767 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • embedded/音频功率放大器.md | 标题: 音频功率放大器 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口

frontend

  • frontend/css.md | 标题: CSS 交互技巧 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/d3.md | 标题: D3 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/echarts.md | 标题: ECharts | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/electron+neon.md | 标题: electron+neon | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/node-red.md | 标题: Node-RED | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/nodejs.md | 标题: Node.js 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/pixijs.md | 标题: PixiJS 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/react/react.md | 标题: React 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/README.md | 标题: 前端总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • frontend/svelte.md | 标题: Svelte | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/tool.md | 标题: 前端工具库 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/typescript.md | 标题: Typescript 的一些用法记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/unocss.md | 标题: UnoCSS | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/vite.md | 标题: Vite | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/vue/vue.md | 标题: vue 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/web.md | 标题: web | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • frontend/图表工具.md | 标题: 前端图表工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口

hardware

  • hardware/MOS管.md | 标题: MOS管 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/README.md | 标题: 硬件总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • hardware/三极管.md | 标题: 三极管 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/二极管.md | 标题: 二极管 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/晶振.md | 标题: 晶振 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/电容.md | 标题: 电容 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/电源电路.md | 标题: 电源电路 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/电阻.md | 标题: 电阻 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/设备/EC11旋转编码器.md | 标题: EC11 旋转编码器 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/设备/README.md | 标题: 设备记录总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • hardware/设备/TFT_LCD ST7735 显示屏.md | 标题: TFT_LCD ST7735 显示屏 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/设备/兰科芯128GU盘.md | 标题: 兰科芯128G U盘 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/设备/红米note4拆解.md | 标题: 红米note4拆解 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/运算放大器.md | 标题: 运算放大器 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • hardware/门电路.md | 标题: 门电路 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口

math

  • math/README.md | 标题: 数学总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • math/微分.md | 标题: 微分 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • math/积分.md | 标题: 积分 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • math/线性代数.md | 标题: 线性代数 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口

other

  • other/README.md | 标题: 治理文档总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 治理文档 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • other/兼容页审计清单.md | 标题: 兼容页审计清单 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: 未直接收录 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/归档目录治理清单.md | 标题: 归档目录治理清单 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: 未直接收录 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/目录层级治理清单.md | 标题: 目录层级治理清单 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: 未直接收录 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/知识库分类清单.md | 标题: 知识库分类清单 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/知识库总索引.md | 标题: 知识库总索引 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/知识库整理清单.md | 标题: 知识库整理清单 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构
  • other/知识库维护机制.md | 标题: 知识库维护机制 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: 未直接收录 / 治理文档 | 说明: 属于治理或审计文档, 用于长期维护知识库结构

program

  • program/assembly/AT&T汇编基础.md | 标题: AT&T 汇编基础 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c#/README.md | 标题: C# 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/c#/生成动态库.md | 标题: C# 生成动态库 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c#/笔记.md | 标题: C# 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c#/调用c_dll的两种方式.md | 标题: 调用 C DLL 的两种方式 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/boost/README.md | 标题: Boost 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/c_c++/boost/win10下cmake boost环境搭建.md | 标题: Windows 10 下 CMake + Boost + MinGW-w64 环境搭建 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/c++笔记.md | 标题: c++学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/C语言笔记.md | 标题: C语言笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/gcc笔记.md | 标题: gcc 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/nasm/nasm笔记.md | 标题: nasm 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/qt/MXSpice/README.md | 标题: MXSpice 归档说明 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/c_c++/qt/qtusb.md | 标题: QtUsb | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/qt/qt笔记.md | 标题: Qt 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/qt/qwt笔记.md | 标题: Qwt | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/qt/README.md | 标题: Qt 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/c_c++/README.md | 标题: C 与 C++ 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/c_c++/spice/ex01.md | 标题: SPICE 示例 ex01 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/spice/ltspice笔记.md | 标题: LTspice 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/spice/multisim.md | 标题: Multisim 瞬态仿真收敛问题 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/c_c++/spice/ngspice.md | 标题: ngspice笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/spice/README.md | 标题: SPICE 仿真总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/c_c++/tess.md | 标题: Tesseract OCR | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/volitale笔记.md | 标题: volatile 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/windows_api.md | 标题: Windows API | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/c_c++/windows异步通知IO模型与重叠IO模型.md | 标题: Windows 异步通知 IO 模型与重叠 IO 模型 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/dart.md | 标题: Dart 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/flutter.md | 标题: Flutter 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/flutter_rust.md | 标题: Flutter + Rust 环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/flutter_rust_bridge.md | 标题: flutter_rust_bridge | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/flutter编译.md | 标题: Flutter 引擎编译 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/dart/README.md | 标题: Dart 与 Flutter 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/dart/异常处理.md | 标题: Flutter 异常处理 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/java/java开发.md | 标题: Java 开发环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/java/JeecgBoot/jeecg.md | 标题: JeecgBoot 环境与启动记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/java/README.md | 标题: Java 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/python/asyncio/asyncio.md | 标题: asyncio 学习笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/conda.md | 标题: Conda | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/dramatiq/Readme.md | 标题: Dramatiq | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/paramiko.md | 标题: Paramiko | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/process.md | 标题: 创建进程 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/pyenv使用说明.md | 标题: pyenv 使用说明 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/pyside2/使用pyside2.md | 标题: PySide2 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/python3笔记.md | 标题: python3 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/python工具.md | 标题: Python 工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/python技巧.md | 标题: Python 技巧 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/py正则.md | 标题: Python 正则速记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/python/README.md | 标题: Python 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/python/socket.md | 标题: socket 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/README.md | 标题: 编程总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/robot/dora/dora.md | 标题: Dora 向导 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/robot/robot.md | 标题: 机器人技术总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/robot/ros2.md | 标题: ROS2 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/bindgen.md | 标题: bindgen | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/candle/candle.md | 标题: Candle | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/code/call-process/README.md | 标题: call-process 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/css-parser-macro/README.md | 标题: css-parser-macro 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/css-parser/README.md | 标题: css-parser 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/fonts/README.md | 标题: 字体资源归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/hbs-examples/README.md | 标题: Handlebars 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/libp2p-learn/Readmd.md | 标题: libp2p relay 示例运行记录 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/nom-examples/README.md | 标题: nom 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/notify-copy/README.md | 标题: notify-copy 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/pest-examples/README.md | 标题: pest 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/poem-examples/README.md | 标题: Poem 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/raqote-examples/README.md | 标题: raqote 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/README.md | 标题: Rust 代码示例归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/serial-demo/README.md | 标题: serial-demo 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/svelte-web/README.md | 标题: Svelte Web + Tauri 示例归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/tera-examples/README.md | 标题: Tera 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/wgpu-examples/README.md | 标题: wgpu 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/code/windows-api-demo/README.md | 标题: Windows API 示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/dioxus.md | 标题: Dioxus | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/egui.md | 标题: egui 学习 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/fontdue.md | 标题: fontdue | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/game/bevy.md | 标题: bevy 一个游戏引擎 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/game/README.md | 标题: Rust 游戏开发 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/rust/game/winit.md | 标题: winit | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/guillotiere.md | 标题: guillotiere | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/libc.md | 标题: libc 与 C 运行时互操作 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/library_usage/rusb.md | 标题: rusb | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/makepad.md | 标题: Makepad | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/mesa.md | 标题: Mesa Windows 构建记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/opencv.md | 标题: OpenCV | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/openra.md | 标题: OpenRA 构建记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/regex.md | 标题: Regex | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust crate 工具集.md | 标题: Rust crate 工具集 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust for android/egui_ardroid.md | 标题: egui for Android | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust for android/README.md | 标题: Rust for Android 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/rust/rust for android/tauri.md | 标题: Tauri for Android | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust for android/基本环境设置.md | 标题: Rust for Android 基本环境设置 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust for wasm.md | 标题: Rust for WebAssembly | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust.md | 标题: Rust 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • program/rust/rust_iter_迭代器.md | 标题: Rust 迭代器技巧 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust代码集锦.md | 标题: Rust 代码集锦 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust后端.md | 标题: Rust 后端开发 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust命令.md | 标题: Rust 命令速记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust宏.md | 标题: Rust 宏 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust工具.md | 标题: Rust 工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust源码编译.md | 标题: rust 源码编译 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust索引性能讨论.md | 标题: Rust 索引性能讨论 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust要点.md | 标题: Rust 要点 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust调试技巧.md | 标题: Rust 调试技巧 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/rust问题汇总.md | 标题: Rust 问题汇总 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/serde_json.md | 标题: serde_json | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/servo.md | 标题: Servo 构建记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/tauri.md | 标题: Tauri | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/test_usb/README.md | 标题: test_usb 实验目录说明 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/rust/wasm.md | 标题: wasm-bindgen CLI | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/wgpu.md | 标题: wgpu | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/windows-rs.md | 标题: windows-rs | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/rust/windows7.md | 标题: Rust for Windows 7 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • program/web/js/README.md | 标题: JavaScript 零散脚本归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/ant-app/README.md | 标题: Umi React 示例项目 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/demo/README.md | 标题: React 单文件示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/my-app/README.md | 标题: React my-app 示例说明 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/react-app/README.md | 标题: CRA React 示例归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/react-demo/README.md | 标题: Vite React 示例项目 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/react-demo/wasm-test/README.md | 标题: wasm-test 实验说明 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/react/README.md | 标题: React 示例目录 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/README.md | 标题: Web 示例项目归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/vite-svelte/README.md | 标题: Vite + Svelte 示例归档 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/vue/demo/README.md | 标题: Vue 单文件示例 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/vue/README.md | 标题: Vue 示例目录 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • program/web/vue/vue-app/README.md | 标题: Vue 示例项目说明 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接

tools

  • tools/ai/AutoGen_Ollama_本地部署.md | 标题: AutoGen Studio + Ollama 本地部署 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ai/chandra_ocr.md | 标题: Chandra OCR | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ai/claude.md | 标题: Claude | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ai/ragflow.md | 标题: RAGFlow | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ai/README.md | 标题: AI 工具总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/ai/大模型工作流.md | 标题: 大模型工作流 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/aliyun服务.md | 标题: 阿里云服务 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/android/android.md | 标题: Windows 11 安卓子系统 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/autocad.md | 标题: AutoCAD | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/bochs/新手指南.md | 标题: Bochs 上手指南 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/clash.md | 标题: Clash | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/cmake/cmake笔记.md | 标题: CMake 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/cmake/README.md | 标题: CMake 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: 未直接收录 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/compile/openocd gdb 笔记.md | 标题: OpenOCD 与 GDB | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/compile/README.md | 标题: 编译与调试总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/compile/编译工具.md | 标题: 编译工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/docker-compose用法.md | 标题: docker-compose 用法 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/Dockerfile用法.md | 标题: Dockerfile 用法 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/docker笔记.md | 标题: docker的基本使用及技巧 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/gitlab/Readme.md | 标题: GitLab 与 Taiga 部署记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/README.md | 标题: Docker 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/docker/service/Readme.md | 标题: 服务容器记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/docker/部署自动化环境.md | 标题: 部署自动化环境 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ffmpeg.md | 标题: FFmpeg | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/git.md | 标题: git 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/go/README.md | 标题: Go 环境总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/go/ubuntu安装go.md | 标题: Ubuntu 安装 Go | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/iceoryx/iceoryx.md | 标题: iceoryx | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/latex笔记.md | 标题: LaTeX 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/deepin.md | 标题: Deepin | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/homebrew.md | 标题: Homebrew | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/issues.md | 标题: Linux 常见问题 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/kde.md | 标题: KDE | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/linux工具.md | 标题: Linux 工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/linux常用命令.md | 标题: Linux 常用命令 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/Linux笔记.md | 标题: Linux 运维笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/manjaro笔记.md | 标题: Manjaro 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/README.md | 标题: Linux 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/linux/ubuntu.md | 标题: Ubuntu | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/linux/wine/wine笔记.md | 标题: Wine 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/make/make笔记.md | 标题: make 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/markdown.md | 标题: markdown 常用语法 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/mdbook.md | 标题: mdbook 构建工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/mongodb.md | 标题: MongoDB | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/msys2.md | 标题: MSYS2 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/nginx笔记.md | 标题: Nginx 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/openresty.md | 标题: OpenResty | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/openwrt/leda编译.md | 标题: OpenWrt / LEDE 编译 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/openwrt/openwrt笔记.md | 标题: OpenWrt 试玩笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/openwrt/README.md | 标题: OpenWrt 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/qemu_stm32/qemu_stm32.md | 标题: qemu_stm32 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/raspberry笔记.md | 标题: Raspberry Pi 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/README.md | 标题: 工具总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/rustdesk.md | 标题: RustDesk | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/shell笔记.md | 标题: Shell 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/sql/mysql.md | 标题: MySQL | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/sql/PostgreSQL使用.md | 标题: PostgreSQL 使用 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/sql/README.md | 标题: SQL 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/squid/说明.md | 标题: Squid 用法 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/ssh.md | 标题: SSH | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/technology.md | 标题: 技术线索 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/tools.md | 标题: 历史工具清单 | 类型: 归档页 | 动作: 归档保留 | SUMMARY: | 标签: 未直接收录 / 归档目录 | 说明: 位于已确认的历史样例归档目录, 通过上层入口统一承接
  • tools/trae.md | 标题: Trae 插件安装记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/uv.md | 标题: uv | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/virtualbox.md | 标题: VirtualBox 使用指南 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/vps.md | 标题: VPS 运维笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/vscode/vscode.md | 标题: VSCode 笔记 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/vscode/vscode插件开发.md | 标题: VSCode 插件开发 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/chocolatey.md | 标题: Chocolatey | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/dll lib 文件接口查询.md | 标题: 查看 dll 或 lib 文件函数定义 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/nssm.md | 标题: NSSM | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/README.md | 标题: Windows 总览 | 类型: 总览页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 / 目录入口 | 说明: 承担目录导航职责, 应作为稳定入口
  • tools/windows/vcpkg.md | 标题: vcpkg | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/windows.md | 标题: Windows 笔记 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/windows工具.md | 标题: Windows 工具 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/wsl.md | 标题: wsl 使用 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/windows/安装openssh server.md | 标题: Windows 安装 OpenSSH Server | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/wireshark.md | 标题: Wireshark | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口
  • tools/主板.md | 标题: 主板记录 | 类型: 专题页 | 动作: 保留 | SUMMARY: | 标签: SUMMARY直达 | 说明: 已在 SUMMARY 中有直接入口