韦东山开发手册阅读笔记(五)

第十三章 LCD控制器

13.1 帧缓冲区(Frame Buffer)本质

scss 复制代码
┌─────────────────────────────────────────┐
│              物理内存(SDRAM)            │
│  ┌─────────────────────────────────────┐ │
│  │         帧缓冲区(Frame Buffer)      │ │
│  │   每个像素对应一个内存地址             │ │
│  │                                     │ │
│  │   像素(0,0)  像素(1,0)  像素(2,0)... │ │
│  │   [0x30000000] [0x30000002] [...]   │ │
│  │                                     │ │
│  │   像素(0,1)  像素(1,1)  像素(2,1)... │ │
│  │   [...]      [...]      [...]       │ │
│  │                                     │ │
│  └─────────────────────────────────────┘ │
│                   ↓ DMA自动传输            │
│  ┌─────────────────────────────────────┐ │
│  │         LCD控制器                    │ │
│  │   按扫描时序读出像素数据 → 显示屏      │ │  VCLK信号:每个时钟周期传输 1 个像素
│  └─────────────────────────────────────┘ │
└─────────────────────────────────────────┘

┌─────────────────────────────────┐
│  帧缓冲区 = 内存中的一块区域     │
│  • 每个像素对应内存中的若干位    │
│  • LCD控制器通过DMA自动读取显示  │
│  • CPU只需修改内存,无需操作硬件 │
└─────────────────────────────────┘

核心理解 :显示的本质是内存到屏幕的DMA传输,CPU只需填内存,硬件自动刷新

像素格式:
格式 每像素位数 颜色表示 现代对应
8BPP 8位 调色板索引(256色) 已淘汰,理解原理
16BPP 16位 RGB565(5红6绿5蓝) ✅ 仍常用(资源受限设备)
24BPP 24/32位 RGB888/ARGB8888 ✅ 现代主流

💡 关键公式

帧缓冲区大小 = 分辨率 × 每像素字节数

例:800×480 @ 16BPP = 800×480×2 = 768KB

13.2 显示时序

scss 复制代码
一帧图像的扫描过程(Z字形):

VSYNC脉冲 ─┐
           │  ↑ 垂直同步(帧开始)
           ▼  │
           ┌──┴────────────────────────┐
           │ 上边框(VSPW+VBPD行无效数据) │
           │                             │
           ┌──┬──┬──┬──┬──┬──┬──┬──┐     │
HSYNC→     │  │  │  │  │  │  │  │  │ ... │  ← 有效显示区
脉冲       │  │  │  │  │  │  │  │  │     │    (LINEVAL+1行)
           │  │  │  │  │  │  │  │  │     │ 
           └──┴──┴──┴──┴──┴──┴──┴──┘     │
           │  左边框  有效像素  右边框     │
           │  (HBPD) (HOZVAL+1) (HFPD)   │
           │                             │
           │  下边框(VFPD行无效数据)    │
           └─────────────────────────────┘
           
关键参数(现代DRM驱动中叫"display timing"):
- VSYNC/HSYNC:同步脉冲宽度
- VBP/VFP:垂直前后肩(上下黑边)
- HBP/HFP:水平前后肩(左右黑边)
- CLK:像素时钟频率

13.2.1 一帧是怎么"扫"出来的?

ini 复制代码
假设分辨率:800×480(宽×高)

🎬 垂直扫描过程(从上到下):

① VSYNC 脉冲(VSPW=2 行)
   [无效][无效] ← 上方黑边(部分)
   
② 后肩等待(VBPD=10 行)
   [无效]×10 ← 上方黑边(剩余)
   
③ 有效数据(LINEVAL=479 → 480 行)✅
   [有效行 0]  ← 屏幕第 1 行
   [有效行 1]  ← 屏幕第 2 行
   ...
   [有效行 479]← 屏幕第 480 行
   
④ 前肩等待(VFPD=20 行)
   [无效]×20 ← 下方黑边
   
⑤ 下一个 VSYNC 脉冲 → 回到①,开始新一帧


🎬 水平扫描过程(每行从左到右):

对每一行重复:
① HSYNC 脉冲(HSPW=4 像素)
   [无效]×4 ← 左侧黑边(部分)
   
② 后肩等待(HBPD=40 像素)
   [无效]×40 ← 左侧黑边(剩余)
   
③ 有效数据(HOZVAL=799 → 800 像素)✅
   [像素 0][像素 1]...[像素 799] ← 屏幕显示
   
④ 前肩等待(HFPD=10 像素)
   [无效]×10 ← 右侧黑边
   
⑤ 下一个 HSYNC → 回到①,开始下一行

13.2.2 水平方向(行)与 垂直方向(帧)的嵌套关系:

水平方向 (Horizontal Timing):刷出一行

当你看到屏幕上的一行文字时,背后发生了这些事:

  1. HSPW (同步脉冲): 刷子发出"咔哒"一声,准备开始新的一行。
  2. HBPD (后肩): 刷子挪到纸张边缘,还没开始动笔。
  3. HOZVAL (有效数据): 刷子疯狂输出,这才是你屏幕上的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 640 640 </math>640 或 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1024 1024 </math>1024 个像素。
  4. HFPD (前肩): 刷子到了纸张右边缘,停笔,准备下一行动作。
垂直方向 (Vertical Timing):刷出一屏

把上面的"行"看作一个整体。刷出一屏(一帧)的过程:

  1. VSPW (同步脉冲): 发出信号,准备刷全新的第一帧。
  2. VBPD (后肩): 刷子挪到屏幕顶端,等待几行的时间。
  3. LINEVAL (有效行): 刷子一行一行地刷,刷够 <math xmlns="http://www.w3.org/1998/Math/MathML"> 480 480 </math>480 行或 <math xmlns="http://www.w3.org/1998/Math/MathML"> 768 768 </math>768 行。
  4. VFPD (前肩): 刷到底部,等待一会儿,准备回到顶端刷下一帧。

13.2.3 为什么会有这些"无效"的参数(Porch)?

VSPW、VBPD、VFPD (垂直方向)和 HSPW、HBPD、HFPD (水平方向),统称为 "消隐期"

  • 历史原因: 以前的电子束扫完一行回到左边(回扫)需要时间。
  • 一句话"肩" = 同步信号和有效数据之间的"缓冲带" ,确保显示稳定。

13.2.4 时序参数

像素时钟频率计算
ini 复制代码
VCLK = 水平总像素 × 垂直总行数 × 刷新率

例如:1024×768@60Hz
水平总像素 = HSPW + HBP + 1024 + HFP ≈ 1344(含消隐期)
垂直总行数 = VSPW + VBP + 768 + VFP ≈ 806(含消隐期)

VCLK = 1344 × 806 × 60 ≈ 65 MHz

现代MIPI DSI/LVDS接口速率更高:
4K@60Hz ≈ 594 MHz(需压缩或双通道)
帧率计算
参数 公式 说明
像素时钟 VCLK = HCLK / [(CLKVAL+1)×2] S3C2440特定
帧率 FrameRate = VCLK / (总像素 × 总行数) 通用
总像素 (HSPW+1)+(HBPD+1)+(HOZVAL+1)+(HFPD+1) 含消隐
总行数 (VSPW+1)+(VBPD+1)+(LINEVAL+1)+(VFPD+1) 含消隐
帧缓冲区大小计算
ini 复制代码
帧缓冲区大小 = 宽度 × 高度 × 每像素字节数

例1:1920×1080, 32BPP(4字节)
大小 = 1920 × 1080 × 4 = 8,294,400 字节 ≈ 7.9 MB

例2:双缓冲(前台/后台切换)
总需内存 = 7.9 MB × 2 = 15.8 MB
设计原则
  1. 先确定分辨率(HOZVAL+1, LINEVAL+1)
  2. 根据LCD手册设置最小要求的porch/sync值
  3. 计算所需VCLK = 目标帧率 × 总像素 × 总行数
  4. 反推CLKVAL,取整后验证实际帧率
  5. 微调porch使帧率精确到目标值(如60.00Hz)

13.3 # 帧缓冲区像素寻址

ini 复制代码
// 书中代码(16BPP示例)
uint16_t *addr = (uint16_t*)fb_base_addr + (y * xsize + x);
*addr = color;  // 写入像素

一句话定义:这是把屏幕二维坐标 (x, y) 翻译成内存一维物理地址的"核心翻译器"

所有 GUI 库(LVGL/Qt/FreeType)的底层渲染,最终都会落到这行数学计算上。

公式逐段拆解

部分 含义 生活类比
fb_base 帧缓冲区起始地址(RAM 中的一块内存) 🖼️ 画布的左上角起点
y * xsize 跳过上面 y 整行的像素总数 📏 先往下走 y 行(每行 xsize 个像素)
+ x 在当前行里,向右偏移 x 个像素 ➡️ 再往右走 x
* (bpp/8) 把"第几个像素"转成"第几个字节" 📦 16位色=2字节/像素,32位色=4字节/像素

求像素 (x=100, y=50) 的内存地址:

  • 1.y * xsize = 50 * 800 = 40,000 ← 跳过前50行
  • 2.+ x = 40,000 + 100 = 40,100 ← 在第50行向右100步
  • 3.* (16/8) = 40,100 * 2 = 80,200 ← 转成字节偏移
  • 4.+ fb_base = 0x30000000 + 80200 ← 最终物理地址

核心认知

认知 说明 工程意义
内存是线性的 屏幕没有"二维数组",只有一块连续 RAM 理解为什么需要 y*xsize+x
bpp 决定步长 16位色跳2字节,32位色跳4字节 选错步长 = 画面错位/花屏
GUI 库只是封装 LVGL/Qt 的 lv_draw_rect() 底层还是循环算地址+写内存 调试显示问题直接看帧缓冲区

第十四章 ADC

14.1 定义

ADC是模拟到数字转换器(Analog-to-Digital Converter)缩写,主要用于将连续传输的模拟信号转换为数字信号,便于数字系统(如中央处理器CPU、微控制器MCU等)对传输信息进行快速处理和分析。

ini 复制代码
┌─────────────────────────────────────────┐
│           物理世界(模拟)              │
│    温度、压力、光照、电压...             │
│           ↓ 传感器转换                   │
│    0~3.3V 连续变化的电压                 │
│           ↓ ADC采样                     │
│    ┌─────────────┐                     │
│    │  采样保持    │ ◄── 关键!冻结瞬间值  │
│    │  (S/H)      │                     │
│    └──────┬──────┘                     │
│           ↓ 量化                        │
│    0~1023(10位)离散值                 │
│           ↓ 数字输出                     │
│    二进制数据(0x000~0x3FF)             │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│           数字世界(CPU处理)             │
│    电压值 = ADC值 × 参考电压 / 2^位数    │
│    例:512 × 3.3V / 1024 = 1.65V        │
└─────────────────────────────────────────┘

14.2 相关参数

参数 含义 现代对应
分辨率 位数(10/12/16/24位) 决定精度
采样率 KSPS/MSPS (必须 ≥ 信号最高频率的 2 倍(奈奎斯特定理)) 决定能采多快
参考电压 Vref(3.3V/5V/2.5V) 决定量程
INL/DNL 积分/微分非线性 决定精度
SNR 信噪比 决定有效位数

量化误差

量化误差是 ADC 用"有限个数字台阶"去逼近"连续模拟电压"时,必然产生的"舍入差值"。

它不是故障,而是数字化的物理定律

scss 复制代码
 ADC 同理:
真实电压:1.653 V
10位ADC只能输出:1.651 V (对应512) 或 1.654 V (对应513)
误差:-0.002 V 或 +0.001 V
  • LSB (Least Significant Bit) :ADC 能分辨的最小电压台阶
  • LSB = Vref / 2^N (N 为 ADC 位数)
  • 量化误差永远在 [-0.5 LSB, +0.5 LSB] 之间
ADC位数 台阶数 (2^N) LSB 大小 最大量化误差
10位 1024 3.3V / 1024 ≈ 3.22 mV ±1.61 mV
12位 4096 3.3V / 4096 ≈ 0.81 mV ±0.40 mV
16位 65536 3.3V / 65536 ≈ 0.05 mV ±0.025 mV

实际开发中如何应对?

方法 原理 适用场景 代码/硬件示例
① 提高分辨率 直接缩小 LSB 对精度要求高的传感器(如称重、医疗) 换 24bit ADC(如 HX711)
② 软件滤波 平均/中值/卡尔曼打散随机误差 常规物联网设备(温湿度、电池电压) avg = (raw1+raw2+raw3+raw4)/4
③ 过采样+抽取 以 N 倍速率采样,求和后右移 想用软件"骗"出更高精度 16倍过采样可等效+2bit精度
④ 加抖动 (Dithering) 注入微小噪声打散量化台阶 音频/高精度测量防"死区" 硬件加白噪声 / 软件加随机数

嵌入式最常用组合中值滤波(去毛刺) + 滑动平均(压误差) + 合理选ADC位数

14.3 ADC 通道复用与采样率缩水

ADC 通道复用

本质: 芯片为了省面积、降功耗、控成本,只做了 1 个 ADC 核心,前面加了个"电子开关"轮流接通多个引脚。总采样时间固定,分的人多了,每人分到的次数自然变少。

大多数芯片其实内部只有 1 个 ADC 转换器,但有 8~16 个 引脚,它们是轮询工作的。

结构示意

python 复制代码
引脚 AIN0 ──┐
引脚 AIN1 ──┤
...         ├─→ 多路选择器 (MUX) ──→ 唯一的 ADC 核心 ──→ 数字结果
引脚 AIN7 ──┘
模块 作用 为什么这样设计
MUX(多路开关) 用 MOSFET 电子开关轮流接通指定引脚 节省芯片面积,8 引脚只需 1 套 ADC
ADC 核心 执行真正的采样+量化+编码 高精度 ADC 占面积大、耗电高、成本高
切换稳定时间 开关切换后,内部电容需充电到稳定电压 物理定律,无法消除(通常几百 ns~几 μs)

采样率"缩水"

scss 复制代码
实际每通道采样率 ≈ 总采样率 ÷ (通道数 × 切换开销系数)

举例:

  • 标称最大:500 KSPS(总)
  • 单通道使用:≈ 450~500 KSPS
  • 8 通道轮询:500K ÷ 8 ≈ 62.5K(实际约 40~50K,扣除切换时间)

第十五章移植U-Boot

系统上电之后,需要一段程序来进行初始化:关闭 WATCHDOG、改变系统时钟、初始化存储控制器、将更多的代码复制到内存中等。如果它 能将操作系统内核复制到内存中运行,无论从本地(比如Flash)还是从远端(比如通过网络), 就称这段程序为Bootloader。

可以增强Bootloader 的功能,比如增加网络功能、从 PC 上通过串口或网络下载文件、 烧写文件、将Flash上压缩的文件解压后再运行等,这就是一个功能更为强大的Bootloader, 也称为Monitor。

Bootloader 的三个使命:

  1. 硬件初始化: 关看门狗、调时钟、初始化内存控制器(这是最关键的,没有内存,C 语言的栈都跑不起来)。
  2. 加载内核: 把压缩的 Linux 内核镜像从非易失性存储(NAND Flash/SD卡)拷贝到内存中。
  3. 启动内核: 设置好启动参数(命令号),然后把 CPU 控制权交给 Linux。

15.1 Bootloader 的「两阶段启动模型」

复制代码
Stage 1(汇编):纯硬件初始化
• 关看门狗、关中断、设 CPU 模式
• 初始化 SDRAM(让内存可用)
• 设置栈指针(SP)
• 把 Stage 2 代码从 Flash 拷贝到 RAM
• 跳转到 Stage 2 的 C 入口

Stage 2(C语言):复杂逻辑与系统交接
• 初始化串口/网络/Flash 驱动
• 检测内存映射
• 加载 Linux 内核 & 根文件系统到 RAM
• 准备启动参数
• 跳转到内核入口

现代意义:现代 U-Boot 仍保留此架构,只是 Stage 1 极短,大部分工作由 SPL(Secondary Program Loader)和 U-Boot Proper 分担。

15.2 U-Boot分析与移植

15.2.1 常用Bootloader介绍

现在Bootloader种类繁多,它们各有特点,下面列出Linux的开放源代码的Bootloader及其支持的体 系架构,如表15.1所示。

15.2.2 U-Boot

「U-Boot 不是用来从头写的,是用来配置、裁剪和调试的。」

在 2026 年,我们的开发哲学是:基于成熟单板进行"差异化配置"。

1. 配置化思维 (Defconfigs)

思想: 现代 U-Boot 拥有数千个单板的支持。不需要从头写,而是找到一个"相似的模板"。

  • 掌握点: 学习如何使用 make menuconfig 这种图形化工具,而不是去改 .h 文件。会看到成百上千个功能开关(比如要不要开启网络功能、要不要支持 USB)。
2. 设备树 (Device Tree - DTS)
  • 思想: 过去硬件信息写死在 C 代码里;现在硬件信息写在一个类似 JSON 的文本文件里(.dts)。
  • 掌握点: 即使是 U-Boot,现在也广泛使用设备树来描述硬件。这样改硬件时,不需要重新编译整个 U-Boot,只需要改这个文本文件。

15.2.3 U-Boot 如何把 Linux 内核"叫醒"?

手册中用 tag 结构体(ATAGs)传参,现代已全面替换为设备树(FDT) ,但交互逻辑不变:

步骤 手册做法(旧) 现代做法(新) 关键点
加载内核 nboot/tftp 读到 RAM 同左,支持 FIT 镜像打包 知道内核在 RAM 的哪个地址
传递参数 ATAG_CORE/ATAG_MEM/ATAG_CMDLINE 设备树 Blob (bootm/bootz 自动加载) 理解 console=ttySAC0 root=/dev/mmcblk0p2 怎么来的
跳转内核 theKernel(0, mach_id, atags_addr) bootm/booti 自动处理寄存器设置 知道 R0=0, R1=板级ID/0, R2=DTB地址 的约定

15.2.4 U-Boot 命令行与环境变量

ruby 复制代码
# ① 网络下载(替代串口,速度快100倍)
tftp 0x30000000 uImage
nfs 0x30000000 192.168.1.10:/rootfs.img

# ② 烧写 Flash
nand erase 0x0 0x400000
nand write 0x30000000 0x0 ${filesize}

# ③ 自动启动配置(产品发布用)
setenv bootcmd 'nand read 0x30000000 0x0 0x400000; bootm 0x30000000'
setenv bootargs 'console=ttySAC0,115200 root=/dev/mtdblock2 rootfstype=yaffs2'
saveenv

# ④ 调试神命令
md 0x30000000 10   # 查内存
mw 0x30000000 0x12 3 # 改内存
go 0x30000000      # 跑裸机程序

15.2.5 编译与配置逻辑

现代 U-Boot 已改用 Kbuild/Kconfig(和 Linux 内核一致)

scss 复制代码
配置阶段:选择板子 → 生成 .config → 决定编译哪些驱动
编译阶段:链接脚本(.lds) 决定代码在 Flash/RAM 的布局
输出阶段:生成 u-boot.bin (烧录用) / u-boot.imx (含头校验)

15.3 现代 U-Boot 启动流程

复制代码
上电 → Boot ROM → SPL → U-Boot → Linux → 用户程序

① 上电瞬间:硬件自动动作

markdown 复制代码
 CPU 复位 → 从固定地址 0x00000000 取第一条指令
                   ↓
                 这个地址映射到芯片内部的 Boot ROM(厂商固化,不可改)

✅ Boot ROM 代码是 NXP 写的,只需要知道它"会找启动设备"。

② Boot ROM:找"第一级引导"

arduino 复制代码
🔍 Boot ROM 做的事:
1. 检测启动引脚(BOOT_MODE)→ 决定从 SD/eMMC/USB/NAND 启动
2. 读取设备前 4KB(IVT + DCD + SPL)
3. 校验 CRC → 加载 SPL 到内部 SRAM(128KB)
4. 跳转到 SPL 入口

📦 SPL 是什么?
• 全称:Secondary Program Loader
• 大小:< 128KB(必须塞进芯片内部小内存)
• 任务:初始化 DDR + 加载完整 U-Boot

③ SPL:初始化内存 + 加载 U-Boot

arduino 复制代码
 ① 初始化 DDR 控制器(让 512MB 内存可用)
 ② 从 eMMC/SD 读 U-Boot.bin 到 DDR 0x87800000
 ③ 跳转到 U-Boot 入口
 

④ U-Boot Proper:我们主要配置的部分

markdown 复制代码
🎯 U-Boot 的核心任务:
1. 初始化串口 → 让你能看到打印
2. 初始化网络/存储 → 能下载、能读写
3. 解析设备树 → 知道板子上有哪些硬件
4. 加载 Linux 内核 + 设备树 + 启动参数
5. 跳转到 Linux 入口
实际开发中的 U-Boot 操作
yaml 复制代码
# ① 串口连接开发板(SecureCRT / minicom)
# 波特率:115200,上电后看到:

U-Boot 2023.04 (Apr 11 2024 - 10:20:30 +0800)

CPU:   Freescale i.MX6ULL rev1.1 900 MHz
DRAM:  512 MiB
MMC:   FSL_SDHC: 0, FSL_SDHC: 1
In:    serial
Out:   serial
Err:   serial
Net:   eth0: ethernet@2188000
Hit any key to stop autoboot:  2  1  0  ← 按任意键中断自动启动

=>  # 出现这个提示符,说明进入 U-Boot 命令行!
常用 U-Boot 命令(现代版)
ruby 复制代码
# 🔍 查看环境变量(启动参数都存在这)
=> printenv

# 📋 关键变量解释:
bootargs=console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait
#        ↑串口控制台      ↑根文件系统在 eMMC 分区 2

bootcmd=run findfdt; load mmc 1:1 ${fdt_addr_r} ${fdtfile}; \
        load mmc 1:1 ${kernel_addr_r} zImage; \
        bootz ${kernel_addr_r} - ${fdt_addr_r}
#        ↑加载设备树   ↑加载内核   ↑启动内核

# 🌐 网络下载内核(开发时常用)
=> setenv serverip 192.168.1.100    # 主机 IP
=> setenv ipaddr 192.168.1.50        # 开发板 IP
=> tftp ${kernel_addr_r} zImage     # 下载内核到内存
=> tftp ${fdt_addr_r} imx6ull-14x14-evk.dtb  # 下载设备树
=> bootz ${kernel_addr_r} - ${fdt_addr_r}    # 启动

# 💾 烧写到 eMMC(产品发布用)
=> mmc dev 1                          # 选择 eMMC
=> mmc write ${kernel_addr_r} 0x400 0x2000  # 烧内核到偏移 0x400

# 🔧 修改启动参数(比如改根文件系统)
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p3 rootwait'
=> saveenv  # ⚠️ 必须 saveenv 否则重启失效!

⑤ 跳转到 Linux

scss 复制代码
// U-Boot 启动内核的核心代码(简化)
void do_bootz(cmd_tbl_t *cmdtp, ...) {
    // ① 准备启动参数(设备树 + 命令行)
    setup_kernel_args(bd, bootargs);
    
    // ② 关闭缓存、中断,切换到 SVC 模式
    prepare_for_linux();
    
    // ③ 跳转到内核入口(0x80008000 是 ARM Linux 约定)
    void (*kernel_entry)(int, char**, struct tag*) = 
        (void *)KERNEL_RAM_BASE;
    
    kernel_entry(0, NULL, (struct tag*)bd->bi_boot_params);
    //        ↑  ↑         ↑
    //       R0=0  R1=0   R2=设备树地址(现代用设备树,ATAGs 已废弃)
}

⑥ Linux Kernel 启动

markdown 复制代码
🐧 Linux 启动后:
1. 解压内核(zImage → Image)
2. 初始化内存管理、进程调度
3. 挂载根文件系统(/dev/mmcblk1p2)
4. 执行 /sbin/init → 启动用户程序

📺 你看到的:
[    0.000000] Linux version 6.1.55 ...
[    1.234567] mmc1: new high speed SDHC card at address 1234
[    2.345678] EXT4-fs (mmcblk1p2): mounted filesystem
[    3.456789] systemd[1]: Started Update UTMP about System Boot/Shutdown.

# 最后出现登录提示:
imx6ull-evk login: 
相关推荐
LIZhang20162 小时前
linux写一个脚本实时保存内存占用情况
linux·运维·服务器
IDC02-阿杰2 小时前
Windows WSL2安装Ubuntu24.04全攻略
linux·windows
s09071362 小时前
ZYNQ7000 AXI DMA 接收中断(S2MM_introut)全解析:从硬件原理到Linux驱动开发
linux·驱动开发·dma·zynq
camellias_2 小时前
ubuntu(二)ubuntu18.04安装mysql8
linux·ubuntu·adb
藤谷性能2 小时前
Ubuntu 22.04:安装串口调试助手CoolTerm
linux·运维·ubuntu·串口·coolterm
路溪非溪3 小时前
如何使用sysfs来排查驱动问题
linux·arm开发·驱动开发
丶伯爵式3 小时前
Ubuntu 新装后常用设置
linux·运维·ubuntu
哼?~3 小时前
Socket编程准备
linux·网络
羌俊恩3 小时前
Vim modeline 命令执行漏洞(CVE-2026-34714)修复指导
linux·编辑器·vim·漏洞·cve-2026-34714